diff --git a/packages/pynumaflow-lite/Makefile b/packages/pynumaflow-lite/Makefile index 8f0f8c58..452300bf 100644 --- a/packages/pynumaflow-lite/Makefile +++ b/packages/pynumaflow-lite/Makefile @@ -43,11 +43,17 @@ test-rust: clean: cargo clean +py-fmt: + uv run black pynumaflow_lite/ tests/ manifests/ + +py-lint: py-fmt + uv run ruff check --fix . + fmt: cargo fmt --all .PHONY: lint -lint: test-fmt clippy +lint: test-fmt clippy py-lint .PHONY: test-fmt test-fmt: diff --git a/packages/pynumaflow-lite/manifests/accumulator/accumulator_stream_sorter.py b/packages/pynumaflow-lite/manifests/accumulator/accumulator_stream_sorter.py index 85d25ae0..3fc8c573 100644 --- a/packages/pynumaflow-lite/manifests/accumulator/accumulator_stream_sorter.py +++ b/packages/pynumaflow-lite/manifests/accumulator/accumulator_stream_sorter.py @@ -4,11 +4,18 @@ This accumulator buffers incoming data and sorts it by event time, flushing sorted data when the watermark advances. """ + +import signal import asyncio from datetime import datetime from typing import AsyncIterator -from pynumaflow_lite.accumulator import Datum, Message, AccumulatorAsyncServer, Accumulator +from pynumaflow_lite.accumulator import ( + Datum, + Message, + AccumulatorAsyncServer, + Accumulator, +) class StreamSorter(Accumulator): @@ -19,6 +26,7 @@ class StreamSorter(Accumulator): def __init__(self): from datetime import timezone + # Initialize with a very old timestamp (timezone-aware) self.latest_wm = datetime.fromtimestamp(-1, tz=timezone.utc) self.sorted_buffer: list[Datum] = [] @@ -33,8 +41,10 @@ async def handler(self, datums: AsyncIterator[Datum]) -> AsyncIterator[Message]: async for datum in datums: datum_count += 1 - print(f"Received datum #{datum_count}: event_time={datum.event_time}, " - f"watermark={datum.watermark}, value={datum.value}") + print( + f"Received datum #{datum_count}: event_time={datum.event_time}, " + f"watermark={datum.watermark}, value={datum.value}" + ) # If watermark has moved forward if datum.watermark and datum.watermark > self.latest_wm: @@ -122,7 +132,7 @@ async def main(): # Optional: ensure default signal handlers are in place so asyncio.run can handle them cleanly. -import signal + signal.signal(signal.SIGINT, signal.default_int_handler) try: signal.signal(signal.SIGTERM, signal.SIG_DFL) diff --git a/packages/pynumaflow-lite/manifests/batchmap/batchmap_cat.py b/packages/pynumaflow-lite/manifests/batchmap/batchmap_cat.py index cf0714cb..7c0cf088 100644 --- a/packages/pynumaflow-lite/manifests/batchmap/batchmap_cat.py +++ b/packages/pynumaflow-lite/manifests/batchmap/batchmap_cat.py @@ -8,7 +8,9 @@ class SimpleBatchCat(batchmapper.BatchMapper): - async def handler(self, batch: AsyncIterable[batchmapper.Datum]) -> batchmapper.BatchResponses: + async def handler( + self, batch: AsyncIterable[batchmapper.Datum] + ) -> batchmapper.BatchResponses: responses = batchmapper.BatchResponses() async for d in batch: resp = batchmapper.BatchResponse(d.id) @@ -29,7 +31,11 @@ async def handler(self, batch: AsyncIterable[batchmapper.Datum]) -> batchmapper. pass -async def start(f: Callable[[AsyncIterable[batchmapper.Datum]], Awaitable[batchmapper.BatchResponses]]): +async def start( + f: Callable[ + [AsyncIterable[batchmapper.Datum]], Awaitable[batchmapper.BatchResponses] + ], +): server = batchmapper.BatchMapAsyncServer() # Register loop-level signal handlers so we control shutdown and avoid asyncio.run diff --git a/packages/pynumaflow-lite/manifests/map/map_cat.py b/packages/pynumaflow-lite/manifests/map/map_cat.py index fe981d2f..ff819dbb 100644 --- a/packages/pynumaflow-lite/manifests/map/map_cat.py +++ b/packages/pynumaflow-lite/manifests/map/map_cat.py @@ -6,9 +6,7 @@ class SimpleCat(mapper.Mapper): - async def handler( - self, keys: list[str], payload: mapper.Datum - ) -> mapper.Messages: + async def handler(self, keys: list[str], payload: mapper.Datum) -> mapper.Messages: messages = mapper.Messages() diff --git a/packages/pynumaflow-lite/manifests/mapstream/mapstream_cat.py b/packages/pynumaflow-lite/manifests/mapstream/mapstream_cat.py index 093fdfaf..2db6c3f4 100644 --- a/packages/pynumaflow-lite/manifests/mapstream/mapstream_cat.py +++ b/packages/pynumaflow-lite/manifests/mapstream/mapstream_cat.py @@ -8,7 +8,9 @@ class SimpleStreamCat(mapstreamer.MapStreamer): - async def handler(self, keys: list[str], datum: mapstreamer.Datum) -> AsyncIterator[Message]: + async def handler( + self, keys: list[str], datum: mapstreamer.Datum + ) -> AsyncIterator[Message]: parts = datum.value.decode("utf-8").split(",") if not parts: yield Message.to_drop() @@ -51,4 +53,3 @@ async def start(f: Callable[[list[str], mapstreamer.Datum], AsyncIterator[Messag if __name__ == "__main__": async_handler = SimpleStreamCat() asyncio.run(start(async_handler)) - diff --git a/packages/pynumaflow-lite/manifests/reduce/reduce_counter_class.py b/packages/pynumaflow-lite/manifests/reduce/reduce_counter_class.py index 7639167d..bac265ce 100644 --- a/packages/pynumaflow-lite/manifests/reduce/reduce_counter_class.py +++ b/packages/pynumaflow-lite/manifests/reduce/reduce_counter_class.py @@ -10,7 +10,10 @@ def __init__(self, initial: int = 0) -> None: self.counter = initial async def handler( - self, keys: list[str], datums: AsyncIterable[reducer.Datum], md: reducer.Metadata + self, + keys: list[str], + datums: AsyncIterable[reducer.Datum], + md: reducer.Metadata, ) -> reducer.Messages: iw = md.interval_window self.counter = 0 @@ -57,4 +60,3 @@ async def start(creator: type[reducer.Reducer], init_args: tuple): if __name__ == "__main__": asyncio.run(start(ReduceCounter, (0,))) - diff --git a/packages/pynumaflow-lite/manifests/reducestream/reducestream_counter.py b/packages/pynumaflow-lite/manifests/reducestream/reducestream_counter.py index 3eff4ef4..c86ab921 100644 --- a/packages/pynumaflow-lite/manifests/reducestream/reducestream_counter.py +++ b/packages/pynumaflow-lite/manifests/reducestream/reducestream_counter.py @@ -7,6 +7,7 @@ The counter increments for each datum and emits a message every 10 items, plus a final message at the end. """ + import asyncio import signal from collections.abc import AsyncIterable, AsyncIterator @@ -17,12 +18,12 @@ class ReduceCounter(reducestreamer.ReduceStreamer): """ A reduce streaming counter that emits intermediate results. - + This demonstrates the key difference from regular Reducer: - Regular Reducer: waits for all data, then returns Messages - ReduceStreamer: yields Message objects incrementally as an async iterator """ - + def __init__(self, initial: int = 0) -> None: self.counter = initial @@ -34,21 +35,21 @@ async def handler( ) -> AsyncIterator[reducestreamer.Message]: """ Process datums and yield messages incrementally. - + Args: keys: List of keys for this window datums: Async iterable of incoming data md: Metadata containing window information - + Yields: Message objects to send to the next vertex """ iw = md.interval_window print(f"Handler started for keys={keys}, window=[{iw.start}, {iw.end}]") - + async for _ in datums: self.counter += 1 - + # Emit intermediate result every 10 items if self.counter % 10 == 0: msg = ( @@ -59,7 +60,7 @@ async def handler( print(f"Yielding intermediate result: counter={self.counter}") # Early release of data - this is the key feature of reduce streaming! yield reducestreamer.Message(msg, keys=keys) - + # Emit final result msg = ( f"counter:{self.counter} (FINAL) " @@ -105,4 +106,3 @@ async def start(creator: type, init_args: tuple): if __name__ == "__main__": asyncio.run(start(ReduceCounter, (0,))) - diff --git a/packages/pynumaflow-lite/manifests/session_reduce/session_reduce_counter_class.py b/packages/pynumaflow-lite/manifests/session_reduce/session_reduce_counter_class.py index d43f8c41..051d8b04 100644 --- a/packages/pynumaflow-lite/manifests/session_reduce/session_reduce_counter_class.py +++ b/packages/pynumaflow-lite/manifests/session_reduce/session_reduce_counter_class.py @@ -25,7 +25,7 @@ def __init__(self, initial: int = 0) -> None: self.counter = initial async def session_reduce( - self, keys: list[str], datums: AsyncIterable[session_reducer.Datum] + self, keys: list[str], datums: AsyncIterable[session_reducer.Datum] ) -> AsyncIterator[session_reducer.Message]: """ Count all incoming messages in this session and yield the count. diff --git a/packages/pynumaflow-lite/manifests/sideinput/sideinput_example.py b/packages/pynumaflow-lite/manifests/sideinput/sideinput_example.py index 781e997b..b1aaddec 100644 --- a/packages/pynumaflow-lite/manifests/sideinput/sideinput_example.py +++ b/packages/pynumaflow-lite/manifests/sideinput/sideinput_example.py @@ -6,6 +6,7 @@ - If MAPPER is set to "true", runs as a Mapper that reads side input files - Otherwise, runs as a SideInput retriever that broadcasts values """ + import asyncio import os import signal @@ -142,4 +143,3 @@ async def start_mapper(): else: print("Starting as SideInput retriever...") asyncio.run(start_sideinput()) - diff --git a/packages/pynumaflow-lite/manifests/sink/sink_log.py b/packages/pynumaflow-lite/manifests/sink/sink_log.py index 87df5bab..0cf3f737 100644 --- a/packages/pynumaflow-lite/manifests/sink/sink_log.py +++ b/packages/pynumaflow-lite/manifests/sink/sink_log.py @@ -1,5 +1,4 @@ import asyncio -import collections import logging import signal from collections.abc import AsyncIterable, AsyncIterator @@ -36,7 +35,9 @@ async def handler(self, datums: AsyncIterable[sinker.Datum]) -> sinker.Responses pass -async def start(f: Callable[[AsyncIterator[sinker.Datum]], Awaitable[sinker.Responses]]): +async def start( + f: Callable[[AsyncIterator[sinker.Datum]], Awaitable[sinker.Responses]], +): server = sinker.SinkAsyncServer() # Register loop-level signal handlers so we control shutdown and avoid asyncio.run @@ -61,4 +62,3 @@ async def start(f: Callable[[AsyncIterator[sinker.Datum]], Awaitable[sinker.Resp if __name__ == "__main__": async_handler = SimpleLogSink() asyncio.run(start(async_handler)) - diff --git a/packages/pynumaflow-lite/manifests/source/simple_source.py b/packages/pynumaflow-lite/manifests/source/simple_source.py index e7d501ba..06bf545b 100644 --- a/packages/pynumaflow-lite/manifests/source/simple_source.py +++ b/packages/pynumaflow-lite/manifests/source/simple_source.py @@ -22,11 +22,15 @@ def __init__(self): self.counter = 0 self.partition_idx = 0 - async def read_handler(self, datum: sourcer.ReadRequest) -> AsyncIterator[sourcer.Message]: + async def read_handler( + self, datum: sourcer.ReadRequest + ) -> AsyncIterator[sourcer.Message]: """ The simple source generates messages with incrementing numbers. """ - _LOGGER.info(f"Read request: num_records={datum.num_records}, timeout_ms={datum.timeout_ms}") + _LOGGER.info( + f"Read request: num_records={datum.num_records}, timeout_ms={datum.timeout_ms}" + ) # Generate the requested number of messages for i in range(datum.num_records): @@ -36,7 +40,7 @@ async def read_handler(self, datum: sourcer.ReadRequest) -> AsyncIterator[source # Create offset offset = sourcer.Offset( offset=str(self.counter).encode("utf-8"), - partition_id=self.partition_idx + partition_id=self.partition_idx, ) # Create message @@ -45,7 +49,7 @@ async def read_handler(self, datum: sourcer.ReadRequest) -> AsyncIterator[source offset=offset, event_time=datetime.now(timezone.utc), keys=["key1"], - headers={"source": "simple"} + headers={"source": "simple"}, ) _LOGGER.info(f"Generated message: {self.counter}") @@ -62,7 +66,9 @@ async def ack_handler(self, request: sourcer.AckRequest) -> None: """ _LOGGER.info(f"Acknowledging {len(request.offsets)} offsets") for offset in request.offsets: - _LOGGER.debug(f"Acked offset: {offset.offset.decode('utf-8')}, partition: {offset.partition_id}") + _LOGGER.debug( + f"Acked offset: {offset.offset.decode('utf-8')}, partition: {offset.partition_id}" + ) async def nack_handler(self, request: sourcer.NackRequest) -> None: """ @@ -70,7 +76,9 @@ async def nack_handler(self, request: sourcer.NackRequest) -> None: """ _LOGGER.info(f"Negatively acknowledging {len(request.offsets)} offsets") for offset in request.offsets: - _LOGGER.warning(f"Nacked offset: {offset.offset.decode('utf-8')}, partition: {offset.partition_id}") + _LOGGER.warning( + f"Nacked offset: {offset.offset.decode('utf-8')}, partition: {offset.partition_id}" + ) async def pending_handler(self) -> sourcer.PendingResponse: """ diff --git a/packages/pynumaflow-lite/manifests/sourcetransform/sourcetransform_event_filter.py b/packages/pynumaflow-lite/manifests/sourcetransform/sourcetransform_event_filter.py index f43310ec..cfe5f91b 100644 --- a/packages/pynumaflow-lite/manifests/sourcetransform/sourcetransform_event_filter.py +++ b/packages/pynumaflow-lite/manifests/sourcetransform/sourcetransform_event_filter.py @@ -13,14 +13,14 @@ class EventFilter(sourcetransformer.SourceTransformer): """ A source transformer that filters and routes messages based on event time. - + - Messages before 2022 are dropped - Messages within 2022 are tagged with "within_year_2022" - Messages after 2022 are tagged with "after_year_2022" """ - + async def handler( - self, keys: list[str], datum: sourcetransformer.Datum + self, keys: list[str], datum: sourcetransformer.Datum ) -> sourcetransformer.Messages: val = datum.value event_time = datum.event_time @@ -30,23 +30,27 @@ async def handler( print(f"Got event time: {event_time}, it is before 2022, so dropping") messages.append(sourcetransformer.Message.message_to_drop(event_time)) elif event_time < january_first_2023: - print(f"Got event time: {event_time}, it is within year 2022, so forwarding to within_year_2022") + print( + f"Got event time: {event_time}, it is within year 2022, so forwarding to within_year_2022" + ) messages.append( sourcetransformer.Message( value=val, event_time=january_first_2022, keys=keys, - tags=["within_year_2022"] + tags=["within_year_2022"], ) ) else: - print(f"Got event time: {event_time}, it is after year 2022, so forwarding to after_year_2022") + print( + f"Got event time: {event_time}, it is after year 2022, so forwarding to after_year_2022" + ) messages.append( sourcetransformer.Message( value=val, event_time=january_first_2023, keys=keys, - tags=["after_year_2022"] + tags=["after_year_2022"], ) ) @@ -61,7 +65,9 @@ async def handler( pass -async def start(f: Callable[[list[str], sourcetransformer.Datum], sourcetransformer.Messages]): +async def start( + f: Callable[[list[str], sourcetransformer.Datum], sourcetransformer.Messages], +): server = sourcetransformer.SourceTransformAsyncServer() # Register loop-level signal handlers so we control shutdown and avoid asyncio.run @@ -92,4 +98,3 @@ async def start(f: Callable[[list[str], sourcetransformer.Datum], sourcetransfor if __name__ == "__main__": async_handler = EventFilter() asyncio.run(start(async_handler)) - diff --git a/packages/pynumaflow-lite/pynumaflow_lite/__init__.py b/packages/pynumaflow-lite/pynumaflow_lite/__init__.py index 477e7b72..7b468e6d 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/__init__.py +++ b/packages/pynumaflow-lite/pynumaflow_lite/__init__.py @@ -1,4 +1,5 @@ -from .pynumaflow_lite import * +from . import pynumaflow_lite # type: ignore[attr-defined] # Rust extension, resolved at runtime +from .pynumaflow_lite import * # noqa: F403 # Rust extension; exports resolved at runtime # Ensure the `mapper`, `batchmapper`, and `mapstreamer` submodules are importable as attributes of the package # even though they're primarily registered by the extension module. @@ -139,8 +140,19 @@ pass # Public API -__all__ = ["mapper", "batchmapper", "mapstreamer", "reducer", "session_reducer", "reducestreamer", "accumulator", - "sinker", "sourcer", "sourcetransformer", "sideinputer"] +__all__ = [ + "mapper", + "batchmapper", + "mapstreamer", + "reducer", + "session_reducer", + "reducestreamer", + "accumulator", + "sinker", + "sourcer", + "sourcetransformer", + "sideinputer", +] __doc__ = pynumaflow_lite.__doc__ if hasattr(pynumaflow_lite, "__all__"): diff --git a/packages/pynumaflow-lite/pynumaflow_lite/__init__.pyi b/packages/pynumaflow-lite/pynumaflow_lite/__init__.pyi index 30d3d3f2..bfe3c0dc 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/__init__.pyi +++ b/packages/pynumaflow-lite/pynumaflow_lite/__init__.pyi @@ -1,7 +1,5 @@ import _typeshed -def __getattr__(name: str) -> _typeshed.Incomplete: ... - from . import mapper as mapper from . import batchmapper as batchmapper from . import mapstreamer as mapstreamer @@ -13,4 +11,17 @@ from . import sourcer as sourcer from . import sourcetransformer as sourcetransformer from . import sideinputer as sideinputer -__all__ = ['mapper', 'batchmapper', 'mapstreamer', 'reducer', 'session_reducer', 'accumulator', 'sinker', 'sourcer', 'sourcetransformer', 'sideinputer'] +def __getattr__(name: str) -> _typeshed.Incomplete: ... + +__all__ = [ + "mapper", + "batchmapper", + "mapstreamer", + "reducer", + "session_reducer", + "accumulator", + "sinker", + "sourcer", + "sourcetransformer", + "sideinputer", +] diff --git a/packages/pynumaflow-lite/pynumaflow_lite/_accumulator_dtypes.py b/packages/pynumaflow-lite/pynumaflow_lite/_accumulator_dtypes.py index a6250647..cdc260d4 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/_accumulator_dtypes.py +++ b/packages/pynumaflow-lite/pynumaflow_lite/_accumulator_dtypes.py @@ -31,4 +31,3 @@ async def handler(self, datums: AsyncIterator[Datum]) -> AsyncIterator[Message]: Message objects to be sent to the next vertex """ pass - diff --git a/packages/pynumaflow-lite/pynumaflow_lite/_mapstream_dtypes.py b/packages/pynumaflow-lite/pynumaflow_lite/_mapstream_dtypes.py index bc4608ec..1b18ef22 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/_mapstream_dtypes.py +++ b/packages/pynumaflow-lite/pynumaflow_lite/_mapstream_dtypes.py @@ -19,4 +19,3 @@ async def handler(self, keys: list[str], datum: Datum) -> AsyncIterator[Message] It should be an async generator yielding Message objects. """ pass - diff --git a/packages/pynumaflow-lite/pynumaflow_lite/_reduce_dtypes.py b/packages/pynumaflow-lite/pynumaflow_lite/_reduce_dtypes.py index cef2537b..b63f337d 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/_reduce_dtypes.py +++ b/packages/pynumaflow-lite/pynumaflow_lite/_reduce_dtypes.py @@ -12,9 +12,10 @@ def __call__(self, *args, **kwargs): return self.handler(*args, **kwargs) @abstractmethod - async def handler(self, keys: list[str], datums: AsyncIterable[Datum], md: Metadata) -> Messages: + async def handler( + self, keys: list[str], datums: AsyncIterable[Datum], md: Metadata + ) -> Messages: """ Implement this handler; consume `datums` async iterable and return Messages. """ pass - diff --git a/packages/pynumaflow-lite/pynumaflow_lite/_reducestreamer_dtypes.py b/packages/pynumaflow-lite/pynumaflow_lite/_reducestreamer_dtypes.py index ef84aebd..e2d12b97 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/_reducestreamer_dtypes.py +++ b/packages/pynumaflow-lite/pynumaflow_lite/_reducestreamer_dtypes.py @@ -6,7 +6,7 @@ class ReduceStreamer(metaclass=ABCMeta): """ Interface for reduce streaming handlers. A new instance will be created per window. - + Unlike regular Reducer which returns all messages at once, ReduceStreamer allows you to yield messages incrementally as an async iterator. """ @@ -16,21 +16,17 @@ def __call__(self, *args, **kwargs): @abstractmethod async def handler( - self, - keys: list[str], - datums: AsyncIterable[Datum], - md: Metadata + self, keys: list[str], datums: AsyncIterable[Datum], md: Metadata ) -> AsyncIterator[Message]: """ Implement this handler; consume `datums` async iterable and yield Messages incrementally. - + Args: keys: List of keys for this window datums: An async iterator of Datum objects md: Metadata containing window information - + Yields: Message objects to be sent to the next vertex """ pass - diff --git a/packages/pynumaflow-lite/pynumaflow_lite/_session_reduce_dtypes.py b/packages/pynumaflow-lite/pynumaflow_lite/_session_reduce_dtypes.py index 440ccc33..a96a072f 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/_session_reduce_dtypes.py +++ b/packages/pynumaflow-lite/pynumaflow_lite/_session_reduce_dtypes.py @@ -11,7 +11,7 @@ class SessionReducer(metaclass=ABCMeta): @abstractmethod async def session_reduce( - self, keys: list[str], datums: AsyncIterator[Datum] + self, keys: list[str], datums: AsyncIterator[Datum] ) -> AsyncIterator[Message]: """ Implement this handler; consume `datums` async iterable and yield Messages. diff --git a/packages/pynumaflow-lite/pynumaflow_lite/_sideinput_dtypes.py b/packages/pynumaflow-lite/pynumaflow_lite/_sideinput_dtypes.py index 4400956d..09a72ebd 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/_sideinput_dtypes.py +++ b/packages/pynumaflow-lite/pynumaflow_lite/_sideinput_dtypes.py @@ -30,4 +30,3 @@ async def retrieve_handler(self) -> Response: or Response.no_broadcast_message() to skip broadcasting. """ pass - diff --git a/packages/pynumaflow-lite/pynumaflow_lite/_sink_dtypes.py b/packages/pynumaflow-lite/pynumaflow_lite/_sink_dtypes.py index 479ad60b..a9b298a5 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/_sink_dtypes.py +++ b/packages/pynumaflow-lite/pynumaflow_lite/_sink_dtypes.py @@ -18,4 +18,3 @@ async def handler(self, datums: AsyncIterable[Datum]) -> Responses: Process the stream of datums and return responses. """ pass - diff --git a/packages/pynumaflow-lite/pynumaflow_lite/_source_dtypes.py b/packages/pynumaflow-lite/pynumaflow_lite/_source_dtypes.py index 1ab72137..00ddf00a 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/_source_dtypes.py +++ b/packages/pynumaflow-lite/pynumaflow_lite/_source_dtypes.py @@ -2,7 +2,6 @@ from collections.abc import AsyncIterator from pynumaflow_lite.sourcer import ( Message, - Offset, ReadRequest, AckRequest, NackRequest, @@ -14,13 +13,13 @@ class Sourcer(metaclass=ABCMeta): """ Provides an interface to write a User Defined Source. - + A Sourcer must implement the following handlers: - read_handler: Read messages from the source - ack_handler: Acknowledge processed messages - pending_handler: Return the number of pending messages - partitions_handler: Return the partitions this source handles - + Optionally, you can implement: - nack_handler: Negatively acknowledge messages (default: no-op) """ @@ -36,13 +35,13 @@ class instance is sent as a callable. async def read_handler(self, request: ReadRequest) -> AsyncIterator[Message]: """ Read messages from the source. - + Args: request: ReadRequest containing num_records and timeout - + Yields: Message: Messages to be sent to the next vertex - + Example: async def read_handler(self, request: ReadRequest) -> AsyncIterator[Message]: for i in range(request.num_records): @@ -60,10 +59,10 @@ async def read_handler(self, request: ReadRequest) -> AsyncIterator[Message]: async def ack_handler(self, request: AckRequest) -> None: """ Acknowledge that messages have been processed. - + Args: request: AckRequest containing the list of offsets to acknowledge - + Example: async def ack_handler(self, request: AckRequest) -> None: for offset in request.offsets: @@ -76,11 +75,11 @@ async def ack_handler(self, request: AckRequest) -> None: async def pending_handler(self) -> PendingResponse: """ Return the number of pending messages yet to be processed. - + Returns: PendingResponse: Response containing the count of pending messages. Return count=-1 if the source doesn't support detecting backlog. - + Example: async def pending_handler(self) -> PendingResponse: return PendingResponse(count=len(self.pending_offsets)) @@ -91,14 +90,14 @@ async def pending_handler(self) -> PendingResponse: async def partitions_handler(self) -> PartitionsResponse: """ Return the partitions associated with this source. - + This is used by the platform to determine the partitions to which the watermark should be published. If your source doesn't have the concept of partitions, return the replica ID. - + Returns: PartitionsResponse: Response containing the list of partition IDs - + Example: async def partitions_handler(self) -> PartitionsResponse: return PartitionsResponse(partitions=[self.partition_id]) @@ -108,13 +107,13 @@ async def partitions_handler(self) -> PartitionsResponse: async def nack_handler(self, request: NackRequest) -> None: """ Negatively acknowledge messages (optional). - + This is called when messages could not be processed and should be retried or handled differently. Default implementation is a no-op. - + Args: request: NackRequest containing the list of offsets to nack - + Example: async def nack_handler(self, request: NackRequest) -> None: for offset in request.offsets: @@ -122,4 +121,3 @@ async def nack_handler(self, request: NackRequest) -> None: self.nacked_offsets.add(offset.offset) """ pass - diff --git a/packages/pynumaflow-lite/pynumaflow_lite/_sourcetransformer_dtypes.py b/packages/pynumaflow-lite/pynumaflow_lite/_sourcetransformer_dtypes.py index 0727b3b3..971d3e67 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/_sourcetransformer_dtypes.py +++ b/packages/pynumaflow-lite/pynumaflow_lite/_sourcetransformer_dtypes.py @@ -6,7 +6,7 @@ class SourceTransformer(metaclass=ABCMeta): """ Provides an interface to write a SourceTransformer which will be exposed over a gRPC server. - + A SourceTransformer is used for transforming and assigning event time to input messages from a source. """ @@ -22,14 +22,13 @@ class instance is sent as a callable. async def handler(self, keys: list[str], datum: Datum) -> Messages: """ Implement this handler function which implements the SourceTransformer interface. - + Args: keys: The keys associated with the message. datum: The input datum containing value, event_time, watermark, and headers. - + Returns: Messages: A collection of transformed messages with potentially modified event times and tags for conditional forwarding. """ pass - diff --git a/packages/pynumaflow-lite/pynumaflow_lite/accumulator.pyi b/packages/pynumaflow-lite/pynumaflow_lite/accumulator.pyi index 32db03b6..2e818ca1 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/accumulator.pyi +++ b/packages/pynumaflow-lite/pynumaflow_lite/accumulator.pyi @@ -1,5 +1,5 @@ from datetime import datetime -from typing import AsyncIterator, Optional, Callable, Awaitable +from typing import AsyncIterator, Optional, Callable from collections.abc import AsyncIterable class Message: @@ -31,6 +31,7 @@ class Message: Drop a Message, do not forward to the next vertex. """ ... + @staticmethod def from_datum( datum: Datum, @@ -75,7 +76,11 @@ class AccumulatorAsyncServer: info_file: str | None = "/var/run/numaflow/accumulator-server-info", ) -> None: ... async def start( - self, py_creator: type[Accumulator] | Callable[[AsyncIterable[Datum]], AsyncIterator[Message]], init_args: tuple | None = None + self, + py_creator: ( + type[Accumulator] | Callable[[AsyncIterable[Datum]], AsyncIterator[Message]] + ), + init_args: tuple | None = None, ) -> None: """ Start the server with the given Python class (creator) or function. @@ -85,6 +90,7 @@ class AccumulatorAsyncServer: init_args: Optional tuple of positional arguments for class instantiation """ ... + def stop(self) -> None: """ Trigger server shutdown from Python (idempotent). @@ -116,4 +122,3 @@ class Accumulator: Message objects to be sent to the next vertex """ ... - diff --git a/packages/pynumaflow-lite/pynumaflow_lite/batchmapper.pyi b/packages/pynumaflow-lite/pynumaflow_lite/batchmapper.pyi index 56f572f2..d1e0903e 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/batchmapper.pyi +++ b/packages/pynumaflow-lite/pynumaflow_lite/batchmapper.pyi @@ -1,25 +1,22 @@ from __future__ import annotations -from typing import Optional, List, Dict, Callable, Awaitable, Any, AsyncIterator +from typing import Optional, List, Dict, Callable, Awaitable, AsyncIterator import datetime as _dt - class Message: keys: Optional[List[str]] value: bytes tags: Optional[List[str]] def __init__( - self, - value: bytes, - keys: Optional[List[str]] = ..., - tags: Optional[List[str]] = ..., + self, + value: bytes, + keys: Optional[List[str]] = ..., + tags: Optional[List[str]] = ..., ) -> None: ... - @staticmethod def message_to_drop() -> Message: ... - class Datum: keys: List[str] value: bytes @@ -29,43 +26,34 @@ class Datum: headers: Dict[str, str] def __repr__(self) -> str: ... - def __str__(self) -> str: ... - class BatchResponse: id: str def __init__(self, id: str) -> None: ... - @staticmethod def from_id(id: str) -> BatchResponse: ... - def append(self, message: Message) -> None: ... - class BatchResponses: def __init__(self) -> None: ... - def append(self, response: BatchResponse) -> None: ... - class BatchMapAsyncServer: def __init__( - self, - sock_file: str | None = ..., - info_file: str | None = ..., + self, + sock_file: str | None = ..., + info_file: str | None = ..., ) -> None: ... - - def start(self, py_func: Callable[[AsyncIterator[Datum]], Awaitable[BatchResponses]]) -> Awaitable[None]: ... - + def start( + self, py_func: Callable[[AsyncIterator[Datum]], Awaitable[BatchResponses]] + ) -> Awaitable[None]: ... def stop(self) -> None: ... - class BatchMapper: async def handler(self, batch: AsyncIterator[Datum]) -> BatchResponses: ... - __all__ = [ "Message", "Datum", diff --git a/packages/pynumaflow-lite/pynumaflow_lite/mapper.pyi b/packages/pynumaflow-lite/pynumaflow_lite/mapper.pyi index ba127e8f..865d9e09 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/mapper.pyi +++ b/packages/pynumaflow-lite/pynumaflow_lite/mapper.pyi @@ -1,17 +1,15 @@ from __future__ import annotations -from typing import Optional, List, Dict, Callable, Awaitable, Any +from typing import Optional, List, Dict, Callable, Awaitable import datetime as _dt # Re-export the Python ABC for user convenience and typing from ._map_dtypes import Mapper as Mapper - class SystemMetadata: """System-generated metadata groups per message (read-only).""" def __init__(self) -> None: ... - def groups(self) -> List[str]: """Returns the groups of the system metadata.""" ... @@ -26,12 +24,10 @@ class SystemMetadata: def __repr__(self) -> str: ... - class UserMetadata: """User-defined metadata groups per message (read-write).""" def __init__(self) -> None: ... - def groups(self) -> List[str]: """Returns the groups of the user metadata.""" ... @@ -62,17 +58,12 @@ class UserMetadata: def __repr__(self) -> str: ... - class Messages: def __init__(self) -> None: ... - def append(self, message: Message) -> None: ... - def __repr__(self) -> str: ... - def __str__(self) -> str: ... - class Message: keys: Optional[List[str]] value: bytes @@ -80,17 +71,15 @@ class Message: user_metadata: Optional[UserMetadata] def __init__( - self, - value: bytes, - keys: Optional[List[str]] = ..., - tags: Optional[List[str]] = ..., - user_metadata: Optional[UserMetadata] = ..., + self, + value: bytes, + keys: Optional[List[str]] = ..., + tags: Optional[List[str]] = ..., + user_metadata: Optional[UserMetadata] = ..., ) -> None: ... - @staticmethod def message_to_drop() -> Message: ... - class Datum: # Read-only attributes provided by the extension keys: List[str] @@ -102,25 +91,21 @@ class Datum: system_metadata: SystemMetadata def __repr__(self) -> str: ... - def __str__(self) -> str: ... - class MapAsyncServer: def __init__( - self, - sock_file: str | None = ..., - info_file: str | None = ..., + self, + sock_file: str | None = ..., + info_file: str | None = ..., ) -> None: ... - - def start(self, py_func: Callable[[list[str], Datum], Awaitable[Messages]]) -> Awaitable[None]: ... - + def start( + self, py_func: Callable[[list[str], Datum], Awaitable[Messages]] + ) -> Awaitable[None]: ... def stop(self) -> None: ... - # Simple utility function exposed by the extension - __all__ = [ "SystemMetadata", "UserMetadata", diff --git a/packages/pynumaflow-lite/pynumaflow_lite/mapstreamer.pyi b/packages/pynumaflow-lite/pynumaflow_lite/mapstreamer.pyi index 48244004..00e50c90 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/mapstreamer.pyi +++ b/packages/pynumaflow-lite/pynumaflow_lite/mapstreamer.pyi @@ -3,29 +3,22 @@ from __future__ import annotations from typing import Optional, List, Dict, Callable, Awaitable, AsyncIterator import datetime as _dt -# Re-export the Python ABC for user convenience and typing -from ._stream_dtypes import MapStreamer as MapStreamer - - class Message: keys: Optional[List[str]] value: bytes tags: Optional[List[str]] def __init__( - self, - value: bytes, - keys: Optional[List[str]] = ..., - tags: Optional[List[str]] = ..., + self, + value: bytes, + keys: Optional[List[str]] = ..., + tags: Optional[List[str]] = ..., ) -> None: ... - @staticmethod def message_to_drop() -> Message: ... - @staticmethod def to_drop() -> Message: ... - class Datum: keys: List[str] value: bytes @@ -34,25 +27,23 @@ class Datum: headers: Dict[str, str] def __repr__(self) -> str: ... - def __str__(self) -> str: ... - class MapStreamAsyncServer: def __init__( - self, - sock_file: str | None = ..., - info_file: str | None = ..., + self, + sock_file: str | None = ..., + info_file: str | None = ..., ) -> None: ... - - def start(self, py_func: Callable[[list[str], Datum], AsyncIterator[Message]]) -> Awaitable[None]: ... - + def start( + self, py_func: Callable[[list[str], Datum], AsyncIterator[Message]] + ) -> Awaitable[None]: ... def stop(self) -> None: ... - class MapStreamer: - async def handler(self, keys: list[str], datum: Datum) -> AsyncIterator[Message]: ... - + async def handler( + self, keys: list[str], datum: Datum + ) -> AsyncIterator[Message]: ... __all__ = [ "Message", diff --git a/packages/pynumaflow-lite/pynumaflow_lite/reducer.pyi b/packages/pynumaflow-lite/pynumaflow_lite/reducer.pyi index 66a087fb..6f31c43b 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/reducer.pyi +++ b/packages/pynumaflow-lite/pynumaflow_lite/reducer.pyi @@ -7,36 +7,28 @@ from collections.abc import AsyncIterable # Re-export the Python ABC for user convenience and typing from ._reduce_dtypes import Reducer as Reducer - class Message: keys: Optional[List[str]] value: bytes tags: Optional[List[str]] def __init__( - self, - value: bytes, - keys: Optional[List[str]] = ..., - tags: Optional[List[str]] = ..., + self, + value: bytes, + keys: Optional[List[str]] = ..., + tags: Optional[List[str]] = ..., ) -> None: ... - @staticmethod def message_to_drop() -> Message: ... - @staticmethod def to_drop() -> Message: ... - class Messages: def __init__(self) -> None: ... - def append(self, message: Message) -> None: ... - def __repr__(self) -> str: ... - def __str__(self) -> str: ... - class Datum: keys: List[str] value: bytes @@ -45,35 +37,31 @@ class Datum: headers: Dict[str, str] def __repr__(self) -> str: ... - def __str__(self) -> str: ... - class IntervalWindow: start: _dt.datetime end: _dt.datetime - class Metadata: interval_window: IntervalWindow - class ReduceAsyncServer: def __init__( - self, - sock_file: str | None = ..., - info_file: str | None = ..., + self, + sock_file: str | None = ..., + info_file: str | None = ..., ) -> None: ... - def start( self, - py_creator: type[Reducer] | Callable[[list[str], AsyncIterable[Datum], Metadata], Awaitable[Messages]], + py_creator: ( + type[Reducer] + | Callable[[list[str], AsyncIterable[Datum], Metadata], Awaitable[Messages]] + ), init_args: tuple | None = ..., ) -> Awaitable[None]: ... - def stop(self) -> None: ... - __all__ = [ "Message", "Messages", diff --git a/packages/pynumaflow-lite/pynumaflow_lite/reducestreamer.pyi b/packages/pynumaflow-lite/pynumaflow_lite/reducestreamer.pyi index b6176bdd..782716d2 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/reducestreamer.pyi +++ b/packages/pynumaflow-lite/pynumaflow_lite/reducestreamer.pyi @@ -7,23 +7,20 @@ from collections.abc import AsyncIterable # Re-export the Python ABC for user convenience and typing from ._reducestreamer_dtypes import ReduceStreamer as ReduceStreamer - class Message: keys: Optional[List[str]] value: bytes tags: Optional[List[str]] def __init__( - self, - value: bytes, - keys: Optional[List[str]] = ..., - tags: Optional[List[str]] = ..., + self, + value: bytes, + keys: Optional[List[str]] = ..., + tags: Optional[List[str]] = ..., ) -> None: ... - @staticmethod def message_to_drop() -> Message: ... - class Datum: keys: List[str] value: bytes @@ -32,19 +29,15 @@ class Datum: headers: Dict[str, str] def __repr__(self) -> str: ... - def __str__(self) -> str: ... - class IntervalWindow: start: _dt.datetime end: _dt.datetime - class Metadata: interval_window: IntervalWindow - class PyAsyncDatumStream: """ Python-visible async iterator that yields Datum items from a Tokio mpsc channel. @@ -54,19 +47,24 @@ class PyAsyncDatumStream: def __aiter__(self) -> PyAsyncDatumStream: ... def __anext__(self) -> Datum: ... - class ReduceStreamAsyncServer: def __init__( - self, - sock_file: str = ..., - info_file: str = ..., + self, + sock_file: str = ..., + info_file: str = ..., ) -> None: ... - - def start(self, py_creator: type[ReduceStreamer] | Callable[[list[str], AsyncIterable[Datum], Metadata], AsyncIterator[Message]], init_args: tuple | None = ...) -> Awaitable[None]: ... - + def start( + self, + py_creator: ( + type[ReduceStreamer] + | Callable[ + [list[str], AsyncIterable[Datum], Metadata], AsyncIterator[Message] + ] + ), + init_args: tuple | None = ..., + ) -> Awaitable[None]: ... def stop(self) -> None: ... - __all__ = [ "Message", "Datum", @@ -76,4 +74,3 @@ __all__ = [ "ReduceStreamAsyncServer", "ReduceStreamer", ] - diff --git a/packages/pynumaflow-lite/pynumaflow_lite/session_reducer.pyi b/packages/pynumaflow-lite/pynumaflow_lite/session_reducer.pyi index 7cf9df5b..198dfd7e 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/session_reducer.pyi +++ b/packages/pynumaflow-lite/pynumaflow_lite/session_reducer.pyi @@ -6,23 +6,20 @@ from typing import Optional, List, Dict, Awaitable # Re-export the Python ABC for user convenience and typing from ._session_reduce_dtypes import SessionReducer as SessionReducer - class Message: keys: Optional[List[str]] value: bytes tags: Optional[List[str]] def __init__( - self, - value: bytes, - keys: Optional[List[str]] = ..., - tags: Optional[List[str]] = ..., + self, + value: bytes, + keys: Optional[List[str]] = ..., + tags: Optional[List[str]] = ..., ) -> None: ... - @staticmethod def message_to_drop() -> Message: ... - class Datum: keys: List[str] value: bytes @@ -31,26 +28,22 @@ class Datum: headers: Dict[str, str] def __repr__(self) -> str: ... - def __str__(self) -> str: ... - class SessionReduceAsyncServer: def __init__( - self, - sock_file: str | None = ..., - info_file: str | None = ..., + self, + sock_file: str | None = ..., + info_file: str | None = ..., ) -> None: ... - - def start(self, py_creator: type[SessionReducer], init_args: tuple | None = ...) -> Awaitable[None]: ... - + def start( + self, py_creator: type[SessionReducer], init_args: tuple | None = ... + ) -> Awaitable[None]: ... def stop(self) -> None: ... - __all__ = [ "Message", "Datum", "SessionReduceAsyncServer", "SessionReducer", ] - diff --git a/packages/pynumaflow-lite/pynumaflow_lite/sideinputer.pyi b/packages/pynumaflow-lite/pynumaflow_lite/sideinputer.pyi index 475b6d1c..db7936fb 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/sideinputer.pyi +++ b/packages/pynumaflow-lite/pynumaflow_lite/sideinputer.pyi @@ -1,14 +1,13 @@ from __future__ import annotations -from typing import Callable, Awaitable, Any +from typing import Awaitable # Re-export the Python ABC for user convenience and typing from ._sideinput_dtypes import SideInput as SideInput - class Response: """Response from the side input retrieve handler.""" - + value: bytes broadcast: bool @@ -23,24 +22,19 @@ class Response: ... def __repr__(self) -> str: ... - def __str__(self) -> str: ... - class SideInputAsyncServer: """Async SideInput Server that can be started from Python.""" - + def __init__( - self, - sock_file: str | None = ..., - info_file: str | None = ..., + self, + sock_file: str | None = ..., + info_file: str | None = ..., ) -> None: ... - def start(self, py_sideinput: SideInput) -> Awaitable[None]: ... - def stop(self) -> None: ... - DIR_PATH: str """Default directory path where side input files are stored.""" @@ -50,4 +44,3 @@ __all__ = [ "SideInput", "DIR_PATH", ] - diff --git a/packages/pynumaflow-lite/pynumaflow_lite/sinker.pyi b/packages/pynumaflow-lite/pynumaflow_lite/sinker.pyi index 753fe963..4b432f1b 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/sinker.pyi +++ b/packages/pynumaflow-lite/pynumaflow_lite/sinker.pyi @@ -1,14 +1,12 @@ from __future__ import annotations -from typing import Optional, List, Dict, Callable, Awaitable, Any, AsyncIterator +from typing import Optional, List, Dict, Callable, Awaitable, AsyncIterator import datetime as _dt - class SystemMetadata: """System-generated metadata groups per message (read-only for sink).""" def __init__(self) -> None: ... - def groups(self) -> List[str]: """Returns the groups of the system metadata.""" ... @@ -23,12 +21,10 @@ class SystemMetadata: def __repr__(self) -> str: ... - class UserMetadata: """User-defined metadata groups per message (read-only for sink).""" def __init__(self) -> None: ... - def groups(self) -> List[str]: """Returns the groups of the user metadata.""" ... @@ -43,54 +39,43 @@ class UserMetadata: def __repr__(self) -> str: ... - class KeyValueGroup: key_value: Dict[str, bytes] def __init__(self, key_value: Optional[Dict[str, bytes]] = ...) -> None: ... - @staticmethod def from_dict(key_value: Dict[str, bytes]) -> KeyValueGroup: ... - class Message: keys: Optional[List[str]] value: bytes user_metadata: Optional[Dict[str, KeyValueGroup]] def __init__( - self, - value: bytes, - keys: Optional[List[str]] = ..., - user_metadata: Optional[Dict[str, KeyValueGroup]] = ..., + self, + value: bytes, + keys: Optional[List[str]] = ..., + user_metadata: Optional[Dict[str, KeyValueGroup]] = ..., ) -> None: ... - class Response: id: str @staticmethod def as_success(id: str) -> Response: ... - @staticmethod def as_failure(id: str, err_msg: str) -> Response: ... - @staticmethod def as_fallback(id: str) -> Response: ... - @staticmethod def as_serve(id: str, payload: bytes) -> Response: ... - @staticmethod def as_on_success(id: str, message: Optional[Message] = ...) -> Response: ... - class Responses: def __init__(self) -> None: ... - def append(self, response: Response) -> None: ... - class Datum: keys: List[str] value: bytes @@ -102,26 +87,22 @@ class Datum: system_metadata: SystemMetadata def __repr__(self) -> str: ... - def __str__(self) -> str: ... - class SinkAsyncServer: def __init__( - self, - sock_file: str | None = ..., - info_file: str | None = ..., + self, + sock_file: str | None = ..., + info_file: str | None = ..., ) -> None: ... - - def start(self, py_func: Callable[[AsyncIterator[Datum]], Awaitable[Responses]]) -> Awaitable[None]: ... - + def start( + self, py_func: Callable[[AsyncIterator[Datum]], Awaitable[Responses]] + ) -> Awaitable[None]: ... def stop(self) -> None: ... - class Sinker: async def handler(self, datums: AsyncIterator[Datum]) -> Responses: ... - __all__ = [ "SystemMetadata", "UserMetadata", @@ -133,4 +114,3 @@ __all__ = [ "SinkAsyncServer", "Sinker", ] - diff --git a/packages/pynumaflow-lite/pynumaflow_lite/sourcer.pyi b/packages/pynumaflow-lite/pynumaflow_lite/sourcer.pyi index 3bc41b0e..dc2d9614 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/sourcer.pyi +++ b/packages/pynumaflow-lite/pynumaflow_lite/sourcer.pyi @@ -1,17 +1,15 @@ from __future__ import annotations -from typing import Optional, List, Dict, Callable, Awaitable, Any +from typing import Optional, List, Dict, Awaitable import datetime as _dt # Re-export the Python ABC for user convenience and typing from ._source_dtypes import Sourcer as Sourcer - class UserMetadata: """User-defined metadata groups per message (read-write for source).""" def __init__(self) -> None: ... - def groups(self) -> List[str]: """Returns the groups of the user metadata.""" ... @@ -42,9 +40,9 @@ class UserMetadata: def __repr__(self) -> str: ... - class Message: """A message to be sent from the source.""" + payload: bytes offset: Offset event_time: _dt.datetime @@ -61,14 +59,12 @@ class Message: headers: Optional[Dict[str, str]] = ..., user_metadata: Optional[UserMetadata] = ..., ) -> None: ... - def __repr__(self) -> str: ... - def __str__(self) -> str: ... - class Offset: """The offset of a message.""" + offset: bytes partition_id: int @@ -77,14 +73,12 @@ class Offset: offset: bytes, partition_id: int = ..., ) -> None: ... - def __repr__(self) -> str: ... - def __str__(self) -> str: ... - class ReadRequest: """A request to read messages from the source.""" + num_records: int timeout_ms: int @@ -93,58 +87,52 @@ class ReadRequest: num_records: int, timeout_ms: int = ..., ) -> None: ... - def __repr__(self) -> str: ... - class AckRequest: """A request to acknowledge messages.""" + offsets: List[Offset] def __init__( self, offsets: List[Offset], ) -> None: ... - def __repr__(self) -> str: ... - class NackRequest: """A request to negatively acknowledge messages.""" + offsets: List[Offset] def __init__( self, offsets: List[Offset], ) -> None: ... - def __repr__(self) -> str: ... - class PendingResponse: """Response for pending messages count.""" + count: int def __init__( self, count: int = ..., ) -> None: ... - def __repr__(self) -> str: ... - class PartitionsResponse: """Response for partitions.""" + partitions: List[int] def __init__( self, partitions: List[int], ) -> None: ... - def __repr__(self) -> str: ... - class SourceAsyncServer: """Async Source Server that can be started from Python code.""" @@ -153,12 +141,9 @@ class SourceAsyncServer: sock_file: str | None = ..., info_file: str | None = ..., ) -> None: ... - def start(self, py_func: Sourcer) -> Awaitable[None]: ... - def stop(self) -> None: ... - __all__ = [ "UserMetadata", "Message", @@ -171,4 +156,3 @@ __all__ = [ "SourceAsyncServer", "Sourcer", ] - diff --git a/packages/pynumaflow-lite/pynumaflow_lite/sourcetransformer.pyi b/packages/pynumaflow-lite/pynumaflow_lite/sourcetransformer.pyi index 437bc7aa..63a828b1 100644 --- a/packages/pynumaflow-lite/pynumaflow_lite/sourcetransformer.pyi +++ b/packages/pynumaflow-lite/pynumaflow_lite/sourcetransformer.pyi @@ -1,17 +1,15 @@ from __future__ import annotations -from typing import Optional, List, Dict, Callable, Awaitable, Any +from typing import Optional, List, Dict, Awaitable import datetime as _dt # Re-export the Python ABC for user convenience and typing from ._sourcetransformer_dtypes import SourceTransformer as SourceTransformer - class SystemMetadata: """System-generated metadata groups per message (read-only).""" def __init__(self) -> None: ... - def groups(self) -> List[str]: """Returns the groups of the system metadata.""" ... @@ -26,12 +24,10 @@ class SystemMetadata: def __repr__(self) -> str: ... - class UserMetadata: """User-defined metadata groups per message (read-write).""" def __init__(self) -> None: ... - def groups(self) -> List[str]: """Returns the groups of the user metadata.""" ... @@ -62,17 +58,12 @@ class UserMetadata: def __repr__(self) -> str: ... - class Messages: def __init__(self) -> None: ... - def append(self, message: Message) -> None: ... - def __repr__(self) -> str: ... - def __str__(self) -> str: ... - class Message: keys: Optional[List[str]] value: bytes @@ -81,18 +72,16 @@ class Message: user_metadata: Optional[UserMetadata] def __init__( - self, - value: bytes, - event_time: _dt.datetime, - keys: Optional[List[str]] = ..., - tags: Optional[List[str]] = ..., - user_metadata: Optional[UserMetadata] = ..., + self, + value: bytes, + event_time: _dt.datetime, + keys: Optional[List[str]] = ..., + tags: Optional[List[str]] = ..., + user_metadata: Optional[UserMetadata] = ..., ) -> None: ... - @staticmethod def message_to_drop(event_time: _dt.datetime) -> Message: ... - class Datum: # Read-only attributes provided by the extension keys: List[str] @@ -104,22 +93,17 @@ class Datum: system_metadata: SystemMetadata def __repr__(self) -> str: ... - def __str__(self) -> str: ... - class SourceTransformAsyncServer: def __init__( - self, - sock_file: str | None = ..., - info_file: str | None = ..., + self, + sock_file: str | None = ..., + info_file: str | None = ..., ) -> None: ... - def start(self, py_func: SourceTransformer) -> Awaitable[None]: ... - def stop(self) -> None: ... - __all__ = [ "SystemMetadata", "UserMetadata", @@ -129,4 +113,3 @@ __all__ = [ "SourceTransformAsyncServer", "SourceTransformer", ] - diff --git a/packages/pynumaflow-lite/pyproject.toml b/packages/pynumaflow-lite/pyproject.toml index 6784ba57..bb198180 100644 --- a/packages/pynumaflow-lite/pyproject.toml +++ b/packages/pynumaflow-lite/pyproject.toml @@ -16,5 +16,7 @@ features = ["pyo3/extension-module"] [dependency-groups] dev = [ + "black>=24.8.0", "pytest>=8.3.5", + "ruff>=0.15.8", ] diff --git a/packages/pynumaflow-lite/tests/_test_utils.py b/packages/pynumaflow-lite/tests/_test_utils.py index 5a3c9150..0780dba2 100644 --- a/packages/pynumaflow-lite/tests/_test_utils.py +++ b/packages/pynumaflow-lite/tests/_test_utils.py @@ -26,14 +26,14 @@ def _wait_for_socket(path: Path, timeout: float = 10.0) -> None: def run_python_server_with_rust_client( - script: str, - sock_path: Path, - server_info_path: Path, - rust_bin_name: str, - rust_bin_args: Optional[List[str]] = None, - socket_timeout: float = 20.0, - rust_timeout: float = 60.0, - server_shutdown_timeout: float = 15.0, + script: str, + sock_path: Path, + server_info_path: Path, + rust_bin_name: str, + rust_bin_args: Optional[List[str]] = None, + socket_timeout: float = 20.0, + rust_timeout: float = 60.0, + server_shutdown_timeout: float = 15.0, ) -> None: """ Generic test runner for Python server + Rust client integration tests. @@ -86,7 +86,12 @@ def run_python_server_with_rust_client( rust_cmd.extend(["--"] + rust_bin_args) rust = subprocess.run( - rust_cmd, cwd=str(cargo_root), capture_output=True, text=True, env=env, timeout=rust_timeout + rust_cmd, + cwd=str(cargo_root), + capture_output=True, + text=True, + env=env, + timeout=rust_timeout, ) if rust.returncode != 0: # Dump helpful logs for debugging @@ -118,4 +123,6 @@ def run_python_server_with_rust_client( except Exception: pass - assert server.returncode == 0, f"Server did not exit cleanly, code={server.returncode}" + assert ( + server.returncode == 0 + ), f"Server did not exit cleanly, code={server.returncode}" diff --git a/packages/pynumaflow-lite/tests/examples/accumulator_stream_sorter.py b/packages/pynumaflow-lite/tests/examples/accumulator_stream_sorter.py index 484cb371..c76b9237 100644 --- a/packages/pynumaflow-lite/tests/examples/accumulator_stream_sorter.py +++ b/packages/pynumaflow-lite/tests/examples/accumulator_stream_sorter.py @@ -4,11 +4,18 @@ This accumulator buffers incoming data and sorts it by event time, flushing sorted data when the watermark advances. """ + +import signal import asyncio from datetime import datetime from typing import AsyncIterator -from pynumaflow_lite.accumulator import Datum, Message, AccumulatorAsyncServer, Accumulator +from pynumaflow_lite.accumulator import ( + Datum, + Message, + AccumulatorAsyncServer, + Accumulator, +) class StreamSorter(Accumulator): @@ -19,6 +26,7 @@ class StreamSorter(Accumulator): def __init__(self): from datetime import timezone + # Initialize with a very old timestamp (timezone-aware) self.latest_wm = datetime.fromtimestamp(-1, tz=timezone.utc) self.sorted_buffer: list[Datum] = [] @@ -33,8 +41,10 @@ async def handler(self, datums: AsyncIterator[Datum]) -> AsyncIterator[Message]: async for datum in datums: datum_count += 1 - print(f"Received datum #{datum_count}: event_time={datum.event_time}, " - f"watermark={datum.watermark}, value={datum.value}") + print( + f"Received datum #{datum_count}: event_time={datum.event_time}, " + f"watermark={datum.watermark}, value={datum.value}" + ) # If watermark has moved forward if datum.watermark and datum.watermark > self.latest_wm: @@ -124,7 +134,6 @@ async def main(): # Optional: ensure default signal handlers are in place so asyncio.run can handle them cleanly. -import signal signal.signal(signal.SIGINT, signal.default_int_handler) try: signal.signal(signal.SIGTERM, signal.SIG_DFL) diff --git a/packages/pynumaflow-lite/tests/examples/batchmap_cat.py b/packages/pynumaflow-lite/tests/examples/batchmap_cat.py index 78490cab..a84ab7be 100644 --- a/packages/pynumaflow-lite/tests/examples/batchmap_cat.py +++ b/packages/pynumaflow-lite/tests/examples/batchmap_cat.py @@ -6,7 +6,9 @@ from pynumaflow_lite import batchmapper -async def async_handler(batch: collections.abc.AsyncIterator[batchmapper.Datum]) -> batchmapper.BatchResponses: +async def async_handler( + batch: collections.abc.AsyncIterator[batchmapper.Datum], +) -> batchmapper.BatchResponses: responses = batchmapper.BatchResponses() async for d in batch: resp = batchmapper.BatchResponse.from_id(d.id) @@ -19,7 +21,12 @@ async def async_handler(batch: collections.abc.AsyncIterator[batchmapper.Datum]) return responses -async def start(f: Callable[[collections.abc.AsyncIterator[batchmapper.Datum]], Awaitable[batchmapper.BatchResponses]]): +async def start( + f: Callable[ + [collections.abc.AsyncIterator[batchmapper.Datum]], + Awaitable[batchmapper.BatchResponses], + ], +): sock_file = "/tmp/var/run/numaflow/batchmap.sock" server_info_file = "/tmp/var/run/numaflow/mapper-server-info" server = batchmapper.BatchMapAsyncServer(sock_file, server_info_file) diff --git a/packages/pynumaflow-lite/tests/examples/batchmap_cat_class.py b/packages/pynumaflow-lite/tests/examples/batchmap_cat_class.py index 6288925b..8bf22989 100644 --- a/packages/pynumaflow-lite/tests/examples/batchmap_cat_class.py +++ b/packages/pynumaflow-lite/tests/examples/batchmap_cat_class.py @@ -8,7 +8,9 @@ class SimpleBatchCat(batchmapper.BatchMapper): - async def handler(self, batch: AsyncIterator[batchmapper.Datum]) -> batchmapper.BatchResponses: + async def handler( + self, batch: AsyncIterator[batchmapper.Datum] + ) -> batchmapper.BatchResponses: responses = batchmapper.BatchResponses() async for d in batch: resp = batchmapper.BatchResponse(d.id) @@ -29,7 +31,11 @@ async def handler(self, batch: AsyncIterator[batchmapper.Datum]) -> batchmapper. pass -async def start(f: Callable[[AsyncIterator[batchmapper.Datum]], Awaitable[batchmapper.BatchResponses]]): +async def start( + f: Callable[ + [AsyncIterator[batchmapper.Datum]], Awaitable[batchmapper.BatchResponses] + ], +): sock_file = "/tmp/var/run/numaflow/batchmap.sock" server_info_file = "/tmp/var/run/numaflow/mapper-server-info" server = batchmapper.BatchMapAsyncServer(sock_file, server_info_file) diff --git a/packages/pynumaflow-lite/tests/examples/map_cat.py b/packages/pynumaflow-lite/tests/examples/map_cat.py index f0a38db8..7b04ca11 100644 --- a/packages/pynumaflow-lite/tests/examples/map_cat.py +++ b/packages/pynumaflow-lite/tests/examples/map_cat.py @@ -5,9 +5,7 @@ from pynumaflow_lite import mapper -async def async_handler( - keys: list[str], payload: mapper.Datum -) -> mapper.Messages: +async def async_handler(keys: list[str], payload: mapper.Datum) -> mapper.Messages: messages = mapper.Messages() # Read system metadata (read-only) @@ -31,9 +29,13 @@ async def async_handler( user_metadata = mapper.UserMetadata() user_metadata.create_group("processing") user_metadata.add_kv("processing", "handler", b"map_cat") - user_metadata.add_kv("processing", "msg_length", str(len(payload.value)).encode()) + user_metadata.add_kv( + "processing", "msg_length", str(len(payload.value)).encode() + ) - messages.append(mapper.Message(payload.value, keys, user_metadata=user_metadata)) + messages.append( + mapper.Message(payload.value, keys, user_metadata=user_metadata) + ) return messages diff --git a/packages/pynumaflow-lite/tests/examples/map_cat_class.py b/packages/pynumaflow-lite/tests/examples/map_cat_class.py index cf23854e..8d608805 100644 --- a/packages/pynumaflow-lite/tests/examples/map_cat_class.py +++ b/packages/pynumaflow-lite/tests/examples/map_cat_class.py @@ -6,9 +6,7 @@ class SimpleCat(mapper.Mapper): - async def handler( - self, keys: list[str], payload: mapper.Datum - ) -> mapper.Messages: + async def handler(self, keys: list[str], payload: mapper.Datum) -> mapper.Messages: messages = mapper.Messages() @@ -33,9 +31,13 @@ async def handler( user_metadata = mapper.UserMetadata() user_metadata.create_group("processing") user_metadata.add_kv("processing", "handler", b"map_cat_class") - user_metadata.add_kv("processing", "msg_length", str(len(payload.value)).encode()) + user_metadata.add_kv( + "processing", "msg_length", str(len(payload.value)).encode() + ) - messages.append(mapper.Message(payload.value, keys, user_metadata=user_metadata)) + messages.append( + mapper.Message(payload.value, keys, user_metadata=user_metadata) + ) return messages diff --git a/packages/pynumaflow-lite/tests/examples/mapstream_cat.py b/packages/pynumaflow-lite/tests/examples/mapstream_cat.py index b8dea5ef..8433ba17 100644 --- a/packages/pynumaflow-lite/tests/examples/mapstream_cat.py +++ b/packages/pynumaflow-lite/tests/examples/mapstream_cat.py @@ -7,7 +7,9 @@ from pynumaflow_lite.mapstreamer import Message -async def async_handler(keys: list[str], datum: mapstreamer.Datum) -> AsyncIterator[Message]: +async def async_handler( + keys: list[str], datum: mapstreamer.Datum +) -> AsyncIterator[Message]: """ A handler that splits the input datum value into multiple strings by `,` separator and emits them as a stream. @@ -46,4 +48,3 @@ async def start(f: Callable[[list[str], mapstreamer.Datum], AsyncIterator[Messag if __name__ == "__main__": asyncio.run(start(async_handler)) - diff --git a/packages/pynumaflow-lite/tests/examples/mapstream_cat_class.py b/packages/pynumaflow-lite/tests/examples/mapstream_cat_class.py index d0a09ebe..2e62bb90 100644 --- a/packages/pynumaflow-lite/tests/examples/mapstream_cat_class.py +++ b/packages/pynumaflow-lite/tests/examples/mapstream_cat_class.py @@ -8,7 +8,9 @@ class SimpleStreamCat(mapstreamer.MapStreamer): - async def handler(self, keys: list[str], datum: mapstreamer.Datum) -> AsyncIterator[Message]: + async def handler( + self, keys: list[str], datum: mapstreamer.Datum + ) -> AsyncIterator[Message]: parts = datum.value.decode("utf-8").split(",") if not parts: yield Message.to_drop() @@ -52,4 +54,3 @@ async def start(f: Callable[[list[str], mapstreamer.Datum], AsyncIterator[Messag if __name__ == "__main__": async_handler = SimpleStreamCat() asyncio.run(start(async_handler)) - diff --git a/packages/pynumaflow-lite/tests/examples/reduce_counter_class.py b/packages/pynumaflow-lite/tests/examples/reduce_counter_class.py index 020c49d9..0faf22a6 100644 --- a/packages/pynumaflow-lite/tests/examples/reduce_counter_class.py +++ b/packages/pynumaflow-lite/tests/examples/reduce_counter_class.py @@ -10,7 +10,10 @@ def __init__(self, initial: int = 0) -> None: self.counter = initial async def handler( - self, keys: list[str], datums: AsyncIterable[reducer.Datum], md: reducer.Metadata + self, + keys: list[str], + datums: AsyncIterable[reducer.Datum], + md: reducer.Metadata, ) -> reducer.Messages: iw = md.interval_window self.counter = 0 @@ -57,4 +60,3 @@ async def start(creator: type, init_args: tuple): if __name__ == "__main__": asyncio.run(start(ReduceCounter, (0,))) - diff --git a/packages/pynumaflow-lite/tests/examples/reduce_counter_func.py b/packages/pynumaflow-lite/tests/examples/reduce_counter_func.py index efb07418..294c80af 100644 --- a/packages/pynumaflow-lite/tests/examples/reduce_counter_func.py +++ b/packages/pynumaflow-lite/tests/examples/reduce_counter_func.py @@ -7,7 +7,7 @@ async def reduce_handler( - keys: list[str], datums: AsyncIterable[reducer.Datum], md: reducer.Metadata + keys: list[str], datums: AsyncIterable[reducer.Datum], md: reducer.Metadata ) -> reducer.Messages: interval_window = md.interval_window counter = 0 @@ -31,7 +31,10 @@ async def reduce_handler( async def start( - handler: Callable[[list[str], AsyncIterable[reducer.Datum], reducer.Metadata], Awaitable[reducer.Messages]] + handler: Callable[ + [list[str], AsyncIterable[reducer.Datum], reducer.Metadata], + Awaitable[reducer.Messages], + ], ): sock_file = "/tmp/var/run/numaflow/reduce.sock" server_info_file = "/tmp/var/run/numaflow/reducer-server-info" diff --git a/packages/pynumaflow-lite/tests/examples/reducestream_counter.py b/packages/pynumaflow-lite/tests/examples/reducestream_counter.py index fcebeb0e..c535b76d 100644 --- a/packages/pynumaflow-lite/tests/examples/reducestream_counter.py +++ b/packages/pynumaflow-lite/tests/examples/reducestream_counter.py @@ -8,11 +8,11 @@ class ReduceStreamCounter(reducestreamer.ReduceStreamer): """ A reduce streaming counter that emits intermediate results. - + This test implementation counts datums and yields a message for every datum received, demonstrating the streaming capability. """ - + def __init__(self, initial: int = 0) -> None: self.counter = initial @@ -24,12 +24,12 @@ async def handler( ) -> AsyncIterator[reducestreamer.Message]: """ Process datums and yield messages incrementally. - + For testing purposes, we yield a message for each datum received. """ iw = md.interval_window self.counter = 0 - + async for _ in datums: self.counter += 1 # Yield a message for each datum (streaming behavior) @@ -75,4 +75,3 @@ async def start(creator: type, init_args: tuple): if __name__ == "__main__": asyncio.run(start(ReduceStreamCounter, (0,))) - diff --git a/packages/pynumaflow-lite/tests/examples/session_reduce_counter_class.py b/packages/pynumaflow-lite/tests/examples/session_reduce_counter_class.py index 003c52a7..98d9bb9a 100644 --- a/packages/pynumaflow-lite/tests/examples/session_reduce_counter_class.py +++ b/packages/pynumaflow-lite/tests/examples/session_reduce_counter_class.py @@ -25,7 +25,7 @@ def __init__(self, initial: int = 0) -> None: self.counter = initial async def session_reduce( - self, keys: list[str], datums: AsyncIterable[session_reducer.Datum] + self, keys: list[str], datums: AsyncIterable[session_reducer.Datum] ) -> AsyncIterator[session_reducer.Message]: """ Count all incoming messages in this session and yield the count. diff --git a/packages/pynumaflow-lite/tests/examples/sideinput_example.py b/packages/pynumaflow-lite/tests/examples/sideinput_example.py index b23b41da..59f54d06 100644 --- a/packages/pynumaflow-lite/tests/examples/sideinput_example.py +++ b/packages/pynumaflow-lite/tests/examples/sideinput_example.py @@ -40,10 +40,10 @@ async def main(): # Set up signal handling for graceful shutdown loop = asyncio.get_running_loop() - + def handle_signal(): server.stop() - + for sig in (signal.SIGINT, signal.SIGTERM): loop.add_signal_handler(sig, handle_signal) @@ -53,4 +53,3 @@ def handle_signal(): if __name__ == "__main__": asyncio.run(main()) - diff --git a/packages/pynumaflow-lite/tests/examples/sink_log.py b/packages/pynumaflow-lite/tests/examples/sink_log.py index f782c020..2d1448ba 100644 --- a/packages/pynumaflow-lite/tests/examples/sink_log.py +++ b/packages/pynumaflow-lite/tests/examples/sink_log.py @@ -11,7 +11,9 @@ _LOGGER = logging.getLogger(__name__) -async def async_handler(datums: collections.abc.AsyncIterator[sinker.Datum]) -> sinker.Responses: +async def async_handler( + datums: collections.abc.AsyncIterator[sinker.Datum], +) -> sinker.Responses: """ Simple log sink that logs each message and returns success responses. Also demonstrates reading metadata (read-only for sink). @@ -40,7 +42,11 @@ async def async_handler(datums: collections.abc.AsyncIterator[sinker.Datum]) -> return responses -async def start(f: Callable[[collections.abc.AsyncIterator[sinker.Datum]], Awaitable[sinker.Responses]]): +async def start( + f: Callable[ + [collections.abc.AsyncIterator[sinker.Datum]], Awaitable[sinker.Responses] + ], +): sock_file = "/tmp/var/run/numaflow/sink.sock" server_info_file = "/tmp/var/run/numaflow/sinker-server-info" server = sinker.SinkAsyncServer(sock_file, server_info_file) @@ -66,4 +72,3 @@ async def start(f: Callable[[collections.abc.AsyncIterator[sinker.Datum]], Await if __name__ == "__main__": asyncio.run(start(async_handler)) - diff --git a/packages/pynumaflow-lite/tests/examples/sink_log_class.py b/packages/pynumaflow-lite/tests/examples/sink_log_class.py index ade74e73..de4b2976 100644 --- a/packages/pynumaflow-lite/tests/examples/sink_log_class.py +++ b/packages/pynumaflow-lite/tests/examples/sink_log_class.py @@ -72,4 +72,3 @@ async def start(): if __name__ == "__main__": asyncio.run(start()) - diff --git a/packages/pynumaflow-lite/tests/examples/source_simple.py b/packages/pynumaflow-lite/tests/examples/source_simple.py index ca90ec03..924118ba 100644 --- a/packages/pynumaflow-lite/tests/examples/source_simple.py +++ b/packages/pynumaflow-lite/tests/examples/source_simple.py @@ -22,12 +22,16 @@ def __init__(self): self.counter = 0 self.partition_idx = 0 - async def read_handler(self, datum: sourcer.ReadRequest) -> AsyncIterator[sourcer.Message]: + async def read_handler( + self, datum: sourcer.ReadRequest + ) -> AsyncIterator[sourcer.Message]: """ The simple source generates messages with incrementing numbers. Also demonstrates creating user metadata (source is origin, so only user metadata). """ - _LOGGER.info(f"Read request: num_records={datum.num_records}, timeout_ms={datum.timeout_ms}") + _LOGGER.info( + f"Read request: num_records={datum.num_records}, timeout_ms={datum.timeout_ms}" + ) # Generate the requested number of messages for i in range(datum.num_records): @@ -37,15 +41,19 @@ async def read_handler(self, datum: sourcer.ReadRequest) -> AsyncIterator[source # Create offset offset = sourcer.Offset( offset=str(self.counter).encode("utf-8"), - partition_id=self.partition_idx + partition_id=self.partition_idx, ) # Create user metadata for the message user_metadata = sourcer.UserMetadata() user_metadata.create_group("source_info") user_metadata.add_kv("source_info", "source_name", b"simple_source") - user_metadata.add_kv("source_info", "message_id", str(self.counter).encode()) - user_metadata.add_kv("source_info", "partition", str(self.partition_idx).encode()) + user_metadata.add_kv( + "source_info", "message_id", str(self.counter).encode() + ) + user_metadata.add_kv( + "source_info", "partition", str(self.partition_idx).encode() + ) # Create message message = sourcer.Message( @@ -54,7 +62,7 @@ async def read_handler(self, datum: sourcer.ReadRequest) -> AsyncIterator[source event_time=datetime.now(timezone.utc), keys=["key1"], headers={"source": "simple"}, - user_metadata=user_metadata + user_metadata=user_metadata, ) _LOGGER.info(f"Generated message: {self.counter}") @@ -71,7 +79,9 @@ async def ack_handler(self, request: sourcer.AckRequest) -> None: """ _LOGGER.info(f"Acknowledging {len(request.offsets)} offsets") for offset in request.offsets: - _LOGGER.debug(f"Acked offset: {offset.offset.decode('utf-8')}, partition: {offset.partition_id}") + _LOGGER.debug( + f"Acked offset: {offset.offset.decode('utf-8')}, partition: {offset.partition_id}" + ) async def nack_handler(self, request: sourcer.NackRequest) -> None: """ @@ -79,7 +89,9 @@ async def nack_handler(self, request: sourcer.NackRequest) -> None: """ _LOGGER.info(f"Negatively acknowledging {len(request.offsets)} offsets") for offset in request.offsets: - _LOGGER.warning(f"Nacked offset: {offset.offset.decode('utf-8')}, partition: {offset.partition_id}") + _LOGGER.warning( + f"Nacked offset: {offset.offset.decode('utf-8')}, partition: {offset.partition_id}" + ) async def pending_handler(self) -> sourcer.PendingResponse: """ @@ -123,4 +135,3 @@ async def start(): if __name__ == "__main__": asyncio.run(start()) - diff --git a/packages/pynumaflow-lite/tests/examples/sourcetransform_event_filter.py b/packages/pynumaflow-lite/tests/examples/sourcetransform_event_filter.py index 3f9e19f6..8c33ab2d 100644 --- a/packages/pynumaflow-lite/tests/examples/sourcetransform_event_filter.py +++ b/packages/pynumaflow-lite/tests/examples/sourcetransform_event_filter.py @@ -22,7 +22,7 @@ class EventFilter(sourcetransformer.SourceTransformer): """ async def handler( - self, keys: list[str], datum: sourcetransformer.Datum + self, keys: list[str], datum: sourcetransformer.Datum ) -> sourcetransformer.Messages: val = datum.value event_time = datum.event_time @@ -46,13 +46,17 @@ async def handler( print(f"Got event time: {event_time}, it is before 2022, so dropping") messages.append(sourcetransformer.Message.message_to_drop(event_time)) elif event_time < january_first_2023: - print(f"Got event time: {event_time}, it is within year 2022, so forwarding to within_year_2022") + print( + f"Got event time: {event_time}, it is within year 2022, so forwarding to within_year_2022" + ) # Create user metadata for the outgoing message user_metadata = sourcetransformer.UserMetadata() user_metadata.create_group("filter_info") user_metadata.add_kv("filter_info", "filter_result", b"within_year_2022") - user_metadata.add_kv("filter_info", "original_event_time", str(event_time).encode()) + user_metadata.add_kv( + "filter_info", "original_event_time", str(event_time).encode() + ) messages.append( sourcetransformer.Message( @@ -60,17 +64,21 @@ async def handler( event_time=january_first_2022, keys=keys, tags=["within_year_2022"], - user_metadata=user_metadata + user_metadata=user_metadata, ) ) else: - print(f"Got event time: {event_time}, it is after year 2022, so forwarding to after_year_2022") + print( + f"Got event time: {event_time}, it is after year 2022, so forwarding to after_year_2022" + ) # Create user metadata for the outgoing message user_metadata = sourcetransformer.UserMetadata() user_metadata.create_group("filter_info") user_metadata.add_kv("filter_info", "filter_result", b"after_year_2022") - user_metadata.add_kv("filter_info", "original_event_time", str(event_time).encode()) + user_metadata.add_kv( + "filter_info", "original_event_time", str(event_time).encode() + ) messages.append( sourcetransformer.Message( @@ -78,7 +86,7 @@ async def handler( event_time=january_first_2023, keys=keys, tags=["after_year_2022"], - user_metadata=user_metadata + user_metadata=user_metadata, ) ) @@ -93,7 +101,9 @@ async def handler( pass -async def start(f: Callable[[list[str], sourcetransformer.Datum], sourcetransformer.Messages]): +async def start( + f: Callable[[list[str], sourcetransformer.Datum], sourcetransformer.Messages], +): sock_file = "/tmp/var/run/numaflow/sourcetransform.sock" server_info_file = "/tmp/var/run/numaflow/sourcetransformer-server-info" server = sourcetransformer.SourceTransformAsyncServer(sock_file, server_info_file) @@ -126,4 +136,3 @@ async def start(f: Callable[[list[str], sourcetransformer.Datum], sourcetransfor if __name__ == "__main__": async_handler = EventFilter() asyncio.run(start(async_handler)) - diff --git a/packages/pynumaflow-lite/tests/test_reducestream.py b/packages/pynumaflow-lite/tests/test_reducestream.py index b42cfa08..6d2ce0df 100644 --- a/packages/pynumaflow-lite/tests/test_reducestream.py +++ b/packages/pynumaflow-lite/tests/test_reducestream.py @@ -20,4 +20,3 @@ def test_python_reducestream_server_and_rust_client(script: str, tmp_path: Path) server_info_path=SERVER_INFO, rust_bin_name="test_reducestream", ) - diff --git a/packages/pynumaflow-lite/tests/test_sideinput.py b/packages/pynumaflow-lite/tests/test_sideinput.py index 8922dfa7..9fe4bab6 100644 --- a/packages/pynumaflow-lite/tests/test_sideinput.py +++ b/packages/pynumaflow-lite/tests/test_sideinput.py @@ -20,4 +20,3 @@ def test_python_server_and_rust_client(script: str, tmp_path: Path): server_info_path=SERVER_INFO, rust_bin_name="test_sideinput", ) - diff --git a/packages/pynumaflow-lite/tests/test_sink.py b/packages/pynumaflow-lite/tests/test_sink.py index 3890e416..a50c7670 100644 --- a/packages/pynumaflow-lite/tests/test_sink.py +++ b/packages/pynumaflow-lite/tests/test_sink.py @@ -21,4 +21,3 @@ def test_python_sink_server_and_rust_client(script: str, tmp_path: Path): server_info_path=SERVER_INFO, rust_bin_name="test_sink", ) - diff --git a/packages/pynumaflow-lite/tests/test_source.py b/packages/pynumaflow-lite/tests/test_source.py index beb40d12..581a912d 100644 --- a/packages/pynumaflow-lite/tests/test_source.py +++ b/packages/pynumaflow-lite/tests/test_source.py @@ -20,4 +20,3 @@ def test_python_source_server_and_rust_client(script: str, tmp_path: Path): server_info_path=SERVER_INFO, rust_bin_name="test_source", ) - diff --git a/packages/pynumaflow-lite/tests/test_sourcetransform.py b/packages/pynumaflow-lite/tests/test_sourcetransform.py index a5182048..01274f15 100644 --- a/packages/pynumaflow-lite/tests/test_sourcetransform.py +++ b/packages/pynumaflow-lite/tests/test_sourcetransform.py @@ -20,4 +20,3 @@ def test_python_server_and_rust_client(script: str, tmp_path: Path): server_info_path=SERVER_INFO, rust_bin_name="test_sourcetransform", ) - diff --git a/packages/pynumaflow-lite/uv.lock b/packages/pynumaflow-lite/uv.lock index ef06e0bf..11fb20ec 100644 --- a/packages/pynumaflow-lite/uv.lock +++ b/packages/pynumaflow-lite/uv.lock @@ -1,18 +1,184 @@ version = 1 -revision = 1 +revision = 3 requires-python = ">=3.8" resolution-markers = [ - "python_full_version >= '3.9'", + "python_full_version >= '3.10'", + "python_full_version == '3.9.*'", "python_full_version < '3.9'", ] +[[package]] +name = "black" +version = "24.8.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +dependencies = [ + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "mypy-extensions", marker = "python_full_version < '3.9'" }, + { name = "packaging", marker = "python_full_version < '3.9'" }, + { name = "pathspec", version = "0.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "platformdirs", version = "4.3.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "tomli", marker = "python_full_version < '3.9'" }, + { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/b0/46fb0d4e00372f4a86a6f8efa3cb193c9f64863615e39010b1477e010578/black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f", size = 644810, upload-time = "2024-08-02T17:43:18.405Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/6e/74e29edf1fba3887ed7066930a87f698ffdcd52c5dbc263eabb06061672d/black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6", size = 1632092, upload-time = "2024-08-02T17:47:26.911Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/575cb6c3faee690b05c9d11ee2e8dba8fbd6d6c134496e644c1feb1b47da/black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb", size = 1457529, upload-time = "2024-08-02T17:47:29.109Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/d34099e95c437b53d01c4aa37cf93944b233066eb034ccf7897fa4e5f286/black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42", size = 1757443, upload-time = "2024-08-02T17:46:20.306Z" }, + { url = "https://files.pythonhosted.org/packages/87/a0/6d2e4175ef364b8c4b64f8441ba041ed65c63ea1db2720d61494ac711c15/black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a", size = 1418012, upload-time = "2024-08-02T17:47:20.33Z" }, + { url = "https://files.pythonhosted.org/packages/08/a6/0a3aa89de9c283556146dc6dbda20cd63a9c94160a6fbdebaf0918e4a3e1/black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1", size = 1615080, upload-time = "2024-08-02T17:48:05.467Z" }, + { url = "https://files.pythonhosted.org/packages/db/94/b803d810e14588bb297e565821a947c108390a079e21dbdcb9ab6956cd7a/black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af", size = 1438143, upload-time = "2024-08-02T17:47:30.247Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b5/f485e1bbe31f768e2e5210f52ea3f432256201289fd1a3c0afda693776b0/black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4", size = 1738774, upload-time = "2024-08-02T17:46:17.837Z" }, + { url = "https://files.pythonhosted.org/packages/a8/69/a000fc3736f89d1bdc7f4a879f8aaf516fb03613bb51a0154070383d95d9/black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af", size = 1427503, upload-time = "2024-08-02T17:46:22.654Z" }, + { url = "https://files.pythonhosted.org/packages/a2/a8/05fb14195cfef32b7c8d4585a44b7499c2a4b205e1662c427b941ed87054/black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368", size = 1646132, upload-time = "2024-08-02T17:49:52.843Z" }, + { url = "https://files.pythonhosted.org/packages/41/77/8d9ce42673e5cb9988f6df73c1c5c1d4e9e788053cccd7f5fb14ef100982/black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed", size = 1448665, upload-time = "2024-08-02T17:47:54.479Z" }, + { url = "https://files.pythonhosted.org/packages/cc/94/eff1ddad2ce1d3cc26c162b3693043c6b6b575f538f602f26fe846dfdc75/black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018", size = 1762458, upload-time = "2024-08-02T17:46:19.384Z" }, + { url = "https://files.pythonhosted.org/packages/28/ea/18b8d86a9ca19a6942e4e16759b2fa5fc02bbc0eb33c1b866fcd387640ab/black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2", size = 1436109, upload-time = "2024-08-02T17:46:52.97Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d4/ae03761ddecc1a37d7e743b89cccbcf3317479ff4b88cfd8818079f890d0/black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd", size = 1617322, upload-time = "2024-08-02T17:51:20.203Z" }, + { url = "https://files.pythonhosted.org/packages/14/4b/4dfe67eed7f9b1ddca2ec8e4418ea74f0d1dc84d36ea874d618ffa1af7d4/black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2", size = 1442108, upload-time = "2024-08-02T17:50:40.824Z" }, + { url = "https://files.pythonhosted.org/packages/97/14/95b3f91f857034686cae0e73006b8391d76a8142d339b42970eaaf0416ea/black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e", size = 1745786, upload-time = "2024-08-02T17:46:02.939Z" }, + { url = "https://files.pythonhosted.org/packages/95/54/68b8883c8aa258a6dde958cd5bdfada8382bec47c5162f4a01e66d839af1/black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920", size = 1426754, upload-time = "2024-08-02T17:46:38.603Z" }, + { url = "https://files.pythonhosted.org/packages/13/b2/b3f24fdbb46f0e7ef6238e131f13572ee8279b70f237f221dd168a9dba1a/black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c", size = 1631706, upload-time = "2024-08-02T17:49:57.606Z" }, + { url = "https://files.pythonhosted.org/packages/d9/35/31010981e4a05202a84a3116423970fd1a59d2eda4ac0b3570fbb7029ddc/black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e", size = 1457429, upload-time = "2024-08-02T17:49:12.764Z" }, + { url = "https://files.pythonhosted.org/packages/27/25/3f706b4f044dd569a20a4835c3b733dedea38d83d2ee0beb8178a6d44945/black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47", size = 1756488, upload-time = "2024-08-02T17:46:08.067Z" }, + { url = "https://files.pythonhosted.org/packages/63/72/79375cd8277cbf1c5670914e6bd4c1b15dea2c8f8e906dc21c448d0535f0/black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb", size = 1417721, upload-time = "2024-08-02T17:46:42.637Z" }, + { url = "https://files.pythonhosted.org/packages/27/1e/83fa8a787180e1632c3d831f7e58994d7aaf23a0961320d21e84f922f919/black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed", size = 206504, upload-time = "2024-08-02T17:43:15.747Z" }, +] + +[[package]] +name = "black" +version = "25.11.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.9.*'", +] +dependencies = [ + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "mypy-extensions", marker = "python_full_version == '3.9.*'" }, + { name = "packaging", marker = "python_full_version == '3.9.*'" }, + { name = "pathspec", version = "1.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "pytokens", marker = "python_full_version == '3.9.*'" }, + { name = "tomli", marker = "python_full_version == '3.9.*'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/ad/33adf4708633d047950ff2dfdea2e215d84ac50ef95aff14a614e4b6e9b2/black-25.11.0.tar.gz", hash = "sha256:9a323ac32f5dc75ce7470501b887250be5005a01602e931a15e45593f70f6e08", size = 655669, upload-time = "2025-11-10T01:53:50.558Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/d2/6caccbc96f9311e8ec3378c296d4f4809429c43a6cd2394e3c390e86816d/black-25.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ec311e22458eec32a807f029b2646f661e6859c3f61bc6d9ffb67958779f392e", size = 1743501, upload-time = "2025-11-10T01:59:06.202Z" }, + { url = "https://files.pythonhosted.org/packages/69/35/b986d57828b3f3dccbf922e2864223197ba32e74c5004264b1c62bc9f04d/black-25.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1032639c90208c15711334d681de2e24821af0575573db2810b0763bcd62e0f0", size = 1597308, upload-time = "2025-11-10T01:57:58.633Z" }, + { url = "https://files.pythonhosted.org/packages/39/8e/8b58ef4b37073f52b64a7b2dd8c9a96c84f45d6f47d878d0aa557e9a2d35/black-25.11.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c0f7c461df55cf32929b002335883946a4893d759f2df343389c4396f3b6b37", size = 1656194, upload-time = "2025-11-10T01:57:10.909Z" }, + { url = "https://files.pythonhosted.org/packages/8d/30/9c2267a7955ecc545306534ab88923769a979ac20a27cf618d370091e5dd/black-25.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:f9786c24d8e9bd5f20dc7a7f0cdd742644656987f6ea6947629306f937726c03", size = 1347996, upload-time = "2025-11-10T01:57:22.391Z" }, + { url = "https://files.pythonhosted.org/packages/c4/62/d304786b75ab0c530b833a89ce7d997924579fb7484ecd9266394903e394/black-25.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:895571922a35434a9d8ca67ef926da6bc9ad464522a5fe0db99b394ef1c0675a", size = 1727891, upload-time = "2025-11-10T02:01:40.507Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/ffe8a006aa522c9e3f430e7b93568a7b2163f4b3f16e8feb6d8c3552761a/black-25.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb4f4b65d717062191bdec8e4a442539a8ea065e6af1c4f4d36f0cdb5f71e170", size = 1581875, upload-time = "2025-11-10T01:57:51.192Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c8/7c8bda3108d0bb57387ac41b4abb5c08782b26da9f9c4421ef6694dac01a/black-25.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d81a44cbc7e4f73a9d6ae449ec2317ad81512d1e7dce7d57f6333fd6259737bc", size = 1642716, upload-time = "2025-11-10T01:56:51.589Z" }, + { url = "https://files.pythonhosted.org/packages/34/b9/f17dea34eecb7cc2609a89627d480fb6caea7b86190708eaa7eb15ed25e7/black-25.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:7eebd4744dfe92ef1ee349dc532defbf012a88b087bb7ddd688ff59a447b080e", size = 1352904, upload-time = "2025-11-10T01:59:26.252Z" }, + { url = "https://files.pythonhosted.org/packages/7f/12/5c35e600b515f35ffd737da7febdb2ab66bb8c24d88560d5e3ef3d28c3fd/black-25.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:80e7486ad3535636657aa180ad32a7d67d7c273a80e12f1b4bfa0823d54e8fac", size = 1772831, upload-time = "2025-11-10T02:03:47Z" }, + { url = "https://files.pythonhosted.org/packages/1a/75/b3896bec5a2bb9ed2f989a970ea40e7062f8936f95425879bbe162746fe5/black-25.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6cced12b747c4c76bc09b4db057c319d8545307266f41aaee665540bc0e04e96", size = 1608520, upload-time = "2025-11-10T01:58:46.895Z" }, + { url = "https://files.pythonhosted.org/packages/f3/b5/2bfc18330eddbcfb5aab8d2d720663cd410f51b2ed01375f5be3751595b0/black-25.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb2d54a39e0ef021d6c5eef442e10fd71fcb491be6413d083a320ee768329dd", size = 1682719, upload-time = "2025-11-10T01:56:55.24Z" }, + { url = "https://files.pythonhosted.org/packages/96/fb/f7dc2793a22cdf74a72114b5ed77fe3349a2e09ef34565857a2f917abdf2/black-25.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae263af2f496940438e5be1a0c1020e13b09154f3af4df0835ea7f9fe7bfa409", size = 1362684, upload-time = "2025-11-10T01:57:07.639Z" }, + { url = "https://files.pythonhosted.org/packages/ad/47/3378d6a2ddefe18553d1115e36aea98f4a90de53b6a3017ed861ba1bd3bc/black-25.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a1d40348b6621cc20d3d7530a5b8d67e9714906dfd7346338249ad9c6cedf2b", size = 1772446, upload-time = "2025-11-10T02:02:16.181Z" }, + { url = "https://files.pythonhosted.org/packages/ba/4b/0f00bfb3d1f7e05e25bfc7c363f54dc523bb6ba502f98f4ad3acf01ab2e4/black-25.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:51c65d7d60bb25429ea2bf0731c32b2a2442eb4bd3b2afcb47830f0b13e58bfd", size = 1607983, upload-time = "2025-11-10T02:02:52.502Z" }, + { url = "https://files.pythonhosted.org/packages/99/fe/49b0768f8c9ae57eb74cc10a1f87b4c70453551d8ad498959721cc345cb7/black-25.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:936c4dd07669269f40b497440159a221ee435e3fddcf668e0c05244a9be71993", size = 1682481, upload-time = "2025-11-10T01:57:12.35Z" }, + { url = "https://files.pythonhosted.org/packages/55/17/7e10ff1267bfa950cc16f0a411d457cdff79678fbb77a6c73b73a5317904/black-25.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:f42c0ea7f59994490f4dccd64e6b2dd49ac57c7c84f38b8faab50f8759db245c", size = 1363869, upload-time = "2025-11-10T01:58:24.608Z" }, + { url = "https://files.pythonhosted.org/packages/67/c0/cc865ce594d09e4cd4dfca5e11994ebb51604328489f3ca3ae7bb38a7db5/black-25.11.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:35690a383f22dd3e468c85dc4b915217f87667ad9cce781d7b42678ce63c4170", size = 1771358, upload-time = "2025-11-10T02:03:33.331Z" }, + { url = "https://files.pythonhosted.org/packages/37/77/4297114d9e2fd2fc8ab0ab87192643cd49409eb059e2940391e7d2340e57/black-25.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dae49ef7369c6caa1a1833fd5efb7c3024bb7e4499bf64833f65ad27791b1545", size = 1612902, upload-time = "2025-11-10T01:59:33.382Z" }, + { url = "https://files.pythonhosted.org/packages/de/63/d45ef97ada84111e330b2b2d45e1dd163e90bd116f00ac55927fb6bf8adb/black-25.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bd4a22a0b37401c8e492e994bce79e614f91b14d9ea911f44f36e262195fdda", size = 1680571, upload-time = "2025-11-10T01:57:04.239Z" }, + { url = "https://files.pythonhosted.org/packages/ff/4b/5604710d61cdff613584028b4cb4607e56e148801ed9b38ee7970799dab6/black-25.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:aa211411e94fdf86519996b7f5f05e71ba34835d8f0c0f03c00a26271da02664", size = 1382599, upload-time = "2025-11-10T01:57:57.427Z" }, + { url = "https://files.pythonhosted.org/packages/d5/9a/5b2c0e3215fe748fcf515c2dd34658973a1210bf610e24de5ba887e4f1c8/black-25.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3bb5ce32daa9ff0605d73b6f19da0b0e6c1f8f2d75594db539fdfed722f2b06", size = 1743063, upload-time = "2025-11-10T02:02:43.175Z" }, + { url = "https://files.pythonhosted.org/packages/a1/20/245164c6efc27333409c62ba54dcbfbe866c6d1957c9a6c0647786e950da/black-25.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9815ccee1e55717fe9a4b924cae1646ef7f54e0f990da39a34fc7b264fcf80a2", size = 1596867, upload-time = "2025-11-10T02:00:17.157Z" }, + { url = "https://files.pythonhosted.org/packages/ca/6f/1a3859a7da205f3d50cf3a8bec6bdc551a91c33ae77a045bb24c1f46ab54/black-25.11.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92285c37b93a1698dcbc34581867b480f1ba3a7b92acf1fe0467b04d7a4da0dc", size = 1655678, upload-time = "2025-11-10T01:57:09.028Z" }, + { url = "https://files.pythonhosted.org/packages/56/1a/6dec1aeb7be90753d4fcc273e69bc18bfd34b353223ed191da33f7519410/black-25.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:43945853a31099c7c0ff8dface53b4de56c41294fa6783c0441a8b1d9bf668bc", size = 1347452, upload-time = "2025-11-10T01:57:01.871Z" }, + { url = "https://files.pythonhosted.org/packages/00/5d/aed32636ed30a6e7f9efd6ad14e2a0b0d687ae7c8c7ec4e4a557174b895c/black-25.11.0-py3-none-any.whl", hash = "sha256:e3f562da087791e96cefcd9dda058380a442ab322a02e222add53736451f604b", size = 204918, upload-time = "2025-11-10T01:53:48.917Z" }, +] + +[[package]] +name = "black" +version = "26.3.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +dependencies = [ + { name = "click", version = "8.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "mypy-extensions", marker = "python_full_version >= '3.10'" }, + { name = "packaging", marker = "python_full_version >= '3.10'" }, + { name = "pathspec", version = "1.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "platformdirs", version = "4.9.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pytokens", marker = "python_full_version >= '3.10'" }, + { name = "tomli", marker = "python_full_version == '3.10.*'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/c5/61175d618685d42b005847464b8fb4743a67b1b8fdb75e50e5a96c31a27a/black-26.3.1.tar.gz", hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07", size = 666155, upload-time = "2026-03-12T03:36:03.593Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/a8/11170031095655d36ebc6664fe0897866f6023892396900eec0e8fdc4299/black-26.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:86a8b5035fce64f5dcd1b794cf8ec4d31fe458cf6ce3986a30deb434df82a1d2", size = 1866562, upload-time = "2026-03-12T03:39:58.639Z" }, + { url = "https://files.pythonhosted.org/packages/69/ce/9e7548d719c3248c6c2abfd555d11169457cbd584d98d179111338423790/black-26.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5602bdb96d52d2d0672f24f6ffe5218795736dd34807fd0fd55ccd6bf206168b", size = 1703623, upload-time = "2026-03-12T03:40:00.347Z" }, + { url = "https://files.pythonhosted.org/packages/7f/0a/8d17d1a9c06f88d3d030d0b1d4373c1551146e252afe4547ed601c0e697f/black-26.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c54a4a82e291a1fee5137371ab488866b7c86a3305af4026bdd4dc78642e1ac", size = 1768388, upload-time = "2026-03-12T03:40:01.765Z" }, + { url = "https://files.pythonhosted.org/packages/52/79/c1ee726e221c863cde5164f925bacf183dfdf0397d4e3f94889439b947b4/black-26.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:6e131579c243c98f35bce64a7e08e87fb2d610544754675d4a0e73a070a5aa3a", size = 1412969, upload-time = "2026-03-12T03:40:03.252Z" }, + { url = "https://files.pythonhosted.org/packages/73/a5/15c01d613f5756f68ed8f6d4ec0a1e24b82b18889fa71affd3d1f7fad058/black-26.3.1-cp310-cp310-win_arm64.whl", hash = "sha256:5ed0ca58586c8d9a487352a96b15272b7fa55d139fc8496b519e78023a8dab0a", size = 1220345, upload-time = "2026-03-12T03:40:04.892Z" }, + { url = "https://files.pythonhosted.org/packages/17/57/5f11c92861f9c92eb9dddf515530bc2d06db843e44bdcf1c83c1427824bc/black-26.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:28ef38aee69e4b12fda8dba75e21f9b4f979b490c8ac0baa7cb505369ac9e1ff", size = 1851987, upload-time = "2026-03-12T03:40:06.248Z" }, + { url = "https://files.pythonhosted.org/packages/54/aa/340a1463660bf6831f9e39646bf774086dbd8ca7fc3cded9d59bbdf4ad0a/black-26.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bf162ed91a26f1adba8efda0b573bc6924ec1408a52cc6f82cb73ec2b142c", size = 1689499, upload-time = "2026-03-12T03:40:07.642Z" }, + { url = "https://files.pythonhosted.org/packages/f3/01/b726c93d717d72733da031d2de10b92c9fa4c8d0c67e8a8a372076579279/black-26.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:474c27574d6d7037c1bc875a81d9be0a9a4f9ee95e62800dab3cfaadbf75acd5", size = 1754369, upload-time = "2026-03-12T03:40:09.279Z" }, + { url = "https://files.pythonhosted.org/packages/e3/09/61e91881ca291f150cfc9eb7ba19473c2e59df28859a11a88248b5cbbc4d/black-26.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e9d0d86df21f2e1677cc4bd090cd0e446278bcbbe49bf3659c308c3e402843e", size = 1413613, upload-time = "2026-03-12T03:40:10.943Z" }, + { url = "https://files.pythonhosted.org/packages/16/73/544f23891b22e7efe4d8f812371ab85b57f6a01b2fc45e3ba2e52ba985b8/black-26.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9a5e9f45e5d5e1c5b5c29b3bd4265dcc90e8b92cf4534520896ed77f791f4da5", size = 1219719, upload-time = "2026-03-12T03:40:12.597Z" }, + { url = "https://files.pythonhosted.org/packages/dc/f8/da5eae4fc75e78e6dceb60624e1b9662ab00d6b452996046dfa9b8a6025b/black-26.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e6f89631eb88a7302d416594a32faeee9fb8fb848290da9d0a5f2903519fc1", size = 1895920, upload-time = "2026-03-12T03:40:13.921Z" }, + { url = "https://files.pythonhosted.org/packages/2c/9f/04e6f26534da2e1629b2b48255c264cabf5eedc5141d04516d9d68a24111/black-26.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cd2012d35b47d589cb8a16faf8a32ef7a336f56356babd9fcf70939ad1897f", size = 1718499, upload-time = "2026-03-12T03:40:15.239Z" }, + { url = "https://files.pythonhosted.org/packages/04/91/a5935b2a63e31b331060c4a9fdb5a6c725840858c599032a6f3aac94055f/black-26.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f76ff19ec5297dd8e66eb64deda23631e642c9393ab592826fd4bdc97a4bce7", size = 1794994, upload-time = "2026-03-12T03:40:17.124Z" }, + { url = "https://files.pythonhosted.org/packages/e7/0a/86e462cdd311a3c2a8ece708d22aba17d0b2a0d5348ca34b40cdcbea512e/black-26.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ddb113db38838eb9f043623ba274cfaf7d51d5b0c22ecb30afe58b1bb8322983", size = 1420867, upload-time = "2026-03-12T03:40:18.83Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e5/22515a19cb7eaee3440325a6b0d95d2c0e88dd180cb011b12ae488e031d1/black-26.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:dfdd51fc3e64ea4f35873d1b3fb25326773d55d2329ff8449139ebaad7357efb", size = 1230124, upload-time = "2026-03-12T03:40:20.425Z" }, + { url = "https://files.pythonhosted.org/packages/f5/77/5728052a3c0450c53d9bb3945c4c46b91baa62b2cafab6801411b6271e45/black-26.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:855822d90f884905362f602880ed8b5df1b7e3ee7d0db2502d4388a954cc8c54", size = 1895034, upload-time = "2026-03-12T03:40:21.813Z" }, + { url = "https://files.pythonhosted.org/packages/52/73/7cae55fdfdfbe9d19e9a8d25d145018965fe2079fa908101c3733b0c55a0/black-26.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8a33d657f3276328ce00e4d37fe70361e1ec7614da5d7b6e78de5426cb56332f", size = 1718503, upload-time = "2026-03-12T03:40:23.666Z" }, + { url = "https://files.pythonhosted.org/packages/e1/87/af89ad449e8254fdbc74654e6467e3c9381b61472cc532ee350d28cfdafb/black-26.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1cd08e99d2f9317292a311dfe578fd2a24b15dbce97792f9c4d752275c1fa56", size = 1793557, upload-time = "2026-03-12T03:40:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/43/10/d6c06a791d8124b843bf325ab4ac7d2f5b98731dff84d6064eafd687ded1/black-26.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:c7e72339f841b5a237ff14f7d3880ddd0fc7f98a1199e8c4327f9a4f478c1839", size = 1422766, upload-time = "2026-03-12T03:40:27.14Z" }, + { url = "https://files.pythonhosted.org/packages/59/4f/40a582c015f2d841ac24fed6390bd68f0fc896069ff3a886317959c9daf8/black-26.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc622538b430aa4c8c853f7f63bc582b3b8030fd8c80b70fb5fa5b834e575c2", size = 1232140, upload-time = "2026-03-12T03:40:28.882Z" }, + { url = "https://files.pythonhosted.org/packages/d5/da/e36e27c9cebc1311b7579210df6f1c86e50f2d7143ae4fcf8a5017dc8809/black-26.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2d6bfaf7fd0993b420bed691f20f9492d53ce9a2bcccea4b797d34e947318a78", size = 1889234, upload-time = "2026-03-12T03:40:30.964Z" }, + { url = "https://files.pythonhosted.org/packages/0e/7b/9871acf393f64a5fa33668c19350ca87177b181f44bb3d0c33b2d534f22c/black-26.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f89f2ab047c76a9c03f78d0d66ca519e389519902fa27e7a91117ef7611c0568", size = 1720522, upload-time = "2026-03-12T03:40:32.346Z" }, + { url = "https://files.pythonhosted.org/packages/03/87/e766c7f2e90c07fb7586cc787c9ae6462b1eedab390191f2b7fc7f6170a9/black-26.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b07fc0dab849d24a80a29cfab8d8a19187d1c4685d8a5e6385a5ce323c1f015f", size = 1787824, upload-time = "2026-03-12T03:40:33.636Z" }, + { url = "https://files.pythonhosted.org/packages/ac/94/2424338fb2d1875e9e83eed4c8e9c67f6905ec25afd826a911aea2b02535/black-26.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:0126ae5b7c09957da2bdbd91a9ba1207453feada9e9fe51992848658c6c8e01c", size = 1445855, upload-time = "2026-03-12T03:40:35.442Z" }, + { url = "https://files.pythonhosted.org/packages/86/43/0c3338bd928afb8ee7471f1a4eec3bdbe2245ccb4a646092a222e8669840/black-26.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:92c0ec1f2cc149551a2b7b47efc32c866406b6891b0ee4625e95967c8f4acfb1", size = 1258109, upload-time = "2026-03-12T03:40:36.832Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0d/52d98722666d6fc6c3dd4c76df339501d6efd40e0ff95e6186a7b7f0befd/black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b", size = 207542, upload-time = "2026-03-12T03:36:01.668Z" }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.9.*'", + "python_full_version < '3.9'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] @@ -23,27 +189,97 @@ dependencies = [ { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749 } +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674 }, + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, ] [[package]] name = "iniconfig" version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] [[package]] name = "packaging" version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "pathspec" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", + "python_full_version == '3.9.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302, upload-time = "2024-09-17T19:06:50.688Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439, upload-time = "2024-09-17T19:06:49.212Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.9.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, + { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, ] [[package]] @@ -53,9 +289,9 @@ source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.9'", ] -sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" }, ] [[package]] @@ -63,20 +299,21 @@ name = "pluggy" version = "1.6.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.9'", + "python_full_version >= '3.10'", + "python_full_version == '3.9.*'", ] -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412 } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] [[package]] name = "pygments" version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] [[package]] @@ -85,14 +322,22 @@ source = { editable = "." } [package.dev-dependencies] dev = [ + { name = "black", version = "24.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "black", version = "25.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "black", version = "26.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "pytest", version = "8.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "ruff" }, ] [package.metadata] [package.metadata.requires-dev] -dev = [{ name = "pytest", specifier = ">=8.3.5" }] +dev = [ + { name = "black", specifier = ">=24.8.0" }, + { name = "pytest", specifier = ">=8.3.5" }, + { name = "ruff", specifier = ">=0.15.8" }, +] [[package]] name = "pytest" @@ -109,9 +354,9 @@ dependencies = [ { name = "pluggy", version = "1.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "tomli", marker = "python_full_version < '3.9'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } +sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, + { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" }, ] [[package]] @@ -119,7 +364,8 @@ name = "pytest" version = "8.4.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.9'", + "python_full_version >= '3.10'", + "python_full_version == '3.9.*'", ] dependencies = [ { name = "colorama", marker = "python_full_version >= '3.9' and sys_platform == 'win32'" }, @@ -130,48 +376,122 @@ dependencies = [ { name = "pygments", marker = "python_full_version >= '3.9'" }, { name = "tomli", marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618 } +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, +] + +[[package]] +name = "pytokens" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750 }, + { url = "https://files.pythonhosted.org/packages/42/24/f206113e05cb8ef51b3850e7ef88f20da6f4bf932190ceb48bd3da103e10/pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5", size = 161522, upload-time = "2026-01-30T01:02:50.393Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e9/06a6bf1b90c2ed81a9c7d2544232fe5d2891d1cd480e8a1809ca354a8eb2/pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe", size = 246945, upload-time = "2026-01-30T01:02:52.399Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/f6fb1007a4c3d8b682d5d65b7c1fb33257587a5f782647091e3408abe0b8/pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c", size = 259525, upload-time = "2026-01-30T01:02:53.737Z" }, + { url = "https://files.pythonhosted.org/packages/04/92/086f89b4d622a18418bac74ab5db7f68cf0c21cf7cc92de6c7b919d76c88/pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7", size = 262693, upload-time = "2026-01-30T01:02:54.871Z" }, + { url = "https://files.pythonhosted.org/packages/b4/7b/8b31c347cf94a3f900bdde750b2e9131575a61fdb620d3d3c75832262137/pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2", size = 103567, upload-time = "2026-01-30T01:02:56.414Z" }, + { url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440", size = 160864, upload-time = "2026-01-30T01:02:57.882Z" }, + { url = "https://files.pythonhosted.org/packages/13/25/a4f555281d975bfdd1eba731450e2fe3a95870274da73fb12c40aeae7625/pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc", size = 248565, upload-time = "2026-01-30T01:02:59.912Z" }, + { url = "https://files.pythonhosted.org/packages/17/50/bc0394b4ad5b1601be22fa43652173d47e4c9efbf0044c62e9a59b747c56/pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d", size = 260824, upload-time = "2026-01-30T01:03:01.471Z" }, + { url = "https://files.pythonhosted.org/packages/4e/54/3e04f9d92a4be4fc6c80016bc396b923d2a6933ae94b5f557c939c460ee0/pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16", size = 264075, upload-time = "2026-01-30T01:03:04.143Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1b/44b0326cb5470a4375f37988aea5d61b5cc52407143303015ebee94abfd6/pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6", size = 103323, upload-time = "2026-01-30T01:03:05.412Z" }, + { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, + { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, + { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, + { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, + { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, + { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, + { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, + { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, + { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, + { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, + { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, + { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, + { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/4a/08/968c22e06ab6570788964e2d5a702db9a3816e20ffde380b2b1385541d64/pytokens-0.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:da5baeaf7116dced9c6bb76dc31ba04a2dc3695f3d9f74741d7910122b456edc", size = 154847, upload-time = "2026-01-30T01:03:32.268Z" }, + { url = "https://files.pythonhosted.org/packages/09/2b/2061bb4b300e6921f7968724b185237627a8a3dc4f311e34079dfadf9b65/pytokens-0.4.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11edda0942da80ff58c4408407616a310adecae1ddd22eef8c692fe266fa5009", size = 238610, upload-time = "2026-01-30T01:03:33.809Z" }, + { url = "https://files.pythonhosted.org/packages/1e/64/abf6e43523ea9b4aea69bfe22788a518806741107238674e5c0fb6fc8dc1/pytokens-0.4.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0fc71786e629cef478cbf29d7ea1923299181d0699dbe7c3c0f4a583811d9fc1", size = 252493, upload-time = "2026-01-30T01:03:35.715Z" }, + { url = "https://files.pythonhosted.org/packages/dc/fb/bcb6784c87d1de182afb284f37b07bc172eebec91ddc20e83aec767e4963/pytokens-0.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dcafc12c30dbaf1e2af0490978352e0c4041a7cde31f4f81435c2a5e8b9cabb6", size = 255651, upload-time = "2026-01-30T01:03:36.961Z" }, + { url = "https://files.pythonhosted.org/packages/1a/0c/0c33752be2209498661903f6f240779aea5c9adbd85d22336ce3f7718e81/pytokens-0.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:42f144f3aafa5d92bad964d471a581651e28b24434d184871bd02e3a0d956037", size = 104346, upload-time = "2026-01-30T01:03:38.069Z" }, + { url = "https://files.pythonhosted.org/packages/51/2a/f125667ce48105bf1f4e50e03cfa7b24b8c4f47684d7f1cf4dcb6f6b1c15/pytokens-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:34bcc734bd2f2d5fe3b34e7b3c0116bfb2397f2d9666139988e7a3eb5f7400e3", size = 161464, upload-time = "2026-01-30T01:03:39.11Z" }, + { url = "https://files.pythonhosted.org/packages/40/df/065a30790a7ca6bb48ad9018dd44668ed9135610ebf56a2a4cb8e513fd5c/pytokens-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941d4343bf27b605e9213b26bfa1c4bf197c9c599a9627eb7305b0defcfe40c1", size = 246159, upload-time = "2026-01-30T01:03:40.131Z" }, + { url = "https://files.pythonhosted.org/packages/a5/1c/fd09976a7e04960dabc07ab0e0072c7813d566ec67d5490a4c600683c158/pytokens-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ad72b851e781478366288743198101e5eb34a414f1d5627cdd585ca3b25f1db", size = 259120, upload-time = "2026-01-30T01:03:41.233Z" }, + { url = "https://files.pythonhosted.org/packages/52/49/59fdc6fc5a390ae9f308eadeb97dfc70fc2d804ffc49dd39fc97604622ec/pytokens-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:682fa37ff4d8e95f7df6fe6fe6a431e8ed8e788023c6bcc0f0880a12eab80ad1", size = 262196, upload-time = "2026-01-30T01:03:42.696Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e7/d6734dccf0080e3dc00a55b0827ab5af30c886f8bc127bbc04bc3445daec/pytokens-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:30f51edd9bb7f85c748979384165601d028b84f7bd13fe14d3e065304093916a", size = 103510, upload-time = "2026-01-30T01:03:43.915Z" }, + { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/b0/73cf7550861e2b4824950b8b52eebdcc5adc792a00c514406556c5b80817/ruff-0.15.8.tar.gz", hash = "sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e", size = 4610921, upload-time = "2026-03-26T18:39:38.675Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/92/c445b0cd6da6e7ae51e954939cb69f97e008dbe750cfca89b8cedc081be7/ruff-0.15.8-py3-none-linux_armv6l.whl", hash = "sha256:cbe05adeba76d58162762d6b239c9056f1a15a55bd4b346cfd21e26cd6ad7bc7", size = 10527394, upload-time = "2026-03-26T18:39:41.566Z" }, + { url = "https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570", size = 10905693, upload-time = "2026-03-26T18:39:30.364Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3", size = 10323044, upload-time = "2026-03-26T18:39:33.37Z" }, + { url = "https://files.pythonhosted.org/packages/67/18/1bf38e20914a05e72ef3b9569b1d5c70a7ef26cd188d69e9ca8ef588d5bf/ruff-0.15.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdce027ada77baa448077ccc6ebb2fa9c3c62fd110d8659d601cf2f475858d94", size = 10629135, upload-time = "2026-03-26T18:39:44.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e9/138c150ff9af60556121623d41aba18b7b57d95ac032e177b6a53789d279/ruff-0.15.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12e617fc01a95e5821648a6df341d80456bd627bfab8a829f7cfc26a14a4b4a3", size = 10348041, upload-time = "2026-03-26T18:39:52.178Z" }, + { url = "https://files.pythonhosted.org/packages/02/f1/5bfb9298d9c323f842c5ddeb85f1f10ef51516ac7a34ba446c9347d898df/ruff-0.15.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:432701303b26416d22ba696c39f2c6f12499b89093b61360abc34bcc9bf07762", size = 11121987, upload-time = "2026-03-26T18:39:55.195Z" }, + { url = "https://files.pythonhosted.org/packages/10/11/6da2e538704e753c04e8d86b1fc55712fdbdcc266af1a1ece7a51fff0d10/ruff-0.15.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d910ae974b7a06a33a057cb87d2a10792a3b2b3b35e33d2699fdf63ec8f6b17a", size = 11951057, upload-time = "2026-03-26T18:39:19.18Z" }, + { url = "https://files.pythonhosted.org/packages/83/f0/c9208c5fd5101bf87002fed774ff25a96eea313d305f1e5d5744698dc314/ruff-0.15.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2033f963c43949d51e6fdccd3946633c6b37c484f5f98c3035f49c27395a8ab8", size = 11464613, upload-time = "2026-03-26T18:40:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1", size = 11257557, upload-time = "2026-03-26T18:39:57.972Z" }, + { url = "https://files.pythonhosted.org/packages/71/8c/382a9620038cf6906446b23ce8632ab8c0811b8f9d3e764f58bedd0c9a6f/ruff-0.15.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:ac51d486bf457cdc985a412fb1801b2dfd1bd8838372fc55de64b1510eff4bec", size = 11169440, upload-time = "2026-03-26T18:39:22.205Z" }, + { url = "https://files.pythonhosted.org/packages/4d/0d/0994c802a7eaaf99380085e4e40c845f8e32a562e20a38ec06174b52ef24/ruff-0.15.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c9861eb959edab053c10ad62c278835ee69ca527b6dcd72b47d5c1e5648964f6", size = 10605963, upload-time = "2026-03-26T18:39:46.682Z" }, + { url = "https://files.pythonhosted.org/packages/19/aa/d624b86f5b0aad7cef6bbf9cd47a6a02dfdc4f72c92a337d724e39c9d14b/ruff-0.15.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8d9a5b8ea13f26ae90838afc33f91b547e61b794865374f114f349e9036835fb", size = 10357484, upload-time = "2026-03-26T18:39:49.176Z" }, + { url = "https://files.pythonhosted.org/packages/35/c3/e0b7835d23001f7d999f3895c6b569927c4d39912286897f625736e1fd04/ruff-0.15.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c2a33a529fb3cbc23a7124b5c6ff121e4d6228029cba374777bd7649cc8598b8", size = 10830426, upload-time = "2026-03-26T18:40:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/f0/51/ab20b322f637b369383adc341d761eaaa0f0203d6b9a7421cd6e783d81b9/ruff-0.15.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:75e5cd06b1cf3f47a3996cfc999226b19aa92e7cce682dcd62f80d7035f98f49", size = 11345125, upload-time = "2026-03-26T18:39:27.799Z" }, + { url = "https://files.pythonhosted.org/packages/37/e6/90b2b33419f59d0f2c4c8a48a4b74b460709a557e8e0064cf33ad894f983/ruff-0.15.8-py3-none-win32.whl", hash = "sha256:bc1f0a51254ba21767bfa9a8b5013ca8149dcf38092e6a9eb704d876de94dc34", size = 10571959, upload-time = "2026-03-26T18:39:36.117Z" }, + { url = "https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl", hash = "sha256:04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89", size = 11743893, upload-time = "2026-03-26T18:39:25.01Z" }, + { url = "https://files.pythonhosted.org/packages/15/e2/77be4fff062fa78d9b2a4dea85d14785dac5f1d0c1fb58ed52331f0ebe28/ruff-0.15.8-py3-none-win_arm64.whl", hash = "sha256:cf891fa8e3bb430c0e7fac93851a5978fc99c8fa2c053b57b118972866f8e5f2", size = 11048175, upload-time = "2026-03-26T18:40:01.06Z" }, ] [[package]] name = "tomli" version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 }, - { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 }, - { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 }, - { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 }, - { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 }, - { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 }, - { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 }, - { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 }, - { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 }, - { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 }, - { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 }, - { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 }, - { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 }, - { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 }, - { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 }, - { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 }, - { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 }, - { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 }, - { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 }, - { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 }, - { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 }, - { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 }, - { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 }, - { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 }, - { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 }, - { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 }, - { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 }, - { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 }, - { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 }, - { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 }, - { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, ] [[package]] @@ -181,9 +501,9 @@ source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.9'", ] -sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } +sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, + { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, ] [[package]] @@ -191,9 +511,10 @@ name = "typing-extensions" version = "4.15.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.9'", + "python_full_version >= '3.10'", + "python_full_version == '3.9.*'", ] -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ]