switch to strict typing with pyright
Some checks failed
ci/woodpecker/push/tests Pipeline failed

Signed-off-by: Ivan Schaller <ivan@schaller.sh>
This commit is contained in:
Ivan Schaller 2023-02-18 16:21:03 +01:00
parent ef7a914869
commit 03461b80bf
Signed by: olofvndrhr
GPG key ID: 2A6BE07D99C8C205
13 changed files with 104 additions and 66 deletions

View file

@ -33,6 +33,13 @@ pipeline:
commands: commands:
- just test_mypy - just test_mypy
# check static typing - python
test-pyright:
image: cr.44net.ch/ci-plugins/tests
pull: true
commands:
- just test_pyright
# ruff test - python # ruff test - python
test-ruff: test-ruff:
image: cr.44net.ch/ci-plugins/tests image: cr.44net.ch/ci-plugins/tests

View file

@ -17,3 +17,4 @@ black>=22.1.0
mypy>=0.940 mypy>=0.940
tox>=3.24.5 tox>=3.24.5
ruff>=0.0.247 ruff>=0.0.247
pyright>=1.1.294

View file

@ -82,7 +82,10 @@ test_black:
@python3 -m black --check --diff . @python3 -m black --check --diff .
test_mypy: test_mypy:
@python3 -m mypy --install-types --non-interactive --ignore-missing-imports . @python3 -m mypy --install-types --non-interactive --ignore-missing-imports mangadlp/
test_pyright:
@python3 -m pyright mangadlp/
test_ruff: test_ruff:
@python3 -m ruff --diff . @python3 -m ruff --diff .
@ -118,6 +121,7 @@ lint:
just test_shfmt just test_shfmt
just test_black just test_black
just test_mypy just test_mypy
just test_pyright
just test_ruff just test_ruff
@echo -e "\n\033[0;32m=== ALL DONE ===\033[0m\n" @echo -e "\n\033[0;32m=== ALL DONE ===\033[0m\n"
@ -127,6 +131,7 @@ tests:
just test_shfmt just test_shfmt
just test_black just test_black
just test_mypy just test_mypy
just test_pyright
just test_ruff just test_ruff
just test_pytest just test_pytest
@echo -e "\n\033[0;32m=== ALL DONE ===\033[0m\n" @echo -e "\n\033[0;32m=== ALL DONE ===\033[0m\n"
@ -137,6 +142,7 @@ tests_full:
just test_shfmt just test_shfmt
just test_black just test_black
just test_mypy just test_mypy
just test_pyright
just test_ruff just test_ruff
just test_build just test_build
just test_tox just test_tox

View file

@ -1,5 +1,6 @@
import re import re
from time import sleep from time import sleep
from typing import Any, Dict, List, Union
import requests import requests
from loguru import logger as log from loguru import logger as log
@ -65,10 +66,10 @@ class Mangadex:
log.error("No valid UUID found") log.error("No valid UUID found")
raise exc raise exc
return uuid return uuid # pyright:ignore
# make initial request # make initial request
def get_manga_data(self) -> dict: def get_manga_data(self) -> Dict[str, Any]:
log.debug(f"Getting manga data for: {self.manga_uuid}") log.debug(f"Getting manga data for: {self.manga_uuid}")
counter = 1 counter = 1
while counter <= 3: while counter <= 3:
@ -85,12 +86,14 @@ class Mangadex:
counter += 1 counter += 1
else: else:
break break
response_body: Dict[str, Dict[str, Any]] = response.json() # pyright:ignore
# check if manga exists # check if manga exists
if response.json()["result"] != "ok": if response_body["result"] != "ok": # type:ignore
log.error("Manga not found") log.error("Manga not found")
raise KeyError raise KeyError
return response.json()["data"] return response_body["data"]
# get the title of the manga (and fix the filename) # get the title of the manga (and fix the filename)
def get_manga_title(self) -> str: def get_manga_title(self) -> str:
@ -112,7 +115,7 @@ class Mangadex:
if item.get(self.language): if item.get(self.language):
alt_title = item alt_title = item
break break
title = alt_title[self.language] title = alt_title[self.language] # pyright:ignore
except (KeyError, UnboundLocalError): except (KeyError, UnboundLocalError):
log.warning( log.warning(
"Manga title also not found in alt titles. Falling back to english title" "Manga title also not found in alt titles. Falling back to english title"
@ -133,7 +136,7 @@ class Mangadex:
timeout=10, timeout=10,
) )
try: try:
total_chapters = r.json()["total"] total_chapters: int = r.json()["total"]
except Exception as exc: except Exception as exc:
log.error( log.error(
"Error retrieving the chapters list. Did you specify a valid language code?" "Error retrieving the chapters list. Did you specify a valid language code?"
@ -147,7 +150,7 @@ class Mangadex:
return total_chapters return total_chapters
# get chapter data like name, uuid etc # get chapter data like name, uuid etc
def get_chapter_data(self) -> dict: def get_chapter_data(self) -> Dict[str, Dict[str, Union[str, int]]]:
log.debug(f"Getting chapter data for: {self.manga_uuid}") log.debug(f"Getting chapter data for: {self.manga_uuid}")
api_sorting = "order[chapter]=asc&order[volume]=asc" api_sorting = "order[chapter]=asc&order[volume]=asc"
# check for chapters in specified lang # check for chapters in specified lang
@ -161,8 +164,9 @@ class Mangadex:
f"{self.api_base_url}/manga/{self.manga_uuid}/feed?{api_sorting}&limit=500&offset={offset}&{self.api_additions}", f"{self.api_base_url}/manga/{self.manga_uuid}/feed?{api_sorting}&limit=500&offset={offset}&{self.api_additions}",
timeout=10, timeout=10,
) )
for chapter in r.json()["data"]: response_body: Dict[str, Any] = r.json()
attributes: dict = chapter["attributes"] for chapter in response_body["data"]:
attributes: Dict[str, Any] = chapter["attributes"]
# chapter infos from feed # chapter infos from feed
chapter_num: str = attributes.get("chapter") or "" chapter_num: str = attributes.get("chapter") or ""
chapter_vol: str = attributes.get("volume") or "" chapter_vol: str = attributes.get("volume") or ""
@ -201,10 +205,10 @@ class Mangadex:
# increase offset for mangas with more than 500 chapters # increase offset for mangas with more than 500 chapters
offset += 500 offset += 500
return chapter_data return chapter_data # type:ignore
# get images for the chapter (mangadex@home) # get images for the chapter (mangadex@home)
def get_chapter_images(self, chapter: str, wait_time: float) -> list: def get_chapter_images(self, chapter: str, wait_time: float) -> List[str]:
log.debug(f"Getting chapter images for: {self.manga_uuid}") log.debug(f"Getting chapter images for: {self.manga_uuid}")
athome_url = f"{self.api_base_url}/at-home/server" athome_url = f"{self.api_base_url}/at-home/server"
chapter_uuid = self.manga_chapter_data[chapter]["uuid"] chapter_uuid = self.manga_chapter_data[chapter]["uuid"]
@ -238,11 +242,11 @@ class Mangadex:
if api_error: if api_error:
return [] return []
chapter_hash = api_data["chapter"]["hash"] chapter_hash = api_data["chapter"]["hash"] # pyright:ignore
chapter_img_data = api_data["chapter"]["data"] chapter_img_data = api_data["chapter"]["data"] # pyright:ignore
# get list of image urls # get list of image urls
image_urls = [] image_urls: List[str] = []
for image in chapter_img_data: for image in chapter_img_data:
image_urls.append(f"{self.img_base_url}/data/{chapter_hash}/{image}") image_urls.append(f"{self.img_base_url}/data/{chapter_hash}/{image}")
@ -251,12 +255,12 @@ class Mangadex:
return image_urls return image_urls
# create list of chapters # create list of chapters
def create_chapter_list(self) -> list: def create_chapter_list(self) -> List[str]:
log.debug(f"Creating chapter list for: {self.manga_uuid}") log.debug(f"Creating chapter list for: {self.manga_uuid}")
chapter_list = [] chapter_list: List[str] = []
for data in self.manga_chapter_data.values(): for data in self.manga_chapter_data.values():
chapter_number: str = data["chapter"] chapter_number: str = data["chapter"] # type:ignore
volume_number: str = data["volume"] volume_number: str = data["volume"] # type:ignore
if self.forcevol: if self.forcevol:
chapter_list.append(f"{volume_number}:{chapter_number}") chapter_list.append(f"{volume_number}:{chapter_number}")
else: else:
@ -264,12 +268,12 @@ class Mangadex:
return chapter_list return chapter_list
def create_metadata(self, chapter: str) -> dict: def create_metadata(self, chapter: str) -> Dict[str, Union[str, int, None]]:
log.info("Creating metadata from api") log.info("Creating metadata from api")
chapter_data = self.manga_chapter_data[chapter] chapter_data = self.manga_chapter_data[chapter]
try: try:
volume = int(chapter_data.get("volume")) volume = int(chapter_data["volume"])
except (ValueError, TypeError): except (ValueError, TypeError):
volume = None volume = None
metadata = { metadata = {
@ -285,4 +289,4 @@ class Mangadex:
"Web": f"https://mangadex.org/title/{self.manga_uuid}", "Web": f"https://mangadex.org/title/{self.manga_uuid}",
} }
return metadata return metadata # pyright:ignore

View file

@ -1,7 +1,7 @@
import re import re
import shutil import shutil
from pathlib import Path from pathlib import Path
from typing import Any, Union from typing import Any, Dict, List, Tuple, Union
from loguru import logger as log from loguru import logger as log
@ -23,7 +23,7 @@ def match_api(url_uuid: str) -> type:
The class of the API to use The class of the API to use
""" """
# apis to check # apis to check
apis: list[tuple[str, re.Pattern, type]] = [ apis: List[Tuple[str, re.Pattern[str], type]] = [
( (
"mangadex.org", "mangadex.org",
re.compile( re.compile(
@ -108,7 +108,7 @@ class MangaDLP:
self.chapter_post_hook_cmd = chapter_post_hook_cmd self.chapter_post_hook_cmd = chapter_post_hook_cmd
self.cache_path = cache_path self.cache_path = cache_path
self.add_metadata = add_metadata self.add_metadata = add_metadata
self.hook_infos: dict = {} self.hook_infos: Dict[str, Any] = {}
# prepare everything # prepare everything
self._prepare() self._prepare()
@ -226,7 +226,7 @@ class MangaDLP:
skipped_chapters: list[Any] = [] skipped_chapters: list[Any] = []
error_chapters: list[Any] = [] error_chapters: list[Any] = []
for chapter in chapters_to_download: for chapter in chapters_to_download:
if self.cache_path and chapter in cached_chapters: if self.cache_path and chapter in cached_chapters: # pyright:ignore
log.info(f"Chapter '{chapter}' is in cache. Skipping download") log.info(f"Chapter '{chapter}' is in cache. Skipping download")
continue continue
@ -240,7 +240,7 @@ class MangaDLP:
skipped_chapters.append(chapter) skipped_chapters.append(chapter)
# update cache # update cache
if self.cache_path: if self.cache_path:
cache.add_chapter(chapter) cache.add_chapter(chapter) # pyright:ignore
continue continue
except Exception: except Exception:
# skip download/packing due to an error # skip download/packing due to an error
@ -273,7 +273,7 @@ class MangaDLP:
# update cache # update cache
if self.cache_path: if self.cache_path:
cache.add_chapter(chapter) cache.add_chapter(chapter) # pyright:ignore
# start chapter post hook # start chapter post hook
run_hook( run_hook(
@ -310,7 +310,7 @@ class MangaDLP:
# once called per chapter # once called per chapter
def get_chapter(self, chapter: str) -> Path: def get_chapter(self, chapter: str) -> Path:
# get chapter infos # get chapter infos
chapter_infos: dict = self.api.manga_chapter_data[chapter] chapter_infos: Dict[str, Union[str, int]] = self.api.manga_chapter_data[chapter]
log.debug(f"Chapter infos: {chapter_infos}") log.debug(f"Chapter infos: {chapter_infos}")
# get image urls for chapter # get image urls for chapter
@ -342,8 +342,8 @@ class MangaDLP:
# get filename for chapter (without suffix) # get filename for chapter (without suffix)
chapter_filename = utils.get_filename( chapter_filename = utils.get_filename(
self.manga_title, self.manga_title,
chapter_infos["name"], chapter_infos["name"], # type:ignore
chapter_infos["volume"], chapter_infos["volume"], # type:ignore
chapter, chapter,
self.forcevol, self.forcevol,
self.name_format, self.name_format,
@ -352,7 +352,7 @@ class MangaDLP:
log.debug(f"Filename: '{chapter_filename}'") log.debug(f"Filename: '{chapter_filename}'")
# set download path for chapter (image folder) # set download path for chapter (image folder)
chapter_path = self.manga_path / chapter_filename chapter_path: Path = self.manga_path / chapter_filename
# set archive path with file format # set archive path with file format
chapter_archive_path = Path(f"{chapter_path}{self.file_format}") chapter_archive_path = Path(f"{chapter_path}{self.file_format}")

View file

@ -26,12 +26,14 @@ class CacheDB:
if not self.db_data.get(self.db_key): if not self.db_data.get(self.db_key):
self.db_data[self.db_key] = {} self.db_data[self.db_key] = {}
self.db_uuid_data: dict = self.db_data[self.db_key] self.db_uuid_data = self.db_data[self.db_key]
if not self.db_uuid_data.get("name"): if not self.db_uuid_data.get("name"):
self.db_uuid_data.update({"name": self.name}) self.db_uuid_data.update({"name": self.name})
self._write_db() self._write_db()
self.db_uuid_chapters: list = self.db_uuid_data.get("chapters") or [] self.db_uuid_chapters: List[str] = (
self.db_uuid_data.get("chapters") or [] # type:ignore
)
def _prepare_db(self) -> None: def _prepare_db(self) -> None:
if self.db_path.exists(): if self.db_path.exists():
@ -44,11 +46,11 @@ class CacheDB:
log.error("Can't create db-file") log.error("Can't create db-file")
raise exc raise exc
def _read_db(self) -> Dict[str, dict]: def _read_db(self) -> Dict[str, Dict[str, Union[str, List[str]]]]:
log.info(f"Reading cache-db: {self.db_path}") log.info(f"Reading cache-db: {self.db_path}")
try: try:
db_txt = self.db_path.read_text(encoding="utf8") db_txt = self.db_path.read_text(encoding="utf8")
db_dict: dict[str, dict] = json.loads(db_txt) db_dict: Dict[str, Dict[str, Union[str, List[str]]]] = json.loads(db_txt)
except Exception as exc: except Exception as exc:
log.error("Can't load cache-db") log.error("Can't load cache-db")
raise exc raise exc
@ -73,7 +75,7 @@ class CacheDB:
raise exc raise exc
def sort_chapters(chapters: list) -> List[str]: def sort_chapters(chapters: List[str]) -> List[str]:
try: try:
sorted_list = sorted(chapters, key=float) sorted_list = sorted(chapters, key=float)
except Exception: except Exception:

View file

@ -1,5 +1,6 @@
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Any, List
import click import click
from click_option_group import ( from click_option_group import (
@ -15,7 +16,7 @@ from mangadlp.logger import prepare_logger
# read in the list of links from a file # read in the list of links from a file
def readin_list(_ctx, _param, value) -> list: def readin_list(_ctx: click.Context, _param: str, value: str) -> List[str]:
if not value: if not value:
return [] return []
@ -38,8 +39,8 @@ def readin_list(_ctx, _param, value) -> list:
@click.help_option() @click.help_option()
@click.version_option(version=__version__, package_name="manga-dlp") @click.version_option(version=__version__, package_name="manga-dlp")
# manga selection # manga selection
@optgroup.group("source", cls=RequiredMutuallyExclusiveOptionGroup) @optgroup.group("source", cls=RequiredMutuallyExclusiveOptionGroup) # type: ignore
@optgroup.option( @optgroup.option( # type: ignore
"-u", "-u",
"--url", "--url",
"--uuid", "--uuid",
@ -49,19 +50,19 @@ def readin_list(_ctx, _param, value) -> list:
show_default=True, show_default=True,
help="URL or UUID of the manga", help="URL or UUID of the manga",
) )
@optgroup.option( @optgroup.option( # type: ignore
"--read", "--read",
"read_mangas", "read_mangas",
is_eager=True, is_eager=True,
callback=readin_list, callback=readin_list,
type=click.Path(exists=True, dir_okay=False), type=click.Path(exists=True, dir_okay=False, path_type=str),
default=None, default=None,
show_default=True, show_default=True,
help="Path of file with manga links to download. One per line", help="Path of file with manga links to download. One per line",
) )
# logging options # logging options
@optgroup.group("verbosity", cls=MutuallyExclusiveOptionGroup) @optgroup.group("verbosity", cls=MutuallyExclusiveOptionGroup) # type: ignore
@optgroup.option( @optgroup.option( # type: ignore
"--loglevel", "--loglevel",
"verbosity", "verbosity",
type=int, type=int,
@ -69,7 +70,7 @@ def readin_list(_ctx, _param, value) -> list:
show_default=True, show_default=True,
help="Custom log level", help="Custom log level",
) )
@optgroup.option( @optgroup.option( # type: ignore
"--warn", "--warn",
"verbosity", "verbosity",
flag_value=30, flag_value=30,
@ -77,7 +78,7 @@ def readin_list(_ctx, _param, value) -> list:
show_default=True, show_default=True,
help="Only log warnings and higher", help="Only log warnings and higher",
) )
@optgroup.option( @optgroup.option( # type: ignore
"--debug", "--debug",
"verbosity", "verbosity",
flag_value=10, flag_value=10,
@ -227,7 +228,7 @@ def readin_list(_ctx, _param, value) -> list:
help="Enable/disable creation of metadata via ComicInfo.xml", help="Enable/disable creation of metadata via ComicInfo.xml",
) )
@click.pass_context @click.pass_context
def main(ctx: click.Context, **kwargs) -> None: def main(ctx: click.Context, **kwargs: Any) -> None:
"""Script to download mangas from various sites.""" """Script to download mangas from various sites."""
url_uuid: str = kwargs.pop("url_uuid") url_uuid: str = kwargs.pop("url_uuid")
read_mangas: list[str] = kwargs.pop("read_mangas") read_mangas: list[str] = kwargs.pop("read_mangas")

View file

@ -2,7 +2,7 @@ import logging
import shutil import shutil
from pathlib import Path from pathlib import Path
from time import sleep from time import sleep
from typing import Union from typing import List, Union
import requests import requests
from loguru import logger as log from loguru import logger as log
@ -12,7 +12,7 @@ from mangadlp import utils
# download images # download images
def download_chapter( def download_chapter(
image_urls: list, image_urls: List[str],
chapter_path: Union[str, Path], chapter_path: Union[str, Path],
download_wait: float, download_wait: float,
) -> None: ) -> None:
@ -48,8 +48,8 @@ def download_chapter(
# write image # write image
try: try:
with image_path.open("wb") as file: with image_path.open("wb") as file:
r.raw.decode_content = True r.raw.decode_content = True # pyright:ignore
shutil.copyfileobj(r.raw, file) shutil.copyfileobj(r.raw, file) # pyright:ignore
except Exception as exc: except Exception as exc:
log.error("Can't write file") log.error("Can't write file")
raise exc raise exc

View file

@ -1,10 +1,11 @@
import os import os
import subprocess import subprocess
from typing import Any
from loguru import logger as log from loguru import logger as log
def run_hook(command: str, hook_type: str, **kwargs) -> int: def run_hook(command: str, hook_type: str, **kwargs: Any) -> int:
"""Run a command. """Run a command.
Run a command with subprocess.run and add kwargs to the environment. Run a command with subprocess.run and add kwargs to the environment.

View file

@ -1,5 +1,6 @@
import logging import logging
import sys import sys
from typing import Any, Dict
from loguru import logger from loguru import logger
@ -10,7 +11,7 @@ LOGURU_FMT = "{time:%Y-%m-%dT%H:%M:%S%z} | <level>[{level: <7}]</level> [{name:
class InterceptHandler(logging.Handler): class InterceptHandler(logging.Handler):
"""Intercept python logging messages and log them via loguru.logger.""" """Intercept python logging messages and log them via loguru.logger."""
def emit(self, record): def emit(self, record: Any) -> None:
# Get corresponding Loguru level if it exists # Get corresponding Loguru level if it exists
try: try:
level = logger.level(record.levelname).name level = logger.level(record.levelname).name
@ -19,8 +20,8 @@ class InterceptHandler(logging.Handler):
# Find caller from where originated the logged message # Find caller from where originated the logged message
frame, depth = logging.currentframe(), 2 frame, depth = logging.currentframe(), 2
while frame.f_code.co_filename == logging.__file__: while frame.f_code.co_filename == logging.__file__: # pyright:ignore
frame = frame.f_back frame = frame.f_back # type: ignore
depth += 1 depth += 1
logger.opt(depth=depth, exception=record.exc_info).log( logger.opt(depth=depth, exception=record.exc_info).log(
@ -30,7 +31,7 @@ class InterceptHandler(logging.Handler):
# init logger with format and log level # init logger with format and log level
def prepare_logger(loglevel: int = 20) -> None: def prepare_logger(loglevel: int = 20) -> None:
config: dict = { config: Dict[str, Any] = {
"handlers": [ "handlers": [
{ {
"sink": sys.stdout, "sink": sys.stdout,

View file

@ -1,5 +1,5 @@
from pathlib import Path from pathlib import Path
from typing import Any, Dict, Tuple from typing import Any, Dict, Tuple, Union
import xmltodict import xmltodict
from loguru import logger as log from loguru import logger as log
@ -8,7 +8,7 @@ METADATA_FILENAME = "ComicInfo.xml"
METADATA_TEMPLATE = Path("mangadlp/metadata/ComicInfo_v2.0.xml") METADATA_TEMPLATE = Path("mangadlp/metadata/ComicInfo_v2.0.xml")
# define metadata types, defaults and valid values. an empty list means no value check # define metadata types, defaults and valid values. an empty list means no value check
# {key: (type, default value, valid values)} # {key: (type, default value, valid values)}
METADATA_TYPES: Dict[str, Tuple[type, Any, list]] = { METADATA_TYPES: Dict[str, Tuple[type, Any, list[Union[str, int]]]] = {
"Title": (str, None, []), "Title": (str, None, []),
"Series": (str, None, []), "Series": (str, None, []),
"Number": (str, None, []), "Number": (str, None, []),
@ -59,10 +59,12 @@ METADATA_TYPES: Dict[str, Tuple[type, Any, list]] = {
} }
def validate_metadata(metadata_in: dict) -> Dict[str, dict]: def validate_metadata(
metadata_in: Dict[str, Union[str, int]]
) -> Dict[str, Dict[str, Union[str, int]]]:
log.info("Validating metadata") log.info("Validating metadata")
metadata_valid: dict[str, dict] = {"ComicInfo": {}} metadata_valid: dict[str, Dict[str, Union[str, int]]] = {"ComicInfo": {}}
for key, value in METADATA_TYPES.items(): for key, value in METADATA_TYPES.items():
metadata_type, metadata_default, metadata_validation = value metadata_type, metadata_default, metadata_validation = value
@ -104,7 +106,7 @@ def validate_metadata(metadata_in: dict) -> Dict[str, dict]:
return metadata_valid return metadata_valid
def write_metadata(chapter_path: Path, metadata: dict) -> None: def write_metadata(chapter_path: Path, metadata: Dict[str, Union[str, int]]) -> None:
if metadata["Format"] == "pdf": if metadata["Format"] == "pdf":
log.warning("Can't add metadata for pdf format. Skipping") log.warning("Can't add metadata for pdf format. Skipping")
return return

View file

@ -24,7 +24,7 @@ def make_archive(chapter_path: Path, file_format: str) -> None:
def make_pdf(chapter_path: Path) -> None: def make_pdf(chapter_path: Path) -> None:
try: try:
import img2pdf # pylint: disable=import-outside-toplevel import img2pdf # pylint: disable=import-outside-toplevel # pyright:ignore
except Exception as exc: except Exception as exc:
log.error("Cant import img2pdf. Please install it first") log.error("Cant import img2pdf. Please install it first")
raise exc raise exc
@ -34,14 +34,14 @@ def make_pdf(chapter_path: Path) -> None:
for file in chapter_path.iterdir(): for file in chapter_path.iterdir():
images.append(str(file)) images.append(str(file))
try: try:
pdf_path.write_bytes(img2pdf.convert(images)) pdf_path.write_bytes(img2pdf.convert(images)) # pyright:ignore
except Exception as exc: except Exception as exc:
log.error("Can't create '.pdf' archive") log.error("Can't create '.pdf' archive")
raise exc raise exc
# create a list of chapters # create a list of chapters
def get_chapter_list(chapters: str, available_chapters: list) -> List[str]: def get_chapter_list(chapters: str, available_chapters: List[str]) -> List[str]:
# check if there are available chapter # check if there are available chapter
chapter_list: list[str] = [] chapter_list: list[str] = []
for chapter in chapters.split(","): for chapter in chapters.split(","):

View file

@ -71,7 +71,7 @@ dependencies = [
# mypy # mypy
[tool.mypy] [tool.mypy]
#strict = true strict = true
python_version = "3.9" python_version = "3.9"
disallow_untyped_defs = false disallow_untyped_defs = false
follow_imports = "normal" follow_imports = "normal"
@ -84,6 +84,18 @@ show_error_codes = true
pretty = true pretty = true
no_implicit_optional = false no_implicit_optional = false
# pyright
[tool.pyright]
typeCheckingMode = "strict"
pythonVersion = "3.9"
reportUnnecessaryTypeIgnoreComment = true
reportShadowedImports = true
reportUnusedExpression = true
reportMatchNotExhaustive = true
# venvPath = "."
# venv = "venv"
# ruff # ruff
[tool.ruff] [tool.ruff]
@ -103,10 +115,11 @@ select = [
] ]
line-length = 88 line-length = 88
fix = true fix = true
show-fixes = true
format = "grouped" format = "grouped"
ignore-init-module-imports = true ignore-init-module-imports = true
respect-gitignore = true respect-gitignore = true
ignore = ["E501", "D103", "D100"] ignore = ["E501", "D103", "D100", "D102", "PLR2004"]
exclude = [ exclude = [
".direnv", ".direnv",
".git", ".git",