Skip to content

Commit 05984c3

Browse files
authored
Merge pull request #169 from openzim/check_zim_filename
Add utility to validate ZIM folder and filename are OK
2 parents baec9c1 + f4176f0 commit 05984c3

File tree

2 files changed

+129
-1
lines changed

2 files changed

+129
-1
lines changed

src/zimscraperlib/zim/filesystem.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import datetime
3232
import pathlib
3333
import re
34+
import tempfile
3435
from collections.abc import Sequence
3536

3637
from zimscraperlib import logger
@@ -210,3 +211,86 @@ def make_zim_file(
210211
raise
211212
finally:
212213
zim_file.finish()
214+
215+
216+
class BadZimfileDataError(Exception):
217+
"""A generic exception for any problem encountered in validate_zimfile_creatable"""
218+
219+
pass
220+
221+
222+
class ZimFolderNotFoundError(BadZimfileDataError):
223+
"""Exception raised in validate_zimfile_creatable when folder does not exists"""
224+
225+
pass
226+
227+
228+
class BadZimFolderError(BadZimfileDataError):
229+
"""Exception raised in validate_zimfile_creatable when folder is not a directory"""
230+
231+
pass
232+
233+
234+
class ZimFolderNotWritableError(BadZimfileDataError):
235+
"""Exception raised in validate_zimfile_creatable when folder is not writable"""
236+
237+
pass
238+
239+
240+
class BadZimFilenameError(BadZimfileDataError):
241+
"""
242+
Exception raised in validate_zimfile_creatable when filename is not creatable
243+
244+
This usually occurs when bad characters are present in filename (typically
245+
characters not supported on current filesystem).
246+
"""
247+
248+
pass
249+
250+
251+
def validate_zimfile_creatable(folder: str | pathlib.Path, filename: str):
252+
"""Validate that a ZIM can be created in given folder with given filename
253+
254+
Any problem encountered raises an exception inheriting from BadZimfileDataError
255+
256+
Checks that:
257+
- folder passed exists (or raise ZimFolderNotFoundError exception)
258+
- folder passed is a directory (or raise BadZimFolderError exception)
259+
- folder is writable, i.e. it is possible to create a file in folder (or raise
260+
ZimFolderNotWritableError exception with inner exception details)
261+
- filename is creatable, i.e. there is no bad characters in filename (or raise
262+
BadZimFilenameError exception with inner exception details)
263+
"""
264+
folder = pathlib.Path(folder)
265+
266+
# ensure folder exists
267+
if not folder.exists():
268+
raise ZimFolderNotFoundError(
269+
f"Folder to create the ZIM does not exist: {folder}"
270+
)
271+
272+
# ensure folder is a directory
273+
if not folder.is_dir():
274+
raise BadZimFolderError(
275+
f"Folder to create the ZIM is not a directory: {folder}"
276+
)
277+
278+
logger.debug(f"Attempting to confirm output is writable in directory {folder}")
279+
280+
try:
281+
# ensure folder is writable
282+
with tempfile.NamedTemporaryFile(dir=folder, delete=True) as fh:
283+
logger.debug(f"Output is writable. Temporary file used for test: {fh.name}")
284+
except Exception as e:
285+
raise ZimFolderNotWritableError(
286+
f"Folder to create the ZIM is not writable: {folder}"
287+
) from e
288+
289+
# ensure ZIM file is creatable with the given name
290+
file_path = folder / filename
291+
try:
292+
logger.debug(f"Confirming ZIM file can be created at {file_path}")
293+
file_path.touch()
294+
file_path.unlink()
295+
except Exception as e:
296+
raise BadZimFilenameError(f"ZIM filename is not creatable: {file_path}") from e

tests/zim/test_fs.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,15 @@
88
import pytest
99

1010
from zimscraperlib.zim.archive import Archive
11-
from zimscraperlib.zim.filesystem import FileItem, make_zim_file
11+
from zimscraperlib.zim.filesystem import (
12+
BadZimFilenameError,
13+
BadZimFolderError,
14+
FileItem,
15+
ZimFolderNotFoundError,
16+
ZimFolderNotWritableError,
17+
make_zim_file,
18+
validate_zimfile_creatable,
19+
)
1220

1321

1422
def test_fileitem(tmp_path, png_image):
@@ -143,3 +151,39 @@ def test_make_zim_file_no_file_on_error(tmp_path, png_image, build_data):
143151
# create a ZIM file, so SEGFAULT on exit it (somewhat) OK
144152
assert py.returncode in (0, 11, -6, -11) # SIGSEV is 11
145153
assert not build_data["fpath"].exists()
154+
155+
156+
@pytest.fixture(scope="module")
157+
def valid_zim_filename():
158+
return "myfile.zim"
159+
160+
161+
def test_validate_zimfile_creatable_ok(tmp_path, valid_zim_filename):
162+
163+
validate_zimfile_creatable(tmp_path, valid_zim_filename)
164+
165+
166+
def test_validate_zimfile_creatable_folder_not_exists(tmp_path, valid_zim_filename):
167+
168+
with pytest.raises(ZimFolderNotFoundError):
169+
validate_zimfile_creatable(tmp_path / "foo", valid_zim_filename)
170+
171+
172+
def test_validate_zimfile_creatable_bad_folder(tmp_path, valid_zim_filename):
173+
174+
with pytest.raises(BadZimFolderError):
175+
(tmp_path / "foo.txt").touch()
176+
validate_zimfile_creatable(tmp_path / "foo.txt", valid_zim_filename)
177+
178+
179+
def test_validate_zimfile_creatable_folder_not_writable(tmp_path, valid_zim_filename):
180+
181+
with pytest.raises(ZimFolderNotWritableError):
182+
(tmp_path / "foo").mkdir(mode=111)
183+
validate_zimfile_creatable(tmp_path / "foo", valid_zim_filename)
184+
185+
186+
def test_validate_zimfile_creatable_bad_name(tmp_path):
187+
188+
with pytest.raises(BadZimFilenameError):
189+
validate_zimfile_creatable(tmp_path, "t\0t\0.zim")

0 commit comments

Comments
 (0)