From 8ee775d02cf3b93f5deb7319f28b6eeb8269b6cf Mon Sep 17 00:00:00 2001 From: Raventric <78981416+Ravencentric@users.noreply.github.com> Date: Sat, 6 Jul 2024 12:10:01 +0530 Subject: [PATCH] feat!: AniList.get() now accepts every parameter accepted by anilist (#21) * feat!: AniList.get() now accepts every parameter accepted by anilist BREAKING CHANGE: .search() is removed because it was redundant * chore: remove redundant test, add new tests * chore: format * fix: workaround pydantic bug * docs: update * fix: async client --- .pre-commit-config.yaml | 4 +- README.md | 100 +--- docs/api-reference/clients.md | 12 - docs/api-reference/enums.md | 23 +- docs/api-reference/exceptions.md | 119 +--- docs/api-reference/types.md | 18 +- docs/examples.md | 78 +-- docs/index.md | 98 +--- mkdocs.yml | 5 +- poetry.lock | 888 +++++++++++++++-------------- pyproject.toml | 31 +- src/pyanilist/__init__.py | 74 +-- src/pyanilist/_clients/__init__.py | 2 + src/pyanilist/_clients/_async.py | 495 +++++++++------- src/pyanilist/_clients/_sync.py | 493 +++++++++------- src/pyanilist/_compat.py | 2 + src/pyanilist/_enums.py | 47 ++ src/pyanilist/_exceptions.py | 67 --- src/pyanilist/_models.py | 20 +- src/pyanilist/_parser.py | 127 +++++ src/pyanilist/_query.py | 145 ++++- src/pyanilist/_types.py | 49 +- src/pyanilist/_utils.py | 90 +++ src/pyanilist/_version.py | 11 +- tests/test_anilist.py | 46 +- tests/test_async_anilist.py | 46 +- tests/test_async_exceptions.py | 6 +- tests/test_exceptions.py | 6 +- tests/test_models.py | 5 + tests/test_utils.py | 86 ++- 30 files changed, 1690 insertions(+), 1503 deletions(-) delete mode 100644 src/pyanilist/_exceptions.py create mode 100644 src/pyanilist/_parser.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 224901b..31f1414 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,12 @@ --- repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.5 + rev: v0.5.0 hooks: - id: ruff args: ['--fix'] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.5 + rev: v0.5.0 hooks: - id: ruff-format - repo: https://github.com/python-poetry/poetry diff --git a/README.md b/README.md index 8f8a8cf..e53b809 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,7 @@ ## About - Supports both sync and async. -- Provides easy access to almost every field present in AniList's `Media` type. -- Only supports querying the `Media` type +- Only supports querying the `Media` type. ## Installation @@ -45,91 +44,18 @@ pip install pyanilist ## Usage -1. `AniList()` - Synchronous class - - `search()` - Search a media - - ```py - from pyanilist import AniList, MediaType - - media = AniList().search("Attack on Titan", type=MediaType.ANIME) - - print(media.title.romaji) - """ - Shingeki no Kyojin - """ - print(media.site_url) - """ - https://anilist.co/anime/16498 - """ - print(media.episodes) - """ - 25 - """ - ``` - - `get()` - Get a media by it's AniList ID - - ```py - from pyanilist import AniList - - media = AniList().get(21459) - - print(media.title.english) - """ - My Hero Academia - """ - print(media.site_url) - """ - https://anilist.co/anime/21459 - """ - print(media.episodes) - """ - 13 - """ - ``` - -2. `AsyncAniList()` - Asynchronous class - - `search()` - Search a media - - ```py - import asyncio - from pyanilist import AsyncAniList, MediaType - - media = asyncio.run(AsyncAniList().search("Attack on Titan", type=MediaType.ANIME)) - - print(media.title.romaji) - """ - Shingeki no Kyojin - """ - print(media.site_url) - """ - https://anilist.co/anime/16498 - """ - print(media.episodes) - """ - 25 - """ - ``` - - `get()` - Get a media by it's AniList ID - - ```py - import asyncio - from pyanilist import AsyncAniList - - media = asyncio.run(AsyncAniList().get(21459)) - - print(media.title.english) - """ - My Hero Academia - """ - print(media.site_url) - """ - https://anilist.co/anime/21459 - """ - print(media.episodes) - """ - 13 - """ - ``` +```py +from pyanilist import AniList + +media = AniList().get("My Hero Academia") + +print(media.title.romaji) +#> Boku no Hero Academia +print(media.site_url) +#> https://anilist.co/anime/21459 +print(media.episodes) +#> 13 +``` ## Docs diff --git a/docs/api-reference/clients.md b/docs/api-reference/clients.md index 06f94a4..ceb60e5 100644 --- a/docs/api-reference/clients.md +++ b/docs/api-reference/clients.md @@ -1,14 +1,2 @@ ::: pyanilist.AniList - options: - members: - - __init__ - - search - - get - - ::: pyanilist.AsyncAniList - options: - members: - - __init__ - - search - - get \ No newline at end of file diff --git a/docs/api-reference/enums.md b/docs/api-reference/enums.md index c2a331a..3c43a48 100644 --- a/docs/api-reference/enums.md +++ b/docs/api-reference/enums.md @@ -1,10 +1,31 @@ ::: pyanilist._enums.BaseStrEnum ::: pyanilist._enums.CharacterRole + options: + members: true ::: pyanilist._enums.ExternalLinkType + options: + members: true ::: pyanilist._enums.MediaFormat + options: + members: true ::: pyanilist._enums.MediaRankType + options: + members: true ::: pyanilist._enums.MediaRelation + options: + members: true ::: pyanilist._enums.MediaSeason + options: + members: true +::: pyanilist._enums.MediaSort + options: + members: true ::: pyanilist._enums.MediaSource + options: + members: true ::: pyanilist._enums.MediaStatus -::: pyanilist._enums.MediaType \ No newline at end of file + options: + members: true +::: pyanilist._enums.MediaType + options: + members: true diff --git a/docs/api-reference/exceptions.md b/docs/api-reference/exceptions.md index 03e9f18..c6ad7ae 100644 --- a/docs/api-reference/exceptions.md +++ b/docs/api-reference/exceptions.md @@ -1,117 +1,4 @@ -!!! note - PyAniList simply re-exports exceptions from - [`httpx`](https://www.python-httpx.org/exceptions/) and - [`pydantic`](https://docs.pydantic.dev/latest/api/pydantic_core/#pydantic_core.ValidationError) for convenience. +PyAniList doesn't raise any custom exceptions of it's own. The two most likely errors you'll encounter will be either of these: -::: pyanilist._exceptions.CloseError - options: - show_source: false - members: false -::: pyanilist._exceptions.ConnectError - options: - show_source: false - members: false -::: pyanilist._exceptions.ConnectTimeout - options: - show_source: false - members: false -::: pyanilist._exceptions.CookieConflict - options: - show_source: false - members: false -::: pyanilist._exceptions.DecodingError - options: - show_source: false - members: false -::: pyanilist._exceptions.HTTPError - options: - show_source: false - members: false -::: pyanilist._exceptions.HTTPStatusError - options: - show_source: false - members: false -::: pyanilist._exceptions.InvalidURL - options: - show_source: false - members: false -::: pyanilist._exceptions.LocalProtocolError - options: - show_source: false - members: false -::: pyanilist._exceptions.NetworkError - options: - show_source: false - members: false -::: pyanilist._exceptions.PoolTimeout - options: - show_source: false - members: false -::: pyanilist._exceptions.ProtocolError - options: - show_source: false - members: false -::: pyanilist._exceptions.ProxyError - options: - show_source: false - members: false -::: pyanilist._exceptions.ReadError - options: - show_source: false - members: false -::: pyanilist._exceptions.ReadTimeout - options: - show_source: false - members: false -::: pyanilist._exceptions.RemoteProtocolError - options: - show_source: false - members: false -::: pyanilist._exceptions.RequestError - options: - show_source: false - members: false -::: pyanilist._exceptions.RequestNotRead - options: - show_source: false - members: false -::: pyanilist._exceptions.ResponseNotRead - options: - show_source: false - members: false -::: pyanilist._exceptions.StreamClosed - options: - show_source: false - members: false -::: pyanilist._exceptions.StreamConsumed - options: - show_source: false - members: false -::: pyanilist._exceptions.StreamError - options: - show_source: false - members: false -::: pyanilist._exceptions.TimeoutException - options: - show_source: false - members: false -::: pyanilist._exceptions.TooManyRedirects - options: - show_source: false - members: false -::: pyanilist._exceptions.UnsupportedProtocol - options: - show_source: false - members: false -::: pyanilist._exceptions.WriteError - options: - show_source: false - members: false -::: pyanilist._exceptions.WriteTimeout - options: - show_source: false - members: false -::: pyanilist._exceptions.ValidationError - options: - show_source: false - members: false \ No newline at end of file +- `pyanilist.HTTPStatusError` - Alias for [`httpx.HTTPStatusError`](https://www.python-httpx.org/exceptions/). Raised if a request returns a non 2xx status code. +- `pyanilist.ValidationError` - Alias for [`pydantic.ValidationError`](https://docs.pydantic.dev/latest/errors/validation_errors/). Raised if an input is invalid. diff --git a/docs/api-reference/types.md b/docs/api-reference/types.md index bb124b3..1db306c 100644 --- a/docs/api-reference/types.md +++ b/docs/api-reference/types.md @@ -1,15 +1,5 @@ -!!! note - Additionally, PyAniList also uses and exports the following [pydantic](https://docs.pydantic.dev/latest/) types for convenience: - - - [HttpUrl](https://docs.pydantic.dev/latest/api/networks/#pydantic.networks.HttpUrl) - - - [Color](https://docs.pydantic.dev/latest/api/pydantic_extra_types_color/#pydantic_extra_types.color.Color) - - - [CountryAlpha2 as CountryCode](https://docs.pydantic.dev/latest/api/pydantic_extra_types_country/#pydantic_extra_types.country.CountryAlpha2) - -::: pyanilist._types.AniListID -::: pyanilist._types.AniListTitle -::: pyanilist._types.AniListYear ::: pyanilist._types.YearsActive -::: pyanilist._types.HTTPXAsyncClientKwargs -::: pyanilist._types.HTTPXClientKwargs + options: + members: true +::: pyanilist._types.FuzzyDateInt +::: pyanilist._types.Iterable diff --git a/docs/examples.md b/docs/examples.md index c49b73a..3157fdc 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -1,19 +1,17 @@ # Examples !!! note - These examples use `pyanilist.AniList` but you can do the same thing with `pyanilist.AsyncAniList` since they share the same methods + These examples use [`pyanilist.AniList`][pyanilist.AniList] but you can do the same thing with [`pyanilist.AsyncAniList`][pyanilist.AsyncAniList] since they share the same methods ## AniList ID ```py from pyanilist import AniList -media = AniList().get(16498) +media = AniList().get(id=16498) print(media.title.english) -""" -Attack on Titan -""" +#> Attack on Titan ``` ## Search @@ -21,27 +19,21 @@ Attack on Titan ```py from pyanilist import AniList -media = AniList().search("Attack on titan") +media = AniList().get("Attack on titan") print(media.format) -""" -TV -""" +#> TV print(media.title.romaji) -""" -Shingeki no Kyojin -""" +#> Shingeki no Kyojin print(media.episodes) -""" -25 -""" +#> 25 ``` ## Search with constraints ```py from pyanilist import AniList, MediaSeason, MediaType, MediaStatus, MediaFormat -media = AniList().search( +media = AniList().get( "My Hero Academia", season=MediaSeason.SPRING, season_year=2016, @@ -51,17 +43,11 @@ media = AniList().search( ) print(media.title.romaji) -""" -Boku no Hero Academia -""" +#> Boku no Hero Academia print(media.start_date.iso_format()) -""" -2016-04-03 -""" +#> 2016-04-03 print(media.site_url) -""" -https://anilist.co/anime/21459 -""" +#> https://anilist.co/anime/21459 ``` ## Related media @@ -69,21 +55,17 @@ https://anilist.co/anime/21459 ```py from pyanilist import AniList -media = AniList().search("violet evergarden") +media = AniList().get("violet evergarden") print(media.format) -""" -TV -""" +#> TV for relation in media.relations: print(f"{relation.title.romaji} ({relation.format}) - {relation.site_url}") -""" -Violet Evergarden: Kitto "Ai" wo Shiru Hi ga Kuru no Darou (OVA) - https://anilist.co/anime/101432 -Violet Evergarden (NOVEL) - https://anilist.co/manga/97298 -Violet Evergarden Gaiden: Eien to Jidou Shuki Ningyou (MOVIE) - https://anilist.co/anime/109190 -Violet Evergarden CM (ONA) - https://anilist.co/anime/154164 -""" + #> Violet Evergarden: Kitto "Ai" wo Shiru Hi ga Kuru no Darou (OVA) - https://anilist.co/anime/101432 + #> Violet Evergarden (NOVEL) - https://anilist.co/manga/97298 + #> Violet Evergarden Gaiden: Eien to Jidou Shuki Ningyou (MOVIE) - https://anilist.co/anime/109190 + #> Violet Evergarden CM (ONA) - https://anilist.co/anime/154164 ``` ## Characters @@ -91,7 +73,7 @@ Violet Evergarden CM (ONA) - https://anilist.co/anime/154164 ```py from pyanilist import AniList, CharacterRole -media = AniList().get(20954) +media = AniList().get(id=20954) all_characters = [character.name.full for character in media.characters] @@ -99,19 +81,15 @@ all_characters = [character.name.full for character in media.characters] main_characters = [character.name.full for character in media.characters if character.role is CharacterRole.MAIN] print(all_characters) -""" -['Shouya Ishida', 'Shouko Nishimiya', 'Yuzuru Nishimiya', 'Naoka Ueno', 'Miyako Ishida', 'Maria Ishida', 'Miki Kawai', 'Satoshi Mashiba', 'Tomohiro Nagatsuka', 'Yaeko Nishimiya', 'Ito Nishimiya', 'Miyoko Sahara', 'Kazuki Shimada', 'Takeuchi', 'Pedro', 'Keisuke Hirose', 'Ishida no Ane', 'Kita'] -""" +#> ['Shouya Ishida', 'Shouko Nishimiya', 'Yuzuru Nishimiya', 'Naoka Ueno', 'Miyako Ishida', 'Maria Ishida', 'Miki Kawai', 'Satoshi Mashiba', 'Tomohiro Nagatsuka', 'Yaeko Nishimiya', 'Ito Nishimiya', 'Miyoko Sahara', 'Kazuki Shimada', 'Takeuchi', 'Pedro', 'Keisuke Hirose', 'Ishida no Ane', 'Kita'] print(main_characters) -""" -['Shouya Ishida', 'Shouko Nishimiya'] -""" +#> ['Shouya Ishida', 'Shouko Nishimiya'] ``` ## Retries -AniList API is flaky, sometimes it might return an error for a perfectly valid request. `pyanilist` handles this by simply retrying failed requests a specified number of times (default is 5) before raising an error. Every subsequent retry also adds an additional one-second delay between requests. +AniList API is flaky, sometimes it might return an error for a perfectly valid request. `pyanilist` handles this by simply retrying failed requests a specified number of times (default is 5) before raising an error. Every subsequent retry also adds an additional delay between requests. ```py from pyanilist import AniList @@ -119,17 +97,15 @@ from pyanilist import AniList # Configure the number of retries. Setting it to 1 disables retrying. anilist = AniList(retries=1) -media = anilist.search("violet evergarden") +media = anilist.get("violet evergarden") print(f"{media.title.english} - {media.site_url}") -""" -Violet Evergarden - https://anilist.co/anime/21827 -""" +#> Violet Evergarden - https://anilist.co/anime/21827 ``` ## Client -`pyanilist` gives you direct access to the internal [`httpx.Client()`](https://www.python-httpx.org/api/#client) used to send the POST request. +`pyanilist` lets you pass keyword arguments to the internal [`httpx.Client`](https://www.python-httpx.org/api/#client) used to send the POST request. ```py from pyanilist import AniList @@ -139,10 +115,8 @@ headers = {'user-agent': 'my-app/0.0.1'} # You can pass any httpx.Client() keyword argument to AniList() anilist = AniList(headers=headers) -media = anilist.get(105333) +media = anilist.get(id=105333) print(media.title.english) -""" -Dr. STONE -""" +#> Dr. STONE ``` diff --git a/docs/index.md b/docs/index.md index 61aabe3..0f65f75 100644 --- a/docs/index.md +++ b/docs/index.md @@ -28,8 +28,7 @@ ## About - Supports both sync and async. -- Provides easy access to almost every field present in AniList's `Media` type. -- Only supports querying the `Media` type +- Only supports querying the `Media` type. ## Installation @@ -41,93 +40,18 @@ pip install pyanilist ## Usage -PyAniList offers two main classes: +```py +from pyanilist import AniList -1. `AniList()` - Synchronous class - - `search()` - Search a media +media = AniList().get("My Hero Academia") - ```py - from pyanilist import AniList, MediaType - - media = AniList().search("Attack on Titan", type=MediaType.ANIME) - - print(media.title.romaji) - """ - Shingeki no Kyojin - """ - print(media.site_url) - """ - https://anilist.co/anime/16498 - """ - print(media.episodes) - """ - 25 - """ - ``` - - `get()` - Get a media by it's AniList ID - - ```py - from pyanilist import AniList - - media = AniList().get(21459) - - print(media.title.english) - """ - My Hero Academia - """ - print(media.site_url) - """ - https://anilist.co/anime/21459 - """ - print(media.episodes) - """ - 13 - """ - ``` - -2. `AsyncAniList()` - Asynchronous class - - `search()` - Search a media - - ```py - import asyncio - from pyanilist import AsyncAniList, MediaType - - media = asyncio.run(AsyncAniList().search("Attack on Titan", type=MediaType.ANIME)) - - print(media.title.romaji) - """ - Shingeki no Kyojin - """ - print(media.site_url) - """ - https://anilist.co/anime/16498 - """ - print(media.episodes) - """ - 25 - """ - ``` - - `get()` - Get a media by it's AniList ID - - ```py - import asyncio - from pyanilist import AsyncAniList - - media = asyncio.run(AsyncAniList().get(21459)) - - print(media.title.english) - """ - My Hero Academia - """ - print(media.site_url) - """ - https://anilist.co/anime/21459 - """ - print(media.episodes) - """ - 13 - """ - ``` +print(media.title.romaji) +#> Boku no Hero Academia +print(media.site_url) +#> https://anilist.co/anime/21459 +print(media.episodes) +#> 13 +``` ## License diff --git a/mkdocs.yml b/mkdocs.yml index a9d1a64..c6dcc99 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -47,7 +47,6 @@ plugins: options: allow_inspection: false docstring_style: numpy - members: true show_root_heading: true show_root_full_path: false show_signature_annotations: true @@ -56,9 +55,7 @@ plugins: show_symbol_type_toc: true signature_crossrefs: true merge_init_into_class: true - preload_modules: - - httpx - - pydantic + filters: ["!^_", "^__init__$"] markdown_extensions: - md_in_html diff --git a/poetry.lock b/poetry.lock index cfd36c0..bae9903 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,25 +1,25 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "annotated-types" -version = "0.6.0" +version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" files = [ - {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, - {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] [[package]] name = "anyio" -version = "4.3.0" +version = "4.4.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" files = [ - {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, - {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, + {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, + {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, ] [package.dependencies] @@ -35,13 +35,13 @@ trio = ["trio (>=0.23)"] [[package]] name = "babel" -version = "2.14.0" +version = "2.15.0" description = "Internationalization utilities" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, - {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, + {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"}, + {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, ] [package.extras] @@ -71,13 +71,13 @@ files = [ [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.7.4" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, ] [[package]] @@ -217,63 +217,63 @@ files = [ [[package]] name = "coverage" -version = "7.5.0" +version = "7.5.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"}, - {file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"}, - {file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"}, - {file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"}, - {file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"}, - {file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"}, - {file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"}, - {file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"}, - {file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"}, - {file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"}, - {file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"}, - {file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"}, - {file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"}, - {file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"}, - {file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"}, - {file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"}, - {file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"}, - {file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"}, - {file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"}, - {file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"}, - {file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"}, - {file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"}, + {file = "coverage-7.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6cfb5a4f556bb51aba274588200a46e4dd6b505fb1a5f8c5ae408222eb416f99"}, + {file = "coverage-7.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2174e7c23e0a454ffe12267a10732c273243b4f2d50d07544a91198f05c48f47"}, + {file = "coverage-7.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2214ee920787d85db1b6a0bd9da5f8503ccc8fcd5814d90796c2f2493a2f4d2e"}, + {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1137f46adb28e3813dec8c01fefadcb8c614f33576f672962e323b5128d9a68d"}, + {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b385d49609f8e9efc885790a5a0e89f2e3ae042cdf12958b6034cc442de428d3"}, + {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b4a474f799456e0eb46d78ab07303286a84a3140e9700b9e154cfebc8f527016"}, + {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5cd64adedf3be66f8ccee418473c2916492d53cbafbfcff851cbec5a8454b136"}, + {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e564c2cf45d2f44a9da56f4e3a26b2236504a496eb4cb0ca7221cd4cc7a9aca9"}, + {file = "coverage-7.5.4-cp310-cp310-win32.whl", hash = "sha256:7076b4b3a5f6d2b5d7f1185fde25b1e54eb66e647a1dfef0e2c2bfaf9b4c88c8"}, + {file = "coverage-7.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:018a12985185038a5b2bcafab04ab833a9a0f2c59995b3cec07e10074c78635f"}, + {file = "coverage-7.5.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db14f552ac38f10758ad14dd7b983dbab424e731588d300c7db25b6f89e335b5"}, + {file = "coverage-7.5.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3257fdd8e574805f27bb5342b77bc65578e98cbc004a92232106344053f319ba"}, + {file = "coverage-7.5.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a6612c99081d8d6134005b1354191e103ec9705d7ba2754e848211ac8cacc6b"}, + {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d45d3cbd94159c468b9b8c5a556e3f6b81a8d1af2a92b77320e887c3e7a5d080"}, + {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed550e7442f278af76d9d65af48069f1fb84c9f745ae249c1a183c1e9d1b025c"}, + {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a892be37ca35eb5019ec85402c3371b0f7cda5ab5056023a7f13da0961e60da"}, + {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8192794d120167e2a64721d88dbd688584675e86e15d0569599257566dec9bf0"}, + {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:820bc841faa502e727a48311948e0461132a9c8baa42f6b2b84a29ced24cc078"}, + {file = "coverage-7.5.4-cp311-cp311-win32.whl", hash = "sha256:6aae5cce399a0f065da65c7bb1e8abd5c7a3043da9dceb429ebe1b289bc07806"}, + {file = "coverage-7.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:d2e344d6adc8ef81c5a233d3a57b3c7d5181f40e79e05e1c143da143ccb6377d"}, + {file = "coverage-7.5.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:54317c2b806354cbb2dc7ac27e2b93f97096912cc16b18289c5d4e44fc663233"}, + {file = "coverage-7.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:042183de01f8b6d531e10c197f7f0315a61e8d805ab29c5f7b51a01d62782747"}, + {file = "coverage-7.5.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6bb74ed465d5fb204b2ec41d79bcd28afccf817de721e8a807d5141c3426638"}, + {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3d45ff86efb129c599a3b287ae2e44c1e281ae0f9a9bad0edc202179bcc3a2e"}, + {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5013ed890dc917cef2c9f765c4c6a8ae9df983cd60dbb635df8ed9f4ebc9f555"}, + {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1014fbf665fef86cdfd6cb5b7371496ce35e4d2a00cda501cf9f5b9e6fced69f"}, + {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3684bc2ff328f935981847082ba4fdc950d58906a40eafa93510d1b54c08a66c"}, + {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:581ea96f92bf71a5ec0974001f900db495488434a6928a2ca7f01eee20c23805"}, + {file = "coverage-7.5.4-cp312-cp312-win32.whl", hash = "sha256:73ca8fbc5bc622e54627314c1a6f1dfdd8db69788f3443e752c215f29fa87a0b"}, + {file = "coverage-7.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:cef4649ec906ea7ea5e9e796e68b987f83fa9a718514fe147f538cfeda76d7a7"}, + {file = "coverage-7.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdd31315fc20868c194130de9ee6bfd99755cc9565edff98ecc12585b90be882"}, + {file = "coverage-7.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:02ff6e898197cc1e9fa375581382b72498eb2e6d5fc0b53f03e496cfee3fac6d"}, + {file = "coverage-7.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d05c16cf4b4c2fc880cb12ba4c9b526e9e5d5bb1d81313d4d732a5b9fe2b9d53"}, + {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5986ee7ea0795a4095ac4d113cbb3448601efca7f158ec7f7087a6c705304e4"}, + {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5df54843b88901fdc2f598ac06737f03d71168fd1175728054c8f5a2739ac3e4"}, + {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ab73b35e8d109bffbda9a3e91c64e29fe26e03e49addf5b43d85fc426dde11f9"}, + {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:aea072a941b033813f5e4814541fc265a5c12ed9720daef11ca516aeacd3bd7f"}, + {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:16852febd96acd953b0d55fc842ce2dac1710f26729b31c80b940b9afcd9896f"}, + {file = "coverage-7.5.4-cp38-cp38-win32.whl", hash = "sha256:8f894208794b164e6bd4bba61fc98bf6b06be4d390cf2daacfa6eca0a6d2bb4f"}, + {file = "coverage-7.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:e2afe743289273209c992075a5a4913e8d007d569a406ffed0bd080ea02b0633"}, + {file = "coverage-7.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b95c3a8cb0463ba9f77383d0fa8c9194cf91f64445a63fc26fb2327e1e1eb088"}, + {file = "coverage-7.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d7564cc09dd91b5a6001754a5b3c6ecc4aba6323baf33a12bd751036c998be4"}, + {file = "coverage-7.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44da56a2589b684813f86d07597fdf8a9c6ce77f58976727329272f5a01f99f7"}, + {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e16f3d6b491c48c5ae726308e6ab1e18ee830b4cdd6913f2d7f77354b33f91c8"}, + {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbc5958cb471e5a5af41b0ddaea96a37e74ed289535e8deca404811f6cb0bc3d"}, + {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a04e990a2a41740b02d6182b498ee9796cf60eefe40cf859b016650147908029"}, + {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ddbd2f9713a79e8e7242d7c51f1929611e991d855f414ca9996c20e44a895f7c"}, + {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b1ccf5e728ccf83acd313c89f07c22d70d6c375a9c6f339233dcf792094bcbf7"}, + {file = "coverage-7.5.4-cp39-cp39-win32.whl", hash = "sha256:56b4eafa21c6c175b3ede004ca12c653a88b6f922494b023aeb1e836df953ace"}, + {file = "coverage-7.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:65e528e2e921ba8fd67d9055e6b9f9e34b21ebd6768ae1c1723f4ea6ace1234d"}, + {file = "coverage-7.5.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:79b356f3dd5b26f3ad23b35c75dbdaf1f9e2450b6bcefc6d0825ea0aa3f86ca5"}, + {file = "coverage-7.5.4.tar.gz", hash = "sha256:a44963520b069e12789d0faea4e9fdb1e410cdc4aab89d94f7f55cbb7fef0353"}, ] [package.extras] @@ -320,18 +320,18 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.13.4" +version = "3.15.4" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"}, - {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"}, + {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, + {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] typing = ["typing-extensions (>=4.8)"] [[package]] @@ -353,13 +353,13 @@ dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "griffe" -version = "0.44.0" +version = "0.47.0" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.8" files = [ - {file = "griffe-0.44.0-py3-none-any.whl", hash = "sha256:8a4471c469ba980b87c843f1168850ce39d0c1d0c7be140dca2480f76c8e5446"}, - {file = "griffe-0.44.0.tar.gz", hash = "sha256:34aee1571042f9bf00529bc715de4516fb6f482b164e90d030300601009e0223"}, + {file = "griffe-0.47.0-py3-none-any.whl", hash = "sha256:07a2fd6a8c3d21d0bbb0decf701d62042ccc8a576645c7f8799fe1f10de2b2de"}, + {file = "griffe-0.47.0.tar.gz", hash = "sha256:95119a440a3c932b13293538bdbc405bee4c36428547553dc6b327e7e7d35e5a"}, ] [package.dependencies] @@ -458,22 +458,22 @@ files = [ [[package]] name = "importlib-metadata" -version = "7.1.0" +version = "8.0.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, - {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, + {file = "importlib_metadata-8.0.0-py3-none-any.whl", hash = "sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f"}, + {file = "importlib_metadata-8.0.0.tar.gz", hash = "sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "iniconfig" @@ -488,13 +488,13 @@ files = [ [[package]] name = "jinja2" -version = "3.1.3" +version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, - {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [package.dependencies] @@ -603,34 +603,34 @@ files = [ [[package]] name = "mkdocs" -version = "1.5.3" +version = "1.6.0" description = "Project documentation with Markdown." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "mkdocs-1.5.3-py3-none-any.whl", hash = "sha256:3b3a78e736b31158d64dbb2f8ba29bd46a379d0c6e324c2246c3bc3d2189cfc1"}, - {file = "mkdocs-1.5.3.tar.gz", hash = "sha256:eb7c99214dcb945313ba30426c2451b735992c73c2e10838f76d09e39ff4d0e2"}, + {file = "mkdocs-1.6.0-py3-none-any.whl", hash = "sha256:1eb5cb7676b7d89323e62b56235010216319217d4af5ddc543a91beb8d125ea7"}, + {file = "mkdocs-1.6.0.tar.gz", hash = "sha256:a73f735824ef83a4f3bcb7a231dcab23f5a838f88b7efc54a0eef5fbdbc3c512"}, ] [package.dependencies] click = ">=7.0" colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} ghp-import = ">=1.0" -importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} jinja2 = ">=2.11.1" -markdown = ">=3.2.1" +markdown = ">=3.3.6" markupsafe = ">=2.0.1" mergedeep = ">=1.3.4" +mkdocs-get-deps = ">=0.2.0" packaging = ">=20.5" pathspec = ">=0.11.1" -platformdirs = ">=2.2.0" pyyaml = ">=5.1" pyyaml-env-tag = ">=0.1" watchdog = ">=2.0" [package.extras] i18n = ["babel (>=2.9.0)"] -min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pathspec (==0.11.1)", "platformdirs (==2.2.0)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] [[package]] name = "mkdocs-autorefs" @@ -648,15 +648,32 @@ Markdown = ">=3.3" markupsafe = ">=2.0.1" mkdocs = ">=1.1" +[[package]] +name = "mkdocs-get-deps" +version = "0.2.0" +description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, + {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} +mergedeep = ">=1.3.4" +platformdirs = ">=2.2.0" +pyyaml = ">=5.1" + [[package]] name = "mkdocs-material" -version = "9.5.18" +version = "9.5.28" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.18-py3-none-any.whl", hash = "sha256:1e0e27fc9fe239f9064318acf548771a4629d5fd5dfd45444fd80a953fe21eb4"}, - {file = "mkdocs_material-9.5.18.tar.gz", hash = "sha256:a43f470947053fa2405c33995f282d24992c752a50114f23f30da9d8d0c57e62"}, + {file = "mkdocs_material-9.5.28-py3-none-any.whl", hash = "sha256:ff48b11b2a9f705dd210409ec3b418ab443dd36d96915bcba45a41f10ea27bfd"}, + {file = "mkdocs_material-9.5.28.tar.gz", hash = "sha256:9cba305283ad1600e3d0a67abe72d7a058b54793b47be39930911a588fe0336b"}, ] [package.dependencies] @@ -664,7 +681,7 @@ babel = ">=2.10,<3.0" colorama = ">=0.4,<1.0" jinja2 = ">=3.0,<4.0" markdown = ">=3.2,<4.0" -mkdocs = ">=1.5.3,<1.6.0" +mkdocs = ">=1.6,<2.0" mkdocs-material-extensions = ">=1.3,<2.0" paginate = ">=0.5,<1.0" pygments = ">=2.16,<3.0" @@ -690,13 +707,13 @@ files = [ [[package]] name = "mkdocstrings" -version = "0.24.3" +version = "0.25.1" description = "Automatic documentation from sources, for MkDocs." optional = false python-versions = ">=3.8" files = [ - {file = "mkdocstrings-0.24.3-py3-none-any.whl", hash = "sha256:5c9cf2a32958cd161d5428699b79c8b0988856b0d4a8c5baf8395fc1bf4087c3"}, - {file = "mkdocstrings-0.24.3.tar.gz", hash = "sha256:f327b234eb8d2551a306735436e157d0a22d45f79963c60a8b585d5f7a94c1d2"}, + {file = "mkdocstrings-0.25.1-py3-none-any.whl", hash = "sha256:da01fcc2670ad61888e8fe5b60afe9fee5781017d67431996832d63e887c2e51"}, + {file = "mkdocstrings-0.25.1.tar.gz", hash = "sha256:c3a2515f31577f311a9ee58d089e4c51fc6046dbd9e9b4c3de4c3194667fe9bf"}, ] [package.dependencies] @@ -719,53 +736,53 @@ python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] [[package]] name = "mkdocstrings-python" -version = "1.10.0" +version = "1.10.5" description = "A Python handler for mkdocstrings." optional = false python-versions = ">=3.8" files = [ - {file = "mkdocstrings_python-1.10.0-py3-none-any.whl", hash = "sha256:ba833fbd9d178a4b9d5cb2553a4df06e51dc1f51e41559a4d2398c16a6f69ecc"}, - {file = "mkdocstrings_python-1.10.0.tar.gz", hash = "sha256:71678fac657d4d2bb301eed4e4d2d91499c095fd1f8a90fa76422a87a5693828"}, + {file = "mkdocstrings_python-1.10.5-py3-none-any.whl", hash = "sha256:92e3c588ef1b41151f55281d075de7558dd8092e422cb07a65b18ee2b0863ebb"}, + {file = "mkdocstrings_python-1.10.5.tar.gz", hash = "sha256:acdc2a98cd9d46c7ece508193a16ca03ccabcb67520352b7449f84b57c162bdf"}, ] [package.dependencies] -griffe = ">=0.44" -mkdocstrings = ">=0.24.2" +griffe = ">=0.47" +mkdocstrings = ">=0.25" [[package]] name = "mypy" -version = "1.9.0" +version = "1.10.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, - {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, - {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, - {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, - {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, - {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, - {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, - {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, - {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, - {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, - {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, - {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, - {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, - {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, - {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, - {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, - {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, - {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, - {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, + {file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"}, + {file = "mypy-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51a46974340baaa4145363b9e051812a2446cf583dfaeba124af966fa44593f7"}, + {file = "mypy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:901c89c2d67bba57aaaca91ccdb659aa3a312de67f23b9dfb059727cce2e2e0a"}, + {file = "mypy-1.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0cd62192a4a32b77ceb31272d9e74d23cd88c8060c34d1d3622db3267679a5d9"}, + {file = "mypy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a2cbc68cb9e943ac0814c13e2452d2046c2f2b23ff0278e26599224cf164e78d"}, + {file = "mypy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd6f629b67bb43dc0d9211ee98b96d8dabc97b1ad38b9b25f5e4c4d7569a0c6a"}, + {file = "mypy-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1bbb3a6f5ff319d2b9d40b4080d46cd639abe3516d5a62c070cf0114a457d84"}, + {file = "mypy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8edd4e9bbbc9d7b79502eb9592cab808585516ae1bcc1446eb9122656c6066f"}, + {file = "mypy-1.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6166a88b15f1759f94a46fa474c7b1b05d134b1b61fca627dd7335454cc9aa6b"}, + {file = "mypy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bb9cd11c01c8606a9d0b83ffa91d0b236a0e91bc4126d9ba9ce62906ada868e"}, + {file = "mypy-1.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d8681909f7b44d0b7b86e653ca152d6dff0eb5eb41694e163c6092124f8246d7"}, + {file = "mypy-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:378c03f53f10bbdd55ca94e46ec3ba255279706a6aacaecac52ad248f98205d3"}, + {file = "mypy-1.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bacf8f3a3d7d849f40ca6caea5c055122efe70e81480c8328ad29c55c69e93e"}, + {file = "mypy-1.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:701b5f71413f1e9855566a34d6e9d12624e9e0a8818a5704d74d6b0402e66c04"}, + {file = "mypy-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c4c2992f6ea46ff7fce0072642cfb62af7a2484efe69017ed8b095f7b39ef31"}, + {file = "mypy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:604282c886497645ffb87b8f35a57ec773a4a2721161e709a4422c1636ddde5c"}, + {file = "mypy-1.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37fd87cab83f09842653f08de066ee68f1182b9b5282e4634cdb4b407266bade"}, + {file = "mypy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8addf6313777dbb92e9564c5d32ec122bf2c6c39d683ea64de6a1fd98b90fe37"}, + {file = "mypy-1.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cc3ca0a244eb9a5249c7c583ad9a7e881aa5d7b73c35652296ddcdb33b2b9c7"}, + {file = "mypy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:1b3a2ffce52cc4dbaeee4df762f20a2905aa171ef157b82192f2e2f368eec05d"}, + {file = "mypy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe85ed6836165d52ae8b88f99527d3d1b2362e0cb90b005409b8bed90e9059b3"}, + {file = "mypy-1.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2ae450d60d7d020d67ab440c6e3fae375809988119817214440033f26ddf7bf"}, + {file = "mypy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be84c06e6abd72f960ba9a71561c14137a583093ffcf9bbfaf5e613d63fa531"}, + {file = "mypy-1.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2189ff1e39db399f08205e22a797383613ce1cb0cb3b13d8bcf0170e45b96cc3"}, + {file = "mypy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:97a131ee36ac37ce9581f4220311247ab6cba896b4395b9c87af0675a13a755f"}, + {file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"}, + {file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"}, ] [package.dependencies] @@ -817,27 +834,24 @@ files = [ [[package]] name = "nodeenv" -version = "1.8.0" +version = "1.9.1" description = "Node.js virtual environment builder" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, ] -[package.dependencies] -setuptools = "*" - [[package]] name = "packaging" -version = "24.0" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] @@ -863,13 +877,13 @@ files = [ [[package]] name = "platformdirs" -version = "4.2.1" +version = "4.2.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, - {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] @@ -894,13 +908,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "3.7.0" +version = "3.7.1" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" files = [ - {file = "pre_commit-3.7.0-py2.py3-none-any.whl", hash = "sha256:5eae9e10c2b5ac51577c3452ec0a490455c45a0533f7960f993a0d01e59decab"}, - {file = "pre_commit-3.7.0.tar.gz", hash = "sha256:e209d61b8acdcf742404408531f0c37d49d2c734fd7cff2d6076083d191cb060"}, + {file = "pre_commit-3.7.1-py2.py3-none-any.whl", hash = "sha256:fae36fd1d7ad7d6a5a1c0b0d5adb2ed1a3bda5a21bf6c3e5372073d7a11cd4c5"}, + {file = "pre_commit-3.7.1.tar.gz", hash = "sha256:8ca3ad567bc78a4972a3f1a477e94a79d4597e8140a6e0b651c5e33899c3654a"}, ] [package.dependencies] @@ -912,120 +926,133 @@ virtualenv = ">=20.10.0" [[package]] name = "pycountry" -version = "23.12.11" +version = "24.6.1" description = "ISO country, subdivision, language, currency and script definitions and their translations" optional = false python-versions = ">=3.8" files = [ - {file = "pycountry-23.12.11-py3-none-any.whl", hash = "sha256:2ff91cff4f40ff61086e773d61e72005fe95de4a57bfc765509db05695dc50ab"}, - {file = "pycountry-23.12.11.tar.gz", hash = "sha256:00569d82eaefbc6a490a311bfa84a9c571cff9ddbf8b0a4f4e7b4f868b4ad925"}, + {file = "pycountry-24.6.1-py3-none-any.whl", hash = "sha256:f1a4fb391cd7214f8eefd39556d740adcc233c778a27f8942c8dca351d6ce06f"}, + {file = "pycountry-24.6.1.tar.gz", hash = "sha256:b61b3faccea67f87d10c1f2b0fc0be714409e8fcdcc1315613174f6466c10221"}, ] [[package]] name = "pydantic" -version = "2.7.1" +version = "2.8.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"}, - {file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"}, + {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"}, + {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.18.2" -typing-extensions = ">=4.6.1" +pydantic-core = "2.20.1" +typing-extensions = [ + {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, + {version = ">=4.6.1", markers = "python_version < \"3.13\""}, +] [package.extras] email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.18.2" +version = "2.20.1" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"}, - {file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"}, - {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"}, - {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"}, - {file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"}, - {file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"}, - {file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"}, - {file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"}, - {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"}, - {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"}, - {file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"}, - {file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"}, - {file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"}, - {file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"}, - {file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"}, - {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"}, - {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"}, - {file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"}, - {file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"}, - {file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"}, - {file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"}, - {file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"}, - {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"}, - {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"}, - {file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"}, - {file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"}, - {file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"}, - {file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"}, - {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"}, - {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"}, - {file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"}, - {file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"}, - {file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"}, + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"}, + {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"}, + {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"}, + {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"}, + {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"}, + {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"}, + {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"}, + {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"}, + {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"}, + {file = "pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91"}, + {file = "pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd"}, + {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa"}, + {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987"}, + {file = "pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a"}, + {file = "pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"}, + {file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"}, + {file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"}, + {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"}, ] [package.dependencies] @@ -1033,45 +1060,49 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydantic-extra-types" -version = "2.7.0" +version = "2.9.0" description = "Extra Pydantic types." optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_extra_types-2.7.0-py3-none-any.whl", hash = "sha256:ac01bbdaa4f85e4c4744a9792c5b0cfe61efa5028a0e670c3d8bfbf8b36c8543"}, - {file = "pydantic_extra_types-2.7.0.tar.gz", hash = "sha256:b9d9ddd755fa5960ec5a77cffcbd5d8796a0116e1dfc8f7c3a27fa0041693382"}, + {file = "pydantic_extra_types-2.9.0-py3-none-any.whl", hash = "sha256:f0bb975508572ba7bf3390b7337807588463b7248587e69f43b1ad7c797530d0"}, + {file = "pydantic_extra_types-2.9.0.tar.gz", hash = "sha256:e061c01636188743bb69f368dcd391f327b8cfbfede2fe1cbb1211b06601ba3b"}, ] [package.dependencies] pydantic = ">=2.5.2" [package.extras] -all = ["pendulum (>=3.0.0,<4.0.0)", "phonenumbers (>=8,<9)", "pycountry (>=23)", "python-ulid (>=1,<2)", "python-ulid (>=1,<3)"] +all = ["pendulum (>=3.0.0,<4.0.0)", "phonenumbers (>=8,<9)", "pycountry (>=23)", "python-ulid (>=1,<2)", "python-ulid (>=1,<3)", "pytz (>=2024.1)", "semver (>=3.0.2)", "tzdata (>=2024.1)"] +pendulum = ["pendulum (>=3.0.0,<4.0.0)"] +phonenumbers = ["phonenumbers (>=8,<9)"] +pycountry = ["pycountry (>=23)"] +python-ulid = ["python-ulid (>=1,<2)", "python-ulid (>=1,<3)"] +semver = ["semver (>=3.0.2)"] [[package]] name = "pygments" -version = "2.17.2" +version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, - {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, ] [package.extras] -plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymdown-extensions" -version = "10.8" +version = "10.8.1" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" files = [ - {file = "pymdown_extensions-10.8-py3-none-any.whl", hash = "sha256:3539003ff0d5e219ba979d2dc961d18fcad5ac259e66c764482e8347b4c0503c"}, - {file = "pymdown_extensions-10.8.tar.gz", hash = "sha256:91ca336caf414e1e5e0626feca86e145de9f85a3921a7bcbd32890b51738c428"}, + {file = "pymdown_extensions-10.8.1-py3-none-any.whl", hash = "sha256:f938326115884f48c6059c67377c46cf631c733ef3629b6eed1349989d1b30cb"}, + {file = "pymdown_extensions-10.8.1.tar.gz", hash = "sha256:3ab1db5c9e21728dabf75192d71471f8e50f216627e9a1fa9535ecb0231b9940"}, ] [package.dependencies] @@ -1083,13 +1114,13 @@ extra = ["pygments (>=2.12)"] [[package]] name = "pytest" -version = "8.1.1" +version = "8.2.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, + {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, + {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, ] [package.dependencies] @@ -1097,21 +1128,21 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.4,<2.0" +pluggy = ">=1.5,<2.0" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-asyncio" -version = "0.23.6" +version = "0.23.7" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"}, - {file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"}, + {file = "pytest_asyncio-0.23.7-py3-none-any.whl", hash = "sha256:009b48127fbe44518a547bddd25611551b0e43ccdbf1e67d12479f569832c20b"}, + {file = "pytest_asyncio-0.23.7.tar.gz", hash = "sha256:5f5c72948f4c49e7db4f29f2521d4031f1c27f86e57b046126654083d4770268"}, ] [package.dependencies] @@ -1211,115 +1242,101 @@ pyyaml = "*" [[package]] name = "regex" -version = "2024.4.16" +version = "2024.5.15" description = "Alternative regular expression module, to replace re." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "regex-2024.4.16-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb83cc090eac63c006871fd24db5e30a1f282faa46328572661c0a24a2323a08"}, - {file = "regex-2024.4.16-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c91e1763696c0eb66340c4df98623c2d4e77d0746b8f8f2bee2c6883fd1fe18"}, - {file = "regex-2024.4.16-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:10188fe732dec829c7acca7422cdd1bf57d853c7199d5a9e96bb4d40db239c73"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:956b58d692f235cfbf5b4f3abd6d99bf102f161ccfe20d2fd0904f51c72c4c66"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a70b51f55fd954d1f194271695821dd62054d949efd6368d8be64edd37f55c86"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c02fcd2bf45162280613d2e4a1ca3ac558ff921ae4e308ecb307650d3a6ee51"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4ed75ea6892a56896d78f11006161eea52c45a14994794bcfa1654430984b22"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd727ad276bb91928879f3aa6396c9a1d34e5e180dce40578421a691eeb77f47"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7cbc5d9e8a1781e7be17da67b92580d6ce4dcef5819c1b1b89f49d9678cc278c"}, - {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:78fddb22b9ef810b63ef341c9fcf6455232d97cfe03938cbc29e2672c436670e"}, - {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:445ca8d3c5a01309633a0c9db57150312a181146315693273e35d936472df912"}, - {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:95399831a206211d6bc40224af1c635cb8790ddd5c7493e0bd03b85711076a53"}, - {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:7731728b6568fc286d86745f27f07266de49603a6fdc4d19c87e8c247be452af"}, - {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4facc913e10bdba42ec0aee76d029aedda628161a7ce4116b16680a0413f658a"}, - {file = "regex-2024.4.16-cp310-cp310-win32.whl", hash = "sha256:911742856ce98d879acbea33fcc03c1d8dc1106234c5e7d068932c945db209c0"}, - {file = "regex-2024.4.16-cp310-cp310-win_amd64.whl", hash = "sha256:e0a2df336d1135a0b3a67f3bbf78a75f69562c1199ed9935372b82215cddd6e2"}, - {file = "regex-2024.4.16-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1210365faba7c2150451eb78ec5687871c796b0f1fa701bfd2a4a25420482d26"}, - {file = "regex-2024.4.16-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ab40412f8cd6f615bfedea40c8bf0407d41bf83b96f6fc9ff34976d6b7037fd"}, - {file = "regex-2024.4.16-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fd80d1280d473500d8086d104962a82d77bfbf2b118053824b7be28cd5a79ea5"}, - {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bb966fdd9217e53abf824f437a5a2d643a38d4fd5fd0ca711b9da683d452969"}, - {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20b7a68444f536365af42a75ccecb7ab41a896a04acf58432db9e206f4e525d6"}, - {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b74586dd0b039c62416034f811d7ee62810174bb70dffcca6439f5236249eb09"}, - {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8290b44d8b0af4e77048646c10c6e3aa583c1ca67f3b5ffb6e06cf0c6f0f89"}, - {file = "regex-2024.4.16-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2d80a6749724b37853ece57988b39c4e79d2b5fe2869a86e8aeae3bbeef9eb0"}, - {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3a1018e97aeb24e4f939afcd88211ace472ba566efc5bdf53fd8fd7f41fa7170"}, - {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8d015604ee6204e76569d2f44e5a210728fa917115bef0d102f4107e622b08d5"}, - {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:3d5ac5234fb5053850d79dd8eb1015cb0d7d9ed951fa37aa9e6249a19aa4f336"}, - {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:0a38d151e2cdd66d16dab550c22f9521ba79761423b87c01dae0a6e9add79c0d"}, - {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:159dc4e59a159cb8e4e8f8961eb1fa5d58f93cb1acd1701d8aff38d45e1a84a6"}, - {file = "regex-2024.4.16-cp311-cp311-win32.whl", hash = "sha256:ba2336d6548dee3117520545cfe44dc28a250aa091f8281d28804aa8d707d93d"}, - {file = "regex-2024.4.16-cp311-cp311-win_amd64.whl", hash = "sha256:8f83b6fd3dc3ba94d2b22717f9c8b8512354fd95221ac661784df2769ea9bba9"}, - {file = "regex-2024.4.16-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:80b696e8972b81edf0af2a259e1b2a4a661f818fae22e5fa4fa1a995fb4a40fd"}, - {file = "regex-2024.4.16-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d61ae114d2a2311f61d90c2ef1358518e8f05eafda76eaf9c772a077e0b465ec"}, - {file = "regex-2024.4.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ba6745440b9a27336443b0c285d705ce73adb9ec90e2f2004c64d95ab5a7598"}, - {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295004b2dd37b0835ea5c14a33e00e8cfa3c4add4d587b77287825f3418d310"}, - {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4aba818dcc7263852aabb172ec27b71d2abca02a593b95fa79351b2774eb1d2b"}, - {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0800631e565c47520aaa04ae38b96abc5196fe8b4aa9bd864445bd2b5848a7a"}, - {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08dea89f859c3df48a440dbdcd7b7155bc675f2fa2ec8c521d02dc69e877db70"}, - {file = "regex-2024.4.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eeaa0b5328b785abc344acc6241cffde50dc394a0644a968add75fcefe15b9d4"}, - {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4e819a806420bc010489f4e741b3036071aba209f2e0989d4750b08b12a9343f"}, - {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c2d0e7cbb6341e830adcbfa2479fdeebbfbb328f11edd6b5675674e7a1e37730"}, - {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:91797b98f5e34b6a49f54be33f72e2fb658018ae532be2f79f7c63b4ae225145"}, - {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:d2da13568eff02b30fd54fccd1e042a70fe920d816616fda4bf54ec705668d81"}, - {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:370c68dc5570b394cbaadff50e64d705f64debed30573e5c313c360689b6aadc"}, - {file = "regex-2024.4.16-cp312-cp312-win32.whl", hash = "sha256:904c883cf10a975b02ab3478bce652f0f5346a2c28d0a8521d97bb23c323cc8b"}, - {file = "regex-2024.4.16-cp312-cp312-win_amd64.whl", hash = "sha256:785c071c982dce54d44ea0b79cd6dfafddeccdd98cfa5f7b86ef69b381b457d9"}, - {file = "regex-2024.4.16-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e2f142b45c6fed48166faeb4303b4b58c9fcd827da63f4cf0a123c3480ae11fb"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e87ab229332ceb127a165612d839ab87795972102cb9830e5f12b8c9a5c1b508"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:81500ed5af2090b4a9157a59dbc89873a25c33db1bb9a8cf123837dcc9765047"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b340cccad138ecb363324aa26893963dcabb02bb25e440ebdf42e30963f1a4e0"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c72608e70f053643437bd2be0608f7f1c46d4022e4104d76826f0839199347a"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a01fe2305e6232ef3e8f40bfc0f0f3a04def9aab514910fa4203bafbc0bb4682"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:03576e3a423d19dda13e55598f0fd507b5d660d42c51b02df4e0d97824fdcae3"}, - {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:549c3584993772e25f02d0656ac48abdda73169fe347263948cf2b1cead622f3"}, - {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:34422d5a69a60b7e9a07a690094e824b66f5ddc662a5fc600d65b7c174a05f04"}, - {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:5f580c651a72b75c39e311343fe6875d6f58cf51c471a97f15a938d9fe4e0d37"}, - {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3399dd8a7495bbb2bacd59b84840eef9057826c664472e86c91d675d007137f5"}, - {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8d1f86f3f4e2388aa3310b50694ac44daefbd1681def26b4519bd050a398dc5a"}, - {file = "regex-2024.4.16-cp37-cp37m-win32.whl", hash = "sha256:dd5acc0a7d38fdc7a3a6fd3ad14c880819008ecb3379626e56b163165162cc46"}, - {file = "regex-2024.4.16-cp37-cp37m-win_amd64.whl", hash = "sha256:ba8122e3bb94ecda29a8de4cf889f600171424ea586847aa92c334772d200331"}, - {file = "regex-2024.4.16-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:743deffdf3b3481da32e8a96887e2aa945ec6685af1cfe2bcc292638c9ba2f48"}, - {file = "regex-2024.4.16-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7571f19f4a3fd00af9341c7801d1ad1967fc9c3f5e62402683047e7166b9f2b4"}, - {file = "regex-2024.4.16-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:df79012ebf6f4efb8d307b1328226aef24ca446b3ff8d0e30202d7ebcb977a8c"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e757d475953269fbf4b441207bb7dbdd1c43180711b6208e129b637792ac0b93"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4313ab9bf6a81206c8ac28fdfcddc0435299dc88cad12cc6305fd0e78b81f9e4"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d83c2bc678453646f1a18f8db1e927a2d3f4935031b9ad8a76e56760461105dd"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9df1bfef97db938469ef0a7354b2d591a2d438bc497b2c489471bec0e6baf7c4"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62120ed0de69b3649cc68e2965376048793f466c5a6c4370fb27c16c1beac22d"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c2ef6f7990b6e8758fe48ad08f7e2f66c8f11dc66e24093304b87cae9037bb4a"}, - {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8fc6976a3395fe4d1fbeb984adaa8ec652a1e12f36b56ec8c236e5117b585427"}, - {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:03e68f44340528111067cecf12721c3df4811c67268b897fbe695c95f860ac42"}, - {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ec7e0043b91115f427998febaa2beb82c82df708168b35ece3accb610b91fac1"}, - {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c21fc21a4c7480479d12fd8e679b699f744f76bb05f53a1d14182b31f55aac76"}, - {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:12f6a3f2f58bb7344751919a1876ee1b976fe08b9ffccb4bbea66f26af6017b9"}, - {file = "regex-2024.4.16-cp38-cp38-win32.whl", hash = "sha256:479595a4fbe9ed8f8f72c59717e8cf222da2e4c07b6ae5b65411e6302af9708e"}, - {file = "regex-2024.4.16-cp38-cp38-win_amd64.whl", hash = "sha256:0534b034fba6101611968fae8e856c1698da97ce2efb5c2b895fc8b9e23a5834"}, - {file = "regex-2024.4.16-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a7ccdd1c4a3472a7533b0a7aa9ee34c9a2bef859ba86deec07aff2ad7e0c3b94"}, - {file = "regex-2024.4.16-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f2f017c5be19984fbbf55f8af6caba25e62c71293213f044da3ada7091a4455"}, - {file = "regex-2024.4.16-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:803b8905b52de78b173d3c1e83df0efb929621e7b7c5766c0843704d5332682f"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:684008ec44ad275832a5a152f6e764bbe1914bea10968017b6feaecdad5736e0"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65436dce9fdc0aeeb0a0effe0839cb3d6a05f45aa45a4d9f9c60989beca78b9c"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea355eb43b11764cf799dda62c658c4d2fdb16af41f59bb1ccfec517b60bcb07"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c1165f3809ce7774f05cb74e5408cd3aa93ee8573ae959a97a53db3ca3180d"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cccc79a9be9b64c881f18305a7c715ba199e471a3973faeb7ba84172abb3f317"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00169caa125f35d1bca6045d65a662af0202704489fada95346cfa092ec23f39"}, - {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6cc38067209354e16c5609b66285af17a2863a47585bcf75285cab33d4c3b8df"}, - {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:23cff1b267038501b179ccbbd74a821ac4a7192a1852d1d558e562b507d46013"}, - {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d320b3bf82a39f248769fc7f188e00f93526cc0fe739cfa197868633d44701"}, - {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:89ec7f2c08937421bbbb8b48c54096fa4f88347946d4747021ad85f1b3021b3c"}, - {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4918fd5f8b43aa7ec031e0fef1ee02deb80b6afd49c85f0790be1dc4ce34cb50"}, - {file = "regex-2024.4.16-cp39-cp39-win32.whl", hash = "sha256:684e52023aec43bdf0250e843e1fdd6febbe831bd9d52da72333fa201aaa2335"}, - {file = "regex-2024.4.16-cp39-cp39-win_amd64.whl", hash = "sha256:e697e1c0238133589e00c244a8b676bc2cfc3ab4961318d902040d099fec7483"}, - {file = "regex-2024.4.16.tar.gz", hash = "sha256:fa454d26f2e87ad661c4f0c5a5fe4cf6aab1e307d1b94f16ffdfcb089ba685c0"}, + {file = "regex-2024.5.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a81e3cfbae20378d75185171587cbf756015ccb14840702944f014e0d93ea09f"}, + {file = "regex-2024.5.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b59138b219ffa8979013be7bc85bb60c6f7b7575df3d56dc1e403a438c7a3f6"}, + {file = "regex-2024.5.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0bd000c6e266927cb7a1bc39d55be95c4b4f65c5be53e659537537e019232b1"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eaa7ddaf517aa095fa8da0b5015c44d03da83f5bd49c87961e3c997daed0de7"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba68168daedb2c0bab7fd7e00ced5ba90aebf91024dea3c88ad5063c2a562cca"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e8d717bca3a6e2064fc3a08df5cbe366369f4b052dcd21b7416e6d71620dca1"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1337b7dbef9b2f71121cdbf1e97e40de33ff114801263b275aafd75303bd62b5"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9ebd0a36102fcad2f03696e8af4ae682793a5d30b46c647eaf280d6cfb32796"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9efa1a32ad3a3ea112224897cdaeb6aa00381627f567179c0314f7b65d354c62"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1595f2d10dff3d805e054ebdc41c124753631b6a471b976963c7b28543cf13b0"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b802512f3e1f480f41ab5f2cfc0e2f761f08a1f41092d6718868082fc0d27143"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a0981022dccabca811e8171f913de05720590c915b033b7e601f35ce4ea7019f"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:19068a6a79cf99a19ccefa44610491e9ca02c2be3305c7760d3831d38a467a6f"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1b5269484f6126eee5e687785e83c6b60aad7663dafe842b34691157e5083e53"}, + {file = "regex-2024.5.15-cp310-cp310-win32.whl", hash = "sha256:ada150c5adfa8fbcbf321c30c751dc67d2f12f15bd183ffe4ec7cde351d945b3"}, + {file = "regex-2024.5.15-cp310-cp310-win_amd64.whl", hash = "sha256:ac394ff680fc46b97487941f5e6ae49a9f30ea41c6c6804832063f14b2a5a145"}, + {file = "regex-2024.5.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f5b1dff3ad008dccf18e652283f5e5339d70bf8ba7c98bf848ac33db10f7bc7a"}, + {file = "regex-2024.5.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c6a2b494a76983df8e3d3feea9b9ffdd558b247e60b92f877f93a1ff43d26656"}, + {file = "regex-2024.5.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a32b96f15c8ab2e7d27655969a23895eb799de3665fa94349f3b2fbfd547236f"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10002e86e6068d9e1c91eae8295ef690f02f913c57db120b58fdd35a6bb1af35"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec54d5afa89c19c6dd8541a133be51ee1017a38b412b1321ccb8d6ddbeb4cf7d"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10e4ce0dca9ae7a66e6089bb29355d4432caed736acae36fef0fdd7879f0b0cb"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e507ff1e74373c4d3038195fdd2af30d297b4f0950eeda6f515ae3d84a1770f"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1f059a4d795e646e1c37665b9d06062c62d0e8cc3c511fe01315973a6542e40"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0721931ad5fe0dda45d07f9820b90b2148ccdd8e45bb9e9b42a146cb4f695649"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:833616ddc75ad595dee848ad984d067f2f31be645d603e4d158bba656bbf516c"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:287eb7f54fc81546346207c533ad3c2c51a8d61075127d7f6d79aaf96cdee890"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:19dfb1c504781a136a80ecd1fff9f16dddf5bb43cec6871778c8a907a085bb3d"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:119af6e56dce35e8dfb5222573b50c89e5508d94d55713c75126b753f834de68"}, + {file = "regex-2024.5.15-cp311-cp311-win32.whl", hash = "sha256:1c1c174d6ec38d6c8a7504087358ce9213d4332f6293a94fbf5249992ba54efa"}, + {file = "regex-2024.5.15-cp311-cp311-win_amd64.whl", hash = "sha256:9e717956dcfd656f5055cc70996ee2cc82ac5149517fc8e1b60261b907740201"}, + {file = "regex-2024.5.15-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:632b01153e5248c134007209b5c6348a544ce96c46005d8456de1d552455b014"}, + {file = "regex-2024.5.15-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e64198f6b856d48192bf921421fdd8ad8eb35e179086e99e99f711957ffedd6e"}, + {file = "regex-2024.5.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68811ab14087b2f6e0fc0c2bae9ad689ea3584cad6917fc57be6a48bbd012c49"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ec0c2fea1e886a19c3bee0cd19d862b3aa75dcdfb42ebe8ed30708df64687a"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0c0c0003c10f54a591d220997dd27d953cd9ccc1a7294b40a4be5312be8797b"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2431b9e263af1953c55abbd3e2efca67ca80a3de8a0437cb58e2421f8184717a"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a605586358893b483976cffc1723fb0f83e526e8f14c6e6614e75919d9862cf"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391d7f7f1e409d192dba8bcd42d3e4cf9e598f3979cdaed6ab11288da88cb9f2"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9ff11639a8d98969c863d4617595eb5425fd12f7c5ef6621a4b74b71ed8726d5"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4eee78a04e6c67e8391edd4dad3279828dd66ac4b79570ec998e2155d2e59fd5"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8fe45aa3f4aa57faabbc9cb46a93363edd6197cbc43523daea044e9ff2fea83e"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d0a3d8d6acf0c78a1fff0e210d224b821081330b8524e3e2bc5a68ef6ab5803d"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c486b4106066d502495b3025a0a7251bf37ea9540433940a23419461ab9f2a80"}, + {file = "regex-2024.5.15-cp312-cp312-win32.whl", hash = "sha256:c49e15eac7c149f3670b3e27f1f28a2c1ddeccd3a2812cba953e01be2ab9b5fe"}, + {file = "regex-2024.5.15-cp312-cp312-win_amd64.whl", hash = "sha256:673b5a6da4557b975c6c90198588181029c60793835ce02f497ea817ff647cb2"}, + {file = "regex-2024.5.15-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:87e2a9c29e672fc65523fb47a90d429b70ef72b901b4e4b1bd42387caf0d6835"}, + {file = "regex-2024.5.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3bea0ba8b73b71b37ac833a7f3fd53825924165da6a924aec78c13032f20850"}, + {file = "regex-2024.5.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bfc4f82cabe54f1e7f206fd3d30fda143f84a63fe7d64a81558d6e5f2e5aaba9"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5bb9425fe881d578aeca0b2b4b3d314ec88738706f66f219c194d67179337cb"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64c65783e96e563103d641760664125e91bd85d8e49566ee560ded4da0d3e704"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf2430df4148b08fb4324b848672514b1385ae3807651f3567871f130a728cc3"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5397de3219a8b08ae9540c48f602996aa6b0b65d5a61683e233af8605c42b0f2"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:455705d34b4154a80ead722f4f185b04c4237e8e8e33f265cd0798d0e44825fa"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2b6f1b3bb6f640c1a92be3bbfbcb18657b125b99ecf141fb3310b5282c7d4ed"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3ad070b823ca5890cab606c940522d05d3d22395d432f4aaaf9d5b1653e47ced"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5b5467acbfc153847d5adb21e21e29847bcb5870e65c94c9206d20eb4e99a384"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e6662686aeb633ad65be2a42b4cb00178b3fbf7b91878f9446075c404ada552f"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:2b4c884767504c0e2401babe8b5b7aea9148680d2e157fa28f01529d1f7fcf67"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3cd7874d57f13bf70078f1ff02b8b0aa48d5b9ed25fc48547516c6aba36f5741"}, + {file = "regex-2024.5.15-cp38-cp38-win32.whl", hash = "sha256:e4682f5ba31f475d58884045c1a97a860a007d44938c4c0895f41d64481edbc9"}, + {file = "regex-2024.5.15-cp38-cp38-win_amd64.whl", hash = "sha256:d99ceffa25ac45d150e30bd9ed14ec6039f2aad0ffa6bb87a5936f5782fc1569"}, + {file = "regex-2024.5.15-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13cdaf31bed30a1e1c2453ef6015aa0983e1366fad2667657dbcac7b02f67133"}, + {file = "regex-2024.5.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cac27dcaa821ca271855a32188aa61d12decb6fe45ffe3e722401fe61e323cd1"}, + {file = "regex-2024.5.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7dbe2467273b875ea2de38ded4eba86cbcbc9a1a6d0aa11dcf7bd2e67859c435"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f18a9a3513a99c4bef0e3efd4c4a5b11228b48aa80743be822b71e132ae4f5"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d347a741ea871c2e278fde6c48f85136c96b8659b632fb57a7d1ce1872547600"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1878b8301ed011704aea4c806a3cadbd76f84dece1ec09cc9e4dc934cfa5d4da"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4babf07ad476aaf7830d77000874d7611704a7fcf68c9c2ad151f5d94ae4bfc4"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35cb514e137cb3488bce23352af3e12fb0dbedd1ee6e60da053c69fb1b29cc6c"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cdd09d47c0b2efee9378679f8510ee6955d329424c659ab3c5e3a6edea696294"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:72d7a99cd6b8f958e85fc6ca5b37c4303294954eac1376535b03c2a43eb72629"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a094801d379ab20c2135529948cb84d417a2169b9bdceda2a36f5f10977ebc16"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c0c18345010870e58238790a6779a1219b4d97bd2e77e1140e8ee5d14df071aa"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:16093f563098448ff6b1fa68170e4acbef94e6b6a4e25e10eae8598bb1694b5d"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e38a7d4e8f633a33b4c7350fbd8bad3b70bf81439ac67ac38916c4a86b465456"}, + {file = "regex-2024.5.15-cp39-cp39-win32.whl", hash = "sha256:71a455a3c584a88f654b64feccc1e25876066c4f5ef26cd6dd711308aa538694"}, + {file = "regex-2024.5.15-cp39-cp39-win_amd64.whl", hash = "sha256:cab12877a9bdafde5500206d1020a584355a97884dfd388af3699e9137bf7388"}, + {file = "regex-2024.5.15.tar.gz", hash = "sha256:d3ee02d9e5f482cc8309134a91eeaacbdd2261ba111b0fef3748eeb4913e6a2c"}, ] [[package]] name = "requests" -version = "2.31.0" +version = "2.32.3" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -1334,46 +1351,31 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "ruff" -version = "0.3.7" +version = "0.5.1" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e8377cccb2f07abd25e84fc5b2cbe48eeb0fea9f1719cad7caedb061d70e5ce"}, - {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:15a4d1cc1e64e556fa0d67bfd388fed416b7f3b26d5d1c3e7d192c897e39ba4b"}, - {file = "ruff-0.3.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d28bdf3d7dc71dd46929fafeec98ba89b7c3550c3f0978e36389b5631b793663"}, - {file = "ruff-0.3.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:379b67d4f49774ba679593b232dcd90d9e10f04d96e3c8ce4a28037ae473f7bb"}, - {file = "ruff-0.3.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c060aea8ad5ef21cdfbbe05475ab5104ce7827b639a78dd55383a6e9895b7c51"}, - {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ebf8f615dde968272d70502c083ebf963b6781aacd3079081e03b32adfe4d58a"}, - {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d48098bd8f5c38897b03604f5428901b65e3c97d40b3952e38637b5404b739a2"}, - {file = "ruff-0.3.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8a4fda219bf9024692b1bc68c9cff4b80507879ada8769dc7e985755d662ea"}, - {file = "ruff-0.3.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c44e0149f1d8b48c4d5c33d88c677a4aa22fd09b1683d6a7ff55b816b5d074f"}, - {file = "ruff-0.3.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3050ec0af72b709a62ecc2aca941b9cd479a7bf2b36cc4562f0033d688e44fa1"}, - {file = "ruff-0.3.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a29cc38e4c1ab00da18a3f6777f8b50099d73326981bb7d182e54a9a21bb4ff7"}, - {file = "ruff-0.3.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b15cc59c19edca917f51b1956637db47e200b0fc5e6e1878233d3a938384b0b"}, - {file = "ruff-0.3.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e491045781b1e38b72c91247cf4634f040f8d0cb3e6d3d64d38dcf43616650b4"}, - {file = "ruff-0.3.7-py3-none-win32.whl", hash = "sha256:bc931de87593d64fad3a22e201e55ad76271f1d5bfc44e1a1887edd0903c7d9f"}, - {file = "ruff-0.3.7-py3-none-win_amd64.whl", hash = "sha256:5ef0e501e1e39f35e03c2acb1d1238c595b8bb36cf7a170e7c1df1b73da00e74"}, - {file = "ruff-0.3.7-py3-none-win_arm64.whl", hash = "sha256:789e144f6dc7019d1f92a812891c645274ed08af6037d11fc65fcbc183b7d59f"}, - {file = "ruff-0.3.7.tar.gz", hash = "sha256:d5c1aebee5162c2226784800ae031f660c350e7a3402c4d1f8ea4e97e232e3ba"}, -] - -[[package]] -name = "setuptools" -version = "69.5.1" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, - {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, + {file = "ruff-0.5.1-py3-none-linux_armv6l.whl", hash = "sha256:6ecf968fcf94d942d42b700af18ede94b07521bd188aaf2cd7bc898dd8cb63b6"}, + {file = "ruff-0.5.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:204fb0a472f00f2e6280a7c8c7c066e11e20e23a37557d63045bf27a616ba61c"}, + {file = "ruff-0.5.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d235968460e8758d1e1297e1de59a38d94102f60cafb4d5382033c324404ee9d"}, + {file = "ruff-0.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38beace10b8d5f9b6bdc91619310af6d63dd2019f3fb2d17a2da26360d7962fa"}, + {file = "ruff-0.5.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e478d2f09cf06add143cf8c4540ef77b6599191e0c50ed976582f06e588c994"}, + {file = "ruff-0.5.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0368d765eec8247b8550251c49ebb20554cc4e812f383ff9f5bf0d5d94190b0"}, + {file = "ruff-0.5.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3a9a9a1b582e37669b0138b7c1d9d60b9edac880b80eb2baba6d0e566bdeca4d"}, + {file = "ruff-0.5.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bdd9f723e16003623423affabcc0a807a66552ee6a29f90eddad87a40c750b78"}, + {file = "ruff-0.5.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:be9fd62c1e99539da05fcdc1e90d20f74aec1b7a1613463ed77870057cd6bd96"}, + {file = "ruff-0.5.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e216fc75a80ea1fbd96af94a6233d90190d5b65cc3d5dfacf2bd48c3e067d3e1"}, + {file = "ruff-0.5.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c4c2112e9883a40967827d5c24803525145e7dab315497fae149764979ac7929"}, + {file = "ruff-0.5.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dfaf11c8a116394da3b65cd4b36de30d8552fa45b8119b9ef5ca6638ab964fa3"}, + {file = "ruff-0.5.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d7ceb9b2fe700ee09a0c6b192c5ef03c56eb82a0514218d8ff700f6ade004108"}, + {file = "ruff-0.5.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:bac6288e82f6296f82ed5285f597713acb2a6ae26618ffc6b429c597b392535c"}, + {file = "ruff-0.5.1-py3-none-win32.whl", hash = "sha256:5c441d9c24ec09e1cb190a04535c5379b36b73c4bc20aa180c54812c27d1cca4"}, + {file = "ruff-0.5.1-py3-none-win_amd64.whl", hash = "sha256:b1789bf2cd3d1b5a7d38397cac1398ddf3ad7f73f4de01b1e913e2abc7dfc51d"}, + {file = "ruff-0.5.1-py3-none-win_arm64.whl", hash = "sha256:2875b7596a740cbbd492f32d24be73e545a4ce0a3daf51e4f4e609962bfd3cd2"}, + {file = "ruff-0.5.1.tar.gz", hash = "sha256:3164488aebd89b1745b47fd00604fb4358d774465f20d1fcd907f9c0fc1b0655"}, ] -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - [[package]] name = "six" version = "1.16.0" @@ -1419,17 +1421,18 @@ typing = ["mypy (>=1.4)"] [[package]] name = "tenacity" -version = "8.2.3" +version = "8.5.0" description = "Retry code until it succeeds" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"}, - {file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"}, + {file = "tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687"}, + {file = "tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78"}, ] [package.extras] -doc = ["reno", "sphinx", "tornado (>=4.5)"] +doc = ["reno", "sphinx"] +test = ["pytest", "tornado (>=4.5)", "typeguard"] [[package]] name = "tomli" @@ -1444,35 +1447,35 @@ files = [ [[package]] name = "types-boltons" -version = "23.1.0.20240331" +version = "24.0.0.20240423" description = "Typing stubs for boltons" optional = false python-versions = ">=3.8" files = [ - {file = "types-boltons-23.1.0.20240331.tar.gz", hash = "sha256:7e95cd94f009f54b6d75e4b96de204287cc43e7f7602ce3702d575345c67bbb1"}, - {file = "types_boltons-23.1.0.20240331-py3-none-any.whl", hash = "sha256:58c1f57aa40b1615d48e2aca776174484f7b963ca5f3e9c6ee4743db8ede50f3"}, + {file = "types-boltons-24.0.0.20240423.tar.gz", hash = "sha256:a0e418f2e1d6b6a561ef441cc14e8b3bf5477cbb307ec27d7f330caca13dd3aa"}, + {file = "types_boltons-24.0.0.20240423-py3-none-any.whl", hash = "sha256:5965274b6123ac6c7d90cae54beb699b83e550e66501b0dfb5dcca4fc53955ab"}, ] [[package]] name = "typing-extensions" -version = "4.11.0" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "urllib3" -version = "2.2.1" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] @@ -1483,13 +1486,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.26.0" +version = "20.26.3" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.0-py3-none-any.whl", hash = "sha256:0846377ea76e818daaa3e00a4365c018bc3ac9760cbb3544de542885aad61fb3"}, - {file = "virtualenv-20.26.0.tar.gz", hash = "sha256:ec25a9671a5102c8d2657f62792a27b48f016664c6873f6beed3800008577210"}, + {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, + {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, ] [package.dependencies] @@ -1503,40 +1506,43 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [[package]] name = "watchdog" -version = "4.0.0" +version = "4.0.1" description = "Filesystem events monitoring" optional = false python-versions = ">=3.8" files = [ - {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b"}, - {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b"}, - {file = "watchdog-4.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c"}, - {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b"}, - {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935"}, - {file = "watchdog-4.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b"}, - {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257"}, - {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19"}, - {file = "watchdog-4.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b"}, - {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:45cc09cc4c3b43fb10b59ef4d07318d9a3ecdbff03abd2e36e77b6dd9f9a5c85"}, - {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eed82cdf79cd7f0232e2fdc1ad05b06a5e102a43e331f7d041e5f0e0a34a51c4"}, - {file = "watchdog-4.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba30a896166f0fee83183cec913298151b73164160d965af2e93a20bbd2ab605"}, - {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d18d7f18a47de6863cd480734613502904611730f8def45fc52a5d97503e5101"}, - {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2895bf0518361a9728773083908801a376743bcc37dfa252b801af8fd281b1ca"}, - {file = "watchdog-4.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87e9df830022488e235dd601478c15ad73a0389628588ba0b028cb74eb72fed8"}, - {file = "watchdog-4.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b"}, - {file = "watchdog-4.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6a4db54edea37d1058b08947c789a2354ee02972ed5d1e0dca9b0b820f4c7f92"}, - {file = "watchdog-4.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269"}, - {file = "watchdog-4.0.0-py3-none-win32.whl", hash = "sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c"}, - {file = "watchdog-4.0.0-py3-none-win_amd64.whl", hash = "sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245"}, - {file = "watchdog-4.0.0-py3-none-win_ia64.whl", hash = "sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7"}, - {file = "watchdog-4.0.0.tar.gz", hash = "sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec"}, + {file = "watchdog-4.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:da2dfdaa8006eb6a71051795856bedd97e5b03e57da96f98e375682c48850645"}, + {file = "watchdog-4.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e93f451f2dfa433d97765ca2634628b789b49ba8b504fdde5837cdcf25fdb53b"}, + {file = "watchdog-4.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ef0107bbb6a55f5be727cfc2ef945d5676b97bffb8425650dadbb184be9f9a2b"}, + {file = "watchdog-4.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:17e32f147d8bf9657e0922c0940bcde863b894cd871dbb694beb6704cfbd2fb5"}, + {file = "watchdog-4.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:03e70d2df2258fb6cb0e95bbdbe06c16e608af94a3ffbd2b90c3f1e83eb10767"}, + {file = "watchdog-4.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:123587af84260c991dc5f62a6e7ef3d1c57dfddc99faacee508c71d287248459"}, + {file = "watchdog-4.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:093b23e6906a8b97051191a4a0c73a77ecc958121d42346274c6af6520dec175"}, + {file = "watchdog-4.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:611be3904f9843f0529c35a3ff3fd617449463cb4b73b1633950b3d97fa4bfb7"}, + {file = "watchdog-4.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:62c613ad689ddcb11707f030e722fa929f322ef7e4f18f5335d2b73c61a85c28"}, + {file = "watchdog-4.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d4925e4bf7b9bddd1c3de13c9b8a2cdb89a468f640e66fbfabaf735bd85b3e35"}, + {file = "watchdog-4.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cad0bbd66cd59fc474b4a4376bc5ac3fc698723510cbb64091c2a793b18654db"}, + {file = "watchdog-4.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a3c2c317a8fb53e5b3d25790553796105501a235343f5d2bf23bb8649c2c8709"}, + {file = "watchdog-4.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c9904904b6564d4ee8a1ed820db76185a3c96e05560c776c79a6ce5ab71888ba"}, + {file = "watchdog-4.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:667f3c579e813fcbad1b784db7a1aaa96524bed53437e119f6a2f5de4db04235"}, + {file = "watchdog-4.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d10a681c9a1d5a77e75c48a3b8e1a9f2ae2928eda463e8d33660437705659682"}, + {file = "watchdog-4.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0144c0ea9997b92615af1d94afc0c217e07ce2c14912c7b1a5731776329fcfc7"}, + {file = "watchdog-4.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:998d2be6976a0ee3a81fb8e2777900c28641fb5bfbd0c84717d89bca0addcdc5"}, + {file = "watchdog-4.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e7921319fe4430b11278d924ef66d4daa469fafb1da679a2e48c935fa27af193"}, + {file = "watchdog-4.0.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f0de0f284248ab40188f23380b03b59126d1479cd59940f2a34f8852db710625"}, + {file = "watchdog-4.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bca36be5707e81b9e6ce3208d92d95540d4ca244c006b61511753583c81c70dd"}, + {file = "watchdog-4.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ab998f567ebdf6b1da7dc1e5accfaa7c6992244629c0fdaef062f43249bd8dee"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:dddba7ca1c807045323b6af4ff80f5ddc4d654c8bce8317dde1bd96b128ed253"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_armv7l.whl", hash = "sha256:4513ec234c68b14d4161440e07f995f231be21a09329051e67a2118a7a612d2d"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_i686.whl", hash = "sha256:4107ac5ab936a63952dea2a46a734a23230aa2f6f9db1291bf171dac3ebd53c6"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_ppc64.whl", hash = "sha256:6e8c70d2cd745daec2a08734d9f63092b793ad97612470a0ee4cbb8f5f705c57"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f27279d060e2ab24c0aa98363ff906d2386aa6c4dc2f1a374655d4e02a6c5e5e"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:f8affdf3c0f0466e69f5b3917cdd042f89c8c63aebdb9f7c078996f607cdb0f5"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ac7041b385f04c047fcc2951dc001671dee1b7e0615cde772e84b01fbf68ee84"}, + {file = "watchdog-4.0.1-py3-none-win32.whl", hash = "sha256:206afc3d964f9a233e6ad34618ec60b9837d0582b500b63687e34011e15bb429"}, + {file = "watchdog-4.0.1-py3-none-win_amd64.whl", hash = "sha256:7577b3c43e5909623149f76b099ac49a1a01ca4e167d1785c76eb52fa585745a"}, + {file = "watchdog-4.0.1-py3-none-win_ia64.whl", hash = "sha256:d7b9f5f3299e8dd230880b6c55504a1f69cf1e4316275d1b215ebdd8187ec88d"}, + {file = "watchdog-4.0.1.tar.gz", hash = "sha256:eebaacf674fa25511e8867028d281e602ee6500045b57f43b08778082f7f8b44"}, ] [package.extras] @@ -1544,20 +1550,20 @@ watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "zipp" -version = "3.18.1" +version = "3.19.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, - {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, + {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, + {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [metadata] lock-version = "2.0" python-versions = ">=3.9" -content-hash = "33b8e5e4f5ea85fdc3e362575eff7506824b6f6d60231145992ee6122446b0f9" +content-hash = "f698ccbb6ecba2a3182497a16e5d88170a6f04c1c481cfbfd7405342d7e872e5" diff --git a/pyproject.toml b/pyproject.toml index d4d19c9..d47b899 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,31 +22,31 @@ classifiers = [ [tool.poetry.dependencies] python = ">=3.9" -pydantic = ">=2.7.1" -typing-extensions =">=4.11.0" -pydantic-extra-types =">=2.7.0" -pycountry = ">=23.12.11" +pydantic = ">=2.8.2" +typing-extensions =">=4.12.2" +pydantic-extra-types =">=2.9.0" +pycountry = ">=24.6.1" httpx = ">=0.27.0" boltons = ">=24.0.0" stamina = ">=24.2.0" -nh3 = "^0.2.17" -html2text = "^2024.2.26" -importlib-metadata = { version = ">=7.1.0", python = "<3.10" } +nh3 = ">=0.2.17" +html2text = ">=2024.2.26" +importlib-metadata = { version = ">=8.0.0", python = "<3.10" } eval-type-backport = { version = ">=0.2.0", python = "<3.10" } backports-strenum = {version = ">=1.3.1", python = "<3.11"} [tool.poetry.group.dev.dependencies] -ruff = "^0.3.5" -mypy = "^1.9.0" -types-boltons = "^23.1.0.20240331" -pytest = "^8.1.1" +ruff = "^0.5.0" +mypy = "^1.10.1" +types-boltons = "^24.0.0.20240423" +pytest = "^8.2.2" pytest-asyncio = "^0.23.5.post1" -pre-commit = "^3.7.0" -coverage = "^7.5.0" +pre-commit = "^3.7.1" +coverage = "^7.5.4" [tool.poetry.group.docs.dependencies] -mkdocs-material = "^9.5.18" -mkdocstrings = {extras = ["python"], version = "^0.24.0"} +mkdocs-material = "^9.5.28" +mkdocstrings = {extras = ["python"], version = "^0.25.1"} mkdocs-autorefs = "^1.0.1" [tool.ruff] @@ -59,6 +59,7 @@ fixable = ["ALL"] [tool.mypy] strict = true pretty = true +exclude = "/tests/" [tool.pytest.ini_options] asyncio_mode = "auto" diff --git a/src/pyanilist/__init__.py b/src/pyanilist/__init__.py index 61b9e67..0d8e12f 100644 --- a/src/pyanilist/__init__.py +++ b/src/pyanilist/__init__.py @@ -1,3 +1,8 @@ +from __future__ import annotations + +from httpx import HTTPStatusError +from pydantic import ValidationError + from ._clients import AniList, AsyncAniList from ._enums import ( CharacterRole, @@ -6,40 +11,11 @@ MediaRankType, MediaRelation, MediaSeason, + MediaSort, MediaSource, MediaStatus, MediaType, ) -from ._exceptions import ( - CloseError, - ConnectError, - ConnectTimeout, - CookieConflict, - DecodingError, - HTTPError, - HTTPStatusError, - InvalidURL, - LocalProtocolError, - NetworkError, - PoolTimeout, - ProtocolError, - ProxyError, - ReadError, - ReadTimeout, - RemoteProtocolError, - RequestError, - RequestNotRead, - ResponseNotRead, - StreamClosed, - StreamConsumed, - StreamError, - TimeoutException, - TooManyRedirects, - UnsupportedProtocol, - ValidationError, - WriteError, - WriteTimeout, -) from ._models import ( AiringSchedule, Character, @@ -61,10 +37,10 @@ StaffName, Studio, ) -from ._types import AniListID, AniListTitle, AniListYear, Color, CountryCode, HttpUrl, YearsActive +from ._types import FuzzyDateInt, YearsActive from ._version import Version, _get_version -__version__ = _get_version() +__version__ = _get_version() __version_tuple__ = Version(*map(int, __version__.split("."))) __all__ = [ @@ -78,17 +54,13 @@ "MediaRankType", "MediaRelation", "MediaSeason", + "MediaSort", "MediaSource", "MediaStatus", "MediaType", # Types - "AniListID", - "AniListTitle", - "AniListYear", - "Color", - "CountryCode", - "HttpUrl", "YearsActive", + "FuzzyDateInt", # Models "AiringSchedule", "Character", @@ -110,32 +82,6 @@ "StaffName", "Studio", # Exceptions - "CloseError", - "ConnectError", - "ConnectTimeout", - "CookieConflict", - "DecodingError", - "HTTPError", "HTTPStatusError", - "InvalidURL", - "LocalProtocolError", - "NetworkError", - "PoolTimeout", - "ProtocolError", - "ProxyError", - "ReadError", - "ReadTimeout", - "RemoteProtocolError", - "RequestError", - "RequestNotRead", - "ResponseNotRead", - "StreamClosed", - "StreamConsumed", - "StreamError", - "TimeoutException", - "TooManyRedirects", - "UnsupportedProtocol", - "WriteError", - "WriteTimeout", "ValidationError", ] diff --git a/src/pyanilist/_clients/__init__.py b/src/pyanilist/_clients/__init__.py index ce756d3..b9d4a57 100644 --- a/src/pyanilist/_clients/__init__.py +++ b/src/pyanilist/_clients/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from ._async import AsyncAniList from ._sync import AniList diff --git a/src/pyanilist/_clients/_async.py b/src/pyanilist/_clients/_async.py index dd5d824..48b03e2 100644 --- a/src/pyanilist/_clients/_async.py +++ b/src/pyanilist/_clients/_async.py @@ -2,24 +2,23 @@ from typing import Any -import httpx +from httpx import AsyncClient, HTTPError, Response from pydantic import PositiveInt, validate_call from stamina import retry_context -from .._enums import MediaFormat, MediaSeason, MediaStatus, MediaType +from .._enums import MediaFormat, MediaSeason, MediaSort, MediaSource, MediaStatus, MediaType from .._models import Media +from .._parser import post_process_response from .._query import query_string -from .._types import AniListID, AniListTitle, AniListYear, HTTPXAsyncClientKwargs -from .._utils import flatten, markdown_formatter, remove_null_fields, sanitize_description, text_formatter +from .._types import FuzzyDateInt, Iterable +from .._utils import to_anilist_case class AsyncAniList: @validate_call - def __init__( - self, api_url: str = "https://graphql.anilist.co", retries: PositiveInt = 5, **kwargs: HTTPXAsyncClientKwargs - ) -> None: + def __init__(self, api_url: str = "https://graphql.anilist.co", retries: PositiveInt = 5, **kwargs: Any) -> None: """ - Async AniList API client. + AniList API client. Parameters ---------- @@ -28,43 +27,22 @@ def __init__( retries : PositiveInt, optional Number of times to retry a failed request before raising an error. Default is 5. Set this to 1 to disable retrying. - kwargs : HTTPXAsyncClientKwargs, optional - Keyword arguments to pass to the underlying [httpx.AsyncClient()](https://www.python-httpx.org/api/#asyncclient) + kwargs : Any, optional + Keyword arguments to pass to the underlying [`httpx.AsyncClient`](https://www.python-httpx.org/api/#asyncclient) used to make the POST request. """ self.api_url = api_url self.retries = retries self.kwargs = kwargs - async def _post_request( - self, - id: AniListID | None = None, - season: MediaSeason | None = None, - season_year: AniListYear | None = None, - type: MediaType | None = None, - format: MediaFormat | None = None, - status: MediaStatus | None = None, - title: AniListTitle | None = None, - ) -> httpx.Response: + async def _post_request(self, **kwargs: Any) -> Response: """ Make a POST request to the AniList API. Parameters ---------- - id : AniListID, optional - AniList ID of the media as found in the URL: `https://anilist.co/{type}/{id}`. Default is None. - season : MediaSeason | None, optional - The season the media was initially released in. Default is None. - season_year : AniListYear | None, optional - The season year the media was initially released in. Default is None. - type : MediaType | None, optional - The type of the media; anime or manga. Default is None. - format : MediaFormat | None, optional - The format the media was released in. Default is None. - status : MediaStatus | None, optional - The current releasing status of the media. Default is None. - title : AniListTitle | None, optional - The string used for searching on AniList. Default is None. + kwargs : Any + Anilist query kwargs Raises ------ @@ -78,175 +56,233 @@ async def _post_request( The response object from the AniList API. """ - # map params with AniList's - query_variables = dict( - id=id, - season=season, - seasonYear=season_year, - type=type, - format=format, - status=status, - search=title, - ) - payload = { "query": query_string, - "variables": {key: value for key, value in query_variables.items() if value is not None}, + "variables": {to_anilist_case(key): value for key, value in kwargs.items() if value is not None}, } - async for attempt in retry_context(on=httpx.HTTPError, attempts=self.retries): + async for attempt in retry_context(on=HTTPError, attempts=self.retries): with attempt: - async with httpx.AsyncClient(**self.kwargs) as client: + async with AsyncClient(**self.kwargs) as client: response = await client.post(self.api_url, json=payload) response.raise_for_status() return response - @staticmethod - async def _process_description(dictionary: dict[str, Any]) -> dict[str, Any]: - """ - Anilist's description field takes a parameter `asHtml: boolean`, effectively - resulting in two differently formatted descriptions. - - Despite what the name implies, `asHtml` being `False` does not gurantee that there will be no `HTML` - tags in the description. - - So we do a bit of post processing: - - Sanitize the resulting descriptions - - Introduce two more formats derived from the original two, i.e, markdown and plain text - - Nest our newly acquired 4 descriptions into a single parent dictionary - - Example: - - This - ```py - { - "defaultDescription": "...", - "htmlDescription": "...", - } - ``` - Turns into this: - ```py - { - "description": { - "default": "...", - "html": "...", - "markdown": "...", - "text": "...", - } - } - ```py - - """ - - default_description = sanitize_description(dictionary.get("defaultDescription")) - html_description = sanitize_description(dictionary.get("htmlDescription")) - markdown_description = markdown_formatter(html_description) - text_description = text_formatter(default_description) - - # Delete the processed keys - dictionary.pop("defaultDescription", None) - dictionary.pop("htmlDescription", None) - - # Nest them inside a parent dictionary - return dict( - default=default_description, - html=html_description, - markdown=markdown_description, - text=text_description, - ) - - async def _post_process_response(self, response: httpx.Response) -> dict[str, Any]: - """ - Post-processes the response from AniList API. - - Parameters - ---------- - response : Response - The response object received from AniList API. - - Returns - ------- - dict[str, Any] - Processed dictionary containing media information. - - Notes - ----- - Currently, this function does two things: - 1. Flattening nested structures such as relations, studios, characters, and staff. - 2. Removing null fields to ensure a cleaner output. - """ - - # type hinting for IDE because it doesn't detect it automagically - dictionary: dict[str, Any] - - dictionary = response.json()["data"]["Media"] - - # "Flatten" the nested list of dictionaries of list of dictionaries... blah blah - # into simpler list for more intuitive access - relations = dictionary.get("relations") - dictionary.pop("relations", None) - flattened_relations = flatten(relations, "relationType") - - # same thing here - studios = dictionary.get("studios") - dictionary.pop("studios", None) - flattened_studios = flatten(studios, "isMain") - - # same thing here - characters = dictionary.get("characters") - dictionary.pop("characters", None) - flattened_characters = flatten(characters, "role") - - # same thing here - staff = dictionary.get("staff") - dictionary.pop("staff", None) - flattened_staff = flatten(staff, "role") - - # Process description - dictionary["description"] = await self._process_description(dictionary) - - # Process description of every relation - for relation in flattened_relations: - relation["description"] = await self._process_description(relation) - - # replace the original - dictionary["relations"] = flattened_relations - dictionary["studios"] = flattened_studios - dictionary["characters"] = flattened_characters - dictionary["staff"] = flattened_staff - - # self explanatory, details on why - # are in the function docstring - return remove_null_fields(dictionary) - @validate_call - async def search( + async def get( self, - title: AniListTitle, + search: str | None = None, + *, + id: int | None = None, + id_mal: int | None = None, + start_date: FuzzyDateInt | None = None, + end_date: FuzzyDateInt | None = None, season: MediaSeason | None = None, - season_year: AniListYear | None = None, + season_year: int | None = None, type: MediaType | None = None, format: MediaFormat | None = None, status: MediaStatus | None = None, + episodes: int | None = None, + chapters: int | None = None, + duration: int | None = None, + volumes: int | None = None, + is_adult: bool | None = None, + genre: str | None = None, + tag: str | None = None, + minimum_tag_rank: int | None = None, + tag_category: str | None = None, + licensed_by: str | None = None, + licensed_by_id: int | None = None, + average_score: int | None = None, + popularity: int | None = None, + source: MediaSource | None = None, + country_of_origin: str | None = None, + is_licensed: bool | None = None, + id_not: int | None = None, + id_in: Iterable[int] | None = None, + id_not_in: Iterable[int] | None = None, + id_mal_not: int | None = None, + id_mal_in: Iterable[int] | None = None, + id_mal_not_in: Iterable[int] | None = None, + start_date_greater: FuzzyDateInt | None = None, + start_date_lesser: FuzzyDateInt | None = None, + start_date_like: str | None = None, + end_date_greater: FuzzyDateInt | None = None, + end_date_lesser: FuzzyDateInt | None = None, + end_date_like: str | None = None, + format_in: Iterable[MediaFormat] | None = None, + format_not: MediaFormat | None = None, + format_not_in: Iterable[MediaFormat] | None = None, + status_in: Iterable[MediaStatus] | None = None, + status_not: MediaStatus | None = None, + status_not_in: Iterable[MediaStatus] | None = None, + episodes_greater: int | None = None, + episodes_lesser: int | None = None, + duration_greater: int | None = None, + duration_lesser: int | None = None, + chapters_greater: int | None = None, + chapters_lesser: int | None = None, + volumes_greater: int | None = None, + volumes_lesser: int | None = None, + genre_in: Iterable[str] | None = None, + genre_not_in: Iterable[str] | None = None, + tag_in: Iterable[str] | None = None, + tag_not_in: Iterable[str] | None = None, + tag_category_in: Iterable[str] | None = None, + tag_category_not_in: Iterable[str] | None = None, + licensed_by_in: Iterable[str] | None = None, + licensed_by_id_in: Iterable[int] | None = None, + average_score_not: int | None = None, + average_score_greater: int | None = None, + average_score_lesser: int | None = None, + popularity_not: int | None = None, + popularity_greater: int | None = None, + popularity_lesser: int | None = None, + source_in: Iterable[MediaSource] | None = None, + sort: Iterable[MediaSort] | None = None, ) -> Media: """ Search for media on AniList based on the provided parameters. Parameters ---------- - title : AniListTitle - The string used for searching on AniList. - season : MediaSeason | None, optional - The season the media was initially released in. Default is None. - season_year : AniListYear | None, optional - The season year the media was initially released in. Default is None. - type : MediaType | None, optional - The type of the media; anime or manga. Default is None. - format : MediaFormat | None, optional - The format the media was released in. Default is None. - status : MediaStatus | None, optional - The current releasing status of the media. Default is None. + search : str, optional + Filter by search query + id : int, optional + Filter by the media id + id_mal : int, optional + Filter by the media's MyAnimeList id + start_date : FuzzyDateInt, optional + Filter by the start date of the media + end_date : FuzzyDateInt, optional + Filter by the end date of the media + season : MediaSeason, optional + Filter by the season the media was released in + season_year : int, optional + The year of the season (Winter 2017 would also include December 2016 releases). Requires season argument + type : MediaType, optional + Filter by the media's type + format : MediaFormat, optional + Filter by the media's format + status : MediaStatus, optional + Filter by the media's current release status + episodes : int, optional + Filter by amount of episodes the media has + chapters : int, optional + Filter by the media's episode length + duration : int, optional + Filter by the media's chapter count + volumes : int, optional + Filter by the media's volume count + is_adult : bool, optional + Filter by if the media's intended for 18+ adult audiences + genre : str, optional + Filter by the media's genres + tag : str, optional + Filter by the media's tags + minimum_tag_rank : int, optional + Only apply the tags filter argument to tags above this rank. Default: 18 + tag_category : str, optional + Filter by the media's tags with in a tag category + licensed_by : str, optional + Filter media by sites name with a online streaming or reading license + licensed_by_id : int, optional + Filter media by sites id with a online streaming or reading license + average_score : int, optional + Filter by the media's average score + popularity : int, optional + Filter by the number of users with this media on their list + source : MediaSource, optional + Filter by the source type of the media + country_of_origin : CountryCode | str, optional + Filter by the media's country of origin + is_licensed : bool, optional + If the media is officially licensed or a self-published doujin release + id_not : int, optional + Filter by the media id + id_in : Iterable[int], optional + Filter by the media id + id_not_in : Iterable[int], optional + Filter by the media id + id_mal_not : int, optional + Filter by the media's MyAnimeList id + id_mal_in : Iterable[int], optional + Filter by the media's MyAnimeList id + id_mal_not_in : Iterable[int], optional + Filter by the media's MyAnimeList id + start_date_greater : FuzzyDateInt, optional + Filter by the start date of the media + start_date_lesser : FuzzyDateInt, optional + Filter by the start date of the media + start_date_like : str, optional + Filter by the start date of the media + end_date_greater : FuzzyDateInt, optional + Filter by the end date of the media + end_date_lesser : FuzzyDateInt, optional + Filter by the end date of the media + end_date_like : str, optional + Filter by the end date of the media + format_in : Iterable[MediaFormat], optional + Filter by the media's format + format_not : MediaFormat, optional + Filter by the media's format + format_not_in : Iterable[MediaFormat], optional + Filter by the media's format + status_in : Iterable[MediaStatus], optional + Filter by the media's current release status + status_not : MediaStatus, optional + Filter by the media's current release status + status_not_in : Iterable[MediaStatus], optional + Filter by the media's current release status + episodes_greater : int, optional + Filter by amount of episodes the media has + episodes_lesser : int, optional + Filter by amount of episodes the media has + duration_greater : int, optional + Filter by the media's episode length + duration_lesser : int, optional + Filter by the media's episode length + chapters_greater : int, optional + Filter by the media's chapter count + chapters_lesser : int, optional + Filter by the media's chapter count + volumes_greater : int, optional + Filter by the media's volume count + volumes_lesser : int, optional + Filter by the media's volume count + genre_in : Iterable[str], optional + Filter by the media's genres + genre_not_in : Iterable[str], optional + Filter by the media's genres + tag_in : Iterable[str], optional + Filter by the media's tags + tag_not_in : Iterable[str], optional + Filter by the media's tags + tag_category_in : Iterable[str], optional + Filter by the media's tags with in a tag category + tag_category_not_in : Iterable[str], optional + Filter by the media's tags with in a tag category + licensed_by_in : Iterable[str], optional + Filter media by sites name with a online streaming or reading license + licensed_by_id_in : Iterable[int], optional + Filter media by sites id with a online streaming or reading license + average_score_not : int, optional + Filter by the media's average score + average_score_greater : int, optional + Filter by the media's average score + average_score_lesser : int, optional + Filter by the media's average score + popularity_not : int, optional + Filter by the number of users with this media on their list + popularity_greater : int, optional + Filter by the number of users with this media on their list + popularity_lesser : int, optional + Filter by the number of users with this media on their list + source_in : Iterable[MediaSource], optional + Filter by the source type of the media + sort : Iterable[MediaSort], optional + The order the results will be returned in Raises ------ @@ -262,45 +298,76 @@ async def search( """ return Media.model_validate( - await self._post_process_response( + post_process_response( await self._post_request( - title=title, + id=id, + id_mal=id_mal, + start_date=start_date, + end_date=end_date, season=season, season_year=season_year, type=type, format=format, status=status, - ) - ) - ) - - @validate_call - async def get(self, id: AniListID) -> Media: - """ - Retrieve media information from AniList based on it's ID. - - Parameters - ---------- - id : int - AniList ID of the media as found in the URL: `https://anilist.co/{type}/{id}`. - - Raises - ------ - ValidationError - Invalid input - HTTPStatusError - AniList returned a non 2xx response. - - Returns - ------- - Media - A Media object representing the retrieved media information. - """ - - return Media.model_validate( - await self._post_process_response( - await self._post_request( - id=id, + episodes=episodes, + chapters=chapters, + duration=duration, + volumes=volumes, + is_adult=is_adult, + genre=genre, + tag=tag, + minimum_tag_rank=minimum_tag_rank, + tag_category=tag_category, + licensed_by=licensed_by, + licensed_by_id=licensed_by_id, + average_score=average_score, + popularity=popularity, + source=source, + country_of_origin=country_of_origin, + is_licensed=is_licensed, + search=search, + id_not=id_not, + id_in=id_in, + id_not_in=id_not_in, + id_mal_not=id_mal_not, + id_mal_in=id_mal_in, + id_mal_not_in=id_mal_not_in, + start_date_greater=start_date_greater, + start_date_lesser=start_date_lesser, + start_date_like=start_date_like, + end_date_greater=end_date_greater, + end_date_lesser=end_date_lesser, + end_date_like=end_date_like, + format_in=format_in, + format_not=format_not, + format_not_in=format_not_in, + status_in=status_in, + status_not=status_not, + status_not_in=status_not_in, + episodes_greater=episodes_greater, + episodes_lesser=episodes_lesser, + duration_greater=duration_greater, + duration_lesser=duration_lesser, + chapters_greater=chapters_greater, + chapters_lesser=chapters_lesser, + volumes_greater=volumes_greater, + volumes_lesser=volumes_lesser, + genre_in=genre_in, + genre_not_in=genre_not_in, + tag_in=tag_in, + tag_not_in=tag_not_in, + tag_category_in=tag_category_in, + tag_category_not_in=tag_category_not_in, + licensed_by_in=licensed_by_in, + licensed_by_id_in=licensed_by_id_in, + average_score_not=average_score_not, + average_score_greater=average_score_greater, + average_score_lesser=average_score_lesser, + popularity_not=popularity_not, + popularity_greater=popularity_greater, + popularity_lesser=popularity_lesser, + source_in=source_in, + sort=sort, ) ) ) diff --git a/src/pyanilist/_clients/_sync.py b/src/pyanilist/_clients/_sync.py index 33b2560..0427e39 100644 --- a/src/pyanilist/_clients/_sync.py +++ b/src/pyanilist/_clients/_sync.py @@ -2,22 +2,21 @@ from typing import Any -import httpx +from httpx import Client, HTTPError, Response from pydantic import PositiveInt, validate_call from stamina import retry_context -from .._enums import MediaFormat, MediaSeason, MediaStatus, MediaType +from .._enums import MediaFormat, MediaSeason, MediaSort, MediaSource, MediaStatus, MediaType from .._models import Media +from .._parser import post_process_response from .._query import query_string -from .._types import AniListID, AniListTitle, AniListYear, HTTPXClientKwargs -from .._utils import flatten, markdown_formatter, remove_null_fields, sanitize_description, text_formatter +from .._types import FuzzyDateInt, Iterable +from .._utils import to_anilist_case class AniList: @validate_call - def __init__( - self, api_url: str = "https://graphql.anilist.co", retries: PositiveInt = 5, **kwargs: HTTPXClientKwargs - ) -> None: + def __init__(self, api_url: str = "https://graphql.anilist.co", retries: PositiveInt = 5, **kwargs: Any) -> None: """ AniList API client. @@ -28,43 +27,22 @@ def __init__( retries : PositiveInt, optional Number of times to retry a failed request before raising an error. Default is 5. Set this to 1 to disable retrying. - kwargs : HTTPXClientKwargs, optional - Keyword arguments to pass to the underlying [httpx.Client()](https://www.python-httpx.org/api/#client) + kwargs : Any, optional + Keyword arguments to pass to the underlying [`httpx.Client`](https://www.python-httpx.org/api/#client) used to make the POST request. """ self.api_url = api_url self.retries = retries self.kwargs = kwargs - def _post_request( - self, - id: AniListID | None = None, - season: MediaSeason | None = None, - season_year: AniListYear | None = None, - type: MediaType | None = None, - format: MediaFormat | None = None, - status: MediaStatus | None = None, - title: AniListTitle | None = None, - ) -> httpx.Response: + def _post_request(self, **kwargs: Any) -> Response: """ Make a POST request to the AniList API. Parameters ---------- - id : AniListID, optional - AniList ID of the media as found in the URL: `https://anilist.co/{type}/{id}`. Default is None. - season : MediaSeason | None, optional - The season the media was initially released in. Default is None. - season_year : AniListYear | None, optional - The season year the media was initially released in. Default is None. - type : MediaType | None, optional - The type of the media; anime or manga. Default is None. - format : MediaFormat | None, optional - The format the media was released in. Default is None. - status : MediaStatus | None, optional - The current releasing status of the media. Default is None. - title : AniListTitle | None, optional - The string used for searching on AniList. Default is None. + kwargs : Any + Anilist query kwargs Raises ------ @@ -78,174 +56,232 @@ def _post_request( The response object from the AniList API. """ - # map params with AniList's - query_variables = dict( - id=id, - season=season, - seasonYear=season_year, - type=type, - format=format, - status=status, - search=title, - ) - payload = { "query": query_string, - "variables": {key: value for key, value in query_variables.items() if value is not None}, + "variables": {to_anilist_case(key): value for key, value in kwargs.items() if value is not None}, } - for attempt in retry_context(on=httpx.HTTPError, attempts=self.retries): + for attempt in retry_context(on=HTTPError, attempts=self.retries): with attempt: - with httpx.Client(**self.kwargs) as client: + with Client(**self.kwargs) as client: response = client.post(self.api_url, json=payload).raise_for_status() return response - @staticmethod - def _process_description(dictionary: dict[str, Any]) -> dict[str, Any]: - """ - Anilist's description field takes a parameter `asHtml: boolean`, effectively - resulting in two differently formatted descriptions. - - Despite what the name implies, `asHtml` being `False` does not gurantee that there will be no `HTML` - tags in the description. - - So we do a bit of post processing: - - Sanitize the resulting descriptions - - Introduce two more formats derived from the original two, i.e, markdown and plain text - - Nest our newly acquired 4 descriptions into a single parent dictionary - - Example: - - This - ```py - { - "defaultDescription": "...", - "htmlDescription": "...", - } - ``` - Turns into this: - ```py - { - "description": { - "default": "...", - "html": "...", - "markdown": "...", - "text": "...", - } - } - ```py - - """ - - default_description = sanitize_description(dictionary.get("defaultDescription")) - html_description = sanitize_description(dictionary.get("htmlDescription")) - markdown_description = markdown_formatter(html_description) - text_description = text_formatter(default_description) - - # Delete the processed keys - dictionary.pop("defaultDescription", None) - dictionary.pop("htmlDescription", None) - - # Nest them inside a parent dictionary - return dict( - default=default_description, - html=html_description, - markdown=markdown_description, - text=text_description, - ) - - def _post_process_response(self, response: httpx.Response) -> dict[str, Any]: - """ - Post-processes the response from AniList API. - - Parameters - ---------- - response : Response - The response object received from AniList API. - - Returns - ------- - dict[str, Any] - Processed dictionary containing media information. - - Notes - ----- - Currently, this function does two things: - 1. Flattening nested structures such as relations, studios, characters, and staff. - 2. Removing null fields to ensure a cleaner output. - """ - - # type hinting for IDE because it doesn't detect it automagically - dictionary: dict[str, Any] - - dictionary = response.json()["data"]["Media"] - - # "Flatten" the nested list of dictionaries of list of dictionaries... blah blah - # into simpler list for more intuitive access - relations = dictionary.get("relations") - dictionary.pop("relations", None) - flattened_relations = flatten(relations, "relationType") - - # same thing here - studios = dictionary.get("studios") - dictionary.pop("studios", None) - flattened_studios = flatten(studios, "isMain") - - # same thing here - characters = dictionary.get("characters") - dictionary.pop("characters", None) - flattened_characters = flatten(characters, "role") - - # same thing here - staff = dictionary.get("staff") - dictionary.pop("staff", None) - flattened_staff = flatten(staff, "role") - - # Process description - dictionary["description"] = self._process_description(dictionary) - - # Process description of every relation - for relation in flattened_relations: - relation["description"] = self._process_description(relation) - - # replace the original - dictionary["relations"] = flattened_relations - dictionary["studios"] = flattened_studios - dictionary["characters"] = flattened_characters - dictionary["staff"] = flattened_staff - - # self explanatory, details on why - # are in the function docstring - return remove_null_fields(dictionary) - @validate_call - def search( + def get( self, - title: AniListTitle, + search: str | None = None, + *, + id: int | None = None, + id_mal: int | None = None, + start_date: FuzzyDateInt | None = None, + end_date: FuzzyDateInt | None = None, season: MediaSeason | None = None, - season_year: AniListYear | None = None, + season_year: int | None = None, type: MediaType | None = None, format: MediaFormat | None = None, status: MediaStatus | None = None, + episodes: int | None = None, + chapters: int | None = None, + duration: int | None = None, + volumes: int | None = None, + is_adult: bool | None = None, + genre: str | None = None, + tag: str | None = None, + minimum_tag_rank: int | None = None, + tag_category: str | None = None, + licensed_by: str | None = None, + licensed_by_id: int | None = None, + average_score: int | None = None, + popularity: int | None = None, + source: MediaSource | None = None, + country_of_origin: str | None = None, + is_licensed: bool | None = None, + id_not: int | None = None, + id_in: Iterable[int] | None = None, + id_not_in: Iterable[int] | None = None, + id_mal_not: int | None = None, + id_mal_in: Iterable[int] | None = None, + id_mal_not_in: Iterable[int] | None = None, + start_date_greater: FuzzyDateInt | None = None, + start_date_lesser: FuzzyDateInt | None = None, + start_date_like: str | None = None, + end_date_greater: FuzzyDateInt | None = None, + end_date_lesser: FuzzyDateInt | None = None, + end_date_like: str | None = None, + format_in: Iterable[MediaFormat] | None = None, + format_not: MediaFormat | None = None, + format_not_in: Iterable[MediaFormat] | None = None, + status_in: Iterable[MediaStatus] | None = None, + status_not: MediaStatus | None = None, + status_not_in: Iterable[MediaStatus] | None = None, + episodes_greater: int | None = None, + episodes_lesser: int | None = None, + duration_greater: int | None = None, + duration_lesser: int | None = None, + chapters_greater: int | None = None, + chapters_lesser: int | None = None, + volumes_greater: int | None = None, + volumes_lesser: int | None = None, + genre_in: Iterable[str] | None = None, + genre_not_in: Iterable[str] | None = None, + tag_in: Iterable[str] | None = None, + tag_not_in: Iterable[str] | None = None, + tag_category_in: Iterable[str] | None = None, + tag_category_not_in: Iterable[str] | None = None, + licensed_by_in: Iterable[str] | None = None, + licensed_by_id_in: Iterable[int] | None = None, + average_score_not: int | None = None, + average_score_greater: int | None = None, + average_score_lesser: int | None = None, + popularity_not: int | None = None, + popularity_greater: int | None = None, + popularity_lesser: int | None = None, + source_in: Iterable[MediaSource] | None = None, + sort: Iterable[MediaSort] | None = None, ) -> Media: """ Search for media on AniList based on the provided parameters. Parameters ---------- - title : AniListTitle - The string used for searching on AniList. - season : MediaSeason | None, optional - The season the media was initially released in. Default is None. - season_year : AniListYear | None, optional - The season year the media was initially released in. Default is None. - type : MediaType | None, optional - The type of the media; anime or manga. Default is None. - format : MediaFormat | None, optional - The format the media was released in. Default is None. - status : MediaStatus | None, optional - The current releasing status of the media. Default is None. + search : str, optional + Filter by search query + id : int, optional + Filter by the media id + id_mal : int, optional + Filter by the media's MyAnimeList id + start_date : FuzzyDateInt, optional + Filter by the start date of the media + end_date : FuzzyDateInt, optional + Filter by the end date of the media + season : MediaSeason, optional + Filter by the season the media was released in + season_year : int, optional + The year of the season (Winter 2017 would also include December 2016 releases). Requires season argument + type : MediaType, optional + Filter by the media's type + format : MediaFormat, optional + Filter by the media's format + status : MediaStatus, optional + Filter by the media's current release status + episodes : int, optional + Filter by amount of episodes the media has + chapters : int, optional + Filter by the media's episode length + duration : int, optional + Filter by the media's chapter count + volumes : int, optional + Filter by the media's volume count + is_adult : bool, optional + Filter by if the media's intended for 18+ adult audiences + genre : str, optional + Filter by the media's genres + tag : str, optional + Filter by the media's tags + minimum_tag_rank : int, optional + Only apply the tags filter argument to tags above this rank. Default: 18 + tag_category : str, optional + Filter by the media's tags with in a tag category + licensed_by : str, optional + Filter media by sites name with a online streaming or reading license + licensed_by_id : int, optional + Filter media by sites id with a online streaming or reading license + average_score : int, optional + Filter by the media's average score + popularity : int, optional + Filter by the number of users with this media on their list + source : MediaSource, optional + Filter by the source type of the media + country_of_origin : CountryCode | str, optional + Filter by the media's country of origin + is_licensed : bool, optional + If the media is officially licensed or a self-published doujin release + id_not : int, optional + Filter by the media id + id_in : Iterable[int], optional + Filter by the media id + id_not_in : Iterable[int], optional + Filter by the media id + id_mal_not : int, optional + Filter by the media's MyAnimeList id + id_mal_in : Iterable[int], optional + Filter by the media's MyAnimeList id + id_mal_not_in : Iterable[int], optional + Filter by the media's MyAnimeList id + start_date_greater : FuzzyDateInt, optional + Filter by the start date of the media + start_date_lesser : FuzzyDateInt, optional + Filter by the start date of the media + start_date_like : str, optional + Filter by the start date of the media + end_date_greater : FuzzyDateInt, optional + Filter by the end date of the media + end_date_lesser : FuzzyDateInt, optional + Filter by the end date of the media + end_date_like : str, optional + Filter by the end date of the media + format_in : Iterable[MediaFormat], optional + Filter by the media's format + format_not : MediaFormat, optional + Filter by the media's format + format_not_in : Iterable[MediaFormat], optional + Filter by the media's format + status_in : Iterable[MediaStatus], optional + Filter by the media's current release status + status_not : MediaStatus, optional + Filter by the media's current release status + status_not_in : Iterable[MediaStatus], optional + Filter by the media's current release status + episodes_greater : int, optional + Filter by amount of episodes the media has + episodes_lesser : int, optional + Filter by amount of episodes the media has + duration_greater : int, optional + Filter by the media's episode length + duration_lesser : int, optional + Filter by the media's episode length + chapters_greater : int, optional + Filter by the media's chapter count + chapters_lesser : int, optional + Filter by the media's chapter count + volumes_greater : int, optional + Filter by the media's volume count + volumes_lesser : int, optional + Filter by the media's volume count + genre_in : Iterable[str], optional + Filter by the media's genres + genre_not_in : Iterable[str], optional + Filter by the media's genres + tag_in : Iterable[str], optional + Filter by the media's tags + tag_not_in : Iterable[str], optional + Filter by the media's tags + tag_category_in : Iterable[str], optional + Filter by the media's tags with in a tag category + tag_category_not_in : Iterable[str], optional + Filter by the media's tags with in a tag category + licensed_by_in : Iterable[str], optional + Filter media by sites name with a online streaming or reading license + licensed_by_id_in : Iterable[int], optional + Filter media by sites id with a online streaming or reading license + average_score_not : int, optional + Filter by the media's average score + average_score_greater : int, optional + Filter by the media's average score + average_score_lesser : int, optional + Filter by the media's average score + popularity_not : int, optional + Filter by the number of users with this media on their list + popularity_greater : int, optional + Filter by the number of users with this media on their list + popularity_lesser : int, optional + Filter by the number of users with this media on their list + source_in : Iterable[MediaSource], optional + Filter by the source type of the media + sort : Iterable[MediaSort], optional + The order the results will be returned in Raises ------ @@ -261,45 +297,76 @@ def search( """ return Media.model_validate( - self._post_process_response( + post_process_response( self._post_request( - title=title, + id=id, + id_mal=id_mal, + start_date=start_date, + end_date=end_date, season=season, season_year=season_year, type=type, format=format, status=status, - ) - ) - ) - - @validate_call - def get(self, id: AniListID) -> Media: - """ - Retrieve media information from AniList based on it's ID. - - Parameters - ---------- - id : int - AniList ID of the media as found in the URL: `https://anilist.co/{type}/{id}`. - - Raises - ------ - ValidationError - Invalid input - HTTPStatusError - AniList returned a non 2xx response. - - Returns - ------- - Media - A Media object representing the retrieved media information. - """ - - return Media.model_validate( - self._post_process_response( - self._post_request( - id=id, + episodes=episodes, + chapters=chapters, + duration=duration, + volumes=volumes, + is_adult=is_adult, + genre=genre, + tag=tag, + minimum_tag_rank=minimum_tag_rank, + tag_category=tag_category, + licensed_by=licensed_by, + licensed_by_id=licensed_by_id, + average_score=average_score, + popularity=popularity, + source=source, + country_of_origin=country_of_origin, + is_licensed=is_licensed, + search=search, + id_not=id_not, + id_in=id_in, + id_not_in=id_not_in, + id_mal_not=id_mal_not, + id_mal_in=id_mal_in, + id_mal_not_in=id_mal_not_in, + start_date_greater=start_date_greater, + start_date_lesser=start_date_lesser, + start_date_like=start_date_like, + end_date_greater=end_date_greater, + end_date_lesser=end_date_lesser, + end_date_like=end_date_like, + format_in=format_in, + format_not=format_not, + format_not_in=format_not_in, + status_in=status_in, + status_not=status_not, + status_not_in=status_not_in, + episodes_greater=episodes_greater, + episodes_lesser=episodes_lesser, + duration_greater=duration_greater, + duration_lesser=duration_lesser, + chapters_greater=chapters_greater, + chapters_lesser=chapters_lesser, + volumes_greater=volumes_greater, + volumes_lesser=volumes_lesser, + genre_in=genre_in, + genre_not_in=genre_not_in, + tag_in=tag_in, + tag_not_in=tag_not_in, + tag_category_in=tag_category_in, + tag_category_not_in=tag_category_not_in, + licensed_by_in=licensed_by_in, + licensed_by_id_in=licensed_by_id_in, + average_score_not=average_score_not, + average_score_greater=average_score_greater, + average_score_lesser=average_score_lesser, + popularity_not=popularity_not, + popularity_greater=popularity_greater, + popularity_lesser=popularity_lesser, + source_in=source_in, + sort=sort, ) ) ) diff --git a/src/pyanilist/_compat.py b/src/pyanilist/_compat.py index 018be54..ba07533 100644 --- a/src/pyanilist/_compat.py +++ b/src/pyanilist/_compat.py @@ -1,5 +1,7 @@ """Compatibility module to for older python versions""" +from __future__ import annotations + import sys if sys.version_info >= (3, 11): diff --git a/src/pyanilist/_enums.py b/src/pyanilist/_enums.py index 127be97..8f81061 100644 --- a/src/pyanilist/_enums.py +++ b/src/pyanilist/_enums.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from ._compat import StrEnum @@ -15,6 +17,8 @@ def title(self) -> str: # type: ignore "TV_SHORT": "TV Short", "OVA": "OVA", "ONA": "ONA", + "ID": "ID", + "ID_DESC": "ID Desc", } if self.value in exceptions: @@ -237,6 +241,48 @@ class MediaRankType(BaseStrEnum): """Ranking is based on the media's popularity""" +class MediaSort(BaseStrEnum): + """Media sort enums""" + + ID = "ID" + ID_DESC = "ID_DESC" + TITLE_ROMAJI = "TITLE_ROMAJI" + TITLE_ROMAJI_DESC = "TITLE_ROMAJI_DESC" + TITLE_ENGLISH = "TITLE_ENGLISH" + TITLE_ENGLISH_DESC = "TITLE_ENGLISH_DESC" + TITLE_NATIVE = "TITLE_NATIVE" + TITLE_NATIVE_DESC = "TITLE_NATIVE_DESC" + TYPE = "TYPE" + TYPE_DESC = "TYPE_DESC" + FORMAT = "FORMAT" + FORMAT_DESC = "FORMAT_DESC" + START_DATE = "START_DATE" + START_DATE_DESC = "START_DATE_DESC" + END_DATE = "END_DATE" + END_DATE_DESC = "END_DATE_DESC" + SCORE = "SCORE" + SCORE_DESC = "SCORE_DESC" + POPULARITY = "POPULARITY" + POPULARITY_DESC = "POPULARITY_DESC" + TRENDING = "TRENDING" + TRENDING_DESC = "TRENDING_DESC" + EPISODES = "EPISODES" + EPISODES_DESC = "EPISODES_DESC" + DURATION = "DURATION" + DURATION_DESC = "DURATION_DESC" + STATUS = "STATUS" + STATUS_DESC = "STATUS_DESC" + CHAPTERS = "CHAPTERS" + CHAPTERS_DESC = "CHAPTERS_DESC" + VOLUMES = "VOLUMES" + VOLUMES_DESC = "VOLUMES_DESC" + UPDATED_AT = "UPDATED_AT" + UPDATED_AT_DESC = "UPDATED_AT_DESC" + SEARCH_MATCH = "SEARCH_MATCH" + FAVOURITES = "FAVOURITES" + FAVOURITES_DESC = "FAVOURITES_DESC" + + __all__ = [ "CharacterRole", "ExternalLinkType", @@ -244,6 +290,7 @@ class MediaRankType(BaseStrEnum): "MediaRankType", "MediaRelation", "MediaSeason", + "MediaSort", "MediaSource", "MediaStatus", "MediaType", diff --git a/src/pyanilist/_exceptions.py b/src/pyanilist/_exceptions.py deleted file mode 100644 index 2a29f2f..0000000 --- a/src/pyanilist/_exceptions.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -This module simply re-exports Exceptions from -[`httpx`](https://www.python-httpx.org/exceptions/) and -[`pydantic`](https://docs.pydantic.dev/latest/api/pydantic_core/#pydantic_core.ValidationError) for convenience. -""" - -from httpx import ( - CloseError, - ConnectError, - ConnectTimeout, - CookieConflict, - DecodingError, - HTTPError, - HTTPStatusError, - InvalidURL, - LocalProtocolError, - NetworkError, - PoolTimeout, - ProtocolError, - ProxyError, - ReadError, - ReadTimeout, - RemoteProtocolError, - RequestError, - RequestNotRead, - ResponseNotRead, - StreamClosed, - StreamConsumed, - StreamError, - TimeoutException, - TooManyRedirects, - UnsupportedProtocol, - WriteError, - WriteTimeout, -) -from pydantic import ValidationError - -__all__ = [ - "CloseError", - "ConnectError", - "ConnectTimeout", - "CookieConflict", - "DecodingError", - "HTTPError", - "HTTPStatusError", - "InvalidURL", - "LocalProtocolError", - "NetworkError", - "PoolTimeout", - "ProtocolError", - "ProxyError", - "ReadError", - "ReadTimeout", - "RemoteProtocolError", - "RequestError", - "RequestNotRead", - "ResponseNotRead", - "StreamClosed", - "StreamConsumed", - "StreamError", - "TimeoutException", - "TooManyRedirects", - "UnsupportedProtocol", - "WriteError", - "WriteTimeout", - "ValidationError", -] diff --git a/src/pyanilist/_models.py b/src/pyanilist/_models.py index 4f1b0aa..1cf86e6 100644 --- a/src/pyanilist/_models.py +++ b/src/pyanilist/_models.py @@ -2,8 +2,10 @@ from datetime import datetime -from pydantic import AliasGenerator, BaseModel, ConfigDict +from pydantic import AliasGenerator, BaseModel, ConfigDict, HttpUrl from pydantic.alias_generators import to_camel +from pydantic_extra_types.color import Color +from pydantic_extra_types.country import CountryAlpha2 as CountryCode from ._enums import ( CharacterRole, @@ -16,7 +18,7 @@ MediaStatus, MediaType, ) -from ._types import Color, CountryCode, HttpUrl, YearsActive +from ._types import FuzzyDateInt, YearsActive class ParentModel(BaseModel): @@ -81,6 +83,20 @@ def iso_format(self) -> str: else: return "" + def as_int(self) -> FuzzyDateInt: + """ + Return an 8 digit long date integer (YYYYMMDD). + Unknown dates represented by 0. + For example, 2016 is 20160000 and May 1976 is 19760500 + + The result is equivalent to AniList's FuzzyDateInt type. + """ + year = str(self.year).zfill(4) if self.year is not None else "1000" + month = str(self.month).zfill(2) if self.month is not None else "00" + day = str(self.day).zfill(2) if self.day is not None else "00" + + return int(f"{year}{month}{day}") + class MediaTrailer(ParentModel): """Media trailer or advertisement.""" diff --git a/src/pyanilist/_parser.py b/src/pyanilist/_parser.py new file mode 100644 index 0000000..75eb38b --- /dev/null +++ b/src/pyanilist/_parser.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from ._utils import flatten, markdown_formatter, remove_null_fields, sanitize_description, text_formatter + +if TYPE_CHECKING: # pragma: no cover + from httpx import Response + + +def process_description(dictionary: dict[str, Any]) -> dict[str, Any]: + """ + Anilist's description field takes a parameter `asHtml: boolean`, effectively + resulting in two differently formatted descriptions. + + Despite what the name implies, `asHtml` being `False` does not gurantee that there will be no `HTML` + tags in the description. + + So we do a bit of post processing: + - Sanitize the resulting descriptions + - Introduce two more formats derived from the original two, i.e, markdown and plain text + - Nest our newly acquired 4 descriptions into a single parent dictionary + + Example: + + This + ```py + { + "defaultDescription": "...", + "htmlDescription": "...", + } + ``` + Turns into this: + ```py + { + "description": { + "default": "...", + "html": "...", + "markdown": "...", + "text": "...", + } + } + ```py + + """ + + default_description = sanitize_description(dictionary.get("defaultDescription")) + html_description = sanitize_description(dictionary.get("htmlDescription")) + markdown_description = markdown_formatter(html_description) + text_description = text_formatter(default_description) + + # Delete the processed keys + dictionary.pop("defaultDescription", None) + dictionary.pop("htmlDescription", None) + + # Nest them inside a parent dictionary + return dict( + default=default_description, + html=html_description, + markdown=markdown_description, + text=text_description, + ) + + +def post_process_response(response: Response) -> dict[str, Any]: + """ + Post-processes the response from AniList API. + + Parameters + ---------- + response : Response + The response object received from AniList API. + + Returns + ------- + dict[str, Any] + Processed dictionary containing media information. + + Notes + ----- + Currently, this function does two things: + 1. Flattening nested structures such as relations, studios, characters, and staff. + 2. Removing null fields to ensure a cleaner output. + """ + + # type hinting for IDE because it doesn't detect it automagically + dictionary: dict[str, Any] + + dictionary = response.json()["data"]["Media"] + + # "Flatten" the nested list of dictionaries of list of dictionaries... blah blah + # into simpler list for more intuitive access + relations = dictionary.get("relations") + dictionary.pop("relations", None) + flattened_relations = flatten(relations, "relationType") + + # same thing here + studios = dictionary.get("studios") + dictionary.pop("studios", None) + flattened_studios = flatten(studios, "isMain") + + # same thing here + characters = dictionary.get("characters") + dictionary.pop("characters", None) + flattened_characters = flatten(characters, "role") + + # same thing here + staff = dictionary.get("staff") + dictionary.pop("staff", None) + flattened_staff = flatten(staff, "role") + + # Process description + dictionary["description"] = process_description(dictionary) + + # Process description of every relation + for relation in flattened_relations: + relation["description"] = process_description(relation) + + # replace the original + dictionary["relations"] = flattened_relations + dictionary["studios"] = flattened_studios + dictionary["characters"] = flattened_characters + dictionary["staff"] = flattened_staff + + # self explanatory, details on why + # are in the function docstring + return remove_null_fields(dictionary) diff --git a/src/pyanilist/_query.py b/src/pyanilist/_query.py index e95595b..cc21b8c 100644 --- a/src/pyanilist/_query.py +++ b/src/pyanilist/_query.py @@ -1,6 +1,146 @@ query_string = """ -query ($id: Int, $season: MediaSeason, $seasonYear: Int, $type: MediaType, $format: MediaFormat, $status: MediaStatus, $search: String) { - Media(id: $id, season: $season, seasonYear: $seasonYear, type: $type, format: $format, status: $status, search: $search) { +query ( + $id: Int + $idMal: Int + $startDate: FuzzyDateInt + $endDate: FuzzyDateInt + $season: MediaSeason + $seasonYear: Int + $type: MediaType + $format: MediaFormat + $status: MediaStatus + $episodes: Int + $chapters: Int + $duration: Int + $volumes: Int + $isAdult: Boolean + $genre: String + $tag: String + $minimumTagRank: Int + $tagCategory: String + # $onList: Boolean + $licensedBy: String + $licensedById: Int + $averageScore: Int + $popularity: Int + $source: MediaSource + $countryOfOrigin: CountryCode + $isLicensed: Boolean + $search: String + $id_not: Int + $id_in: [Int] + $id_not_in: [Int] + $idMal_not: Int + $idMal_in: [Int] + $idMal_not_in: [Int] + $startDate_greater: FuzzyDateInt + $startDate_lesser: FuzzyDateInt + $startDate_like: String + $endDate_greater: FuzzyDateInt + $endDate_lesser: FuzzyDateInt + $endDate_like: String + $format_in: [MediaFormat] + $format_not: MediaFormat + $format_not_in: [MediaFormat] + $status_in: [MediaStatus] + $status_not: MediaStatus + $status_not_in: [MediaStatus] + $episodes_greater: Int + $episodes_lesser: Int + $duration_greater: Int + $duration_lesser: Int + $chapters_greater: Int + $chapters_lesser: Int + $volumes_greater: Int + $volumes_lesser: Int + $genre_in: [String] + $genre_not_in: [String] + $tag_in: [String] + $tag_not_in: [String] + $tagCategory_in: [String] + $tagCategory_not_in: [String] + $licensedBy_in: [String] + $licensedById_in: [Int] + $averageScore_not: Int + $averageScore_greater: Int + $averageScore_lesser: Int + $popularity_not: Int + $popularity_greater: Int + $popularity_lesser: Int + $source_in: [MediaSource] + $sort: [MediaSort] +) { + Media( + id: $id + idMal: $idMal + startDate: $startDate + endDate: $endDate + season: $season + seasonYear: $seasonYear + type: $type + format: $format + status: $status + episodes: $episodes + chapters: $chapters + duration: $duration + volumes: $volumes + isAdult: $isAdult + genre: $genre + tag: $tag + minimumTagRank: $minimumTagRank + tagCategory: $tagCategory + # $onList: $onList + licensedBy: $licensedBy + licensedById: $licensedById + averageScore: $averageScore + popularity: $popularity + source: $source + countryOfOrigin: $countryOfOrigin + isLicensed: $isLicensed + search: $search + id_not: $id_not + id_in: $id_in + id_not_in: $id_not_in + idMal_not: $idMal_not + idMal_in: $idMal_in + idMal_not_in: $idMal_not_in + startDate_greater: $startDate_greater + startDate_lesser: $startDate_lesser + startDate_like: $startDate_like + endDate_greater: $endDate_greater + endDate_lesser: $endDate_lesser + endDate_like: $endDate_like + format_in: $format_in + format_not: $format_not + format_not_in: $format_not_in + status_in: $status_in + status_not: $status_not + status_not_in: $status_not_in + episodes_greater: $episodes_greater + episodes_lesser: $episodes_lesser + duration_greater: $duration_greater + duration_lesser: $duration_lesser + chapters_greater: $chapters_greater + chapters_lesser: $chapters_lesser + volumes_greater: $volumes_greater + volumes_lesser: $volumes_lesser + genre_in: $genre_in + genre_not_in: $genre_not_in + tag_in: $tag_in + tag_not_in: $tag_not_in + tagCategory_in: $tagCategory_in + tagCategory_not_in: $tagCategory_not_in + licensedBy_in: $licensedBy_in + licensedById_in: $licensedById_in + averageScore_not: $averageScore_not + averageScore_greater: $averageScore_greater + averageScore_lesser: $averageScore_lesser + popularity_not: $popularity_not + popularity_greater: $popularity_greater + popularity_lesser: $popularity_lesser + source_in: $source_in + sort: $sort + ) { id idMal title { @@ -287,5 +427,4 @@ siteUrl } } - """ diff --git a/src/pyanilist/_types.py b/src/pyanilist/_types.py index bbf2402..abe23ba 100644 --- a/src/pyanilist/_types.py +++ b/src/pyanilist/_types.py @@ -1,46 +1,33 @@ -""" -Aside from providing it's own types, this module also re-exports the following from pydantic for convenience: -- [HttpUrl](https://docs.pydantic.dev/latest/api/networks/#pydantic.networks.HttpUrl) -- [Color](https://docs.pydantic.dev/latest/api/pydantic_extra_types_color/#pydantic_extra_types.color.Color) -- [CountryAlpha2 as CountryCoude](https://docs.pydantic.dev/latest/api/pydantic_extra_types_country/#pydantic_extra_types.country.CountryAlpha2) -""" - from __future__ import annotations -from typing import Annotated, Any +from typing import Annotated + +from pydantic import Field +from typing_extensions import NamedTuple, TypeAlias, TypeVar, Union -from pydantic import Field, HttpUrl -from pydantic_extra_types.color import Color -from pydantic_extra_types.country import CountryAlpha2 as CountryCode -from typing_extensions import NamedTuple, TypeAlias +# A simpler Iterable type instead of collections.abc.Iterable +# to stop pydantic from converting them to ValidatorIterator +# https://github.com/pydantic/pydantic/issues/9541 +T = TypeVar("T") +Iterable: TypeAlias = Union[set[T], tuple[T, ...], list[T]] class YearsActive(NamedTuple): """ Simple Named Tuple for - `_models.Staff.years_active` + [`Staff.years_active`][pyanilist._models.Staff.years_active] """ start_year: int | None = None end_year: int | None = None -AniListID = Annotated[int, Field(gt=0, description="AniList ID as found in the URL: https://anilist.co/{type}/{id}")] -AniListYear = Annotated[int, Field(ge=1000, description="Release Year")] -AniListTitle = Annotated[str, Field(min_length=1, description="Title of the media")] - -HTTPXClientKwargs: TypeAlias = Any -"""Simple TypeAlias to refer to `httpx.Client()` kwargs""" - -HTTPXAsyncClientKwargs: TypeAlias = Any -"""Simple TypeAlias to refer to `httpx.AsyncClient()` kwargs""" - -__all__ = [ - "AniListID", - "AniListTitle", - "AniListYear", - "Color", - "CountryCode", - "HttpUrl", - "YearsActive", +FuzzyDateInt = Annotated[ + int, + Field( + ge=10000000, + le=99999999, + description="8 digit long date integer (YYYYMMDD). Unknown dates represented by 0. E.g. 2016: 20160000, May 1976: 19760500", + ), ] +"""8 digit long date integer (YYYYMMDD). Unknown dates represented by 0. E.g. 2016: 20160000, May 1976: 19760500""" \ No newline at end of file diff --git a/src/pyanilist/_utils.py b/src/pyanilist/_utils.py index 4881e5d..94f9d6d 100644 --- a/src/pyanilist/_utils.py +++ b/src/pyanilist/_utils.py @@ -99,3 +99,93 @@ def remove_null_fields(dictionary: dict[str, Any]) -> dict[str, Any]: `None`, empty list, or empty dictionary. """ return remap(dictionary, lambda path, key, value: value not in [None, {}, []]) # type: ignore + + +def to_anilist_case(var: str) -> str: + """ + Anilist doesn't stick to a single casing. + Most of it is camelCase but then there's some made up stuff in there too. + So can do nothing but create a mapping from snake_case to anilistCase + + Parameters + ---------- + var : str + A snake_case variable. + + Returns + ------- + str + Same thing but in anilist's case. + """ + casemap = { + "id": "id", + "id_mal": "idMal", + "start_date": "startDate", + "end_date": "endDate", + "season": "season", + "season_year": "seasonYear", + "type": "type", + "format": "format", + "status": "status", + "episodes": "episodes", + "chapters": "chapters", + "duration": "duration", + "volumes": "volumes", + "is_adult": "isAdult", + "genre": "genre", + "tag": "tag", + "minimum_tag_rank": "minimumTagRank", + "tag_category": "tagCategory", + "licensed_by": "licensedBy", + "licensed_by_id": "licensedById", + "average_score": "averageScore", + "popularity": "popularity", + "source": "source", + "country_of_origin": "countryOfOrigin", + "is_licensed": "isLicensed", + "search": "search", + "id_not": "id_not", + "id_in": "id_in", + "id_not_in": "id_not_in", + "id_mal_not": "idMal_not", + "id_mal_in": "idMal_in", + "id_mal_not_in": "idMal_not_in", + "start_date_greater": "startDate_greater", + "start_date_lesser": "startDate_lesser", + "start_date_like": "startDate_like", + "end_date_greater": "endDate_greater", + "end_date_lesser": "endDate_lesser", + "end_date_like": "endDate_like", + "format_in": "format_in", + "format_not": "format_not", + "format_not_in": "format_not_in", + "status_in": "status_in", + "status_not": "status_not", + "status_not_in": "status_not_in", + "episodes_greater": "episodes_greater", + "episodes_lesser": "episodes_lesser", + "duration_greater": "duration_greater", + "duration_lesser": "duration_lesser", + "chapters_greater": "chapters_greater", + "chapters_lesser": "chapters_lesser", + "volumes_greater": "volumes_greater", + "volumes_lesser": "volumes_lesser", + "genre_in": "genre_in", + "genre_not_in": "genre_not_in", + "tag_in": "tag_in", + "tag_not_in": "tag_not_in", + "tag_category_in": "tagCategory_in", + "tag_category_not_in": "tagCategory_not_in", + "licensed_by_in": "licensedBy_in", + "licensed_by_id_in": "licensedById_in", + "average_score_not": "averageScore_not", + "average_score_greater": "averageScore_greater", + "average_score_lesser": "averageScore_lesser", + "popularity_not": "popularity_not", + "popularity_greater": "popularity_greater", + "popularity_lesser": "popularity_lesser", + "source_in": "source_in", + "sort": "sort", + } + + return casemap[var] \ No newline at end of file diff --git a/src/pyanilist/_version.py b/src/pyanilist/_version.py index c44892e..d0d415e 100644 --- a/src/pyanilist/_version.py +++ b/src/pyanilist/_version.py @@ -1,22 +1,19 @@ +from __future__ import annotations + from typing_extensions import NamedTuple from ._compat import metadata class Version(NamedTuple): - """Version tuple based on SemVer""" - major: int - """Major version number""" minor: int - """Minor version number""" - patch: int - """Patch version number""" + micro: int def _get_version() -> str: """ - Get the version of juicenet + Get the version of pyanilist """ try: return metadata.version("pyanilist") diff --git a/tests/test_anilist.py b/tests/test_anilist.py index 5593c24..27779ee 100644 --- a/tests/test_anilist.py +++ b/tests/test_anilist.py @@ -1,7 +1,6 @@ from pyanilist import ( AniList, CharacterRole, - HttpUrl, MediaFormat, MediaSeason, MediaSource, @@ -13,7 +12,7 @@ def test_anilist_anime() -> None: - media = AniList().search("Attack on titan", type=MediaType.ANIME) + media = AniList().get("Attack on titan", type=MediaType.ANIME) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 assert media.start_date.iso_format() == "2013-04-07" @@ -47,11 +46,11 @@ def test_anilist_anime() -> None: ("Daizen Komatsuda", "Storyboard (ep 23)"), ("You Moriyama", "Key Animation (OP1)"), ] - assert media.site_url == HttpUrl("https://anilist.co/anime/16498") + assert media.site_url.__str__() == "https://anilist.co/anime/16498" def test_anilist_manga() -> None: - media = AniList().search("Attack on titan", type=MediaType.MANGA) + media = AniList().get("Attack on titan", type=MediaType.MANGA) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2009 assert media.start_date.iso_format() == "2009-09-09" @@ -73,34 +72,17 @@ def test_anilist_manga() -> None: ("Shintarou Kawakubo", "Editing"), ("Yifeng Zhang", "Translator (Chinese)"), ] - assert media.site_url == HttpUrl("https://anilist.co/manga/53390") + assert media.site_url.__str__() == "https://anilist.co/manga/53390" -def test_anilist_with_some_constraints() -> None: - media = AniList().search( - "violet evergarden", type=MediaType.MANGA, format=MediaFormat.NOVEL, status=MediaStatus.FINISHED - ) - assert media.title.romaji == "Violet Evergarden" - assert media.start_date.year == 2015 - assert media.start_date.iso_format() == "2015-12-25" - assert media.end_date.iso_format() == "2016-12-26" - assert media.source is MediaSource.ORIGINAL - assert media.type is MediaType.MANGA - assert [(staff.name.full, staff.role) for staff in media.staff] == [ - ("Akiko Takase", "Illustration"), - ("Kana Akatsuki", "Story"), - ] - assert media.site_url == HttpUrl("https://anilist.co/manga/97298") - - -def test_anilist_with_all_constraints() -> None: - media = AniList().search( +def test_anilist_with_constraints() -> None: + media = AniList().get( "My Hero Academia", season=MediaSeason.SPRING, season_year=2016, type=MediaType.ANIME, format=MediaFormat.TV, - status=MediaStatus.FINISHED, + status_in=[MediaStatus.FINISHED], ) assert media.title.romaji == "Boku no Hero Academia" assert media.start_date.year == 2016 @@ -135,11 +117,11 @@ def test_anilist_with_all_constraints() -> None: ("Katsuyuki Kodera", "Storyboard (eps 5, 9, 13)"), ("Kenji Nagasaki", "Storyboard (OP, ED, eps 1, 2)"), ] - assert media.site_url == HttpUrl("https://anilist.co/anime/21459") + assert media.site_url.__str__() == "https://anilist.co/anime/21459" def test_anilist_id() -> None: - media = AniList().get(16498) + media = AniList().get(id=16498) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 assert media.source is MediaSource.MANGA @@ -173,11 +155,11 @@ def test_anilist_id() -> None: ("Daizen Komatsuda", "Storyboard (ep 23)"), ("You Moriyama", "Key Animation (OP1)"), ] - assert media.site_url == HttpUrl("https://anilist.co/anime/16498") + assert media.site_url.__str__() == "https://anilist.co/anime/16498" def test_anilist_description() -> None: - media = AniList().get(106794) + media = AniList().get(id=106794) assert media.title.english == "Bloom Into You Anthology" assert media.start_date.year == 2018 assert media.source is MediaSource.MANGA @@ -190,11 +172,11 @@ def test_anilist_description() -> None: assert media.relations[0].description.html == BloomIntoYouDescriptions.HTML assert media.relations[0].description.markdown == BloomIntoYouDescriptions.MARKDOWN assert media.relations[0].description.text == BloomIntoYouDescriptions.TEXT - assert media.site_url == HttpUrl("https://anilist.co/manga/106794") + assert media.site_url.__str__() == "https://anilist.co/manga/106794" def test_anilist_characters() -> None: - media = AniList().get(20954) + media = AniList().get(id=20954) assert media.title.english == "A Silent Voice" assert media.start_date.year == 2016 assert media.source is MediaSource.MANGA @@ -203,4 +185,4 @@ def test_anilist_characters() -> None: "Shouya Ishida", "Shouko Nishimiya", ] - assert media.site_url == HttpUrl("https://anilist.co/anime/20954") + assert media.site_url.__str__() == "https://anilist.co/anime/20954" diff --git a/tests/test_async_anilist.py b/tests/test_async_anilist.py index 0724fcd..f20cda9 100644 --- a/tests/test_async_anilist.py +++ b/tests/test_async_anilist.py @@ -1,7 +1,6 @@ from pyanilist import ( AsyncAniList, CharacterRole, - HttpUrl, MediaFormat, MediaSeason, MediaSource, @@ -13,7 +12,7 @@ async def test_anilist_anime() -> None: - media = await AsyncAniList().search("Attack on titan", type=MediaType.ANIME) + media = await AsyncAniList().get("Attack on titan", type=MediaType.ANIME) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 assert media.start_date.iso_format() == "2013-04-07" @@ -47,11 +46,11 @@ async def test_anilist_anime() -> None: ("Daizen Komatsuda", "Storyboard (ep 23)"), ("You Moriyama", "Key Animation (OP1)"), ] - assert media.site_url == HttpUrl("https://anilist.co/anime/16498") + assert media.site_url.__str__() == "https://anilist.co/anime/16498" async def test_anilist_manga() -> None: - media = await AsyncAniList().search("Attack on titan", type=MediaType.MANGA) + media = await AsyncAniList().get("Attack on titan", type=MediaType.MANGA) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2009 assert media.start_date.iso_format() == "2009-09-09" @@ -73,34 +72,17 @@ async def test_anilist_manga() -> None: ("Shintarou Kawakubo", "Editing"), ("Yifeng Zhang", "Translator (Chinese)"), ] - assert media.site_url == HttpUrl("https://anilist.co/manga/53390") + assert media.site_url.__str__() == "https://anilist.co/manga/53390" -async def test_anilist_with_some_constraints() -> None: - media = await AsyncAniList().search( - "violet evergarden", type=MediaType.MANGA, format=MediaFormat.NOVEL, status=MediaStatus.FINISHED - ) - assert media.title.romaji == "Violet Evergarden" - assert media.start_date.year == 2015 - assert media.start_date.iso_format() == "2015-12-25" - assert media.end_date.iso_format() == "2016-12-26" - assert media.source is MediaSource.ORIGINAL - assert media.type is MediaType.MANGA - assert [(staff.name.full, staff.role) for staff in media.staff] == [ - ("Akiko Takase", "Illustration"), - ("Kana Akatsuki", "Story"), - ] - assert media.site_url == HttpUrl("https://anilist.co/manga/97298") - - -async def test_anilist_with_all_constraints() -> None: - media = await AsyncAniList().search( +async def test_anilist_with_constraints() -> None: + media = await AsyncAniList().get( "My Hero Academia", season=MediaSeason.SPRING, season_year=2016, type=MediaType.ANIME, format=MediaFormat.TV, - status=MediaStatus.FINISHED, + status_in=[MediaStatus.FINISHED], ) assert media.title.romaji == "Boku no Hero Academia" assert media.start_date.year == 2016 @@ -135,11 +117,11 @@ async def test_anilist_with_all_constraints() -> None: ("Katsuyuki Kodera", "Storyboard (eps 5, 9, 13)"), ("Kenji Nagasaki", "Storyboard (OP, ED, eps 1, 2)"), ] - assert media.site_url == HttpUrl("https://anilist.co/anime/21459") + assert media.site_url.__str__() == "https://anilist.co/anime/21459" async def test_anilist_id() -> None: - media = await AsyncAniList().get(16498) + media = await AsyncAniList().get(id=16498) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 assert media.source is MediaSource.MANGA @@ -173,11 +155,11 @@ async def test_anilist_id() -> None: ("Daizen Komatsuda", "Storyboard (ep 23)"), ("You Moriyama", "Key Animation (OP1)"), ] - assert media.site_url == HttpUrl("https://anilist.co/anime/16498") + assert media.site_url.__str__() == "https://anilist.co/anime/16498" async def test_anilist_description() -> None: - media = await AsyncAniList().get(106794) + media = await AsyncAniList().get(id=106794) assert media.title.english == "Bloom Into You Anthology" assert media.start_date.year == 2018 assert media.source is MediaSource.MANGA @@ -190,11 +172,11 @@ async def test_anilist_description() -> None: assert media.relations[0].description.html == BloomIntoYouDescriptions.HTML assert media.relations[0].description.markdown == BloomIntoYouDescriptions.MARKDOWN assert media.relations[0].description.text == BloomIntoYouDescriptions.TEXT - assert media.site_url == HttpUrl("https://anilist.co/manga/106794") + assert media.site_url.__str__() == "https://anilist.co/manga/106794" async def test_anilist_characters() -> None: - media = await AsyncAniList().get(20954) + media = await AsyncAniList().get(id=20954) assert media.title.english == "A Silent Voice" assert media.start_date.year == 2016 assert media.source is MediaSource.MANGA @@ -203,4 +185,4 @@ async def test_anilist_characters() -> None: "Shouya Ishida", "Shouko Nishimiya", ] - assert media.site_url == HttpUrl("https://anilist.co/anime/20954") + assert media.site_url.__str__() == "https://anilist.co/anime/20954" diff --git a/tests/test_async_exceptions.py b/tests/test_async_exceptions.py index 31077ae..2666008 100644 --- a/tests/test_async_exceptions.py +++ b/tests/test_async_exceptions.py @@ -10,14 +10,14 @@ async def test_anilist_bad_search_combo() -> None: with pytest.raises(HTTPStatusError): - await anilist.search("Attack on titan", season_year=1999) + await anilist.get("Attack on titan", season_year=1999) async def test_anilist_wrong_input_types() -> None: with pytest.raises(ValidationError): - await anilist.search(123456789, season_year="hello", type=True) # type: ignore + await anilist.get(id=123456789, season_year="hello", type=True) # type: ignore async def test_anilist_bad_id() -> None: with pytest.raises(HTTPStatusError): - await anilist.get(9999999999) + await anilist.get(id=9999999999) diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 3ddfdf2..b80a665 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -10,14 +10,14 @@ def test_anilist_bad_search_combo() -> None: with pytest.raises(HTTPStatusError): - anilist.search("Attack on titan", season_year=1999) + anilist.get("Attack on titan", season_year=1999) def test_anilist_wrong_input_types() -> None: with pytest.raises(ValidationError): - anilist.search(123456789, season_year="hello", type=True) # type: ignore + anilist.get(id=123456789, season_year="hello", type=True) # type: ignore def test_anilist_bad_id() -> None: with pytest.raises(HTTPStatusError): - anilist.get(9999999999) + anilist.get(id=9999999999) diff --git a/tests/test_models.py b/tests/test_models.py index ec21a07..1e3332a 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -6,3 +6,8 @@ def test_fuzzy_date() -> None: assert FuzzyDate(year=2023, month=1).iso_format() == "2023-01" assert FuzzyDate(year=2023).iso_format() == "2023" assert FuzzyDate().iso_format() == "" + + assert FuzzyDate(year=2023, month=1, day=4).as_int() == 20230104 + assert FuzzyDate(year=2023, month=1).as_int() == 20230100 + assert FuzzyDate(year=2023).as_int() == 20230000 + assert FuzzyDate().as_int() == 10000000 diff --git a/tests/test_utils.py b/tests/test_utils.py index dfa4f99..13c2f24 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,4 +1,11 @@ -from pyanilist._utils import flatten, markdown_formatter, remove_null_fields, sanitize_description, text_formatter +from pyanilist._utils import ( + flatten, + markdown_formatter, + remove_null_fields, + sanitize_description, + text_formatter, + to_anilist_case, +) from .mock_descriptions import BloomIntoYouAnthologyDescriptions @@ -105,3 +112,80 @@ def test_formatters() -> None: assert markdown_formatter(None) is None assert text_formatter(None) is None # fmt: on + + +def test_query_variables_constructor() -> None: + + casemap = { + "id": "id", + "id_mal": "idMal", + "start_date": "startDate", + "end_date": "endDate", + "season": "season", + "season_year": "seasonYear", + "type": "type", + "format": "format", + "status": "status", + "episodes": "episodes", + "chapters": "chapters", + "duration": "duration", + "volumes": "volumes", + "is_adult": "isAdult", + "genre": "genre", + "tag": "tag", + "minimum_tag_rank": "minimumTagRank", + "tag_category": "tagCategory", + "licensed_by": "licensedBy", + "licensed_by_id": "licensedById", + "average_score": "averageScore", + "popularity": "popularity", + "source": "source", + "country_of_origin": "countryOfOrigin", + "is_licensed": "isLicensed", + "search": "search", + "id_not": "id_not", + "id_in": "id_in", + "id_not_in": "id_not_in", + "id_mal_not": "idMal_not", + "id_mal_in": "idMal_in", + "id_mal_not_in": "idMal_not_in", + "start_date_greater": "startDate_greater", + "start_date_lesser": "startDate_lesser", + "start_date_like": "startDate_like", + "end_date_greater": "endDate_greater", + "end_date_lesser": "endDate_lesser", + "end_date_like": "endDate_like", + "format_in": "format_in", + "format_not": "format_not", + "format_not_in": "format_not_in", + "status_in": "status_in", + "status_not": "status_not", + "status_not_in": "status_not_in", + "episodes_greater": "episodes_greater", + "episodes_lesser": "episodes_lesser", + "duration_greater": "duration_greater", + "duration_lesser": "duration_lesser", + "chapters_greater": "chapters_greater", + "chapters_lesser": "chapters_lesser", + "volumes_greater": "volumes_greater", + "volumes_lesser": "volumes_lesser", + "genre_in": "genre_in", + "genre_not_in": "genre_not_in", + "tag_in": "tag_in", + "tag_not_in": "tag_not_in", + "tag_category_in": "tagCategory_in", + "tag_category_not_in": "tagCategory_not_in", + "licensed_by_in": "licensedBy_in", + "licensed_by_id_in": "licensedById_in", + "average_score_not": "averageScore_not", + "average_score_greater": "averageScore_greater", + "average_score_lesser": "averageScore_lesser", + "popularity_not": "popularity_not", + "popularity_greater": "popularity_greater", + "popularity_lesser": "popularity_lesser", + "source_in": "source_in", + "sort": "sort", + } + + for key, value in casemap.items(): + assert to_anilist_case(key) == value