diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e882f3..86a8968 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -146,7 +146,7 @@ jobs: python-version: "3.10" - name: Setup Poetry - uses: abatilo/actions-poetry@v3 + uses: abatilo/actions-poetry@v4 with: poetry-version: 1.7.1 diff --git a/aiobungie/__init__.py b/aiobungie/__init__.py index 877e282..642c2af 100644 --- a/aiobungie/__init__.py +++ b/aiobungie/__init__.py @@ -1,5 +1,7 @@ # MIT License -# +# ruff: noqa: F401 +# ruff: noqa: F403 +# ruff: noqa: F405 # Copyright (c) 2020 - Present nxtlo # # Permission is hereby granted, free of charge, to any person obtaining a copy @@ -36,13 +38,7 @@ from __future__ import annotations -from aiobungie import api -from aiobungie import builders -from aiobungie import crates -from aiobungie import framework -from aiobungie import traits -from aiobungie import typedefs -from aiobungie import url +from aiobungie import api, builders, crates, framework, traits, typedefs, url from aiobungie.client import Client from aiobungie.error import * from aiobungie.internal.enums import * @@ -58,26 +54,28 @@ from .crates.components import ComponentPrivacy # Entity enums -from .crates.entity import GatingScope -from .crates.entity import ObjectiveUIStyle -from .crates.entity import ValueUIStyle +from .crates.entity import GatingScope, ObjectiveUIStyle, ValueUIStyle # Fireteam enums. -from .crates.fireteams import FireteamActivity -from .crates.fireteams import FireteamDate -from .crates.fireteams import FireteamLanguage -from .crates.fireteams import FireteamPlatform +from .crates.fireteams import ( + FireteamActivity, + FireteamDate, + FireteamLanguage, + FireteamPlatform, +) # Records enums from .crates.records import RecordState # Package metadata -from .metadata import __about__ -from .metadata import __author__ -from .metadata import __docs__ -from .metadata import __email__ -from .metadata import __license__ -from .metadata import __url__ -from .metadata import __version__ +from .metadata import ( + __about__, + __author__, + __docs__, + __email__, + __license__, + __url__, + __version__, +) __all__ = [mod for mod in dir() if not mod.startswith("_")] # type: ignore diff --git a/aiobungie/api/framework.py b/aiobungie/api/framework.py index d4c07ac..5de021d 100644 --- a/aiobungie/api/framework.py +++ b/aiobungie/api/framework.py @@ -34,21 +34,23 @@ from sain import Iterator from aiobungie import typedefs - from aiobungie.crates import activity - from aiobungie.crates import application - from aiobungie.crates import character - from aiobungie.crates import clans - from aiobungie.crates import components - from aiobungie.crates import entity - from aiobungie.crates import fireteams - from aiobungie.crates import friends - from aiobungie.crates import items - from aiobungie.crates import milestones - from aiobungie.crates import profile - from aiobungie.crates import progressions - from aiobungie.crates import records - from aiobungie.crates import season - from aiobungie.crates import user + from aiobungie.crates import ( + activity, + application, + character, + clans, + components, + entity, + fireteams, + friends, + items, + milestones, + profile, + progressions, + records, + season, + user, + ) class Framework(abc.ABC): diff --git a/aiobungie/api/rest.py b/aiobungie/api/rest.py index f479b52..daa319b 100644 --- a/aiobungie/api/rest.py +++ b/aiobungie/api/rest.py @@ -34,8 +34,7 @@ import typing from aiobungie import traits -from aiobungie.internal import enums -from aiobungie.internal import helpers +from aiobungie.internal import enums, helpers if typing.TYPE_CHECKING: import concurrent.futures @@ -43,10 +42,8 @@ from typing_extensions import Self - from aiobungie import builders - from aiobungie import typedefs - from aiobungie.crates import clans - from aiobungie.crates import fireteams + from aiobungie import builders, typedefs + from aiobungie.crates import clans, fireteams _ALLOWED_LANGS = typing.Literal[ "en", diff --git a/aiobungie/builders.py b/aiobungie/builders.py index 0d6c63d..e68b99b 100644 --- a/aiobungie/builders.py +++ b/aiobungie/builders.py @@ -23,7 +23,7 @@ from __future__ import annotations -__all__ = ("OAuth2Response", "PlugSocketBuilder", "OAuthURL", "Image") +__all__ = ("OAuth2Response", "PlugSocketBuilder", "OAuthURL", "Image", "Settings") import asyncio import datetime @@ -35,22 +35,19 @@ import aiohttp import attrs -from . import error -from . import url -from .internal import enums -from .internal import helpers +from . import error, url +from .internal import enums, helpers if typing.TYPE_CHECKING: import collections.abc as collections import concurrent.futures import os + import ssl import types - from typing_extensions import Required - from typing_extensions import Self + from typing_extensions import Required, Self - from aiobungie import traits - from aiobungie import typedefs + from aiobungie import traits, typedefs class _FinderListingValue(typing.TypedDict): valueType: Required[int] @@ -62,6 +59,49 @@ class _ListingFilter(typing.TypedDict): matchType: int +@typing.final +@attrs.define(kw_only=True) +class Settings: + """Basic settings used within aiobungie HTTP clients.""" + + http_timeout: aiohttp.ClientTimeout = attrs.field( + default=aiohttp.ClientTimeout(30.0) + ) + """Setting to control HTTP request timeouts, This includes + the time it takes to acquire the client, + timeout for connecting and reading the socket, and more. + + Defaults to total of `30.0` seconds. + """ + + trust_env: bool = attrs.field(default=False) + """Trust environment settings for proxy configuration. + + Gets proxy credentials from `~/.netrc` file if present or + Gets HTTP Basic Auth credentials from `~/.netrc` file if present. + + If `NETRC` environment variable is set, read from file specified + there rather than from `~/.netrc`. + """ + + auth: aiohttp.BasicAuth | None = attrs.field(default=None) + """an object that represents HTTP Basic Authorization, Defaults to `None`.""" + + headers: collections.Mapping[str, typing.Any] | None = attrs.field(default=None) + """Default HTTP headers to send the request with, Defaults to `None`.""" + + use_dns_cache: bool = attrs.field(default=True) + """References [use_dns_cache](https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.TCPConnector)""" + ttl_dns_cache: int = attrs.field(default=10) + """References [ttl_dns_cache](https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.TCPConnector)""" + verify_ssl: bool = attrs.field(default=True) + """References [verify_ssl](https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.TCPConnector)""" + ssl_context: ssl.SSLContext | None = attrs.field(default=None) + """References [ssl_context](https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.TCPConnector)""" + ssl: bool | aiohttp.Fingerprint | ssl.SSLContext = attrs.field(default=True) + """References [ssl](https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.TCPConnector)""" + + @typing.final class MimeType(str, enums.Enum): """Image mime types enum.""" @@ -82,7 +122,9 @@ def _open_write_path(path: pathlib.Path) -> typing.BinaryIO: @typing.final class Image: - """A stream-able Bungie resource. + """A streamable Bungie resource. + + Images are _lazy_, which mean they do nothing unless you `await` or poll them. Example ------- @@ -171,7 +213,7 @@ def create_url(self) -> str: """ return url.BASE + "/" + self.path - async def static_request(self) -> aiohttp.ClientResponse: + async def _static_request(self) -> aiohttp.ClientResponse: client_session = aiohttp.ClientSession() request = client_session.request( "GET", self.create_url(), raise_for_status=False @@ -200,7 +242,7 @@ async def save( file_name: str, path: str | os.PathLike[str], *, - mime_type: MimeType | str = MimeType.JPEG, + mime_type: MimeType | str = MimeType.PNG, executor: concurrent.futures.Executor | None = None, ) -> None: """Saves this image to a file. @@ -239,7 +281,7 @@ async def save( loop = helpers.get_or_make_loop() file = await loop.run_in_executor(executor, _open_write_path, path) - reader = await self.static_request() + reader = await self._static_request() try: async for chunk in reader.content: await loop.run_in_executor(executor, file.write, chunk) @@ -268,7 +310,7 @@ async def read(self) -> bytes: `bytes`: The bytes of this image. """ - return await (await self.static_request()).read() + return await (await self._static_request()).read() async def stream(self) -> aiohttp.streams.AsyncStreamIterator[bytes]: """Stream this image's data. @@ -287,17 +329,16 @@ async def stream(self) -> aiohttp.streams.AsyncStreamIterator[bytes]: Returns ------- `AsyncStreamIterator[bytes]`: - A lazy async iterator that is ready in memory. + A streaming iterator of this image bytes, yielding the entire data as soon as its received. """ - return (await self.static_request()).content.iter_any() + return (await self._static_request()).content.iter_any() - async def chunks(self, size: int) -> aiohttp.streams.AsyncStreamIterator[bytes]: - """Stream this image's data in chunks. + async def chunks(self, n: int) -> aiohttp.streams.AsyncStreamIterator[bytes]: + """Stream the bytes of this image in `n` chunks. Example ------- ```py - # it must be awaited to fetch the image first. buffer_size = 1024 image = Image.default() @@ -313,9 +354,9 @@ async def chunks(self, size: int) -> aiohttp.streams.AsyncStreamIterator[bytes]: Returns ------- `AsyncStreamIterator[bytes]`: - lazy async iterator that is ready in memory. + A chunking stream of bytes. """ - return (await self.static_request()).content.iter_chunked(size) + return (await self._static_request()).content.iter_chunked(n) async def iter(self) -> collections.AsyncGenerator[bytes, None]: """Yield each byte in this image from start to end. @@ -332,7 +373,7 @@ async def iter(self) -> collections.AsyncGenerator[bytes, None]: Returns ------- `collections.AsyncGenerator[bytes, None]` - An async generator that yields this image's bytes. + An async generator that yields this image's bytes from start to end. """ reader = await self.chunks(1024) diff --git a/aiobungie/client.py b/aiobungie/client.py index 6402c41..d1a9ab7 100644 --- a/aiobungie/client.py +++ b/aiobungie/client.py @@ -33,23 +33,23 @@ from aiobungie import framework from aiobungie import rest as rest_ from aiobungie import traits -from aiobungie.crates import fireteams -from aiobungie.crates import user -from aiobungie.internal import enums -from aiobungie.internal import helpers +from aiobungie.crates import fireteams, user +from aiobungie.internal import enums, helpers if typing.TYPE_CHECKING: import collections.abc as collections - from aiobungie import api - from aiobungie.crates import activity - from aiobungie.crates import application - from aiobungie.crates import clans - from aiobungie.crates import components - from aiobungie.crates import entity - from aiobungie.crates import friends - from aiobungie.crates import milestones - from aiobungie.crates import profile + from aiobungie import api, builders + from aiobungie.crates import ( + activity, + application, + clans, + components, + entity, + friends, + milestones, + profile, + ) class Client(traits.Compact): @@ -81,14 +81,16 @@ async def main(): Other Parameters ---------------- - max_retries : `int` - The max retries number to retry if the request hit a `5xx` status code. client_secret : `str | None` An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client. client_id : `int | None` An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client. + settings: `aiobungie.builders.Settings | None` + The client settings to use, if `None` the default will be used. + max_retries : `int` + The max retries number to retry if the request hit a `5xx` status code. debug: `"TRACE" | bool | int` The level of logging to enable. """ @@ -102,6 +104,7 @@ def __init__( *, client_secret: str | None = None, client_id: int | None = None, + settings: builders.Settings | None = None, max_retries: int = 4, debug: typing.Literal["TRACE"] | bool | int = False, ) -> None: @@ -109,6 +112,7 @@ def __init__( token, client_secret=client_secret, client_id=client_id, + settings=settings, max_retries=max_retries, debug=debug, ) @@ -123,14 +127,14 @@ def framework(self) -> api.Framework: def rest(self) -> api.RESTClient: return self._rest - @property - def request(self) -> Client: - return self - @property def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]: return self._rest.metadata + @property + def settings(self) -> builders.Settings: + return self._rest.settings + # * User methods. async def fetch_current_user_memberships(self, access_token: str, /) -> user.User: diff --git a/aiobungie/crates/__init__.py b/aiobungie/crates/__init__.py index d7a8bac..41cc42a 100644 --- a/aiobungie/crates/__init__.py +++ b/aiobungie/crates/__init__.py @@ -1,5 +1,6 @@ # MIT License -# +# ruff: noqa: F405 +# ruff: noqa: F403 # Copyright (c) 2020 - Present nxtlo # # Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/aiobungie/crates/character.py b/aiobungie/crates/character.py index a4ea942..9a12151 100644 --- a/aiobungie/crates/character.py +++ b/aiobungie/crates/character.py @@ -50,8 +50,7 @@ from aiobungie import builders from aiobungie.crates import milestones as milestones_ from aiobungie.crates import progressions as progressions_ - from aiobungie.crates import records - from aiobungie.crates import season + from aiobungie.crates import records, season from aiobungie.internal import enums diff --git a/aiobungie/crates/components.py b/aiobungie/crates/components.py index b3fba8e..d684c3d 100644 --- a/aiobungie/crates/components.py +++ b/aiobungie/crates/components.py @@ -45,17 +45,14 @@ import attrs -from aiobungie.internal import enums -from aiobungie.internal import helpers +from aiobungie.internal import enums, helpers if typing.TYPE_CHECKING: import collections.abc as collections from aiobungie.crates import activity from aiobungie.crates import character as character_ - from aiobungie.crates import fireteams - from aiobungie.crates import items - from aiobungie.crates import profile + from aiobungie.crates import fireteams, items, profile from aiobungie.crates import records as records_ diff --git a/aiobungie/crates/entity.py b/aiobungie/crates/entity.py index 10928c7..5d3b8b5 100644 --- a/aiobungie/crates/entity.py +++ b/aiobungie/crates/entity.py @@ -47,8 +47,7 @@ if typing.TYPE_CHECKING: import collections.abc as collections - from aiobungie import builders - from aiobungie import typedefs + from aiobungie import builders, typedefs from aiobungie.crates import activity diff --git a/aiobungie/crates/user.py b/aiobungie/crates/user.py index 412a138..1f0b848 100644 --- a/aiobungie/crates/user.py +++ b/aiobungie/crates/user.py @@ -43,8 +43,7 @@ import attrs -from aiobungie import builders -from aiobungie import url +from aiobungie import builders, url from aiobungie.internal import enums if typing.TYPE_CHECKING: diff --git a/aiobungie/error.py b/aiobungie/error.py index dd42f1a..a0a382f 100644 --- a/aiobungie/error.py +++ b/aiobungie/error.py @@ -46,8 +46,7 @@ import attrs -from aiobungie.internal import enums -from aiobungie.internal import helpers +from aiobungie.internal import enums, helpers if typing.TYPE_CHECKING: import aiohttp @@ -65,6 +64,15 @@ "TigerDemon": enums.MembershipType.DEMON, } +_SENSITIVE_KEYS = { + "Authorization", + "X-API-KEY", + "client_secret", + "client_id", + "access_token", + "refresh_token", +} + @attrs.define(auto_exc=True) class AiobungieError(RuntimeError): @@ -434,21 +442,22 @@ async def panic(response: aiohttp.ClientResponse) -> HTTPError: ) +def filtered_headers( + details: collections.Mapping[str, typing.Any], +) -> collections.Mapping[str, typing.Any]: + return {k: v for k, v in details.items() if k not in _SENSITIVE_KEYS} + + def stringify_headers(headers: collections.Mapping[str, typing.Any]) -> str: + if not headers: + return "" + return ( "{ \n" + "\n".join( # noqa: W503 f"{f' {key}'}: {value}" - if key - not in { - "Authorization", - "X-API-KEY", - "client_secret", - "client_id", - "access_token", - "refresh_token", - } - else f" {key}: REDACTED_TOKEN" + if key not in _SENSITIVE_KEYS + else f" {key}: REDACTED_KEY" for key, value in headers.items() ) + "\n}" # noqa: W503 diff --git a/aiobungie/framework.py b/aiobungie/framework.py index c3ae3fb..5035114 100644 --- a/aiobungie/framework.py +++ b/aiobungie/framework.py @@ -19,43 +19,39 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -"""Concrete implementations of the library's deserialization framework. +"""Implementation of the library's deserialization framework. -Exports -------- -* `Framework`: The default deserialization framework that's used within client impl's. -* `Empty`: An empty deserialization framework, it can be used standalone without the need of a client owner. +* `Framework` Is the default deserialization framework that's used within client impl's. +* `Global` Is a pre-initialized instance of `Framework` that can be used at any scope. """ from __future__ import annotations -__all__ = ("Framework", "Empty") +__all__ = ("Framework", "Global") import typing import sain -from aiobungie import api -from aiobungie import builders -from aiobungie import typedefs -from aiobungie.crates import activity -from aiobungie.crates import application -from aiobungie.crates import character -from aiobungie.crates import clans -from aiobungie.crates import components -from aiobungie.crates import entity -from aiobungie.crates import fireteams -from aiobungie.crates import friends -from aiobungie.crates import items -from aiobungie.crates import milestones -from aiobungie.crates import profile -from aiobungie.crates import progressions -from aiobungie.crates import records -from aiobungie.crates import season -from aiobungie.crates import user -from aiobungie.internal import enums -from aiobungie.internal import helpers -from aiobungie.internal import time +from aiobungie import api, builders, typedefs +from aiobungie.crates import ( + activity, + application, + character, + clans, + components, + entity, + fireteams, + friends, + items, + milestones, + profile, + progressions, + records, + season, + user, +) +from aiobungie.internal import enums, time if typing.TYPE_CHECKING: import collections.abc as collections @@ -76,20 +72,18 @@ class Framework(api.Framework): from aiobungie import traits from aiobungie import framework - from aiobungie import crates class MyClient(traits.Deserialize): - rest = aiobungie.RESTClient("token") - my_name = "Fate怒" - my_code = 4275 + def __init__(self) -> None: + self.rest = aiobungie.RESTClient("token") + self.my_name = "Fate怒" + self.my_code = 4275 @property # required method. def framework(self) -> framework.Empty: - return framework.Empty() + return framework.Global - async def my_memberships(self) -> tuple[crates.DestinyMembership, ...]: - # Note, Do not call methods within objects, Since this is an empty - # factory, The client reference that makes these calls will be `None`. + async def my_memberships(self): response = await self.rest.fetch_membership(self.my_name, self.my_code) return self.factory.deserialize_destiny_memberships(response) @@ -102,7 +96,7 @@ async def main() -> None: ``` """ - __slots__ = ("_app",) + __slots__ = () def __init__(self) -> None: super().__init__() @@ -2574,14 +2568,9 @@ def deserialize_plug_item_state( ) -@typing.final -@helpers.deprecated( - since="0.3.0", - removed_in="0.3.1", - use_instead="aiobungie.Framework()", - hint="Framework doesn't need a client owner anymore.", -) -class Empty(Framework): - """A stand-alone framework that doesn't require a client owner.""" +Global = Framework() +"""The global framework instance. - __slots__ = () +This type is global constant that can be used to deserialize payloads +without the need to initialize a new instance of the framework. +""" diff --git a/aiobungie/internal/helpers.py b/aiobungie/internal/helpers.py index 7898836..24653f7 100644 --- a/aiobungie/internal/helpers.py +++ b/aiobungie/internal/helpers.py @@ -46,8 +46,7 @@ T = typing.TypeVar("T", bound=collections.Callable[..., typing.Any]) -from sain import deprecated -from sain import unimplemented +from sain import deprecated, unimplemented def unstable(obj: T) -> collections.Callable[[T], typing.NoReturn]: diff --git a/aiobungie/internal/time.py b/aiobungie/internal/time.py index 120c924..a91bed6 100644 --- a/aiobungie/internal/time.py +++ b/aiobungie/internal/time.py @@ -34,29 +34,34 @@ import datetime import sys as _sys import time as _time +import warnings + +_has_backport = True +if _sys.version_info.minor == 10: + try: + from backports.datetime_fromisoformat import MonkeyPatch # pyright: ignore + + MonkeyPatch.patch_fromisoformat() # pyright: ignore[reportUnknownMemberType] + except ModuleNotFoundError: + _has_backport = False + warnings.warn( + "The backports module is required for Python 3.10 compatibility.\n" + "Please install it with `pip install backports-datetime-fromisoformat`" + ) def from_timestamp( - timer: int | float, tz: datetime.timezone = datetime.timezone.utc, / + timestamp: int | float, tz: datetime.timezone = datetime.timezone.utc, / ) -> datetime.datetime: """Converts a timestamp to `datetime.datetime`""" - return datetime.datetime.fromtimestamp(timer, tz=tz) + return datetime.datetime.fromtimestamp(float(timestamp), tz=tz) def clean_date(iso_date: str, /) -> datetime.datetime: """Parse an `ISO8601` string datetime into a Python `datetime.datetime` object.""" # Python 3.10 doesn't parse all ISO8601 formats, Need a backport for that. - - if _sys.version_info.minor == 10: - try: - from backports.datetime_fromisoformat import MonkeyPatch # pyright: ignore - - MonkeyPatch.patch_fromisoformat() # pyright: ignore[reportUnknownMemberType] - except ModuleNotFoundError: - raise ModuleNotFoundError( - "The backports module is required for Python 3.10 compatibility.\n" - "Please install it with `pip install backports-datetime-fromisoformat`" - ) + if not _has_backport: + return datetime.datetime.min return datetime.datetime.fromisoformat(iso_date) diff --git a/aiobungie/metadata.py b/aiobungie/metadata.py index 70a530c..8ade2d5 100644 --- a/aiobungie/metadata.py +++ b/aiobungie/metadata.py @@ -36,7 +36,7 @@ import typing -__version__: typing.Final[str] = "0.3.1" +__version__: typing.Final[str] = "0.4.0" __about__: typing.Final[str] = ( "A statically typed, asynchronous API wrapper for building clients for Bungie's API in Python." ) diff --git a/aiobungie/rest.py b/aiobungie/rest.py index 05f5bd3..7ad20c0 100644 --- a/aiobungie/rest.py +++ b/aiobungie/rest.py @@ -41,18 +41,10 @@ import aiohttp -from aiobungie import api -from aiobungie import builders -from aiobungie import error -from aiobungie import metadata -from aiobungie import typedefs -from aiobungie import url -from aiobungie.crates import clans -from aiobungie.crates import fireteams +from aiobungie import api, builders, error, metadata, typedefs, url +from aiobungie.crates import clans, fireteams from aiobungie.internal import _backoff as backoff -from aiobungie.internal import enums -from aiobungie.internal import helpers -from aiobungie.internal import time +from aiobungie.internal import enums, helpers, time if typing.TYPE_CHECKING: import collections.abc as collections @@ -209,11 +201,9 @@ def __init__( class RESTPool: - """a Pool of `RESTClient` instances. + """a Pool of `RESTClient` instances that shares the same TCP client connection. - This allows to acquire multiple instances of `RESTClient`s which can be acquired with the same token and metadata. - - A full example of this client can be found in the examples directory. + This allows you to acquire instances of `RESTClient`s from single settings and credentials. Example ------- @@ -222,13 +212,16 @@ class RESTPool: import asyncio pool = aiobungie.RESTPool("token") - pool.metadata['auth_code'] = 'code' async def get() -> None: - async with pool.acquire() as rest: - this = await rest.fetch_current_user_memberships(pool.metadata['auth_code']) + await pool.start() + + async with pool.acquire() as client: + await client.fetch_character(...) + + await pool.stop() - await asyncio.run(get()) + asyncio.run(get()) ``` Parameters @@ -238,14 +231,16 @@ async def get() -> None: Other Parameters ---------------- - max_retries : `int` - The max retries number to retry if the request hit a `5xx` status code. client_secret : `str | None` An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client. client_id : `int | None` An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client. + settings: `aiobungie.builders.Settings | None` + The client settings to use, if `None` the default will be used. + max_retries : `int` + The max retries number to retry if the request hit a `5xx` status code. debug : `bool | str` Whether to enable logging responses or not. @@ -267,6 +262,7 @@ async def get() -> None: "_client_session", "_loads", "_dumps", + "_settings", ) # Looks like mypy doesn't like this. @@ -280,7 +276,7 @@ def __init__( *, client_secret: str | None = None, client_id: int | None = None, - client_session: aiohttp.ClientSession | None = None, + settings: builders.Settings | None = None, dumps: typedefs.Dumps = helpers.dumps, loads: typedefs.Loads = helpers.loads, max_retries: int = 4, @@ -292,19 +288,29 @@ def __init__( self._max_retries = max_retries self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {} self._enable_debug = debug - self._client_session = client_session + self._client_session: aiohttp.ClientSession | None = None self._loads = loads self._dumps = dumps + self._settings = settings or builders.Settings() @property def client_id(self) -> int | None: + """Return the client id of this REST client if provided, Otherwise None.""" return self._client_id @property def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]: - """Pool's Metadata. This is different from client instance metadata.""" + """A general-purpose mutable mapping you can use to store data. + + This mapping can be accessed from any process that has a reference to this pool. + """ return self._metadata + @property + def settings(self) -> builders.Settings: + """Internal client settings used within the HTTP client session.""" + return self._settings + @typing.overload def build_oauth2_url(self, client_id: int) -> builders.OAuthURL: ... @@ -338,6 +344,67 @@ def build_oauth2_url( return builders.OAuthURL(client_id=client_id) + async def start(self) -> None: + """Start the TCP connection of this client pool. + + This will raise `RuntimeError` if the connection has already been started. + + Example + ------- + ```py + pool = aiobungie.RESTPool(...) + + async def run() -> None: + await pool.start() + async with pool.acquire() as client: + # use client + + async def stop(self) -> None: + await pool.close() + ``` + """ + if self._client_session is not None: + raise RuntimeError(" has already been started.") from None + + self._client_session = aiohttp.ClientSession( + connector=aiohttp.TCPConnector( + use_dns_cache=self._settings.use_dns_cache, + ttl_dns_cache=self._settings.ttl_dns_cache, + ssl_context=self._settings.ssl_context, + ssl=self._settings.ssl, + ), + connector_owner=True, + raise_for_status=False, + timeout=self._settings.http_timeout, + trust_env=self._settings.trust_env, + headers=self._settings.headers, + ) + + async def stop(self) -> None: + """Stop the TCP connection of this client pool. + + This will raise `RuntimeError` if the connection has already been closed. + + Example + ------- + ```py + pool = aiobungie.RESTPool(...) + + async def run() -> None: + await pool.start() + async with pool.acquire() as client: + # use client + + async def stop(self) -> None: + await pool.close() + ``` + """ + if self._client_session is None: + raise RuntimeError(" is already stopped.") + + await self._client_session.close() + self._client_session = None + @typing.final def acquire(self) -> RESTClient: """Acquires a new `RESTClient` instance from this pool. @@ -356,6 +423,8 @@ def acquire(self) -> RESTClient: max_retries=self._max_retries, debug=self._enable_debug, client_session=self._client_session, + owned_client=False, + settings=self._settings, ) @@ -383,14 +452,24 @@ class RESTClient(api.RESTClient): Other Parameters ---------------- - max_retries : `int` - The max retries number to retry if the request hit a `5xx` status code. client_secret : `str | None` An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client. client_id : `int | None` An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client. + settings: `aiobungie.builders.Settings | None` + The client settings to use, if `None` the default will be used. + owned_client: `bool` + * If set to `True`, this client will use the provided `client_session` parameter instead, + * If set to `True` and `client_session` is `None`, `ValueError` will be raised. + * If set to `False`, aiobungie will initialize a new client session for you. + + client_session: `aiohttp.ClientSession | None` + If provided, this client session will be used to make all the HTTP requests. + The `owned_client` must be set to `True` for this to work. + max_retries : `int` + The max retries number to retry if the request hit a `5xx` status code. debug : `bool | str` Whether to enable logging responses or not. @@ -411,6 +490,8 @@ class RESTClient(api.RESTClient): "_metadata", "_dumps", "_loads", + "_owned_client", + "_settings", ) def __init__( @@ -420,13 +501,22 @@ def __init__( *, client_secret: str | None = None, client_id: int | None = None, + settings: builders.Settings | None = None, + owned_client: bool = True, client_session: aiohttp.ClientSession | None = None, dumps: typedefs.Dumps = helpers.dumps, loads: typedefs.Loads = helpers.loads, max_retries: int = 4, debug: typing.Literal["TRACE"] | bool | int = False, ) -> None: + if owned_client is False and client_session is None: + raise ValueError( + "Expected an owned client session, but got `None`, Cannot have `owned_client` set to `False` and `client_session` to `None`" + ) + + self._settings = settings or builders.Settings() self._session = client_session + self._owned_client = owned_client self._lock: asyncio.Lock | None = None self._client_secret = client_secret self._client_id = client_id @@ -449,26 +539,37 @@ def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]: def is_alive(self) -> bool: return self._session is not None - @typing.final + @property + def settings(self) -> builders.Settings: + return self._settings + async def close(self) -> None: if self._session is None: raise RuntimeError("REST client is not running.") - await self._session.close() - self._session = None + if self._owned_client: + await self._session.close() + self._session = None - @typing.final def open(self) -> None: """Open a new client session. This is called internally with contextmanager usage.""" - if self._session: + if self.is_alive and self._owned_client: raise RuntimeError("Cannot open REST client when it's already open.") - self._session = aiohttp.ClientSession( - connector=aiohttp.TCPConnector(), - connector_owner=True, - raise_for_status=False, - timeout=aiohttp.ClientTimeout(total=30.0), - ) + if self._owned_client: + self._session = aiohttp.ClientSession( + connector=aiohttp.TCPConnector( + use_dns_cache=self._settings.use_dns_cache, + ttl_dns_cache=self._settings.ttl_dns_cache, + ssl_context=self._settings.ssl_context, + ssl=self._settings.ssl, + ), + connector_owner=True, + raise_for_status=False, + timeout=self._settings.http_timeout, + trust_env=self._settings.trust_env, + headers=self._settings.headers, + ) @typing.final async def static_request( @@ -498,6 +599,7 @@ def build_oauth2_url( return builders.OAuthURL(client_id=client_id) + @typing.final async def _request( self, method: _HTTP_METHOD, @@ -626,13 +728,13 @@ async def _request( details: collections.MutableMapping[str, typing.Any] = {} if json: - details["json"] = json + details["json"] = error.filtered_headers(json) if data: - details["data"] = data + details["data"] = error.filtered_headers(data) if params: - details["params"] = params + details["params"] = error.filtered_headers(params) if details: _LOGGER.log(TRACE, "%s", error.stringify_headers(details)) @@ -678,7 +780,6 @@ async def __aexit__( await self.close() # We don't want this to be super complicated. - @typing.final async def _handle_ratelimit( self, response: aiohttp.ClientResponse, diff --git a/aiobungie/traits.py b/aiobungie/traits.py index 5cbee47..1b11c95 100644 --- a/aiobungie/traits.py +++ b/aiobungie/traits.py @@ -43,8 +43,7 @@ import collections.abc as collections import os - from aiobungie import api - from aiobungie import client + from aiobungie import api, client from aiobungie.internal import enums @@ -105,7 +104,8 @@ def client_id(self) -> int | None: def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]: """A mutable mapping storage for the user's needs. - This mapping is useful for storing any kind of data that the user may need. + This mapping is useful for storing any kind of data that the user may need + to access later from a different process. Example ------- @@ -134,6 +134,11 @@ def is_alive(self) -> bool: """Returns `True` if the REST client is alive and `False` otherwise.""" raise NotImplementedError + @property + def settings(self) -> builders.Settings: + """Internal client settings used within the HTTP client session.""" + raise NotImplementedError + @typing.final def with_debug( self, @@ -327,7 +332,7 @@ async def static_request( @typing.runtime_checkable -class Compact(Send, Deserialize, typing.Protocol): +class Compact(Deserialize, typing.Protocol): """A structural super-type that can perform all actions that other traits provide. This trait includes all aiobungie traits. is also automatically implemented for `aiobungie.Client` @@ -344,3 +349,8 @@ def rest(self) -> api.RESTClient: def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]: """A mutable mapping storage for the user's needs.""" raise NotImplementedError + + @property + def settings(self) -> builders.Settings: + """Internal client settings used within the HTTP client session.""" + raise NotImplementedError diff --git a/dev-requirements.txt b/dev-requirements.txt index 63bd3dc..e6bf05d 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -2,17 +2,17 @@ nox~=2024.10.9 # Ducumentation. -pdoc~=15.0.0 +pdoc~=15.0.1 # Testing and Mocking. -pytest~=8.3.3 -pytest-asyncio~=0.24.0 +pytest~=8.3.4 +pytest-asyncio~=0.25.2 mock~=5.1.0 python-dotenv~=1.0.1 # Format - Type checking. -ruff~=0.8.2 +ruff~=0.9.0 isort~=5.13.2 -pyright~=1.1.389 +pyright~=1.1.391 typing-extensions~=4.12.2 codespell~=2.3.0 diff --git a/poetry.lock b/poetry.lock index 28dda3f..531e073 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -13,87 +13,87 @@ files = [ [[package]] name = "aiohttp" -version = "3.11.10" +version = "3.11.11" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" files = [ - {file = "aiohttp-3.11.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cbad88a61fa743c5d283ad501b01c153820734118b65aee2bd7dbb735475ce0d"}, - {file = "aiohttp-3.11.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80886dac673ceaef499de2f393fc80bb4481a129e6cb29e624a12e3296cc088f"}, - {file = "aiohttp-3.11.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61b9bae80ed1f338c42f57c16918853dc51775fb5cb61da70d590de14d8b5fb4"}, - {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e2e576caec5c6a6b93f41626c9c02fc87cd91538b81a3670b2e04452a63def6"}, - {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02c13415b5732fb6ee7ff64583a5e6ed1c57aa68f17d2bda79c04888dfdc2769"}, - {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfce37f31f20800a6a6620ce2cdd6737b82e42e06e6e9bd1b36f546feb3c44f"}, - {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3bbbfff4c679c64e6e23cb213f57cc2c9165c9a65d63717108a644eb5a7398df"}, - {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49c7dbbc1a559ae14fc48387a115b7d4bbc84b4a2c3b9299c31696953c2a5219"}, - {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:68386d78743e6570f054fe7949d6cb37ef2b672b4d3405ce91fafa996f7d9b4d"}, - {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9ef405356ba989fb57f84cac66f7b0260772836191ccefbb987f414bcd2979d9"}, - {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5d6958671b296febe7f5f859bea581a21c1d05430d1bbdcf2b393599b1cdce77"}, - {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:99b7920e7165be5a9e9a3a7f1b680f06f68ff0d0328ff4079e5163990d046767"}, - {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0dc49f42422163efb7e6f1df2636fe3db72713f6cd94688e339dbe33fe06d61d"}, - {file = "aiohttp-3.11.10-cp310-cp310-win32.whl", hash = "sha256:40d1c7a7f750b5648642586ba7206999650208dbe5afbcc5284bcec6579c9b91"}, - {file = "aiohttp-3.11.10-cp310-cp310-win_amd64.whl", hash = "sha256:68ff6f48b51bd78ea92b31079817aff539f6c8fc80b6b8d6ca347d7c02384e33"}, - {file = "aiohttp-3.11.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:77c4aa15a89847b9891abf97f3d4048f3c2d667e00f8a623c89ad2dccee6771b"}, - {file = "aiohttp-3.11.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:909af95a72cedbefe5596f0bdf3055740f96c1a4baa0dd11fd74ca4de0b4e3f1"}, - {file = "aiohttp-3.11.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:386fbe79863eb564e9f3615b959e28b222259da0c48fd1be5929ac838bc65683"}, - {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3de34936eb1a647aa919655ff8d38b618e9f6b7f250cc19a57a4bf7fd2062b6d"}, - {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c9527819b29cd2b9f52033e7fb9ff08073df49b4799c89cb5754624ecd98299"}, - {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a96e3e03300b41f261bbfd40dfdbf1c301e87eab7cd61c054b1f2e7c89b9e8"}, - {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f5635f7b74bcd4f6f72fcd85bea2154b323a9f05226a80bc7398d0c90763b0"}, - {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:03b6002e20938fc6ee0918c81d9e776bebccc84690e2b03ed132331cca065ee5"}, - {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6362cc6c23c08d18ddbf0e8c4d5159b5df74fea1a5278ff4f2c79aed3f4e9f46"}, - {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3691ed7726fef54e928fe26344d930c0c8575bc968c3e239c2e1a04bd8cf7838"}, - {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31d5093d3acd02b31c649d3a69bb072d539d4c7659b87caa4f6d2bcf57c2fa2b"}, - {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8b3cf2dc0f0690a33f2d2b2cb15db87a65f1c609f53c37e226f84edb08d10f52"}, - {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fbbaea811a2bba171197b08eea288b9402faa2bab2ba0858eecdd0a4105753a3"}, - {file = "aiohttp-3.11.10-cp311-cp311-win32.whl", hash = "sha256:4b2c7ac59c5698a7a8207ba72d9e9c15b0fc484a560be0788b31312c2c5504e4"}, - {file = "aiohttp-3.11.10-cp311-cp311-win_amd64.whl", hash = "sha256:974d3a2cce5fcfa32f06b13ccc8f20c6ad9c51802bb7f829eae8a1845c4019ec"}, - {file = "aiohttp-3.11.10-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b78f053a7ecfc35f0451d961dacdc671f4bcbc2f58241a7c820e9d82559844cf"}, - {file = "aiohttp-3.11.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab7485222db0959a87fbe8125e233b5a6f01f4400785b36e8a7878170d8c3138"}, - {file = "aiohttp-3.11.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cf14627232dfa8730453752e9cdc210966490992234d77ff90bc8dc0dce361d5"}, - {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076bc454a7e6fd646bc82ea7f98296be0b1219b5e3ef8a488afbdd8e81fbac50"}, - {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:482cafb7dc886bebeb6c9ba7925e03591a62ab34298ee70d3dd47ba966370d2c"}, - {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf3d1a519a324af764a46da4115bdbd566b3c73fb793ffb97f9111dbc684fc4d"}, - {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24213ba85a419103e641e55c27dc7ff03536c4873470c2478cce3311ba1eee7b"}, - {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b99acd4730ad1b196bfb03ee0803e4adac371ae8efa7e1cbc820200fc5ded109"}, - {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:14cdb5a9570be5a04eec2ace174a48ae85833c2aadc86de68f55541f66ce42ab"}, - {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7e97d622cb083e86f18317282084bc9fbf261801b0192c34fe4b1febd9f7ae69"}, - {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:012f176945af138abc10c4a48743327a92b4ca9adc7a0e078077cdb5dbab7be0"}, - {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44224d815853962f48fe124748227773acd9686eba6dc102578defd6fc99e8d9"}, - {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c87bf31b7fdab94ae3adbe4a48e711bfc5f89d21cf4c197e75561def39e223bc"}, - {file = "aiohttp-3.11.10-cp312-cp312-win32.whl", hash = "sha256:06a8e2ee1cbac16fe61e51e0b0c269400e781b13bcfc33f5425912391a542985"}, - {file = "aiohttp-3.11.10-cp312-cp312-win_amd64.whl", hash = "sha256:be2b516f56ea883a3e14dda17059716593526e10fb6303189aaf5503937db408"}, - {file = "aiohttp-3.11.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8cc5203b817b748adccb07f36390feb730b1bc5f56683445bfe924fc270b8816"}, - {file = "aiohttp-3.11.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ef359ebc6949e3a34c65ce20230fae70920714367c63afd80ea0c2702902ccf"}, - {file = "aiohttp-3.11.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9bca390cb247dbfaec3c664326e034ef23882c3f3bfa5fbf0b56cad0320aaca5"}, - {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:811f23b3351ca532af598405db1093f018edf81368e689d1b508c57dcc6b6a32"}, - {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddf5f7d877615f6a1e75971bfa5ac88609af3b74796ff3e06879e8422729fd01"}, - {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6ab29b8a0beb6f8eaf1e5049252cfe74adbaafd39ba91e10f18caeb0e99ffb34"}, - {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c49a76c1038c2dd116fa443eba26bbb8e6c37e924e2513574856de3b6516be99"}, - {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f3dc0e330575f5b134918976a645e79adf333c0a1439dcf6899a80776c9ab39"}, - {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:efb15a17a12497685304b2d976cb4939e55137df7b09fa53f1b6a023f01fcb4e"}, - {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:db1d0b28fcb7f1d35600150c3e4b490775251dea70f894bf15c678fdd84eda6a"}, - {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:15fccaf62a4889527539ecb86834084ecf6e9ea70588efde86e8bc775e0e7542"}, - {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:593c114a2221444f30749cc5e5f4012488f56bd14de2af44fe23e1e9894a9c60"}, - {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7852bbcb4d0d2f0c4d583f40c3bc750ee033265d80598d0f9cb6f372baa6b836"}, - {file = "aiohttp-3.11.10-cp313-cp313-win32.whl", hash = "sha256:65e55ca7debae8faaffee0ebb4b47a51b4075f01e9b641c31e554fd376595c6c"}, - {file = "aiohttp-3.11.10-cp313-cp313-win_amd64.whl", hash = "sha256:beb39a6d60a709ae3fb3516a1581777e7e8b76933bb88c8f4420d875bb0267c6"}, - {file = "aiohttp-3.11.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0580f2e12de2138f34debcd5d88894786453a76e98febaf3e8fe5db62d01c9bf"}, - {file = "aiohttp-3.11.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a55d2ad345684e7c3dd2c20d2f9572e9e1d5446d57200ff630e6ede7612e307f"}, - {file = "aiohttp-3.11.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:04814571cb72d65a6899db6099e377ed00710bf2e3eafd2985166f2918beaf59"}, - {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e44a9a3c053b90c6f09b1bb4edd880959f5328cf63052503f892c41ea786d99f"}, - {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:502a1464ccbc800b4b1995b302efaf426e8763fadf185e933c2931df7db9a199"}, - {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:613e5169f8ae77b1933e42e418a95931fb4867b2991fc311430b15901ed67079"}, - {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cca22a61b7fe45da8fc73c3443150c3608750bbe27641fc7558ec5117b27fdf"}, - {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86a5dfcc39309470bd7b68c591d84056d195428d5d2e0b5ccadfbaf25b026ebc"}, - {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:77ae58586930ee6b2b6f696c82cf8e78c8016ec4795c53e36718365f6959dc82"}, - {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:78153314f26d5abef3239b4a9af20c229c6f3ecb97d4c1c01b22c4f87669820c"}, - {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:98283b94cc0e11c73acaf1c9698dea80c830ca476492c0fe2622bd931f34b487"}, - {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:53bf2097e05c2accc166c142a2090e4c6fd86581bde3fd9b2d3f9e93dda66ac1"}, - {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5532f0441fc09c119e1dca18fbc0687e64fbeb45aa4d6a87211ceaee50a74c4"}, - {file = "aiohttp-3.11.10-cp39-cp39-win32.whl", hash = "sha256:47ad15a65fb41c570cd0ad9a9ff8012489e68176e7207ec7b82a0940dddfd8be"}, - {file = "aiohttp-3.11.10-cp39-cp39-win_amd64.whl", hash = "sha256:c6b9e6d7e41656d78e37ce754813fa44b455c3d0d0dced2a047def7dc5570b74"}, - {file = "aiohttp-3.11.10.tar.gz", hash = "sha256:b1fc6b45010a8d0ff9e88f9f2418c6fd408c99c211257334aff41597ebece42e"}, + {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8"}, + {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5"}, + {file = "aiohttp-3.11.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:731468f555656767cda219ab42e033355fe48c85fbe3ba83a349631541715ba2"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb23d8bb86282b342481cad4370ea0853a39e4a32a0042bb52ca6bdde132df43"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f047569d655f81cb70ea5be942ee5d4421b6219c3f05d131f64088c73bb0917f"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd7659baae9ccf94ae5fe8bfaa2c7bc2e94d24611528395ce88d009107e00c6d"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af01e42ad87ae24932138f154105e88da13ce7d202a6de93fafdafb2883a00ef"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5854be2f3e5a729800bac57a8d76af464e160f19676ab6aea74bde18ad19d438"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6526e5fb4e14f4bbf30411216780c9967c20c5a55f2f51d3abd6de68320cc2f3"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:85992ee30a31835fc482468637b3e5bd085fa8fe9392ba0bdcbdc1ef5e9e3c55"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:88a12ad8ccf325a8a5ed80e6d7c3bdc247d66175afedbe104ee2aaca72960d8e"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0a6d3fbf2232e3a08c41eca81ae4f1dff3d8f1a30bae415ebe0af2d2458b8a33"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84a585799c58b795573c7fa9b84c455adf3e1d72f19a2bf498b54a95ae0d194c"}, + {file = "aiohttp-3.11.11-cp310-cp310-win32.whl", hash = "sha256:bfde76a8f430cf5c5584553adf9926534352251d379dcb266ad2b93c54a29745"}, + {file = "aiohttp-3.11.11-cp310-cp310-win_amd64.whl", hash = "sha256:0fd82b8e9c383af11d2b26f27a478640b6b83d669440c0a71481f7c865a51da9"}, + {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ba74ec819177af1ef7f59063c6d35a214a8fde6f987f7661f4f0eecc468a8f76"}, + {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4af57160800b7a815f3fe0eba9b46bf28aafc195555f1824555fa2cfab6c1538"}, + {file = "aiohttp-3.11.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffa336210cf9cd8ed117011085817d00abe4c08f99968deef0013ea283547204"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b8fe282183e4a3c7a1b72f5ade1094ed1c6345a8f153506d114af5bf8accd9"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af41686ccec6a0f2bdc66686dc0f403c41ac2089f80e2214a0f82d001052c03"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70d1f9dde0e5dd9e292a6d4d00058737052b01f3532f69c0c65818dac26dc287"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:249cc6912405917344192b9f9ea5cd5b139d49e0d2f5c7f70bdfaf6b4dbf3a2e"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0eb98d90b6690827dcc84c246811feeb4e1eea683c0eac6caed7549be9c84665"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec82bf1fda6cecce7f7b915f9196601a1bd1a3079796b76d16ae4cce6d0ef89b"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9fd46ce0845cfe28f108888b3ab17abff84ff695e01e73657eec3f96d72eef34"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd176afcf8f5d2aed50c3647d4925d0db0579d96f75a31e77cbaf67d8a87742d"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ec2aa89305006fba9ffb98970db6c8221541be7bee4c1d027421d6f6df7d1ce2"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:92cde43018a2e17d48bb09c79e4d4cb0e236de5063ce897a5e40ac7cb4878773"}, + {file = "aiohttp-3.11.11-cp311-cp311-win32.whl", hash = "sha256:aba807f9569455cba566882c8938f1a549f205ee43c27b126e5450dc9f83cc62"}, + {file = "aiohttp-3.11.11-cp311-cp311-win_amd64.whl", hash = "sha256:ae545f31489548c87b0cced5755cfe5a5308d00407000e72c4fa30b19c3220ac"}, + {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e595c591a48bbc295ebf47cb91aebf9bd32f3ff76749ecf282ea7f9f6bb73886"}, + {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ea1b59dc06396b0b424740a10a0a63974c725b1c64736ff788a3689d36c02d2"}, + {file = "aiohttp-3.11.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8811f3f098a78ffa16e0ea36dffd577eb031aea797cbdba81be039a4169e242c"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7227b87a355ce1f4bf83bfae4399b1f5bb42e0259cb9405824bd03d2f4336a"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d40f9da8cabbf295d3a9dae1295c69975b86d941bc20f0a087f0477fa0a66231"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffb3dc385f6bb1568aa974fe65da84723210e5d9707e360e9ecb51f59406cd2e"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8f5f7515f3552d899c61202d99dcb17d6e3b0de777900405611cd747cecd1b8"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3499c7ffbfd9c6a3d8d6a2b01c26639da7e43d47c7b4f788016226b1e711caa8"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8e2bf8029dbf0810c7bfbc3e594b51c4cc9101fbffb583a3923aea184724203c"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b6212a60e5c482ef90f2d788835387070a88d52cf6241d3916733c9176d39eab"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d119fafe7b634dbfa25a8c597718e69a930e4847f0b88e172744be24515140da"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:6fba278063559acc730abf49845d0e9a9e1ba74f85f0ee6efd5803f08b285853"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92fc484e34b733704ad77210c7957679c5c3877bd1e6b6d74b185e9320cc716e"}, + {file = "aiohttp-3.11.11-cp312-cp312-win32.whl", hash = "sha256:9f5b3c1ed63c8fa937a920b6c1bec78b74ee09593b3f5b979ab2ae5ef60d7600"}, + {file = "aiohttp-3.11.11-cp312-cp312-win_amd64.whl", hash = "sha256:1e69966ea6ef0c14ee53ef7a3d68b564cc408121ea56c0caa2dc918c1b2f553d"}, + {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:541d823548ab69d13d23730a06f97460f4238ad2e5ed966aaf850d7c369782d9"}, + {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:929f3ed33743a49ab127c58c3e0a827de0664bfcda566108989a14068f820194"}, + {file = "aiohttp-3.11.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0882c2820fd0132240edbb4a51eb8ceb6eef8181db9ad5291ab3332e0d71df5f"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b63de12e44935d5aca7ed7ed98a255a11e5cb47f83a9fded7a5e41c40277d104"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa54f8ef31d23c506910c21163f22b124facb573bff73930735cf9fe38bf7dff"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a344d5dc18074e3872777b62f5f7d584ae4344cd6006c17ba12103759d407af3"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7fb429ab1aafa1f48578eb315ca45bd46e9c37de11fe45c7f5f4138091e2f1"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c341c7d868750e31961d6d8e60ff040fb9d3d3a46d77fd85e1ab8e76c3e9a5c4"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed9ee95614a71e87f1a70bc81603f6c6760128b140bc4030abe6abaa988f1c3d"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:de8d38f1c2810fa2a4f1d995a2e9c70bb8737b18da04ac2afbf3971f65781d87"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a9b7371665d4f00deb8f32208c7c5e652059b0fda41cf6dbcac6114a041f1cc2"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:620598717fce1b3bd14dd09947ea53e1ad510317c85dda2c9c65b622edc96b12"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf8d9bfee991d8acc72d060d53860f356e07a50f0e0d09a8dfedea1c554dd0d5"}, + {file = "aiohttp-3.11.11-cp313-cp313-win32.whl", hash = "sha256:9d73ee3725b7a737ad86c2eac5c57a4a97793d9f442599bea5ec67ac9f4bdc3d"}, + {file = "aiohttp-3.11.11-cp313-cp313-win_amd64.whl", hash = "sha256:c7a06301c2fb096bdb0bd25fe2011531c1453b9f2c163c8031600ec73af1cc99"}, + {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3e23419d832d969f659c208557de4a123e30a10d26e1e14b73431d3c13444c2e"}, + {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21fef42317cf02e05d3b09c028712e1d73a9606f02467fd803f7c1f39cc59add"}, + {file = "aiohttp-3.11.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1f21bb8d0235fc10c09ce1d11ffbd40fc50d3f08a89e4cf3a0c503dc2562247a"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1642eceeaa5ab6c9b6dfeaaa626ae314d808188ab23ae196a34c9d97efb68350"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2170816e34e10f2fd120f603e951630f8a112e1be3b60963a1f159f5699059a6"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8be8508d110d93061197fd2d6a74f7401f73b6d12f8822bbcd6d74f2b55d71b1"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eed954b161e6b9b65f6be446ed448ed3921763cc432053ceb606f89d793927e"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6c9af134da4bc9b3bd3e6a70072509f295d10ee60c697826225b60b9959acdd"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:44167fc6a763d534a6908bdb2592269b4bf30a03239bcb1654781adf5e49caf1"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:479b8c6ebd12aedfe64563b85920525d05d394b85f166b7873c8bde6da612f9c"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:10b4ff0ad793d98605958089fabfa350e8e62bd5d40aa65cdc69d6785859f94e"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b540bd67cfb54e6f0865ceccd9979687210d7ed1a1cc8c01f8e67e2f1e883d28"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1dac54e8ce2ed83b1f6b1a54005c87dfed139cf3f777fdc8afc76e7841101226"}, + {file = "aiohttp-3.11.11-cp39-cp39-win32.whl", hash = "sha256:568c1236b2fde93b7720f95a890741854c1200fba4a3471ff48b2934d2d93fd3"}, + {file = "aiohttp-3.11.11-cp39-cp39-win_amd64.whl", hash = "sha256:943a8b052e54dfd6439fd7989f67fc6a7f2138d0a2cf0a7de5f18aa4fe7eb3b1"}, + {file = "aiohttp-3.11.11.tar.gz", hash = "sha256:bb49c7f1e6ebf3821a42d81d494f538107610c3a705987f53068546b0e90303e"}, ] [package.dependencies] @@ -136,77 +136,77 @@ files = [ [[package]] name = "attrs" -version = "24.2.0" +version = "24.3.0" description = "Classes Without Boilerplate" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, - {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, + {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, + {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, ] [package.extras] benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "backports-datetime-fromisoformat" -version = "2.0.2" +version = "2.0.3" description = "Backport of Python 3.11's datetime.fromisoformat" optional = false python-versions = ">3" files = [ - {file = "backports_datetime_fromisoformat-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:09e70210726a70f3dd02ab9725bf2fcf469bda6d7554ea955588202e43e45b7d"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:ec971f93353e0ee957b3bbb037d58371331eedb9bee1b6676a866f8be97289a4"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:191b0d327838eb21818e94a66b89118c086ada8f77ac9e6161980ef486fe0cbb"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00441807d47dec7b89acafaa6570f561c43f5c7b7934d86f101b783a365a0f0c"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0af8719e161ce2fa5f5e426cceef1ff04b611c69a61636c8a7bf25d687cfa0"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5afc32e1cdac293b054af04187d4adafcaceca99e12e5ff7807aee08074d85cb"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:70b044fdd274e32ece726d30b1728b4a21bc78fed0be6294091c6f04228b39ec"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6f493622b06e23e10646df7ea23e0d8350e8b1caccb5509ea82f8c3e64db32c7"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55f59c88511dd15dabccf7916cbf23f8610203ac026454588084ddabf46127ee"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:65ca1f21319d78145456a70301396483ceebf078353641233494ea548ccc47db"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:79fc695afd66989f28e73de0ad91019abad789045577180dd482b6ede5bdca1e"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:019a87bd234734c2badb4c3e1ce4e807c5f2081f398a45a320e0c4919e5cee13"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea2b77e8810b691f1dd347d5c3d4ad829d18a9e81a04a0ebbc958d431967db31"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:944c987b777d7a81d97c94cdee2a8597bf6bdc94090094689456d3b02760cb73"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:30a2ab8c1fe4eb0013e7fcca29906fbe54e89f9120731ea71032b048dcf2fa17"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e23b602892827e15b1b4f94c61d4872b03b5d13417344d9a8daec80277244a32"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:64ec1ee18bc839847b067ab21a34a27e0d2cc4c6d041e4b05218cf6fed787740"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:54a3df9d6ae0e64b7677b9e3bba4fc7dce3ad56a3fa6bd66fb26796f8911de67"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:e54fa5663efcba6122bca037fd49220b7311e94cf6cc72e2f2a6f5d05c700bef"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00ecff906ed4eb19808d8e4f0b141c14a1963d3688ba318c9e00aa7da7f71301"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e85f1ad56e2bcb24408e420de5508be47e54b0912ebe1325134e71837ec23a08"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36d5cbece09dff2a3f8f517f3cda64f2ccec56db07808714b1f122326cd76fbd"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d47e186dcc366e6063248730a137a90de0472b2aaa5047ef39104fcacbcbcdbe"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:3e9c81c6acc21953ffa9a627f15c4afcdbce6e456ca1d03e0d6dbf131429bd56"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5a2574f4b542b9679db2b8a786c779249d2d5057dad01f9433cfb79a921da92c"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:e62aa2eb6dc87a76a29b88601747925db439f793de7a8d2bbca4355e805088a6"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:964ec2d2c23908e96f1064560def1547b355e33e7c1ab418265e7e6242d25841"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8003f0cebeb6a5c47a1a871d0d09897d3dd54a9e1bcbe313f3e0463d470eed97"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c88e6660e1fb96476cb9df17d6f5002a2fb5c87546d62b2daa3642aa537e144"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:7124cda6acdc66755df916c1f52b4e2e9cad85591d40bcd4a80341144fd98b32"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c2b0a4a407479964b3f79fde080aad066fe64a350a3fcbb729d3c44b0db21240"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:5616519470bc8131429266a869c3c5eeee5817a9a8357e2dd9c521383b774d1b"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2eb563509f19e803dbbef3e4901d9553c9c3ea2b73c8d8fb85219fc57f16787a"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:d37d2f4238e0f412e56fe2c41e8e60bda93be0230d0ee846823b54254ccb95e0"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:7dcefbba71194c73b3b26593c2ea4ad254b19084d0eb83e98e2541651a692703"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:352f6b793cb402cc62c5b60ceab13d30c06fad1372869c716d4d07927b5c7c43"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7d6a21b482001a9ea44f277dc21d9fb6590e543146aaabe816407d1b87cf41b"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f97285e80ea192357380cfd2fb2dce056ec65672597172f3af549dcf5d019b1e"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5a5cfff34bf80f0cd2771da88bd898be1fa60250d6f2dd9e4a59885dbcb7aa7c"}, - {file = "backports_datetime_fromisoformat-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:ed392607d457b1ed50a88dcaf459e11d81c30a2f2d8dab818a1564de6897e76f"}, - {file = "backports_datetime_fromisoformat-2.0.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0f24d2c596991e39dfaa60c685b8c69bc9b1da77e9baf2c453882adeec483b"}, - {file = "backports_datetime_fromisoformat-2.0.2-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0083552588270acfaa31ac8de81b29786a1515d7608ff11ccdfcdffc2486212e"}, - {file = "backports_datetime_fromisoformat-2.0.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f367b7d7bc00aa6738c95eb48b90817f7f9bd9c61592ceedda29ece97983ee3f"}, - {file = "backports_datetime_fromisoformat-2.0.2-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e0914e357d8559f1821e46fd5ef5d3bd22ec568125ba9e680b6e70cdc352910"}, - {file = "backports_datetime_fromisoformat-2.0.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d5a7cf9cdee221b7721544f424c69747a04091cbff53aa6ae8454644b59f9"}, - {file = "backports_datetime_fromisoformat-2.0.2-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a5e4c77a91db6f434c2eec46c0199d3617c19c812f0c74f7ed8e0f9779da9f0"}, - {file = "backports_datetime_fromisoformat-2.0.2.tar.gz", hash = "sha256:142313bde1f93b0ea55f20f5a6ea034f84c79713daeb252dc47d40019db3812f"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f681f638f10588fa3c101ee9ae2b63d3734713202ddfcfb6ec6cea0778a29d4"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:cd681460e9142f1249408e5aee6d178c6d89b49e06d44913c8fdfb6defda8d1c"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:ee68bc8735ae5058695b76d3bb2aee1d137c052a11c8303f1e966aa23b72b65b"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8273fe7932db65d952a43e238318966eab9e49e8dd546550a41df12175cc2be4"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39d57ea50aa5a524bb239688adc1d1d824c31b6094ebd39aa164d6cadb85de22"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ac6272f87693e78209dc72e84cf9ab58052027733cd0721c55356d3c881791cf"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:44c497a71f80cd2bcfc26faae8857cf8e79388e3d5fbf79d2354b8c360547d58"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:6335a4c9e8af329cb1ded5ab41a666e1448116161905a94e054f205aa6d263bc"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2e4b66e017253cdbe5a1de49e0eecff3f66cd72bcb1229d7db6e6b1832c0443"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:43e2d648e150777e13bbc2549cc960373e37bf65bd8a5d2e0cef40e16e5d8dd0"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:4ce6326fd86d5bae37813c7bf1543bae9e4c215ec6f5afe4c518be2635e2e005"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7c8fac333bf860208fd522a5394369ee3c790d0aa4311f515fcc4b6c5ef8d75"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24a4da5ab3aa0cc293dc0662a0c6d1da1a011dc1edcbc3122a288cfed13a0b45"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:58ea11e3bf912bd0a36b0519eae2c5b560b3cb972ea756e66b73fb9be460af01"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8a375c7dbee4734318714a799b6c697223e4bbb57232af37fbfff88fb48a14c6"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:ac677b1664c4585c2e014739f6678137c8336815406052349c85898206ec7061"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66ce47ee1ba91e146149cf40565c3d750ea1be94faf660ca733d8601e0848147"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:8b7e069910a66b3bba61df35b5f879e5253ff0821a70375b9daf06444d046fa4"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:a3b5d1d04a9e0f7b15aa1e647c750631a873b298cdd1255687bb68779fe8eb35"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec1b95986430e789c076610aea704db20874f0781b8624f648ca9fb6ef67c6e1"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffe5f793db59e2f1d45ec35a1cf51404fdd69df9f6952a0c87c3060af4c00e32"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:620e8e73bd2595dfff1b4d256a12b67fce90ece3de87b38e1dde46b910f46f4d"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4cf9c0a985d68476c1cabd6385c691201dda2337d7453fb4da9679ce9f23f4e7"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:d144868a73002e6e2e6fef72333e7b0129cecdd121aa8f1edba7107fd067255d"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e81b26497a17c29595bc7df20bc6a872ceea5f8c9d6537283945d4b6396aec10"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:5ba00ead8d9d82fd6123eb4891c566d30a293454e54e32ff7ead7644f5f7e575"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:24d574cb4072e1640b00864e94c4c89858033936ece3fc0e1c6f7179f120d0a8"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9735695a66aad654500b0193525e590c693ab3368478ce07b34b443a1ea5e824"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63d39709e17eb72685d052ac82acf0763e047f57c86af1b791505b1fec96915d"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:1ea2cc84224937d6b9b4c07f5cb7c667f2bde28c255645ba27f8a675a7af8234"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4024e6d35a9fdc1b3fd6ac7a673bd16cb176c7e0b952af6428b7129a70f72cce"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5e2dcc94dc9c9ab8704409d86fcb5236316e9dcef6feed8162287634e3568f4c"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fa2de871801d824c255fac7e5e7e50f2be6c9c376fd9268b40c54b5e9da91f42"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:1314d4923c1509aa9696712a7bc0c7160d3b7acf72adafbbe6c558d523f5d491"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:b750ecba3a8815ad8bc48311552f3f8ab99dd2326d29df7ff670d9c49321f48f"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d5117dce805d8a2f78baeddc8c6127281fa0a5e2c40c6dd992ba6b2b367876"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb35f607bd1cbe37b896379d5f5ed4dc298b536f4b959cb63180e05cacc0539d"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:61c74710900602637d2d145dda9720c94e303380803bf68811b2a151deec75c2"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ece59af54ebf67ecbfbbf3ca9066f5687879e36527ad69d8b6e3ac565d565a62"}, + {file = "backports_datetime_fromisoformat-2.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:d0a7c5f875068efe106f62233bc712d50db4d07c13c7db570175c7857a7b5dbd"}, + {file = "backports_datetime_fromisoformat-2.0.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90e202e72a3d5aae673fcc8c9a4267d56b2f532beeb9173361293625fe4d2039"}, + {file = "backports_datetime_fromisoformat-2.0.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2df98ef1b76f5a58bb493dda552259ba60c3a37557d848e039524203951c9f06"}, + {file = "backports_datetime_fromisoformat-2.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7100adcda5e818b5a894ad0626e38118bb896a347f40ebed8981155675b9ba7b"}, + {file = "backports_datetime_fromisoformat-2.0.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e410383f5d6a449a529d074e88af8bc80020bb42b402265f9c02c8358c11da5"}, + {file = "backports_datetime_fromisoformat-2.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2797593760da6bcc32c4a13fa825af183cd4bfd333c60b3dbf84711afca26ef"}, + {file = "backports_datetime_fromisoformat-2.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35a144fd681a0bea1013ccc4cd3fd4dc758ea17ee23dca019c02b82ec46fc0c4"}, + {file = "backports_datetime_fromisoformat-2.0.3.tar.gz", hash = "sha256:b58edc8f517b66b397abc250ecc737969486703a66eb97e01e6d51291b1a139d"}, ] [[package]] @@ -607,4 +607,4 @@ speedup = [] [metadata] lock-version = "2.0" python-versions = ">=3.9.0,<3.12" -content-hash = "15554fce838607450525ee31d2a29e242c794405bdfbaea190154703ac3eb3d1" +content-hash = "20494aa029157d39cccd6bcae2e48d75b75f41cdc3cbe4d2b1b87772f4f4d023"