| import os |
| from hashlib import md5 |
|
|
| import pytest |
|
|
| from fsspec.implementations.local import LocalFileSystem |
| from fsspec.tests.abstract.copy import AbstractCopyTests |
| from fsspec.tests.abstract.get import AbstractGetTests |
| from fsspec.tests.abstract.put import AbstractPutTests |
|
|
|
|
| class BaseAbstractFixtures: |
| """ |
| Abstract base class containing fixtures that are used by but never need to |
| be overridden in derived filesystem-specific classes to run the abstract |
| tests on such filesystems. |
| """ |
|
|
| @pytest.fixture |
| def fs_bulk_operations_scenario_0(self, fs, fs_join, fs_path): |
| """ |
| Scenario on remote filesystem that is used for many cp/get/put tests. |
| |
| Cleans up at the end of each test it which it is used. |
| """ |
| source = self._bulk_operations_scenario_0(fs, fs_join, fs_path) |
| yield source |
| fs.rm(source, recursive=True) |
|
|
| @pytest.fixture |
| def fs_glob_edge_cases_files(self, fs, fs_join, fs_path): |
| """ |
| Scenario on remote filesystem that is used for glob edge cases cp/get/put tests. |
| |
| Cleans up at the end of each test it which it is used. |
| """ |
| source = self._glob_edge_cases_files(fs, fs_join, fs_path) |
| yield source |
| fs.rm(source, recursive=True) |
|
|
| @pytest.fixture |
| def fs_dir_and_file_with_same_name_prefix(self, fs, fs_join, fs_path): |
| """ |
| Scenario on remote filesystem that is used to check cp/get/put on directory |
| and file with the same name prefixes. |
| |
| Cleans up at the end of each test it which it is used. |
| """ |
| source = self._dir_and_file_with_same_name_prefix(fs, fs_join, fs_path) |
| yield source |
| fs.rm(source, recursive=True) |
|
|
| @pytest.fixture |
| def fs_10_files_with_hashed_names(self, fs, fs_join, fs_path): |
| """ |
| Scenario on remote filesystem that is used to check cp/get/put files order |
| when source and destination are lists. |
| |
| Cleans up at the end of each test it which it is used. |
| """ |
| source = self._10_files_with_hashed_names(fs, fs_join, fs_path) |
| yield source |
| fs.rm(source, recursive=True) |
|
|
| @pytest.fixture |
| def fs_target(self, fs, fs_join, fs_path): |
| """ |
| Return name of remote directory that does not yet exist to copy into. |
| |
| Cleans up at the end of each test it which it is used. |
| """ |
| target = fs_join(fs_path, "target") |
| yield target |
| if fs.exists(target): |
| fs.rm(target, recursive=True) |
|
|
| @pytest.fixture |
| def local_bulk_operations_scenario_0(self, local_fs, local_join, local_path): |
| """ |
| Scenario on local filesystem that is used for many cp/get/put tests. |
| |
| Cleans up at the end of each test it which it is used. |
| """ |
| source = self._bulk_operations_scenario_0(local_fs, local_join, local_path) |
| yield source |
| local_fs.rm(source, recursive=True) |
|
|
| @pytest.fixture |
| def local_glob_edge_cases_files(self, local_fs, local_join, local_path): |
| """ |
| Scenario on local filesystem that is used for glob edge cases cp/get/put tests. |
| |
| Cleans up at the end of each test it which it is used. |
| """ |
| source = self._glob_edge_cases_files(local_fs, local_join, local_path) |
| yield source |
| local_fs.rm(source, recursive=True) |
|
|
| @pytest.fixture |
| def local_dir_and_file_with_same_name_prefix( |
| self, local_fs, local_join, local_path |
| ): |
| """ |
| Scenario on local filesystem that is used to check cp/get/put on directory |
| and file with the same name prefixes. |
| |
| Cleans up at the end of each test it which it is used. |
| """ |
| source = self._dir_and_file_with_same_name_prefix( |
| local_fs, local_join, local_path |
| ) |
| yield source |
| local_fs.rm(source, recursive=True) |
|
|
| @pytest.fixture |
| def local_10_files_with_hashed_names(self, local_fs, local_join, local_path): |
| """ |
| Scenario on local filesystem that is used to check cp/get/put files order |
| when source and destination are lists. |
| |
| Cleans up at the end of each test it which it is used. |
| """ |
| source = self._10_files_with_hashed_names(local_fs, local_join, local_path) |
| yield source |
| local_fs.rm(source, recursive=True) |
|
|
| @pytest.fixture |
| def local_target(self, local_fs, local_join, local_path): |
| """ |
| Return name of local directory that does not yet exist to copy into. |
| |
| Cleans up at the end of each test it which it is used. |
| """ |
| target = local_join(local_path, "target") |
| yield target |
| if local_fs.exists(target): |
| local_fs.rm(target, recursive=True) |
|
|
| def _glob_edge_cases_files(self, some_fs, some_join, some_path): |
| """ |
| Scenario that is used for glob edge cases cp/get/put tests. |
| Creates the following directory and file structure: |
| |
| π source |
| βββ π file1 |
| βββ π file2 |
| βββ π subdir0 |
| β βββ π subfile1 |
| β βββ π subfile2 |
| β βββ π nesteddir |
| β βββ π nestedfile |
| βββ π subdir1 |
| βββ π subfile1 |
| βββ π subfile2 |
| βββ π nesteddir |
| βββ π nestedfile |
| """ |
| source = some_join(some_path, "source") |
| some_fs.touch(some_join(source, "file1")) |
| some_fs.touch(some_join(source, "file2")) |
|
|
| for subdir_idx in range(2): |
| subdir = some_join(source, f"subdir{subdir_idx}") |
| nesteddir = some_join(subdir, "nesteddir") |
| some_fs.makedirs(nesteddir) |
| some_fs.touch(some_join(subdir, "subfile1")) |
| some_fs.touch(some_join(subdir, "subfile2")) |
| some_fs.touch(some_join(nesteddir, "nestedfile")) |
|
|
| return source |
|
|
| def _bulk_operations_scenario_0(self, some_fs, some_join, some_path): |
| """ |
| Scenario that is used for many cp/get/put tests. Creates the following |
| directory and file structure: |
| |
| π source |
| βββ π file1 |
| βββ π file2 |
| βββ π subdir |
| βββ π subfile1 |
| βββ π subfile2 |
| βββ π nesteddir |
| βββ π nestedfile |
| """ |
| source = some_join(some_path, "source") |
| subdir = some_join(source, "subdir") |
| nesteddir = some_join(subdir, "nesteddir") |
| some_fs.makedirs(nesteddir) |
| some_fs.touch(some_join(source, "file1")) |
| some_fs.touch(some_join(source, "file2")) |
| some_fs.touch(some_join(subdir, "subfile1")) |
| some_fs.touch(some_join(subdir, "subfile2")) |
| some_fs.touch(some_join(nesteddir, "nestedfile")) |
| return source |
|
|
| def _dir_and_file_with_same_name_prefix(self, some_fs, some_join, some_path): |
| """ |
| Scenario that is used to check cp/get/put on directory and file with |
| the same name prefixes. Creates the following directory and file structure: |
| |
| π source |
| βββ π subdir.txt |
| βββ π subdir |
| βββ π subfile.txt |
| """ |
| source = some_join(some_path, "source") |
| subdir = some_join(source, "subdir") |
| file = some_join(source, "subdir.txt") |
| subfile = some_join(subdir, "subfile.txt") |
| some_fs.makedirs(subdir) |
| some_fs.touch(file) |
| some_fs.touch(subfile) |
| return source |
|
|
| def _10_files_with_hashed_names(self, some_fs, some_join, some_path): |
| """ |
| Scenario that is used to check cp/get/put files order when source and |
| destination are lists. Creates the following directory and file structure: |
| |
| π source |
| βββ π {hashed([0-9])}.txt |
| """ |
| source = some_join(some_path, "source") |
| for i in range(10): |
| hashed_i = md5(str(i).encode("utf-8")).hexdigest() |
| path = some_join(source, f"{hashed_i}.txt") |
| some_fs.pipe(path=path, value=f"{i}".encode("utf-8")) |
| return source |
|
|
|
|
| class AbstractFixtures(BaseAbstractFixtures): |
| """ |
| Abstract base class containing fixtures that may be overridden in derived |
| filesystem-specific classes to run the abstract tests on such filesystems. |
| |
| For any particular filesystem some of these fixtures must be overridden, |
| such as ``fs`` and ``fs_path``, and others may be overridden if the |
| default functions here are not appropriate, such as ``fs_join``. |
| """ |
|
|
| @pytest.fixture |
| def fs(self): |
| raise NotImplementedError("This function must be overridden in derived classes") |
|
|
| @pytest.fixture |
| def fs_join(self): |
| """ |
| Return a function that joins its arguments together into a path. |
| |
| Most fsspec implementations join paths in a platform-dependent way, |
| but some will override this to always use a forward slash. |
| """ |
| return os.path.join |
|
|
| @pytest.fixture |
| def fs_path(self): |
| raise NotImplementedError("This function must be overridden in derived classes") |
|
|
| @pytest.fixture(scope="class") |
| def local_fs(self): |
| |
| |
| return LocalFileSystem(auto_mkdir=True) |
|
|
| @pytest.fixture |
| def local_join(self): |
| """ |
| Return a function that joins its arguments together into a path, on |
| the local filesystem. |
| """ |
| return os.path.join |
|
|
| @pytest.fixture |
| def local_path(self, tmpdir): |
| return tmpdir |
|
|
| @pytest.fixture |
| def supports_empty_directories(self): |
| """ |
| Return whether this implementation supports empty directories. |
| """ |
| return True |
|
|
| @pytest.fixture |
| def fs_sanitize_path(self): |
| return lambda x: x |
|
|