Skip to content

Commit 5558962

Browse files
authored
Merge pull request #132 from openzim/fix_type_hints
Fix type hints
2 parents 5d95d59 + 824504f commit 5558962

File tree

7 files changed

+112
-39
lines changed

7 files changed

+112
-39
lines changed

src/zimscraperlib/image/convertion.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
#!/usr/bin/env python3
22
# vim: ai ts=4 sts=4 et sw=4 nu
33

4+
import io
45
import pathlib
5-
from typing import Optional
6+
from typing import Union
67

78
import PIL
89

@@ -13,7 +14,9 @@
1314

1415

1516
def convert_image(
16-
src: pathlib.Path, dst: pathlib.Path, **params: Optional[dict]
17+
src: Union[pathlib.Path, io.BytesIO],
18+
dst: Union[pathlib.Path, io.BytesIO],
19+
**params: str,
1720
) -> None:
1821
"""convert an image file from one format to another
1922
params: Image.save() parameters. Depends on dest format.

src/zimscraperlib/image/transformation.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def resize_image(
1919
dst: Optional[Union[pathlib.Path, io.BytesIO]] = None,
2020
method: Optional[str] = "width",
2121
allow_upscaling: Optional[bool] = True, # noqa: FBT002
22-
**params: Optional[dict],
22+
**params: str,
2323
) -> None:
2424
"""resize an image to requested dimensions
2525

src/zimscraperlib/image/utils.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
#!/usr/bin/env python
22
# vim: ai ts=4 sts=4 et sw=4 nu
33

4+
import io
45
import pathlib
5-
from typing import Optional
6+
from typing import Optional, Union
67

78
from PIL import Image
89

910

1011
def save_image(
1112
src: Image, # pyright: ignore
12-
dst: pathlib.Path,
13+
dst: Union[pathlib.Path, io.BytesIO],
1314
fmt: Optional[str] = None,
14-
**params: Optional[dict],
15+
**params: str,
1516
) -> None:
1617
"""PIL.Image.save() wrapper setting default parameters"""
1718
args = {"JPEG": {"quality": 100}, "PNG": {}}.get(fmt, {}) # pyright: ignore

src/zimscraperlib/zim/filesystem.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@ def __init__(
4646
self,
4747
root: pathlib.Path,
4848
filepath: pathlib.Path,
49-
): # pyright: ignore
50-
super().__init__(root=root, filepath=filepath) # pyright: ignore
49+
):
50+
super().__init__()
51+
self.root = root
52+
self.filepath = filepath
5153
# first look inside the file's magic headers
5254
self.mimetype = get_file_mimetype(self.filepath)
5355
# most web-specific files are plain text. In this case, use extension

src/zimscraperlib/zim/items.py

+64-14
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import re
1010
import tempfile
1111
import urllib.parse
12-
from typing import Dict, Union
12+
from typing import Any, Optional
1313

1414
import libzim.writer # pyright: ignore
1515

@@ -23,12 +23,25 @@
2323

2424

2525
class Item(libzim.writer.Item):
26-
"""libzim.writer.Item returning props for path/title/mimetype plus a callback
27-
28-
Calls your `callback` prop on deletion"""
29-
30-
def __init__(self, **kwargs: Dict[str, Union[str, bool, bytes]]):
26+
"""libzim.writer.Item returning props for path/title/mimetype"""
27+
28+
def __init__(
29+
self,
30+
path: Optional[str] = None,
31+
title: Optional[str] = None,
32+
mimetype: Optional[str] = None,
33+
hints: Optional[dict] = None,
34+
**kwargs: Any,
35+
):
3136
super().__init__()
37+
if path:
38+
kwargs["path"] = path
39+
if title:
40+
kwargs["title"] = title
41+
if mimetype:
42+
kwargs["mimetype"] = mimetype
43+
if hints:
44+
kwargs["hints"] = hints
3245
for k, v in kwargs.items():
3346
setattr(self, k, v)
3447

@@ -57,21 +70,45 @@ class StaticItem(Item):
5770
more efficiently: now when the libzim destroys the CP, python will destroy
5871
the Item and we can be notified that we're effectively through with our content"""
5972

73+
def __init__(
74+
self,
75+
content: Optional[str] = None,
76+
fileobj: Optional[io.IOBase] = None,
77+
filepath: Optional[pathlib.Path] = None,
78+
path: Optional[str] = None,
79+
title: Optional[str] = None,
80+
mimetype: Optional[str] = None,
81+
hints: Optional[dict] = None,
82+
**kwargs: Any,
83+
):
84+
if content:
85+
kwargs["content"] = content
86+
if fileobj:
87+
kwargs["fileobj"] = fileobj
88+
if filepath:
89+
kwargs["filepath"] = filepath
90+
super().__init__(
91+
path=path, title=title, mimetype=mimetype, hints=hints, **kwargs
92+
)
93+
6094
def get_contentprovider(self) -> libzim.writer.ContentProvider:
6195
# content was set manually
62-
if getattr(self, "content", None) is not None:
63-
return StringProvider(content=self.content, ref=self)
96+
content = getattr(self, "content", None)
97+
if content is not None:
98+
return StringProvider(content=content, ref=self)
6499

65100
# using a file-like object
66-
if getattr(self, "fileobj", None):
101+
fileobj = getattr(self, "fileobj", None)
102+
if fileobj:
67103
return FileLikeProvider(
68-
fileobj=self.fileobj, ref=self, size=getattr(self, "size", None)
104+
fileobj=fileobj, ref=self, size=getattr(self, "size", None)
69105
)
70106

71107
# we had to download locally to get size
72-
if getattr(self, "filepath", None):
108+
filepath = getattr(self, "filepath", None)
109+
if filepath:
73110
return FileProvider(
74-
filepath=self.filepath, ref=self, size=getattr(self, "size", None)
111+
filepath=filepath, ref=self, size=getattr(self, "size", None)
75112
)
76113

77114
raise NotImplementedError("No data to provide`")
@@ -106,8 +143,21 @@ def download_for_size(url, on_disk, tmp_dir=None):
106143
size, _ = stream_file(url.geturl(), fpath=fpath, byte_stream=stream)
107144
return fpath or stream, size
108145

109-
def __init__(self, url: str, **kwargs):
110-
super().__init__(**kwargs)
146+
def __init__(
147+
self,
148+
url: str,
149+
path: Optional[str] = None,
150+
title: Optional[str] = None,
151+
mimetype: Optional[str] = None,
152+
hints: Optional[dict] = None,
153+
use_disk: Optional[bool] = None,
154+
**kwargs: Any,
155+
):
156+
if use_disk:
157+
kwargs["use_disk"] = use_disk
158+
super().__init__(
159+
path=path, title=title, mimetype=mimetype, hints=hints, **kwargs
160+
)
111161
self.url = urllib.parse.urlparse(url)
112162
use_disk = getattr(self, "use_disk", False)
113163

tests/image/test_image.py

+24
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,30 @@ def test_change_image_format_defaults(png_image, tmp_path):
296296
assert dst_image.format == "WEBP"
297297

298298

299+
def test_convert_io_src_dst(png_image: pathlib.Path):
300+
src = io.BytesIO(png_image.read_bytes())
301+
dst = io.BytesIO()
302+
convert_image(src, dst, fmt="PNG")
303+
dst_image = Image.open(dst)
304+
assert dst_image.format == "PNG"
305+
306+
307+
def test_convert_io_src_path_dst(png_image: pathlib.Path, tmp_path: pathlib.Path):
308+
src = io.BytesIO(png_image.read_bytes())
309+
dst = tmp_path / "test.png"
310+
convert_image(src, dst, fmt="PNG")
311+
dst_image = Image.open(dst)
312+
assert dst_image.format == "PNG"
313+
314+
315+
def test_convert_path_src_io_dst(png_image: pathlib.Path):
316+
src = png_image
317+
dst = io.BytesIO()
318+
convert_image(src, dst, fmt="PNG")
319+
dst_image = Image.open(dst)
320+
assert dst_image.format == "PNG"
321+
322+
299323
@pytest.mark.parametrize(
300324
"fmt,exp_size",
301325
[("png", 128), ("jpg", 128)],

tests/zim/test_zim_creator.py

+10-17
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import base64
55
import datetime
66
import io
7-
import os
87
import pathlib
98
import random
109
import shutil
@@ -41,6 +40,8 @@ def get_contentprovider(self):
4140

4241
class FileLikeProviderItem(StaticItem):
4342
def get_contentprovider(self):
43+
if not self.fileobj:
44+
raise AttributeError("fileobj cannot be None")
4445
return FileLikeProvider(self.fileobj)
4546

4647

@@ -119,7 +120,7 @@ def test_noindexlanguage(tmp_path):
119120
creator = Creator(fpath, "welcome").config_dev_metadata(Language="bam")
120121
creator.config_indexing(False)
121122
with creator as creator:
122-
creator.add_item(StaticItem(path="welcome", content="hello")) # pyright: ignore
123+
creator.add_item(StaticItem(path="welcome", content="hello"))
123124
creator.add_item_for("index", "Index", content="-", mimetype="text/html")
124125

125126
reader = Archive(fpath)
@@ -165,15 +166,11 @@ def test_add_item_for_delete_fail(tmp_path, png_image):
165166
# copy file to local path
166167
shutil.copyfile(png_image, local_path)
167168

168-
def remove_source(item):
169-
os.remove(item.filepath)
170-
171169
with Creator(fpath, "welcome").config_dev_metadata() as creator:
172170
creator.add_item(
173171
StaticItem(
174-
filepath=local_path, # pyright: ignore
175-
path="index", # pyright: ignore
176-
callback=remove_source, # pyright: ignore
172+
filepath=local_path,
173+
path="index",
177174
),
178175
callback=(delete_callback, local_path),
179176
)
@@ -188,18 +185,18 @@ def test_compression(tmp_path):
188185
with Creator(
189186
tmp_path / "test.zim", "welcome", compression="zstd"
190187
).config_dev_metadata() as creator:
191-
creator.add_item(StaticItem(path="welcome", content="hello")) # pyright: ignore
188+
creator.add_item(StaticItem(path="welcome", content="hello"))
192189

193190
with Creator(
194191
fpath, "welcome", compression=Compression.zstd # pyright: ignore
195192
).config_dev_metadata() as creator:
196-
creator.add_item(StaticItem(path="welcome", content="hello")) # pyright: ignore
193+
creator.add_item(StaticItem(path="welcome", content="hello"))
197194

198195

199196
def test_double_finish(tmp_path):
200197
fpath = tmp_path / "test.zim"
201198
with Creator(fpath, "welcome").config_dev_metadata() as creator:
202-
creator.add_item(StaticItem(path="welcome", content="hello")) # pyright: ignore
199+
creator.add_item(StaticItem(path="welcome", content="hello"))
203200

204201
# ensure we can finish an already finished creator
205202
creator.finish()
@@ -219,11 +216,7 @@ def test_sourcefile_removal(tmp_path, html_file):
219216
# copy html to folder
220217
src_path = pathlib.Path(tmpdir.name, "source.html")
221218
shutil.copyfile(html_file, src_path)
222-
creator.add_item(
223-
StaticItem(
224-
filepath=src_path, path=src_path.name, ref=tmpdir # pyright: ignore
225-
)
226-
)
219+
creator.add_item(StaticItem(filepath=src_path, path=src_path.name, ref=tmpdir))
227220
del tmpdir
228221

229222
assert not src_path.exists()
@@ -241,7 +234,7 @@ def test_sourcefile_removal_std(tmp_path, html_file):
241234
StaticItem(
242235
filepath=paths[-1],
243236
path=paths[-1].name,
244-
mimetype="text/html", # pyright: ignore
237+
mimetype="text/html",
245238
),
246239
callback=(delete_callback, paths[-1]),
247240
)

0 commit comments

Comments
 (0)