From 96563dbe544d820400bc4bcd1aaf3144974da1d1 Mon Sep 17 00:00:00 2001 From: Mosquito Date: Tue, 4 Jun 2024 12:19:53 +0200 Subject: [PATCH] DNS server basic implementation (#211) * DNS server basic implementation WIP * Fix GRPSService --- aiomisc/process_pool.py | 4 +- aiomisc/service/dns/__init__.py | 14 + aiomisc/service/dns/records.py | 400 +++++++++ aiomisc/service/dns/service.py | 131 +++ aiomisc/service/dns/store.py | 51 ++ aiomisc/service/dns/tree.py | 59 ++ aiomisc/service/dns/zone.py | 44 + aiomisc/service/grpc_server.py | 18 +- aiomisc/thread_pool.py | 4 +- .../locale/ru/LC_MESSAGES/api/aiomisc.po | 324 ++++--- .../locale/ru/LC_MESSAGES/api/aiomisc_log.po | 10 +- docs/source/locale/ru/LC_MESSAGES/logging.po | 48 +- docs/source/locale/ru/LC_MESSAGES/plugins.po | 52 +- docs/source/locale/ru/LC_MESSAGES/services.po | 609 +++++++++++-- docs/source/logging.rst | 2 +- docs/source/services.rst | 326 +++++++ poetry.lock | 830 +++++++++--------- pyproject.toml | 22 +- tests/test_dns.py | 518 +++++++++++ tests/test_dns_server.py | 223 +++++ 20 files changed, 3033 insertions(+), 656 deletions(-) create mode 100644 aiomisc/service/dns/__init__.py create mode 100644 aiomisc/service/dns/records.py create mode 100644 aiomisc/service/dns/service.py create mode 100644 aiomisc/service/dns/store.py create mode 100644 aiomisc/service/dns/tree.py create mode 100644 aiomisc/service/dns/zone.py create mode 100644 tests/test_dns.py create mode 100644 tests/test_dns_server.py diff --git a/aiomisc/process_pool.py b/aiomisc/process_pool.py index be593d62..e102df33 100644 --- a/aiomisc/process_pool.py +++ b/aiomisc/process_pool.py @@ -39,9 +39,7 @@ def _statistic_callback( self._statistic.sum_time += loop.time() - start_time def submit(self, *args: Any, **kwargs: Any) -> Future: - """ - Submit blocking function to the pool - """ + """Submit blocking function to the pool""" loop = asyncio.get_running_loop() start_time = loop.time() future = super().submit(*args, **kwargs) diff --git a/aiomisc/service/dns/__init__.py b/aiomisc/service/dns/__init__.py new file mode 100644 index 00000000..5d78c6b8 --- /dev/null +++ b/aiomisc/service/dns/__init__.py @@ -0,0 +1,14 @@ +from . import records +from .service import DNSServer, TCPDNSServer, UDPDNSServer +from .store import DNSStore +from .zone import DNSZone + + +__all__ = ( + "DNSServer", + "DNSStore", + "DNSZone", + "TCPDNSServer", + "UDPDNSServer", + "records", +) diff --git a/aiomisc/service/dns/records.py b/aiomisc/service/dns/records.py new file mode 100644 index 00000000..3e1467d0 --- /dev/null +++ b/aiomisc/service/dns/records.py @@ -0,0 +1,400 @@ +import enum +from abc import ABC, abstractmethod +from dataclasses import dataclass, field +from typing import Any, Hashable, Iterable, List + +import dnslib # type: ignore[import-untyped] + + +class RecordType(enum.IntEnum): + A = 1 + A6 = 38 + AAAA = 28 + AFSDB = 18 + ANY = 255 + APL = 42 + AXFR = 252 + CAA = 257 + CDNSKEY = 60 + CDS = 59 + CERT = 37 + CNAME = 5 + CSYNC = 62 + DHCID = 49 + DLV = 32769 + DNAME = 39 + DNSKEY = 48 + DS = 43 + EUI48 = 108 + EUI64 = 109 + HINFO = 13 + HIP = 53 + HIP_AC = 55 + HTTPS = 65 + IPSECKEY = 45 + IXFR = 251 + KEY = 25 + KX = 36 + LOC = 29 + MX = 15 + NAPTR = 35 + NS = 2 + NSEC = 47 + NSEC3 = 50 + NSEC3PARAM = 51 + NULL = 10 + OPENPGPKEY = 61 + OPT = 41 + PTR = 12 + RP = 17 + RRSIG = 46 + SIG = 24 + SOA = 6 + SPF = 99 + SRV = 33 + SSHFP = 44 + SVCB = 64 + TA = 32768 + TKEY = 249 + TLSA = 52 + TSIG = 250 + TXT = 16 + URI = 256 + ZONEMD = 63 + + +class DNSClass(enum.IntEnum): + IN = 1 + CS = 2 + CH = 3 + Hesiod = 4 + NONE = 254 + ASTERISK = 255 + + +class RD(dnslib.RD, Hashable, ABC): + def __hash__(self) -> int: + return hash(self.data) + + @classmethod + @abstractmethod + def create(cls, *args: Any, **kwargs: Any) -> "DNSRecord": + raise NotImplementedError + + +@dataclass(frozen=True) +class DNSRecord: + name: str + type: RecordType + data: RD + cls: DNSClass = field(default=DNSClass.IN) + ttl: int = field(default=3600) + + def rr(self, query_type: int) -> dnslib.RR: + return dnslib.RR( + rname=self.name, + rtype=query_type, + rclass=self.cls, + ttl=self.ttl, + rdata=self.data, + ) + + +class A(dnslib.A, RD): + @classmethod + def create(cls, name: str, ip: str, ttl: int = 3600) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.A, + data=cls(ip), + ttl=ttl, + ) + + +class AAAA(dnslib.AAAA, RD): + @classmethod + def create(cls, name: str, ipv6: str, ttl: int = 3600) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.AAAA, + data=cls(ipv6), + ttl=ttl, + ) + + +class RDLabel(RD): + label: bytes + __type__: RecordType + + @classmethod + def create(cls, name: str, label: str, ttl: int = 3600) -> DNSRecord: + return DNSRecord( + name=name, + type=cls.__type__, + data=cls(label), + ttl=ttl, + ) + + def __hash__(self) -> int: + return hash(self.label) + + +class CNAME(dnslib.CNAME, RDLabel): + __type__ = RecordType.CNAME + + +class NS(dnslib.NS, RDLabel): + __type__ = RecordType.NS + + +class PTR(dnslib.PTR, RDLabel): + __type__ = RecordType.PTR + + +class DNAME(dnslib.DNAME, RDLabel): + __type__ = RecordType.DNAME + + +class MX(dnslib.MX, RD): + @classmethod + def create( + cls, name: str, exchange: str, preference: int, ttl: int = 3600, + ) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.MX, + data=cls(exchange, preference), + ttl=ttl, + ) + + +class TXT(dnslib.TXT, RD): + @classmethod + def create(cls, name: str, text: str, ttl: int = 3600) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.TXT, + data=cls(text), + ttl=ttl, + ) + + +class SOA(dnslib.SOA, RD): + @classmethod + def create( + cls, name: str, mname: str, rname: str, serial: int, + refresh: int, retry: int, expire: int, minimum: int, + ttl: int = 3600, + ) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.SOA, + data=cls( + mname, rname, + (serial, refresh, retry, expire, minimum), + ), + ttl=ttl, + ) + + +class SRV(dnslib.SRV, RD): + @classmethod + def create( + cls, name: str, priority: int, weight: int, port: int, + target: str, ttl: int = 3600, + ) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.SRV, + data=cls(priority, weight, port, target), + ttl=ttl, + ) + + +class CAA(dnslib.CAA, RD): + @classmethod + def create( + cls, name: str, flags: int, tag: str, value: str, + ttl: int = 3600, + ) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.CAA, + data=cls(flags, tag, value), + ttl=ttl, + ) + + +class NAPTR(dnslib.NAPTR, RD): + @classmethod + def create( + cls, name: str, order: int, preference: int, flags: str, + service: str, regexp: str, replacement: str, ttl: int = 3600, + ) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.NAPTR, + data=cls(order, preference, flags, service, regexp, replacement), + ttl=ttl, + ) + + +class DS(dnslib.DS, RD): + @classmethod + def create( + cls, name: str, key_tag: int, algorithm: int, digest_type: int, + digest: str, ttl: int = 3600, + ) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.DS, + data=cls(key_tag, algorithm, digest_type, digest), + ttl=ttl, + ) + + +class DNSKEY(dnslib.DNSKEY, RD): + @classmethod + def create( + cls, name: str, flags: int, protocol: int, algorithm: int, + key: str, ttl: int = 3600, + ) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.DNSKEY, + data=cls(flags, protocol, algorithm, key), + ttl=ttl, + ) + + +class RRSIG(dnslib.RRSIG, RD): + @classmethod + def create( + cls, name: str, type_covered: int, algorithm: int, labels: int, + original_ttl: int, expiration: int, inception: int, key_tag: int, + signer: str, signature: str, ttl: int = 3600, + ) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.RRSIG, + data=cls( + type_covered, algorithm, labels, original_ttl, expiration, + inception, key_tag, signer, signature, + ), + ttl=ttl, + ) + + +class NSEC(dnslib.NSEC, RD): + @classmethod + def create( + cls, name: str, next_domain: str, + rrtypes: Iterable[int], ttl: int = 3600, + ) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.NSEC, + data=cls(next_domain, list(rrtypes)), + ttl=ttl, + ) + + +class HTTPS(dnslib.HTTPS, RD): + @classmethod + def create( + cls, name: str, priority: int, target: str, + params: List[str], ttl: int = 3600, + ) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.HTTPS, + data=cls(priority, target, params), + ttl=ttl, + ) + + +class LOC(dnslib.LOC, RD): + @classmethod + def create( + cls, name: str, latitude: float, longitude: float, + altitude: float, size: float, h_precision: float, + v_precision: float, ttl: int = 3600, + ) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.LOC, + data=cls( + latitude, longitude, altitude, size, h_precision, v_precision, + ), + ttl=ttl, + ) + + +class RP(dnslib.RP, RD): + @classmethod + def create( + cls, name: str, mbox: str, txt: str, ttl: int = 3600, + ) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.RP, + data=cls(mbox, txt), + ttl=ttl, + ) + + +class TLSA(dnslib.TLSA, RD): + @classmethod + def create( + cls, name: str, usage: int, selector: int, mtype: int, cert: str, + ttl: int = 3600, + ) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.TLSA, + data=cls(usage, selector, mtype, cert), + ttl=ttl, + ) + + +class SSHFP(dnslib.SSHFP, RD): + @classmethod + def create( + cls, name: str, algorithm: int, fptype: int, + fingerprint: str, ttl: int = 3600, + ) -> DNSRecord: + return DNSRecord( + name=name, + type=RecordType.SSHFP, + data=cls(algorithm, fptype, fingerprint), + ttl=ttl, + ) + + +__all__ = ( + "AAAA", + "CAA", + "CNAME", + "DNAME", + "DNSKEY", + "DS", + "HTTPS", + "LOC", + "MX", + "NAPTR", + "NS", + "NSEC", + "PTR", + "RD", + "RP", + "RRSIG", + "SOA", + "SRV", + "SSHFP", + "TLSA", + "TXT", + "A", + "DNSRecord", + "DNSClass", + "RecordType", +) diff --git a/aiomisc/service/dns/service.py b/aiomisc/service/dns/service.py new file mode 100644 index 00000000..28d68955 --- /dev/null +++ b/aiomisc/service/dns/service.py @@ -0,0 +1,131 @@ +import asyncio +import logging +from asyncio import StreamReader, StreamWriter +from struct import Struct +from typing import Optional, Tuple + +import dnslib # type: ignore[import-untyped] + +from aiomisc.service import TCPServer, UDPServer + +from .records import RecordType +from .store import DNSStore + + +log = logging.getLogger(__name__) + + +class DNSServer: + store: DNSStore + + __proto__: str + + @staticmethod + def get_edns_max_size(record: dnslib.DNSRecord) -> Optional[int]: + for rr in record.ar: + if rr.rtype != dnslib.QTYPE.OPT: + continue + return rr.edns_len + return None + + def handle_request( + self, addr: tuple, data: bytes, should_truncate: bool = False, + ) -> Tuple[bytes, tuple]: + record = dnslib.DNSRecord.parse(data) + question: dnslib.DNSQuestion = record.get_q() + reply = record.reply() + query_name = str(question.get_qname()) + query_type = question.qtype + + try: + log.debug( + "Processing %s request from %r:\n%s\n", + self.__proto__, addr, record, + ) + edns_max_size = self.get_edns_max_size(record) + if edns_max_size is not None: + reply.add_ar(dnslib.EDNS0(udp_len=edns_max_size)) + + records = self.store.query(query_name, RecordType(query_type)) + for rec in records: + reply.add_answer(rec.rr(query_type)) + + if not records: + reply.header.rcode = dnslib.RCODE.NXDOMAIN + + log.debug( + "Sending %s answer to %r:\n%s\n", + self.__proto__, addr, reply, + ) + + reply_body = reply.pack() + + if len(reply_body) > 512: + reply.header.tc = 1 + reply_body = reply.pack() + + if should_truncate: + reply_body = reply_body[:edns_max_size] + except Exception as e: + log.exception( + "Failed to process %s request from %r: %s", + self.__proto__, addr, e, + ) + reply = record.reply() + reply.header.set_rcode(dnslib.RCODE.SERVFAIL) + reply_body = reply.pack() + + return reply_body, addr + + +class UDPDNSServer(DNSServer, UDPServer): + MAX_SIZE = 512 + + store: DNSStore + + __required__ = ("store",) + __proto__ = "UDP" + + async def handle_datagram(self, data: bytes, addr: tuple) -> None: + self.sendto( + *self.handle_request( + data=data, addr=addr, should_truncate=True, + ), + ) + + +TCP_HEADER_STRUCT: Struct = Struct("!H") + + +class TCPDNSServer(DNSServer, TCPServer): + store: DNSStore + + __required__ = ("store",) + + __proto__ = "TCP" + + async def handle_client( + self, reader: StreamReader, writer: StreamWriter, + ) -> None: + addr = writer.get_extra_info("peername") + try: + while True: + data = await reader.readexactly(TCP_HEADER_STRUCT.size) + if not data: + break + length = TCP_HEADER_STRUCT.unpack(data)[0] + packet = await reader.read(length) + + if not data: + break + + reply_body, _ = self.handle_request(data=packet, addr=addr) + + writer.write(TCP_HEADER_STRUCT.pack(len(reply_body))) + writer.write(reply_body) + await writer.drain() + except asyncio.IncompleteReadError: + pass + finally: + writer.close() + await writer.wait_closed() diff --git a/aiomisc/service/dns/store.py b/aiomisc/service/dns/store.py new file mode 100644 index 00000000..d765ed17 --- /dev/null +++ b/aiomisc/service/dns/store.py @@ -0,0 +1,51 @@ +from typing import Optional, Sequence, Tuple + +from .records import DNSRecord, RecordType +from .tree import RadixTree +from .zone import DNSZone + + +class DNSStore: + zones: RadixTree[DNSZone] + + __slots__ = ("zones",) + + def __init__(self) -> None: + self.zones = RadixTree() + + def add_zone(self, zone: DNSZone) -> None: + zone_tuple = self.get_reverse_tuple(zone.name) + if self.zones.search(zone_tuple): + raise ValueError(f"Zone {zone.name} already exists.") + self.zones.insert(zone_tuple, zone) + + def remove_zone(self, zone_name: str) -> None: + zone_tuple = self.get_reverse_tuple(zone_name) + if not self.zones.search(zone_tuple): + raise ValueError( + f"Zone {zone_name} does not exist.", + ) + # Clear zone from RadixTree + self.zones.insert(zone_tuple, None) + + def get_zone(self, zone_name: str) -> Optional[DNSZone]: + zone_tuple = self.get_reverse_tuple(zone_name) + return self.zones.search(zone_tuple) + + def query(self, name: str, record_type: RecordType) -> Sequence[DNSRecord]: + if not name.endswith("."): + name += "." + zone_tuple = self.get_zone_for_name(name) + if not zone_tuple: + return () + zone = self.zones.search(zone_tuple) + return zone.get_records(name, record_type) if zone is not None else () + + def get_zone_for_name(self, name: str) -> Optional[Tuple[str, ...]]: + labels = self.get_reverse_tuple(name) + result = self.zones.find_prefix(labels) + return result[0] if result else None + + @staticmethod + def get_reverse_tuple(zone_name: str) -> Tuple[str, ...]: + return tuple(zone_name.strip(".").split("."))[::-1] diff --git a/aiomisc/service/dns/tree.py b/aiomisc/service/dns/tree.py new file mode 100644 index 00000000..1b32eec9 --- /dev/null +++ b/aiomisc/service/dns/tree.py @@ -0,0 +1,59 @@ +from typing import Any, Dict, Generic, Hashable, List, Optional, Tuple, TypeVar + + +K = Tuple[str, ...] +T = TypeVar("T", bound=Any) + + +class RadixNode(Generic[T]): + __slots__ = ("children", "value") + + def __init__(self) -> None: + self.children: Dict[Hashable, RadixNode[T]] = {} + self.value: Optional[T] = None + + +class RadixTree(Generic[T]): + root: RadixNode[T] + + __slots__ = ("root",) + + def __init__(self) -> None: + self.root = RadixNode() + + def insert(self, key: K, value: Optional[T]) -> None: + node = self.root + for part in key: + if part not in node.children: + node.children[part] = RadixNode() + node = node.children[part] + node.value = value + + def search(self, key: K) -> Optional[T]: + node = self.root + for part in key: + if part not in node.children: + return None + node = node.children[part] + return node.value + + def find_prefix(self, key: K) -> Optional[Tuple[K, T]]: + node = self.root + longest_prefix: List[str] = [] + value = None + part: Hashable + if node.value is not None: + value = node.value + for part in key: + if part in node.children: + node = node.children[part] + longest_prefix.append(part) + if node.value is not None: + value = node.value + else: + break + + if value is None: + return None + + return tuple(longest_prefix), value diff --git a/aiomisc/service/dns/zone.py b/aiomisc/service/dns/zone.py new file mode 100644 index 00000000..60224b46 --- /dev/null +++ b/aiomisc/service/dns/zone.py @@ -0,0 +1,44 @@ +from collections import defaultdict +from typing import DefaultDict, Sequence, Set, Tuple + +from .records import DNSRecord, RecordType + + +class DNSZone: + records: DefaultDict[Tuple[str, RecordType], Set[DNSRecord]] + name: str + + __slots__ = ("name", "records") + + def __init__(self, name: str): + if not name.endswith("."): + name += "." + self.name = name + self.records = defaultdict(set) + + def add_record(self, record: DNSRecord) -> None: + if not self._is_valid_record(record): + raise ValueError( + f"Record {record.name} does not belong to zone {self.name}", + ) + key = (record.name, record.type) + self.records[key].add(record) + + def remove_record(self, record: DNSRecord) -> None: + key = (record.name, record.type) + if key in self.records: + self.records[key].discard(record) + if self.records[key]: + return + del self.records[key] + + def get_records( + self, name: str, record_type: RecordType, + ) -> Sequence[DNSRecord]: + if not name.endswith("."): + name += "." + key = (name, record_type) + return tuple(self.records.get(key, ())) + + def _is_valid_record(self, record: DNSRecord) -> bool: + return record.name.endswith(self.name) diff --git a/aiomisc/service/grpc_server.py b/aiomisc/service/grpc_server.py index 1d18a2cf..72ebcf23 100644 --- a/aiomisc/service/grpc_server.py +++ b/aiomisc/service/grpc_server.py @@ -2,9 +2,12 @@ import logging import re import sys +from collections import defaultdict from concurrent.futures import Executor from types import MappingProxyType -from typing import Any, Optional, Sequence, Set, Tuple +from typing import ( + Any, DefaultDict, Dict, Mapping, Optional, Sequence, Set, Tuple, +) from .base import Service @@ -36,6 +39,7 @@ class GRPCService(Service): _server_args: MappingProxyType _insecure_ports: Set[Tuple[str, PortFuture]] _secure_ports: Set[Tuple[str, grpc.ServerCredentials, PortFuture]] + _registered_services: DefaultDict[str, Dict[str, grpc.RpcMethodHandler]] def __init__( self, *, @@ -60,6 +64,7 @@ def __init__( self._insecure_ports = set() self._secure_ports = set() self._reflection = reflection + self._registered_services = defaultdict(dict) super().__init__(**kwds) @classmethod @@ -90,6 +95,12 @@ async def start(self) -> None: service_names.append(reflection.SERVICE_NAME) reflection.enable_server_reflection(service_names, self._server) + for name, handlers in self._registered_services.items(): + # noinspection PyUnresolvedReferences + self._server.add_registered_method_handlers( # type: ignore + name, handlers, + ) + self._server.add_generic_rpc_handlers(tuple(self._services)) await self._server.start() @@ -102,6 +113,11 @@ def add_generic_rpc_handlers( for service in generic_rpc_handlers: self._services.add(service) + def add_registered_method_handlers( + self, name: str, handlers: Mapping[str, grpc.RpcMethodHandler], + ) -> None: + self._registered_services[name].update(handlers) + def add_insecure_port(self, address: str) -> PortFuture: future: PortFuture = asyncio.Future() self._insecure_ports.add((address, future)) diff --git a/aiomisc/thread_pool.py b/aiomisc/thread_pool.py index 1e4e9f1e..26919f07 100644 --- a/aiomisc/thread_pool.py +++ b/aiomisc/thread_pool.py @@ -224,9 +224,7 @@ def _start_thread(self, idx: int) -> threading.Thread: def submit( # type: ignore self, fn: F, *args: Any, **kwargs: Any, ) -> asyncio.Future: - """ - Submit blocking function to the pool - """ + """Submit blocking function to the pool""" if fn is None or not callable(fn): raise ValueError("First argument must be callable") diff --git a/docs/source/locale/ru/LC_MESSAGES/api/aiomisc.po b/docs/source/locale/ru/LC_MESSAGES/api/aiomisc.po index f70ae6cb..1363b7dd 100644 --- a/docs/source/locale/ru/LC_MESSAGES/api/aiomisc.po +++ b/docs/source/locale/ru/LC_MESSAGES/api/aiomisc.po @@ -6,14 +6,14 @@ msgid "" msgstr "" "Project-Id-Version: aiomisc 16.1.16\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-11-21 11:47+0100\n" +"POT-Creation-Date: 2024-06-03 23:11+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Ivan Sitkin \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.13.1\n" +"Generated-By: Babel 2.15.0\n" #: ../../source/api/aiomisc.rst:2 msgid "``aiomisc`` module" @@ -23,6 +23,18 @@ msgstr "Модуль ``aiomisc``" msgid "``aiomisc.aggregate`` module" msgstr "Модуль ``aiomisc.aggregate``" +#: aiomisc.aggregate.AggregateAsyncFunc:1 of +msgid "" +"Bases: :py:class:`~typing.Protocol`, :py:class:`~typing.Generic`\\ " +"[:py:obj:`~aiomisc.aggregate.V`, :py:obj:`~aiomisc.aggregate.R`]" +msgstr "" + +#: aiomisc.aggregate.AggregateFunc:1 of +msgid "" +"Bases: :py:class:`~typing.Protocol`, :py:class:`~typing.Generic`\\ " +"[:py:obj:`~aiomisc.aggregate.S`, :py:obj:`~aiomisc.aggregate.T`]" +msgstr "" + #: aiomisc.aggregate.AggregateStatistic:1 aiomisc.backoff.BackoffStatistic:1 #: aiomisc.circuit_breaker.CircuitBreakerStatistic:1 #: aiomisc.iterator_wrapper.IteratorWrapperStatistic:1 @@ -33,34 +45,25 @@ msgstr "Модуль ``aiomisc.aggregate``" msgid "Bases: :py:class:`~aiomisc.counters.Statistic`" msgstr "" -#: aiomisc.aggregate.Aggregator:1 aiomisc.circuit_breaker.CircuitBreaker:1 -#: aiomisc.cron.CronCallback:1 aiomisc.periodic.PeriodicCallback:1 of -msgid "Bases: :py:class:`~aiomisc.compat.EventLoopMixin`" +#: aiomisc.aggregate.Aggregator:1 of +msgid "" +"Bases: :py:class:`~aiomisc.aggregate.AggregatorAsync`\\ " +"[:py:obj:`~aiomisc.aggregate.V`, :py:obj:`~aiomisc.aggregate.R`], " +":py:class:`~typing.Generic`\\ [:py:obj:`~aiomisc.aggregate.V`, " +":py:obj:`~aiomisc.aggregate.R`]" msgstr "" #: aiomisc.aggregate.AggregatorAsync:1 of -msgid "Bases: :py:class:`~aiomisc.aggregate.Aggregator`" -msgstr "" - -#: aiomisc.aggregate.Arg:1 aiomisc.counters.StatisticResult:1 -#: aiomisc.thread_pool.WorkItemBase:1 of -msgid "Bases: :py:class:`~typing.NamedTuple`" +msgid "" +"Bases: :py:class:`~aiomisc.compat.EventLoopMixin`, " +":py:class:`~typing.Generic`\\ [:py:obj:`~aiomisc.aggregate.V`, " +":py:obj:`~aiomisc.aggregate.R`]" msgstr "" #: aiomisc.aggregate.Arg:1 of -msgid "Create new instance of Arg(value, future)" -msgstr "" - -#: ../../docstring aiomisc.aggregate.Arg.future:1 -#: aiomisc.counters.StatisticResult.name:1 -#: aiomisc.thread_pool.WorkItemBase.args:1 of -msgid "Alias for field number 1" -msgstr "" - -#: ../../docstring aiomisc.aggregate.Arg.value:1 -#: aiomisc.counters.StatisticResult.kind:1 -#: aiomisc.thread_pool.WorkItemBase.func:1 of -msgid "Alias for field number 0" +msgid "" +"Bases: :py:class:`~typing.Generic`\\ [:py:obj:`~aiomisc.aggregate.V`, " +":py:obj:`~aiomisc.aggregate.R`]" msgstr "" #: aiomisc.aggregate.ResultNotSetError:1 @@ -196,6 +199,11 @@ msgstr "" msgid "``aiomisc.circuit_breaker`` module" msgstr "Модуль ``aiomisc.circuit_breaker``" +#: aiomisc.circuit_breaker.CircuitBreaker:1 aiomisc.cron.CronCallback:1 +#: aiomisc.periodic.PeriodicCallback:1 of +msgid "Bases: :py:class:`~aiomisc.compat.EventLoopMixin`" +msgstr "" + #: aiomisc.circuit_breaker.CircuitBreaker:1 of msgid "" "Circuit Breaker pattern implementation. The class instance collects call " @@ -263,12 +271,6 @@ msgstr "" msgid "Bases: :py:class:`~enum.IntEnum`" msgstr "" -#: aiomisc.circuit_breaker.CircuitBreakerStates:1 -#: aiomisc.circuit_breaker.CounterKey:1 aiomisc.io.Compression:1 -#: aiomisc_log.enum.LogFormat:1 aiomisc_log.enum.LogLevel:1 of -msgid "An enumeration." -msgstr "" - #: ../../source/api/aiomisc.rst:29 msgid "``aiomisc.compat`` module" msgstr "Модуль ``aiomisc.compat``" @@ -279,18 +281,20 @@ msgstr "" #: aiomisc.compat.EventLoopMixin:1 aiomisc.context.Context:1 #: aiomisc.counters.AbstractStatistic:1 aiomisc.counters.Metric:1 -#: aiomisc.entrypoint.Entrypoint:1 aiomisc.iterator_wrapper.FromThreadChannel:1 +#: aiomisc.counters.StatisticResult:1 aiomisc.entrypoint.Entrypoint:1 +#: aiomisc.iterator_wrapper.FromThreadChannel:1 #: aiomisc.iterator_wrapper.QueueWrapperBase:1 #: aiomisc.recurring.RecurringCallback:1 aiomisc.signal.Signal:1 -#: aiomisc.thread_pool.CoroutineWaiter:1 aiomisc.utils.SelectAwaitable:1 -#: aiomisc.worker_pool.WorkerPool:1 of +#: aiomisc.thread_pool.CoroutineWaiter:1 aiomisc.thread_pool.WorkItemBase:1 +#: aiomisc.utils.SelectAwaitable:1 aiomisc.worker_pool.WorkerPool:1 of msgid "Bases: :py:class:`object`" msgstr "" #: of typing.ParamSpec:1 msgid "" "Bases: :py:class:`~typing._Final`, :py:class:`~typing._Immutable`, " -":py:class:`~typing._TypeVarLike`" +":py:class:`~typing._BoundVarianceMixin`, " +":py:class:`~typing._PickleUsingNameMixin`" msgstr "" #: of typing.ParamSpec:1 @@ -335,24 +339,14 @@ msgid "" msgstr "" #: of typing.ParamSpec:30 -msgid "" -"Parameter specification variables defined with covariant=True or " -"contravariant=True can be used to declare covariant or contravariant " -"generic types. These keyword arguments are valid, but their actual " -"semantics are yet to be decided. See PEP 612 for details." -msgstr "" - -#: of typing.ParamSpec:35 msgid "Parameter specification variables can be introspected. e.g.:" msgstr "" -#: of typing.ParamSpec:37 -msgid "" -"P.__name__ == 'P' P.__bound__ == None P.__covariant__ == False " -"P.__contravariant__ == False" +#: of typing.ParamSpec:32 +msgid "P.__name__ == 'P'" msgstr "" -#: of typing.ParamSpec:42 +#: of typing.ParamSpec:34 msgid "" "Note that only parameter specification variables defined in global scope " "can be pickled." @@ -380,10 +374,14 @@ msgstr "" #: of typing.Protocol:9 msgid "" "Such classes are primarily used with static type checkers that recognize " -"structural subtyping (static duck-typing), for example::" +"structural subtyping (static duck-typing)." +msgstr "" + +#: of typing.Protocol:12 typing.final:6 +msgid "For example::" msgstr "" -#: of typing.Protocol:12 +#: of typing.Protocol:14 msgid "" "class C:\n" " def meth(self) -> int:\n" @@ -395,7 +393,7 @@ msgid "" "func(C()) # Passes static type check" msgstr "" -#: of typing.Protocol:21 +#: of typing.Protocol:23 msgid "" "See PEP 544 for details. Protocol classes decorated with " "@typing.runtime_checkable act as simple-minded runtime protocols that " @@ -403,7 +401,7 @@ msgid "" "signatures. Protocol classes can be generic, they are defined as::" msgstr "" -#: of typing.Protocol:26 +#: of typing.Protocol:28 msgid "" "class GenProto(Protocol[T]):\n" " def meth(self) -> T:\n" @@ -411,46 +409,37 @@ msgid "" msgstr "" #: of typing.final:1 -msgid "A decorator to indicate final methods and final classes." +msgid "Decorator to indicate final methods and final classes." msgstr "" #: of typing.final:3 msgid "" "Use this decorator to indicate to type checkers that the decorated method" -" cannot be overridden, and decorated class cannot be subclassed. For " -"example:" -msgstr "" - -#: of typing.final:9 -msgid "class Base:" +" cannot be overridden, and decorated class cannot be subclassed." msgstr "" #: of typing.final:8 -msgid "@final def done(self) -> None:" -msgstr "" - -#: of typing.final:10 typing.final:13 typing.final:17 typing.final:19 -msgid "..." -msgstr "" - -#: of typing.final:13 -msgid "class Sub(Base):" -msgstr "" - -#: of typing.final:13 -msgid "def done(self) -> None: # Error reported by type checker" -msgstr "" - -#: of typing.final:15 -msgid "@final class Leaf:" -msgstr "" - -#: of typing.final:19 -msgid "class Other(Leaf): # Error reported by type checker" +msgid "" +"class Base:\n" +" @final\n" +" def done(self) -> None:\n" +" ...\n" +"class Sub(Base):\n" +" def done(self) -> None: # Error reported by type checker\n" +" ...\n" +"\n" +"@final\n" +"class Leaf:\n" +" ...\n" +"class Other(Leaf): # Error reported by type checker\n" +" ..." msgstr "" -#: of typing.final:21 -msgid "There is no runtime checking of these properties." +#: of typing.final:22 +msgid "" +"There is no runtime checking of these properties. The decorator attempts " +"to set the ``__final__`` attribute to ``True`` on the decorated object to" +" allow runtime introspection." msgstr "" #: of time.time_ns:1 @@ -473,20 +462,6 @@ msgstr "" msgid "Bases: :py:class:`~aiomisc.counters.AbstractStatistic`" msgstr "" -#: aiomisc.counters.StatisticResult:1 of -msgid "Create new instance of StatisticResult(kind, name, metric, value)" -msgstr "" - -#: ../../docstring aiomisc.counters.StatisticResult.metric:1 -#: aiomisc.thread_pool.WorkItemBase.kwargs:1 of -msgid "Alias for field number 2" -msgstr "" - -#: ../../docstring aiomisc.counters.StatisticResult.value:1 -#: aiomisc.thread_pool.WorkItemBase.future:1 of -msgid "Alias for field number 3" -msgstr "" - #: ../../source/api/aiomisc.rst:53 msgid "``aiomisc.cron`` module" msgstr "Модуль ``aiomisc.cron``" @@ -586,6 +561,7 @@ msgid "``aiomisc.iterator_wrapper`` module" msgstr "Модуль ``aiomisc.iterator_wrapper``" #: aiomisc.iterator_wrapper.ChannelClosed:1 +#: aiomisc.thread_pool.TaskChannelCloseException:1 #: aiomisc.thread_pool.ThreadPoolException:1 of msgid "Bases: :py:class:`RuntimeError`" msgstr "" @@ -651,7 +627,8 @@ msgstr "" msgid "Initializes a new ProcessPoolExecutor instance." msgstr "" -#: aiomisc.process_pool.ProcessPoolExecutor:11 +#: aiomisc.process_pool.ProcessPoolExecutor:18 +#: aiomisc.thread_pool.ThreadPoolExecutor:9 #: aiomisc.thread_pool.ThreadPoolExecutor.shutdown:11 of msgid "Args:" msgstr "" @@ -666,18 +643,29 @@ msgid "" "processes will be created as the machine has processors." msgstr "" -#: aiomisc.process_pool.ProcessPoolExecutor:7 of +#: aiomisc.process_pool.ProcessPoolExecutor:8 of msgid "mp_context: A multiprocessing context to launch the workers. This" msgstr "" #: aiomisc.process_pool.ProcessPoolExecutor:8 of -msgid "object should provide SimpleQueue, Queue and Process." +msgid "" +"object should provide SimpleQueue, Queue and Process. Useful to allow " +"specific multiprocessing start methods." msgstr "" -#: aiomisc.process_pool.ProcessPoolExecutor:9 of +#: aiomisc.process_pool.ProcessPoolExecutor:10 of msgid "" "initializer: A callable used to initialize worker processes. initargs: A " -"tuple of arguments to pass to the initializer." +"tuple of arguments to pass to the initializer. max_tasks_per_child: The " +"maximum number of tasks a worker process" +msgstr "" + +#: aiomisc.process_pool.ProcessPoolExecutor:13 of +msgid "" +"can complete before it will exit and be replaced with a fresh worker " +"process. The default of None means worker process will live as long as " +"the executor. Requires a non-'fork' mp_context start method. When given, " +"we default to using 'spawn' if no mp_context is supplied." msgstr "" #: aiomisc.process_pool.ProcessPoolExecutor.submit:1 @@ -713,10 +701,47 @@ msgstr "Модуль ``aiomisc.thread_pool``" msgid "Bases: :py:class:`~aiomisc.iterator_wrapper.IteratorWrapper`" msgstr "" +#: aiomisc.thread_pool.TaskChannel:1 of +msgid "Bases: :py:class:`~_queue.SimpleQueue`" +msgstr "" + +#: aiomisc.thread_pool.TaskChannel.get:1 of +msgid "Remove and return an item from the queue." +msgstr "" + +#: aiomisc.thread_pool.TaskChannel.get:3 of +msgid "" +"If optional args 'block' is true and 'timeout' is None (the default), " +"block if necessary until an item is available. If 'timeout' is a non-" +"negative number, it blocks at most 'timeout' seconds and raises the Empty" +" exception if no item was available within that time. Otherwise ('block' " +"is false), return an item if one is immediately available, else raise the" +" Empty exception ('timeout' is ignored in that case)." +msgstr "" + #: aiomisc.thread_pool.ThreadPoolExecutor:1 of msgid "Bases: :py:class:`~concurrent.futures.thread.ThreadPoolExecutor`" msgstr "" +#: aiomisc.thread_pool.ThreadPoolExecutor:1 of +msgid "Initializes a new ThreadPoolExecutor instance." +msgstr "" + +#: aiomisc.thread_pool.ThreadPoolExecutor:4 of +msgid "max_workers: The maximum number of threads that can be used to" +msgstr "" + +#: aiomisc.thread_pool.ThreadPoolExecutor:5 of +msgid "execute the given calls." +msgstr "" + +#: aiomisc.thread_pool.ThreadPoolExecutor:6 of +msgid "" +"thread_name_prefix: An optional name prefix to give our threads. " +"initializer: A callable used to initialize worker threads. initargs: A " +"tuple of arguments to pass to the initializer." +msgstr "" + #: aiomisc.thread_pool.ThreadPoolExecutor.shutdown:1 of msgid "Clean-up the resources associated with the Executor." msgstr "" @@ -749,14 +774,6 @@ msgstr "" msgid "Bases: :py:class:`~aiomisc.thread_pool.WorkItemBase`" msgstr "" -#: aiomisc.thread_pool.WorkItem:1 aiomisc.thread_pool.WorkItemBase:1 of -msgid "Create new instance of WorkItemBase(func, args, kwargs, future, loop)" -msgstr "" - -#: ../../docstring aiomisc.thread_pool.WorkItemBase.loop:1 of -msgid "Alias for field number 4" -msgstr "" - #: ../../source/api/aiomisc.rst:149 msgid "``aiomisc.timeout`` module" msgstr "Модуль ``aiomisc.timeout``" @@ -969,3 +986,94 @@ msgstr "Модуль ``aiomisc.worker_pool``" #~ "if no mp_context is supplied." #~ msgstr "" +#~ msgid "Bases: :py:class:`~aiomisc.aggregate.Aggregator`" +#~ msgstr "" + +#~ msgid "Bases: :py:class:`~typing.NamedTuple`" +#~ msgstr "" + +#~ msgid "Create new instance of Arg(value, future)" +#~ msgstr "" + +#~ msgid "Alias for field number 1" +#~ msgstr "" + +#~ msgid "Alias for field number 0" +#~ msgstr "" + +#~ msgid "" +#~ "Bases: :py:class:`~typing._Final`, " +#~ ":py:class:`~typing._Immutable`, :py:class:`~typing._TypeVarLike`" +#~ msgstr "" + +#~ msgid "" +#~ "Parameter specification variables defined with" +#~ " covariant=True or contravariant=True can " +#~ "be used to declare covariant or " +#~ "contravariant generic types. These keyword" +#~ " arguments are valid, but their " +#~ "actual semantics are yet to be " +#~ "decided. See PEP 612 for details." +#~ msgstr "" + +#~ msgid "" +#~ "P.__name__ == 'P' P.__bound__ == None" +#~ " P.__covariant__ == False P.__contravariant__ " +#~ "== False" +#~ msgstr "" + +#~ msgid "" +#~ "Such classes are primarily used with " +#~ "static type checkers that recognize " +#~ "structural subtyping (static duck-typing), " +#~ "for example::" +#~ msgstr "" + +#~ msgid "class Base:" +#~ msgstr "" + +#~ msgid "@final def done(self) -> None:" +#~ msgstr "" + +#~ msgid "..." +#~ msgstr "" + +#~ msgid "class Sub(Base):" +#~ msgstr "" + +#~ msgid "def done(self) -> None: # Error reported by type checker" +#~ msgstr "" + +#~ msgid "@final class Leaf:" +#~ msgstr "" + +#~ msgid "class Other(Leaf): # Error reported by type checker" +#~ msgstr "" + +#~ msgid "There is no runtime checking of these properties." +#~ msgstr "" + +#~ msgid "Create new instance of StatisticResult(kind, name, metric, value)" +#~ msgstr "" + +#~ msgid "Alias for field number 2" +#~ msgstr "" + +#~ msgid "Alias for field number 3" +#~ msgstr "" + +#~ msgid "object should provide SimpleQueue, Queue and Process." +#~ msgstr "" + +#~ msgid "" +#~ "initializer: A callable used to " +#~ "initialize worker processes. initargs: A " +#~ "tuple of arguments to pass to the" +#~ " initializer." +#~ msgstr "" + +#~ msgid "Create new instance of WorkItemBase(func, args, kwargs, future, loop)" +#~ msgstr "" + +#~ msgid "Alias for field number 4" +#~ msgstr "" diff --git a/docs/source/locale/ru/LC_MESSAGES/api/aiomisc_log.po b/docs/source/locale/ru/LC_MESSAGES/api/aiomisc_log.po index 9a60ac7e..878f4359 100644 --- a/docs/source/locale/ru/LC_MESSAGES/api/aiomisc_log.po +++ b/docs/source/locale/ru/LC_MESSAGES/api/aiomisc_log.po @@ -6,14 +6,14 @@ msgid "" msgstr "" "Project-Id-Version: aiomisc 16.1.16\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-11-21 11:47+0100\n" +"POT-Creation-Date: 2024-06-03 23:11+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Sitkin Ivan \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.13.1\n" +"Generated-By: Babel 2.15.0\n" #: ../../source/api/aiomisc_log.rst:2 msgid "``aiomisc_log`` module" @@ -116,15 +116,9 @@ msgstr "Модуль ``aiomisc_log.enum``" msgid "Bases: :py:class:`~enum.Enum`" msgstr "" -#: aiomisc_log.enum.DateFormat:1 aiomisc_log.enum.LogFormat:1 -#: aiomisc_log.enum.LogLevel:1 of -msgid "An enumeration." -msgstr "" - #: aiomisc_log.enum.LogFormat:1 aiomisc_log.enum.LogLevel:1 of msgid "Bases: :py:class:`~enum.IntEnum`" msgstr "" #~ msgid "An enumeration." #~ msgstr "" - diff --git a/docs/source/locale/ru/LC_MESSAGES/logging.po b/docs/source/locale/ru/LC_MESSAGES/logging.po index 838218b2..4a196417 100644 --- a/docs/source/locale/ru/LC_MESSAGES/logging.po +++ b/docs/source/locale/ru/LC_MESSAGES/logging.po @@ -6,14 +6,14 @@ msgid "" msgstr "" "Project-Id-Version: 14\n" "Report-Msgid-Bugs-To: me@mosquito.su\n" -"POT-Creation-Date: 2022-12-29 11:36+0300\n" +"POT-Creation-Date: 2024-06-03 23:15+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Dmitry Orlov \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.11.0\n" +"Generated-By: Babel 2.15.0\n" #: ../../source/logging.rst:2 msgid "Logging configuration" @@ -187,19 +187,49 @@ msgstr "" "except:\n" " logging.exception(\"Rich traceback logger\")" -#: ../../source/logging.rst:112 +#: ../../source/logging.rst:111 +msgid "Disabled" +msgstr "" + +#: ../../source/logging.rst:113 +msgid "" +"Disable to configure logging handler. Useful when you want to configure " +"your own logging handlers using `handlers=` argument." +msgstr "" +"Отключает конфигурацию логов. Полезно когда вы сами хотите " +"сконфигурировать свои собственные logging хендлеры передав их в аргумент " +"`handlers=`." + +#: ../../source/logging.rst:116 +msgid "" +"import logging\n" +"from aiomisc.log import basic_config\n" +"\n" +"# Configure your own log handlers\n" +"basic_config(\n" +" level=logging.INFO,\n" +" log_format='disabled',\n" +" handlers=[logging.StreamHandler()],\n" +" buffered=False,\n" +")\n" +"\n" +"logging.info(\"Use default python logger for example\")" +msgstr "" + +#: ../../source/logging.rst:135 msgid "Buffered log handler" msgstr "Буфферизирующий лог-хендлер" -#: ../../source/logging.rst:114 +#: ../../source/logging.rst:137 +#, fuzzy msgid "" "Parameter `buffered=True` enables a memory buffer that flushes logs in a " -"thread." +"thread. In case the `handlers=` each will be buffered." msgstr "" "Параметр `buffered=True` включает буферизацию логов в памяти, отдельный " -"поток переодически сбрасывает логи в поток." +"поток переодически сбрасывает логи в файловый дескриптор." -#: ../../source/logging.rst:116 +#: ../../source/logging.rst:140 #, python-format msgid "" "import asyncio\n" @@ -262,13 +292,13 @@ msgstr "" " # Ждем пока журналы не попадут в журнал\n" " loop.run_until_complete(asyncio.sleep(1))" -#: ../../source/logging.rst:152 +#: ../../source/logging.rst:176 msgid "``entrypoint`` accepts ``log_format`` parameter for configure it." msgstr "" "``entrypoint`` принимает аргумент ``log_format`` через который можно это " "настроить." -#: ../../source/logging.rst:154 +#: ../../source/logging.rst:178 msgid "" "List of all supported log formats is available from " "``aiomisc.log.LogFormat.choices()``" diff --git a/docs/source/locale/ru/LC_MESSAGES/plugins.po b/docs/source/locale/ru/LC_MESSAGES/plugins.po index 5f91fc1a..93ead304 100644 --- a/docs/source/locale/ru/LC_MESSAGES/plugins.po +++ b/docs/source/locale/ru/LC_MESSAGES/plugins.po @@ -6,14 +6,14 @@ msgid "" msgstr "" "Project-Id-Version: 14\n" "Report-Msgid-Bugs-To: me@mosquito.su\n" -"POT-Creation-Date: 2023-04-24 12:31+0300\n" +"POT-Creation-Date: 2024-06-03 23:11+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Dmitry Orlov \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.12.1\n" +"Generated-By: Babel 2.15.0\n" #: ../../source/plugins.rst:2 msgid "Plugins" @@ -82,6 +82,7 @@ msgstr "" "параметре services." #: ../../source/plugins.rst:40 +#, fuzzy msgid "" "# Content of: ``aiomisc_myplugin/plugin.py``\n" "from typing import Tuple\n" @@ -130,7 +131,10 @@ msgid "" "\n" " assert event.is_set()\n" "\n" -"main()" +"main()\n" +"\n" +"# remove the plugin on when unneeded\n" +"aiomisc.entrypoint.PRE_START.disconnect(hello)" msgstr "" "# Содержимое: ``aiomisc_myplugin/plugin.py``\n" "from typing import Tuple\n" @@ -181,35 +185,35 @@ msgstr "" "\n" "main()" -#: ../../source/plugins.rst:93 +#: ../../source/plugins.rst:96 msgid "The following signals are available in total:" msgstr "Список доступных сигналов такой:" -#: ../../source/plugins.rst:95 +#: ../../source/plugins.rst:98 msgid "``Entrypoint.PRE_START`` - Will be called before `starting` services." msgstr "``Entrypoint.PRE_START`` - Запускается перед `стартом` сервисов." -#: ../../source/plugins.rst:96 +#: ../../source/plugins.rst:99 msgid "``Entrypoint.PRE_STOP`` - Will be called before `stopping` services." msgstr "``Entrypoint.PRE_STOP`` - Запускается перед `остановкой` сервисов." -#: ../../source/plugins.rst:97 +#: ../../source/plugins.rst:100 msgid "" "``Entrypoint.POST_START`` - Will be called after services has been " "`started`." msgstr "``Entrypoint.POST_START`` - Запускается после `старта` сервисов." -#: ../../source/plugins.rst:98 +#: ../../source/plugins.rst:101 msgid "" "``Entrypoint.POST_STOP`` - Will be called after services has been " "`stopped`." msgstr "``Entrypoint.POST_STOP`` - Запускается после `остановки` сервисов" -#: ../../source/plugins.rst:102 +#: ../../source/plugins.rst:105 msgid "List available plugins" msgstr "Список доступный плагинов" -#: ../../source/plugins.rst:104 +#: ../../source/plugins.rst:107 msgid "" "To see a list of all available plugins, you can call from the command " "line ``python -m aiomisc.plugins``:" @@ -217,7 +221,7 @@ msgstr "" "Чтобы просмотреть список всех доступных плагинов, вы можете вызвать из " "командной строки ``python -m aiomisc.plugins``:" -#: ../../source/plugins.rst:107 +#: ../../source/plugins.rst:110 msgid "" "$ python -m aiomisc.plugins\n" "[11:14:42] INFO Available 1 plugins.\n" @@ -226,15 +230,15 @@ msgid "" "systemd_watchdog" msgstr "" -#: ../../source/plugins.rst:114 +#: ../../source/plugins.rst:117 msgid "" "You can also change the behavior and output of the list of modules. To do" " this, there are the following flags:" msgstr "" -"Вы также можете изменить поведение и вывод списка модулей. " -"Для этого существуют следующие флаги:" +"Вы также можете изменить поведение и вывод списка модулей. Для этого " +"существуют следующие флаги:" -#: ../../source/plugins.rst:117 +#: ../../source/plugins.rst:120 msgid "" "$ python3 -m aiomisc.plugins -h\n" "usage: python3 -m aiomisc.plugins [-h] [-q] [-n]\n" @@ -258,11 +262,11 @@ msgid "" " Logging format" msgstr "" -#: ../../source/plugins.rst:135 +#: ../../source/plugins.rst:138 msgid "Here are some run examples." msgstr "Вот несколько примеров запуска." -#: ../../source/plugins.rst:137 +#: ../../source/plugins.rst:140 msgid "" "$ python3 -m aiomisc.plugins -n\n" "[12:25:57] INFO Available 1 plugins.\n" @@ -270,26 +274,26 @@ msgid "" " the entrypoint." msgstr "" -#: ../../source/plugins.rst:143 +#: ../../source/plugins.rst:146 msgid "This prints human-readable list of plugins and its descriptions." msgstr "Этот пример, печатает удобочитаемый список плагинов и их описания." -#: ../../source/plugins.rst:146 +#: ../../source/plugins.rst:149 msgid "" "$ python3 -m aiomisc.plugins -s\n" "systemd_watchdog" msgstr "" -#: ../../source/plugins.rst:151 +#: ../../source/plugins.rst:154 msgid "This useful for ``grep`` or other pipelining tools." msgstr "Это полезно для ``grep`` или других утилит." -#: ../../source/plugins.rst:153 +#: ../../source/plugins.rst:156 msgid "" "The default prints both, the human-readable log to stderr and the list of" " plugins to stdout, so you can use this without options in a pipeline, " "and read the list to stderr." msgstr "" -"По умолчанию печатается человекочитаемый лог в stderr, и список плагинов в " -"stdout, поэтому можно использовать это без параметров в конвейере, " -"и прочитать список в stderr." +"По умолчанию печатается человекочитаемый лог в stderr, и список плагинов " +"в stdout, поэтому можно использовать это без параметров в конвейере, и " +"прочитать список в stderr." diff --git a/docs/source/locale/ru/LC_MESSAGES/services.po b/docs/source/locale/ru/LC_MESSAGES/services.po index cb6c6d00..a4e50243 100644 --- a/docs/source/locale/ru/LC_MESSAGES/services.po +++ b/docs/source/locale/ru/LC_MESSAGES/services.po @@ -6,14 +6,14 @@ msgid "" msgstr "" "Project-Id-Version: 14\n" "Report-Msgid-Bugs-To: me@mosquito.su\n" -"POT-Creation-Date: 2023-11-21 11:45+0100\n" +"POT-Creation-Date: 2024-06-03 23:29+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Sitkin Ivan \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.13.1\n" +"Generated-By: Babel 2.15.0\n" #: ../../source/services.rst:2 msgid "Services" @@ -522,10 +522,459 @@ msgstr "" " loop.run_forever()" #: ../../source/services.rst:393 +#, fuzzy +msgid "DNS Server" +msgstr "asgi сервис" + +#: ../../source/services.rst:395 +msgid "" +"The DNS server described here uses the ``aiomisc`` library, which " +"provides utilities for asynchronous I/O operations, and the ``dnslib`` " +"library for handling DNS records and packets. This setup is ideal for " +"high-performance, non-blocking DNS query handling." +msgstr "" +"Описанный здесь DNS-сервер использует библиотеку «aiomisc», которая " +"предоставляет утилиты для асинхронных операций ввода-вывода, и библиотеку" +" «dnslib» для обработки DNS-записей и пакетов. Эта настройка идеально " +"подходит для высокопроизводительной неблокирующей обработки DNS-запросов." + +#: ../../source/services.rst:401 +msgid "Key Features" +msgstr "Ключевые Характеристики" + +#: ../../source/services.rst:403 +msgid "" +"**Asynchronous I/O**: Utilizes asynchronous operations to handle multiple" +" DNS queries concurrently, ensuring high performance and scalability." +msgstr "" +"**Асинхронный ввод-вывод**: использует асинхронные операции для " +"одновременной обработки нескольких DNS-запросов, обеспечивая высокую " +"производительность и масштабируемость." + +#: ../../source/services.rst:405 +msgid "" +"**UDP and TCP Support**: Supports DNS queries over both UDP and TCP " +"protocols, making it versatile for various network configurations." +msgstr "" +"**Поддержка UDP и TCP**: сервер поддерживает DNS-запросы по протоколам " +"UDP и TCP, что делает его универсальным для различных сетевых " +"конфигураций." + +#: ../../source/services.rst:407 +msgid "" +"**``EDNS0`` Support**: Implements Extension Mechanisms for DNS " +"(``EDNS0``) to handle larger DNS messages and extended functionalities." +msgstr "" +"**Поддержка ``EDNS0``**: реализует механизмы расширения DNS (``EDNS0``) " +"для обработки более крупных сообщений DNS и расширенных функций." + +#: ../../source/services.rst:409 +msgid "" +"**Customizable DNS Records**: Allows easy configuration of DNS zones and " +"records, enabling you to define and manage your DNS entries efficiently." +msgstr "" +"**Настраиваемые записи DNS**: позволяет легко настраивать зоны и записи " +"DNS, позволяя эффективно разрешать записи DNS и управлять ими." + +#: ../../source/services.rst:414 +msgid "Prerequisites" +msgstr "Пререквизиты" + +#: ../../source/services.rst:416 +msgid "Install the required libraries using pip:" +msgstr "Установите необходимые библиотеки с помощью pip:" + +#: ../../source/services.rst:418 +msgid "pip install aiomisc[dns]" +msgstr "" + +#: ../../source/services.rst:424 +msgid "Setting Up the Server" +msgstr "Настройка сервера" + +#: ../../source/services.rst:426 +msgid "" +"from aiomisc import entrypoint\n" +"from aiomisc.service.dns import (\n" +" DNSStore, DNSZone, TCPDNSServer, UDPDNSServer, records,\n" +")\n" +"\n" +"zone = DNSZone(\"test.\")\n" +"zone.add_record(records.A.create(\"test.\", \"10.10.10.10\"))\n" +"zone.add_record(records.AAAA.create(\"test.\", \"fd10::10\"))\n" +"\n" +"store = DNSStore()\n" +"store.add_zone(zone)\n" +"services = [\n" +" UDPDNSServer(\n" +" store=store, address=\"::1\", port=5053,\n" +" ),\n" +" TCPDNSServer(\n" +" store=store, address=\"::1\", port=5053,\n" +" ),\n" +"]\n" +"\n" +"if __name__ == \"__main__\":\n" +" with entrypoint(*services, log_level=\"debug\") as loop:\n" +" loop.run_forever()" +msgstr "" + +#: ../../source/services.rst:454 +msgid "Testing the Server" +msgstr "Тестирование сервера" + +#: ../../source/services.rst:456 +msgid "" +"You can test the DNS server using tools like ``dig``. For example, to " +"query the A and AAAA records for ``test.``, use the following commands:" +msgstr "" +"Вы можете протестировать DNS-сервер, используя такие инструменты, как " +"«dig». Например, чтобы запросить записи A и AAAA для ``test.``, " +"используйте следующие команды:" + +#: ../../source/services.rst:460 +msgid "" +"dig @::1 -p 5053 test. A\n" +"dig @::1 -p 5053 test. AAAA\n" +"dig @::1 -p 5053 +tcp test. A\n" +"dig @::1 -p 5053 +tcp test. AAAA" +msgstr "" + +#: ../../source/services.rst:468 +msgid "" +"These commands should return the IP addresses ``10.10.10.10`` and " +"``fd10::10`` respectively, confirming that the DNS server is working " +"correctly." +msgstr "" +"Эти команды должны вернуть IP-адреса ``10.10.10.10`` и ``fd10::10`` " +"соответственно, значит DNS-сервер работает правильно." + +#: ../../source/services.rst:474 +msgid "Dynamic Store Management" +msgstr "Динамическое управление хранилищем записей" + +#: ../../source/services.rst:476 +msgid "" +"One of the powerful features of this DNS server setup is the ability to " +"dynamically manage the DNS store. This allows you to add or remove zones " +"and records at runtime, without needing to restart the server." +msgstr "" +"Одной из мощных функций этой реализации DNS-сервера является возможность " +"динамического управления хранилищем записей DNS. Это позволяет добавлять " +"или удалять зоны и записи во время выполнения без необходимости " +"перезапуска сервера." + +#: ../../source/services.rst:481 +msgid "" +"Managing DNS zones and records dynamically is essential for a flexible " +"DNS server setup. This guide focuses on how to manipulate DNS zones and " +"records using the ``DNSStore`` and ``DNSZone`` classes, providing " +"practical examples for each operation." +msgstr "" +"Динамическое управление DNS-зонами и записями необходимо для гибкой " +"настройки DNS-сервера. В этом руководстве основное внимание уделяется " +"управлению зонами и записями DNS с помощью классов DNSStore и DNSZone, а " +"также приведены практические примеры для каждой операции." + +#: ../../source/services.rst:488 +msgid "Adding a Zone" +msgstr "Добавление зоны" + +#: ../../source/services.rst:490 +msgid "" +"You can add a new zone to the ``DNSStore`` to manage its records. This " +"operation ensures that the zone is available for DNS queries." +msgstr "" +"Вы можете добавить новую зону в DNSStore для управления ее записями. Эта " +"операция гарантирует, что зона доступна для DNS-запросов." + +#: ../../source/services.rst:493 +msgid "" +"from aiomisc.service.dns import DNSStore, DNSZone\n" +"\n" +"# Create a DNSStore instance\n" +"dns_store = DNSStore()\n" +"\n" +"# Create a DNSZone instance\n" +"zone = DNSZone(\"example.com.\")\n" +"\n" +"# Add the zone to the store\n" +"dns_store.add_zone(zone)\n" +"\n" +"# Verify the zone is added\n" +"assert dns_store.get_zone(\"example.com.\") is zone" +msgstr "" + +#: ../../source/services.rst:511 +msgid "Removing a Zone" +msgstr "Удаление зоны" + +#: ../../source/services.rst:513 +msgid "" +"Removing a zone from the ``DNSStore`` ensures it is no longer available " +"for DNS queries." +msgstr "" +"Удаление зоны из DNSStore гарантирует, что она больше не будет доступна " +"для запросов DNS." + +#: ../../source/services.rst:515 +msgid "" +"# Add the zone to the store\n" +"dns_store.add_zone(zone)\n" +"\n" +"# Remove the zone from the store\n" +"dns_store.remove_zone(\"example.com.\")\n" +"\n" +"# Verify the zone is removed\n" +"assert dns_store.get_zone(\"example.com.\") is None" +msgstr "" + +#: ../../source/services.rst:528 +msgid "Adding a Record to a Zone" +msgstr "Добавление записей в зону" + +#: ../../source/services.rst:530 +msgid "" +"To manage DNS entries, you can add records to a specific zone. This " +"operation makes the record available for DNS resolution within that zone." +msgstr "" +"Для управления записями DNS вы можете добавлять записи в определенную " +"зону. Эта операция делает запись доступной для разрешения DNS в этой " +"зоне." + +#: ../../source/services.rst:533 +msgid "" +"from aiomisc.service.dns.records import A, RecordType\n" +"\n" +"# Create a DNSZone instance\n" +"zone = DNSZone(\"example.com.\")\n" +"\n" +"# Create an A record\n" +"record = A.create(name=\"www.example.com.\", ip=\"192.0.2.1\")\n" +"\n" +"# Add the record to the zone\n" +"zone.add_record(record)\n" +"\n" +"# Add the zone to the store\n" +"dns_store.add_zone(zone)\n" +"\n" +"# Query the store for the record\n" +"records = dns_store.query(\"www.example.com.\", RecordType.A)\n" +"\n" +"# Verify the record is added\n" +"assert record in records" +msgstr "" + +#: ../../source/services.rst:557 +msgid "Querying Records" +msgstr "Запрос DNS записей" + +#: ../../source/services.rst:559 +msgid "" +"You can query the ``DNSStore`` to retrieve records for a specific domain " +"name. This is useful to verify if a record exists or to handle DNS " +"queries." +msgstr "" +"Вы можете запросить DNSStore для получения записей для определенного " +"доменного имени. Это полезно для проверки существования записи или для " +"обработки DNS-запросов." + +#: ../../source/services.rst:562 +msgid "" +"# Query the store for a nonexistent record\n" +"records = dns_store.query(\"nonexistent.example.com.\", RecordType.A)\n" +"\n" +"# Verify no records are found\n" +"assert len(records) == 0" +msgstr "" + +#: ../../source/services.rst:572 +msgid "Handling Duplicate Zones" +msgstr "Обработка дубликатов зон" + +#: ../../source/services.rst:574 +msgid "" +"Attempting to add a zone that already exists should raise an error, " +"ensuring that each zone is unique within the ``DNSStore``." +msgstr "" +"Попытка добавить уже существующую зону должна вызвать ошибку, гарантируя " +"уникальность каждой зоны в DNSStore." + +#: ../../source/services.rst:577 +msgid "" +"# Add the zone to the store\n" +"dns_store.add_zone(zone)\n" +"\n" +"# Attempt to add the same zone again\n" +"try:\n" +" dns_store.add_zone(zone)\n" +"except ValueError as e:\n" +" print(f\"Error: {e}\")" +msgstr "" + +#: ../../source/services.rst:590 +msgid "Removing a Nonexistent Zone" +msgstr "Удаление несуществующей зоны" + +#: ../../source/services.rst:592 +msgid "" +"Removing a zone that does not exist should raise an error, indicating " +"that the operation is invalid." +msgstr "" +"Удаление несуществующей зоны должно вызвать ошибку, " +"указывающую на недопустимость операции." + +#: ../../source/services.rst:595 +msgid "" +"# Attempt to remove a nonexistent zone\n" +"try:\n" +" dns_store.remove_zone(\"nonexistent.com.\")\n" +"except ValueError as e:\n" +" print(f\"Error: {e}\")" +msgstr "" + +#: ../../source/services.rst:605 +msgid "Querying Subdomains" +msgstr "Запросы поддоменов" + +#: ../../source/services.rst:607 +msgid "" +"The ``DNSStore`` supports querying subdomains, allowing you to resolve " +"records within subdomains of an existing zone." +msgstr "" +"``DNSStore`` поддерживает запросы к субдоменам, что позволяет вам разрешать " +"записи внутри субдоменов существующей зоны." + +#: ../../source/services.rst:612 +msgid "" +"# Create a DNSZone instance\n" +"zone = DNSZone(\"example.com.\")\n" +"\n" +"# Create an A record for a subdomain\n" +"record = A.create(name=\"sub.example.com.\", ip=\"192.0.2.2\")\n" +"\n" +"# Add the record to the zone\n" +"zone.add_record(record)\n" +"\n" +"# Add the zone to the store\n" +"dns_store.add_zone(zone)\n" +"\n" +"# Query the store for the subdomain record\n" +"records = dns_store.query(\"sub.example.com.\", RecordType.A)\n" +"\n" +"# Verify the subdomain record is found\n" +"assert record in records" +msgstr "" + +#: ../../source/services.rst:634 +msgid "Retrieving a Zone" +msgstr "Получение зоны" + +#: ../../source/services.rst:636 +msgid "" +"You can retrieve a zone from the ``DNSStore`` to inspect or manipulate it" +" further." +msgstr "" +"Вы можете получить зону из ``DNSStore``, чтобы проверить ее или управлять ею." + +#: ../../source/services.rst:639 +msgid "" +"# Add the zone to the store\n" +"dns_store.add_zone(zone)\n" +"\n" +"# Retrieve the zone from the store\n" +"retrieved_zone = dns_store.get_zone(\"example.com.\")\n" +"\n" +"# Verify the zone is retrieved\n" +"assert retrieved_zone is zone" +msgstr "" + +#: ../../source/services.rst:652 +msgid "Handling Nonexistent Zones" +msgstr "Обработка несуществующих зон" + +#: ../../source/services.rst:654 +msgid "Retrieving a zone that does not exist should return ``None``." +msgstr "При получении несуществующей зоны должно возвращаться значение ``None``." + +#: ../../source/services.rst:656 +msgid "" +"# Attempt to retrieve a nonexistent zone\n" +"nonexistent_zone = dns_store.get_zone(\"nonexistent.com.\")\n" +"\n" +"# Verify no zone is retrieved\n" +"assert nonexistent_zone is None" +msgstr "" + +#: ../../source/services.rst:666 +msgid "Removing a Record from a Zone" +msgstr "Удаление записи из зоны" + +#: ../../source/services.rst:668 +msgid "" +"To remove a DNS record from a zone, you can use the ``remove_record`` " +"method. This operation ensures the record is no longer available for DNS " +"resolution." +msgstr "" +"Чтобы удалить запись DNS из зоны, вы можете использовать метод ``remove_record``" +". Эта операция гарантирует, что запись больше не будет доступна для разрешения DNS." + +#: ../../source/services.rst:673 +msgid "" +"# Create a DNSZone instance\n" +"zone = DNSZone(\"example.com.\")\n" +"\n" +"# Create an A record\n" +"record = A.create(name=\"www.example.com.\", ip=\"192.0.2.1\")\n" +"\n" +"# Add the record to the zone\n" +"zone.add_record(record)\n" +"\n" +"# Remove the record from the zone\n" +"zone.remove_record(record)\n" +"\n" +"# Add the zone to the store\n" +"dns_store.add_zone(zone)\n" +"\n" +"# Query the store for the record\n" +"records = dns_store.query(\"www.example.com.\", RecordType.A)\n" +"\n" +"# Verify the record is removed\n" +"assert len(records) == 0" +msgstr "" + +#: ../../source/services.rst:698 +msgid "Finding Zone by Prefix" +msgstr "Поиск зоны по префиксу" + +#: ../../source/services.rst:700 +msgid "" +"The ``DNSStore`` can find the appropriate zone for a given domain name, " +"which is useful for handling queries with subdomains." +msgstr "" +"``DNSStore`` может найти подходящую зону для данного доменного имени, " +"что полезно для обработки запросов с поддоменами." + +#: ../../source/services.rst:704 +msgid "" +"# Create a DNSZone instance\n" +"zone = DNSZone(\"example.com.\")\n" +"\n" +"# Add the zone to the store\n" +"dns_store.add_zone(zone)\n" +"\n" +"# Find the zone for a subdomain\n" +"zone_prefix = dns_store.get_zone_for_name(\"sub.example.com.\")\n" +"\n" +"# Verify the correct zone is found\n" +"assert zone_prefix == (\"com\", \"example\")" +msgstr "" + +#: ../../source/services.rst:721 msgid "``CronService``" msgstr "Класс ``CronService``" -#: ../../source/services.rst:395 +#: ../../source/services.rst:723 msgid "" "``CronService`` runs ``CronCallback's`` as a service and waits for " "running callbacks to complete on the stop method." @@ -533,7 +982,7 @@ msgstr "" "``CronService`` запускает ``CronCallback`` в качестве сервиса и ожидает " "завершения выполнения обратных вызовов при остановке." -#: ../../source/services.rst:398 +#: ../../source/services.rst:726 msgid "" "It's based on croniter_. You can register async coroutine method with " "``spec`` argument - cron like format:" @@ -541,24 +990,24 @@ msgstr "" "Основан на croniter_. Вы можете зарегистрировать асинхронный метод с " "аргументом ``spec`` - формат, подобный cron:" -#: ../../source/services.rst:404 +#: ../../source/services.rst:732 msgid "requires installed croniter_:" msgstr "необходимо установить библиотеку croniter_:" -#: ../../source/services.rst:406 +#: ../../source/services.rst:734 msgid "pip install croniter" msgstr "" -#: ../../source/services.rst:410 ../../source/services.rst:552 -#: ../../source/services.rst:618 ../../source/services.rst:681 +#: ../../source/services.rst:738 ../../source/services.rst:880 +#: ../../source/services.rst:946 ../../source/services.rst:1009 msgid "or using extras:" msgstr "или как дополнительную зависимость" -#: ../../source/services.rst:412 +#: ../../source/services.rst:740 msgid "pip install aiomisc[cron]" msgstr "" -#: ../../source/services.rst:417 +#: ../../source/services.rst:745 msgid "" "import aiomisc\n" "from aiomisc.service.cron import CronService\n" @@ -590,7 +1039,7 @@ msgstr "" "with entrypoint(service) as loop:\n" " loop.run_forever()" -#: ../../source/services.rst:434 +#: ../../source/services.rst:762 msgid "" "You can also inherit from ``CronService``, but remember that callback " "registration should be proceeded before start" @@ -598,7 +1047,7 @@ msgstr "" "Вы также можете наследовать от ``CronService``, но помните, что " "регистрация обратного вызова должна выполняться до запуска" -#: ../../source/services.rst:437 +#: ../../source/services.rst:765 msgid "" "import aiomisc\n" "from aiomisc.service.cron import CronService\n" @@ -619,11 +1068,11 @@ msgid "" " loop.run_forever()" msgstr "" -#: ../../source/services.rst:459 +#: ../../source/services.rst:787 msgid "Multiple services" msgstr "Несколько сервисов" -#: ../../source/services.rst:461 +#: ../../source/services.rst:789 msgid "" "Pass several service instances to the ``entrypoint`` to run all of them. " "After exiting the entrypoint service instances will be gracefully shut " @@ -634,7 +1083,7 @@ msgstr "" "корректно закрыты вызовом метода ``stop()`` или через отмену метода " "``start()``." -#: ../../source/services.rst:464 +#: ../../source/services.rst:792 msgid "" "import asyncio\n" "from aiomisc import entrypoint\n" @@ -669,11 +1118,11 @@ msgid "" " loop.run_forever()" msgstr "" -#: ../../source/services.rst:500 +#: ../../source/services.rst:828 msgid "Configuration" msgstr "Конфигурация" -#: ../../source/services.rst:502 +#: ../../source/services.rst:830 msgid "" "``Service`` metaclass accepts all kwargs and will set it to ``self`` as " "attributes." @@ -681,7 +1130,7 @@ msgstr "" "Метакласс ``Service`` принимает все именованные аргументы в ``__init__`` " "и устанавливает из как атрибуты в ``self``." -#: ../../source/services.rst:505 +#: ../../source/services.rst:833 msgid "" "import asyncio\n" "from aiomisc import entrypoint\n" @@ -745,27 +1194,27 @@ msgstr "" "with entrypoint(*services) as loop:\n" " loop.run_forever()" -#: ../../source/services.rst:542 +#: ../../source/services.rst:870 msgid "aiohttp service" msgstr "aiohttp сервис" -#: ../../source/services.rst:546 +#: ../../source/services.rst:874 msgid "requires installed aiohttp:" msgstr "требуется установленная библиотека aiohttp" -#: ../../source/services.rst:548 +#: ../../source/services.rst:876 msgid "pip install aiohttp" msgstr "" -#: ../../source/services.rst:554 +#: ../../source/services.rst:882 msgid "pip install aiomisc[aiohttp]" msgstr "" -#: ../../source/services.rst:559 +#: ../../source/services.rst:887 msgid "aiohttp application can be started as a service:" msgstr "Приложение aiohttp может быть запущено как сервис:" -#: ../../source/services.rst:561 +#: ../../source/services.rst:889 msgid "" "import aiohttp.web\n" "import argparse\n" @@ -805,7 +1254,7 @@ msgid "" " loop.run_forever()" msgstr "" -#: ../../source/services.rst:601 +#: ../../source/services.rst:929 msgid "" "Class ``AIOHTTPSSLService`` is similar to ``AIOHTTPService`` but creates " "an HTTPS server. You must pass SSL-required options (see ``TLSServer`` " @@ -815,27 +1264,27 @@ msgstr "" " сервер. Вы должны передать требуемые для SSL параметры (см. Класс " "TLSServer)." -#: ../../source/services.rst:608 +#: ../../source/services.rst:936 msgid "asgi service" msgstr "asgi сервис" -#: ../../source/services.rst:612 +#: ../../source/services.rst:940 msgid "requires installed aiohttp-asgi:" msgstr "требуется установленная библиотека aiohttp-asgi:" -#: ../../source/services.rst:614 +#: ../../source/services.rst:942 msgid "pip install aiohttp-asgi" msgstr "" -#: ../../source/services.rst:620 +#: ../../source/services.rst:948 msgid "pip install aiomisc[asgi]" msgstr "" -#: ../../source/services.rst:625 +#: ../../source/services.rst:953 msgid "Any ASGI-like application can be started as a service:" msgstr "Любое ASGI совместимое приложение может быть запущено как сервис:" -#: ../../source/services.rst:627 +#: ../../source/services.rst:955 msgid "" "import argparse\n" "\n" @@ -873,7 +1322,7 @@ msgid "" " loop.run_forever()" msgstr "" -#: ../../source/services.rst:665 +#: ../../source/services.rst:993 msgid "" "Class ``ASGIHTTPSSLService`` is similar to ``ASGIHTTPService`` but " "creates HTTPS server. You must pass SSL-required options (see " @@ -883,30 +1332,32 @@ msgstr "" "HTTPS сервер. Вы должны передать требуемые для SSL параметры (см. Класс " "``TLSServer``)." -#: ../../source/services.rst:671 +#: ../../source/services.rst:999 #, fuzzy msgid "uvicorn service" msgstr "Uvicorn сервис" -#: ../../source/services.rst:675 +#: ../../source/services.rst:1003 #, fuzzy msgid "requires installed uvicorn:" msgstr "необходимо установить библиотеку uvicorn_:" -#: ../../source/services.rst:677 +#: ../../source/services.rst:1005 msgid "pip install uvicorn" msgstr "" -#: ../../source/services.rst:683 +#: ../../source/services.rst:1011 msgid "pip install aiomisc[uvicorn]" msgstr "" -#: ../../source/services.rst:688 +#: ../../source/services.rst:1016 #, fuzzy msgid "Any ASGI-like application can be started via uvicorn as a service:" -msgstr "Любое ASGI совместимое приложение может быть запущено через uvicorn как сервис:" +msgstr "" +"Любое ASGI совместимое приложение может быть запущено через uvicorn как " +"сервис:" -#: ../../source/services.rst:690 +#: ../../source/services.rst:1018 msgid "" "import argparse\n" "\n" @@ -944,12 +1395,12 @@ msgid "" " loop.run_forever()" msgstr "" -#: ../../source/services.rst:733 +#: ../../source/services.rst:1061 #, fuzzy msgid "GRPC service" msgstr "GRPC сервис" -#: ../../source/services.rst:735 +#: ../../source/services.rst:1063 msgid "" "This is an example of a GRPC service which is defined in a file and loads" " a `hello.proto` file without code generation, this example is one of the" @@ -959,11 +1410,11 @@ msgstr "" "`hello.proto` без кодогенерации, этот пример является одним из примеров " "из `grpcio`, остальные примеры будут работать как ожидается." -#: ../../source/services.rst:739 +#: ../../source/services.rst:1067 msgid "Proto definition:" msgstr "Определение proto файла" -#: ../../source/services.rst:741 +#: ../../source/services.rst:1069 msgid "" "syntax = \"proto3\";\n" "\n" @@ -1005,11 +1456,11 @@ msgstr "" " string message = 1;\n" "}" -#: ../../source/services.rst:764 +#: ../../source/services.rst:1092 msgid "Service initialization example:" msgstr "Пример инициализации сервиса:" -#: ../../source/services.rst:767 +#: ../../source/services.rst:1095 #, python-format msgid "" "import grpc\n" @@ -1048,11 +1499,19 @@ msgid "" " main()" msgstr "" -#: ../../source/services.rst:806 +#: ../../source/services.rst:1131 +msgid "To enable reflection for the service you use reflection flag:" +msgstr "" + +#: ../../source/services.rst:1133 +msgid "GRPCService(reflection=True)" +msgstr "" + +#: ../../source/services.rst:1141 msgid "Memory Tracer" msgstr "Трассировщик памяти" -#: ../../source/services.rst:808 +#: ../../source/services.rst:1143 msgid "" "Simple and useful service for logging large python objects allocated in " "memory." @@ -1060,7 +1519,7 @@ msgstr "" "Простой и полезный сервис для логирования больших объектов Python, " "размещенных в памяти." -#: ../../source/services.rst:812 +#: ../../source/services.rst:1147 msgid "" "import asyncio\n" "import os\n" @@ -1080,11 +1539,11 @@ msgid "" " loop.run_until_complete(main())" msgstr "" -#: ../../source/services.rst:832 ../../source/services.rst:881 +#: ../../source/services.rst:1167 ../../source/services.rst:1216 msgid "Output example:" msgstr "Пример вывода:" -#: ../../source/services.rst:834 +#: ../../source/services.rst:1169 msgid "" "[T:[1] Thread Pool] INFO:aiomisc.service.tracer: Top memory usage:\n" " Objects | Obj.Diff | Memory | Mem.Diff | Traceback\n" @@ -1106,11 +1565,11 @@ msgid "" " 14 | 14 | 2.4KiB | 2.4KiB | aiomisc/periodic.py:40" msgstr "" -#: ../../source/services.rst:856 +#: ../../source/services.rst:1191 msgid "Profiler" msgstr "``Profiler`` - профилировщик" -#: ../../source/services.rst:858 +#: ../../source/services.rst:1193 msgid "" "Simple service for profiling. Optional `path` argument can be provided to" " dump complete profiling data, which can be later used by, for example, " @@ -1122,7 +1581,7 @@ msgstr "" "позже могут быть использованы, например, snakeviz. Также можно изменить " "порядок с аргументом ``order`` (по умолчанию \"cumulative\")." -#: ../../source/services.rst:864 +#: ../../source/services.rst:1199 msgid "" "import asyncio\n" "import os\n" @@ -1139,7 +1598,7 @@ msgid "" " loop.run_until_complete(main())" msgstr "" -#: ../../source/services.rst:883 +#: ../../source/services.rst:1218 msgid "" "108 function calls in 1.117 seconds\n" "\n" @@ -1157,21 +1616,21 @@ msgid "" "<...>/lib/python3.7/cProfile.py:50(create_stats)" msgstr "" -#: ../../source/services.rst:900 +#: ../../source/services.rst:1235 msgid "Raven service" msgstr "Raven сервис" -#: ../../source/services.rst:902 +#: ../../source/services.rst:1237 msgid "" "Simple service for sending unhandled exceptions to the `sentry`_ service " "instance." msgstr "Простой сервис для отправки необработанных исключений в сервис `sentry`_." -#: ../../source/services.rst:907 +#: ../../source/services.rst:1242 msgid "Simple example:" msgstr "Простой пример:" -#: ../../source/services.rst:909 +#: ../../source/services.rst:1244 msgid "" "import asyncio\n" "import logging\n" @@ -1253,11 +1712,11 @@ msgstr "" "with entrypoint(raven_sender) as loop:\n" " loop.run_until_complete(main())" -#: ../../source/services.rst:949 +#: ../../source/services.rst:1284 msgid "Full configuration:" msgstr "Все опции для клиента:" -#: ../../source/services.rst:951 +#: ../../source/services.rst:1286 msgid "" "import asyncio\n" "import logging\n" @@ -1375,17 +1834,17 @@ msgstr "" "with entrypoint(raven_sender) as loop:\n" " loop.run_until_complete(main())" -#: ../../source/services.rst:1009 +#: ../../source/services.rst:1344 msgid "" "You will find the full specification of options in the `Raven " "documentation`_." msgstr "Вы можете найти полное описание параметров в `документации Raven`_." -#: ../../source/services.rst:1015 +#: ../../source/services.rst:1350 msgid "``SDWatchdogService``" msgstr "``SDWatchdogService``" -#: ../../source/services.rst:1017 +#: ../../source/services.rst:1352 msgid "" "Ready to use service just adding to your entrypoint and notifying SystemD" " service watchdog timer." @@ -1393,7 +1852,7 @@ msgstr "" "Готовый к использованию сервис, просто добавьте его в entrypoint и он " "будет отправлять уведомления сторожевому таймеру SystemD." -#: ../../source/services.rst:1020 +#: ../../source/services.rst:1355 msgid "" "This can be safely added at any time, since if the service does not " "detect systemd-related environment variables, then its initialization is " @@ -1403,11 +1862,11 @@ msgstr "" "найдет переменных окружения, которые устанавливает systemd, Сервис просто" " не запустится, однако выполнение приложения продолжится." -#: ../../source/services.rst:1023 +#: ../../source/services.rst:1358 msgid "Example of python file:" msgstr "Пример python файла:" -#: ../../source/services.rst:1025 +#: ../../source/services.rst:1360 msgid "" "import logging\n" "from time import sleep\n" @@ -1421,11 +1880,11 @@ msgid "" " pass" msgstr "" -#: ../../source/services.rst:1040 +#: ../../source/services.rst:1375 msgid "Example of systemd service file:" msgstr "Пример systemd сервис-файла:" -#: ../../source/services.rst:1042 +#: ../../source/services.rst:1377 msgid "" "[Service]\n" "# Activating the notification mechanism\n" @@ -1488,11 +1947,11 @@ msgstr "" "FinalKillSignal=SIGKILL\n" "SendSIGKILL=yes" -#: ../../source/services.rst:1077 +#: ../../source/services.rst:1412 msgid "``ProcessService``" msgstr "Класс ``ProcessService``" -#: ../../source/services.rst:1079 +#: ../../source/services.rst:1414 msgid "" "A base class for launching a function by a separate system process, and " "by termination when the parent process is stopped." @@ -1500,7 +1959,7 @@ msgstr "" "Базовый класс для запуска функции отдельным системным процессом и " "завершения при остановке родительского процесса." -#: ../../source/services.rst:1082 +#: ../../source/services.rst:1417 msgid "" "from typing import Dict, Any\n" "\n" @@ -1576,11 +2035,11 @@ msgstr "" " with aiomisc.entrypoint(*services) as loop:\n" " loop.run_forever()" -#: ../../source/services.rst:1123 +#: ../../source/services.rst:1458 msgid "``RespawningProcessService``" msgstr "Класс ``RespawningProcessService``" -#: ../../source/services.rst:1125 +#: ../../source/services.rst:1460 msgid "" "A base class for launching a function by a separate system process, and " "by termination when the parent process is stopped, It's pretty like " @@ -1592,7 +2051,7 @@ msgstr "" "`ProcessService` с одним отличием - если дочерний процесс неожиданно " "завершится, то он будет перезапущен." -#: ../../source/services.rst:1130 +#: ../../source/services.rst:1465 msgid "" "import logging\n" "from typing import Any\n" @@ -1614,3 +2073,5 @@ msgid "" " loop.run_forever()" msgstr "" +#~ msgid "Overview" +#~ msgstr "Сервисы" diff --git a/docs/source/logging.rst b/docs/source/logging.rst index c223eb27..fd9219e0 100644 --- a/docs/source/logging.rst +++ b/docs/source/logging.rst @@ -119,7 +119,7 @@ Disable to configure logging handler. Useful when you want to configure your own import logging from aiomisc.log import basic_config - # Configure rich log handler + # Configure your own log handlers basic_config( level=logging.INFO, log_format='disabled', diff --git a/docs/source/services.rst b/docs/source/services.rst index d26386de..e7134b16 100644 --- a/docs/source/services.rst +++ b/docs/source/services.rst @@ -387,6 +387,332 @@ optional ``delay`` argument - periodic execution delay in seconds (0 by default) loop.run_forever() +.. _dns_server: + +DNS Server +++++++++++ + +The DNS server described here uses the ``aiomisc`` library, which provides +utilities for asynchronous I/O operations, and the ``dnslib`` library for +handling DNS records and packets. This setup is ideal for high-performance, +non-blocking DNS query handling. + +Key Features +~~~~~~~~~~~~ + +1. **Asynchronous I/O**: Utilizes asynchronous operations to handle multiple + DNS queries concurrently, ensuring high performance and scalability. +2. **UDP and TCP Support**: Supports DNS queries over both UDP and TCP + protocols, making it versatile for various network configurations. +3. **``EDNS0`` Support**: Implements Extension Mechanisms for DNS (``EDNS0``) + to handle larger DNS messages and extended functionalities. +4. **Customizable DNS Records**: Allows easy configuration of DNS zones + and records, enabling you to define and manage your DNS entries + efficiently. + +Prerequisites +~~~~~~~~~~~~~ + +Install the required libraries using pip: + +.. code-block:: + + pip install aiomisc[dns] + + +Setting Up the Server +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + from aiomisc import entrypoint + from aiomisc.service.dns import ( + DNSStore, DNSZone, TCPDNSServer, UDPDNSServer, records, + ) + + zone = DNSZone("test.") + zone.add_record(records.A.create("test.", "10.10.10.10")) + zone.add_record(records.AAAA.create("test.", "fd10::10")) + + store = DNSStore() + store.add_zone(zone) + services = [ + UDPDNSServer( + store=store, address="::1", port=5053, + ), + TCPDNSServer( + store=store, address="::1", port=5053, + ), + ] + + if __name__ == "__main__": + with entrypoint(*services, log_level="debug") as loop: + loop.run_forever() + + +Testing the Server +~~~~~~~~~~~~~~~~~~ + +You can test the DNS server using tools like ``dig``. For example, +to query the A and AAAA records for ``test.``, +use the following commands: + +.. code-block:: + + dig @::1 -p 5053 test. A + dig @::1 -p 5053 test. AAAA + dig @::1 -p 5053 +tcp test. A + dig @::1 -p 5053 +tcp test. AAAA + + +These commands should return the IP addresses +``10.10.10.10`` and ``fd10::10`` respectively, confirming that the +DNS server is working correctly. + + +Dynamic Store Management +~~~~~~~~~~~~~~~~~~~~~~~~ + +One of the powerful features of this DNS server setup is the +ability to dynamically manage the DNS store. This allows you +to add or remove zones and records at runtime, without needing +to restart the server. + +Managing DNS zones and records dynamically is essential for a +flexible DNS server setup. This guide focuses on how to +manipulate DNS zones and records using the ``DNSStore`` and ``DNSZone`` +classes, providing practical examples for each operation. + + +Adding a Zone +~~~~~~~~~~~~~ + +You can add a new zone to the ``DNSStore`` to manage its records. +This operation ensures that the zone is available for DNS queries. + +.. code-block:: python + + from aiomisc.service.dns import DNSStore, DNSZone + + # Create a DNSStore instance + dns_store = DNSStore() + + # Create a DNSZone instance + zone = DNSZone("example.com.") + + # Add the zone to the store + dns_store.add_zone(zone) + + # Verify the zone is added + assert dns_store.get_zone("example.com.") is zone + + +Removing a Zone +~~~~~~~~~~~~~~~ + +Removing a zone from the ``DNSStore`` ensures it is no longer available for DNS queries. + +.. code-block:: python + + # Add the zone to the store + dns_store.add_zone(zone) + + # Remove the zone from the store + dns_store.remove_zone("example.com.") + + # Verify the zone is removed + assert dns_store.get_zone("example.com.") is None + + +Adding a Record to a Zone +~~~~~~~~~~~~~~~~~~~~~~~~~ + +To manage DNS entries, you can add records to a specific zone. +This operation makes the record available for DNS resolution within that zone. + +.. code-block:: python + + from aiomisc.service.dns.records import A, RecordType + + # Create a DNSZone instance + zone = DNSZone("example.com.") + + # Create an A record + record = A.create(name="www.example.com.", ip="192.0.2.1") + + # Add the record to the zone + zone.add_record(record) + + # Add the zone to the store + dns_store.add_zone(zone) + + # Query the store for the record + records = dns_store.query("www.example.com.", RecordType.A) + + # Verify the record is added + assert record in records + + +Querying Records +~~~~~~~~~~~~~~~~ + +You can query the ``DNSStore`` to retrieve records for a specific domain +name. This is useful to verify if a record exists or to handle DNS queries. + +.. code-block:: python + + # Query the store for a nonexistent record + records = dns_store.query("nonexistent.example.com.", RecordType.A) + + # Verify no records are found + assert len(records) == 0 + + +Handling Duplicate Zones +~~~~~~~~~~~~~~~~~~~~~~~~ + +Attempting to add a zone that already exists should raise an error, +ensuring that each zone is unique within the ``DNSStore``. + +.. code-block:: python + + # Add the zone to the store + dns_store.add_zone(zone) + + # Attempt to add the same zone again + try: + dns_store.add_zone(zone) + except ValueError as e: + print(f"Error: {e}") + + +Removing a Nonexistent Zone +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Removing a zone that does not exist should raise an error, +indicating that the operation is invalid. + +.. code-block:: python + + # Attempt to remove a nonexistent zone + try: + dns_store.remove_zone("nonexistent.com.") + except ValueError as e: + print(f"Error: {e}") + + +Querying Subdomains +~~~~~~~~~~~~~~~~~~~ + +The ``DNSStore`` supports querying subdomains, allowing you to resolve +records within subdomains of an existing zone. + +.. code-block:: python + + # Create a DNSZone instance + zone = DNSZone("example.com.") + + # Create an A record for a subdomain + record = A.create(name="sub.example.com.", ip="192.0.2.2") + + # Add the record to the zone + zone.add_record(record) + + # Add the zone to the store + dns_store.add_zone(zone) + + # Query the store for the subdomain record + records = dns_store.query("sub.example.com.", RecordType.A) + + # Verify the subdomain record is found + assert record in records + + +Retrieving a Zone +~~~~~~~~~~~~~~~~~ + +You can retrieve a zone from the ``DNSStore`` to +inspect or manipulate it further. + +.. code-block:: python + + # Add the zone to the store + dns_store.add_zone(zone) + + # Retrieve the zone from the store + retrieved_zone = dns_store.get_zone("example.com.") + + # Verify the zone is retrieved + assert retrieved_zone is zone + + +Handling Nonexistent Zones +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Retrieving a zone that does not exist should return ``None``. + +.. code-block:: python + + # Attempt to retrieve a nonexistent zone + nonexistent_zone = dns_store.get_zone("nonexistent.com.") + + # Verify no zone is retrieved + assert nonexistent_zone is None + + +Removing a Record from a Zone +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To remove a DNS record from a zone, you can use the +``remove_record`` method. This operation ensures the +record is no longer available for DNS resolution. + + +.. code-block:: python + + # Create a DNSZone instance + zone = DNSZone("example.com.") + + # Create an A record + record = A.create(name="www.example.com.", ip="192.0.2.1") + + # Add the record to the zone + zone.add_record(record) + + # Remove the record from the zone + zone.remove_record(record) + + # Add the zone to the store + dns_store.add_zone(zone) + + # Query the store for the record + records = dns_store.query("www.example.com.", RecordType.A) + + # Verify the record is removed + assert len(records) == 0 + + +Finding Zone by Prefix +~~~~~~~~~~~~~~~~~~~~~~ + +The ``DNSStore`` can find the appropriate zone for a given domain +name, which is useful for handling queries with subdomains. + + +.. code-block:: python + + # Create a DNSZone instance + zone = DNSZone("example.com.") + + # Add the zone to the store + dns_store.add_zone(zone) + + # Find the zone for a subdomain + zone_prefix = dns_store.get_zone_for_name("sub.example.com.") + + # Verify the correct zone is found + assert zone_prefix == ("com", "example") + .. _cron-service: ``CronService`` diff --git a/poetry.lock b/poetry.lock index 363b8440..08a65de8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,10 @@ -# 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 = "aiocarbon" version = "0.15.3" description = "Asynchronous client for carbon." -optional = true +optional = false python-versions = ">=3.5, <4" files = [ {file = "aiocarbon-0.15.3-py3-none-any.whl", hash = "sha256:1b707cf07269fb131ec0bd1aca612d8c42bee7c58babafa59384cbb985b09ba2"}, @@ -16,87 +16,87 @@ immutables = "*" [[package]] name = "aiohttp" -version = "3.9.4" +version = "3.9.5" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:76d32588ef7e4a3f3adff1956a0ba96faabbdee58f2407c122dd45aa6e34f372"}, - {file = "aiohttp-3.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:56181093c10dbc6ceb8a29dfeea1e815e1dfdc020169203d87fd8d37616f73f9"}, - {file = "aiohttp-3.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7a5b676d3c65e88b3aca41816bf72831898fcd73f0cbb2680e9d88e819d1e4d"}, - {file = "aiohttp-3.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1df528a85fb404899d4207a8d9934cfd6be626e30e5d3a5544a83dbae6d8a7e"}, - {file = "aiohttp-3.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f595db1bceabd71c82e92df212dd9525a8a2c6947d39e3c994c4f27d2fe15b11"}, - {file = "aiohttp-3.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c0b09d76e5a4caac3d27752027fbd43dc987b95f3748fad2b924a03fe8632ad"}, - {file = "aiohttp-3.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:689eb4356649ec9535b3686200b231876fb4cab4aca54e3bece71d37f50c1d13"}, - {file = "aiohttp-3.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3666cf4182efdb44d73602379a66f5fdfd5da0db5e4520f0ac0dcca644a3497"}, - {file = "aiohttp-3.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b65b0f8747b013570eea2f75726046fa54fa8e0c5db60f3b98dd5d161052004a"}, - {file = "aiohttp-3.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1885d2470955f70dfdd33a02e1749613c5a9c5ab855f6db38e0b9389453dce7"}, - {file = "aiohttp-3.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0593822dcdb9483d41f12041ff7c90d4d1033ec0e880bcfaf102919b715f47f1"}, - {file = "aiohttp-3.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:47f6eb74e1ecb5e19a78f4a4228aa24df7fbab3b62d4a625d3f41194a08bd54f"}, - {file = "aiohttp-3.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c8b04a3dbd54de6ccb7604242fe3ad67f2f3ca558f2d33fe19d4b08d90701a89"}, - {file = "aiohttp-3.9.4-cp310-cp310-win32.whl", hash = "sha256:8a78dfb198a328bfb38e4308ca8167028920fb747ddcf086ce706fbdd23b2926"}, - {file = "aiohttp-3.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:e78da6b55275987cbc89141a1d8e75f5070e577c482dd48bd9123a76a96f0bbb"}, - {file = "aiohttp-3.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c111b3c69060d2bafc446917534150fd049e7aedd6cbf21ba526a5a97b4402a5"}, - {file = "aiohttp-3.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:efbdd51872cf170093998c87ccdf3cb5993add3559341a8e5708bcb311934c94"}, - {file = "aiohttp-3.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7bfdb41dc6e85d8535b00d73947548a748e9534e8e4fddd2638109ff3fb081df"}, - {file = "aiohttp-3.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd9d334412961125e9f68d5b73c1d0ab9ea3f74a58a475e6b119f5293eee7ba"}, - {file = "aiohttp-3.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35d78076736f4a668d57ade00c65d30a8ce28719d8a42471b2a06ccd1a2e3063"}, - {file = "aiohttp-3.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:824dff4f9f4d0f59d0fa3577932ee9a20e09edec8a2f813e1d6b9f89ced8293f"}, - {file = "aiohttp-3.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52b8b4e06fc15519019e128abedaeb56412b106ab88b3c452188ca47a25c4093"}, - {file = "aiohttp-3.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eae569fb1e7559d4f3919965617bb39f9e753967fae55ce13454bec2d1c54f09"}, - {file = "aiohttp-3.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:69b97aa5792428f321f72aeb2f118e56893371f27e0b7d05750bcad06fc42ca1"}, - {file = "aiohttp-3.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4d79aad0ad4b980663316f26d9a492e8fab2af77c69c0f33780a56843ad2f89e"}, - {file = "aiohttp-3.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:d6577140cd7db19e430661e4b2653680194ea8c22c994bc65b7a19d8ec834403"}, - {file = "aiohttp-3.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:9860d455847cd98eb67897f5957b7cd69fbcb436dd3f06099230f16a66e66f79"}, - {file = "aiohttp-3.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:69ff36d3f8f5652994e08bd22f093e11cfd0444cea310f92e01b45a4e46b624e"}, - {file = "aiohttp-3.9.4-cp311-cp311-win32.whl", hash = "sha256:e27d3b5ed2c2013bce66ad67ee57cbf614288bda8cdf426c8d8fe548316f1b5f"}, - {file = "aiohttp-3.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d6a67e26daa686a6fbdb600a9af8619c80a332556245fa8e86c747d226ab1a1e"}, - {file = "aiohttp-3.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c5ff8ff44825736a4065d8544b43b43ee4c6dd1530f3a08e6c0578a813b0aa35"}, - {file = "aiohttp-3.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d12a244627eba4e9dc52cbf924edef905ddd6cafc6513849b4876076a6f38b0e"}, - {file = "aiohttp-3.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dcad56c8d8348e7e468899d2fb3b309b9bc59d94e6db08710555f7436156097f"}, - {file = "aiohttp-3.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7e69a7fd4b5ce419238388e55abd220336bd32212c673ceabc57ccf3d05b55"}, - {file = "aiohttp-3.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4870cb049f10d7680c239b55428916d84158798eb8f353e74fa2c98980dcc0b"}, - {file = "aiohttp-3.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2feaf1b7031ede1bc0880cec4b0776fd347259a723d625357bb4b82f62687b"}, - {file = "aiohttp-3.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:939393e8c3f0a5bcd33ef7ace67680c318dc2ae406f15e381c0054dd658397de"}, - {file = "aiohttp-3.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d2334e387b2adcc944680bebcf412743f2caf4eeebd550f67249c1c3696be04"}, - {file = "aiohttp-3.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e0198ea897680e480845ec0ffc5a14e8b694e25b3f104f63676d55bf76a82f1a"}, - {file = "aiohttp-3.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e40d2cd22914d67c84824045861a5bb0fb46586b15dfe4f046c7495bf08306b2"}, - {file = "aiohttp-3.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:aba80e77c227f4234aa34a5ff2b6ff30c5d6a827a91d22ff6b999de9175d71bd"}, - {file = "aiohttp-3.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:fb68dc73bc8ac322d2e392a59a9e396c4f35cb6fdbdd749e139d1d6c985f2527"}, - {file = "aiohttp-3.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f3460a92638dce7e47062cf088d6e7663adb135e936cb117be88d5e6c48c9d53"}, - {file = "aiohttp-3.9.4-cp312-cp312-win32.whl", hash = "sha256:32dc814ddbb254f6170bca198fe307920f6c1308a5492f049f7f63554b88ef36"}, - {file = "aiohttp-3.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:63f41a909d182d2b78fe3abef557fcc14da50c7852f70ae3be60e83ff64edba5"}, - {file = "aiohttp-3.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c3770365675f6be220032f6609a8fbad994d6dcf3ef7dbcf295c7ee70884c9af"}, - {file = "aiohttp-3.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:305edae1dea368ce09bcb858cf5a63a064f3bff4767dec6fa60a0cc0e805a1d3"}, - {file = "aiohttp-3.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6f121900131d116e4a93b55ab0d12ad72573f967b100e49086e496a9b24523ea"}, - {file = "aiohttp-3.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b71e614c1ae35c3d62a293b19eface83d5e4d194e3eb2fabb10059d33e6e8cbf"}, - {file = "aiohttp-3.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:419f009fa4cfde4d16a7fc070d64f36d70a8d35a90d71aa27670bba2be4fd039"}, - {file = "aiohttp-3.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b39476ee69cfe64061fd77a73bf692c40021f8547cda617a3466530ef63f947"}, - {file = "aiohttp-3.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b33f34c9c7decdb2ab99c74be6443942b730b56d9c5ee48fb7df2c86492f293c"}, - {file = "aiohttp-3.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c78700130ce2dcebb1a8103202ae795be2fa8c9351d0dd22338fe3dac74847d9"}, - {file = "aiohttp-3.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:268ba22d917655d1259af2d5659072b7dc11b4e1dc2cb9662fdd867d75afc6a4"}, - {file = "aiohttp-3.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:17e7c051f53a0d2ebf33013a9cbf020bb4e098c4bc5bce6f7b0c962108d97eab"}, - {file = "aiohttp-3.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7be99f4abb008cb38e144f85f515598f4c2c8932bf11b65add0ff59c9c876d99"}, - {file = "aiohttp-3.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:d58a54d6ff08d2547656356eea8572b224e6f9bbc0cf55fa9966bcaac4ddfb10"}, - {file = "aiohttp-3.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7673a76772bda15d0d10d1aa881b7911d0580c980dbd16e59d7ba1422b2d83cd"}, - {file = "aiohttp-3.9.4-cp38-cp38-win32.whl", hash = "sha256:e4370dda04dc8951012f30e1ce7956a0a226ac0714a7b6c389fb2f43f22a250e"}, - {file = "aiohttp-3.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:eb30c4510a691bb87081192a394fb661860e75ca3896c01c6d186febe7c88530"}, - {file = "aiohttp-3.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:84e90494db7df3be5e056f91412f9fa9e611fbe8ce4aaef70647297f5943b276"}, - {file = "aiohttp-3.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7d4845f8501ab28ebfdbeab980a50a273b415cf69e96e4e674d43d86a464df9d"}, - {file = "aiohttp-3.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:69046cd9a2a17245c4ce3c1f1a4ff8c70c7701ef222fce3d1d8435f09042bba1"}, - {file = "aiohttp-3.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b73a06bafc8dcc508420db43b4dd5850e41e69de99009d0351c4f3007960019"}, - {file = "aiohttp-3.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:418bb0038dfafeac923823c2e63226179976c76f981a2aaad0ad5d51f2229bca"}, - {file = "aiohttp-3.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71a8f241456b6c2668374d5d28398f8e8cdae4cce568aaea54e0f39359cd928d"}, - {file = "aiohttp-3.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:935c369bf8acc2dc26f6eeb5222768aa7c62917c3554f7215f2ead7386b33748"}, - {file = "aiohttp-3.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74e4e48c8752d14ecfb36d2ebb3d76d614320570e14de0a3aa7a726ff150a03c"}, - {file = "aiohttp-3.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:916b0417aeddf2c8c61291238ce25286f391a6acb6f28005dd9ce282bd6311b6"}, - {file = "aiohttp-3.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9b6787b6d0b3518b2ee4cbeadd24a507756ee703adbac1ab6dc7c4434b8c572a"}, - {file = "aiohttp-3.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:221204dbda5ef350e8db6287937621cf75e85778b296c9c52260b522231940ed"}, - {file = "aiohttp-3.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:10afd99b8251022ddf81eaed1d90f5a988e349ee7d779eb429fb07b670751e8c"}, - {file = "aiohttp-3.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2506d9f7a9b91033201be9ffe7d89c6a54150b0578803cce5cb84a943d075bc3"}, - {file = "aiohttp-3.9.4-cp39-cp39-win32.whl", hash = "sha256:e571fdd9efd65e86c6af2f332e0e95dad259bfe6beb5d15b3c3eca3a6eb5d87b"}, - {file = "aiohttp-3.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:7d29dd5319d20aa3b7749719ac9685fbd926f71ac8c77b2477272725f882072d"}, - {file = "aiohttp-3.9.4.tar.gz", hash = "sha256:6ff71ede6d9a5a58cfb7b6fffc83ab5d4a63138276c771ac91ceaaddf5459644"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"}, + {file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"}, + {file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"}, + {file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"}, + {file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"}, + {file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"}, + {file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"}, + {file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"}, + {file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"}, + {file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"}, ] [package.dependencies] @@ -166,13 +166,13 @@ files = [ [[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.dependencies] @@ -180,13 +180,13 @@ typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} [[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] @@ -202,13 +202,13 @@ trio = ["trio (>=0.23)"] [[package]] name = "asgiref" -version = "3.7.2" +version = "3.8.1" description = "ASGI specs, helper code, and adapters" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"}, - {file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"}, + {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"}, + {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, ] [package.dependencies] @@ -276,13 +276,13 @@ pyflakes = ">=1.1.0" [[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.dependencies] @@ -314,13 +314,13 @@ lxml = ["lxml"] [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.6.2" 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.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, + {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, ] [[package]] @@ -623,6 +623,18 @@ files = [ {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] +[[package]] +name = "dnslib" +version = "0.9.24" +description = "Simple library to encode/decode DNS wire-format packets" +optional = false +python-versions = "*" +files = [ + {file = "dnslib-0.9.24-py2-none-any.whl", hash = "sha256:4f26c55603ce9f961b84404f19ff03b3ca4a051eafb2b1e141ef9b96485467c6"}, + {file = "dnslib-0.9.24-py3-none-any.whl", hash = "sha256:39327e695f871574198b76ef506d9691d762b5344e0d66f5f78fefe1df99e7fd"}, + {file = "dnslib-0.9.24.tar.gz", hash = "sha256:ef167868a30d4ce7c90b921279d7ecfb986be8ebc530f3e6050a2ecb68707c76"}, +] + [[package]] name = "docopt" version = "0.6.2" @@ -646,13 +658,13 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.2.0" +version = "1.2.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, ] [package.extras] @@ -660,48 +672,48 @@ test = ["pytest (>=6)"] [[package]] name = "fastapi" -version = "0.110.0" +version = "0.110.3" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.110.0-py3-none-any.whl", hash = "sha256:87a1f6fb632a218222c5984be540055346a8f5d8a68e8f6fb647b1dc9934de4b"}, - {file = "fastapi-0.110.0.tar.gz", hash = "sha256:266775f0dcc95af9d3ef39bad55cff525329a931d5fd51930aadd4f428bf7ff3"}, + {file = "fastapi-0.110.3-py3-none-any.whl", hash = "sha256:fd7600612f755e4050beb74001310b5a7e1796d149c2ee363124abdfa0289d32"}, + {file = "fastapi-0.110.3.tar.gz", hash = "sha256:555700b0159379e94fdbfc6bb66a0f1c43f4cf7060f25239af3d84b63a656626"}, ] [package.dependencies] pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.36.3,<0.37.0" +starlette = ">=0.37.2,<0.38.0" typing-extensions = ">=4.8.0" [package.extras] -all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] [[package]] name = "filelock" -version = "3.13.1" +version = "3.14.0" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, - {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, + {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, + {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +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)"] typing = ["typing-extensions (>=4.8)"] [[package]] name = "freezegun" -version = "1.4.0" +version = "1.5.1" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" files = [ - {file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"}, - {file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"}, + {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, + {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, ] [package.dependencies] @@ -826,151 +838,135 @@ grpcio = "*" [[package]] name = "grpcio" -version = "1.62.1" +version = "1.64.0" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, - {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, - {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, - {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, - {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, - {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, - {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, - {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, - {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, - {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, - {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, - {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, - {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, - {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, - {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, - {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, - {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, - {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, - {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, - {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, - {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, - {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, - {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, - {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, + {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, + {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, + {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, + {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, + {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, + {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, + {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, + {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, + {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, + {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, + {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, + {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, + {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, + {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, + {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, + {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, + {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, + {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, + {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, + {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, + {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.62.1)"] +protobuf = ["grpcio-tools (>=1.64.0)"] [[package]] name = "grpcio-reflection" -version = "1.62.1" +version = "1.64.0" description = "Standard Protobuf Reflection Service for gRPC" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "grpcio-reflection-1.62.1.tar.gz", hash = "sha256:abd453001991871031315ef2d82affe93080c0433fa3a007be34bf427e28a88a"}, - {file = "grpcio_reflection-1.62.1-py3-none-any.whl", hash = "sha256:3eff85f74b6b40f8e6116e8363da1efccf775b7a063d2c6fd12c190bbb9676ed"}, + {file = "grpcio_reflection-1.64.0-py3-none-any.whl", hash = "sha256:78942d0077c5a8b4779ed2cb710f68518ed565454e04c88fca70e83690e849ec"}, + {file = "grpcio_reflection-1.64.0.tar.gz", hash = "sha256:e4e4e1c29437431f3b0ce41dd124701cf11281079c60d988305419c61c3c5f90"}, ] [package.dependencies] -grpcio = ">=1.62.1" -protobuf = ">=4.21.6" +grpcio = ">=1.64.0" +protobuf = ">=5.26.1,<6.0dev" [[package]] name = "grpcio-tools" -version = "1.62.1" +version = "1.64.0" description = "Protobuf code generator for gRPC" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "grpcio-tools-1.62.1.tar.gz", hash = "sha256:a4991e5ee8a97ab791296d3bf7e8700b1445635cc1828cc98df945ca1802d7f2"}, - {file = "grpcio_tools-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:f2b404bcae7e2ef9b0b9803b2a95119eb7507e6dc80ea4a64a78be052c30cebc"}, - {file = "grpcio_tools-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:fdd987a580b4474769adfd40144486f54bcc73838d5ec5d3647a17883ea78e76"}, - {file = "grpcio_tools-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:07af1a6442e2313cff22af93c2c4dd37ae32b5239b38e0d99e2cbf93de65429f"}, - {file = "grpcio_tools-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41384c9ee18e61ef20cad2774ef71bd8854b63efce263b5177aa06fccb84df1f"}, - {file = "grpcio_tools-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c38006f7702d2ff52122e4c77a47348709374050c76216e84b30a9f06e45afa"}, - {file = "grpcio_tools-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:08fecc3c5b4e6dd3278f2b9d12837e423c7dcff551ca1e587018b4a0fc5f8019"}, - {file = "grpcio_tools-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a01e8dcd0f041f6fa6d815c54a2017d032950e310c41d514a8bc041e872c4d12"}, - {file = "grpcio_tools-1.62.1-cp310-cp310-win32.whl", hash = "sha256:dd933b8e0b3c13fe3543d58f849a6a5e0d7987688cb6801834278378c724f695"}, - {file = "grpcio_tools-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:2b04844a9382f1bde4b4174e476e654ab3976168d2469cb4b29e352f4f35a5aa"}, - {file = "grpcio_tools-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:024380536ba71a96cdf736f0954f6ad03f5da609c09edbcc2ca02fdd639e0eed"}, - {file = "grpcio_tools-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:21f14b99e0cd38ad56754cc0b62b2bf3cf75f9f7fc40647da54669e0da0726fe"}, - {file = "grpcio_tools-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:975ac5fb482c23f3608c16e06a43c8bab4d79c2e2564cdbc25cf753c6e998775"}, - {file = "grpcio_tools-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50739aaab0c8076ad5957204e71f2e0c9876e11fd8338f7f09de12c2d75163c5"}, - {file = "grpcio_tools-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:598c54318f0326cf5020aa43fc95a15e933aba4a71943d3bff2677d2d21ddfa1"}, - {file = "grpcio_tools-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f309bdb33a61f8e049480d41498ee2e525cfb5e959958b326abfdf552bf9b9cb"}, - {file = "grpcio_tools-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f358effd3c11d66c150e0227f983d54a5cd30e14038566dadcf25f9f6844e6e8"}, - {file = "grpcio_tools-1.62.1-cp311-cp311-win32.whl", hash = "sha256:b76aead9b73f1650a091870fe4e9ed15ac4d8ed136f962042367255199c23594"}, - {file = "grpcio_tools-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:d66a5d47eaa427039752fa0a83a425ff2a487b6a0ac30556fd3be2f3a27a0130"}, - {file = "grpcio_tools-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:575535d039b97d63e6a9abee626d6c7cd47bd8cb73dd00a5c84a98254a2164a4"}, - {file = "grpcio_tools-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:22644c90e43d1a888477899af917979e17364fdd6e9bbb92679cd6a54c4d36c3"}, - {file = "grpcio_tools-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:156d3e1b227c16e903003a56881dbe60e40f2b4bd66f0bc3b27c53e466e6384d"}, - {file = "grpcio_tools-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ad7c5691625a85327e5b683443baf73ae790fd5afc938252041ed5cd665e377"}, - {file = "grpcio_tools-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e140bbc08eea8abf51c0274f45fb1e8350220e64758998d7f3c7f985a0b2496"}, - {file = "grpcio_tools-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7444fcab861911525470d398e5638b70d5cbea3b4674a3de92b5c58c5c515d4d"}, - {file = "grpcio_tools-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e643cd14a5d1e59865cba68a5a6f0175d987f36c5f4cb0db80dee9ed60b4c174"}, - {file = "grpcio_tools-1.62.1-cp312-cp312-win32.whl", hash = "sha256:1344a773d2caa9bb7fbea7e879b84f33740c808c34a5bd2a2768e526117a6b44"}, - {file = "grpcio_tools-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:2eea1db3748b2f37b4dce84d8e0c15d9bc811094807cabafe7b0ea47f424dfd5"}, - {file = "grpcio_tools-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:45d2e6cf04d27286b6f73e6e20ba3f0a1f6d8f5535e5dcb1356200419bb457f4"}, - {file = "grpcio_tools-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:46ae58e6926773e7315e9005f0f17aacedbc0895a8752bec087d24efa2f1fb21"}, - {file = "grpcio_tools-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:4c28086df31478023a36f45e50767872ab3aed2419afff09814cb61c88b77db4"}, - {file = "grpcio_tools-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4fba5b339f4797548591036c9481e6895bf920fab7d3dc664d2697f8fb7c0bf"}, - {file = "grpcio_tools-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23eb3d47f78f509fcd201749b1f1e44b76f447913f7fbb3b8bae20f109086295"}, - {file = "grpcio_tools-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fd5d47707bd6bc2b707ece765c362d2a1d2e8f6cd92b04c99fab49a929f3610c"}, - {file = "grpcio_tools-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d1924a6a943df7c73b9ef0048302327c75962b567451479710da729ead241228"}, - {file = "grpcio_tools-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:fe71ca30aabe42591e84ecb9694c0297dc699cc20c5b24d2cb267fb0fc01f947"}, - {file = "grpcio_tools-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:1819fd055c1ae672d1d725ec75eefd1f700c18acba0ed9332202be31d69c401d"}, - {file = "grpcio_tools-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:5dbe1f7481dd14b6d477b4bace96d275090bc7636b9883975a08b802c94e7b78"}, - {file = "grpcio_tools-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:771c051c5ece27ad03e4f2e33624a925f0ad636c01757ab7dbb04a37964af4ba"}, - {file = "grpcio_tools-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:98209c438b38b6f1276dbc27b1c04e346a75bfaafe72a25a548f2dc5ce71d226"}, - {file = "grpcio_tools-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2152308e5321cb90fb45aaa84d03d6dedb19735a8779aaf36c624f97b831842d"}, - {file = "grpcio_tools-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ed1f27dc2b2262c8b8d9036276619c1bb18791311c16ccbf1f31b660f2aad7cf"}, - {file = "grpcio_tools-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2744947b6c5e907af21133431809ccca535a037356864e32c122efed8cb9de1f"}, - {file = "grpcio_tools-1.62.1-cp38-cp38-win32.whl", hash = "sha256:13b20e269d14ad629ff9a2c9a2450f3dbb119d5948de63b27ffe624fa7aea85a"}, - {file = "grpcio_tools-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:999823758e9eacd0095863d06cd6d388be769f80c9abb65cdb11c4f2cfce3fea"}, - {file = "grpcio_tools-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:941f8a5c31986053e75fa466bcfa743c2bf1b513b7978cf1f4ab4e96a8219d27"}, - {file = "grpcio_tools-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:b9c02c88c77ef6057c6cbeea8922d7c2424aabf46bfc40ddf42a32765ba91061"}, - {file = "grpcio_tools-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:6abd4eb3ccb444383a40156139acc3aaa73745d395139cb6bc8e2a3429e1e627"}, - {file = "grpcio_tools-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:449503213d142f8470b331a1c2f346f8457f16c7fe20f531bc2500e271f7c14c"}, - {file = "grpcio_tools-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a11bcf609d00cfc9baed77ab308223cabc1f0b22a05774a26dd4c94c0c80f1f"}, - {file = "grpcio_tools-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:5d7bdea33354b55acf40bb4dd3ba7324d6f1ef6b4a1a4da0807591f8c7e87b9a"}, - {file = "grpcio_tools-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d03b645852d605f43003020e78fe6d573cae6ee6b944193e36b8b317e7549a20"}, - {file = "grpcio_tools-1.62.1-cp39-cp39-win32.whl", hash = "sha256:52b185dfc3bf32e70929310367dbc66185afba60492a6a75a9b1141d407e160c"}, - {file = "grpcio_tools-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:63a273b70896d3640b7a883eb4a080c3c263d91662d870a2e9c84b7bbd978e7b"}, + {file = "grpcio_tools-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:8de2c2a956cd6e9331a417e118c5b1a60a111cd93b1ea8366234081eee1871f4"}, + {file = "grpcio_tools-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:18be83c9996b29d75c634731bedd654464ca7bf0a533d5bd2fb8e5e735f3ba2d"}, + {file = "grpcio_tools-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:944d5d00b177e67be7eac80fc04ee41e9b4c2a790f0a61cd95cae5aaa59328b8"}, + {file = "grpcio_tools-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9800e2699ddc6e794abeca17a10b0ab75291eceac00ed256caf75283ee813e77"}, + {file = "grpcio_tools-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ac55d595bc0ed9ef2757e688f68641de6dc5f4696e6848cae380aa34f0ac0d9"}, + {file = "grpcio_tools-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:af5436ff5865fd80d8a6796e904ae71bc8377f6646a42a3931ae801869d790fc"}, + {file = "grpcio_tools-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6c377252957be7bd9e479ba3c23f755237b42941460128c3240831f025f8294c"}, + {file = "grpcio_tools-1.64.0-cp310-cp310-win32.whl", hash = "sha256:c2729d2ecdfc63c3852510f62b0dddb929648303fa9f394a86eb1e2121811c3b"}, + {file = "grpcio_tools-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:f96302dad3292c797914caf9bc3c44528194a30b446b502838cc92834942ef6a"}, + {file = "grpcio_tools-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:ff57dd9c87da6995039e04f76a6791ae46fec94d7531fe1403fd94a0e3d80cdd"}, + {file = "grpcio_tools-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8be1f77c8dab0412eb6cc596abac985cdf1c42608c921966cbb84d6d66f31b2c"}, + {file = "grpcio_tools-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:de5e1e7d4deaebfe16ea0c734bc933ddbdcacd04b980ab44e881c8ad3e88670a"}, + {file = "grpcio_tools-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0118866227874203a2cd27ca9cf8d62cddf703449f009b548b9fee83594d12b"}, + {file = "grpcio_tools-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2aa82c4af0bac8940e429bf6bd4c029994ab85a963ddf62c0072116588fcf75b"}, + {file = "grpcio_tools-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:691369981d2f680bbf754ebde29a4fa630795543c565919558c178671d7e2a11"}, + {file = "grpcio_tools-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:99ab2be15a8027cb3c65e957c2f8bb7e596af954f399c4b1d257b34996860b36"}, + {file = "grpcio_tools-1.64.0-cp311-cp311-win32.whl", hash = "sha256:f0e694b9f427e437bf60e1138d0ca9a7bee5b0ce1a6d70b1e1c3b99720692d4d"}, + {file = "grpcio_tools-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:087da41ee5f4cf8f0bed419d5ac59e06f07ff1109662724112bf3105fa168eb2"}, + {file = "grpcio_tools-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:2968b63e02a7e3ef2b4ad607303f52db774736017a51b03150136306817d6d9a"}, + {file = "grpcio_tools-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:489b0e18603a30ee7c54c622d4f2cbfe5a13134ca91282b4ea9c4e8153a544b3"}, + {file = "grpcio_tools-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:78d37deb5207b06d14f95f375c094631009f95b3834541aec9c866442908c7ca"}, + {file = "grpcio_tools-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fef19899d65e90f46784a0a5a8c9bd90c8d73b3449eaca231f1123b4d65d03a6"}, + {file = "grpcio_tools-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c21c53595080518feaed2ce10c6ad39b643eb1c225b162506222c64d74088e0"}, + {file = "grpcio_tools-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:51b75ca0b86b967e1fbdea52416ef79a1507959bb2a64783e5c1dd9515608bf5"}, + {file = "grpcio_tools-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fdf13921cb8cc226a8b7a101ba81a4a4626dfcd8fd817a480615f6e09b6ef43a"}, + {file = "grpcio_tools-1.64.0-cp312-cp312-win32.whl", hash = "sha256:a64ae624a0496b9f45824325fdc14fb8cbcab2c25ecb67c42cd0edd796a5b79e"}, + {file = "grpcio_tools-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:9db97ef2e3b2ec17d1d5b5471b16bfed10521fe6e00674e36f9ba35344e20153"}, + {file = "grpcio_tools-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:2a2dc978b5f6e62fea88ba39589b37d71d09e6c43bb7c05ac1be256020187406"}, + {file = "grpcio_tools-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:36842cc0db9026572d970a615e7b1e55fce89eda733fa64df4727ff7910a0a4b"}, + {file = "grpcio_tools-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:5816b722a0ce9eebe9228264dbd8c9e0a4552ff92256590c73a97c1ab20e48a8"}, + {file = "grpcio_tools-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32a76a524f4d20eb8aadd9c57a542322c0d21b508c304bbd92b88d105b449c0a"}, + {file = "grpcio_tools-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09168716a11dab29de2950c08898fd5946a0f6479dfe95e751459685b8d9ff8f"}, + {file = "grpcio_tools-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:711d268f03d4d6cb2f90b55b8965c205ce4a5405fcf10a60aadbebd8b4c6a7e7"}, + {file = "grpcio_tools-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d86cdd273a8d29a856b1a1da27e3bd7cea524072dbe364b03bc16e12cb0f85cd"}, + {file = "grpcio_tools-1.64.0-cp38-cp38-win32.whl", hash = "sha256:32a9e2a7613bf4361c1876161d76a5d6b12e2ac10ebc2b43253f5d7483c3ed88"}, + {file = "grpcio_tools-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:9a3db8b94b094ae3b5bf30b055f0f29570a57221602d31dec6c19b4a2933c0c2"}, + {file = "grpcio_tools-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:89f5d7985d483f48406c10d1658e587d50c4188c3c1aa2bc882d06c0592f6186"}, + {file = "grpcio_tools-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f6cae2a6f1270c52c9bce9d44b390404ce02b64e72d480b9c09bcc9468e13ce6"}, + {file = "grpcio_tools-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:e0375536d220edc897e39a7d0ca5859dba586d27170614bb8f5edae6a6f3276e"}, + {file = "grpcio_tools-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f89f8a5b8b49b724ff371676c56e658a3ee2a9bbbb5c45ac181366c6834bcafc"}, + {file = "grpcio_tools-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c32e77fcff8861a1d781c561e939cc7171efff73804e817c76bc513bb1073ab"}, + {file = "grpcio_tools-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0ac6f77ceddf3c93ab5ecbbd3c63ba6b8bb4aa6ad059fa64d14bcaa618d3add6"}, + {file = "grpcio_tools-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:852e1bdb30ce4ff47eb53d8bcdf9f72654c11d7569006afb86b8b0ad47e93cf5"}, + {file = "grpcio_tools-1.64.0-cp39-cp39-win32.whl", hash = "sha256:335fa345eca68274800d53d055d70a87c2dc0c735a19dba0d3ae443b6801cbcc"}, + {file = "grpcio_tools-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:2a23f739518c091b6a821f118b4f3f006cfd52d567e76eccd3b14654cfcccde8"}, + {file = "grpcio_tools-1.64.0.tar.gz", hash = "sha256:fa4c47897a0ddb78204456d002923294724e1b7fc87f0745528727383c2260ad"}, ] [package.dependencies] -grpcio = ">=1.62.1" -protobuf = ">=4.21.6,<5.0dev" +grpcio = ">=1.64.0" +protobuf = ">=5.26.1,<6.0dev" setuptools = "*" [[package]] @@ -986,13 +982,13 @@ files = [ [[package]] name = "identify" -version = "2.5.35" +version = "2.5.36" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, - {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, + {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"}, + {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"}, ] [package.extras] @@ -1024,7 +1020,7 @@ files = [ name = "immutables" version = "0.20" description = "Immutable Collections" -optional = true +optional = false python-versions = ">=3.8.0" files = [ {file = "immutables-0.20-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dea0ae4d7f31b145c18c16badeebc2f039d09411be4a8febb86e1244cf7f1ce0"}, @@ -1075,13 +1071,13 @@ test = ["flake8 (>=5.0,<6.0)", "mypy (>=1.4,<2.0)", "pycodestyle (>=2.9,<3.0)", [[package]] name = "importlib-metadata" -version = "7.0.2" +version = "7.1.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-7.0.2-py3-none-any.whl", hash = "sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100"}, - {file = "importlib_metadata-7.0.2.tar.gz", hash = "sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792"}, + {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, + {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, ] [package.dependencies] @@ -1090,7 +1086,7 @@ zipp = ">=0.5" [package.extras] docs = ["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)", "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)"] +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)"] [[package]] name = "iniconfig" @@ -1327,38 +1323,38 @@ files = [ [[package]] name = "mypy" -version = "1.9.0" +version = "1.10.0" 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.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"}, + {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"}, + {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"}, + {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"}, + {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"}, + {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, + {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, + {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, + {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, + {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, + {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, + {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, + {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, + {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"}, + {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"}, + {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"}, + {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"}, + {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"}, + {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"}, + {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"}, + {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"}, + {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"}, + {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"}, + {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, + {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, ] [package.dependencies] @@ -1385,18 +1381,15 @@ files = [ [[package]] name = "nodeenv" -version = "1.8.0" +version = "1.9.0" 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.0-py2.py3-none-any.whl", hash = "sha256:508ecec98f9f3330b636d4448c0f1a56fc68017c68f1e7857ebc52acf0eb879a"}, + {file = "nodeenv-1.9.0.tar.gz", hash = "sha256:07f144e90dae547bf0d4ee8da0ee42664a42a04e02ed68e06324348dafe4bdb1"}, ] -[package.dependencies] -setuptools = "*" - [[package]] name = "packaging" version = "24.0" @@ -1410,28 +1403,29 @@ files = [ [[package]] name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +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.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] [[package]] name = "pluggy" -version = "1.4.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -1458,22 +1452,22 @@ virtualenv = ">=20.10.0" [[package]] name = "protobuf" -version = "4.25.3" +version = "5.27.0" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, - {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, - {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, - {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, - {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, - {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, - {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, - {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, - {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, - {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, - {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, + {file = "protobuf-5.27.0-cp310-abi3-win32.whl", hash = "sha256:2f83bf341d925650d550b8932b71763321d782529ac0eaf278f5242f513cc04e"}, + {file = "protobuf-5.27.0-cp310-abi3-win_amd64.whl", hash = "sha256:b276e3f477ea1eebff3c2e1515136cfcff5ac14519c45f9b4aa2f6a87ea627c4"}, + {file = "protobuf-5.27.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:744489f77c29174328d32f8921566fb0f7080a2f064c5137b9d6f4b790f9e0c1"}, + {file = "protobuf-5.27.0-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:f51f33d305e18646f03acfdb343aac15b8115235af98bc9f844bf9446573827b"}, + {file = "protobuf-5.27.0-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:56937f97ae0dcf4e220ff2abb1456c51a334144c9960b23597f044ce99c29c89"}, + {file = "protobuf-5.27.0-cp38-cp38-win32.whl", hash = "sha256:a17f4d664ea868102feaa30a674542255f9f4bf835d943d588440d1f49a3ed15"}, + {file = "protobuf-5.27.0-cp38-cp38-win_amd64.whl", hash = "sha256:aabbbcf794fbb4c692ff14ce06780a66d04758435717107c387f12fb477bf0d8"}, + {file = "protobuf-5.27.0-cp39-cp39-win32.whl", hash = "sha256:587be23f1212da7a14a6c65fd61995f8ef35779d4aea9e36aad81f5f3b80aec5"}, + {file = "protobuf-5.27.0-cp39-cp39-win_amd64.whl", hash = "sha256:7cb65fc8fba680b27cf7a07678084c6e68ee13cab7cace734954c25a43da6d0f"}, + {file = "protobuf-5.27.0-py3-none-any.whl", hash = "sha256:673ad60f1536b394b4fa0bcd3146a4130fcad85bfe3b60eaa86d6a0ace0fa374"}, + {file = "protobuf-5.27.0.tar.gz", hash = "sha256:07f2b9a15255e3cf3f137d884af7972407b556a7a220912b252f26dc3121e6bf"}, ] [[package]] @@ -1489,18 +1483,18 @@ files = [ [[package]] name = "pydantic" -version = "2.6.3" +version = "2.7.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.6.3-py3-none-any.whl", hash = "sha256:72c6034df47f46ccdf81869fddb81aade68056003900a8724a4f160700016a2a"}, - {file = "pydantic-2.6.3.tar.gz", hash = "sha256:e07805c4c7f5c6826e33a1d4c9d47950d7eaf34868e2690f8594d2e30241f11f"}, + {file = "pydantic-2.7.2-py3-none-any.whl", hash = "sha256:834ab954175f94e6e68258537dc49402c4a5e9d0409b9f1b86b7e934a8372de7"}, + {file = "pydantic-2.7.2.tar.gz", hash = "sha256:71b2945998f9c9b7919a45bde9a50397b289937d215ae141c1d0903ba7149fd7"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.16.3" +pydantic-core = "2.18.3" typing-extensions = ">=4.6.1" [package.extras] @@ -1508,90 +1502,90 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.16.3" -description = "" +version = "2.18.3" +description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"}, - {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"}, - {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"}, - {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"}, - {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"}, - {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"}, - {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"}, - {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"}, - {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"}, - {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"}, - {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"}, - {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"}, - {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"}, - {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"}, - {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"}, - {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"}, - {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"}, - {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"}, - {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"}, - {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"}, - {file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"}, - {file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"}, - {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"}, - {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"}, - {file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"}, - {file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"}, - {file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"}, - {file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"}, - {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"}, - {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"}, - {file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"}, - {file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"}, - {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"}, + {file = "pydantic_core-2.18.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:744697428fcdec6be5670460b578161d1ffe34743a5c15656be7ea82b008197c"}, + {file = "pydantic_core-2.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b40c05ced1ba4218b14986fe6f283d22e1ae2ff4c8e28881a70fb81fbfcda7"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a9a75622357076efb6b311983ff190fbfb3c12fc3a853122b34d3d358126c"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2e253af04ceaebde8eb201eb3f3e3e7e390f2d275a88300d6a1959d710539e2"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:855ec66589c68aa367d989da5c4755bb74ee92ccad4fdb6af942c3612c067e34"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d3e42bb54e7e9d72c13ce112e02eb1b3b55681ee948d748842171201a03a98a"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6ac9ffccc9d2e69d9fba841441d4259cb668ac180e51b30d3632cd7abca2b9b"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c56eca1686539fa0c9bda992e7bd6a37583f20083c37590413381acfc5f192d6"}, + {file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:17954d784bf8abfc0ec2a633108207ebc4fa2df1a0e4c0c3ccbaa9bb01d2c426"}, + {file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:98ed737567d8f2ecd54f7c8d4f8572ca7c7921ede93a2e52939416170d357812"}, + {file = "pydantic_core-2.18.3-cp310-none-win32.whl", hash = "sha256:9f9e04afebd3ed8c15d67a564ed0a34b54e52136c6d40d14c5547b238390e779"}, + {file = "pydantic_core-2.18.3-cp310-none-win_amd64.whl", hash = "sha256:45e4ffbae34f7ae30d0047697e724e534a7ec0a82ef9994b7913a412c21462a0"}, + {file = "pydantic_core-2.18.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b9ebe8231726c49518b16b237b9fe0d7d361dd221302af511a83d4ada01183ab"}, + {file = "pydantic_core-2.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b8e20e15d18bf7dbb453be78a2d858f946f5cdf06c5072453dace00ab652e2b2"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0d9ff283cd3459fa0bf9b0256a2b6f01ac1ff9ffb034e24457b9035f75587cb"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f7ef5f0ebb77ba24c9970da18b771711edc5feaf00c10b18461e0f5f5949231"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73038d66614d2e5cde30435b5afdced2b473b4c77d4ca3a8624dd3e41a9c19be"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6afd5c867a74c4d314c557b5ea9520183fadfbd1df4c2d6e09fd0d990ce412cd"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd7df92f28d351bb9f12470f4c533cf03d1b52ec5a6e5c58c65b183055a60106"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:80aea0ffeb1049336043d07799eace1c9602519fb3192916ff525b0287b2b1e4"}, + {file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaee40f25bba38132e655ffa3d1998a6d576ba7cf81deff8bfa189fb43fd2bbe"}, + {file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9128089da8f4fe73f7a91973895ebf2502539d627891a14034e45fb9e707e26d"}, + {file = "pydantic_core-2.18.3-cp311-none-win32.whl", hash = "sha256:fec02527e1e03257aa25b1a4dcbe697b40a22f1229f5d026503e8b7ff6d2eda7"}, + {file = "pydantic_core-2.18.3-cp311-none-win_amd64.whl", hash = "sha256:58ff8631dbab6c7c982e6425da8347108449321f61fe427c52ddfadd66642af7"}, + {file = "pydantic_core-2.18.3-cp311-none-win_arm64.whl", hash = "sha256:3fc1c7f67f34c6c2ef9c213e0f2a351797cda98249d9ca56a70ce4ebcaba45f4"}, + {file = "pydantic_core-2.18.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f0928cde2ae416a2d1ebe6dee324709c6f73e93494d8c7aea92df99aab1fc40f"}, + {file = "pydantic_core-2.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bee9bb305a562f8b9271855afb6ce00223f545de3d68560b3c1649c7c5295e9"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e862823be114387257dacbfa7d78547165a85d7add33b446ca4f4fae92c7ff5c"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a36f78674cbddc165abab0df961b5f96b14461d05feec5e1f78da58808b97e7"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba905d184f62e7ddbb7a5a751d8a5c805463511c7b08d1aca4a3e8c11f2e5048"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fdd362f6a586e681ff86550b2379e532fee63c52def1c666887956748eaa326"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b214b7ee3bd3b865e963dbed0f8bc5375f49449d70e8d407b567af3222aae4"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:691018785779766127f531674fa82bb368df5b36b461622b12e176c18e119022"}, + {file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:60e4c625e6f7155d7d0dcac151edf5858102bc61bf959d04469ca6ee4e8381bd"}, + {file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4e651e47d981c1b701dcc74ab8fec5a60a5b004650416b4abbef13db23bc7be"}, + {file = "pydantic_core-2.18.3-cp312-none-win32.whl", hash = "sha256:ffecbb5edb7f5ffae13599aec33b735e9e4c7676ca1633c60f2c606beb17efc5"}, + {file = "pydantic_core-2.18.3-cp312-none-win_amd64.whl", hash = "sha256:2c8333f6e934733483c7eddffdb094c143b9463d2af7e6bd85ebcb2d4a1b82c6"}, + {file = "pydantic_core-2.18.3-cp312-none-win_arm64.whl", hash = "sha256:7a20dded653e516a4655f4c98e97ccafb13753987434fe7cf044aa25f5b7d417"}, + {file = "pydantic_core-2.18.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:eecf63195be644b0396f972c82598cd15693550f0ff236dcf7ab92e2eb6d3522"}, + {file = "pydantic_core-2.18.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c44efdd3b6125419c28821590d7ec891c9cb0dff33a7a78d9d5c8b6f66b9702"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e59fca51ffbdd1638b3856779342ed69bcecb8484c1d4b8bdb237d0eb5a45e2"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70cf099197d6b98953468461d753563b28e73cf1eade2ffe069675d2657ed1d5"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63081a49dddc6124754b32a3774331467bfc3d2bd5ff8f10df36a95602560361"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:370059b7883485c9edb9655355ff46d912f4b03b009d929220d9294c7fd9fd60"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a64faeedfd8254f05f5cf6fc755023a7e1606af3959cfc1a9285744cc711044"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19d2e725de0f90d8671f89e420d36c3dd97639b98145e42fcc0e1f6d492a46dc"}, + {file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:67bc078025d70ec5aefe6200ef094576c9d86bd36982df1301c758a9fff7d7f4"}, + {file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:adf952c3f4100e203cbaf8e0c907c835d3e28f9041474e52b651761dc248a3c0"}, + {file = "pydantic_core-2.18.3-cp38-none-win32.whl", hash = "sha256:9a46795b1f3beb167eaee91736d5d17ac3a994bf2215a996aed825a45f897558"}, + {file = "pydantic_core-2.18.3-cp38-none-win_amd64.whl", hash = "sha256:200ad4e3133cb99ed82342a101a5abf3d924722e71cd581cc113fe828f727fbc"}, + {file = "pydantic_core-2.18.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:304378b7bf92206036c8ddd83a2ba7b7d1a5b425acafff637172a3aa72ad7083"}, + {file = "pydantic_core-2.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c826870b277143e701c9ccf34ebc33ddb4d072612683a044e7cce2d52f6c3fef"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e201935d282707394f3668380e41ccf25b5794d1b131cdd96b07f615a33ca4b1"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5560dda746c44b48bf82b3d191d74fe8efc5686a9ef18e69bdabccbbb9ad9442"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b32c2a1f8032570842257e4c19288eba9a2bba4712af542327de9a1204faff8"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:929c24e9dea3990bc8bcd27c5f2d3916c0c86f5511d2caa69e0d5290115344a9"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a8376fef60790152564b0eab376b3e23dd6e54f29d84aad46f7b264ecca943"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dccf3ef1400390ddd1fb55bf0632209d39140552d068ee5ac45553b556780e06"}, + {file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:41dbdcb0c7252b58fa931fec47937edb422c9cb22528f41cb8963665c372caf6"}, + {file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:666e45cf071669fde468886654742fa10b0e74cd0fa0430a46ba6056b24fb0af"}, + {file = "pydantic_core-2.18.3-cp39-none-win32.whl", hash = "sha256:f9c08cabff68704a1b4667d33f534d544b8a07b8e5d039c37067fceb18789e78"}, + {file = "pydantic_core-2.18.3-cp39-none-win_amd64.whl", hash = "sha256:4afa5f5973e8572b5c0dcb4e2d4fda7890e7cd63329bd5cc3263a25c92ef0026"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:77319771a026f7c7d29c6ebc623de889e9563b7087911b46fd06c044a12aa5e9"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:df11fa992e9f576473038510d66dd305bcd51d7dd508c163a8c8fe148454e059"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d531076bdfb65af593326ffd567e6ab3da145020dafb9187a1d131064a55f97c"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d33ce258e4e6e6038f2b9e8b8a631d17d017567db43483314993b3ca345dcbbb"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1f9cd7f5635b719939019be9bda47ecb56e165e51dd26c9a217a433e3d0d59a9"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cd4a032bb65cc132cae1fe3e52877daecc2097965cd3914e44fbd12b00dae7c5"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f2718430098bcdf60402136c845e4126a189959d103900ebabb6774a5d9fdb"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c0037a92cf0c580ed14e10953cdd26528e8796307bb8bb312dc65f71547df04d"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b95a0972fac2b1ff3c94629fc9081b16371dad870959f1408cc33b2f78ad347a"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a62e437d687cc148381bdd5f51e3e81f5b20a735c55f690c5be94e05da2b0d5c"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b367a73a414bbb08507da102dc2cde0fa7afe57d09b3240ce82a16d608a7679c"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ecce4b2360aa3f008da3327d652e74a0e743908eac306198b47e1c58b03dd2b"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd4435b8d83f0c9561a2a9585b1de78f1abb17cb0cef5f39bf6a4b47d19bafe3"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:616221a6d473c5b9aa83fa8982745441f6a4a62a66436be9445c65f241b86c94"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7e6382ce89a92bc1d0c0c5edd51e931432202b9080dc921d8d003e616402efd1"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ff58f379345603d940e461eae474b6bbb6dab66ed9a851ecd3cb3709bf4dcf6a"}, + {file = "pydantic_core-2.18.3.tar.gz", hash = "sha256:432e999088d85c8f36b9a3f769a8e2b57aabd817bbb729a90d1fe7f18f6f1f39"}, ] [package.dependencies] @@ -1627,17 +1621,16 @@ files = [ [[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]] @@ -1797,7 +1790,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -1849,13 +1841,13 @@ tests = ["Flask (>=0.8)", "Flask-Login (>=0.2.0)", "ZConfig", "aiohttp", "anyjso [[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] @@ -1989,19 +1981,18 @@ test = ["pytest"] [[package]] name = "setuptools" -version = "69.1.1" +version = "70.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"}, - {file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"}, + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "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)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "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"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "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] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.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-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -2120,13 +2111,13 @@ docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-ta [[package]] name = "sphinx-intl" -version = "2.1.0" +version = "2.2.0" description = "Sphinx utility that make it easy to translate and to apply translation." optional = false python-versions = ">=3.7" files = [ - {file = "sphinx-intl-2.1.0.tar.gz", hash = "sha256:9d9849ae42515b39786824e99f1e30db0404c377b01bb022690fc932b0221c02"}, - {file = "sphinx_intl-2.1.0-py3-none-any.whl", hash = "sha256:9798946b995989de691387651d70c3fc191275b587e2e519655541edfd7bbd68"}, + {file = "sphinx_intl-2.2.0-py3-none-any.whl", hash = "sha256:56ad5f360fae4aa1cb963448c802f141b55c87223bb32a7b29e936620bd1a381"}, + {file = "sphinx_intl-2.2.0.tar.gz", hash = "sha256:66976a85d31624dfcb564059a6918f90b31669269bfe3f30b2d72e81f225ab20"}, ] [package.dependencies] @@ -2136,7 +2127,7 @@ setuptools = "*" sphinx = "*" [package.extras] -test = ["mock", "pytest", "six"] +test = ["mock", "pytest"] [[package]] name = "sphinxcontrib-applehelp" @@ -2229,13 +2220,13 @@ test = ["pytest"] [[package]] name = "starlette" -version = "0.36.3" +version = "0.37.2" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.36.3-py3-none-any.whl", hash = "sha256:13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044"}, - {file = "starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080"}, + {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, + {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, ] [package.dependencies] @@ -2299,13 +2290,13 @@ files = [ [[package]] name = "types-docutils" -version = "0.20.0.20240311" +version = "0.21.0.20240423" description = "Typing stubs for docutils" optional = false python-versions = ">=3.8" files = [ - {file = "types-docutils-0.20.0.20240311.tar.gz", hash = "sha256:b29df362e9e6efcba68e27af78135902d56614f6fa76c073d29989272eedb069"}, - {file = "types_docutils-0.20.0.20240311-py3-none-any.whl", hash = "sha256:7d6ade724d89c7cbec390da4c63104f46d3f4e3eff0804998b38a274aa1e3d0c"}, + {file = "types-docutils-0.21.0.20240423.tar.gz", hash = "sha256:7716ec6c68b5179b7ba1738cace2f1326e64df9f44b7ab08d9904d32c23fc15f"}, + {file = "types_docutils-0.21.0.20240423-py3-none-any.whl", hash = "sha256:7f6e84ba8fcd2454c5b8bb8d77384d091a901929cc2b31079316e10eb346580a"}, ] [[package]] @@ -2324,13 +2315,13 @@ types-docutils = "*" [[package]] name = "typing-extensions" -version = "4.10.0" +version = "4.12.1" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, - {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, + {file = "typing_extensions-4.12.1-py3-none-any.whl", hash = "sha256:6024b58b69089e5a89c347397254e35f1bf02a907728ec7fee9bf0fe837d203a"}, + {file = "typing_extensions-4.12.1.tar.gz", hash = "sha256:915f5e35ff76f56588223f15fdd5938f9a1cf9195c0de25130c627e4d597f6d1"}, ] [[package]] @@ -2415,13 +2406,13 @@ test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)" [[package]] name = "virtualenv" -version = "20.25.1" +version = "20.26.2" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, - {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, + {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, + {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, ] [package.dependencies] @@ -2430,7 +2421,7 @@ filelock = ">=3.12.2,<4" platformdirs = ">=3.9.1,<5" [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] @@ -2588,24 +2579,25 @@ multidict = ">=4.0" [[package]] name = "zipp" -version = "3.17.0" +version = "3.19.1" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, + {file = "zipp-3.19.1-py3-none-any.whl", hash = "sha256:2828e64edb5386ea6a52e7ba7cdb17bb30a73a858f5eb6eb93d8d36f5ea26091"}, + {file = "zipp-3.19.1.tar.gz", hash = "sha256:35427f6d5594f4acf82d25541438348c26736fa9b3afa2754bcd63cdb99d8e8f"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["big-O", "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)"] [extras] aiohttp = ["aiohttp"] asgi = ["aiohttp-asgi"] carbon = ["aiocarbon"] cron = ["croniter"] +dns = ["dnslib"] grpc = ["grpcio", "grpcio-reflection", "grpcio-tools"] raven = ["aiohttp", "raven"] rich = ["rich"] @@ -2615,4 +2607,4 @@ uvloop = ["uvloop"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "26c5dd4dffcbf6c5d4299bc06790b34cc688054610e747c3108ab8d9672626a9" +content-hash = "2a60df878f74402dea1e6d8300e09bd42fc8c01566434c6148f79e1895e88f75" diff --git a/pyproject.toml b/pyproject.toml index 10b8f797..c74ad661 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,22 +72,28 @@ typing_extensions = [{ version = '*', python = "< 3.10" }] uvloop = { version = ">=0.19, <1", optional = true } uvicorn = { version = "^0.27", optional = true } asgiref = { version = "^3.7", optional = true } +dnslib = { version = "^0.9", optional = true } [tool.poetry.group.dev.dependencies] +aiocarbon = "^0.15.3" aiohttp = "^3.9" aiohttp-asgi = "~0.5.2" aiomisc-pytest = "^1.0.8" +asgiref = "^3.7" async-timeout = "^4.0.2" autodoc = "^0.5.0" autoflake = "1.4" +certifi = "^2024.6.2" collective-checkdocs = "^0.2" coveralls = "^3.3.1" croniter = "^2.0" +dnslib = "^0.9" fastapi = "^0.110" furo = "^2022" -grpcio = "^1.56" -grpcio-tools = "^1.56" -grpcio-reflection = "^1.56" +grpc-stubs = "^1.53.0.2" +grpcio = "^1.64" +grpcio-reflection = "^1.64" +grpcio-tools = "^1.64" mypy = "^1.9" pre-commit = "^2.20.0" pylama = "^8.4.1" @@ -105,9 +111,7 @@ sphinx-intl = "^2.0" timeout-decorator = "^0.5.0" types-croniter = "^1.3" types-setuptools = "^65.6.0.1" -grpc-stubs = "^1.53.0.2" uvicorn = "^0.27" -asgiref = "^3.7" [tool.poetry.group.uvloop.dependencies] uvloop = "^0.19.0" @@ -117,11 +121,12 @@ aiohttp = ["aiohttp"] asgi = ["aiohttp-asgi"] carbon = ["aiocarbon"] cron = ["croniter"] +dns = ["dnslib"] grpc = ["grpcio", "grpcio-tools", "grpcio-reflection"] raven = ["aiohttp", "raven"] rich = ["rich"] -uvloop = ["uvloop"] uvicorn = ["uvicorn", "asgiref"] +uvloop = ["uvloop"] [tool.poetry.plugins.aiomisc] systemd_watchdog = "aiomisc.service.sdwatchdog" @@ -156,6 +161,11 @@ files = [ "tests", ] +[[tool.mypy.overrides]] +module = "aiomisc.service.dns.records" +check_untyped_defs = true +disallow_subclassing_any = false + [[tool.mypy.overrides]] module = ["tests.*"] check_untyped_defs = true diff --git a/tests/test_dns.py b/tests/test_dns.py new file mode 100644 index 00000000..955c80a1 --- /dev/null +++ b/tests/test_dns.py @@ -0,0 +1,518 @@ +import dnslib # type: ignore[import-untyped] +import pytest + +from aiomisc.service.dns import DNSStore, DNSZone +from aiomisc.service.dns.records import ( + AAAA, CAA, CNAME, DNSKEY, DS, HTTPS, LOC, MX, NAPTR, NS, NSEC, PTR, RP, + RRSIG, SOA, SRV, SSHFP, TLSA, TXT, A, RecordType, +) +from aiomisc.service.dns.tree import RadixTree + + +def test_insert_and_search(): + tree: RadixTree[str] = RadixTree() + tree.insert(("com", "example"), "Zone1") + assert tree.search(("com", "example")) == "Zone1" + assert tree.search(("com", "example", "sub")) is None + + +def test_insert_multiple_and_search(): + tree: RadixTree[str] = RadixTree() + tree.insert(("com", "example"), "Zone1") + tree.insert(("com", "example", "sub"), "Zone2") + assert tree.search(("com", "example")) == "Zone1" + assert tree.search(("com", "example", "sub")) == "Zone2" + assert tree.search(("com", "example", "sub", "test")) is None + + +def test_find_prefix_basic(): + tree: RadixTree[str] = RadixTree() + tree.insert(("com", "example"), "Zone1") + assert tree.find_prefix(("com", "example")) == ( + ("com", "example"), "Zone1", + ) + assert tree.find_prefix(("com", "example", "sub")) == ( + ("com", "example"), "Zone1", + ) + + +def test_find_prefix_with_subdomain(): + tree: RadixTree[str] = RadixTree() + tree.insert(("com", "example"), "Zone1") + tree.insert(("com", "example", "sub"), "Zone2") + assert tree.find_prefix(("com", "example", "sub", "test")) == ( + ("com", "example", "sub"), "Zone2", + ) + assert tree.find_prefix(("com", "example", "other")) == ( + ("com", "example"), "Zone1", + ) + + +def test_find_prefix_no_match(): + tree: RadixTree[str] = RadixTree() + tree.insert(("com", "example"), "Zone1") + assert tree.find_prefix(("org", "example")) is None + assert tree.find_prefix(("com", "nonexistent")) is None + + +def test_insert_and_override(): + tree: RadixTree[str] = RadixTree() + tree.insert(("com", "example"), "Zone1") + tree.insert(("com", "example"), "Zone2") + assert tree.search(("com", "example")) == "Zone2" + + +def test_deep_insert_and_search(): + tree: RadixTree[str] = RadixTree() + tree.insert( + ("com", "example", "sub", "deep", "deeper"), "Zone1", + ) + assert tree.search(("com", "example", "sub", "deep", "deeper")) == "Zone1" + assert tree.search(("com", "example", "sub", "deep")) is None + + +def test_find_prefix_with_partial_matches(): + tree: RadixTree[str] = RadixTree() + tree.insert(("com", "example"), "Zone1") + tree.insert(("com", "example", "sub"), "Zone2") + tree.insert(("com", "example", "sub", "subsub"), "Zone3") + assert tree.find_prefix(("com", "example", "sub", "subsub", "deep")) == ( + ("com", "example", "sub", "subsub"), "Zone3", + ) + assert tree.find_prefix(("com", "example", "sub", "other")) == ( + ("com", "example", "sub"), "Zone2", + ) + + +def test_edge_cases_empty_key(): + tree: RadixTree[str] = RadixTree() + tree.insert((), "RootZone") + assert tree.search(()) == "RootZone" + assert tree.find_prefix(("any", "key")) == ((), "RootZone") + + +def test_edge_cases_single_character_keys(): + tree: RadixTree[str] = RadixTree() + tree.insert(("a",), "ZoneA") + tree.insert(("b",), "ZoneB") + assert tree.search(("a",)) == "ZoneA" + assert tree.search(("b",)) == "ZoneB" + assert tree.search(("c",)) is None + assert tree.find_prefix(("a", "key")) == (("a",), "ZoneA") + + +def test_edge_cases_long_keys(): + tree: RadixTree[str] = RadixTree() + long_key = tuple(str(i) for i in range(1000)) + tree.insert(long_key, "LongZone") + assert tree.search(long_key) == "LongZone" + assert tree.find_prefix( + long_key + ("extra",), + ) == (tuple(long_key), "LongZone") + + +@pytest.fixture +def dns_store(): + return DNSStore() + + +def test_add_zone(dns_store): + zone = DNSZone("example.com.") + dns_store.add_zone(zone) + assert dns_store.get_zone("example.com.") is zone + + +def test_remove_zone(dns_store): + zone = DNSZone("example.com.") + dns_store.add_zone(zone) + dns_store.remove_zone("example.com.") + assert dns_store.get_zone("example.com.") is None + + +def test_add_record_to_zone(dns_store): + zone = DNSZone("example.com.") + record = A.create(name="www.example.com.", ip="192.0.2.1") + zone.add_record(record) + dns_store.add_zone(zone) + records = dns_store.query("www.example.com.", RecordType.A) + assert record in records + + +def test_query_nonexistent_record(dns_store): + zone = DNSZone("example.com.") + record = A.create(name="www.example.com.", ip="192.0.2.1") + zone.add_record(record) + dns_store.add_zone(zone) + records = dns_store.query("nonexistent.example.com.", RecordType.A) + assert len(records) == 0 + + +def test_add_zone_with_existing_name(dns_store): + zone = DNSZone("example.com.") + dns_store.add_zone(zone) + with pytest.raises(ValueError): + dns_store.add_zone(zone) + + +def test_remove_nonexistent_zone(dns_store): + with pytest.raises(ValueError): + dns_store.remove_zone("nonexistent.com.") + + +def test_query_with_subdomain(dns_store): + zone = DNSZone("example.com.") + record = A.create(name="sub.example.com.", ip="192.0.2.2") + zone.add_record(record) + dns_store.add_zone(zone) + records = dns_store.query("sub.example.com.", RecordType.A) + assert record in records + + +def test_get_zone(dns_store): + zone = DNSZone("example.com.") + dns_store.add_zone(zone) + assert dns_store.get_zone("example.com.") is zone + + +def test_get_nonexistent_zone(dns_store): + assert dns_store.get_zone("nonexistent.com.") is None + + +def test_remove_record_from_zone(dns_store): + zone = DNSZone("example.com.") + record = A.create(name="www.example.com.", ip="192.0.2.1") + zone.add_record(record) + zone.remove_record(record) + dns_store.add_zone(zone) + records = dns_store.query("www.example.com.", RecordType.A) + assert len(records) == 0 + + +def test_find_prefix(dns_store): + zone = DNSZone("example.com.") + dns_store.add_zone(zone) + assert dns_store.get_zone_for_name("sub.example.com.") == ( + "com", "example", + ) + + +def test_a_create(): + record = A.create(name="example.com.", ip="192.0.2.1", ttl=300) + assert record.name == "example.com." + assert record.type == RecordType.A + assert isinstance(record.data, dnslib.A) + assert record.data.data == tuple(map(int, "192.0.2.1".split("."))) + assert record.ttl == 300 + + +def test_aaaa_create(): + record = AAAA.create(name="example.com.", ipv6="2001:db8::1", ttl=300) + assert record.name == "example.com." + assert record.type == RecordType.AAAA + assert isinstance(record.data, dnslib.AAAA) + assert record.data.data == ( + 32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + ) + assert record.ttl == 300 + + +def test_cname_create(): + record = CNAME.create( + name="example.com.", + label="alias.example.com.", + ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.CNAME + assert isinstance(record.data, dnslib.CNAME) + assert record.data.label == "alias.example.com." + assert record.ttl == 300 + + +def test_mx_create(): + record = MX.create( + name="example.com.", + exchange="mail.example.com.", + preference=10, + ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.MX + assert isinstance(record.data, dnslib.MX) + assert record.data.label == "mail.example.com." + assert record.data.preference == 10 + assert record.ttl == 300 + + +def test_txt_create(): + record = TXT.create(name="example.com.", text="some text", ttl=300) + assert record.name == "example.com." + assert record.type == RecordType.TXT + assert isinstance(record.data, dnslib.TXT) + assert record.data.data == [b"some text"] + assert record.ttl == 300 + + +def test_soa_create(): + record = SOA.create( + name="example.com.", + mname="ns1.example.com.", + rname="hostmaster.example.com.", + serial=1, + refresh=3600, + retry=1800, + expire=1209600, + minimum=600, + ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.SOA + assert isinstance(record.data, dnslib.SOA) + assert record.data.mname == "ns1.example.com." + assert record.data.rname == "hostmaster.example.com." + assert record.data.times == (1, 3600, 1800, 1209600, 600) + assert record.ttl == 300 + + +def test_ns_create(): + record = NS.create( + name="example.com.", label="ns1.example.com.", ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.NS + assert isinstance(record.data, dnslib.NS) + assert record.data.label == "ns1.example.com." + assert record.ttl == 300 + + +def test_ptr_create(): + record = PTR.create( + name="1.2.3.4.in-addr.arpa.", label="example.com.", ttl=300, + ) + assert record.name == "1.2.3.4.in-addr.arpa." + assert record.type == RecordType.PTR + assert isinstance(record.data, dnslib.PTR) + assert record.data.label == "example.com." + assert record.ttl == 300 + + +def test_srv_create(): + record = SRV.create( + name="example.com.", + priority=10, + weight=20, + port=80, + target="target.example.com.", + ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.SRV + assert isinstance(record.data, dnslib.SRV) + assert record.data.priority == 10 + assert record.data.weight == 20 + assert record.data.port == 80 + assert record.data.target == "target.example.com." + assert record.ttl == 300 + + +def test_caa_create(): + record = CAA.create( + name="example.com.", flags=0, + tag="issue", value="letsencrypt.org", + ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.CAA + assert isinstance(record.data, dnslib.CAA) + assert record.data.flags == 0 + assert record.data.tag == "issue" + assert record.data.value == "letsencrypt.org" + assert record.ttl == 300 + + +def test_naptr_create(): + record = NAPTR.create( + name="example.com.", order=100, + preference=10, flags="S", service="SIP+D2U", + regexp="", replacement=".", ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.NAPTR + assert isinstance(record.data, dnslib.NAPTR) + assert record.data.order == 100 + assert record.data.preference == 10 + assert record.data.flags == "S" + assert record.data.service == "SIP+D2U" + assert record.data.regexp == "" + assert record.data.replacement == "." + assert record.ttl == 300 + + +def test_ds_create(): + record = DS.create( + name="example.com.", + key_tag=12345, + algorithm=5, + digest_type=1, + digest="abc123", ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.DS + assert isinstance(record.data, dnslib.DS) + assert record.data.key_tag == 12345 + assert record.data.algorithm == 5 + assert record.data.digest_type == 1 + assert record.data.digest == b"abc123" + assert record.ttl == 300 + + +def test_dnskey_create(): + record = DNSKEY.create( + name="example.com.", + flags=256, protocol=3, + algorithm=5, + key="abcdefg", + ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.DNSKEY + assert isinstance(record.data, dnslib.DNSKEY) + assert record.data.flags == 256 + assert record.data.protocol == 3 + assert record.data.algorithm == 5 + assert record.data.key == b"abcdefg" + assert record.ttl == 300 + + +def test_rrsig_create(): + record = RRSIG.create( + name="example.com.", + type_covered=RecordType.A, + algorithm=5, + labels=2, + original_ttl=300, + expiration=1234567890, + inception=1234560000, + key_tag=12345, + signer="example.com.", + signature="abcdefg", + ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.RRSIG + assert isinstance(record.data, dnslib.RRSIG) + assert record.data.covered == RecordType.A + assert record.data.algorithm == 5 + assert record.data.labels == 2 + assert record.data.orig_ttl == 300 + assert record.data.sig_exp == 1234567890 + assert record.data.sig_inc == 1234560000 + assert record.data.key_tag == 12345 + assert record.data.name == "example.com." + assert record.data.sig == "abcdefg" + assert record.ttl == 300 + + +def test_nsec_create(): + record = NSEC.create( + name="example.com.", + next_domain="next.example.com.", + rrtypes=[RecordType.A, RecordType.AAAA], ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.NSEC + assert isinstance(record.data, dnslib.NSEC) + assert record.data.label == "next.example.com." + assert record.data.rrlist == [RecordType.A, RecordType.AAAA] + assert record.ttl == 300 + + +def test_https_create(): + record = HTTPS.create( + name="example.com.", + priority=10, + target="target.example.com.", + params=["param1", "param2"], + ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.HTTPS + assert isinstance(record.data, dnslib.HTTPS) + assert record.data.priority == 10 + assert record.data.target == "target.example.com." + assert record.data.params == ["param1", "param2"] + assert record.ttl == 300 + + +def test_loc_create(): + record = LOC.create( + name="example.com.", + latitude=12.34, + longitude=56.78, + altitude=90.0, + size=1.0, + h_precision=2.0, + v_precision=3.0, + ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.LOC + assert isinstance(record.data, dnslib.LOC) + assert record.data.lat == "12 20 24.000 N" + assert record.data.lon == "56 46 48.000 E" + assert record.data.alt == 90.0 + assert record.data.siz == 1.0 + assert record.data.hp == 2.0 + assert record.data.vp == 3.0 + assert record.ttl == 300 + + +def test_rp_create(): + record = RP.create( + name="example.com.", + mbox="hostmaster.example.com.", + txt="contact.example.com.", + ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.RP + assert isinstance(record.data, dnslib.RP) + assert record.data.mbox == "hostmaster.example.com." + assert record.data.txt == "contact.example.com." + assert record.ttl == 300 + + +def test_tlsa_create(): + record = TLSA.create( + name="example.com.", + usage=1, + selector=1, + mtype=1, + cert="abcdefg", + ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.TLSA + assert isinstance(record.data, dnslib.TLSA) + assert record.data.cert_usage == 1 + assert record.data.selector == 1 + assert record.data.matching_type == 1 + assert record.data.cert_data == b"abcdefg" + assert record.ttl == 300 + + +def test_sshfp_create(): + record = SSHFP.create( + name="example.com.", + algorithm=1, + fptype=1, + fingerprint="abcdefg", + ttl=300, + ) + assert record.name == "example.com." + assert record.type == RecordType.SSHFP + assert isinstance(record.data, dnslib.SSHFP) + assert record.data.algorithm == 1 + assert record.data.fp_type == 1 + assert record.data.fingerprint == b"abcdefg" + assert record.ttl == 300 diff --git a/tests/test_dns_server.py b/tests/test_dns_server.py new file mode 100644 index 00000000..fd8e8d9d --- /dev/null +++ b/tests/test_dns_server.py @@ -0,0 +1,223 @@ +import socket + +import dnslib # type: ignore[import-untyped] +import pytest + +from aiomisc import threaded +from aiomisc.service.dns import DNSStore, DNSZone, TCPDNSServer, UDPDNSServer +from aiomisc.service.dns.records import AAAA, CNAME, A, RecordType +from aiomisc.service.dns.service import TCP_HEADER_STRUCT + + +@pytest.fixture +def dns_store_filled(): + store = DNSStore() + zone = DNSZone("example.com.") + a_record = A.create( + name="sub.example.com.", ip="192.0.2.1", ttl=3600, + ) + aaaa_record = AAAA.create( + name="ipv6.example.com.", ipv6="2001:db8::1", ttl=3600, + ) + cname_record = CNAME.create( + name="alias.example.com.", label="example.com.", + ) + zone.add_record(a_record) + zone.add_record(aaaa_record) + zone.add_record(cname_record) + store.add_zone(zone) + return store + + +@pytest.fixture +def dns_server_port(aiomisc_unused_port_factory) -> int: + return aiomisc_unused_port_factory() + + +@pytest.fixture +def dns_server_udp( + dns_store_filled: DNSStore, dns_server_port, +) -> UDPDNSServer: + server = UDPDNSServer( + store=dns_store_filled, address="localhost", port=dns_server_port, + ) + return server + + +@pytest.fixture +def dns_server_tcp( + dns_store_filled: DNSStore, dns_server_port, +) -> TCPDNSServer: + server = TCPDNSServer( + store=dns_store_filled, address="localhost", port=dns_server_port, + ) + return server + + +@pytest.fixture +def services(dns_server_udp, dns_server_tcp): + return [dns_server_udp, dns_server_tcp] + + +@threaded +def dns_send_receive_udp(data, port): + # Send the query + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: + sock.settimeout(5) + sock.sendto(data, ("localhost", port)) + # Receive the response + response_data, _ = sock.recvfrom(65535) + return dnslib.DNSRecord.parse(response_data) + + +@threaded +def dns_send_receive_tcp(data, port): + # Send the query + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.settimeout(5) + sock.connect(("localhost", port)) + sock.sendall(data) + # Receive the response length (2 bytes) + length_data = sock.recv(2) + length = int.from_bytes(length_data, byteorder="big") + # Receive the actual response + response_data = sock.recv(length) + return dnslib.DNSRecord.parse(response_data) + + +async def test_handle_datagram_a_record(services, dns_server_port): + # Prepare a DNS query for A record + query = dnslib.DNSRecord.question("sub.example.com.", qtype="A") + query_data = query.pack() + + response = await dns_send_receive_udp(query_data, dns_server_port) + + # Verify the response + assert response.header.rcode == dnslib.RCODE.NOERROR + assert len(response.rr) == 1 + assert str(response.rr[0].rname) == "sub.example.com." + assert response.rr[0].rdata == dnslib.A("192.0.2.1") + + +async def test_handle_datagram_aaaa_record(services, dns_server_port): + # Prepare a DNS query for AAAA record + query = dnslib.DNSRecord.question( + "ipv6.example.com.", qtype="AAAA", + ) + query_data = query.pack() + + response = await dns_send_receive_udp(query_data, dns_server_port) + + # Verify the response + assert response.header.rcode == dnslib.RCODE.NOERROR + assert len(response.rr) == 1 + assert str(response.rr[0].rname) == "ipv6.example.com." + assert response.rr[0].rdata == dnslib.AAAA("2001:db8::1") + + +async def test_handle_datagram_cname_record(services, dns_server_port): + # Prepare a DNS query for CNAME record + query = dnslib.DNSRecord.question( + "alias.example.com.", qtype="CNAME", + ) + query_data = query.pack() + + response = await dns_send_receive_udp(query_data, dns_server_port) + + # Verify the response + assert response.header.rcode == dnslib.RCODE.NOERROR + assert len(response.rr) == 1 + assert str(response.rr[0].rname) == "alias.example.com." + assert response.rr[0].rdata == dnslib.CNAME("example.com.") + + +async def test_handle_datagram_nonexistent_record(services, dns_server_port): + # Prepare a DNS query for a nonexistent record + query = dnslib.DNSRecord.question( + "nonexistent.example.com.", qtype="A", + ) + query_data = query.pack() + + response = await dns_send_receive_udp(query_data, dns_server_port) + + # Verify the response + assert response.header.rcode == dnslib.RCODE.NXDOMAIN + assert len(response.rr) == 0 + + +async def test_handle_datagram_remove_record( + services, dns_store_filled, dns_server_port, +): + # Remove an existing record from the zone + zone = dns_store_filled.get_zone("example.com.") + + for record in zone.get_records("sub.example.com.", RecordType.A): + zone.remove_record(record) + + # Prepare a DNS query for the removed record + query = dnslib.DNSRecord.question("sub.example.com.", qtype="A") + query_data = query.pack() + + response = await dns_send_receive_udp(query_data, dns_server_port) + + # Verify the response + assert response.header.rcode == dnslib.RCODE.NXDOMAIN + assert len(response.rr) == 0 + + +async def test_handle_datagram_edns_record(services, dns_server_port): + # Prepare a DNS query with EDNS0 + query = dnslib.DNSRecord.question("sub.example.com.", qtype="A") + query.add_ar(dnslib.EDNS0(udp_len=4096)) + query_data = query.pack() + + response = await dns_send_receive_udp(query_data, dns_server_port) + + # Verify the response + assert response.header.rcode == dnslib.RCODE.NOERROR + assert len(response.rr) == 1 + assert str(response.rr[0].rname) == "sub.example.com." + assert response.rr[0].rdata == dnslib.A("192.0.2.1") + assert response.ar[0].rtype == dnslib.QTYPE.OPT + + +async def test_handle_large_datagram_truncated_udp( + services, dns_server_port, dns_store_filled, +): + # Add many A records to the store to exceed typical UDP packet size + zone = dns_store_filled.get_zone("example.com.") + for i in range(1, 101): + a_record = A.create(name="rr.example.com.", ip=f"192.0.2.{i}") + zone.add_record(a_record) + + # Prepare a DNS query + query = dnslib.DNSRecord.question("rr.example.com.", qtype="A") + query_data = query.pack() + + response = await dns_send_receive_udp(query_data, dns_server_port) + + # Verify the response is truncated + assert response.header.tc == 1 # Ensure the TC (truncated) bit is set + + +async def test_handle_large_tcp_request( + services, dns_server_port, dns_store_filled, +): + # Add many A records to the store to exceed typical UDP packet size + zone = dns_store_filled.get_zone("example.com.") + for i in range(1, 101): + a_record = A.create( + name="rr.example.com.", ip=f"192.0.2.{i}", ttl=3600, + ) + zone.add_record(a_record) + + # Prepare a DNS query for TCP + query = dnslib.DNSRecord.question("rr.example.com.", qtype="A") + query_data = TCP_HEADER_STRUCT.pack(len(query.pack())) + query.pack() + + response = await dns_send_receive_tcp(query_data, dns_server_port) + + # Verify the TCP response is not truncated and contains all records + assert response.header.rcode == dnslib.RCODE.NOERROR + assert len(response.rr) == 100 + assert all(str(rr.rname).startswith("rr") for rr in response.rr)