diff --git a/CHANGELOG.md b/CHANGELOG.md index 20c71d4a..f94c5a0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Breaking Changes + +- Renamed `filesystem.validate_zimfile_creatable` to `filesystem.file_creatable` to reflect general applicability to check file creation beyond ZIM files #200 +- Remove any "ZIM" reference in exceptions while working with files #200 + +### Added + +- Add `filesystem.validate_folder_writable` to check if a folder can be written to #200 + ## [4.0.0] - 2024-08-05 ### Added @@ -38,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING** Rename `i18.NotFound` to `i18n.NotFoundError` ### Removed + - **BREAKING** Remove translation features in `i18n`: `Locale` class + `_` and `setlocale` functions #134 ### Fixed diff --git a/src/zimscraperlib/__about__.py b/src/zimscraperlib/__about__.py index 0de2e0c2..5bd32610 100644 --- a/src/zimscraperlib/__about__.py +++ b/src/zimscraperlib/__about__.py @@ -1 +1 @@ -__version__ = "4.0.1-dev0" +__version__ = "5.0.0-dev0" diff --git a/src/zimscraperlib/zim/filesystem.py b/src/zimscraperlib/zim/filesystem.py index ecd677d0..bdf0125a 100644 --- a/src/zimscraperlib/zim/filesystem.py +++ b/src/zimscraperlib/zim/filesystem.py @@ -219,33 +219,33 @@ def make_zim_file( zim_file.finish() -class IncorrectZIMPathError(Exception): - """A generic exception for any problem encountered in validate_zimfile_creatable""" +class IncorrectPathError(Exception): + """A generic exception for any problem encountered while working with filepaths""" pass -class MissingZIMFolderError(IncorrectZIMPathError): - """Exception raised in validate_zimfile_creatable when folder does not exists""" +class MissingFolderError(IncorrectPathError): + """Exception raised when folder does not exist""" pass -class NotADirectoryZIMFolderError(IncorrectZIMPathError): - """Exception raised in validate_zimfile_creatable when folder is not a directory""" +class NotADirectoryFolderError(IncorrectPathError): + """Exception raised when folder is not a directory""" pass -class NotWritableZIMFolderError(IncorrectZIMPathError): - """Exception raised in validate_zimfile_creatable when folder is not writable""" +class NotWritableFolderError(IncorrectPathError): + """Exception raised when folder is not writable""" pass -class IncorrectZIMFilenameError(IncorrectZIMPathError): +class IncorrectFilenameError(IncorrectPathError): """ - Exception raised in validate_zimfile_creatable when filename is not creatable + Exception raised when filename is not creatable This usually occurs when bad characters are present in filename (typically characters not supported on current filesystem). @@ -254,32 +254,24 @@ class IncorrectZIMFilenameError(IncorrectZIMPathError): pass -def validate_zimfile_creatable(folder: str | pathlib.Path, filename: str): - """Validate that a ZIM can be created in given folder with given filename +def validate_folder_writable(folder: pathlib.Path): + """Validate that a file can be created in a given folder. - Any problem encountered raises an exception inheriting from IncorrectZIMPathError + Any problem encountered raises an exception inheriting from IncorrectPathError Checks that: - - folder passed exists (or raise MissingZIMFolderError exception) - - folder passed is a directory (or raise NotADirectoryZIMFolderError exception) + - folder passed exists (or raise MissingFolderError exception) + - folder passed is a directory (or raise NotADirectoryFolderError exception) - folder is writable, i.e. it is possible to create a file in folder (or raise - NotWritableZIMFolderError exception with inner exception details) - - filename is creatable, i.e. there is no bad characters in filename (or raise - IncorrectZIMFilenameError exception with inner exception details) + NotWritableFolderError exception with inner exception details) """ - folder = pathlib.Path(folder) - # ensure folder exists if not folder.exists(): - raise MissingZIMFolderError( - f"Folder to create the ZIM does not exist: {folder}" - ) + raise MissingFolderError(f"Folder does not exist: {folder}") # ensure folder is a directory if not folder.is_dir(): - raise NotADirectoryZIMFolderError( - f"Folder to create the ZIM is not a directory: {folder}" - ) + raise NotADirectoryFolderError(f"Folder is not a directory: {folder}") logger.debug(f"Attempting to confirm output is writable in directory {folder}") @@ -288,17 +280,28 @@ def validate_zimfile_creatable(folder: str | pathlib.Path, filename: str): with tempfile.NamedTemporaryFile(dir=folder, delete=True) as fh: logger.debug(f"Output is writable. Temporary file used for test: {fh.name}") except Exception as exc: - raise NotWritableZIMFolderError( - f"Folder to create the ZIM is not writable: {folder}" - ) from exc + raise NotWritableFolderError(f"Folder is not writable: {folder}") from exc + + +def validate_file_creatable(folder: str | pathlib.Path, filename: str): + """Validate that a file can be created in given folder with given filename + + Any problem encountered raises an exception inheriting from IncorrectPathError + + Checks that: + - folder is writable (or raise exception from `validate_folder_writable`) + - file can be created (or raise IncorrectFilenameError exception with + inner exception details) + """ + folder = pathlib.Path(folder) + + validate_folder_writable(folder) - # ensure ZIM file is creatable with the given name + # ensure file is creatable with the given name fpath = folder / filename try: - logger.debug(f"Confirming ZIM file can be created at {fpath}") + logger.debug(f"Confirming file can be created at {fpath}") fpath.touch() fpath.unlink() except Exception as exc: - raise IncorrectZIMFilenameError( - f"ZIM filename is not creatable: {fpath}" - ) from exc + raise IncorrectFilenameError(f"File is not creatable: {fpath}") from exc diff --git a/tests/zim/test_fs.py b/tests/zim/test_fs.py index 41ad1367..3eec120c 100644 --- a/tests/zim/test_fs.py +++ b/tests/zim/test_fs.py @@ -11,12 +11,13 @@ from zimscraperlib.zim.archive import Archive from zimscraperlib.zim.filesystem import ( FileItem, - IncorrectZIMFilenameError, - MissingZIMFolderError, - NotADirectoryZIMFolderError, - NotWritableZIMFolderError, + IncorrectFilenameError, + MissingFolderError, + NotADirectoryFolderError, + NotWritableFolderError, make_zim_file, - validate_zimfile_creatable, + validate_file_creatable, + validate_folder_writable, ) @@ -171,32 +172,36 @@ def valid_zim_filename(): return "myfile.zim" -def test_validate_zimfile_creatable_ok(tmp_path, valid_zim_filename): +def test_validate_folder_writable_not_exists(tmp_path): - validate_zimfile_creatable(tmp_path, valid_zim_filename) + with pytest.raises(MissingFolderError): + validate_folder_writable(tmp_path / "foo") -def test_validate_zimfile_creatable_folder_not_exists(tmp_path, valid_zim_filename): +def test_validate_folder_writable_not_dir(tmp_path): - with pytest.raises(MissingZIMFolderError): - validate_zimfile_creatable(tmp_path / "foo", valid_zim_filename) + with pytest.raises(NotADirectoryFolderError): + (tmp_path / "foo.txt").touch() + validate_folder_writable(tmp_path / "foo.txt") -def test_validate_zimfile_creatable_bad_folder(tmp_path, valid_zim_filename): +def test_validate_folder_writable_not_writable(tmp_path): - with pytest.raises(NotADirectoryZIMFolderError): - (tmp_path / "foo.txt").touch() - validate_zimfile_creatable(tmp_path / "foo.txt", valid_zim_filename) + with pytest.raises(NotWritableFolderError): + (tmp_path / "foo").mkdir(mode=111) + validate_folder_writable(tmp_path / "foo") -def test_validate_zimfile_creatable_folder_not_writable(tmp_path, valid_zim_filename): +def test_validate_folder_writable_ok(tmp_path): + validate_folder_writable(tmp_path) - with pytest.raises(NotWritableZIMFolderError): - (tmp_path / "foo").mkdir(mode=111) - validate_zimfile_creatable(tmp_path / "foo", valid_zim_filename) + +def test_validate_file_creatable_ok(tmp_path, valid_zim_filename): + + validate_file_creatable(tmp_path, valid_zim_filename) -def test_validate_zimfile_creatable_bad_name(tmp_path): +def test_validate_file_creatable_bad_name(tmp_path): - with pytest.raises(IncorrectZIMFilenameError): - validate_zimfile_creatable(tmp_path, "t\0t\0.zim") + with pytest.raises(IncorrectFilenameError): + validate_file_creatable(tmp_path, "t\0t\0.zim")