Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ repos:
- id: trailing-whitespace
- id: end-of-file-fixer
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.14
rev: v0.16.0
hooks:
- id: ruff-check
- id: ruff-format
- repo: https://github.com/RobertCraigie/pyright-python
rev: v1.1.409
rev: v1.1.411
hooks:
- id: pyright
name: pyright (system)
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Do not URL-encode `=` and `,` characters when rewriting document URIs, fixing YouTube JS player breakage (#316)

### Changed

- Upgrade Python and Javascript dependencies (#319)

## [5.4.0] - 2026-05-28

### Added
Expand Down
11 changes: 4 additions & 7 deletions javascript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
"license": "GPL-3.0-or-later",
"author": "openZIM",
"devDependencies": {
"@rollup/plugin-commonjs": "29.0.2",
"@rollup/plugin-commonjs": "29.0.3",
"@rollup/plugin-node-resolve": "16.0.3",
"@rollup/plugin-strip": "3.0.4",
"@rollup/plugin-terser": "1.0.0",
"ava": "8.0.1",
"babel-eslint": "10.1.0",
"eslint": "10.4.0",
"eslint": "10.7.0",
"eslint-config-prettier": "10.1.8",
"prettier": "3.8.3",
"rollup": "4.60.4",
"prettier": "3.9.6",
"rollup": "4.62.2",
"rollup-plugin-version-injector": "1.3.3"
},
"scripts": {
Expand All @@ -35,9 +35,6 @@
"serial": true,
"files": [
"test/*.js"
],
"sources": [
"src/**/*"
]
},
"dependencies": {
Expand Down
851 changes: 423 additions & 428 deletions javascript/yarn.lock

Large diffs are not rendered by default.

26 changes: 11 additions & 15 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ dependencies = [
"libzim>=3.8.0,<4.0",
"beautifulsoup4>=4.9.3,<5.0",
"lxml>=4.6.3,<7.0",
"optimize-images>=1.3.6,<2.0",
"optimize-images>=1.3.6,<3.0",
# regex has no upper-bound due to "date-based" release numbers, no semver, so their
# promise is that they will never (or always) break the API, and the API is very
# limited and we use only a very small subset of it.
Expand Down Expand Up @@ -58,28 +58,28 @@ scripts = [

]
qa = [
"ruff==0.15.14",
"pyright==1.1.409",
"pytest==9.0.3",
"ruff==0.16.0",
"pyright==1.1.411",
"pytest==9.1.1",
]
test = [
"pytest==9.0.3",
"pytest==9.1.1",
"pytest-mock==3.15.1",
"coverage==7.14.1",
"coverage==7.15.2",
]
docs = [
"ruff==0.15.14",
"ruff==0.16.0",
"mkdocs==1.6.1",
"mkdocs-include-markdown-plugin==7.3.0",
"mkdocs-material==9.7.6",
"mkdocstrings[python]==1.0.3",
"pymdown-extensions==10.21.3",
"mkdocs-material==9.7.7",
"mkdocstrings[python]==1.0.6",
"pymdown-extensions==11.0.1",
"mkdocs-gen-files==0.6.1",
"mkdocs-literate-nav==0.6.3",
"pygments==2.19.2", # See https://github.com/pygments/pygments/issues/3076
]
dev = [
"ipython==9.13.0",
"ipython==9.15.0",
"pre-commit==4.6.0",
"zimscraperlib[scripts]",
"zimscraperlib[qa]",
Expand Down Expand Up @@ -147,10 +147,6 @@ features = ["scripts", "docs"]
serve = "inv docs-serve --args '{args}'"
build = "inv docs-build --args '{args}'"

[tool.black]
line-length = 88
target-version = ['py314']

[tool.ruff]
target-version = "py314"
line-length = 88
Expand Down
2 changes: 1 addition & 1 deletion src/zimscraperlib/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def get_session(max_retries: int | None = 5) -> requests.Session:
return session


def stream_file(
def stream_file( # noqa: PLR0917
url: str,
fpath: pathlib.Path | None = None,
byte_stream: SupportsWrite[bytes] | SupportsSeekableWrite[bytes] | None = None,
Expand Down
2 changes: 1 addition & 1 deletion src/zimscraperlib/i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def _get_names_from(self, query: str) -> tuple[str, str]:
default = self.english or query
return default, default

def todict(self) -> dict[str, str | None | list[str]]:
def todict(self) -> dict[str, str | list[str] | None]:
return {
key.replace("_", "-") if key.startswith("iso") else key: getattr(
self, key, None
Expand Down
3 changes: 2 additions & 1 deletion src/zimscraperlib/image/illustration.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ def get_zim_illustration(
width: target illustration width
height: target illustration height
resize_method: method to resize the image ; in general only 'contain' or
'cover' make sense, but 'crop', 'width', 'height' and 'thumbnail' can be used
'cover' make sense, but 'crop', 'width', 'height' and 'thumbnail' can be
used
"""

illustration_path = handle_user_provided_file(illustration_location)
Expand Down
2 changes: 1 addition & 1 deletion src/zimscraperlib/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
VERBOSE_DEPENDENCIES = ["urllib3", "PIL", "boto3", "botocore", "s3transfer"]


def getLogger( # noqa: N802 (intentionally matches the stdlib getLogger name)
def getLogger( # noqa: PLR0917, N802 (intentionally matches the stdlib getLogger name)
name: str,
level: int = logging.INFO,
console: TextIO | io.StringIO | None = sys.stdout,
Expand Down
12 changes: 7 additions & 5 deletions src/zimscraperlib/rewriting/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ def do_drop_attribute(
for rule in self.drop_attribute_rules
)

def do_attribute_rewrite(
def do_attribute_rewrite( # noqa: PLR0917
self,
tag: str,
attr_name: str,
Expand Down Expand Up @@ -594,7 +594,7 @@ def rewrite_style_tags(


@rules.rewrite_attribute()
def rewrite_href_src_attributes(
def rewrite_href_src_attributes( # noqa: PLR0917
tag: str,
attr_name: str,
attr_value: str | None,
Expand Down Expand Up @@ -709,7 +709,7 @@ def rewrite_js_data(


@rules.rewrite_attribute()
def rewrite_meta_http_equiv_redirect(
def rewrite_meta_http_equiv_redirect( # noqa: PLR0917
tag: str,
attr_name: str,
attr_value: str | None,
Expand All @@ -731,6 +731,8 @@ def rewrite_meta_http_equiv_redirect(
return
return (
attr_name,
f"{match['interval']};"
f"url={url_rewriter(match['url'], base_href=base_href).rewriten_url}",
(
f"{match['interval']};"
f"url={url_rewriter(match['url'], base_href=base_href).rewriten_url}"
),
)
2 changes: 1 addition & 1 deletion src/zimscraperlib/uri.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from zimscraperlib.misc import first


def rebuild_uri(
def rebuild_uri( # noqa: PLR0917
uri: urllib.parse.ParseResult,
scheme: str | None = None,
username: str | None = None,
Expand Down
2 changes: 1 addition & 1 deletion src/zimscraperlib/zim/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class StaticItem(Item):
If item title is not set by caller, it is automatically populated from index_data.
"""

def __init__(
def __init__( # noqa: PLR0917
self,
content: str | bytes | None = None,
fileobj: io.IOBase | None = None,
Expand Down
28 changes: 18 additions & 10 deletions tests/html/test_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,25 +69,33 @@ def test_find_language(tmp_path: pathlib.Path, html_page: str):
"html_string, expected_language",
[
(
"<html lang='en-US' xml:lang='zh-CN'><head>"
"<meta http-equiv='content-language' content='en-UK'></head>"
"<body lang='hi-IN'></body></html>",
(
"<html lang='en-US' xml:lang='zh-CN'><head>"
"<meta http-equiv='content-language' content='en-UK'></head>"
"<body lang='hi-IN'></body></html>"
),
"en-US",
),
(
"<html xml:lang='en-US'><head>"
"<meta http-equiv='content-language' content='en-UK'></head>"
"<body lang='hi-IN'></body></html>",
(
"<html xml:lang='en-US'><head>"
"<meta http-equiv='content-language' content='en-UK'></head>"
"<body lang='hi-IN'></body></html>"
),
"en-US",
),
(
"<html><head><meta http-equiv='content-language' content='en-UK'>"
"</head><body lang='hi-IN'></body></html>",
(
"<html><head><meta http-equiv='content-language' content='en-UK'>"
"</head><body lang='hi-IN'></body></html>"
),
"hi-IN",
),
(
"<html><head><meta http-equiv='content-language' content='en-UK'>"
"</head><body></body></html>",
(
"<html><head><meta http-equiv='content-language' content='en-UK'>"
"</head><body></body></html>"
),
"en-UK",
),
],
Expand Down
31 changes: 19 additions & 12 deletions tests/i18n/test_i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ def test_lang_details_inequality_objects():
),
],
)
def test_lang_to_dict(query: str, expected: dict[str, str | None | list[str]]):
def test_lang_to_dict(query: str, expected: dict[str, str | list[str] | None]):
assert Language(query).todict() == expected


Expand All @@ -340,23 +340,30 @@ def test_lang_to_dict(query: str, expected: dict[str, str | None | list[str]]):
[
(
"fra",
'Language(iso_639_1="fr", iso_639_2b="fre", iso_639_2t="fra", '
'iso_639_3="fra", iso_639_5="None", english="French (France)", '
"iso_types=\"['part2t', 'part3']\", native=\"français (France)\", "
'querytype="purecode", query="fra")',
(
'Language(iso_639_1="fr", iso_639_2b="fre", iso_639_2t="fra", '
'iso_639_3="fra", iso_639_5="None", english="French (France)", '
"iso_types=\"['part2t', 'part3']\", native=\"français (France)\", "
'querytype="purecode", query="fra")'
),
),
(
"zh",
'Language(iso_639_1="zh", iso_639_2b="chi", iso_639_2t="zho", '
'iso_639_3="zho", iso_639_5="None", english="Chinese", '
'iso_types="[\'part1\']", native="中文", querytype="purecode", query="zh")',
(
'Language(iso_639_1="zh", iso_639_2b="chi", iso_639_2t="zho", '
'iso_639_3="zho", iso_639_5="None", english="Chinese", '
'iso_types="[\'part1\']", native="中文", querytype="purecode", '
'query="zh")'
),
),
(
"ar",
'Language(iso_639_1="ar", iso_639_2b="ara", iso_639_2t="ara", '
'iso_639_3="ara", iso_639_5="None", english="Arabic", '
'iso_types="[\'part1\']", native="العربية", querytype="purecode", '
'query="ar")',
(
'Language(iso_639_1="ar", iso_639_2b="ara", iso_639_2t="ara", '
'iso_639_3="ara", iso_639_5="None", english="Arabic", '
'iso_types="[\'part1\']", native="العربية", querytype="purecode", '
'query="ar")'
),
),
],
)
Expand Down
15 changes: 12 additions & 3 deletions tests/image/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def get_image_size(fpath: pathlib.Path | io.BytesIO) -> tuple[int, int]:
return Image.open(fpath).size


def get_src_dst(
def get_src_dst( # noqa: PLR0917
tmp_path: pathlib.Path,
fmt: str,
png_image: pathlib.Path | None = None,
Expand Down Expand Up @@ -142,6 +142,7 @@ def test_colors_jpg_palette(jpg_image: pathlib.Path):
],
)
def test_save_image(
*,
png_image: pathlib.Path,
jpg_image: pathlib.Path,
tmp_path: pathlib.Path,
Expand Down Expand Up @@ -313,6 +314,7 @@ def test_resize_small_image_error(
[("png", "JPEG", "RGB"), ("png", "BMP", None), ("jpg", "JPEG", "CMYK")],
)
def test_change_image_format(
*,
png_image: pathlib.Path,
jpg_image: pathlib.Path,
tmp_path: pathlib.Path,
Expand Down Expand Up @@ -475,6 +477,7 @@ def test_wrong_extension(
@pytest.mark.parametrize("src_type", ["path", "bytesio", "bytes"])
@pytest.mark.parametrize("dst_type", ["path", "bytesio"])
def test_optimize_image_default_generic(
*,
png_image2: pathlib.Path,
jpg_image: pathlib.Path,
gif_image: pathlib.Path,
Expand Down Expand Up @@ -518,6 +521,7 @@ def test_optimize_image_default_generic(
@pytest.mark.parametrize("src_type", ["path", "bytesio"])
@pytest.mark.parametrize("dst_type", ["path", "bytesio"])
def test_optimize_image_default_direct(
*,
png_image2: pathlib.Path,
jpg_image: pathlib.Path,
gif_image: pathlib.Path,
Expand Down Expand Up @@ -879,8 +883,8 @@ def test_image_preset_jpg(

def test_image_preset_has_mime_and_ext():
for _, preset in ALL_PRESETS:
assert preset().ext
assert preset().mimetype.startswith("image/")
assert preset().ext # pyright: ignore
assert preset().mimetype.startswith("image/") # pyright: ignore


def test_remove_png_transparency(png_image: pathlib.Path, tmp_path: pathlib.Path):
Expand Down Expand Up @@ -931,6 +935,7 @@ def test_dynamic_jpeg_quality(jpg_image: pathlib.Path, tmp_path: pathlib.Path):
[("png", "PNG"), ("jpg", "JPEG"), ("gif", "GIF"), ("webp", "WEBP"), ("svg", "SVG")],
)
def test_ensure_matches_ok(
*,
png_image: pathlib.Path,
jpg_image: pathlib.Path,
gif_image: pathlib.Path,
Expand Down Expand Up @@ -962,6 +967,7 @@ def test_ensure_matches_ko(webp_image: pathlib.Path):
[("png", "PNG"), ("jpg", "JPEG"), ("gif", "GIF"), ("webp", "WEBP"), ("svg", "SVG")],
)
def test_format_for_real_images_suffix(
*,
png_image: pathlib.Path,
jpg_image: pathlib.Path,
gif_image: pathlib.Path,
Expand All @@ -988,6 +994,7 @@ def test_format_for_real_images_suffix(
[("png", "PNG"), ("jpg", "JPEG"), ("gif", "GIF"), ("webp", "WEBP"), ("svg", "SVG")],
)
def test_format_for_real_images_content_path(
*,
png_image: pathlib.Path,
jpg_image: pathlib.Path,
gif_image: pathlib.Path,
Expand All @@ -1014,6 +1021,7 @@ def test_format_for_real_images_content_path(
[("png", "PNG"), ("jpg", "JPEG"), ("gif", "GIF"), ("webp", "WEBP"), ("svg", "SVG")],
)
def test_format_for_real_images_content_bytes(
*,
png_image: pathlib.Path,
jpg_image: pathlib.Path,
gif_image: pathlib.Path,
Expand Down Expand Up @@ -1200,6 +1208,7 @@ def test_optimize_gif_gifsicle_failure_bytesio(
],
)
def test_optimize_any_image(
*,
png_image: pathlib.Path,
jpg_image: pathlib.Path,
gif_image: pathlib.Path,
Expand Down
Loading