From 4ae73f2fdf144d84b47817071f699991a2fc3c79 Mon Sep 17 00:00:00 2001 From: SigureMo Date: Sat, 10 Jan 2026 20:37:56 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20types:=20update=20retur?= =?UTF-8?q?n=20types=20to=20use=20TypedDicts=20for=20bangumi,=20cheese,=20?= =?UTF-8?q?and=20ugc=5Fvideo=20APIs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 8 +- src/yutto/api/bangumi.py | 80 ++++++------ src/yutto/api/cheese.py | 60 ++++----- src/yutto/api/ugc_video.py | 141 ++++++++++----------- src/yutto/downloader/downloader.py | 10 +- src/yutto/exceptions.py | 6 +- src/yutto/input_parser.py | 2 +- src/yutto/utils/asynclib.py | 3 + src/yutto/utils/functional/async_object.py | 1 - uv.lock | 86 ++++++------- 10 files changed, 199 insertions(+), 198 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5c30b51ad..c0f8f2ff8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,10 +45,10 @@ yutto = "yutto.__main__:main" [dependency-groups] dev = [ - "pyright>=1.1.407", - "ruff>=0.14.6", - "typos>=1.40.0", - "pytest>=9.0.1", + "pyright>=1.1.408", + "ruff>=0.14.11", + "typos>=1.42.0", + "pytest>=9.0.2", "pytest-rerunfailures>=16.1", "syrupy>=5.0.0", "pytest-codspeed>=4.2.0", diff --git a/src/yutto/api/bangumi.py b/src/yutto/api/bangumi.py index 489b3fd30..e36d584d7 100644 --- a/src/yutto/api/bangumi.py +++ b/src/yutto/api/bangumi.py @@ -6,10 +6,12 @@ from yutto.exceptions import NoAccessPermissionError, UnSupportedTypeError from yutto.media.codec import audio_codec_map, video_codec_map from yutto.types import ( + AudioUrlMeta, BvId, CId, EpisodeId, SeasonId, + VideoUrlMeta, format_ids, ) from yutto.utils.console.logger import Logger @@ -22,11 +24,9 @@ from httpx import AsyncClient from yutto.types import ( - AudioUrlMeta, AvId, MediaId, MultiLangSubtitle, - VideoUrlMeta, ) from yutto.utils.fetcher import FetcherContext @@ -75,22 +75,22 @@ async def get_bangumi_list(ctx: FetcherContext, client: AsyncClient, season_id: # 如 https://www.bilibili.com/bangumi/play/ep409825 中的「次元发电机采访」 # 和 https://www.bilibili.com/bangumi/play/ep424859 中的「编辑推荐」 section_episodes += section["episodes"] - return { - "title": result["title"], - "pages": [ - { - "id": i + 1, - "name": _bangumi_episode_title(item["title"], item["long_title"]), - "cid": CId(str(item["cid"])), - "episode_id": EpisodeId(str(item["id"])), - "avid": BvId(item["bvid"]), - "is_section": i >= len(result["episodes"]), - "is_preview": item["badge"] == "预告", # 并不是一种鲁棒的方式,但目前貌似没有更好的方式了 - "metadata": _parse_bangumi_metadata(item), - } + return BangumiList( + title=result["title"], + pages=[ + BangumiListItem( + id=i + 1, + name=_bangumi_episode_title(item["title"], item["long_title"]), + cid=CId(str(item["cid"])), + episode_id=EpisodeId(str(item["id"])), + avid=BvId(item["bvid"]), + is_section=i >= len(result["episodes"]), + is_preview=item["badge"] == "预告", # 并不是一种鲁棒的方式,但目前貌似没有更好的方式了 + metadata=_parse_bangumi_metadata(item), + ) for i, item in enumerate(result["episodes"] + section_episodes) ], - } + ) async def get_bangumi_playurl( @@ -113,38 +113,38 @@ async def get_bangumi_playurl( raise UnSupportedTypeError(f"该视频({format_ids(avid, cid)})尚不支持 DASH 格式") videos: list[VideoUrlMeta] = [ - { - "url": video["base_url"], - "mirrors": video["backup_url"] if video["backup_url"] is not None else [], - "codec": video_codec_map[video["codecid"]], - "width": video["width"], - "height": video["height"], - "quality": video["id"], - } + VideoUrlMeta( + url=video["base_url"], + mirrors=video["backup_url"] if video["backup_url"] is not None else [], + codec=video_codec_map[video["codecid"]], + width=video["width"], + height=video["height"], + quality=video["id"], + ) for video in video_info["dash"]["video"] ] audios: list[AudioUrlMeta] = [ - { - "url": audio["base_url"], - "mirrors": audio["backup_url"] if audio["backup_url"] is not None else [], - "codec": audio_codec_map[audio["codecid"]], - "width": 0, - "height": 0, - "quality": audio["id"], - } + AudioUrlMeta( + url=audio["base_url"], + mirrors=audio["backup_url"] if audio["backup_url"] is not None else [], + codec=audio_codec_map[audio["codecid"]], + width=0, + height=0, + quality=audio["id"], + ) for audio in video_info["dash"]["audio"] ] if video_info["dash"]["dolby"] is not None and video_info["dash"]["dolby"]["audio"] is not None: dolby_audios_json = video_info["dash"]["dolby"]["audio"] audios.extend( - { - "url": dolby_audio_json["base_url"], - "mirrors": dolby_audio_json["backup_url"] if dolby_audio_json["backup_url"] is not None else [], - "codec": "eac3", # TODO: 由于这里的 codecid 仍然是 0,所以无法通过 audio_codec_map 转换,暂时直接硬编码 - "width": 0, - "height": 0, - "quality": dolby_audio_json["id"], - } + AudioUrlMeta( + url=dolby_audio_json["base_url"], + mirrors=dolby_audio_json["backup_url"] if dolby_audio_json["backup_url"] is not None else [], + codec="eac3", # TODO: 由于这里的 codecid 仍然是 0,所以无法通过 audio_codec_map 转换,暂时直接硬编码 + width=0, + height=0, + quality=dolby_audio_json["id"], + ) for dolby_audio_json in dolby_audios_json ) return (videos, audios) diff --git a/src/yutto/api/cheese.py b/src/yutto/api/cheese.py index 5a5bb7810..3365499f5 100644 --- a/src/yutto/api/cheese.py +++ b/src/yutto/api/cheese.py @@ -6,9 +6,11 @@ from yutto.media.codec import audio_codec_map, video_codec_map from yutto.types import ( AId, + AudioUrlMeta, CId, EpisodeId, SeasonId, + VideoUrlMeta, format_ids, ) from yutto.utils.console.logger import Logger @@ -21,10 +23,8 @@ from httpx import AsyncClient from yutto.types import ( - AudioUrlMeta, AvId, MultiLangSubtitle, - VideoUrlMeta, ) from yutto.utils.fetcher import FetcherContext @@ -59,20 +59,20 @@ async def get_cheese_list(ctx: FetcherContext, client: AsyncClient, season_id: S raise NoAccessPermissionError(f"无法解析该课程列表(season_id: {season_id}),原因:{resp_json.get('message')}") result = resp_json["data"] section_episodes = result["episodes"] - return { - "title": result["title"], - "pages": [ - { - "id": i + 1, - "name": item["title"], - "cid": CId(str(item["cid"])), - "episode_id": EpisodeId(str(item["id"])), - "avid": AId(str(item["aid"])), - "metadata": _parse_cheese_metadata(item), - } + return CheeseList( + title=result["title"], + pages=[ + CheeseListItem( + id=i + 1, + name=item["title"], + cid=CId(str(item["cid"])), + episode_id=EpisodeId(str(item["id"])), + avid=AId(str(item["aid"])), + metadata=_parse_cheese_metadata(item), + ) for i, item in enumerate(section_episodes) ], - } + ) async def get_cheese_playurl( @@ -95,25 +95,25 @@ async def get_cheese_playurl( raise UnSupportedTypeError(f"该视频({format_ids(avid, cid)})尚不支持 DASH 格式") return ( [ - { - "url": video["base_url"], - "mirrors": video["backup_url"] if video["backup_url"] is not None else [], - "codec": video_codec_map[video["codecid"]], - "width": video["width"], - "height": video["height"], - "quality": video["id"], - } + VideoUrlMeta( + url=video["base_url"], + mirrors=video["backup_url"] if video["backup_url"] is not None else [], + codec=video_codec_map[video["codecid"]], + width=video["width"], + height=video["height"], + quality=video["id"], + ) for video in resp_json["data"]["dash"]["video"] ], [ - { - "url": audio["base_url"], - "mirrors": audio["backup_url"] if audio["backup_url"] is not None else [], - "codec": audio_codec_map[audio["codecid"]], - "width": 0, - "height": 0, - "quality": audio["id"], - } + AudioUrlMeta( + url=audio["base_url"], + mirrors=audio["backup_url"] if audio["backup_url"] is not None else [], + codec=audio_codec_map[audio["codecid"]], + width=0, + height=0, + quality=audio["id"], + ) for audio in resp_json["data"]["dash"]["audio"] ], ) diff --git a/src/yutto/api/ugc_video.py b/src/yutto/api/ugc_video.py index f3c04ae06..278dd52d7 100644 --- a/src/yutto/api/ugc_video.py +++ b/src/yutto/api/ugc_video.py @@ -11,29 +11,26 @@ from yutto.media.codec import audio_codec_map, video_codec_map from yutto.types import ( AId, + AudioUrlMeta, BvId, CId, EpisodeId, + MultiLangSubtitle, + VideoUrlMeta, format_ids, ) from yutto.utils.console.colorful import colored_string from yutto.utils.console.logger import Logger from yutto.utils.fetcher import Fetcher from yutto.utils.functional.data_access import data_has_chained_keys -from yutto.utils.metadata import Actor, MetaData +from yutto.utils.metadata import Actor, ChapterInfoData, MetaData from yutto.utils.time import get_time_stamp_by_now if TYPE_CHECKING: from httpx import AsyncClient - from yutto.types import ( - AudioUrlMeta, - AvId, - MultiLangSubtitle, - VideoUrlMeta, - ) + from yutto.types import AvId from yutto.utils.fetcher import FetcherContext - from yutto.utils.metadata import ChapterInfoData class _UgcVideoPageInfo(TypedDict): @@ -111,28 +108,28 @@ async def get_ugc_video_info(ctx: FetcherContext, client: AsyncClient, avid: AvI actors = _parse_actor_info(res_json_data) genres = _parse_genre_info(res_json_data) tags: list[str] = await get_ugc_video_tag(ctx, client, avid) - return { - "avid": BvId(res_json_data["bvid"]), - "aid": AId(str(res_json_data["aid"])), - "bvid": BvId(res_json_data["bvid"]), - "episode_id": episode_id, - "is_bangumi": bool(episode_id), - "cid": CId(str(res_json_data["cid"])), - "picture": res_json_data["pic"], - "title": res_json_data["title"], - "pubdate": res_json_data["pubdate"], - "description": res_json_data["desc"], - "pages": [ - { - "part": page["part"], - "first_frame": page.get("first_frame"), - } + return _UgcVideoInfo( + avid=BvId(res_json_data["bvid"]), + aid=AId(str(res_json_data["aid"])), + bvid=BvId(res_json_data["bvid"]), + episode_id=episode_id, + is_bangumi=bool(episode_id), + cid=CId(str(res_json_data["cid"])), + picture=res_json_data["pic"], + title=res_json_data["title"], + pubdate=res_json_data["pubdate"], + description=res_json_data["desc"], + pages=[ + _UgcVideoPageInfo( + part=page["part"], + first_frame=page.get("first_frame"), + ) for page in res_json_data["pages"] ], - "actor": actors, - "tag": tags, - "genre": genres, - } + actor=actors, + tag=tags, + genre=genres, + ) async def get_ugc_video_list(ctx: FetcherContext, client: AsyncClient, avid: AvId) -> UgcVideoList: @@ -161,13 +158,13 @@ async def get_ugc_video_list(ctx: FetcherContext, client: AsyncClient, avid: AvI page_info["part"] = f"{video_title}_P{i + 1:02}" result["pages"] = [ - { - "id": i + 1, - "name": item["part"], - "avid": avid, - "cid": CId(str(item["cid"])), - "metadata": _parse_ugc_video_metadata(video_info, page_info, is_first_page=i == 0), - } + UgcVideoListItem( + id=i + 1, + name=item["part"], + avid=avid, + cid=CId(str(item["cid"])), + metadata=_parse_ugc_video_metadata(video_info, page_info, is_first_page=i == 0), + ) for i, (item, page_info) in enumerate( zip(cast("list[Any]", res_json["data"]), video_info["pages"], strict=True) ) @@ -227,14 +224,14 @@ async def get_ugc_video_playurl( raise UnSupportedTypeError(f"该视频({format_ids(avid, cid)})尚不支持 DASH 格式") videos: list[VideoUrlMeta] = ( [ - { - "url": video["base_url"], - "mirrors": video["backup_url"] if video["backup_url"] is not None else [], - "codec": video_codec_map[video["codecid"]], - "width": video["width"], - "height": video["height"], - "quality": video["id"], - } + VideoUrlMeta( + url=video["base_url"], + mirrors=video["backup_url"] if video["backup_url"] is not None else [], + codec=video_codec_map[video["codecid"]], + width=video["width"], + height=video["height"], + quality=video["id"], + ) for video in resp_json["data"]["dash"]["video"] ] if resp_json["data"]["dash"]["video"] @@ -242,14 +239,14 @@ async def get_ugc_video_playurl( ) audios: list[AudioUrlMeta] = ( [ - { - "url": audio["base_url"], - "mirrors": audio["backup_url"] if audio["backup_url"] is not None else [], - "codec": audio_codec_map[audio["codecid"]], - "width": 0, - "height": 0, - "quality": audio["id"], - } + AudioUrlMeta( + url=audio["base_url"], + mirrors=audio["backup_url"] if audio["backup_url"] is not None else [], + codec=audio_codec_map[audio["codecid"]], + width=0, + height=0, + quality=audio["id"], + ) for audio in resp_json["data"]["dash"]["audio"] ] if resp_json["data"]["dash"]["audio"] @@ -258,27 +255,27 @@ async def get_ugc_video_playurl( if resp_json["data"]["dash"]["dolby"] is not None and resp_json["data"]["dash"]["dolby"]["audio"] is not None: dolby_audios_json = resp_json["data"]["dash"]["dolby"]["audio"] audios.extend( - { - "url": dolby_audio_json["base_url"], - "mirrors": dolby_audio_json["backup_url"] if dolby_audio_json["backup_url"] is not None else [], - "codec": "eac3", # TODO: 由于这里的 codecid 仍然是 0,所以无法通过 audio_codec_map 转换,暂时直接硬编码 - "width": 0, - "height": 0, - "quality": dolby_audio_json["id"], - } + AudioUrlMeta( + url=dolby_audio_json["base_url"], + mirrors=dolby_audio_json["backup_url"] if dolby_audio_json["backup_url"] is not None else [], + codec="eac3", # TODO: 由于这里的 codecid 仍然是 0,所以无法通过 audio_codec_map 转换,暂时直接硬编码 + width=0, + height=0, + quality=dolby_audio_json["id"], + ) for dolby_audio_json in dolby_audios_json ) if resp_json["data"]["dash"]["flac"] is not None and resp_json["data"]["dash"]["flac"]["audio"] is not None: hi_res_audio_json = resp_json["data"]["dash"]["flac"]["audio"] audios.append( - { - "url": hi_res_audio_json["base_url"], - "mirrors": hi_res_audio_json["backup_url"] if hi_res_audio_json["backup_url"] is not None else [], - "codec": "flac", # TODO: 同上,硬编码 - "width": 0, - "height": 0, - "quality": hi_res_audio_json["id"], - } + AudioUrlMeta( + url=hi_res_audio_json["base_url"], + mirrors=hi_res_audio_json["backup_url"] if hi_res_audio_json["backup_url"] is not None else [], + codec="flac", # TODO: 同上,硬编码 + width=0, + height=0, + quality=hi_res_audio_json["id"], + ) ) show_ai_translation_language(resp_json, ai_translation_language) @@ -308,10 +305,10 @@ async def get_ugc_video_subtitles( if subtitle_text is None: continue results.append( - { - "lang": sub_info["lan_doc"], - "lines": subtitle_text["body"], - } + MultiLangSubtitle( + lang=sub_info["lan_doc"], + lines=subtitle_text["body"], + ) ) return results @@ -330,7 +327,7 @@ async def get_ugc_video_chapters( raw_chapter_info = chapter_json_info["data"]["view_points"] return [ - {"content": chapter_info["content"], "start": chapter_info["from"], "end": chapter_info["to"]} + ChapterInfoData(content=chapter_info["content"], start=chapter_info["from"], end=chapter_info["to"]) for chapter_info in raw_chapter_info ] diff --git a/src/yutto/downloader/downloader.py b/src/yutto/downloader/downloader.py index 667bb7beb..273013e70 100644 --- a/src/yutto/downloader/downloader.py +++ b/src/yutto/downloader/downloader.py @@ -4,7 +4,7 @@ import os import re from enum import Enum -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, cast from yutto.downloader.progressbar import show_progress from yutto.downloader.selector import select_audio, select_video @@ -327,25 +327,25 @@ async def process_download( # 显示音视频详细信息 show_videos_info( videos, - videos.index(video) if will_download_video else -1, # pyright: ignore [reportArgumentType] + videos.index(cast("VideoUrlMeta", video)) if will_download_video else -1, ) show_audios_info( audios, - audios.index(audio) if will_download_audio else -1, # pyright: ignore [reportArgumentType] + audios.index(cast("AudioUrlMeta", audio)) if will_download_audio else -1, ) output_format = ".mp4" if not will_download_video: if options["output_format_audio_only"] != "infer": output_format = "." + options["output_format_audio_only"] - elif will_download_audio and audio["codec"] == "flac": # pyright: ignore [reportOptionalSubscript] + elif will_download_audio and cast("AudioUrlMeta", audio)["codec"] == "flac": output_format = ".flac" else: output_format = ".m4a" else: if options["output_format"] != "infer": output_format = "." + options["output_format"] - elif will_download_audio and audio["codec"] == "flac": # pyright: ignore [reportOptionalSubscript] + elif will_download_audio and cast("AudioUrlMeta", audio)["codec"] == "flac": output_format = ".mkv" # MP4 does not support FLAC audio output_path = output_dir.joinpath(filename + output_format) diff --git a/src/yutto/exceptions.py b/src/yutto/exceptions.py index f4281a369..01e3cc849 100644 --- a/src/yutto/exceptions.py +++ b/src/yutto/exceptions.py @@ -2,7 +2,7 @@ import sys from enum import Enum -from typing import TYPE_CHECKING, TypeAlias +from typing import TYPE_CHECKING, Any, TypeAlias if TYPE_CHECKING: from types import TracebackType @@ -69,7 +69,9 @@ class CryptoError(YuttoBaseException): code = ErrorCode.CRYPTO_ERROR -def handle_uncaught_exception(exctype: type[Exception], exception: Exception, trace: TracebackType): +def handle_uncaught_exception( + exctype: type[BaseException], exception: BaseException, trace: TracebackType | None +) -> Any: old_hook(exctype, exception, trace) if isinstance(exception, YuttoBaseException): sys.exit(exception.code.value) diff --git a/src/yutto/input_parser.py b/src/yutto/input_parser.py index 2461364e4..3330a1a8c 100644 --- a/src/yutto/input_parser.py +++ b/src/yutto/input_parser.py @@ -2,7 +2,7 @@ import re import sys -import urllib +import urllib.parse import urllib.request from pathlib import Path diff --git a/src/yutto/utils/asynclib.py b/src/yutto/utils/asynclib.py index a413c2893..c75cf2daf 100644 --- a/src/yutto/utils/asynclib.py +++ b/src/yutto/utils/asynclib.py @@ -5,6 +5,7 @@ import platform import sys import time +import types from functools import wraps from typing import TYPE_CHECKING, Any, Generic, TypeVar @@ -54,6 +55,8 @@ def async_cache( def decorator(fn: Callable[P, Coroutine[Any, Any, RetT]]) -> Callable[P, Coroutine[Any, Any, RetT]]: @wraps(fn) async def wrapper(*args: P.args, **kwargs: P.kwargs) -> RetT: + assert isinstance(fn, types.FunctionType) + sig = inspect.signature(fn) bound_args = sig.bind(*args, **kwargs) bound_args.apply_defaults() diff --git a/src/yutto/utils/functional/async_object.py b/src/yutto/utils/functional/async_object.py index d94ad4d83..049fef4c9 100644 --- a/src/yutto/utils/functional/async_object.py +++ b/src/yutto/utils/functional/async_object.py @@ -14,7 +14,6 @@ class aobject: ``` python class MyClass(aobject): - # pyright: reportIncompatibleMethodOverride=false async def __ainit__(self): ... diff --git a/uv.lock b/uv.lock index 04b87afc3..5c2afbc35 100644 --- a/uv.lock +++ b/uv.lock @@ -1046,20 +1046,20 @@ wheels = [ [[package]] name = "pyright" -version = "1.1.407" +version = "1.1.408" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/1b/0aa08ee42948b61745ac5b5b5ccaec4669e8884b53d31c8ec20b2fcd6b6f/pyright-1.1.407.tar.gz", hash = "sha256:099674dba5c10489832d4a4b2d302636152a9a42d317986c38474c76fe562262", size = 4122872, upload-time = "2025-10-24T23:17:15.145Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/b2/5db700e52554b8f025faa9c3c624c59f1f6c8841ba81ab97641b54322f16/pyright-1.1.408.tar.gz", hash = "sha256:f28f2321f96852fa50b5829ea492f6adb0e6954568d1caa3f3af3a5f555eb684", size = 4400578, upload-time = "2026-01-08T08:07:38.795Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/93/b69052907d032b00c40cb656d21438ec00b3a471733de137a3f65a49a0a0/pyright-1.1.407-py3-none-any.whl", hash = "sha256:6dd419f54fcc13f03b52285796d65e639786373f433e243f8b94cf93a7444d21", size = 5997008, upload-time = "2025-10-24T23:17:13.159Z" }, + { url = "https://files.pythonhosted.org/packages/0c/82/a2c93e32800940d9573fb28c346772a14778b84ba7524e691b324620ab89/pyright-1.1.408-py3-none-any.whl", hash = "sha256:090b32865f4fdb1e0e6cd82bf5618480d48eecd2eb2e70f960982a3d9a4c17c1", size = 6399144, upload-time = "2026-01-08T08:07:37.082Z" }, ] [[package]] name = "pytest" -version = "9.0.1" +version = "9.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1070,9 +1070,9 @@ dependencies = [ { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/07/56/f013048ac4bc4c1d9be45afd4ab209ea62822fb1598f40687e6bf45dcea4/pytest-9.0.1.tar.gz", hash = "sha256:3e9c069ea73583e255c3b21cf46b8d3c56f6e3a1a8f6da94ccb0fcf57b9d73c8", size = 1564125, upload-time = "2025-11-12T13:05:09.333Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl", hash = "sha256:67be0030d194df2dfa7b556f2e56fb3c3315bd5c8822c6951162b92b32ce7dad", size = 373668, upload-time = "2025-11-12T13:05:07.379Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, ] [[package]] @@ -1418,28 +1418,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.14.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/f0/62b5a1a723fe183650109407fa56abb433b00aa1c0b9ba555f9c4efec2c6/ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc", size = 5669501, upload-time = "2025-11-21T14:26:17.903Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/d2/7dd544116d107fffb24a0064d41a5d2ed1c9d6372d142f9ba108c8e39207/ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3", size = 13326119, upload-time = "2025-11-21T14:25:24.2Z" }, - { url = "https://files.pythonhosted.org/packages/36/6a/ad66d0a3315d6327ed6b01f759d83df3c4d5f86c30462121024361137b6a/ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004", size = 13526007, upload-time = "2025-11-21T14:25:26.906Z" }, - { url = "https://files.pythonhosted.org/packages/a3/9d/dae6db96df28e0a15dea8e986ee393af70fc97fd57669808728080529c37/ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332", size = 12676572, upload-time = "2025-11-21T14:25:29.826Z" }, - { url = "https://files.pythonhosted.org/packages/76/a4/f319e87759949062cfee1b26245048e92e2acce900ad3a909285f9db1859/ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef", size = 13140745, upload-time = "2025-11-21T14:25:32.788Z" }, - { url = "https://files.pythonhosted.org/packages/95/d3/248c1efc71a0a8ed4e8e10b4b2266845d7dfc7a0ab64354afe049eaa1310/ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775", size = 13076486, upload-time = "2025-11-21T14:25:35.601Z" }, - { url = "https://files.pythonhosted.org/packages/a5/19/b68d4563fe50eba4b8c92aa842149bb56dd24d198389c0ed12e7faff4f7d/ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce", size = 13727563, upload-time = "2025-11-21T14:25:38.514Z" }, - { url = "https://files.pythonhosted.org/packages/47/ac/943169436832d4b0e867235abbdb57ce3a82367b47e0280fa7b4eabb7593/ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f", size = 15199755, upload-time = "2025-11-21T14:25:41.516Z" }, - { url = "https://files.pythonhosted.org/packages/c9/b9/288bb2399860a36d4bb0541cb66cce3c0f4156aaff009dc8499be0c24bf2/ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d", size = 14850608, upload-time = "2025-11-21T14:25:44.428Z" }, - { url = "https://files.pythonhosted.org/packages/ee/b1/a0d549dd4364e240f37e7d2907e97ee80587480d98c7799d2d8dc7a2f605/ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440", size = 14118754, upload-time = "2025-11-21T14:25:47.214Z" }, - { url = "https://files.pythonhosted.org/packages/13/ac/9b9fe63716af8bdfddfacd0882bc1586f29985d3b988b3c62ddce2e202c3/ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105", size = 13949214, upload-time = "2025-11-21T14:25:50.002Z" }, - { url = "https://files.pythonhosted.org/packages/12/27/4dad6c6a77fede9560b7df6802b1b697e97e49ceabe1f12baf3ea20862e9/ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821", size = 14106112, upload-time = "2025-11-21T14:25:52.841Z" }, - { url = "https://files.pythonhosted.org/packages/6a/db/23e322d7177873eaedea59a7932ca5084ec5b7e20cb30f341ab594130a71/ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55", size = 13035010, upload-time = "2025-11-21T14:25:55.536Z" }, - { url = "https://files.pythonhosted.org/packages/a8/9c/20e21d4d69dbb35e6a1df7691e02f363423658a20a2afacf2a2c011800dc/ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71", size = 13054082, upload-time = "2025-11-21T14:25:58.625Z" }, - { url = "https://files.pythonhosted.org/packages/66/25/906ee6a0464c3125c8d673c589771a974965c2be1a1e28b5c3b96cb6ef88/ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b", size = 13303354, upload-time = "2025-11-21T14:26:01.816Z" }, - { url = "https://files.pythonhosted.org/packages/4c/58/60577569e198d56922b7ead07b465f559002b7b11d53f40937e95067ca1c/ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185", size = 14054487, upload-time = "2025-11-21T14:26:05.058Z" }, - { url = "https://files.pythonhosted.org/packages/67/0b/8e4e0639e4cc12547f41cb771b0b44ec8225b6b6a93393176d75fe6f7d40/ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85", size = 13013361, upload-time = "2025-11-21T14:26:08.152Z" }, - { url = "https://files.pythonhosted.org/packages/fb/02/82240553b77fd1341f80ebb3eaae43ba011c7a91b4224a9f317d8e6591af/ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9", size = 14432087, upload-time = "2025-11-21T14:26:10.891Z" }, - { url = "https://files.pythonhosted.org/packages/a5/1f/93f9b0fad9470e4c829a5bb678da4012f0c710d09331b860ee555216f4ea/ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2", size = 13520930, upload-time = "2025-11-21T14:26:13.951Z" }, +version = "0.14.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/77/9a7fe084d268f8855d493e5031ea03fa0af8cc05887f638bf1c4e3363eb8/ruff-0.14.11.tar.gz", hash = "sha256:f6dc463bfa5c07a59b1ff2c3b9767373e541346ea105503b4c0369c520a66958", size = 5993417, upload-time = "2026-01-08T19:11:58.322Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/a6/a4c40a5aaa7e331f245d2dc1ac8ece306681f52b636b40ef87c88b9f7afd/ruff-0.14.11-py3-none-linux_armv6l.whl", hash = "sha256:f6ff2d95cbd335841a7217bdfd9c1d2e44eac2c584197ab1385579d55ff8830e", size = 12951208, upload-time = "2026-01-08T19:12:09.218Z" }, + { url = "https://files.pythonhosted.org/packages/5c/5c/360a35cb7204b328b685d3129c08aca24765ff92b5a7efedbdd6c150d555/ruff-0.14.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f6eb5c1c8033680f4172ea9c8d3706c156223010b8b97b05e82c59bdc774ee6", size = 13330075, upload-time = "2026-01-08T19:12:02.549Z" }, + { url = "https://files.pythonhosted.org/packages/1b/9e/0cc2f1be7a7d33cae541824cf3f95b4ff40d03557b575912b5b70273c9ec/ruff-0.14.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f2fc34cc896f90080fca01259f96c566f74069a04b25b6205d55379d12a6855e", size = 12257809, upload-time = "2026-01-08T19:12:00.366Z" }, + { url = "https://files.pythonhosted.org/packages/a7/e5/5faab97c15bb75228d9f74637e775d26ac703cc2b4898564c01ab3637c02/ruff-0.14.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53386375001773ae812b43205d6064dae49ff0968774e6befe16a994fc233caa", size = 12678447, upload-time = "2026-01-08T19:12:13.899Z" }, + { url = "https://files.pythonhosted.org/packages/1b/33/e9767f60a2bef779fb5855cab0af76c488e0ce90f7bb7b8a45c8a2ba4178/ruff-0.14.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a697737dce1ca97a0a55b5ff0434ee7205943d4874d638fe3ae66166ff46edbe", size = 12758560, upload-time = "2026-01-08T19:11:42.55Z" }, + { url = "https://files.pythonhosted.org/packages/eb/84/4c6cf627a21462bb5102f7be2a320b084228ff26e105510cd2255ea868e5/ruff-0.14.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6845ca1da8ab81ab1dce755a32ad13f1db72e7fba27c486d5d90d65e04d17b8f", size = 13599296, upload-time = "2026-01-08T19:11:30.371Z" }, + { url = "https://files.pythonhosted.org/packages/88/e1/92b5ed7ea66d849f6157e695dc23d5d6d982bd6aa8d077895652c38a7cae/ruff-0.14.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e36ce2fd31b54065ec6f76cb08d60159e1b32bdf08507862e32f47e6dde8bcbf", size = 15048981, upload-time = "2026-01-08T19:12:04.742Z" }, + { url = "https://files.pythonhosted.org/packages/61/df/c1bd30992615ac17c2fb64b8a7376ca22c04a70555b5d05b8f717163cf9f/ruff-0.14.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590bcc0e2097ecf74e62a5c10a6b71f008ad82eb97b0a0079e85defe19fe74d9", size = 14633183, upload-time = "2026-01-08T19:11:40.069Z" }, + { url = "https://files.pythonhosted.org/packages/04/e9/fe552902f25013dd28a5428a42347d9ad20c4b534834a325a28305747d64/ruff-0.14.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:53fe71125fc158210d57fe4da26e622c9c294022988d08d9347ec1cf782adafe", size = 14050453, upload-time = "2026-01-08T19:11:37.555Z" }, + { url = "https://files.pythonhosted.org/packages/ae/93/f36d89fa021543187f98991609ce6e47e24f35f008dfe1af01379d248a41/ruff-0.14.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a35c9da08562f1598ded8470fcfef2afb5cf881996e6c0a502ceb61f4bc9c8a3", size = 13757889, upload-time = "2026-01-08T19:12:07.094Z" }, + { url = "https://files.pythonhosted.org/packages/b7/9f/c7fb6ecf554f28709a6a1f2a7f74750d400979e8cd47ed29feeaa1bd4db8/ruff-0.14.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0f3727189a52179393ecf92ec7057c2210203e6af2676f08d92140d3e1ee72c1", size = 13955832, upload-time = "2026-01-08T19:11:55.064Z" }, + { url = "https://files.pythonhosted.org/packages/db/a0/153315310f250f76900a98278cf878c64dfb6d044e184491dd3289796734/ruff-0.14.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:eb09f849bd37147a789b85995ff734a6c4a095bed5fd1608c4f56afc3634cde2", size = 12586522, upload-time = "2026-01-08T19:11:35.356Z" }, + { url = "https://files.pythonhosted.org/packages/2f/2b/a73a2b6e6d2df1d74bf2b78098be1572191e54bec0e59e29382d13c3adc5/ruff-0.14.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c61782543c1231bf71041461c1f28c64b961d457d0f238ac388e2ab173d7ecb7", size = 12724637, upload-time = "2026-01-08T19:11:47.796Z" }, + { url = "https://files.pythonhosted.org/packages/f0/41/09100590320394401cd3c48fc718a8ba71c7ddb1ffd07e0ad6576b3a3df2/ruff-0.14.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:82ff352ea68fb6766140381748e1f67f83c39860b6446966cff48a315c3e2491", size = 13145837, upload-time = "2026-01-08T19:11:32.87Z" }, + { url = "https://files.pythonhosted.org/packages/3b/d8/e035db859d1d3edf909381eb8ff3e89a672d6572e9454093538fe6f164b0/ruff-0.14.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:728e56879df4ca5b62a9dde2dd0eb0edda2a55160c0ea28c4025f18c03f86984", size = 13850469, upload-time = "2026-01-08T19:12:11.694Z" }, + { url = "https://files.pythonhosted.org/packages/4e/02/bb3ff8b6e6d02ce9e3740f4c17dfbbfb55f34c789c139e9cd91985f356c7/ruff-0.14.11-py3-none-win32.whl", hash = "sha256:337c5dd11f16ee52ae217757d9b82a26400be7efac883e9e852646f1557ed841", size = 12851094, upload-time = "2026-01-08T19:11:45.163Z" }, + { url = "https://files.pythonhosted.org/packages/58/f1/90ddc533918d3a2ad628bc3044cdfc094949e6d4b929220c3f0eb8a1c998/ruff-0.14.11-py3-none-win_amd64.whl", hash = "sha256:f981cea63d08456b2c070e64b79cb62f951aa1305282974d4d5216e6e0178ae6", size = 14001379, upload-time = "2026-01-08T19:11:52.591Z" }, + { url = "https://files.pythonhosted.org/packages/c4/1c/1dbe51782c0e1e9cfce1d1004752672d2d4629ea46945d19d731ad772b3b/ruff-0.14.11-py3-none-win_arm64.whl", hash = "sha256:649fb6c9edd7f751db276ef42df1f3df41c38d67d199570ae2a7bd6cbc3590f0", size = 12938644, upload-time = "2026-01-08T19:11:50.027Z" }, ] [[package]] @@ -1582,19 +1582,19 @@ wheels = [ [[package]] name = "typos" -version = "1.40.0" +version = "1.42.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/f0/8d988732b10ef72ed82900b055590a210a5ae423b4088d17fa961305ed6b/typos-1.40.0.tar.gz", hash = "sha256:5cb1a04a6291fa1fa358ce6d8cd5b50e396d0a306466b792ac6c246066b1780f", size = 1765534, upload-time = "2025-11-26T20:54:53.792Z" } +sdist = { url = "https://files.pythonhosted.org/packages/93/86/df46b493667a4ce6d5a30b95c7cb4418c881b3e0b6a12b9e4cb7bc938002/typos-1.42.0.tar.gz", hash = "sha256:3e139df21326cfcde3b1520e4318d00181b94968fb5486af38ec02dbffc1f85a", size = 1783357, upload-time = "2026-01-07T21:36:28.509Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/dd/64ceee60d4d1d7d0c90dac8d3bb5ebfcc2e1d1e5b5166f3284abc4052e45/typos-1.40.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:71441cb06044baba29911e4b6500a85b2e915736d1fc0a54d5f575addb12a307", size = 3507274, upload-time = "2025-11-26T20:54:39.564Z" }, - { url = "https://files.pythonhosted.org/packages/18/db/64f7146b86e912041aafe275f627081e4bd005f71932f5280cf0c3944f2b/typos-1.40.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:269e411f342126b06f38936eba9d391a41442c17425e57068797c9e6997e3fca", size = 3391108, upload-time = "2025-11-26T20:54:41.462Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f1/1eead106cc0c025319d23ccff78aa7b9c86a8a918f62359180f119deb96b/typos-1.40.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78d4d7be7e6f61c1bbec01abd9ee2e08254f633b845a9d2c5786051832c3e0c1", size = 8215390, upload-time = "2025-11-26T20:54:43.01Z" }, - { url = "https://files.pythonhosted.org/packages/82/c9/dc027ec8819d1c652d80ac2c3b6216dcc4c6d198907e2c2ed29cd4710685/typos-1.40.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4dbc419aed7cd4b9e8ec71a28045a3b6262fa5a41170734a3fc4dfdf1e7d7a51", size = 7192543, upload-time = "2025-11-26T20:54:44.616Z" }, - { url = "https://files.pythonhosted.org/packages/1a/db/f6fef0f4d173f501b469a90ed3d462bf7e4301a28507b7914cefa1d78ca1/typos-1.40.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0701400559effc6806a043dac55e1b77fc09e540661bf4315eaf55a628138214", size = 7729297, upload-time = "2025-11-26T20:54:46.122Z" }, - { url = "https://files.pythonhosted.org/packages/fe/a4/bb5b415cd352168550170ba5bb7c6b1c53fe457084df5ff07488c525dca6/typos-1.40.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:41ed67ad7cba724841f72d5c7c69de20f79dbee52917fb1fb5f3efa327d44cd3", size = 7107127, upload-time = "2025-11-26T20:54:47.974Z" }, - { url = "https://files.pythonhosted.org/packages/1d/92/1a39cea9ba7369555ed3f540b48ed5fd6f059ec89e24fb87dd21df69bf2a/typos-1.40.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:47764e89fca194b77ff65741b1527210096e39984b2c460ba5bc4868ea05ea88", size = 8141765, upload-time = "2025-11-26T20:54:49.461Z" }, - { url = "https://files.pythonhosted.org/packages/09/64/7d28b539b6d09b59ed3ea13f54e74e0cb8409fff3174928ee2f98ca349fb/typos-1.40.0-py3-none-win32.whl", hash = "sha256:9cd19efd5a3abcc788770ffb9a070f39da0d97c4aadd7eaf471e744a02002464", size = 3065525, upload-time = "2025-11-26T20:54:51.112Z" }, - { url = "https://files.pythonhosted.org/packages/49/0a/e324e17a0407dfe2459ecd8c467b0b3953ec5c553bd552949fdc238bec91/typos-1.40.0-py3-none-win_amd64.whl", hash = "sha256:69c47f0b899bc62d87d6fc431824348782e76dca1867115976915a197b0a1fd2", size = 3254935, upload-time = "2025-11-26T20:54:52.458Z" }, + { url = "https://files.pythonhosted.org/packages/87/f0/9341ee077141e56380b8bc1ba340e3564274625ff3bb759118ae21e969e3/typos-1.42.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4d564efb2e8fe69c8e954a95071b88f109283a388bcab52b6a6d47132cb795c3", size = 3536717, upload-time = "2026-01-07T21:36:10.27Z" }, + { url = "https://files.pythonhosted.org/packages/51/4d/ffe57f3c9f0241f394e61d33d52627ebe3de77b21e8ad71ea6eefa66d099/typos-1.42.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7540f53c3de862aceca41fe6a5d06ee29310badb20329233dc1449cade208b2f", size = 3434045, upload-time = "2026-01-07T21:36:12.557Z" }, + { url = "https://files.pythonhosted.org/packages/d8/75/56a4b7a31cde768612489a141c56e636a8d926eb7f5112cd61978aa43d28/typos-1.42.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773aa98393ab50e7ebb268e57f6d8797044125c0ca6a8ca7b15a88f09a186913", size = 8289950, upload-time = "2026-01-07T21:36:14.607Z" }, + { url = "https://files.pythonhosted.org/packages/57/9c/168786a966ffe5ef0a07200fbff418f0d71656fc18bfe7c3cf72c7a22910/typos-1.42.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1adb94ab86f491d465de1b99a5ca75d1ac57d7744ac203a8c8924ba3f0f0858e", size = 7401544, upload-time = "2026-01-07T21:36:16.661Z" }, + { url = "https://files.pythonhosted.org/packages/84/57/f40b29a8542d64e5168d9d77dee016e5469c47095a9431741b9113db0f60/typos-1.42.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84620ff074f648e29ab5e8a2a241964517e1a6d00f66ffe7884470dd7a7f8cf5", size = 7784920, upload-time = "2026-01-07T21:36:18.785Z" }, + { url = "https://files.pythonhosted.org/packages/76/80/9c1f3f4b014c9c64c9b78c3229aa7327104e8af569c912d2490e04ce8c2e/typos-1.42.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f30a9808a0faf83f7cc7628a2629b4fe82193b44e67984c49caa8b3b7b6ef958", size = 7175153, upload-time = "2026-01-07T21:36:20.989Z" }, + { url = "https://files.pythonhosted.org/packages/05/8a/63f59deb228148d0912fa70b9546b6cc7dba141378e0099dbe9109f7938d/typos-1.42.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:33671f5f569a56bb91a72bc7018327e3b8862dfdc9ae2b95aa2ea165226b843d", size = 8209628, upload-time = "2026-01-07T21:36:23.031Z" }, + { url = "https://files.pythonhosted.org/packages/68/c9/f5bbd7a364dced4e03c205d174e98e6f9746c957950c09d5f21de9cad3bc/typos-1.42.0-py3-none-win32.whl", hash = "sha256:248d49aaeee9f4bc3acfd6f820872e1795aa0123e4978d0c195bda642a5300f3", size = 3103474, upload-time = "2026-01-07T21:36:24.881Z" }, + { url = "https://files.pythonhosted.org/packages/05/b9/f2f195708995aafc296e9ad1af3bc1806c9e0eae7564c20e8ba9dcb34a40/typos-1.42.0-py3-none-win_amd64.whl", hash = "sha256:4a93d34222b2059af351fc4624b0aa7c74616bb1eca67e113f71d76dea88b24c", size = 3289130, upload-time = "2026-01-07T21:36:26.873Z" }, ] [[package]] @@ -1728,13 +1728,13 @@ provides-extras = ["mcp"] [package.metadata.requires-dev] dev = [ - { name = "pyright", specifier = ">=1.1.407" }, - { name = "pytest", specifier = ">=9.0.1" }, + { name = "pyright", specifier = ">=1.1.408" }, + { name = "pytest", specifier = ">=9.0.2" }, { name = "pytest-codspeed", specifier = ">=4.2.0" }, { name = "pytest-rerunfailures", specifier = ">=16.1" }, - { name = "ruff", specifier = ">=0.14.6" }, + { name = "ruff", specifier = ">=0.14.11" }, { name = "syrupy", specifier = ">=5.0.0" }, - { name = "typos", specifier = ">=1.40.0" }, + { name = "typos", specifier = ">=1.42.0" }, ] [[package]]