| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- import os
- from hashlib import md5
- import pytest
- from fsspec.implementations.local import LocalFileSystem
- from fsspec.tests.abstract.copy import AbstractCopyTests # noqa: F401
- from fsspec.tests.abstract.get import AbstractGetTests # noqa: F401
- from fsspec.tests.abstract.open import AbstractOpenTests # noqa: F401
- from fsspec.tests.abstract.pipe import AbstractPipeTests # noqa: F401
- from fsspec.tests.abstract.put import AbstractPutTests # noqa: F401
- 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())
- 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):
- # Maybe need an option for auto_mkdir=False? This is only relevant
- # for certain implementations.
- 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
|