Skip to content

Commit

Permalink
Activate pyright strict mode and fix many typing issues
Browse files Browse the repository at this point in the history
  • Loading branch information
benoit74 committed Nov 18, 2024
1 parent 86fb7dc commit 880b6aa
Show file tree
Hide file tree
Showing 62 changed files with 938 additions and 684 deletions.
2 changes: 2 additions & 0 deletions contrib/encode_video.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ def encode_video(src_path: Path, dst_path: Path, preset: str):
ffmpeg_args=preset_cls().to_ffmpeg_args(),
with_process=True,
) # pyright: ignore[reportGeneralTypeIssues] (returned type is variable, depending on `with_process` value)
if not process: # pragma: no branch
raise ValueError("process should have been returned")
if not success:
logger.error(f"conversion failed:\n{process.stdout}")

Expand Down
5 changes: 1 addition & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -264,12 +264,9 @@ include = ["contrib", "src", "tests", "tasks.py"]
exclude = [".env/**", ".venv/**"]
extraPaths = ["src"]
pythonVersion = "3.12"
typeCheckingMode="basic"
typeCheckingMode="strict"
disableBytesTypePromotions = true

[tool.pyright.overrides]
strict = true # Enable strict mode for specific files

[[tool.pyright.overrides.files]]
files = [
"src/zimscraperlib/rewriting**/*.py",
Expand Down
4 changes: 2 additions & 2 deletions rules/generate_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,11 @@
{% endfor %}
]
)
def {{ rule['name'] }}_case(request):
def {{ rule['name'] }}_case(request: pytest.FixtureRequest):
yield request.param
def test_fuzzyrules_{{ rule['name'] }}({{ rule['name'] }}_case):
def test_fuzzyrules_{{ rule['name'] }}({{ rule['name'] }}_case: ContentForTests):
assert (
ArticleUrlRewriter.apply_additional_rules({{ rule['name'] }}_case.input_str)
== {{ rule['name'] }}_case.expected_str
Expand Down
3 changes: 0 additions & 3 deletions src/zimscraperlib/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
#!/usr/bin/env python
# vim: ai ts=4 sts=4 et sw=4 nu

import logging as stdlogging
import os

Expand Down
3 changes: 0 additions & 3 deletions src/zimscraperlib/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
#!/usr/bin/env python3
# vim: ai ts=4 sts=4 et sw=4 nu

import pathlib

from zimscraperlib.__about__ import __version__
Expand Down
30 changes: 15 additions & 15 deletions src/zimscraperlib/download.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
#!/usr/bin/env python3
# vim: ai ts=4 sts=4 et sw=4 nu

from __future__ import annotations

import pathlib
import subprocess
from concurrent.futures import Future, ThreadPoolExecutor
from typing import IO, ClassVar
from typing import IO, Any, ClassVar

import requests
import requests.adapters
import requests.structures
import urllib3.util
import yt_dlp as youtube_dl
import yt_dlp as youtube_dl # pyright: ignore[reportMissingTypeStubs]

from zimscraperlib import logger

Expand All @@ -29,24 +26,24 @@ def __init__(self, threads: int | None = 1) -> None:
def __enter__(self):
return self

def __exit__(self, *args):
def __exit__(self, *_: Any):
self.shutdown()

def shutdown(self) -> None:
"""shuts down the executor, awaiting completion"""
self.executor.shutdown(wait=True)

def _run_youtube_dl(self, url: str, options: dict) -> None:
def _run_youtube_dl(self, url: str, options: dict[str, Any]) -> None:
with youtube_dl.YoutubeDL(options) as ydl:
ydl.download([url])
ydl.download([url]) # pyright: ignore[reportUnknownMemberType]

def download(
self,
url: str,
options: dict | None,
options: dict[str, Any] | None,
*,
wait: bool | None = True,
) -> bool | Future:
) -> bool | Future[Any]:
"""Downloads video using initialized executor.
url: URL or Video ID
Expand All @@ -65,7 +62,7 @@ def download(
raise future.exception() # pyright: ignore


class YoutubeConfig(dict):
class YoutubeConfig(dict[str, str | bool | int | None]):
options: ClassVar[dict[str, str | bool | int | None]] = {}
defaults: ClassVar[dict[str, str | bool | int | None]] = {
"writethumbnail": True,
Expand All @@ -81,7 +78,7 @@ class YoutubeConfig(dict):
"outtmpl": "video.%(ext)s",
}

def __init__(self, **kwargs):
def __init__(self, **kwargs: str | bool | int | None):
super().__init__(self, **type(self).defaults)
self.update(self.options)
self.update(kwargs)
Expand All @@ -91,7 +88,7 @@ def get_options(
cls,
target_dir: pathlib.Path | None = None,
filepath: pathlib.Path | None = None,
**options,
**options: str | bool | int | None,
):
if "outtmpl" not in options:
outtmpl = cls.options.get("outtmpl", cls.defaults["outtmpl"])
Expand Down Expand Up @@ -142,9 +139,10 @@ def save_large_file(url: str, fpath: pathlib.Path) -> None:
)


def _get_retry_adapter(
def get_retry_adapter(
max_retries: int | None = 5,
) -> requests.adapters.BaseAdapter:
"""A requests adapter to automatically retry on known HTTP status that can be"""
retries = urllib3.util.retry.Retry(
total=max_retries, # total number of retries
connect=max_retries, # connection errors
Expand All @@ -168,7 +166,7 @@ def _get_retry_adapter(
def get_session(max_retries: int | None = 5) -> requests.Session:
"""Session to hold cookies and connection pool together"""
session = requests.Session()
session.mount("http", _get_retry_adapter(max_retries)) # tied to http and https
session.mount("http", get_retry_adapter(max_retries)) # tied to http and https
return session


Expand Down Expand Up @@ -218,6 +216,8 @@ def stream_file(
byte_stream is not None
): # pragma: no branch (we use a precise condition to help type checker)
fp = byte_stream
else:
raise ValueError("Either fpath or byte_stream are mandatory to stream_file")

for data in resp.iter_content(block_size):
total_downloaded += len(data)
Expand Down
7 changes: 2 additions & 5 deletions src/zimscraperlib/filesystem.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
#!/usr/bin/env python
# vim: ai ts=4 sts=4 et sw=4 nu

""" Files manipulation tools
Shortcuts to retrieve mime type using magic"""
Expand Down Expand Up @@ -46,7 +43,7 @@ def get_content_mimetype(content: bytes | str) -> str:

def delete_callback(
fpath: str | pathlib.Path,
callback: Callable | None = None,
callback: Callable[..., Any] | None = None,
*callback_args: Any,
):
"""helper deleting passed filepath, optionnaly calling an additional callback"""
Expand All @@ -55,4 +52,4 @@ def delete_callback(

# call the callback if requested
if callback and callable(callback):
callback.__call__(*callback_args)
callback(*callback_args)
4 changes: 0 additions & 4 deletions src/zimscraperlib/fix_ogvjs_dist.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
#!/usr/bin/env python3
# vim: ai ts=4 sts=4 et sw=4 nu


""" quick script to fix videojs-ogvjs so that it triggers on webm mimetype """

from __future__ import annotations
Expand Down
8 changes: 2 additions & 6 deletions src/zimscraperlib/html.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#!/usr/bin/env python
# vim: ai ts=4 sts=4 et sw=4 nu

""" Tools to work with HTML contents """

from __future__ import annotations

import pathlib
Expand Down Expand Up @@ -43,9 +41,7 @@ def find_language_in(content: str | BinaryIO | TextIO, mime_type: str) -> str:
for key in keylist:
node = soup.find(nodename)
if node:
if not isinstance(node, element.Tag) or (
isinstance(node, element.Tag) and not node.has_attr(key)
):
if not isinstance(node, element.Tag) or not node.has_attr(key):
continue
if (
nodename == "meta"
Expand Down
4 changes: 0 additions & 4 deletions src/zimscraperlib/image/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: ai ts=4 sts=4 et sw=4 nu

# flake8: noqa
from .conversion import convert_image
from .optimization import optimize_image
Expand Down
21 changes: 12 additions & 9 deletions src/zimscraperlib/image/conversion.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
#!/usr/bin/env python3
# vim: ai ts=4 sts=4 et sw=4 nu

from __future__ import annotations

import io
import pathlib
from typing import IO
from typing import IO, Any

import cairosvg.svg
import cairosvg.svg # pyright: ignore[reportMissingTypeStubs]
from PIL.Image import open as pilopen

from zimscraperlib.constants import ALPHA_NOT_SUPPORTED
Expand All @@ -31,7 +28,9 @@ def convert_image(
to RGB. ex: RGB, ARGB, CMYK (and other PIL colorspaces)"""

colorspace = params.get("colorspace") # requested colorspace
fmt = params.pop("fmt").upper() if "fmt" in params else None # requested format
fmt = ( # requested format
(params.pop("fmt") or "").upper() if "fmt" in params else None
)
if not fmt:
fmt = format_for(dst)
if not fmt:
Expand All @@ -53,7 +52,7 @@ def convert_svg2png(
Output width and height might be specified if resize is needed.
PNG background is transparent.
"""
kwargs = {}
kwargs: dict[str, Any] = {}
if isinstance(src, pathlib.Path):
src = str(src)
if isinstance(src, str):
Expand All @@ -65,9 +64,13 @@ def convert_svg2png(
if height:
kwargs["output_height"] = height
if isinstance(dst, pathlib.Path):
cairosvg.svg2png(write_to=str(dst), **kwargs)
cairosvg.svg2png( # pyright: ignore[reportUnknownMemberType]
write_to=str(dst), **kwargs
)
else:
result = cairosvg.svg2png(**kwargs)
result = cairosvg.svg2png( # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
**kwargs
)
if not isinstance(result, bytes):
raise Exception(
"Unexpected type returned by cairosvg.svg2png"
Expand Down
Loading

0 comments on commit 880b6aa

Please sign in to comment.