From 4bc2258409fb6e1e86c26fe15b89be12e940e368 Mon Sep 17 00:00:00 2001 From: Taylor Jones Date: Wed, 21 Oct 2026 16:02:35 -0500 Subject: [PATCH] python(feat): Support additional configurations for sift-stream-bindings --- ...+stream-metrics-rate-limit-options.feat.md | 3 + .../_internal/low_level_wrappers/ingestion.py | 30 ++++- .../test_ingestion_stream_builder.py | 99 +++++++++++++++ .../_tests/resources/test_ingestion.py | 44 +++++++ python/lib/sift_client/resources/ingestion.py | 70 +++++++++++ python/pyproject.toml | 12 +- python/uv.lock | 114 +++++++++--------- 7 files changed, 308 insertions(+), 64 deletions(-) create mode 100644 python/changelog.d/+stream-metrics-rate-limit-options.feat.md create mode 100644 python/lib/sift_client/_tests/_internal/low_level_wrappers/test_ingestion_stream_builder.py create mode 100644 python/lib/sift_client/_tests/resources/test_ingestion.py diff --git a/python/changelog.d/+stream-metrics-rate-limit-options.feat.md b/python/changelog.d/+stream-metrics-rate-limit-options.feat.md new file mode 100644 index 0000000000..b26c8a5994 --- /dev/null +++ b/python/changelog.d/+stream-metrics-rate-limit-options.feat.md @@ -0,0 +1,3 @@ +Added `metrics_streaming_interval` and `rate_limit_policy` to `create_ingestion_config_streaming_client` and `create_auto_register_streaming_client`. + +`metrics_streaming_interval` takes a `timedelta` and defaults to 500 ms. Pass `None` to disable metrics streaming. `rate_limit_policy` takes a `RateLimitPolicyPy` from `sift_stream_bindings` and controls how rate-limited control-plane calls, such as stream setup, `attach_run`, and flow registration, are handled. By default, the stream waits the delay Sift requests before retrying. When a rate limit is not retried, or the retries run out, these calls raise `RateLimitedErrorPy`, and `retry_delay.to_timedelta()` returns the delay Sift requested. This release requires `sift-stream-bindings` 0.6.1. diff --git a/python/lib/sift_client/_internal/low_level_wrappers/ingestion.py b/python/lib/sift_client/_internal/low_level_wrappers/ingestion.py index f99b752603..8cc0e57d7b 100644 --- a/python/lib/sift_client/_internal/low_level_wrappers/ingestion.py +++ b/python/lib/sift_client/_internal/low_level_wrappers/ingestion.py @@ -11,6 +11,7 @@ import hashlib import logging from collections import namedtuple +from datetime import timedelta from typing import TYPE_CHECKING, Any, Iterable, cast from sift.ingestion_configs.v2.ingestion_configs_pb2 import ( @@ -35,6 +36,9 @@ DEFAULT_INGESTION_CONFIG_PAGE_SIZE = 100 """Default page size for ingestion config and flow list calls (flow configs can be large).""" +DEFAULT_METRICS_STREAMING_INTERVAL = timedelta(milliseconds=500) +"""Default interval at which SiftStream pushes stream metrics to Sift. Matches the SiftStream default.""" + if TYPE_CHECKING: from datetime import datetime @@ -47,6 +51,7 @@ IngestWithConfigDataStreamRequestPy, IngestWithConfigDataStreamRequestWrapperPy, MetadataPy, + RateLimitPolicyPy, RetryPolicyPy, RunFormPy, RunSelectorPy, @@ -248,6 +253,8 @@ async def _build_sift_stream_instance( retry_policy: RetryPolicyPy | None = None, disk_backup_policy: DiskBackupPolicyPy | None = None, checkpoint_interval_seconds: int | None = None, + metrics_streaming_interval: timedelta | None = DEFAULT_METRICS_STREAMING_INTERVAL, + rate_limit_policy: RateLimitPolicyPy | None = None, enable_tls: bool = True, tracing_config: TracingConfig | None = None, ) -> SiftStreamPy: @@ -273,6 +280,17 @@ async def _build_sift_stream_instance( if streaming_mode is ...: # type: ignore[comparison-overlap] streaming_mode = StreamingMode.LIVE_WITH_BACKUPS + # None disables metrics streaming, so it passes through to the builder as-is. Zero is + # rejected here because SiftStream cannot schedule metrics on a zero interval. + metrics_interval: DurationPy | None = None + if metrics_streaming_interval is not None: + if metrics_streaming_interval <= timedelta(0): + raise ValueError( + "metrics_streaming_interval must be greater than zero, " + f"got {metrics_streaming_interval}" + ) + metrics_interval = DurationPy.from_timedelta(metrics_streaming_interval) + if not is_tracing_initialized(): if tracing_config is None: tracing_config = TracingConfig.with_file() @@ -295,18 +313,24 @@ async def _build_sift_stream_instance( config_builder.run_id = run_id config_builder.asset_tags = asset_tags config_builder.metadata = asset_metadata + if rate_limit_policy is not None: + config_builder.rate_limit_policy = rate_limit_policy if streaming_mode == StreamingMode.LIVE_ONLY: - return await config_builder.live_only().build() + lo_builder = config_builder.live_only() + lo_builder.metrics_streaming_interval = metrics_interval + return await lo_builder.build() elif streaming_mode == StreamingMode.FILE_BACKUP: fb_builder = config_builder.file_backup() + fb_builder.metrics_streaming_interval = metrics_interval if disk_backup_policy is not None: fb_builder.disk_backup_policy = disk_backup_policy return await fb_builder.build() else: # LIVE_WITH_BACKUPS (default) lwb_builder = config_builder.live_with_backups() + lwb_builder.metrics_streaming_interval = metrics_interval if retry_policy is not None: lwb_builder.retry_policy = retry_policy if disk_backup_policy is not None: @@ -339,6 +363,8 @@ async def create_sift_stream_instance( retry_policy: RetryPolicyPy | None = None, disk_backup_policy: DiskBackupPolicyPy | None = None, checkpoint_interval_seconds: int | None = None, + metrics_streaming_interval: timedelta | None = DEFAULT_METRICS_STREAMING_INTERVAL, + rate_limit_policy: RateLimitPolicyPy | None = None, enable_tls: bool = True, tracing_config: TracingConfig | None = None, ) -> IngestionConfigStreamingLowLevelClient: @@ -354,6 +380,8 @@ async def create_sift_stream_instance( retry_policy=retry_policy, disk_backup_policy=disk_backup_policy, checkpoint_interval_seconds=checkpoint_interval_seconds, + metrics_streaming_interval=metrics_streaming_interval, + rate_limit_policy=rate_limit_policy, enable_tls=enable_tls, tracing_config=tracing_config, ) diff --git a/python/lib/sift_client/_tests/_internal/low_level_wrappers/test_ingestion_stream_builder.py b/python/lib/sift_client/_tests/_internal/low_level_wrappers/test_ingestion_stream_builder.py new file mode 100644 index 0000000000..4ab0cf7002 --- /dev/null +++ b/python/lib/sift_client/_tests/_internal/low_level_wrappers/test_ingestion_stream_builder.py @@ -0,0 +1,99 @@ +"""Tests for how `_build_sift_stream_instance` configures the SiftStream builders.""" + +from datetime import timedelta +from unittest.mock import AsyncMock, MagicMock + +import pytest + +from sift_client._internal.low_level_wrappers.ingestion import _build_sift_stream_instance +from sift_client.resources.ingestion import StreamingMode + +sift_stream_bindings = pytest.importorskip("sift_stream_bindings") + +MODE_BUILDER_METHODS = { + StreamingMode.LIVE_ONLY: "live_only", + StreamingMode.LIVE_WITH_BACKUPS: "live_with_backups", + StreamingMode.FILE_BACKUP: "file_backup", +} + + +@pytest.fixture +def config_builder(monkeypatch): + """Replace `SiftStreamBuilderPy` with a mock and return the config builder it produces.""" + sift_builder = MagicMock() + config_builder = sift_builder.ingestion_config.return_value + for method in MODE_BUILDER_METHODS.values(): + getattr(config_builder, method).return_value.build = AsyncMock() + + monkeypatch.setattr( + sift_stream_bindings, "SiftStreamBuilderPy", MagicMock(return_value=sift_builder) + ) + monkeypatch.setattr(sift_stream_bindings, "is_tracing_initialized", lambda: True) + return config_builder + + +async def _build(**kwargs): + await _build_sift_stream_instance( + api_key="api-key", + grpc_uri="localhost:50051", + ingestion_config_form=MagicMock(), + **kwargs, + ) + + +@pytest.mark.parametrize("streaming_mode", list(MODE_BUILDER_METHODS)) +class TestMetricsStreamingInterval: + @pytest.mark.asyncio + async def test_defaults_to_500_ms(self, config_builder, streaming_mode): + await _build(streaming_mode=streaming_mode) + + mode_builder = getattr(config_builder, MODE_BUILDER_METHODS[streaming_mode]).return_value + assert mode_builder.metrics_streaming_interval.to_timedelta() == timedelta(milliseconds=500) + mode_builder.build.assert_awaited_once() + + @pytest.mark.asyncio + async def test_custom_interval(self, config_builder, streaming_mode): + await _build( + streaming_mode=streaming_mode, + metrics_streaming_interval=timedelta(seconds=2, milliseconds=250), + ) + + mode_builder = getattr(config_builder, MODE_BUILDER_METHODS[streaming_mode]).return_value + assert mode_builder.metrics_streaming_interval.to_timedelta() == timedelta( + seconds=2, milliseconds=250 + ) + + @pytest.mark.asyncio + async def test_none_disables_metrics_streaming(self, config_builder, streaming_mode): + await _build(streaming_mode=streaming_mode, metrics_streaming_interval=None) + + mode_builder = getattr(config_builder, MODE_BUILDER_METHODS[streaming_mode]).return_value + assert mode_builder.metrics_streaming_interval is None + mode_builder.build.assert_awaited_once() + + @pytest.mark.asyncio + @pytest.mark.parametrize("interval", [timedelta(0), timedelta(seconds=-1)]) + async def test_rejects_non_positive_interval(self, config_builder, streaming_mode, interval): + with pytest.raises(ValueError, match="greater than zero"): + await _build(streaming_mode=streaming_mode, metrics_streaming_interval=interval) + + mode_builder = getattr(config_builder, MODE_BUILDER_METHODS[streaming_mode]).return_value + mode_builder.build.assert_not_awaited() + + +class TestRateLimitPolicy: + @pytest.mark.asyncio + async def test_sets_policy_on_config_builder(self, config_builder): + policy = sift_stream_bindings.RateLimitPolicyPy.return_error() + + await _build(rate_limit_policy=policy) + + assert config_builder.rate_limit_policy is policy + + @pytest.mark.asyncio + async def test_leaves_default_when_none(self, config_builder): + await _build() + + assert not isinstance( + config_builder.rate_limit_policy, sift_stream_bindings.RateLimitPolicyPy + ) diff --git a/python/lib/sift_client/_tests/resources/test_ingestion.py b/python/lib/sift_client/_tests/resources/test_ingestion.py new file mode 100644 index 0000000000..192acbc9ef --- /dev/null +++ b/python/lib/sift_client/_tests/resources/test_ingestion.py @@ -0,0 +1,44 @@ +"""Tests that rate-limit errors from SiftStream reach callers of the streaming clients unchanged.""" + +from datetime import timedelta +from unittest.mock import AsyncMock, MagicMock + +import pytest + +from sift_client.resources.ingestion import ( + AutoRegisterStreamingClient, + IngestionConfigStreamingClient, +) + +sift_stream_bindings = pytest.importorskip("sift_stream_bindings") + + +def _rate_limited_error(): + return sift_stream_bindings.RateLimitedErrorPy( + "[RateLimitedError] rate limited", + sift_stream_bindings.DurationPy.from_timedelta(timedelta(seconds=2, milliseconds=500)), + ) + + +class TestRateLimitedErrorPropagation: + @pytest.mark.asyncio + async def test_ingestion_config_client_attach_run(self): + low_level_client = MagicMock() + low_level_client.attach_run = AsyncMock(side_effect=_rate_limited_error()) + client = IngestionConfigStreamingClient(MagicMock(), low_level_client) + + with pytest.raises(sift_stream_bindings.RateLimitedErrorPy) as exc_info: + await client.attach_run("run-id") + + assert exc_info.value.retry_delay.to_timedelta() == timedelta(seconds=2, milliseconds=500) + + @pytest.mark.asyncio + async def test_auto_register_client_send(self): + stream = MagicMock() + stream.send = AsyncMock(side_effect=_rate_limited_error()) + client = AutoRegisterStreamingClient(MagicMock(), stream) + + with pytest.raises(sift_stream_bindings.RateLimitedErrorPy) as exc_info: + await client.send(MagicMock()) + + assert exc_info.value.retry_delay.to_timedelta() == timedelta(seconds=2, milliseconds=500) diff --git a/python/lib/sift_client/resources/ingestion.py b/python/lib/sift_client/resources/ingestion.py index 10ad62da97..462228451e 100644 --- a/python/lib/sift_client/resources/ingestion.py +++ b/python/lib/sift_client/resources/ingestion.py @@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Iterator from sift_client._internal.low_level_wrappers.ingestion import ( + DEFAULT_METRICS_STREAMING_INTERVAL, IngestionConfigStreamingLowLevelClient, IngestionLowLevelClient, _build_sift_stream_instance, @@ -16,6 +17,7 @@ if TYPE_CHECKING: from collections.abc import Iterable + from datetime import timedelta from sift_stream_bindings import ( DiskBackupPolicyPy, @@ -25,6 +27,7 @@ IngestWithConfigDataStreamRequestPy, IngestWithConfigDataStreamRequestWrapperPy, MetadataPy, + RateLimitPolicyPy, RetryPolicyPy, RunFormPy, RunSelectorPy, @@ -160,6 +163,8 @@ async def create_ingestion_config_streaming_client( retry_policy: RetryPolicyPy | None = None, disk_backup_policy: DiskBackupPolicyPy | None = None, checkpoint_interval_seconds: int | None = None, + metrics_streaming_interval: timedelta | None = DEFAULT_METRICS_STREAMING_INTERVAL, + rate_limit_policy: RateLimitPolicyPy | None = None, enable_tls: bool = True, tracing_config: TracingConfig | None = None, ) -> IngestionConfigStreamingClient: @@ -174,6 +179,13 @@ async def create_ingestion_config_streaming_client( retry_policy: Retry policy for LIVE_WITH_BACKUPS mode. disk_backup_policy: Disk backup policy for LIVE_WITH_BACKUPS or FILE_BACKUP mode. checkpoint_interval_seconds: Checkpoint interval in seconds (LIVE_WITH_BACKUPS only). + metrics_streaming_interval: How often stream metrics are pushed to Sift. Defaults to + 500 ms. Must be greater than zero. Set to None to disable metrics streaming. + rate_limit_policy: How rate-limited control-plane calls are handled, such as stream + setup, `attach_run`, and flow registration. Defaults to `RateLimitPolicyPy.retry()`, + which waits the delay Sift requests before retrying. Use + `RateLimitPolicyPy.return_error()` to raise `RateLimitedErrorPy` immediately. + Rate limits while streaming data are handled by `retry_policy`. enable_tls: Whether to enable TLS for the connection. tracing_config: Configuration for SiftStream tracing. Use TracingConfig.console_only() to enable tracing to stdout only, or TracingConfig.with_file() to enable @@ -182,6 +194,11 @@ async def create_ingestion_config_streaming_client( Returns: An initialized IngestionConfigStreamingClient. + + Raises: + RateLimitedErrorPy: If Sift rate limits stream setup and `rate_limit_policy` does not + retry, or the retries run out. When `retry_delay` is not None, + `retry_delay.to_timedelta()` returns the delay Sift requested. """ return await IngestionConfigStreamingClient._create( self.client, @@ -193,6 +210,8 @@ async def create_ingestion_config_streaming_client( retry_policy=retry_policy, disk_backup_policy=disk_backup_policy, checkpoint_interval_seconds=checkpoint_interval_seconds, + metrics_streaming_interval=metrics_streaming_interval, + rate_limit_policy=rate_limit_policy, enable_tls=enable_tls, tracing_config=tracing_config, ) @@ -208,6 +227,8 @@ async def create_auto_register_streaming_client( retry_policy: RetryPolicyPy | None = None, disk_backup_policy: DiskBackupPolicyPy | None = None, checkpoint_interval_seconds: int | None = None, + metrics_streaming_interval: timedelta | None = DEFAULT_METRICS_STREAMING_INTERVAL, + rate_limit_policy: RateLimitPolicyPy | None = None, enable_tls: bool = True, tracing_config: TracingConfig | None = None, staged_configs: list[FlowConfig] | None = None, @@ -239,6 +260,13 @@ async def create_auto_register_streaming_client( retry_policy: Retry policy for LIVE_WITH_BACKUPS mode. disk_backup_policy: Disk backup policy for LIVE_WITH_BACKUPS or FILE_BACKUP mode. checkpoint_interval_seconds: Checkpoint interval in seconds (LIVE_WITH_BACKUPS only). + metrics_streaming_interval: How often stream metrics are pushed to Sift. Defaults to + 500 ms. Must be greater than zero. Set to None to disable metrics streaming. + rate_limit_policy: How rate-limited control-plane calls are handled, such as stream + setup, `attach_run`, and flow registration. Defaults to `RateLimitPolicyPy.retry()`, + which waits the delay Sift requests before retrying. Use + `RateLimitPolicyPy.return_error()` to raise `RateLimitedErrorPy` immediately. + Rate limits while streaming data are handled by `retry_policy`. enable_tls: Whether to enable TLS for the connection. tracing_config: Configuration for SiftStream tracing. staged_configs: Optional flow configs to use when auto-registering flows for the first @@ -249,6 +277,11 @@ async def create_auto_register_streaming_client( Returns: An initialized `AutoRegisterStreamingClient`. + + Raises: + RateLimitedErrorPy: If Sift rate limits stream setup and `rate_limit_policy` does not + retry, or the retries run out. When `retry_delay` is not None, + `retry_delay.to_timedelta()` returns the delay Sift requested. """ return await AutoRegisterStreamingClient._create( self.client, @@ -260,6 +293,8 @@ async def create_auto_register_streaming_client( retry_policy=retry_policy, disk_backup_policy=disk_backup_policy, checkpoint_interval_seconds=checkpoint_interval_seconds, + metrics_streaming_interval=metrics_streaming_interval, + rate_limit_policy=rate_limit_policy, enable_tls=enable_tls, tracing_config=tracing_config, staged_configs=staged_configs, @@ -296,6 +331,8 @@ async def _create( retry_policy: RetryPolicyPy | None = None, disk_backup_policy: DiskBackupPolicyPy | None = None, checkpoint_interval_seconds: int | None = None, + metrics_streaming_interval: timedelta | None = DEFAULT_METRICS_STREAMING_INTERVAL, + rate_limit_policy: RateLimitPolicyPy | None = None, enable_tls: bool = True, tracing_config: TracingConfig | None = None, ) -> IngestionConfigStreamingClient: @@ -311,6 +348,13 @@ async def _create( retry_policy: Retry policy for LIVE_WITH_BACKUPS mode. disk_backup_policy: Disk backup policy for LIVE_WITH_BACKUPS or FILE_BACKUP mode. checkpoint_interval_seconds: Checkpoint interval in seconds (LIVE_WITH_BACKUPS only). + metrics_streaming_interval: How often stream metrics are pushed to Sift. Defaults to + 500 ms. Must be greater than zero. Set to None to disable metrics streaming. + rate_limit_policy: How rate-limited control-plane calls are handled, such as stream + setup, `attach_run`, and flow registration. Defaults to `RateLimitPolicyPy.retry()`, + which waits the delay Sift requests before retrying. Use + `RateLimitPolicyPy.return_error()` to raise `RateLimitedErrorPy` immediately. + Rate limits while streaming data are handled by `retry_policy`. enable_tls: Whether to enable TLS for the connection. tracing_config: Configuration for SiftStream tracing. Use TracingConfig.console_only() to enable tracing to stdout only, or TracingConfig.with_file() to enable @@ -393,6 +437,8 @@ async def _create( retry_policy=retry_policy, disk_backup_policy=disk_backup_policy, checkpoint_interval_seconds=checkpoint_interval_seconds, + metrics_streaming_interval=metrics_streaming_interval, + rate_limit_policy=rate_limit_policy, enable_tls=enable_tls, tracing_config=tracing_config, ) @@ -509,6 +555,11 @@ async def add_new_flows(self, flow_configs: list[FlowConfig]): Args: flow_configs: List of flow configurations to add to the ingestion config. + + Raises: + RateLimitedErrorPy: If Sift rate limits the registration and the stream's + `rate_limit_policy` does not retry, or the retries run out. When `retry_delay` + is not None, `retry_delay.to_timedelta()` returns the delay Sift requested. """ flow_configs_py = [flow_config._to_rust_config() for flow_config in flow_configs] await self._low_level_client.add_new_flows(flow_configs_py) @@ -522,6 +573,11 @@ async def attach_run(self, run: RunCreate | dict | str | Run | RunFormPy): Args: run: The run to attach. Can be a Run, RunCreate, dict, run ID string, or RunFormPy. + + Raises: + RateLimitedErrorPy: If Sift rate limits the run lookup or creation and the stream's + `rate_limit_policy` does not retry, or the retries run out. When `retry_delay` + is not None, `retry_delay.to_timedelta()` returns the delay Sift requested. """ run_selector_py = _to_run_selector(run) await self._low_level_client.attach_run(run_selector_py) @@ -634,6 +690,8 @@ async def _create( retry_policy: RetryPolicyPy | None = None, disk_backup_policy: DiskBackupPolicyPy | None = None, checkpoint_interval_seconds: int | None = None, + metrics_streaming_interval: timedelta | None = DEFAULT_METRICS_STREAMING_INTERVAL, + rate_limit_policy: RateLimitPolicyPy | None = None, enable_tls: bool = True, tracing_config: TracingConfig | None = None, staged_configs: list[FlowConfig] | None = None, @@ -701,6 +759,8 @@ async def _create( retry_policy=retry_policy, disk_backup_policy=disk_backup_policy, checkpoint_interval_seconds=checkpoint_interval_seconds, + metrics_streaming_interval=metrics_streaming_interval, + rate_limit_policy=rate_limit_policy, enable_tls=enable_tls, tracing_config=tracing_config, ) @@ -725,6 +785,11 @@ async def send(self, flow: Flow | FlowPy) -> None: Args: flow: The flow to send. + + Raises: + RateLimitedErrorPy: If Sift rate limits registering a new flow and the stream's + `rate_limit_policy` does not retry, or the retries run out. When `retry_delay` + is not None, `retry_delay.to_timedelta()` returns the delay Sift requested. """ flow_py = flow._to_rust_form() if isinstance(flow, Flow) else flow await self._stream.send(flow_py) @@ -747,6 +812,11 @@ async def attach_run(self, run: RunCreate | dict | str | Run | RunFormPy) -> Non Args: run: The run to attach. Can be a Run, RunCreate, dict, run ID string, or RunFormPy. + + Raises: + RateLimitedErrorPy: If Sift rate limits the run lookup or creation and the stream's + `rate_limit_policy` does not retry, or the retries run out. When `retry_delay` + is not None, `retry_delay.to_timedelta()` returns the delay Sift requested. """ await self._stream.attach_run(_to_run_selector(run)) diff --git a/python/pyproject.toml b/python/pyproject.toml index 4de26b4686..0b87d89ff0 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -71,7 +71,7 @@ all = [ 'pyarrow>=17.0.0', 'pyulog~=1.2.2', "rosbags~=0.0 ; python_full_version >= '3.8.2'", - 'sift-stream-bindings==0.5.2', + 'sift-stream-bindings==0.6.1', 'types-pyOpenSSL<24.0.0', ] build = [ @@ -119,7 +119,7 @@ dev-all = [ 'pyulog~=1.2.2', "rosbags~=0.0 ; python_full_version >= '3.8.2'", 'ruff~=0.12.10', - 'sift-stream-bindings==0.5.2', + 'sift-stream-bindings==0.6.1', 'tomlkit~=0.13.3', 'types-pyOpenSSL<24.0.0', ] @@ -179,7 +179,7 @@ docs-build = [ 'pyulog~=1.2.2', "rosbags~=0.0 ; python_full_version >= '3.8.2'", 'ruff~=0.12.10', - 'sift-stream-bindings==0.5.2', + 'sift-stream-bindings==0.6.1', 'tomlkit~=0.13.3', 'types-pyOpenSSL<24.0.0', ] @@ -209,10 +209,10 @@ rosbags = [ "rosbags~=0.0 ; python_full_version >= '3.8.2'", ] sift-stream = [ - 'sift-stream-bindings==0.5.2', + 'sift-stream-bindings==0.6.1', ] sift-stream-bindings = [ - 'sift-stream-bindings==0.5.2', + 'sift-stream-bindings==0.6.1', ] tdms = [ 'npTDMS~=1.9', @@ -260,7 +260,7 @@ docs = [ openssl = ["pyOpenSSL<24.0.0", "types-pyOpenSSL<24.0.0", "cffi~=1.14"] tdms = ["npTDMS~=1.9"] rosbags = ["rosbags~=0.0 ; python_full_version >= '3.8.2'"] -sift-stream = ["sift-stream-bindings==0.5.2"] +sift-stream = ["sift-stream-bindings==0.6.1"] hdf5 = ["h5py~=3.11", "polars~=1.8"] # polars is only used by sift_py; remove once sift_py is fully deprecated ulog = ["pyulog~=1.2.2"] mcap = ["mcap~=1.4", "mcap-ros2-support~=0.5.7"] diff --git a/python/uv.lock b/python/uv.lock index 24a158147f..2cdcf262d8 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -4838,11 +4838,11 @@ requires-dist = [ { name = "ruff", marker = "extra == 'dev-all'", specifier = "~=0.12.10" }, { name = "ruff", marker = "extra == 'development'", specifier = "~=0.12.10" }, { name = "ruff", marker = "extra == 'docs-build'", specifier = "~=0.12.10" }, - { name = "sift-stream-bindings", marker = "extra == 'all'", specifier = "==0.5.2" }, - { name = "sift-stream-bindings", marker = "extra == 'dev-all'", specifier = "==0.5.2" }, - { name = "sift-stream-bindings", marker = "extra == 'docs-build'", specifier = "==0.5.2" }, - { name = "sift-stream-bindings", marker = "extra == 'sift-stream'", specifier = "==0.5.2" }, - { name = "sift-stream-bindings", marker = "extra == 'sift-stream-bindings'", specifier = "==0.5.2" }, + { name = "sift-stream-bindings", marker = "extra == 'all'", specifier = "==0.6.1" }, + { name = "sift-stream-bindings", marker = "extra == 'dev-all'", specifier = "==0.6.1" }, + { name = "sift-stream-bindings", marker = "extra == 'docs-build'", specifier = "==0.6.1" }, + { name = "sift-stream-bindings", marker = "extra == 'sift-stream'", specifier = "==0.6.1" }, + { name = "sift-stream-bindings", marker = "extra == 'sift-stream-bindings'", specifier = "==0.6.1" }, { name = "tomli", marker = "python_full_version < '3.11'", specifier = "~=2.0" }, { name = "tomlkit", marker = "extra == 'dev'", specifier = "~=0.13.3" }, { name = "tomlkit", marker = "extra == 'dev-all'", specifier = "~=0.13.3" }, @@ -4861,59 +4861,59 @@ provides-extras = ["all", "build", "data-review", "dev", "dev-all", "development [[package]] name = "sift-stream-bindings" -version = "0.5.2" +version = "0.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d2/45/10c47b357e63258a0e2337eb1cb8aa2b06d728cd854c154c5fc01b78cd5e/sift_stream_bindings-0.5.2.tar.gz", hash = "sha256:33f0724d411ee063478ac98a5984357c115bba951a8d58981e134ae2d4b487a9", size = 1512781, upload-time = "2026-09-29T23:41:00.486Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/ba/450c9375fd29d7303d5b68cc87c4082eeccb9216b7f8cd4651029be1d0c4/sift_stream_bindings-0.5.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cf31451b9fc536705d7218777a395e22abde8fae74bfd973c602ca379aa4c37d", size = 3773543, upload-time = "2026-09-29T23:39:23.548Z" }, - { url = "https://files.pythonhosted.org/packages/c2/9d/a78b844eb237d1f6ed88f3541973e69d5c9064d872e5da071961f010f506/sift_stream_bindings-0.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce8f865a8e3c440dd0dcf673f238d8ca34a0cb587f01b1256eb68b82ae8ced6e", size = 3718988, upload-time = "2026-09-29T23:39:25.916Z" }, - { url = "https://files.pythonhosted.org/packages/40/59/c9a0b324bd039c473553adbe89ded8b17c32c1a086e85fc56239a6e1e7d5/sift_stream_bindings-0.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:261c7a04649bdd23ce4e4e55d5781cb699b53c8496c92c6bd6089c9c0425f7c9", size = 4293715, upload-time = "2026-09-29T23:39:28.587Z" }, - { url = "https://files.pythonhosted.org/packages/d8/9a/6045c847eb916fc95b3c19ce4ff7be89c5456aa6a71e9a777ef39eef2515/sift_stream_bindings-0.5.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d573fe7b898f1ba3544f87db228f6894f6913db057400e72a38b1e8e5f3218ad", size = 4018753, upload-time = "2026-09-29T23:39:30.736Z" }, - { url = "https://files.pythonhosted.org/packages/67/d0/25079f27469216cd35e69ee8aa7926180478590fe4444493e13131fc39b4/sift_stream_bindings-0.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d287399e48217d1bc4d90c169b988b919776bdf6812e4c8fbaa0f111ebbb9020", size = 4085613, upload-time = "2026-09-29T23:39:32.526Z" }, - { url = "https://files.pythonhosted.org/packages/70/2e/44d6a6496d506d202d481e6e31e7065fa28396f41ca045dd8e005d1f69f7/sift_stream_bindings-0.5.2-cp310-cp310-win32.whl", hash = "sha256:120491dfc0c5f73da7a19744cd92f8d508b1231fa07176d7331870a48519afc3", size = 2979033, upload-time = "2026-09-29T23:39:34.756Z" }, - { url = "https://files.pythonhosted.org/packages/21/54/814aa6b9c959fd590f7d8b95782b31a264cb536018d86828b0b1d673a216/sift_stream_bindings-0.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:606a8479930ec69e6138769824505ddd0102468913400963bd8ac1b4e094f403", size = 3569663, upload-time = "2026-09-29T23:39:36.788Z" }, - { url = "https://files.pythonhosted.org/packages/12/12/e428ddfc6d55556ea80468060e35583d4b744efb1b8b37ddf82a5a0f9765/sift_stream_bindings-0.5.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:84958ff95eee7aac40466e4d916a7459ebdb78a5c117d47d439e724ae8671e1b", size = 3773683, upload-time = "2026-09-29T23:39:38.771Z" }, - { url = "https://files.pythonhosted.org/packages/62/db/708693bad3be1d310a03c8804ee3e6ce688c0b8817ebe60451cde3a38968/sift_stream_bindings-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3590d36066da51fe536ee63e814d876d036e4cf41fb9f0f170417deb37a19103", size = 3719147, upload-time = "2026-09-29T23:39:40.75Z" }, - { url = "https://files.pythonhosted.org/packages/60/4c/5b93ade8681147460f5f19cfca6efba127199def2b28f5a883098895305b/sift_stream_bindings-0.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f65cbd786c59bbc246e3f78c08abb942105f78ac818cda5ab26801f5c697dc6", size = 4292129, upload-time = "2026-09-29T23:39:42.463Z" }, - { url = "https://files.pythonhosted.org/packages/49/78/c0628d7e863f8a691364b1ca4d4c4da90ef8c3d11bb1967146d37d1ac640/sift_stream_bindings-0.5.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d891130752043c621550eddf15647628d64cbf025f0c0674161a9f8e01e57ba2", size = 4018819, upload-time = "2026-09-29T23:39:44.308Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c3/52366ab43e2b63352680b238e1893aeb8c7ee075b2908f89d97c61051394/sift_stream_bindings-0.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12f91c4c2fc015d29961fee2883b88bf07ae5746377c47f95b7567883a139c17", size = 4086384, upload-time = "2026-09-29T23:39:46.138Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cf/4aaf824b9e23abb070ef9bf59580bb3e7471a4b0b56fb194c2f3d0d8febf/sift_stream_bindings-0.5.2-cp311-cp311-win32.whl", hash = "sha256:210e433da1bc6c040f2f9812899ccf702eeb9f8b39d983442db27c7bcf25d8b8", size = 2979012, upload-time = "2026-09-29T23:39:47.953Z" }, - { url = "https://files.pythonhosted.org/packages/f0/cf/4e410b9c7bd975282fef4e78e642f0cabd41ae2534fc28d7ba53ba85b8bd/sift_stream_bindings-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:339c952fe672de4604003f2a75cad777f9c031227c6d9c5741697a3a06c081a0", size = 3569782, upload-time = "2026-09-29T23:39:49.796Z" }, - { url = "https://files.pythonhosted.org/packages/78/4d/4a09f451cb0b0359680786fc3bd5f52ed6905e4b9e0e6e6746112e7e1f6e/sift_stream_bindings-0.5.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb66347460a1c72480fcb18703fe94cb97f342af302cfba2bf3254a0f8d0b609", size = 3753154, upload-time = "2026-09-29T23:39:51.855Z" }, - { url = "https://files.pythonhosted.org/packages/61/0c/7b11a1f2a5911c20ccfdeeace1117240224437c18b656ec908bad6d86bf1/sift_stream_bindings-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3cd485e433c98763a531eef86db00022036fca89a15005856526953e5ee1b0d", size = 3713516, upload-time = "2026-09-29T23:39:53.659Z" }, - { url = "https://files.pythonhosted.org/packages/32/88/25978caf37484e67bc7aa97df3c54f399592b8e7767d9e72de916a43a030/sift_stream_bindings-0.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6b3b1074ae06593d85b71ea270578cdd65815fef1cdd05617e5d2188b8ee95b", size = 4267455, upload-time = "2026-09-29T23:39:55.484Z" }, - { url = "https://files.pythonhosted.org/packages/9d/5a/d497382014e3cf01fb09355c635f5f2bc2ebc7ff4941b4cbb5b21f0470e1/sift_stream_bindings-0.5.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0f64c42922c6ac69e0f6ec5ffbb6a0f16b09354c1c915438639f5bbdb8032556", size = 4013931, upload-time = "2026-09-29T23:39:58.113Z" }, - { url = "https://files.pythonhosted.org/packages/9b/54/4e61ab967ee0f3c2f733b9ea425a5ddeafc62789a7daa902abc9e8421712/sift_stream_bindings-0.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21d4edd7f05c8e5d8faa626d9a89c0dbdf6dd6a2ffe0f4474f70c4d4cf2853a1", size = 4063999, upload-time = "2026-09-29T23:40:00.084Z" }, - { url = "https://files.pythonhosted.org/packages/2b/64/3df0ff90b1ccf1c45d4c73eb8d576c9fd578938367aa94e5f66c3ebf7d8d/sift_stream_bindings-0.5.2-cp312-cp312-win32.whl", hash = "sha256:bd0e11cd0444f43f29aa75b1951d8c7738c7cb997b0c8b82bc10b492dc5c6fa8", size = 2977016, upload-time = "2026-09-29T23:40:01.87Z" }, - { url = "https://files.pythonhosted.org/packages/80/e4/f033af527146c0c57b67b5ef6dd045a72029957e761323dd44bcb720611b/sift_stream_bindings-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:c88a42489c0c2d64c296959f0dc9c6dc91695e146c5bd0714e446ec9872c3061", size = 3565283, upload-time = "2026-09-29T23:40:03.822Z" }, - { url = "https://files.pythonhosted.org/packages/70/ae/0faf95f1e7ebde23b50474966fc466e5cc6f70713fef396026d7f2d8907c/sift_stream_bindings-0.5.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:abf5fc35701149b3cf86adadcd18a030820a49edce7e8a3e0ad196f4f08f86e4", size = 3752653, upload-time = "2026-09-29T23:40:05.784Z" }, - { url = "https://files.pythonhosted.org/packages/00/77/64cde73560adc73b01a0093db1d8186f486f01344aa81816cf806de451d6/sift_stream_bindings-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bab812acdc088d1e2e8c0591853b4892dd80bb28fac9f28780cd00b9110efd66", size = 3711240, upload-time = "2026-09-29T23:40:07.591Z" }, - { url = "https://files.pythonhosted.org/packages/73/9d/2035b9bd06eb3d450347dfe4194920dfe7d8232ef645477a66a1b76a4925/sift_stream_bindings-0.5.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a03e9aeb31680c94f7317c95fd8ddd537eb2a451aa0990045488dd0e4de2eb2c", size = 4267333, upload-time = "2026-09-29T23:40:09.315Z" }, - { url = "https://files.pythonhosted.org/packages/e4/1d/90c8fff6cceb6b160a6423a8f78c8f47a4a91dfc70642822cb8069fa55d2/sift_stream_bindings-0.5.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5facd0a760c7451e9561a89f5dba9c575e57d9ab5c3ba543e9182bed78b52ae2", size = 4012333, upload-time = "2026-09-29T23:40:11.453Z" }, - { url = "https://files.pythonhosted.org/packages/11/b0/d0b4dfead2f2ef3530bf95f21d4c235385e7951217c27e350c3e988bf7e4/sift_stream_bindings-0.5.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5ed661288d4263e8b37c35cbdafd467655563766a2f6988c38dbe1715b16aeb", size = 4062959, upload-time = "2026-09-29T23:40:13.49Z" }, - { url = "https://files.pythonhosted.org/packages/1a/05/096d1ff5ee4a0d576a5ed71e4a87aa4ab43f9f4bb0bd9883f3019912d238/sift_stream_bindings-0.5.2-cp313-cp313-win32.whl", hash = "sha256:5f9616d1ea0d1d0ce0d93280b3a87d7f25f30b314c21e5ec491826839b02698d", size = 2976642, upload-time = "2026-09-29T23:40:15.161Z" }, - { url = "https://files.pythonhosted.org/packages/d3/f5/5af030618f051abf36c7acb356ef26f06b0fa381eac2c5f958d2f957765e/sift_stream_bindings-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:87fcbd92603011f4a62b06387fae5819982098e4aa6f29fc2f3ea8aeeb45c0a6", size = 3564121, upload-time = "2026-09-29T23:40:16.938Z" }, - { url = "https://files.pythonhosted.org/packages/ae/40/ed5ee4355cb0ecdbd225f082fbe8d28728ea252816175a5ef917d0a0cd4a/sift_stream_bindings-0.5.2-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e18f40c67eb77727d8709df7dda5854f901d312e1776d405566309b056747db4", size = 3754938, upload-time = "2026-09-29T23:40:19.001Z" }, - { url = "https://files.pythonhosted.org/packages/cc/5d/87259d6c13b233207241bb16f128e927ead844a8fa2a2960b9340e5f9c23/sift_stream_bindings-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4ddac18c1ebd1904e53c08895a3352e7e88deb1a403473c4de95b5cbf9470c94", size = 3710816, upload-time = "2026-09-29T23:40:20.968Z" }, - { url = "https://files.pythonhosted.org/packages/f8/2e/1c696e52d2cf87a88f8f7f8bafc9ac410a2ea0098b6962c8165290e4ff29/sift_stream_bindings-0.5.2-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d188f63a098abbbf65246e19dc4d7b78c23424016997873a8b6dade0b75b26c", size = 4266512, upload-time = "2026-09-29T23:40:22.87Z" }, - { url = "https://files.pythonhosted.org/packages/7a/df/582b033f3c18ea26ef7060ba8b2c813800f9e7592891afddae9fc07bccfe/sift_stream_bindings-0.5.2-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1230133d25a95bb5f623fa4fd4fd8c5d0f9bf21765bddc117eb7f35bcaf5746e", size = 4015375, upload-time = "2026-09-29T23:40:24.925Z" }, - { url = "https://files.pythonhosted.org/packages/93/a7/221af18e186f58aef24539cdf3f00b3ffd83dadfba42f344f3b74f74045f/sift_stream_bindings-0.5.2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:230662bc9d050bb231b17c6cf3d567deaf4efd76516bdd1e713ad9348941bc2d", size = 4061861, upload-time = "2026-09-29T23:40:26.721Z" }, - { url = "https://files.pythonhosted.org/packages/b2/f6/13a8fdec64ffa0a7052aa8f69014bc130b661ce9bcc69254bf5ea920bc27/sift_stream_bindings-0.5.2-cp314-cp314-win32.whl", hash = "sha256:58eb66d94e7a2fd2bba42d6102f4bf94c4a05eb6b7deae8022ab04ca6ab71c1c", size = 2976355, upload-time = "2026-09-29T23:40:28.467Z" }, - { url = "https://files.pythonhosted.org/packages/57/6f/5d319aba66fad8ce76043ca6204a0319045bf310d30d9b67103c4eb99be4/sift_stream_bindings-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:772ef3cb095b5411782b4f135a015156dcf7edb24af64c28a48e6a62890db487", size = 3564292, upload-time = "2026-09-29T23:40:30.467Z" }, - { url = "https://files.pythonhosted.org/packages/5f/9e/c3e7d67351d740fd31ef18c0aa6bbb551c98236d231c96dd0a45da7f2d99/sift_stream_bindings-0.5.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:19466b0fc220699fed0f7fef4ad9f8d89ed348ca266fbd8fb3a6eae145ceb4c1", size = 3779756, upload-time = "2026-09-29T23:40:32.241Z" }, - { url = "https://files.pythonhosted.org/packages/80/8d/648e6d1d6d2b47eb442cda6b5ae7d58cd409707becabfd468f3cfd7e0eed/sift_stream_bindings-0.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2b9d32c28d17aa2bf6c088e1370aef4d4a73c08294e2d21ac965391710fdd9a0", size = 3724572, upload-time = "2026-09-29T23:40:33.983Z" }, - { url = "https://files.pythonhosted.org/packages/57/8a/2295a8e007e15d9281facde5f8c89a70d73319f47865207b53ed3ea3a5cd/sift_stream_bindings-0.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a04fc27917d0e89811f44106861a2da70a6a9523c3827159a135466de0fdb82", size = 4297648, upload-time = "2026-09-29T23:40:36.313Z" }, - { url = "https://files.pythonhosted.org/packages/3b/6d/bcd701191e37115308549a00e4818f851608e3c45ad9d0583f2a5231c10a/sift_stream_bindings-0.5.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ad7e2f6bffa3048784a409216650c54be6608a83da0658d371120b11fb88cad", size = 4016482, upload-time = "2026-09-29T23:40:38.681Z" }, - { url = "https://files.pythonhosted.org/packages/9a/2f/8fe9270055afd20d596c73156ef719fe77776d1fb2387cb6325de33c60b4/sift_stream_bindings-0.5.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:095dc9119214e9b0eb08588b2ef1ea829a27b6c2914c0402110339b90a5ef7da", size = 4093758, upload-time = "2026-09-29T23:40:40.762Z" }, - { url = "https://files.pythonhosted.org/packages/a4/5d/d6248fc0abf44898250d86a07e7e18de9088a4c5930563087c5308989359/sift_stream_bindings-0.5.2-cp38-cp38-win32.whl", hash = "sha256:848afc7c2683e07da8cf885016265d5ebf0c32addf7d5813566efd1fe43f97f8", size = 2982076, upload-time = "2026-09-29T23:40:42.678Z" }, - { url = "https://files.pythonhosted.org/packages/9a/c4/7ebd9e3826a09c805a4f13c7338220de31ef45a249369c014fa20dc07d64/sift_stream_bindings-0.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:ef5e0ffb253837415088848192a432cec687c4974361b95485e13233e67944e7", size = 3586969, upload-time = "2026-09-29T23:40:44.792Z" }, - { url = "https://files.pythonhosted.org/packages/84/25/8aab5be3f2f757a25550faffa6eec66903b218e8555d69ecaadbc5280a08/sift_stream_bindings-0.5.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3eaca4ce47212486fd7b2c852f4de75a2c200f263f7c4f61ab28e5788bad32d6", size = 3778438, upload-time = "2026-09-29T23:40:46.561Z" }, - { url = "https://files.pythonhosted.org/packages/43/c9/1321ac85e3e62f193e57262e7cecfd887801532642c6b627b5691f3dfe43/sift_stream_bindings-0.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c1c86217f6254550e68cde1047f76d26f4ed583f087072304eafc1782cbd58a7", size = 3723900, upload-time = "2026-09-29T23:40:48.339Z" }, - { url = "https://files.pythonhosted.org/packages/d1/2c/1aac50e90773b05ecce658471a9b62b223f2ab24ba6ef4bd43f9e9266fe8/sift_stream_bindings-0.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f8e3e8851eb890fbb8e2f69841c36aa22ae94b526fc18a80272c0fe8b2c1f62", size = 4297536, upload-time = "2026-09-29T23:40:50.236Z" }, - { url = "https://files.pythonhosted.org/packages/26/80/7c0b818f9781da416ea530d916792737b0be8990f74953eec565682246d0/sift_stream_bindings-0.5.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:65b284d0811f1327a74515c4bc84325a59313f05e4378da978404602c38a0165", size = 4022309, upload-time = "2026-09-29T23:40:52.575Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e1/b8f7c369bbf307d1a729e55836d3f7606c5672765393023541bb4bf87c65/sift_stream_bindings-0.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3bb6fcafb1b7401dea3209c8871b3ac1277d804a9089410613faebea73cd3f68", size = 4089760, upload-time = "2026-09-29T23:40:55.152Z" }, - { url = "https://files.pythonhosted.org/packages/c1/f4/1ecf5ea731f402e5a9ee70863a4c5a13aea7f79d827da56343f937326ea2/sift_stream_bindings-0.5.2-cp39-cp39-win32.whl", hash = "sha256:0b1959d1383b2e0f79d61ae336f39c0fa12fa1b6bb6b2328fb6fae062c1edde5", size = 2980222, upload-time = "2026-09-29T23:40:56.844Z" }, - { url = "https://files.pythonhosted.org/packages/22/14/dcd346853ff46a919c35c27ac6341bb24e1a78ef398e635c0e2d5c4ced4f/sift_stream_bindings-0.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:2b28bae5d262db088af023e7472381ddaeedff72471b67e8012a9a363ec9d018", size = 3572874, upload-time = "2026-09-29T23:40:58.544Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/96/17/c8d2ea951f4097fd55fb932705b239d908736df813d1e9a1f427120324ff/sift_stream_bindings-0.6.1.tar.gz", hash = "sha256:7991db6a756b0958ae3d19b774c740d0e28c55ea3d5fa4975c36b60ce69b6668", size = 1552100, upload-time = "2026-10-21T21:53:44.241Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/81/8fe454d174f22449366036d32fc3015143f0323a66d9eeda13823becb55f/sift_stream_bindings-0.6.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d6c971dedb630e777d6d29b9346cb8cc6578d225e1b54a72296590ed0454f349", size = 3786102, upload-time = "2026-10-21T21:52:15.969Z" }, + { url = "https://files.pythonhosted.org/packages/a2/29/ca0f7beeddfb9ea6d98fa03a8c3f1dc46b9ca265c1e8d10a08e9d1625620/sift_stream_bindings-0.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:841dc0d0a56577f004a94e4eee0edf37716e9071958d8b14b098e4ae31db6958", size = 3753188, upload-time = "2026-10-21T21:52:18.133Z" }, + { url = "https://files.pythonhosted.org/packages/84/bb/191321393ac1f46c116bb3b6dd57abe8e60e13d204a8234e01c5e5903f1d/sift_stream_bindings-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e64a6d889a5d9d425212790cf100a0e849642ffc3a620182c4b534820ce519fc", size = 4347572, upload-time = "2026-10-21T21:52:20.06Z" }, + { url = "https://files.pythonhosted.org/packages/70/4b/980b042f105fe5173486ac0c22551e8f833b3428c72e781bac31b3009657/sift_stream_bindings-0.6.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb3e2a58aabdc9b6ec43ea0aed1f888433e65781e083e90bdbe27768cf6b7f29", size = 4101691, upload-time = "2026-10-21T21:52:21.974Z" }, + { url = "https://files.pythonhosted.org/packages/57/59/c46dc741bb7bc7cafdd4e602575c4b20a2a927198510e552ad7c3b46d144/sift_stream_bindings-0.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cf1007375d43c6b0b23932873b116062bc545c7078de8c8df37380f95390654", size = 4146585, upload-time = "2026-10-21T21:52:24.036Z" }, + { url = "https://files.pythonhosted.org/packages/44/3b/f13cfe08825e405bb9cceb05a59c0c15621792c666d28f009729169a700e/sift_stream_bindings-0.6.1-cp310-cp310-win32.whl", hash = "sha256:0304af3369ff36baa4cfdfe51372bac608e1caf659c5f7d8a57df5854583604b", size = 3039668, upload-time = "2026-10-21T21:52:25.955Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d2/e94c85686d238308570cc1f1e40674055a30817e79d8936a684d1a758bcc/sift_stream_bindings-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:f99f55d42e11e71ca1e54041ae409b7ebb198db18c4573ab44cd6a998da3e76a", size = 3627696, upload-time = "2026-10-21T21:52:27.624Z" }, + { url = "https://files.pythonhosted.org/packages/2b/7c/e4b2caca8f2093ec83ed8f97e03023d4960ef0ff5acedaaa74f640b71b83/sift_stream_bindings-0.6.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:714ba6bcc38f1866a538886b59ec2e3fcf55922f4fea146f87c3ba583874e215", size = 3785930, upload-time = "2026-10-21T21:52:29.51Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/bbac44d0ba9d38907f055e5cfbb891738948bf2b56e8bfb9046171dee94f/sift_stream_bindings-0.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a505439b8f1189585ce5b59ff76366db0455d905053fd7b0e85aa0fbbf61822e", size = 3752530, upload-time = "2026-10-21T21:52:31.298Z" }, + { url = "https://files.pythonhosted.org/packages/6c/ec/9c034850daa4c5436901401c443adb8f59b12d0e2a4407bcf445effcc4ab/sift_stream_bindings-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ce788e4ac1f767718d32326d3a799c7407c80ec90bad4f5b602a406148e1d34", size = 4346777, upload-time = "2026-10-21T21:52:33.219Z" }, + { url = "https://files.pythonhosted.org/packages/09/4e/ac02125f66462a5c4ee8ecd627528f199efdc2e52c3e797ac3e677139e83/sift_stream_bindings-0.6.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e17f384a759514feda80679041bc7ddc7577c332492d43326abdc966542403c6", size = 4101174, upload-time = "2026-10-21T21:52:35.078Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ec/690d038f38da7991cf4f8e82d7d353bf09a99cea180ad2f78525f7edd392/sift_stream_bindings-0.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb28b39d101b580f889c4b4791ae0b0713e23122936029bc8de28391b7f3ae62", size = 4146461, upload-time = "2026-10-21T21:52:36.752Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b4/c637fb06687824032d5f67a945da7c59115bdc51794e0a140a6c2ac870c3/sift_stream_bindings-0.6.1-cp311-cp311-win32.whl", hash = "sha256:1b657b2a2c278d0b2906a11ee8fc19d102a829fbf95c285e7148b1859f12bd7e", size = 3039646, upload-time = "2026-10-21T21:52:38.418Z" }, + { url = "https://files.pythonhosted.org/packages/5a/6c/7aa17b226e643a89c0a5bc93463f303edeeb691f46f9ecffb717b0605c99/sift_stream_bindings-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:ab4696a8c48e46fce30268b449c0b01c1f03acb91bf8e7a7f8e762b994941601", size = 3628036, upload-time = "2026-10-21T21:52:40.22Z" }, + { url = "https://files.pythonhosted.org/packages/04/3c/f355fed6fcb6e304a2fa080f056fba29ae273b225ae56f7686f431b221af/sift_stream_bindings-0.6.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e0e4900e5601007b35168ce07370c97bff74bc836ba2a339cf89ba0098cd6940", size = 3790747, upload-time = "2026-10-21T21:52:42.123Z" }, + { url = "https://files.pythonhosted.org/packages/20/a3/2a00df99873d87dc9f62c345fda1d0f64d8e755011fbf42d6dee9fb275d6/sift_stream_bindings-0.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6c4e1be12b40182b127390485971a0b60e7b3fcc3113738fbd76c3a783fe514", size = 3751154, upload-time = "2026-10-21T21:52:43.703Z" }, + { url = "https://files.pythonhosted.org/packages/a6/cc/5474bccecfe24343eb48ef684e415a451f76d6f6e7d938f7b5094c357998/sift_stream_bindings-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85b454d829598f1ac02f6f48d676f478b36972de41f25ce75591ea22b07ebb20", size = 4330157, upload-time = "2026-10-21T21:52:46.326Z" }, + { url = "https://files.pythonhosted.org/packages/9f/b2/e58e4c3f3cfbdbb1cec32838a4ca02cb5f00ced1f54ebfcbe9f08fdb114e/sift_stream_bindings-0.6.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:596db153d4baeceada7bc17a85bf9bec79f00464af652c44d917ddea22816da7", size = 4073485, upload-time = "2026-10-21T21:52:47.833Z" }, + { url = "https://files.pythonhosted.org/packages/81/0f/cefbb26432d23677f193271036360f78ca861122e1637e56c03aa802316f/sift_stream_bindings-0.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70c298ece4dc73912b8dff0f98206d2d07cb23b58811ef52d877b15b46197d84", size = 4123448, upload-time = "2026-10-21T21:52:49.448Z" }, + { url = "https://files.pythonhosted.org/packages/d3/b4/e4bfd3cdb928e832a3a3a1f016472f68b121a34d0b6fb64495d067715693/sift_stream_bindings-0.6.1-cp312-cp312-win32.whl", hash = "sha256:b6090d795701c87dc7a46b99bac71e11fe20eaaeeced91de8c4898d1b5740f2b", size = 3039656, upload-time = "2026-10-21T21:52:51.191Z" }, + { url = "https://files.pythonhosted.org/packages/38/9c/1e98522ccdb9d2a5050e795a99eb8d4aa72ac69179a9f2604cd6a0d8adcc/sift_stream_bindings-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:8205dfba29c666e011a8ba6f2f7e21a1026974f63e80468a7143b9724e824fb4", size = 3624868, upload-time = "2026-10-21T21:52:53.058Z" }, + { url = "https://files.pythonhosted.org/packages/bc/63/4be1b84a242645f00c7d67c7de20371fbc2567c23e98ec88ef6439c90876/sift_stream_bindings-0.6.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:c703a1197d0d01424d758882fe6b821a26c650659fe74a7926aa5f1c4720c0a9", size = 3790503, upload-time = "2026-10-21T21:52:55.115Z" }, + { url = "https://files.pythonhosted.org/packages/41/2f/d1d6fc7ea2278d8d4adf1a2f6283527ea6c7c6327832f2dce4d70d402062/sift_stream_bindings-0.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:08ee10a1eac9b59e50c5e60414ce3bf5c73ed3cb3a030ca610f70cf7589775a2", size = 3751029, upload-time = "2026-10-21T21:52:56.745Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ff/5d25378ebbf1bc22dad6b65838aaaeae1c099afa2e817a5a0cad7f6b40c3/sift_stream_bindings-0.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edd90962fc545bde9963e7aae1a28e778e0dda6e3039d8928e1d141a74170d4a", size = 4326993, upload-time = "2026-10-21T21:52:58.762Z" }, + { url = "https://files.pythonhosted.org/packages/5d/62/edd3e4ecc311dd900b7283318de076828124dec28526febdc310ce72e9be/sift_stream_bindings-0.6.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:49530601a936311c00d6e357b4c80426955ab9db1721c5854f52551ceff665a8", size = 4072821, upload-time = "2026-10-21T21:53:00.27Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ab/cd36eb1e34837ffbd6ecb1305a9e4217bdf453200492bdd7a4f8b0115dca/sift_stream_bindings-0.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a953d82b30f9ccb96fe5752e8cbea9d9c4fe6db1f8c166c5eff0323a94db4074", size = 4121722, upload-time = "2026-10-21T21:53:01.998Z" }, + { url = "https://files.pythonhosted.org/packages/78/b5/c6eb6c726414879616c48e22006feede83cee02f542478f327d880a670d3/sift_stream_bindings-0.6.1-cp313-cp313-win32.whl", hash = "sha256:2d28433e6e9f69d058e3a31976f06bdfa1c330feccd3bf6cbdc8937a152d3ce9", size = 3039626, upload-time = "2026-10-21T21:53:03.846Z" }, + { url = "https://files.pythonhosted.org/packages/e3/e1/f1df98de8dd85431b8fe0978d05f747de44ff6f309785fd415bbe4df4790/sift_stream_bindings-0.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:a30d947a1c6c0eccdf7c3d49360d307d5f97922600192e90e71558b102d275a6", size = 3624729, upload-time = "2026-10-21T21:53:05.712Z" }, + { url = "https://files.pythonhosted.org/packages/af/fa/b69416c49c80b3055fba6d61f720679c5da1b13a28d6dca790d443d6810b/sift_stream_bindings-0.6.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:84701ff720eb5090048fcde989693432608937673be85a4d76766ff84b46e524", size = 3788424, upload-time = "2026-10-21T21:53:07.226Z" }, + { url = "https://files.pythonhosted.org/packages/d6/c4/c489d7c54a074a597500ae994f6c6290d86f38ba407a1e1dbe2e4b74cc88/sift_stream_bindings-0.6.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b0bee808835cc6e5d770da1d239e29eb2defd9a272d611aaef1f5670285d8946", size = 3746954, upload-time = "2026-10-21T21:53:09.003Z" }, + { url = "https://files.pythonhosted.org/packages/fa/1a/21c77638807aba2162f2dffc41d647655031ef0e9abd861a4f3c7f59626e/sift_stream_bindings-0.6.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79ad8fb4a48b232a4ce30d064e914349ed8cee394903ff52c0d1a0cfc4c51d80", size = 4327865, upload-time = "2026-10-21T21:53:10.954Z" }, + { url = "https://files.pythonhosted.org/packages/fe/fb/25d897eedbe28f031354ba34fada5d416eb6e5b07f6fd5efd767a4c6606f/sift_stream_bindings-0.6.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1a599b90969fd3bb06b08a042196f80552982daf5445c512411f4092d289a90d", size = 4072689, upload-time = "2026-10-21T21:53:12.879Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a3/89ef371897a25cc19734f21bdf1e4355b08dc8d75b733596f19126b1dcac/sift_stream_bindings-0.6.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b1199813ccf73efcdf4198c338779bb90d34380494264e4921b4f0f0d9b41d", size = 4122810, upload-time = "2026-10-21T21:53:14.584Z" }, + { url = "https://files.pythonhosted.org/packages/ad/da/e26f1ffd852c7faaff3005fc80b15d65c37dd6352e0d3bc8c5f9cac0be82/sift_stream_bindings-0.6.1-cp314-cp314-win32.whl", hash = "sha256:269dc7e5e52fd1f4991778700ce1b0dddc9709b73d0d3a61dcbbfee084cd4513", size = 3041252, upload-time = "2026-10-21T21:53:16.193Z" }, + { url = "https://files.pythonhosted.org/packages/ea/20/cc68e58b22e186bd6485773be3b417c6fecebd23a51be85f94cdf45efb3e/sift_stream_bindings-0.6.1-cp314-cp314-win_amd64.whl", hash = "sha256:721031823b6f09873bdba3c5211112906bacaead7b6527406deb51356d772314", size = 3619575, upload-time = "2026-10-21T21:53:17.874Z" }, + { url = "https://files.pythonhosted.org/packages/b2/eb/bfc778ff393c0613a8558edb3fb9f2be1f0049d8a9ff92f00f1316083442/sift_stream_bindings-0.6.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:fe35a7e6a3024e15ed2e31507bade1f34393de1f50a30d6a97640d465017f9aa", size = 3790295, upload-time = "2026-10-21T21:53:19.664Z" }, + { url = "https://files.pythonhosted.org/packages/ed/7f/5f7ff41810a4d9be2b9cef30c8bd3234bf7bf03319be6184022e2ad467ba/sift_stream_bindings-0.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed1ec282a1d648355d88b3b7416b543cad51b93e628d3bbf9cc0e49e2bf05318", size = 3756716, upload-time = "2026-10-21T21:53:21.431Z" }, + { url = "https://files.pythonhosted.org/packages/b5/60/9a9fc998b70861097c63c56e5c314b2c1fd642e93d6f8904a7b0d2143fba/sift_stream_bindings-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25174abea5ba9ea50de7e8414830dbc5e6b27378d710807bb3c5284c6fe630b", size = 4349303, upload-time = "2026-10-21T21:53:23.129Z" }, + { url = "https://files.pythonhosted.org/packages/37/08/7728ffdc8b0a754f8ca9e18556bc9986af6c7275f9db87ebde7de3b512f0/sift_stream_bindings-0.6.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df487f8ba8790d5f32cbe61162ab777635ed5ca9d1671f0e025c14e31c8802d3", size = 4104766, upload-time = "2026-10-21T21:53:24.767Z" }, + { url = "https://files.pythonhosted.org/packages/87/fc/e2e49222a9b34c7c25f3fc0e0e2f8ee701c31c8641da2f37c0f332a8b87c/sift_stream_bindings-0.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d781d52c77a5bfb68bcbfa63adbc75788405768d18f340106b868e8b6beb10", size = 4147965, upload-time = "2026-10-21T21:53:26.882Z" }, + { url = "https://files.pythonhosted.org/packages/64/43/3ce07b3f1acfcb78f1325ce6cbb161dce687696a5f6c95ccae745fdd2e67/sift_stream_bindings-0.6.1-cp38-cp38-win32.whl", hash = "sha256:89a566c7144c19182c588fc9a43ea4798c298c76dd4056fdcb93272f5452e28c", size = 3042538, upload-time = "2026-10-21T21:53:28.852Z" }, + { url = "https://files.pythonhosted.org/packages/66/db/8a6c28e6d707c1752c0dd94c370080fa3b766ada2f1a990f02eef3d3fe8a/sift_stream_bindings-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:5b490a9b40f4f148f848195f7791419d390864b31b675e9f02e4ec106f22ff06", size = 3632832, upload-time = "2026-10-21T21:53:30.344Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e8/ecb4d3856184c07b33587f5fc3c34902842638ba40e69bd14e4ea9d83da9/sift_stream_bindings-0.6.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:699b472ed98a9b7f05dc719e3d55b76119c1c7e9c40881227cbbb6fb8887230c", size = 3789306, upload-time = "2026-10-21T21:53:32.182Z" }, + { url = "https://files.pythonhosted.org/packages/dc/dd/7396cce1853e57d8dae0491818d053a2ccc27118ad65106ea3970b3015c1/sift_stream_bindings-0.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:01c414be98125c2f8675c9ac42165d249190c04a156a94760230529124876c2c", size = 3756448, upload-time = "2026-10-21T21:53:33.846Z" }, + { url = "https://files.pythonhosted.org/packages/fb/59/fc8c46418ae0900e3a9a16699c590e407ca483a97446599613bc215fb7db/sift_stream_bindings-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69ed682d87a2e31adda57bab245265725eb91b2b6c00354a43a2a6722c22bce1", size = 4348849, upload-time = "2026-10-21T21:53:35.634Z" }, + { url = "https://files.pythonhosted.org/packages/95/5e/5e296863d0e5dcf81d1f9cf9950fcb06c44e4f38775414bfa6c12f05b9c9/sift_stream_bindings-0.6.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:13c7ea31221aba27335cf22265a7bf5f316369df6bda194dbe3a2be1ce8ba15a", size = 4103732, upload-time = "2026-10-21T21:53:37.579Z" }, + { url = "https://files.pythonhosted.org/packages/21/a4/bf1976a9e13690710e800f19c0ae36aa5b5f032ccad39ab2bc5bd0082bb2/sift_stream_bindings-0.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31101bd113f2652713e043b03eecb0fa03d9cbb6c78621c4a2903b48e38ddf2b", size = 4149127, upload-time = "2026-10-21T21:53:39.319Z" }, + { url = "https://files.pythonhosted.org/packages/d8/8b/5c4deeeecf5b44387de849d642e33134e42996ff8d7b7a7f5bff1d4491b6/sift_stream_bindings-0.6.1-cp39-cp39-win32.whl", hash = "sha256:449d21304ac8a4e92972fd075d8e6fc7ae278d19b6b4beb73432efd92114b0a9", size = 3041292, upload-time = "2026-10-21T21:53:40.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/7c/d09ce6cdde3267d262eb63b173af9d87060988bc39b3fd7ab93a27864efb/sift_stream_bindings-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:40c67cfa53766690464fe56d82c9fe0d07011f834af695b852ddaebd0f73f1ad", size = 3631426, upload-time = "2026-10-21T21:53:42.545Z" }, ] [[package]]