From 89784319d7906bdb796a6f96b7c0c012e980ec41 Mon Sep 17 00:00:00 2001 From: Dmitriy Repin Date: Wed, 20 Aug 2025 22:46:11 +0000 Subject: [PATCH 01/24] Export part 1 --- src/mdio/converters/mdio.py | 84 +++++++++++-------- .../v1/templates/abstract_dataset_template.py | 5 +- src/mdio/segy/creation.py | 79 ++++++++--------- tests/conftest.py | 25 ++++-- tests/integration/conftest.py | 38 +++++---- tests/integration/test_segy_import_export.py | 78 ++++++++--------- .../test_segy_import_export_masked.py | 75 +++++++++-------- tests/integration/testing_data.py | 18 ++++ 8 files changed, 220 insertions(+), 182 deletions(-) diff --git a/src/mdio/converters/mdio.py b/src/mdio/converters/mdio.py index 9726cd6b8..f85c0ec94 100644 --- a/src/mdio/converters/mdio.py +++ b/src/mdio/converters/mdio.py @@ -5,12 +5,14 @@ import os from pathlib import Path from tempfile import TemporaryDirectory +from typing import TYPE_CHECKING +import dask.array as da import numpy as np +import xarray as xr from psutil import cpu_count from tqdm.dask import TqdmCallback -from mdio import MDIOReader from mdio.segy.blocked_io import to_segy from mdio.segy.creation import concat_files from mdio.segy.creation import mdio_spec_to_segy @@ -21,17 +23,33 @@ except ImportError: distributed = None +if TYPE_CHECKING: + from segy.schema import SegySpec + + from mdio.core.storage_location import StorageLocation default_cpus = cpu_count(logical=True) NUM_CPUS = int(os.getenv("MDIO__EXPORT__CPU_COUNT", default_cpus)) -def mdio_to_segy( # noqa: PLR0912, PLR0913 - mdio_path_or_buffer: str, - output_segy_path: str, +def _get_dask_array(mdio_xr: xr.Dataset, var_name: str, chunks: tuple[int, ...] = None) -> da.Array: + """Workaround if the MDIO Xarray dataset returns numpy arrays instead of Dask arrays""" + xr_var = mdio_xr[var_name] + # xr_var.chunks: + # Tuple of block lengths for this dataarray’s data, in order of dimensions, + # or None if the underlying data is not a dask array. + if xr_var.chunks is not None: + return xr_var.data.rechunk(chunks) + # For some reason, a NumPy in-memory array was returned + # HACK: Convert NumPy array to a chunked Dask array + return da.from_array(xr_var.data, chunks=chunks) + + +def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 + segy_spec: SegySpec, + input_location: StorageLocation, + output_location: StorageLocation, endian: str = "big", - access_pattern: str = "012", - storage_options: dict = None, new_chunks: tuple[int, ...] = None, selection_mask: np.ndarray = None, client: distributed.Client = None, @@ -47,12 +65,10 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913 A `selection_mask` can be provided (same shape as spatial grid) to export a subset. Args: - mdio_path_or_buffer: Input path where the MDIO is located. - output_segy_path: Path to the output SEG-Y file. + segy_spec: The SEG-Y specification to use for the conversion. + input_location: Store or URL (and cloud options) for MDIO file. + output_location: Path to the output SEG-Y file. endian: Endianness of the input SEG-Y. Rev.2 allows little endian. Default is 'big'. - access_pattern: This specificies the chunk access pattern. Underlying zarr.Array must - exist. Examples: '012', '01' - storage_options: Storage options for the cloud storage backend. Default: None (anonymous) new_chunks: Set manual chunksize. For development purposes only. selection_mask: Array that lists the subset of traces client: Dask client. If `None` we will use local threaded scheduler. If `auto` is used we @@ -84,41 +100,35 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913 ... ) """ - backend = "dask" + output_segy_path = Path(output_location.uri) - output_segy_path = Path(output_segy_path) - - mdio = MDIOReader( - mdio_path_or_buffer=mdio_path_or_buffer, - access_pattern=access_pattern, - storage_options=storage_options, - ) + mdio_xr = xr.open_dataset(input_location.uri, engine="zarr", mask_and_scale=False) + trace_variable_name = mdio_xr.attrs["attributes"]["traceVariableName"] + amplitude = mdio_xr[trace_variable_name] + chunks: tuple[int, ...] = amplitude.encoding.get("chunks") + shape: tuple[int, ...] = amplitude.shape + dtype = amplitude.dtype if new_chunks is None: - new_chunks = segy_export_rechunker(mdio.chunks, mdio.shape, mdio._traces.dtype) - - creation_args = [ - mdio_path_or_buffer, - output_segy_path, - access_pattern, - endian, - storage_options, - new_chunks, - backend, - ] + new_chunks = segy_export_rechunker(chunks, shape, dtype) + mdio_xr.close() + + creation_args = [segy_spec, input_location, output_location, endian] if client is not None: if distributed is not None: # This is in case we work with big data feature = client.submit(mdio_spec_to_segy, *creation_args) - mdio, segy_factory = feature.result() + mdio_xr, segy_factory = feature.result() else: msg = "Distributed client was provided, but `distributed` is not installed" raise ImportError(msg) else: - mdio, segy_factory = mdio_spec_to_segy(*creation_args) + mdio_xr, segy_factory = mdio_spec_to_segy(*creation_args) - live_mask = mdio.live_mask.compute() + # Using XArray.DataArray.values should trigger compute and load the whole array into memory. + live_mask = mdio_xr["trace_mask"].values + # live_mask = mdio.live_mask.compute() if selection_mask is not None: live_mask = live_mask & selection_mask @@ -138,8 +148,12 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913 dim_slices += (slice(start, stop),) # Lazily pull the data with limits now, and limit mask so its the same shape. - live_mask, headers, samples = mdio[dim_slices] - live_mask = live_mask.rechunk(headers.chunks) + # Workaround: currently the MDIO Xarray dataset returns numpy arrays instead of Dask arrays + # TODO (Dmitriy Repin): Revisit after the eager memory allocation is fixed + # https://github.com/TGSAI/mdio-python/issues/608 + live_mask = _get_dask_array(mdio_xr, "trace_mask", new_chunks[:-1])[dim_slices] + headers = _get_dask_array(mdio_xr, "headers", new_chunks[:-1])[dim_slices] + samples = _get_dask_array(mdio_xr, "amplitude", new_chunks)[dim_slices] if selection_mask is not None: selection_mask = selection_mask[dim_slices] diff --git a/src/mdio/schemas/v1/templates/abstract_dataset_template.py b/src/mdio/schemas/v1/templates/abstract_dataset_template.py index 7bd39ff2d..eb7050f76 100644 --- a/src/mdio/schemas/v1/templates/abstract_dataset_template.py +++ b/src/mdio/schemas/v1/templates/abstract_dataset_template.py @@ -84,7 +84,10 @@ def build_dataset( self._dim_sizes = sizes self._horizontal_coord_unit = horizontal_coord_unit - self._builder = MDIODatasetBuilder(name=name, attributes=self._load_dataset_attributes()) + attr = self._load_dataset_attributes() or UserAttributes(attributes={}) + attr.attributes["traceDomain"] = self._trace_domain + attr.attributes["traceVariableName"] = self._trace_variable_name + self._builder = MDIODatasetBuilder(name=name, attributes=attr) self._add_dimensions() self._add_coordinates() self._add_variables() diff --git a/src/mdio/segy/creation.py b/src/mdio/segy/creation.py index b1daf7d5c..6bd82669b 100644 --- a/src/mdio/segy/creation.py +++ b/src/mdio/segy/creation.py @@ -7,98 +7,85 @@ from pathlib import Path from shutil import copyfileobj from typing import TYPE_CHECKING -from typing import Any import numpy as np +import xarray as xr from segy.factory import SegyFactory from segy.schema import Endianness from segy.schema import SegySpec from tqdm.auto import tqdm +from xarray import Dataset as xr_Dataset -from mdio.api.accessor import MDIOReader -from mdio.segy.compat import mdio_segy_spec from mdio.segy.compat import revision_encode if TYPE_CHECKING: from numpy.typing import NDArray + from mdio.core.storage_location import StorageLocation + logger = logging.getLogger(__name__) -def make_segy_factory(mdio: MDIOReader, spec: SegySpec) -> SegyFactory: +def make_segy_factory(mdio_xr: xr_Dataset, spec: SegySpec) -> SegyFactory: """Generate SEG-Y factory from MDIO metadata.""" - grid = mdio.grid - sample_dim = grid.select_dim("sample") - sample_interval = sample_dim[1] - sample_dim[0] - samples_per_trace = len(sample_dim) - + binary_header = mdio_xr.attrs["attributes"]["binaryHeader"] + sample_interval = binary_header["sample_interval"] + samples_per_trace = binary_header["samples_per_trace"] return SegyFactory( spec=spec, - sample_interval=sample_interval * 1000, + sample_interval=sample_interval, # Sample Interval is already in microseconds samples_per_trace=samples_per_trace, ) -def mdio_spec_to_segy( # noqa: PLR0913 - mdio_path_or_buffer: str, - output_segy_path: Path, - access_pattern: str, +def mdio_spec_to_segy( + segy_spec: SegySpec, + input_location: StorageLocation, + output_location: StorageLocation, output_endian: str, - storage_options: dict[str, Any], - new_chunks: tuple[int, ...], - backend: str, -) -> tuple[MDIOReader, SegyFactory]: +) -> tuple[xr_Dataset, SegyFactory]: """Create SEG-Y file without any traces given MDIO specification. This function opens an MDIO file, gets some relevant information for SEG-Y files, then creates a SEG-Y file with the specification it read from the MDIO file. - It then returns the `MDIOReader` instance, and the parsed floating point format `sample_format` - for further use. + It then returns the Xarray Dataset instance and SegyFactory for further use. - Function will attempt to read text, and binary headers, and some grid information from the MDIO - file. If these don't exist, the process will fail. + Function will attempt to read text, and binary headers information from the MDIO file. + If these don't exist, the process will fail. Args: - mdio_path_or_buffer: Store or URL for MDIO file. - output_segy_path: Path to the output SEG-Y file. - access_pattern: Chunk access pattern, optional. Default is "012". Examples: '012', '01'. + segy_spec: The SEG-Y specification to use for the conversion. + input_location: Store or URL (and cloud options) for MDIO file. + output_location: Path to the output SEG-Y file. output_endian: Endianness of the output file. - storage_options: Options for the storage backend. By default, system-wide credentials - will be used. - new_chunks: Set manual chunksize. For development purposes only. - backend: Backend selection, optional. Default is "zarr". Must be in {'zarr', 'dask'}. Returns: - Initialized MDIOReader for MDIO file and return SegyFactory + Opened Xarray Dataset for MDIO file and SegyFactory """ - mdio = MDIOReader( - mdio_path_or_buffer=mdio_path_or_buffer, - access_pattern=access_pattern, - storage_options=storage_options, - return_metadata=True, - new_chunks=new_chunks, - backend=backend, - disk_cache=False, # Making sure disk caching is disabled - ) + mdio_xr = xr.open_dataset(input_location.uri, engine="zarr", mask_and_scale=False) - mdio_file_version = mdio.root.attrs["api_version"] - spec = mdio_segy_spec(mdio_file_version) + mdio_file_version = mdio_xr.attrs["apiVersion"] + spec = segy_spec spec.endianness = Endianness(output_endian) - factory = make_segy_factory(mdio, spec=spec) + factory = make_segy_factory(mdio_xr, spec=spec) + + attr = mdio_xr.attrs["attributes"] - text_str = "\n".join(mdio.text_header) + txt_header = attr["textHeader"] + text_str = "\n".join(txt_header) text_bytes = factory.create_textual_header(text_str) - binary_header = revision_encode(mdio.binary_header, mdio_file_version) + bin_header = attr["binaryHeader"] + binary_header = revision_encode(bin_header, mdio_file_version) bin_hdr_bytes = factory.create_binary_header(binary_header) - with output_segy_path.open(mode="wb") as fp: + with Path(output_location.uri).open(mode="wb") as fp: fp.write(text_bytes) fp.write(bin_hdr_bytes) - return mdio, factory + return mdio_xr, factory @dataclass(slots=True) diff --git a/tests/conftest.py b/tests/conftest.py index 6f4f2d1f3..24bc06ef9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,14 +3,12 @@ from __future__ import annotations import warnings -from typing import TYPE_CHECKING +from pathlib import Path from urllib.request import urlretrieve import pytest -if TYPE_CHECKING: - from pathlib import Path - +DEBUG_MODE = False # Suppress Dask's chunk balancing warning warnings.filterwarnings( @@ -24,6 +22,8 @@ @pytest.fixture(scope="session") def fake_segy_tmp(tmp_path_factory: pytest.TempPathFactory) -> Path: """Make a temp file for the fake SEG-Y files we are going to create.""" + if DEBUG_MODE: + return Path("TMP/fake_segy") return tmp_path_factory.mktemp(r"fake_segy") @@ -36,27 +36,38 @@ def segy_input_uri() -> str: @pytest.fixture(scope="session") def segy_input(segy_input_uri: str, tmp_path_factory: pytest.TempPathFactory) -> Path: """Download teapot dome dataset for testing.""" - tmp_dir = tmp_path_factory.mktemp("segy") + if DEBUG_MODE: + tmp_dir = Path("TMP/segy") + tmp_dir.mkdir(parents=True, exist_ok=True) + else: + tmp_dir = tmp_path_factory.mktemp("segy") tmp_file = tmp_dir / "teapot.segy" urlretrieve(segy_input_uri, tmp_file) # noqa: S310 - return tmp_file @pytest.fixture(scope="module") def zarr_tmp(tmp_path_factory: pytest.TempPathFactory) -> Path: """Make a temp file for the output MDIO.""" + if DEBUG_MODE: + return Path("TMP/mdio") return tmp_path_factory.mktemp(r"mdio") @pytest.fixture(scope="module") def zarr_tmp2(tmp_path_factory: pytest.TempPathFactory) -> Path: """Make a temp file for the output MDIO.""" + if DEBUG_MODE: + return Path("TMP/mdio2") return tmp_path_factory.mktemp(r"mdio2") @pytest.fixture(scope="session") def segy_export_tmp(tmp_path_factory: pytest.TempPathFactory) -> Path: """Make a temp file for the round-trip IBM SEG-Y.""" - tmp_dir = tmp_path_factory.mktemp("segy") + if DEBUG_MODE: + tmp_dir = Path("TMP/segy") + tmp_dir.mkdir(parents=True, exist_ok=True) + else: + tmp_dir = tmp_path_factory.mktemp("segy") return tmp_dir / "teapot_roundtrip.segy" diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index f361e2e60..6140b2958 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -16,6 +16,27 @@ if TYPE_CHECKING: from pathlib import Path + from segy.schema import SegySpec + + +def _segy_spec_mock_4d() -> SegySpec: + """Create a mock SEG-Y spec for 4D data.""" + trace_header_fields = [ + HeaderField(name="field_rec_no", byte=9, format="int32"), + HeaderField(name="channel", byte=13, format="int32"), + HeaderField(name="shot_point", byte=17, format="int32"), + HeaderField(name="offset", byte=37, format="int32"), + HeaderField(name="samples_per_trace", byte=115, format="int32"), + HeaderField(name="sample_interval", byte=117, format="int32"), + HeaderField(name="shot_line", byte=133, format="int16"), + HeaderField(name="cable", byte=137, format="int16"), + HeaderField(name="gun", byte=171, format="int16"), + ] + rev1_spec = get_segy_standard(1.0) + spec = rev1_spec.customize(trace_header_fields=trace_header_fields) + spec.segy_standard = SegyStandard.REV1 + return spec + def create_segy_mock_4d( # noqa: PLR0913 fake_segy_tmp: Path, @@ -61,23 +82,8 @@ def create_segy_mock_4d( # noqa: PLR0913 cable_headers = np.tile(cable_headers, shot_count) channel_headers = np.tile(channel_headers, shot_count) - trace_header_fields = [ - HeaderField(name="field_rec_no", byte=9, format="int32"), - HeaderField(name="channel", byte=13, format="int32"), - HeaderField(name="shot_point", byte=17, format="int32"), - HeaderField(name="offset", byte=37, format="int32"), - HeaderField(name="samples_per_trace", byte=115, format="int32"), - HeaderField(name="sample_interval", byte=117, format="int32"), - HeaderField(name="shot_line", byte=133, format="int16"), - HeaderField(name="cable", byte=137, format="int16"), - HeaderField(name="gun", byte=171, format="int16"), - ] - - rev1_spec = get_segy_standard(1.0) - spec = rev1_spec.customize(trace_header_fields=trace_header_fields) - spec.segy_standard = SegyStandard.REV1 factory = SegyFactory( - spec=spec, + spec=_segy_spec_mock_4d(), sample_interval=1000, samples_per_trace=num_samples, ) diff --git a/tests/integration/test_segy_import_export.py b/tests/integration/test_segy_import_export.py index e80028df1..38409cb64 100644 --- a/tests/integration/test_segy_import_export.py +++ b/tests/integration/test_segy_import_export.py @@ -12,10 +12,9 @@ import pytest import xarray as xr from segy import SegyFile -from segy.standards import get_segy_standard from tests.integration.testing_data import binary_header_teapot_dome +from tests.integration.testing_data import custom_teapot_dome_segy_spec from tests.integration.testing_data import text_header_teapot_dome -from tests.integration.testing_helpers import customize_segy_specs from tests.integration.testing_helpers import get_inline_header_values from tests.integration.testing_helpers import get_values from tests.integration.testing_helpers import validate_variable @@ -246,39 +245,29 @@ def test_import_6d_segy( # noqa: PLR0913 @pytest.mark.dependency -@pytest.mark.parametrize("index_bytes", [(17, 13, 81, 85)]) -@pytest.mark.parametrize("index_names", [("inline", "crossline", "cdp_x", "cdp_y")]) -@pytest.mark.parametrize("index_types", [("int32", "int32", "int32", "int32")]) -def test_3d_import( - segy_input: Path, - zarr_tmp: Path, - index_bytes: tuple[int, ...], - index_names: tuple[str, ...], - index_types: tuple[str, ...], -) -> None: - """Test importing a SEG-Y file to MDIO.""" - segy_spec = get_segy_standard(1.0) - segy_spec = customize_segy_specs( - segy_spec=segy_spec, - index_bytes=index_bytes, - index_names=index_names, - index_types=index_types, - ) - - segy_to_mdio( - segy_spec=segy_spec, - mdio_template=TemplateRegistry().get("PostStack3DTime"), - input_location=StorageLocation(str(segy_input)), - output_location=StorageLocation(str(zarr_tmp)), - overwrite=True, - ) +class Test1_Import3D: # noqa N801 - The name is intentional. It indicates test ordering in the UI + """Test 3D PostStack SEG-Y import.""" + + def test_import_3d_segy( + self, + segy_input: Path, + zarr_tmp: Path, + ) -> None: + """Test importing a SEG-Y file to MDIO.""" + segy_to_mdio( + segy_spec=custom_teapot_dome_segy_spec(), + mdio_template=TemplateRegistry().get("PostStack3DTime"), + input_location=StorageLocation(str(segy_input)), + output_location=StorageLocation(str(zarr_tmp)), + overwrite=True, + ) @pytest.mark.dependency("test_3d_import") -class TestReader: +class Test2_Import3DValidation: # noqa N801 - The name is intentional. It indicates test ordering in the UI """Test reader functionality.""" - def test_meta_dataset_read(self, zarr_tmp: Path) -> None: + def test_meta_dataset(self, zarr_tmp: Path) -> None: """Metadata reading tests.""" # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN @@ -299,19 +288,20 @@ def test_meta_dataset_read(self, zarr_tmp: Path) -> None: attributes = ds.attrs["attributes"] assert attributes is not None - - # Validate attributes provided by the template + assert len(attributes) == 7 + # Validate all attribute provided by the abstract template + assert attributes["traceDomain"] == "time" + assert attributes["traceVariableName"] == "amplitude" + # Validate attributes provided by the PostStack3DTime template assert attributes["surveyDimensionality"] == "3D" assert attributes["ensembleType"] == "line" assert attributes["processingStage"] == "post-stack" - # Validate text header assert attributes["textHeader"] == text_header_teapot_dome() - # Validate binary header assert attributes["binaryHeader"] == binary_header_teapot_dome() - def test_meta_variable_read(self, zarr_tmp: Path) -> None: + def test_meta_variable(self, zarr_tmp: Path) -> None: """Metadata reading tests.""" # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN @@ -404,18 +394,18 @@ def test_zslice(self, zarr_tmp: Path) -> None: @pytest.mark.dependency("test_3d_import") -class TestExport: +class Test3_Export3D: # noqa N801 - The name is intentional. It indicates test ordering in the UI """Test SEG-Y exporting functionaliy.""" - def test_3d_export(self, zarr_tmp: Path, segy_export_tmp: Path) -> None: + def test_export_3d_mdio(self, segy_input: Path, zarr_tmp: Path, segy_export_tmp: Path) -> None: """Test 3D export to IBM and IEEE.""" mdio_to_segy( - mdio_path_or_buffer=zarr_tmp.__str__(), - output_segy_path=segy_export_tmp.__str__(), + segy_spec=custom_teapot_dome_segy_spec(), + input_location=StorageLocation(str(zarr_tmp)), + output_location=StorageLocation(str(segy_export_tmp)), ) - def test_size_equal(self, segy_input: Path, segy_export_tmp: Path) -> None: - """Check if file sizes match on IBM file.""" + # Check if file sizes match on IBM file. assert segy_input.stat().st_size == segy_export_tmp.stat().st_size def test_rand_equal(self, segy_input: Path, segy_export_tmp: Path) -> None: @@ -433,5 +423,7 @@ def test_rand_equal(self, segy_input: Path, segy_export_tmp: Path) -> None: assert in_segy.num_traces == out_segy.num_traces assert in_segy.text_header == out_segy.text_header assert in_segy.binary_header == out_segy.binary_header - npt.assert_array_equal(in_traces.header, out_traces.header) - npt.assert_array_equal(in_traces.sample, out_traces.sample) + # TODO (Dmitriy Repin): Reconcile custom SegySpecs used in the roundtrip SEGY -> MDIO -> SEGY tests + # https://github.com/TGSAI/mdio-python/issues/610 + # npt.assert_array_equal(desired=in_traces.header, actual=out_traces.header) + npt.assert_array_equal(desired=in_traces.sample, actual=out_traces.sample) diff --git a/tests/integration/test_segy_import_export_masked.py b/tests/integration/test_segy_import_export_masked.py index 5c310865f..c9a633ebf 100644 --- a/tests/integration/test_segy_import_export_masked.py +++ b/tests/integration/test_segy_import_export_masked.py @@ -21,17 +21,15 @@ from segy.schema import HeaderField from segy.schema import SegySpec from segy.standards import get_segy_standard +from tests.conftest import DEBUG_MODE -from mdio import MDIOReader from mdio import mdio_to_segy from mdio.converters.segy import segy_to_mdio from mdio.core.storage_location import StorageLocation from mdio.schemas.v1.templates.template_registry import TemplateRegistry -from mdio.segy.utilities import segy_export_rechunker if TYPE_CHECKING: from collections.abc import Iterable - from pathlib import Path from numpy.typing import NDArray @@ -150,10 +148,8 @@ def __iter__(self) -> Iterable[MaskedExportConfigTypes]: # fmt: on -def mock_nd_segy(path: str, grid_conf: GridConfig, segy_factory_conf: SegyFactoryConfig) -> SegySpec: - """Create a fake SEG-Y file with a multidimensional grid.""" +def _segy_spec_mock_nd_segy(grid_conf: GridConfig, segy_factory_conf: SegyFactoryConfig) -> SegySpec: spec = get_segy_standard(segy_factory_conf.revision) - header_flds = [] for dim in grid_conf.dims: byte_loc = segy_factory_conf.header_byte_map[dim.name] @@ -179,6 +175,12 @@ def mock_nd_segy(path: str, grid_conf: GridConfig, segy_factory_conf: SegyFactor spec = spec.customize(trace_header_fields=header_flds) spec.segy_standard = segy_factory_conf.revision + return spec + + +def mock_nd_segy(path: str, grid_conf: GridConfig, segy_factory_conf: SegyFactoryConfig) -> SegySpec: + """Create a fake SEG-Y file with a multidimensional grid.""" + spec = _segy_spec_mock_nd_segy(grid_conf, segy_factory_conf) factory = SegyFactory(spec=spec, samples_per_trace=segy_factory_conf.num_samples) dim_coords = () @@ -247,6 +249,8 @@ def generate_selection_mask(selection_conf: SelectionMaskConfig, grid_conf: Grid @pytest.fixture def export_masked_path(tmp_path_factory: pytest.TempPathFactory) -> Path: """Fixture that generates temp directory for export tests.""" + if DEBUG_MODE: + return Path("TMP/export_masked") return tmp_path_factory.getbasetemp() / "export_masked" @@ -260,7 +264,7 @@ def export_masked_path(tmp_path_factory: pytest.TempPathFactory) -> Path: class TestNdImportExport: """Test import/export of n-D SEG-Ys to MDIO, with and without selection mask.""" - def test_import(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: + def test_1_segy_to_mdio(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: """Test import of an n-D SEG-Y file to MDIO.""" grid_conf, segy_factory_conf, segy_to_mdio_conf, _ = test_conf @@ -297,7 +301,7 @@ def test_import(self, test_conf: MaskedExportConfig, export_masked_path: Path) - overwrite=True, ) - def test_ingested_mdio(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: + def test_2_validate_mdio(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: """Verify if ingested data is correct.""" grid_conf, segy_factory_conf, segy_to_mdio_conf, _ = test_conf mdio_path = export_masked_path / f"{grid_conf.name}.mdio" @@ -352,7 +356,7 @@ def test_ingested_mdio(self, test_conf: MaskedExportConfig, export_masked_path: assert np.array_equal(actual, expected) - def test_export(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: + def test_3_mdio_to_segy(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: """Test export of an n-D MDIO file back to SEG-Y.""" grid_conf, segy_factory_conf, segy_to_mdio_conf, _ = test_conf @@ -360,25 +364,31 @@ def test_export(self, test_conf: MaskedExportConfig, export_masked_path: Path) - mdio_path = export_masked_path / f"{grid_conf.name}.mdio" segy_rt_path = export_masked_path / f"{grid_conf.name}_rt.sgy" - index_names = segy_factory_conf.header_byte_map.keys() - access_pattern = "".join(map(str, range(len(index_names) + 1))) - mdio = MDIOReader(mdio_path.__str__(), access_pattern=access_pattern) - - chunks, shape = mdio.chunks, mdio.shape - new_chunks = segy_export_rechunker(chunks, shape, dtype="float32", limit="0.3M") - mdio_to_segy( - mdio_path.__str__(), - segy_rt_path.__str__(), - access_pattern=access_pattern, - new_chunks=new_chunks, + segy_spec=_segy_spec_mock_nd_segy(grid_conf, segy_factory_conf), + input_location=StorageLocation(str(mdio_path)), + output_location=StorageLocation(str(segy_rt_path)) ) expected_sgy = SegyFile(segy_path) actual_sgy = SegyFile(segy_rt_path) - assert_array_equal(actual_sgy.trace[:], expected_sgy.trace[:]) - def test_export_masked(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: + num_traces = expected_sgy.num_traces + random_indices = np.random.choice(num_traces, 10, replace=False) + expected_traces = expected_sgy.trace[random_indices] + actual_traces = actual_sgy.trace[random_indices] + + assert expected_sgy.num_traces == actual_sgy.num_traces + assert expected_sgy.text_header == actual_sgy.text_header + assert expected_sgy.binary_header == actual_sgy.binary_header + + # TODO (Dmitriy Repin): Reconcile custom SegySpecs used in the roundtrip SEGY -> MDIO -> SEGY tests + # https://github.com/TGSAI/mdio-python/issues/610 + # assert_array_equal(desired=expected_traces.header, actual=actual_traces.header) + assert_array_equal(desired=expected_traces.sample, actual=actual_traces.sample) + + + def test_4_mdio_to_segy_masked(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: """Test export of an n-D MDIO file back to SEG-Y with masked export.""" grid_conf, segy_factory_conf, segy_to_mdio_conf, selection_conf = test_conf @@ -386,23 +396,20 @@ def test_export_masked(self, test_conf: MaskedExportConfig, export_masked_path: mdio_path = export_masked_path / f"{grid_conf.name}.mdio" segy_rt_path = export_masked_path / f"{grid_conf.name}_rt.sgy" - index_names = segy_factory_conf.header_byte_map.keys() - access_pattern = "".join(map(str, range(len(index_names) + 1))) - mdio = MDIOReader(mdio_path.__str__(), access_pattern=access_pattern) - export_chunks = segy_export_rechunker( - mdio.chunks, mdio.shape, dtype="float32", limit="0.3M" - ) selection_mask = generate_selection_mask(selection_conf, grid_conf) mdio_to_segy( - mdio_path.__str__(), - segy_rt_path.__str__(), - access_pattern=access_pattern, - new_chunks=export_chunks, - selection_mask=selection_mask, + segy_spec=_segy_spec_mock_nd_segy(grid_conf, segy_factory_conf), + input_location=StorageLocation(str(mdio_path)), + output_location=StorageLocation(str(segy_rt_path)), + selection_mask=selection_mask ) expected_trc_idx = selection_mask.ravel().nonzero()[0] expected_sgy = SegyFile(segy_path) actual_sgy = SegyFile(segy_rt_path) - assert_array_equal(actual_sgy.trace[:], expected_sgy.trace[expected_trc_idx]) + + # TODO (Dmitriy Repin): Reconcile custom SegySpecs used in the roundtrip SEGY -> MDIO -> SEGY tests + # https://github.com/TGSAI/mdio-python/issues/610 + # assert_array_equal(actual_sgy.trace[:].header, expected_sgy.trace[expected_trc_idx].header) + assert_array_equal(actual_sgy.trace[:].sample, expected_sgy.trace[expected_trc_idx].sample) diff --git a/tests/integration/testing_data.py b/tests/integration/testing_data.py index 3696a7cf1..850f0f34c 100644 --- a/tests/integration/testing_data.py +++ b/tests/integration/testing_data.py @@ -1,5 +1,23 @@ """Integration tests data for teapot dome SEG-Y.""" +from segy.schema import SegySpec +from segy.standards import get_segy_standard +from tests.integration.testing_helpers import customize_segy_specs + + +def custom_teapot_dome_segy_spec() -> SegySpec: + """Return the minimum customized SEG-Y specification for the teapot dome dataset.""" + index_bytes: tuple[int, ...] = (17, 13, 81, 85) + index_names: tuple[str, ...] = ("inline", "crossline", "cdp_x", "cdp_y") + index_types: tuple[str, ...] = ("int32", "int32", "int32", "int32") + segy_spec = get_segy_standard(1.0) + return customize_segy_specs( + segy_spec=segy_spec, + index_bytes=index_bytes, + index_names=index_names, + index_types=index_types, + ) + def text_header_teapot_dome() -> list[str]: """Return the teapot dome expected text header.""" From 04ea8a4c932b70b587fc0336207b8bc1e437a6aa Mon Sep 17 00:00:00 2001 From: Dmitriy Repin Date: Thu, 21 Aug 2025 17:37:43 +0000 Subject: [PATCH 02/24] Enable header value validation --- tests/integration/test_segy_import_export.py | 21 ++++----- .../test_segy_import_export_masked.py | 4 +- tests/integration/testing_data.py | 19 +++++++- tests/integration/testing_helpers.py | 45 ++++++++++++++++--- 4 files changed, 70 insertions(+), 19 deletions(-) diff --git a/tests/integration/test_segy_import_export.py b/tests/integration/test_segy_import_export.py index 38409cb64..0200650e1 100644 --- a/tests/integration/test_segy_import_export.py +++ b/tests/integration/test_segy_import_export.py @@ -254,8 +254,9 @@ def test_import_3d_segy( zarr_tmp: Path, ) -> None: """Test importing a SEG-Y file to MDIO.""" + # TODO: Run the tests both with and without keeping unaltered fields segy_to_mdio( - segy_spec=custom_teapot_dome_segy_spec(), + segy_spec=custom_teapot_dome_segy_spec(keep_unaltered=True), mdio_template=TemplateRegistry().get("PostStack3DTime"), input_location=StorageLocation(str(segy_input)), output_location=StorageLocation(str(zarr_tmp)), @@ -337,9 +338,10 @@ def test_grid(self, zarr_tmp: Path) -> None: validate_variable(ds, "cdp_y", (345, 188), ["inline", "crossline"], np.float64, None, None) # Validate the headers - # We have a subset of headers since we used customize_segy_specs() providing the values only - # for "inline", "crossline", "cdp_x", "cdp_y" - data_type = np.dtype([("inline", " None: """Test 3D export to IBM and IEEE.""" + spec = custom_teapot_dome_segy_spec(keep_unaltered=True) + mdio_to_segy( - segy_spec=custom_teapot_dome_segy_spec(), + segy_spec=spec, input_location=StorageLocation(str(zarr_tmp)), output_location=StorageLocation(str(segy_export_tmp)), ) @@ -408,10 +412,7 @@ def test_export_3d_mdio(self, segy_input: Path, zarr_tmp: Path, segy_export_tmp: # Check if file sizes match on IBM file. assert segy_input.stat().st_size == segy_export_tmp.stat().st_size - def test_rand_equal(self, segy_input: Path, segy_export_tmp: Path) -> None: - """IBM. Is random original traces and headers match round-trip file?""" - spec = mdio_segy_spec() - + # IBM. Is random original traces and headers match round-trip file? in_segy = SegyFile(segy_input, spec=spec) out_segy = SegyFile(segy_export_tmp, spec=spec) @@ -425,5 +426,5 @@ def test_rand_equal(self, segy_input: Path, segy_export_tmp: Path) -> None: assert in_segy.binary_header == out_segy.binary_header # TODO (Dmitriy Repin): Reconcile custom SegySpecs used in the roundtrip SEGY -> MDIO -> SEGY tests # https://github.com/TGSAI/mdio-python/issues/610 - # npt.assert_array_equal(desired=in_traces.header, actual=out_traces.header) + npt.assert_array_equal(desired=in_traces.header, actual=out_traces.header) npt.assert_array_equal(desired=in_traces.sample, actual=out_traces.sample) diff --git a/tests/integration/test_segy_import_export_masked.py b/tests/integration/test_segy_import_export_masked.py index c9a633ebf..86e5a090b 100644 --- a/tests/integration/test_segy_import_export_masked.py +++ b/tests/integration/test_segy_import_export_masked.py @@ -384,7 +384,7 @@ def test_3_mdio_to_segy(self, test_conf: MaskedExportConfig, export_masked_path: # TODO (Dmitriy Repin): Reconcile custom SegySpecs used in the roundtrip SEGY -> MDIO -> SEGY tests # https://github.com/TGSAI/mdio-python/issues/610 - # assert_array_equal(desired=expected_traces.header, actual=actual_traces.header) + assert_array_equal(desired=expected_traces.header, actual=actual_traces.header) assert_array_equal(desired=expected_traces.sample, actual=actual_traces.sample) @@ -411,5 +411,5 @@ def test_4_mdio_to_segy_masked(self, test_conf: MaskedExportConfig, export_maske # TODO (Dmitriy Repin): Reconcile custom SegySpecs used in the roundtrip SEGY -> MDIO -> SEGY tests # https://github.com/TGSAI/mdio-python/issues/610 - # assert_array_equal(actual_sgy.trace[:].header, expected_sgy.trace[expected_trc_idx].header) + assert_array_equal(actual_sgy.trace[:].header, expected_sgy.trace[expected_trc_idx].header) assert_array_equal(actual_sgy.trace[:].sample, expected_sgy.trace[expected_trc_idx].sample) diff --git a/tests/integration/testing_data.py b/tests/integration/testing_data.py index 850f0f34c..b61263e47 100644 --- a/tests/integration/testing_data.py +++ b/tests/integration/testing_data.py @@ -5,8 +5,22 @@ from tests.integration.testing_helpers import customize_segy_specs -def custom_teapot_dome_segy_spec() -> SegySpec: - """Return the minimum customized SEG-Y specification for the teapot dome dataset.""" +def custom_teapot_dome_segy_spec(keep_unaltered: bool) -> SegySpec: + """Return the minimum customized SEG-Y specification for the teapot dome dataset. + + In SEG-Y spec rev 1.0: + inline = (189, "int32") + crossline = (193, "int32") + cdp_x = (181, "int32") + cdp_y = (185, "int32") + and + trace_num_orig_record = (13, "int32") + energy_source_point_num = (17, "int32") + group_coord_x = (81, "int32") + group_coord_y = (85, "int32") + + In SEGY 1.0 - 2.1, the trace header contains unassigned bytes 181-240. + """ index_bytes: tuple[int, ...] = (17, 13, 81, 85) index_names: tuple[str, ...] = ("inline", "crossline", "cdp_x", "cdp_y") index_types: tuple[str, ...] = ("int32", "int32", "int32", "int32") @@ -16,6 +30,7 @@ def custom_teapot_dome_segy_spec() -> SegySpec: index_bytes=index_bytes, index_names=index_names, index_types=index_types, + keep_unaltered = keep_unaltered ) diff --git a/tests/integration/testing_helpers.py b/tests/integration/testing_helpers.py index 0a33fbd69..bcc695149 100644 --- a/tests/integration/testing_helpers.py +++ b/tests/integration/testing_helpers.py @@ -13,6 +13,7 @@ def customize_segy_specs( index_bytes: tuple[int, ...] | None = None, index_names: tuple[int, ...] | None = None, index_types: tuple[int, ...] | None = None, + keep_unaltered: bool = False ) -> SegySpec: """Customize SEG-Y specifications with user-defined index fields.""" if not index_bytes: @@ -26,13 +27,19 @@ def customize_segy_specs( err = "All index fields must have the same length." raise ValueError(err) + fields = {} + if keep_unaltered: + # Keep unaltered fields, but remove the fields that are being customized + # to avoid field name duplication + for f in segy_spec.trace.header.fields: + if f.name not in index_names: + fields[f.byte] = f + # Index the dataset using a spec that interprets the user provided index headers. - index_fields = [] for name, byte, format_ in zip(index_names, index_bytes, index_types, strict=True): - index_fields.append(HeaderField(name=name, byte=byte, format=format_)) - - return segy_spec.customize(trace_header_fields=index_fields) + fields[byte] = HeaderField(name=name, byte=byte, format=format_) + return segy_spec.customize(trace_header_fields=fields.values()) def get_values(arr: xr.DataArray) -> np.ndarray: """Extract actual values from an Xarray DataArray.""" @@ -57,7 +64,35 @@ def validate_variable( # noqa PLR0913 arr = dataset[name] assert shape == arr.shape assert set(dims) == set(arr.dims) - assert data_type == arr.dtype + if hasattr(data_type, "fields") and data_type.fields is not None: + # The following assertion will fail because of differences in endianness and offsets + # assert data_type == arr.dtype + + # Compare field names + expected_names = [name for name in data_type.names] + actual_names = [name for name in arr.dtype.names] + assert expected_names == actual_names + + # Compare field types ignoring endianness + expected_types = [data_type[name].newbyteorder('=') for name in data_type.names] + actual_types = [arr.dtype[name].newbyteorder('=') for name in arr.dtype.names] + assert expected_types == actual_types + + # Compare field offsets (fails): + # name: 'shot_point' dt_exp: (dtype('>i4'), 196) dt_act: (dtype(' Date: Mon, 25 Aug 2025 20:34:07 +0000 Subject: [PATCH 03/24] Revert the test names back --- tests/integration/test_segy_import_export.py | 48 +++++++++++-------- .../test_segy_import_export_masked.py | 32 +++++++++---- 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/tests/integration/test_segy_import_export.py b/tests/integration/test_segy_import_export.py index 0200650e1..5250aed94 100644 --- a/tests/integration/test_segy_import_export.py +++ b/tests/integration/test_segy_import_export.py @@ -245,28 +245,30 @@ def test_import_6d_segy( # noqa: PLR0913 @pytest.mark.dependency -class Test1_Import3D: # noqa N801 - The name is intentional. It indicates test ordering in the UI - """Test 3D PostStack SEG-Y import.""" - - def test_import_3d_segy( - self, +def test_3d_import( segy_input: Path, zarr_tmp: Path, - ) -> None: - """Test importing a SEG-Y file to MDIO.""" - # TODO: Run the tests both with and without keeping unaltered fields - segy_to_mdio( - segy_spec=custom_teapot_dome_segy_spec(keep_unaltered=True), - mdio_template=TemplateRegistry().get("PostStack3DTime"), - input_location=StorageLocation(str(segy_input)), - output_location=StorageLocation(str(zarr_tmp)), - overwrite=True, - ) +) -> None: + """Test importing a SEG-Y file to MDIO. + + NOTE: This test must be executed before the 'TestReader' and 'TestExport' tests. + """ + segy_to_mdio( + segy_spec=custom_teapot_dome_segy_spec(keep_unaltered=True), + mdio_template=TemplateRegistry().get("PostStack3DTime"), + input_location=StorageLocation(str(segy_input)), + output_location=StorageLocation(str(zarr_tmp)), + overwrite=True, + ) @pytest.mark.dependency("test_3d_import") -class Test2_Import3DValidation: # noqa N801 - The name is intentional. It indicates test ordering in the UI - """Test reader functionality.""" +class TestReader: + """Test reader functionality. + + NOTE: This tests must be executed after the 'test_3d_import' successfully completes and + before running 'TestExport' tests. + """ def test_meta_dataset(self, zarr_tmp: Path) -> None: """Metadata reading tests.""" @@ -396,10 +398,14 @@ def test_zslice(self, zarr_tmp: Path) -> None: @pytest.mark.dependency("test_3d_import") -class Test3_Export3D: # noqa N801 - The name is intentional. It indicates test ordering in the UI - """Test SEG-Y exporting functionaliy.""" - - def test_export_3d_mdio(self, segy_input: Path, zarr_tmp: Path, segy_export_tmp: Path) -> None: +class TestExport: + """Test SEG-Y exporting functionality. + + NOTE: This test(s) must be executed after the 'test_3d_import' and 'TestReader' tests + successfully complete. + """ + + def test_3d_export(self, segy_input: Path, zarr_tmp: Path, segy_export_tmp: Path) -> None: """Test 3D export to IBM and IEEE.""" spec = custom_teapot_dome_segy_spec(keep_unaltered=True) diff --git a/tests/integration/test_segy_import_export_masked.py b/tests/integration/test_segy_import_export_masked.py index 86e5a090b..be636574e 100644 --- a/tests/integration/test_segy_import_export_masked.py +++ b/tests/integration/test_segy_import_export_masked.py @@ -264,8 +264,12 @@ def export_masked_path(tmp_path_factory: pytest.TempPathFactory) -> Path: class TestNdImportExport: """Test import/export of n-D SEG-Ys to MDIO, with and without selection mask.""" - def test_1_segy_to_mdio(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: - """Test import of an n-D SEG-Y file to MDIO.""" + def test_import(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: + """Test import of an n-D SEG-Y file to MDIO. + + NOTE: This test must be executed before running 'test_ingested_mdio', 'test_export', and + 'test_export_masked' tests. + """ grid_conf, segy_factory_conf, segy_to_mdio_conf, _ = test_conf domain = "Time" @@ -301,8 +305,12 @@ def test_1_segy_to_mdio(self, test_conf: MaskedExportConfig, export_masked_path: overwrite=True, ) - def test_2_validate_mdio(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: - """Verify if ingested data is correct.""" + def test_ingested_mdio(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: + """Verify if ingested data is correct. + + NOTE: This test must be executed after the 'test_import' successfully completes + and before running 'test_export' and 'test_export_masked' tests. + """ grid_conf, segy_factory_conf, segy_to_mdio_conf, _ = test_conf mdio_path = export_masked_path / f"{grid_conf.name}.mdio" @@ -356,8 +364,12 @@ def test_2_validate_mdio(self, test_conf: MaskedExportConfig, export_masked_path assert np.array_equal(actual, expected) - def test_3_mdio_to_segy(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: - """Test export of an n-D MDIO file back to SEG-Y.""" + def test_export(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: + """Test export of an n-D MDIO file back to SEG-Y. + + NOTE: This test must be executed after the 'test_import' and 'test_ingested_mdio' + successfully complete. + """ grid_conf, segy_factory_conf, segy_to_mdio_conf, _ = test_conf segy_path = export_masked_path / f"{grid_conf.name}.sgy" @@ -388,8 +400,12 @@ def test_3_mdio_to_segy(self, test_conf: MaskedExportConfig, export_masked_path: assert_array_equal(desired=expected_traces.sample, actual=actual_traces.sample) - def test_4_mdio_to_segy_masked(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: - """Test export of an n-D MDIO file back to SEG-Y with masked export.""" + def test_export_masked(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: + """Test export of an n-D MDIO file back to SEG-Y with masked export. + + NOTE: This test must be executed after the 'test_import' and 'test_ingested_mdio' + successfully complete. + """ grid_conf, segy_factory_conf, segy_to_mdio_conf, selection_conf = test_conf segy_path = export_masked_path / f"{grid_conf.name}.sgy" From c5cefdeb664e78a9f953bef5c82daaa053bb4fd3 Mon Sep 17 00:00:00 2001 From: Dmitriy Repin Date: Mon, 25 Aug 2025 21:18:53 +0000 Subject: [PATCH 04/24] Remove Endianness, new_chunks API args and traceDomain, --- src/mdio/converters/mdio.py | 22 +++---------------- .../v1/templates/abstract_dataset_template.py | 1 - src/mdio/segy/creation.py | 9 ++------ tests/integration/test_segy_import_export.py | 1 - tests/integration/testing_helpers.py | 21 +++++------------- 5 files changed, 11 insertions(+), 43 deletions(-) diff --git a/src/mdio/converters/mdio.py b/src/mdio/converters/mdio.py index f85c0ec94..75fa61c86 100644 --- a/src/mdio/converters/mdio.py +++ b/src/mdio/converters/mdio.py @@ -49,8 +49,6 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 segy_spec: SegySpec, input_location: StorageLocation, output_location: StorageLocation, - endian: str = "big", - new_chunks: tuple[int, ...] = None, selection_mask: np.ndarray = None, client: distributed.Client = None, ) -> None: @@ -68,8 +66,6 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 segy_spec: The SEG-Y specification to use for the conversion. input_location: Store or URL (and cloud options) for MDIO file. output_location: Path to the output SEG-Y file. - endian: Endianness of the input SEG-Y. Rev.2 allows little endian. Default is 'big'. - new_chunks: Set manual chunksize. For development purposes only. selection_mask: Array that lists the subset of traces client: Dask client. If `None` we will use local threaded scheduler. If `auto` is used we will create multiple processes (with 8 threads each). @@ -80,8 +76,7 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 Examples: To export an existing local MDIO file to SEG-Y we use the code snippet below. This will - export the full MDIO (without padding) to SEG-Y format using IBM floats and big-endian - byte order. + export the full MDIO (without padding) to SEG-Y format. >>> from mdio import mdio_to_segy >>> @@ -90,15 +85,6 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 ... mdio_path_or_buffer="prefix2/file.mdio", ... output_segy_path="prefix/file.segy", ... ) - - If we want to export this as an IEEE big-endian, using a selection mask, we would run: - - >>> mdio_to_segy( - ... mdio_path_or_buffer="prefix2/file.mdio", - ... output_segy_path="prefix/file.segy", - ... selection_mask=boolean_mask, - ... ) - """ output_segy_path = Path(output_location.uri) @@ -109,11 +95,9 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 chunks: tuple[int, ...] = amplitude.encoding.get("chunks") shape: tuple[int, ...] = amplitude.shape dtype = amplitude.dtype - if new_chunks is None: - new_chunks = segy_export_rechunker(chunks, shape, dtype) - mdio_xr.close() + new_chunks = segy_export_rechunker(chunks, shape, dtype) - creation_args = [segy_spec, input_location, output_location, endian] + creation_args = [segy_spec, input_location, output_location] if client is not None: if distributed is not None: diff --git a/src/mdio/schemas/v1/templates/abstract_dataset_template.py b/src/mdio/schemas/v1/templates/abstract_dataset_template.py index eb7050f76..ef10ecbe7 100644 --- a/src/mdio/schemas/v1/templates/abstract_dataset_template.py +++ b/src/mdio/schemas/v1/templates/abstract_dataset_template.py @@ -85,7 +85,6 @@ def build_dataset( self._horizontal_coord_unit = horizontal_coord_unit attr = self._load_dataset_attributes() or UserAttributes(attributes={}) - attr.attributes["traceDomain"] = self._trace_domain attr.attributes["traceVariableName"] = self._trace_variable_name self._builder = MDIODatasetBuilder(name=name, attributes=attr) self._add_dimensions() diff --git a/src/mdio/segy/creation.py b/src/mdio/segy/creation.py index 6bd82669b..01ec14c15 100644 --- a/src/mdio/segy/creation.py +++ b/src/mdio/segy/creation.py @@ -11,7 +11,6 @@ import numpy as np import xarray as xr from segy.factory import SegyFactory -from segy.schema import Endianness from segy.schema import SegySpec from tqdm.auto import tqdm from xarray import Dataset as xr_Dataset @@ -42,8 +41,7 @@ def make_segy_factory(mdio_xr: xr_Dataset, spec: SegySpec) -> SegyFactory: def mdio_spec_to_segy( segy_spec: SegySpec, input_location: StorageLocation, - output_location: StorageLocation, - output_endian: str, + output_location: StorageLocation ) -> tuple[xr_Dataset, SegyFactory]: """Create SEG-Y file without any traces given MDIO specification. @@ -59,7 +57,6 @@ def mdio_spec_to_segy( segy_spec: The SEG-Y specification to use for the conversion. input_location: Store or URL (and cloud options) for MDIO file. output_location: Path to the output SEG-Y file. - output_endian: Endianness of the output file. Returns: Opened Xarray Dataset for MDIO file and SegyFactory @@ -67,9 +64,7 @@ def mdio_spec_to_segy( mdio_xr = xr.open_dataset(input_location.uri, engine="zarr", mask_and_scale=False) mdio_file_version = mdio_xr.attrs["apiVersion"] - spec = segy_spec - spec.endianness = Endianness(output_endian) - factory = make_segy_factory(mdio_xr, spec=spec) + factory = make_segy_factory(mdio_xr, spec=segy_spec) attr = mdio_xr.attrs["attributes"] diff --git a/tests/integration/test_segy_import_export.py b/tests/integration/test_segy_import_export.py index 5250aed94..6685a41f1 100644 --- a/tests/integration/test_segy_import_export.py +++ b/tests/integration/test_segy_import_export.py @@ -293,7 +293,6 @@ def test_meta_dataset(self, zarr_tmp: Path) -> None: assert attributes is not None assert len(attributes) == 7 # Validate all attribute provided by the abstract template - assert attributes["traceDomain"] == "time" assert attributes["traceVariableName"] == "amplitude" # Validate attributes provided by the PostStack3DTime template assert attributes["surveyDimensionality"] == "3D" diff --git a/tests/integration/testing_helpers.py b/tests/integration/testing_helpers.py index bcc695149..79fdf7c86 100644 --- a/tests/integration/testing_helpers.py +++ b/tests/integration/testing_helpers.py @@ -65,7 +65,7 @@ def validate_variable( # noqa PLR0913 assert shape == arr.shape assert set(dims) == set(arr.dims) if hasattr(data_type, "fields") and data_type.fields is not None: - # The following assertion will fail because of differences in endianness and offsets + # The following assertion will fail because of differences in offsets # assert data_type == arr.dtype # Compare field names @@ -73,23 +73,14 @@ def validate_variable( # noqa PLR0913 actual_names = [name for name in arr.dtype.names] assert expected_names == actual_names - # Compare field types ignoring endianness - expected_types = [data_type[name].newbyteorder('=') for name in data_type.names] - actual_types = [arr.dtype[name].newbyteorder('=') for name in arr.dtype.names] + # Compare field types + expected_types = [data_type[name] for name in data_type.names] + actual_types = [arr.dtype[name] for name in arr.dtype.names] assert expected_types == actual_types - # Compare field offsets (fails): + # Compare field offsets fails. + # However, we believe this is acceptable and do not compare offsets # name: 'shot_point' dt_exp: (dtype('>i4'), 196) dt_act: (dtype(' Date: Mon, 25 Aug 2025 23:25:54 +0000 Subject: [PATCH 05/24] PR review --- src/mdio/converters/mdio.py | 38 +++++-------------- src/mdio/core/utils_read.py | 16 ++++++++ src/mdio/segy/creation.py | 8 ++-- tests/integration/test_segy_import_export.py | 13 ++++--- .../test_segy_import_export_masked.py | 5 +-- 5 files changed, 39 insertions(+), 41 deletions(-) create mode 100644 src/mdio/core/utils_read.py diff --git a/src/mdio/converters/mdio.py b/src/mdio/converters/mdio.py index 75fa61c86..41fc4dd33 100644 --- a/src/mdio/converters/mdio.py +++ b/src/mdio/converters/mdio.py @@ -13,10 +13,11 @@ from psutil import cpu_count from tqdm.dask import TqdmCallback +from mdio.core.utils_read import open_zarr_dataset from mdio.segy.blocked_io import to_segy from mdio.segy.creation import concat_files from mdio.segy.creation import mdio_spec_to_segy -from mdio.segy.utilities import segy_export_rechunker +from mdio.segy.utilities import segy_export_rechunker try: import distributed @@ -31,20 +32,6 @@ default_cpus = cpu_count(logical=True) NUM_CPUS = int(os.getenv("MDIO__EXPORT__CPU_COUNT", default_cpus)) - -def _get_dask_array(mdio_xr: xr.Dataset, var_name: str, chunks: tuple[int, ...] = None) -> da.Array: - """Workaround if the MDIO Xarray dataset returns numpy arrays instead of Dask arrays""" - xr_var = mdio_xr[var_name] - # xr_var.chunks: - # Tuple of block lengths for this dataarray’s data, in order of dimensions, - # or None if the underlying data is not a dask array. - if xr_var.chunks is not None: - return xr_var.data.rechunk(chunks) - # For some reason, a NumPy in-memory array was returned - # HACK: Convert NumPy array to a chunked Dask array - return da.from_array(xr_var.data, chunks=chunks) - - def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 segy_spec: SegySpec, input_location: StorageLocation, @@ -88,12 +75,12 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 """ output_segy_path = Path(output_location.uri) - mdio_xr = xr.open_dataset(input_location.uri, engine="zarr", mask_and_scale=False) + mdio_xr = open_zarr_dataset(input_location) trace_variable_name = mdio_xr.attrs["attributes"]["traceVariableName"] amplitude = mdio_xr[trace_variable_name] - chunks: tuple[int, ...] = amplitude.encoding.get("chunks") - shape: tuple[int, ...] = amplitude.shape + chunks: tuple[int, ...] = amplitude.data.chunksize + shape: tuple[int, ...] = amplitude.data.shape dtype = amplitude.dtype new_chunks = segy_export_rechunker(chunks, shape, dtype) @@ -110,9 +97,7 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 else: mdio_xr, segy_factory = mdio_spec_to_segy(*creation_args) - # Using XArray.DataArray.values should trigger compute and load the whole array into memory. - live_mask = mdio_xr["trace_mask"].values - # live_mask = mdio.live_mask.compute() + live_mask = mdio_xr["trace_mask"].data.compute() if selection_mask is not None: live_mask = live_mask & selection_mask @@ -132,12 +117,9 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 dim_slices += (slice(start, stop),) # Lazily pull the data with limits now, and limit mask so its the same shape. - # Workaround: currently the MDIO Xarray dataset returns numpy arrays instead of Dask arrays - # TODO (Dmitriy Repin): Revisit after the eager memory allocation is fixed - # https://github.com/TGSAI/mdio-python/issues/608 - live_mask = _get_dask_array(mdio_xr, "trace_mask", new_chunks[:-1])[dim_slices] - headers = _get_dask_array(mdio_xr, "headers", new_chunks[:-1])[dim_slices] - samples = _get_dask_array(mdio_xr, "amplitude", new_chunks)[dim_slices] + live_mask = mdio_xr["trace_mask"].data.rechunk(new_chunks[:-1])[dim_slices] + headers = mdio_xr["headers"].data.rechunk(new_chunks[:-1])[dim_slices] + samples = mdio_xr["amplitude"].data.rechunk(new_chunks)[dim_slices] if selection_mask is not None: selection_mask = selection_mask[dim_slices] @@ -168,4 +150,4 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 if client is not None: _ = client.submit(concat_files, paths=ordered_files).result() else: - concat_files(paths=ordered_files, progress=True) + concat_files(paths=ordered_files, progress=True) \ No newline at end of file diff --git a/src/mdio/core/utils_read.py b/src/mdio/core/utils_read.py new file mode 100644 index 000000000..6c76d2474 --- /dev/null +++ b/src/mdio/core/utils_read.py @@ -0,0 +1,16 @@ +"""Utils for reading MDIO dataset""" + +from typing import TYPE_CHECKING + +from xarray import open_dataset as xr_open_dataset +from mdio.core.storage_location import StorageLocation +from xarray import Dataset as xr_Dataset + +def open_zarr_dataset(storage_location: StorageLocation) -> xr_Dataset: + """ + Open a Zarr dataset from the specified storage location. + """ + # NOTE: If mask_and_scale is not set, + # Xarray will convert int to float and replace _FillValue with NaN + # We are using chunks={} to force Xarray to create Dask arrays + return xr_open_dataset(storage_location.uri, engine="zarr", chunks={}, mask_and_scale=False) \ No newline at end of file diff --git a/src/mdio/segy/creation.py b/src/mdio/segy/creation.py index 01ec14c15..903a1c07a 100644 --- a/src/mdio/segy/creation.py +++ b/src/mdio/segy/creation.py @@ -15,6 +15,7 @@ from tqdm.auto import tqdm from xarray import Dataset as xr_Dataset +from mdio.core.utils_read import open_zarr_dataset from mdio.segy.compat import revision_encode if TYPE_CHECKING: @@ -33,7 +34,7 @@ def make_segy_factory(mdio_xr: xr_Dataset, spec: SegySpec) -> SegyFactory: samples_per_trace = binary_header["samples_per_trace"] return SegyFactory( spec=spec, - sample_interval=sample_interval, # Sample Interval is already in microseconds + sample_interval=sample_interval, # Sample Interval is already in milliseconds samples_per_trace=samples_per_trace, ) @@ -61,9 +62,7 @@ def mdio_spec_to_segy( Returns: Opened Xarray Dataset for MDIO file and SegyFactory """ - mdio_xr = xr.open_dataset(input_location.uri, engine="zarr", mask_and_scale=False) - - mdio_file_version = mdio_xr.attrs["apiVersion"] + mdio_xr = open_zarr_dataset(input_location) factory = make_segy_factory(mdio_xr, spec=segy_spec) attr = mdio_xr.attrs["attributes"] @@ -73,6 +72,7 @@ def mdio_spec_to_segy( text_bytes = factory.create_textual_header(text_str) bin_header = attr["binaryHeader"] + mdio_file_version = mdio_xr.attrs["apiVersion"] binary_header = revision_encode(bin_header, mdio_file_version) bin_hdr_bytes = factory.create_binary_header(binary_header) diff --git a/tests/integration/test_segy_import_export.py b/tests/integration/test_segy_import_export.py index 6685a41f1..54842c6fe 100644 --- a/tests/integration/test_segy_import_export.py +++ b/tests/integration/test_segy_import_export.py @@ -22,6 +22,7 @@ from mdio import MDIOReader from mdio import mdio_to_segy from mdio.converters.exceptions import GridTraceSparsityError +from mdio.core.utils_read import open_zarr_dataset from mdio.converters.segy import segy_to_mdio from mdio.core import Dimension from mdio.core.storage_location import StorageLocation @@ -274,7 +275,7 @@ def test_meta_dataset(self, zarr_tmp: Path) -> None: """Metadata reading tests.""" # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN - ds = xr.open_dataset(zarr_tmp, engine="zarr", mask_and_scale=False) + ds = open_zarr_dataset(StorageLocation(str(zarr_tmp))) expected_attrs = { "apiVersion": "1.0.0a1", "createdOn": "2025-08-06 16:21:54.747880+00:00", @@ -307,7 +308,7 @@ def test_meta_variable(self, zarr_tmp: Path) -> None: """Metadata reading tests.""" # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN - ds = xr.open_dataset(zarr_tmp, engine="zarr", mask_and_scale=False) + ds = open_zarr_dataset(StorageLocation(str(zarr_tmp))) expected_attrs = { "count": 97354860, "sum": -8594.551666259766, @@ -324,7 +325,7 @@ def test_grid(self, zarr_tmp: Path) -> None: # Load Xarray dataset from the MDIO file # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN - ds = xr.open_dataset(zarr_tmp, engine="zarr", mask_and_scale=False) + ds = open_zarr_dataset(StorageLocation(str(zarr_tmp))) # Note: in order to create the dataset we used the Time template, so the # sample dimension is called "time" @@ -371,7 +372,7 @@ def test_inline(self, zarr_tmp: Path) -> None: """Read and compare every 75 inlines' mean and std. dev.""" # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN - ds = xr.open_dataset(zarr_tmp, engine="zarr", mask_and_scale=False) + ds = open_zarr_dataset(StorageLocation(str(zarr_tmp))) inlines = ds["amplitude"][::75, :, :] mean, std = inlines.mean(), inlines.std() npt.assert_allclose([mean, std], [1.0555277e-04, 6.0027051e-01]) @@ -380,7 +381,7 @@ def test_crossline(self, zarr_tmp: Path) -> None: """Read and compare every 75 crosslines' mean and std. dev.""" # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN - ds = xr.open_dataset(zarr_tmp, engine="zarr", mask_and_scale=False) + ds = open_zarr_dataset(StorageLocation(str(zarr_tmp))) xlines = ds["amplitude"][:, ::75, :] mean, std = xlines.mean(), xlines.std() @@ -390,7 +391,7 @@ def test_zslice(self, zarr_tmp: Path) -> None: """Read and compare every 225 z-slices' mean and std. dev.""" # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN - ds = xr.open_dataset(zarr_tmp, engine="zarr", mask_and_scale=False) + ds = open_zarr_dataset(StorageLocation(str(zarr_tmp))) slices = ds["amplitude"][:, :, ::225] mean, std = slices.mean(), slices.std() npt.assert_allclose([mean, std], [0.005236923, 0.61279935]) diff --git a/tests/integration/test_segy_import_export_masked.py b/tests/integration/test_segy_import_export_masked.py index be636574e..6ea2baa37 100644 --- a/tests/integration/test_segy_import_export_masked.py +++ b/tests/integration/test_segy_import_export_masked.py @@ -24,6 +24,7 @@ from tests.conftest import DEBUG_MODE from mdio import mdio_to_segy +from mdio.core.utils_read import open_zarr_dataset from mdio.converters.segy import segy_to_mdio from mdio.core.storage_location import StorageLocation from mdio.schemas.v1.templates.template_registry import TemplateRegistry @@ -315,9 +316,7 @@ def test_ingested_mdio(self, test_conf: MaskedExportConfig, export_masked_path: mdio_path = export_masked_path / f"{grid_conf.name}.mdio" # Open the MDIO file - # NOTE: If mask_and_scale is not set, - # Xarray will convert int to float and replace _FillValue with NaN - ds = xr.open_dataset(mdio_path, engine="zarr", mask_and_scale=False) + ds = open_zarr_dataset(StorageLocation(str(mdio_path))) # Test dimensions and ingested dimension headers expected_dims = grid_conf.dims From 2a53a68d8c8b6c22fb808d00f6b5376444b129f7 Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 14:56:00 -0500 Subject: [PATCH 06/24] lint --- tests/integration/testing_data.py | 2 +- tests/integration/testing_helpers.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/integration/testing_data.py b/tests/integration/testing_data.py index b61263e47..e6939a6e6 100644 --- a/tests/integration/testing_data.py +++ b/tests/integration/testing_data.py @@ -30,7 +30,7 @@ def custom_teapot_dome_segy_spec(keep_unaltered: bool) -> SegySpec: index_bytes=index_bytes, index_names=index_names, index_types=index_types, - keep_unaltered = keep_unaltered + keep_unaltered=keep_unaltered, ) diff --git a/tests/integration/testing_helpers.py b/tests/integration/testing_helpers.py index 79fdf7c86..05cf49226 100644 --- a/tests/integration/testing_helpers.py +++ b/tests/integration/testing_helpers.py @@ -13,7 +13,7 @@ def customize_segy_specs( index_bytes: tuple[int, ...] | None = None, index_names: tuple[int, ...] | None = None, index_types: tuple[int, ...] | None = None, - keep_unaltered: bool = False + keep_unaltered: bool = False, ) -> SegySpec: """Customize SEG-Y specifications with user-defined index fields.""" if not index_bytes: @@ -41,6 +41,7 @@ def customize_segy_specs( return segy_spec.customize(trace_header_fields=fields.values()) + def get_values(arr: xr.DataArray) -> np.ndarray: """Extract actual values from an Xarray DataArray.""" return arr.values @@ -69,8 +70,8 @@ def validate_variable( # noqa PLR0913 # assert data_type == arr.dtype # Compare field names - expected_names = [name for name in data_type.names] - actual_names = [name for name in arr.dtype.names] + expected_names = list(data_type.names) + actual_names = list(arr.dtype.names) assert expected_names == actual_names # Compare field types From fe73dee1c5396c9de424c4bdec6f8ac17acfd834 Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 14:58:15 -0500 Subject: [PATCH 07/24] create/use new api location and lint --- src/mdio/__init__.py | 2 ++ src/mdio/api/opener.py | 13 +++++++++ src/mdio/converters/mdio.py | 27 +++++++++--------- src/mdio/core/utils_read.py | 16 ----------- src/mdio/segy/creation.py | 17 ++++++----- tests/integration/test_segy_import_export.py | 28 ++++++++----------- .../test_segy_import_export_masked.py | 13 ++++----- 7 files changed, 53 insertions(+), 63 deletions(-) create mode 100644 src/mdio/api/opener.py delete mode 100644 src/mdio/core/utils_read.py diff --git a/src/mdio/__init__.py b/src/mdio/__init__.py index 0318ac966..f67baf5f8 100644 --- a/src/mdio/__init__.py +++ b/src/mdio/__init__.py @@ -5,6 +5,7 @@ from mdio.api import MDIOReader from mdio.api import MDIOWriter from mdio.api.convenience import copy_mdio +from mdio.api.opener import open_dataset from mdio.converters import mdio_to_segy from mdio.converters import numpy_to_mdio from mdio.converters import segy_to_mdio @@ -19,6 +20,7 @@ "MDIOReader", "MDIOWriter", "copy_mdio", + "open_dataset", "mdio_to_segy", "numpy_to_mdio", "segy_to_mdio", diff --git a/src/mdio/api/opener.py b/src/mdio/api/opener.py new file mode 100644 index 000000000..b22d6a8a4 --- /dev/null +++ b/src/mdio/api/opener.py @@ -0,0 +1,13 @@ +"""Utils for reading MDIO dataset.""" + +import xarray as xr + +from mdio.core.storage_location import StorageLocation + + +def open_dataset(storage_location: StorageLocation) -> xr.Dataset: + """Open a Zarr dataset from the specified storage location.""" + # NOTE: If mask_and_scale is not set, + # Xarray will convert int to float and replace _FillValue with NaN + # We are using chunks={} to force Xarray to create Dask arrays + return xr.open_dataset(storage_location.uri, engine="zarr", chunks={}, mask_and_scale=False) diff --git a/src/mdio/converters/mdio.py b/src/mdio/converters/mdio.py index 41fc4dd33..3a57c005f 100644 --- a/src/mdio/converters/mdio.py +++ b/src/mdio/converters/mdio.py @@ -7,17 +7,15 @@ from tempfile import TemporaryDirectory from typing import TYPE_CHECKING -import dask.array as da import numpy as np -import xarray as xr from psutil import cpu_count from tqdm.dask import TqdmCallback -from mdio.core.utils_read import open_zarr_dataset +from mdio.api.opener import open_dataset from mdio.segy.blocked_io import to_segy from mdio.segy.creation import concat_files from mdio.segy.creation import mdio_spec_to_segy -from mdio.segy.utilities import segy_export_rechunker +from mdio.segy.utilities import segy_export_rechunker try: import distributed @@ -32,6 +30,7 @@ default_cpus = cpu_count(logical=True) NUM_CPUS = int(os.getenv("MDIO__EXPORT__CPU_COUNT", default_cpus)) + def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 segy_spec: SegySpec, input_location: StorageLocation, @@ -75,10 +74,10 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 """ output_segy_path = Path(output_location.uri) - mdio_xr = open_zarr_dataset(input_location) + dataset = open_dataset(input_location) - trace_variable_name = mdio_xr.attrs["attributes"]["traceVariableName"] - amplitude = mdio_xr[trace_variable_name] + trace_variable_name = dataset.attrs["attributes"]["traceVariableName"] + amplitude = dataset[trace_variable_name] chunks: tuple[int, ...] = amplitude.data.chunksize shape: tuple[int, ...] = amplitude.data.shape dtype = amplitude.dtype @@ -90,14 +89,14 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 if distributed is not None: # This is in case we work with big data feature = client.submit(mdio_spec_to_segy, *creation_args) - mdio_xr, segy_factory = feature.result() + dataset, segy_factory = feature.result() else: msg = "Distributed client was provided, but `distributed` is not installed" raise ImportError(msg) else: - mdio_xr, segy_factory = mdio_spec_to_segy(*creation_args) + dataset, segy_factory = mdio_spec_to_segy(*creation_args) - live_mask = mdio_xr["trace_mask"].data.compute() + live_mask = dataset["trace_mask"].data.compute() if selection_mask is not None: live_mask = live_mask & selection_mask @@ -117,9 +116,9 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 dim_slices += (slice(start, stop),) # Lazily pull the data with limits now, and limit mask so its the same shape. - live_mask = mdio_xr["trace_mask"].data.rechunk(new_chunks[:-1])[dim_slices] - headers = mdio_xr["headers"].data.rechunk(new_chunks[:-1])[dim_slices] - samples = mdio_xr["amplitude"].data.rechunk(new_chunks)[dim_slices] + live_mask = dataset["trace_mask"].data.rechunk(new_chunks[:-1])[dim_slices] + headers = dataset["headers"].data.rechunk(new_chunks[:-1])[dim_slices] + samples = dataset["amplitude"].data.rechunk(new_chunks)[dim_slices] if selection_mask is not None: selection_mask = selection_mask[dim_slices] @@ -150,4 +149,4 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 if client is not None: _ = client.submit(concat_files, paths=ordered_files).result() else: - concat_files(paths=ordered_files, progress=True) \ No newline at end of file + concat_files(paths=ordered_files, progress=True) diff --git a/src/mdio/core/utils_read.py b/src/mdio/core/utils_read.py deleted file mode 100644 index 6c76d2474..000000000 --- a/src/mdio/core/utils_read.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Utils for reading MDIO dataset""" - -from typing import TYPE_CHECKING - -from xarray import open_dataset as xr_open_dataset -from mdio.core.storage_location import StorageLocation -from xarray import Dataset as xr_Dataset - -def open_zarr_dataset(storage_location: StorageLocation) -> xr_Dataset: - """ - Open a Zarr dataset from the specified storage location. - """ - # NOTE: If mask_and_scale is not set, - # Xarray will convert int to float and replace _FillValue with NaN - # We are using chunks={} to force Xarray to create Dask arrays - return xr_open_dataset(storage_location.uri, engine="zarr", chunks={}, mask_and_scale=False) \ No newline at end of file diff --git a/src/mdio/segy/creation.py b/src/mdio/segy/creation.py index 903a1c07a..610d4024a 100644 --- a/src/mdio/segy/creation.py +++ b/src/mdio/segy/creation.py @@ -9,17 +9,16 @@ from typing import TYPE_CHECKING import numpy as np -import xarray as xr from segy.factory import SegyFactory -from segy.schema import SegySpec from tqdm.auto import tqdm -from xarray import Dataset as xr_Dataset -from mdio.core.utils_read import open_zarr_dataset +from mdio.api.opener import open_dataset from mdio.segy.compat import revision_encode if TYPE_CHECKING: + import xarray as xr from numpy.typing import NDArray + from segy.schema import SegySpec from mdio.core.storage_location import StorageLocation @@ -27,9 +26,9 @@ logger = logging.getLogger(__name__) -def make_segy_factory(mdio_xr: xr_Dataset, spec: SegySpec) -> SegyFactory: +def make_segy_factory(dataset: xr.Dataset, spec: SegySpec) -> SegyFactory: """Generate SEG-Y factory from MDIO metadata.""" - binary_header = mdio_xr.attrs["attributes"]["binaryHeader"] + binary_header = dataset.attrs["attributes"]["binaryHeader"] sample_interval = binary_header["sample_interval"] samples_per_trace = binary_header["samples_per_trace"] return SegyFactory( @@ -42,8 +41,8 @@ def make_segy_factory(mdio_xr: xr_Dataset, spec: SegySpec) -> SegyFactory: def mdio_spec_to_segy( segy_spec: SegySpec, input_location: StorageLocation, - output_location: StorageLocation -) -> tuple[xr_Dataset, SegyFactory]: + output_location: StorageLocation, +) -> tuple[xr.Dataset, SegyFactory]: """Create SEG-Y file without any traces given MDIO specification. This function opens an MDIO file, gets some relevant information for SEG-Y files, then creates @@ -62,7 +61,7 @@ def mdio_spec_to_segy( Returns: Opened Xarray Dataset for MDIO file and SegyFactory """ - mdio_xr = open_zarr_dataset(input_location) + mdio_xr = open_dataset(input_location) factory = make_segy_factory(mdio_xr, spec=segy_spec) attr = mdio_xr.attrs["attributes"] diff --git a/tests/integration/test_segy_import_export.py b/tests/integration/test_segy_import_export.py index 54842c6fe..7bca325ba 100644 --- a/tests/integration/test_segy_import_export.py +++ b/tests/integration/test_segy_import_export.py @@ -10,7 +10,6 @@ import numpy as np import numpy.testing as npt import pytest -import xarray as xr from segy import SegyFile from tests.integration.testing_data import binary_header_teapot_dome from tests.integration.testing_data import custom_teapot_dome_segy_spec @@ -21,13 +20,12 @@ from mdio import MDIOReader from mdio import mdio_to_segy +from mdio.api.opener import open_dataset from mdio.converters.exceptions import GridTraceSparsityError -from mdio.core.utils_read import open_zarr_dataset from mdio.converters.segy import segy_to_mdio from mdio.core import Dimension from mdio.core.storage_location import StorageLocation from mdio.schemas.v1.templates.template_registry import TemplateRegistry -from mdio.segy.compat import mdio_segy_spec from mdio.segy.geometry import StreamerShotGeometryType if TYPE_CHECKING: @@ -246,12 +244,9 @@ def test_import_6d_segy( # noqa: PLR0913 @pytest.mark.dependency -def test_3d_import( - segy_input: Path, - zarr_tmp: Path, -) -> None: +def test_3d_import(segy_input: Path, zarr_tmp: Path) -> None: """Test importing a SEG-Y file to MDIO. - + NOTE: This test must be executed before the 'TestReader' and 'TestExport' tests. """ segy_to_mdio( @@ -267,15 +262,14 @@ def test_3d_import( class TestReader: """Test reader functionality. - NOTE: This tests must be executed after the 'test_3d_import' successfully completes and - before running 'TestExport' tests. + NOTE: These tests must be executed after the 'test_3d_import' and before running 'TestExport' tests. """ def test_meta_dataset(self, zarr_tmp: Path) -> None: """Metadata reading tests.""" # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN - ds = open_zarr_dataset(StorageLocation(str(zarr_tmp))) + ds = open_dataset(StorageLocation(str(zarr_tmp))) expected_attrs = { "apiVersion": "1.0.0a1", "createdOn": "2025-08-06 16:21:54.747880+00:00", @@ -308,7 +302,7 @@ def test_meta_variable(self, zarr_tmp: Path) -> None: """Metadata reading tests.""" # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN - ds = open_zarr_dataset(StorageLocation(str(zarr_tmp))) + ds = open_dataset(StorageLocation(str(zarr_tmp))) expected_attrs = { "count": 97354860, "sum": -8594.551666259766, @@ -325,7 +319,7 @@ def test_grid(self, zarr_tmp: Path) -> None: # Load Xarray dataset from the MDIO file # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN - ds = open_zarr_dataset(StorageLocation(str(zarr_tmp))) + ds = open_dataset(StorageLocation(str(zarr_tmp))) # Note: in order to create the dataset we used the Time template, so the # sample dimension is called "time" @@ -372,7 +366,7 @@ def test_inline(self, zarr_tmp: Path) -> None: """Read and compare every 75 inlines' mean and std. dev.""" # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN - ds = open_zarr_dataset(StorageLocation(str(zarr_tmp))) + ds = open_dataset(StorageLocation(str(zarr_tmp))) inlines = ds["amplitude"][::75, :, :] mean, std = inlines.mean(), inlines.std() npt.assert_allclose([mean, std], [1.0555277e-04, 6.0027051e-01]) @@ -381,7 +375,7 @@ def test_crossline(self, zarr_tmp: Path) -> None: """Read and compare every 75 crosslines' mean and std. dev.""" # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN - ds = open_zarr_dataset(StorageLocation(str(zarr_tmp))) + ds = open_dataset(StorageLocation(str(zarr_tmp))) xlines = ds["amplitude"][:, ::75, :] mean, std = xlines.mean(), xlines.std() @@ -391,7 +385,7 @@ def test_zslice(self, zarr_tmp: Path) -> None: """Read and compare every 225 z-slices' mean and std. dev.""" # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN - ds = open_zarr_dataset(StorageLocation(str(zarr_tmp))) + ds = open_dataset(StorageLocation(str(zarr_tmp))) slices = ds["amplitude"][:, :, ::225] mean, std = slices.mean(), slices.std() npt.assert_allclose([mean, std], [0.005236923, 0.61279935]) @@ -400,7 +394,7 @@ def test_zslice(self, zarr_tmp: Path) -> None: @pytest.mark.dependency("test_3d_import") class TestExport: """Test SEG-Y exporting functionality. - + NOTE: This test(s) must be executed after the 'test_3d_import' and 'TestReader' tests successfully complete. """ diff --git a/tests/integration/test_segy_import_export_masked.py b/tests/integration/test_segy_import_export_masked.py index 6ea2baa37..344404691 100644 --- a/tests/integration/test_segy_import_export_masked.py +++ b/tests/integration/test_segy_import_export_masked.py @@ -14,7 +14,6 @@ import fsspec import numpy as np import pytest -import xarray as xr from numpy.testing import assert_array_equal from segy import SegyFile from segy.factory import SegyFactory @@ -24,7 +23,7 @@ from tests.conftest import DEBUG_MODE from mdio import mdio_to_segy -from mdio.core.utils_read import open_zarr_dataset +from mdio.api.opener import open_dataset from mdio.converters.segy import segy_to_mdio from mdio.core.storage_location import StorageLocation from mdio.schemas.v1.templates.template_registry import TemplateRegistry @@ -316,7 +315,7 @@ def test_ingested_mdio(self, test_conf: MaskedExportConfig, export_masked_path: mdio_path = export_masked_path / f"{grid_conf.name}.mdio" # Open the MDIO file - ds = open_zarr_dataset(StorageLocation(str(mdio_path))) + ds = open_dataset(StorageLocation(str(mdio_path))) # Test dimensions and ingested dimension headers expected_dims = grid_conf.dims @@ -365,8 +364,8 @@ def test_ingested_mdio(self, test_conf: MaskedExportConfig, export_masked_path: def test_export(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: """Test export of an n-D MDIO file back to SEG-Y. - - NOTE: This test must be executed after the 'test_import' and 'test_ingested_mdio' + + NOTE: This test must be executed after the 'test_import' and 'test_ingested_mdio' successfully complete. """ grid_conf, segy_factory_conf, segy_to_mdio_conf, _ = test_conf @@ -401,8 +400,8 @@ def test_export(self, test_conf: MaskedExportConfig, export_masked_path: Path) - def test_export_masked(self, test_conf: MaskedExportConfig, export_masked_path: Path) -> None: """Test export of an n-D MDIO file back to SEG-Y with masked export. - - NOTE: This test must be executed after the 'test_import' and 'test_ingested_mdio' + + NOTE: This test must be executed after the 'test_import' and 'test_ingested_mdio' successfully complete. """ grid_conf, segy_factory_conf, segy_to_mdio_conf, selection_conf = test_conf From 43d419c785629004cb5a09397764f7c97de8c56d Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 15:36:55 -0500 Subject: [PATCH 08/24] allow configuring opener chunks --- src/mdio/api/opener.py | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/mdio/api/opener.py b/src/mdio/api/opener.py index b22d6a8a4..28a49b113 100644 --- a/src/mdio/api/opener.py +++ b/src/mdio/api/opener.py @@ -1,13 +1,36 @@ """Utils for reading MDIO dataset.""" +from __future__ import annotations + +from typing import TYPE_CHECKING + import xarray as xr -from mdio.core.storage_location import StorageLocation +if TYPE_CHECKING: + from xarray.core.types import T_Chunks + + from mdio.core.storage_location import StorageLocation + + +def open_dataset(storage_location: StorageLocation, chunks: T_Chunks = None) -> xr.Dataset: + """Open a Zarr dataset from the specified storage location. + + Args: + storage_location: StorageLocation for the dataset. + chunks: If provided, loads data into dask arrays with new chunking. + - ``chunks="auto"`` will use dask ``auto`` chunking. + - ``chunks=None`` loads the data with dask using on disk chunk size. + - ``chunks=-1`` loads the data with dask using a single chunk for all arrays. + + See Xarray's open_dataset for more details. + Returns: + An Xarray dataset opened from the storage location. + """ + if chunks is None: + chunks = {} -def open_dataset(storage_location: StorageLocation) -> xr.Dataset: - """Open a Zarr dataset from the specified storage location.""" # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN # We are using chunks={} to force Xarray to create Dask arrays - return xr.open_dataset(storage_location.uri, engine="zarr", chunks={}, mask_and_scale=False) + return xr.open_dataset(storage_location.uri, engine="zarr", chunks=chunks, mask_and_scale=False) From 0f33a7ed649624f1e9bd13eba15749acb6039545 Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 15:37:46 -0500 Subject: [PATCH 09/24] clarify xarray open parameters --- src/mdio/api/opener.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mdio/api/opener.py b/src/mdio/api/opener.py index 28a49b113..3293dbd50 100644 --- a/src/mdio/api/opener.py +++ b/src/mdio/api/opener.py @@ -32,5 +32,5 @@ def open_dataset(storage_location: StorageLocation, chunks: T_Chunks = None) -> # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN - # We are using chunks={} to force Xarray to create Dask arrays + # Fixed in Zarr v3, so we can fix this later. return xr.open_dataset(storage_location.uri, engine="zarr", chunks=chunks, mask_and_scale=False) From 29c3fc490113a29b5b1b27de749af4a1639d326a Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 16:37:57 -0500 Subject: [PATCH 10/24] fix regression of not-opening with native dask re-chunking --- src/mdio/segy/creation.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/mdio/segy/creation.py b/src/mdio/segy/creation.py index 610d4024a..c3161358c 100644 --- a/src/mdio/segy/creation.py +++ b/src/mdio/segy/creation.py @@ -42,6 +42,7 @@ def mdio_spec_to_segy( segy_spec: SegySpec, input_location: StorageLocation, output_location: StorageLocation, + new_chunks: tuple[int, ...] | None = None, ) -> tuple[xr.Dataset, SegyFactory]: """Create SEG-Y file without any traces given MDIO specification. @@ -57,21 +58,22 @@ def mdio_spec_to_segy( segy_spec: The SEG-Y specification to use for the conversion. input_location: Store or URL (and cloud options) for MDIO file. output_location: Path to the output SEG-Y file. + new_chunks: Set in memory chunksize for export or other reasons. Returns: Opened Xarray Dataset for MDIO file and SegyFactory """ - mdio_xr = open_dataset(input_location) - factory = make_segy_factory(mdio_xr, spec=segy_spec) + dataset = open_dataset(input_location, chunks=new_chunks) + factory = make_segy_factory(dataset, spec=segy_spec) - attr = mdio_xr.attrs["attributes"] + attr = dataset.attrs["attributes"] txt_header = attr["textHeader"] text_str = "\n".join(txt_header) text_bytes = factory.create_textual_header(text_str) bin_header = attr["binaryHeader"] - mdio_file_version = mdio_xr.attrs["apiVersion"] + mdio_file_version = dataset.attrs["apiVersion"] binary_header = revision_encode(bin_header, mdio_file_version) bin_hdr_bytes = factory.create_binary_header(binary_header) @@ -79,7 +81,7 @@ def mdio_spec_to_segy( fp.write(text_bytes) fp.write(bin_hdr_bytes) - return mdio_xr, factory + return dataset, factory @dataclass(slots=True) From 9d7f2456a32c7fc6865b29838f472e69e032538c Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 16:38:36 -0500 Subject: [PATCH 11/24] fix regression of not-opening with native dask re-chunking --- src/mdio/converters/mdio.py | 43 ++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/mdio/converters/mdio.py b/src/mdio/converters/mdio.py index 3a57c005f..63877f6a5 100644 --- a/src/mdio/converters/mdio.py +++ b/src/mdio/converters/mdio.py @@ -78,12 +78,12 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 trace_variable_name = dataset.attrs["attributes"]["traceVariableName"] amplitude = dataset[trace_variable_name] - chunks: tuple[int, ...] = amplitude.data.chunksize - shape: tuple[int, ...] = amplitude.data.shape + chunks = amplitude.encoding["preferred_chunks"] + sizes = amplitude.sizes dtype = amplitude.dtype - new_chunks = segy_export_rechunker(chunks, shape, dtype) + new_chunks = segy_export_rechunker(chunks, sizes, dtype) - creation_args = [segy_spec, input_location, output_location] + creation_args = [segy_spec, input_location, output_location, new_chunks] if client is not None: if distributed is not None: @@ -96,33 +96,36 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 else: dataset, segy_factory = mdio_spec_to_segy(*creation_args) - live_mask = dataset["trace_mask"].data.compute() + trace_mask = dataset["trace_mask"].compute() if selection_mask is not None: - live_mask = live_mask & selection_mask + if trace_mask.shape != selection_mask.shape: + msg = "Selection mask and trace mask shapes do not match." + raise ValueError(msg) + selection_mask = trace_mask.copy(data=selection_mask) # make into DataArray + trace_mask = trace_mask & selection_mask # This handles the case if we are skipping a whole block. - if live_mask.sum() == 0: + if trace_mask.sum() == 0: msg = "No traces will be written out. Live mask is empty." raise ValueError(msg) # Find rough dim limits, so we don't unnecessarily hit disk / cloud store. # Typically, gets triggered when there is a selection mask - dim_slices = () - live_nonzeros = live_mask.nonzero() - for dim_nonzeros in live_nonzeros: - start = np.min(dim_nonzeros) - stop = np.max(dim_nonzeros) + 1 - dim_slices += (slice(start, stop),) + dim_slices = {} + dim_live_indices = np.nonzero(trace_mask.values) + for dim_name, dim_live in zip(trace_mask.dims, dim_live_indices, strict=True): + start = dim_live.min().item() + stop = dim_live.max().item() + 1 + dim_slices[dim_name] = slice(start, stop) # Lazily pull the data with limits now, and limit mask so its the same shape. - live_mask = dataset["trace_mask"].data.rechunk(new_chunks[:-1])[dim_slices] - headers = dataset["headers"].data.rechunk(new_chunks[:-1])[dim_slices] - samples = dataset["amplitude"].data.rechunk(new_chunks)[dim_slices] + dataset = dataset[dim_slices] + trace_mask = dataset["trace_mask"] if selection_mask is not None: selection_mask = selection_mask[dim_slices] - live_mask = live_mask & selection_mask + trace_mask = trace_mask & selection_mask # tmp file root out_dir = output_segy_path.parent @@ -131,9 +134,9 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 with tmp_dir: with TqdmCallback(desc="Unwrapping MDIO Blocks"): block_records = to_segy( - samples=samples, - headers=headers, - live_mask=live_mask, + samples=dataset[trace_variable_name].data, + headers=dataset["headers"].data, + live_mask=trace_mask.data, segy_factory=segy_factory, file_root=tmp_dir.name, ) From 8194c38c7ff87b9ba753cc231c1ceb4c0efd1e09 Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 16:39:23 -0500 Subject: [PATCH 12/24] make export rechunker work with named dimension sizes and chunks --- src/mdio/segy/utilities.py | 39 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/mdio/segy/utilities.py b/src/mdio/segy/utilities.py index f5677e96b..96a85d828 100644 --- a/src/mdio/segy/utilities.py +++ b/src/mdio/segy/utilities.py @@ -113,11 +113,11 @@ def find_trailing_ones_index(dim_blocks: tuple[int, ...]) -> int: def segy_export_rechunker( - chunks: tuple[int, ...], - shape: tuple[int, ...], + chunks: dict[str, int], + sizes: dict[str, int], dtype: DTypeLike, limit: str = "300M", -) -> tuple[tuple[int, ...], ...]: +) -> dict[str, int]: """Determine chunk sizes for writing out SEG-Y given limit. This module finds the desired chunk sizes for given chunk size `limit` in a depth first order. @@ -131,36 +131,37 @@ def segy_export_rechunker( serialized in the natural data order. Args: - chunks: The chunk sizes on disk. - shape: Shape of the whole array. + chunks: The chunk sizes on disk, per dimension. + sizes: Shape of the whole array, per dimension. dtype: Numpy `dtype` of the array. limit: Chunk size limit in, optional. Default is "300 MB" Returns: Adjusted chunk sizes for further processing """ - ndim = len(shape) - 1 # minus the sample axis + dim_names = list(sizes.keys()) + sample_dim_key = dim_names[-1] - # set sample chunks to max - prev_chunks = chunks[:-1] + (shape[-1],) + # set sample dim chunks (last one) to max + prev_chunks = chunks.copy() + prev_chunks[sample_dim_key] = sizes[sample_dim_key] - new_chunks = () - for idx in range(ndim, -1, -1): - tmp_chunks = prev_chunks[:idx] + ("auto",) + prev_chunks[idx + 1 :] + new_chunks = {} + for dim_name in reversed(list(prev_chunks)): + tmp_chunks: dict[str, int | str] = prev_chunks.copy() + tmp_chunks[dim_name] = "auto" new_chunks = normalize_chunks( - chunks=tmp_chunks, - shape=shape, + chunks=tuple(tmp_chunks.values()), + shape=tuple(sizes.values()), limit=limit, - previous_chunks=prev_chunks, + previous_chunks=tuple(prev_chunks.values()), dtype=dtype, ) - - prev_chunks = new_chunks + new_chunks = dict(zip(dim_names, new_chunks, strict=True)) + prev_chunks = new_chunks.copy() # Ensure the sample (last dim) is single chunk. - if len(new_chunks[-1]) != 1: - new_chunks = new_chunks[:-1] + (shape[-1],) - + new_chunks[sample_dim_key] = sizes[sample_dim_key] logger.debug("Auto export rechunking to: %s", new_chunks) return new_chunks From a987bf74b730f954c76ffb3a955eedf8fcdd3d06 Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 16:42:59 -0500 Subject: [PATCH 13/24] make StorageLocation available at library level and update mdio to segy example --- src/mdio/__init__.py | 2 ++ src/mdio/converters/mdio.py | 10 ++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mdio/__init__.py b/src/mdio/__init__.py index f67baf5f8..bb66c689f 100644 --- a/src/mdio/__init__.py +++ b/src/mdio/__init__.py @@ -15,6 +15,7 @@ from mdio.core.factory import create_empty from mdio.core.factory import create_empty_like from mdio.core.grid import Grid +from mdio.core.storage_location import StorageLocation __all__ = [ "MDIOReader", @@ -30,6 +31,7 @@ "create_empty", "create_empty_like", "Grid", + "StorageLocation", ] diff --git a/src/mdio/converters/mdio.py b/src/mdio/converters/mdio.py index 63877f6a5..e9ce1c0aa 100644 --- a/src/mdio/converters/mdio.py +++ b/src/mdio/converters/mdio.py @@ -64,13 +64,11 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 To export an existing local MDIO file to SEG-Y we use the code snippet below. This will export the full MDIO (without padding) to SEG-Y format. - >>> from mdio import mdio_to_segy + >>> from mdio import mdio_to_segy, StorageLocation >>> - >>> - >>> mdio_to_segy( - ... mdio_path_or_buffer="prefix2/file.mdio", - ... output_segy_path="prefix/file.segy", - ... ) + >>> input_location = StorageLocation("prefix2/file.mdio") + >>> output_location = StorageLocation("prefix/file.segy") + >>> mdio_to_segy(input_location, output_location) """ output_segy_path = Path(output_location.uri) From 20e52f430a13850e5b5bd3975333f36441d36e7a Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 17:05:50 -0500 Subject: [PATCH 14/24] pre-open with zarr backend and simplify dataset slicing after lazy loading --- src/mdio/api/opener.py | 3 --- src/mdio/converters/mdio.py | 10 ++++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/mdio/api/opener.py b/src/mdio/api/opener.py index 3293dbd50..800f46409 100644 --- a/src/mdio/api/opener.py +++ b/src/mdio/api/opener.py @@ -27,9 +27,6 @@ def open_dataset(storage_location: StorageLocation, chunks: T_Chunks = None) -> Returns: An Xarray dataset opened from the storage location. """ - if chunks is None: - chunks = {} - # NOTE: If mask_and_scale is not set, # Xarray will convert int to float and replace _FillValue with NaN # Fixed in Zarr v3, so we can fix this later. diff --git a/src/mdio/converters/mdio.py b/src/mdio/converters/mdio.py index e9ce1c0aa..e40663309 100644 --- a/src/mdio/converters/mdio.py +++ b/src/mdio/converters/mdio.py @@ -72,6 +72,8 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 """ output_segy_path = Path(output_location.uri) + # First we open with vanilla zarr backend and then get some info + # We will re-open with `new_chunks` and Dask later in mdio_spec_to_segy dataset = open_dataset(input_location) trace_variable_name = dataset.attrs["attributes"]["traceVariableName"] @@ -117,13 +119,13 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 stop = dim_live.max().item() + 1 dim_slices[dim_name] = slice(start, stop) - # Lazily pull the data with limits now, and limit mask so its the same shape. + # Lazily pull the data with limits now. + # All the variables, metadata, etc. is all sliced to the same range. dataset = dataset[dim_slices] - trace_mask = dataset["trace_mask"] if selection_mask is not None: selection_mask = selection_mask[dim_slices] - trace_mask = trace_mask & selection_mask + dataset["trace_mask"] = dataset["trace_mask"] & selection_mask # tmp file root out_dir = output_segy_path.parent @@ -134,7 +136,7 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 block_records = to_segy( samples=dataset[trace_variable_name].data, headers=dataset["headers"].data, - live_mask=trace_mask.data, + live_mask=dataset["trace_mask"].data, segy_factory=segy_factory, file_root=tmp_dir.name, ) From 732f7b5c0cf5ccc572ef9a31a41e04e61621383c Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 17:08:32 -0500 Subject: [PATCH 15/24] better opener docs --- src/mdio/api/opener.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/mdio/api/opener.py b/src/mdio/api/opener.py index 800f46409..6e2ea2809 100644 --- a/src/mdio/api/opener.py +++ b/src/mdio/api/opener.py @@ -18,11 +18,13 @@ def open_dataset(storage_location: StorageLocation, chunks: T_Chunks = None) -> Args: storage_location: StorageLocation for the dataset. chunks: If provided, loads data into dask arrays with new chunking. - - ``chunks="auto"`` will use dask ``auto`` chunking. - - ``chunks=None`` loads the data with dask using on disk chunk size. + - ``chunks="auto"`` will use dask ``auto`` chunking + - ``chunks=None`` skips using dask, which is generally faster for small arrays. - ``chunks=-1`` loads the data with dask using a single chunk for all arrays. + - ``chunks={}`` loads the data with dask using the engine's preferred chunk size (on disk). + - ``chunks={dim: chunk, ...}`` loads the data with dask using the specified chunk size for each dimension. - See Xarray's open_dataset for more details. + See dask chunking for more details. Returns: An Xarray dataset opened from the storage location. From f7b63c55c79459d7f1d6e08c7c8984b5401f4f27 Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 17:11:17 -0500 Subject: [PATCH 16/24] more explicit xarray selection --- src/mdio/converters/mdio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mdio/converters/mdio.py b/src/mdio/converters/mdio.py index e40663309..96255bf48 100644 --- a/src/mdio/converters/mdio.py +++ b/src/mdio/converters/mdio.py @@ -121,7 +121,7 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 # Lazily pull the data with limits now. # All the variables, metadata, etc. is all sliced to the same range. - dataset = dataset[dim_slices] + dataset = dataset.isel(dim_slices) if selection_mask is not None: selection_mask = selection_mask[dim_slices] From b92ec5fdffcd331b832b9ee5e172794e974d965e Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 17:17:31 -0500 Subject: [PATCH 17/24] rename trace variable name to default variable name --- src/mdio/converters/mdio.py | 6 +++--- src/mdio/converters/segy.py | 4 ++-- .../schemas/v1/templates/abstract_dataset_template.py | 10 +++++----- tests/integration/test_segy_import_export.py | 2 +- tests/unit/v1/templates/test_seismic_2d_poststack.py | 4 ++-- tests/unit/v1/templates/test_seismic_3d_poststack.py | 2 +- .../unit/v1/templates/test_seismic_3d_prestack_cdp.py | 2 +- .../unit/v1/templates/test_seismic_3d_prestack_coca.py | 2 +- .../unit/v1/templates/test_seismic_3d_prestack_shot.py | 2 +- tests/unit/v1/templates/test_seismic_templates.py | 6 +++--- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/mdio/converters/mdio.py b/src/mdio/converters/mdio.py index 96255bf48..4da32f143 100644 --- a/src/mdio/converters/mdio.py +++ b/src/mdio/converters/mdio.py @@ -76,8 +76,8 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 # We will re-open with `new_chunks` and Dask later in mdio_spec_to_segy dataset = open_dataset(input_location) - trace_variable_name = dataset.attrs["attributes"]["traceVariableName"] - amplitude = dataset[trace_variable_name] + default_variable_name = dataset.attrs["attributes"]["default_variable_name"] + amplitude = dataset[default_variable_name] chunks = amplitude.encoding["preferred_chunks"] sizes = amplitude.sizes dtype = amplitude.dtype @@ -134,7 +134,7 @@ def mdio_to_segy( # noqa: PLR0912, PLR0913, PLR0915 with tmp_dir: with TqdmCallback(desc="Unwrapping MDIO Blocks"): block_records = to_segy( - samples=dataset[trace_variable_name].data, + samples=dataset[default_variable_name].data, headers=dataset["headers"].data, live_mask=dataset["trace_mask"].data, segy_factory=segy_factory, diff --git a/src/mdio/converters/segy.py b/src/mdio/converters/segy.py index 6a1d7df7e..f4c2fac5f 100644 --- a/src/mdio/converters/segy.py +++ b/src/mdio/converters/segy.py @@ -387,7 +387,7 @@ def segy_to_mdio( xr_dataset = xr_dataset.drop_vars(drop_vars_delayed) # Write the headers and traces in chunks using grid_map to indicate dead traces - data_variable_name = mdio_template.trace_variable_name + default_variable_name = mdio_template.default_variable_name # This is an memory-expensive and time-consuming read-write operation # performed in chunks to save the memory blocked_io.to_zarr( @@ -395,5 +395,5 @@ def segy_to_mdio( output_location=output_location, grid_map=grid.map, dataset=xr_dataset, - data_variable_name=data_variable_name, + data_variable_name=default_variable_name, ) diff --git a/src/mdio/schemas/v1/templates/abstract_dataset_template.py b/src/mdio/schemas/v1/templates/abstract_dataset_template.py index ef10ecbe7..643c4c406 100644 --- a/src/mdio/schemas/v1/templates/abstract_dataset_template.py +++ b/src/mdio/schemas/v1/templates/abstract_dataset_template.py @@ -85,7 +85,7 @@ def build_dataset( self._horizontal_coord_unit = horizontal_coord_unit attr = self._load_dataset_attributes() or UserAttributes(attributes={}) - attr.attributes["traceVariableName"] = self._trace_variable_name + attr.attributes["default_variable_name"] = self._default_variable_name self._builder = MDIODatasetBuilder(name=name, attributes=attr) self._add_dimensions() self._add_coordinates() @@ -101,9 +101,9 @@ def name(self) -> str: return self._name @property - def trace_variable_name(self) -> str: + def default_variable_name(self) -> str: """Returns the name of the trace variable.""" - return self._trace_variable_name + return self._default_variable_name @property def trace_domain(self) -> str: @@ -132,7 +132,7 @@ def _name(self) -> str: """ @property - def _trace_variable_name(self) -> str: + def _default_variable_name(self) -> str: """Get the name of the data variable. A virtual method that can be overwritten by subclasses to return a @@ -228,7 +228,7 @@ def _add_variables(self) -> None: Uses the class field 'builder' to add variables to the dataset. """ self._builder.add_variable( - name=self._trace_variable_name, + name=self.default_variable_name, dimensions=self._dim_names, data_type=ScalarType.FLOAT32, compressor=compressors.Blosc(algorithm=compressors.BloscAlgorithm.ZSTD), diff --git a/tests/integration/test_segy_import_export.py b/tests/integration/test_segy_import_export.py index 7bca325ba..7a72db8ff 100644 --- a/tests/integration/test_segy_import_export.py +++ b/tests/integration/test_segy_import_export.py @@ -288,7 +288,7 @@ def test_meta_dataset(self, zarr_tmp: Path) -> None: assert attributes is not None assert len(attributes) == 7 # Validate all attribute provided by the abstract template - assert attributes["traceVariableName"] == "amplitude" + assert attributes["default_variable_name"] == "amplitude" # Validate attributes provided by the PostStack3DTime template assert attributes["surveyDimensionality"] == "3D" assert attributes["ensembleType"] == "line" diff --git a/tests/unit/v1/templates/test_seismic_2d_poststack.py b/tests/unit/v1/templates/test_seismic_2d_poststack.py index be91f9f09..be4fdb168 100644 --- a/tests/unit/v1/templates/test_seismic_2d_poststack.py +++ b/tests/unit/v1/templates/test_seismic_2d_poststack.py @@ -100,7 +100,7 @@ def test_configuration_depth(self) -> None: "processingStage": "post-stack", } - assert t.trace_variable_name == "amplitude" + assert t.default_variable_name == "amplitude" def test_configuration_time(self) -> None: """Test configuration of Seismic2DPostStackTemplate with time domain.""" @@ -125,7 +125,7 @@ def test_configuration_time(self) -> None: "ensembleType": "line", "processingStage": "post-stack", } - assert t.trace_variable_name == "amplitude" + assert t.default_variable_name == "amplitude" def test_domain_case_handling(self) -> None: """Test that domain parameter handles different cases correctly.""" diff --git a/tests/unit/v1/templates/test_seismic_3d_poststack.py b/tests/unit/v1/templates/test_seismic_3d_poststack.py index 5b46fe78f..fb800bf23 100644 --- a/tests/unit/v1/templates/test_seismic_3d_poststack.py +++ b/tests/unit/v1/templates/test_seismic_3d_poststack.py @@ -115,7 +115,7 @@ def test_configuration_depth(self) -> None: "ensembleType": "line", "processingStage": "post-stack", } - assert t.trace_variable_name == "amplitude" + assert t.default_variable_name == "amplitude" def test_configuration_time(self) -> None: """Unit tests for Seismic3DPostStackTemplate with time domain.""" diff --git a/tests/unit/v1/templates/test_seismic_3d_prestack_cdp.py b/tests/unit/v1/templates/test_seismic_3d_prestack_cdp.py index c6b846e7b..488b510ca 100644 --- a/tests/unit/v1/templates/test_seismic_3d_prestack_cdp.py +++ b/tests/unit/v1/templates/test_seismic_3d_prestack_cdp.py @@ -124,7 +124,7 @@ def test_configuration_depth(self) -> None: "ensembleType": "cdp", "processingStage": "pre-stack", } - assert t.trace_variable_name == "amplitude" + assert t.default_variable_name == "amplitude" def test_configuration_time(self) -> None: """Unit tests for Seismic3DPreStackCDPTemplate.""" diff --git a/tests/unit/v1/templates/test_seismic_3d_prestack_coca.py b/tests/unit/v1/templates/test_seismic_3d_prestack_coca.py index 089c63730..503e63270 100644 --- a/tests/unit/v1/templates/test_seismic_3d_prestack_coca.py +++ b/tests/unit/v1/templates/test_seismic_3d_prestack_coca.py @@ -133,7 +133,7 @@ def test_configuration_time(self) -> None: "ensembleType": "cdp_coca", "processingStage": "pre-stack", } - assert t.trace_variable_name == "amplitude" + assert t.default_variable_name == "amplitude" def test_build_dataset_time(self, structured_headers: StructuredType) -> None: """Unit tests for Seismic3DPreStackShotTemplate build in time domain.""" diff --git a/tests/unit/v1/templates/test_seismic_3d_prestack_shot.py b/tests/unit/v1/templates/test_seismic_3d_prestack_shot.py index 451dbdf4e..1d519c82f 100644 --- a/tests/unit/v1/templates/test_seismic_3d_prestack_shot.py +++ b/tests/unit/v1/templates/test_seismic_3d_prestack_shot.py @@ -150,7 +150,7 @@ def test_configuration_depth(self) -> None: "ensembleType": "shot_point", "processingStage": "pre-stack", } - assert t.trace_variable_name == "amplitude" + assert t.default_variable_name == "amplitude" def test_configuration_time(self) -> None: """Unit tests for Seismic3DPreStackShotTemplate in time domain.""" diff --git a/tests/unit/v1/templates/test_seismic_templates.py b/tests/unit/v1/templates/test_seismic_templates.py index a57882a52..2fdc6433d 100644 --- a/tests/unit/v1/templates/test_seismic_templates.py +++ b/tests/unit/v1/templates/test_seismic_templates.py @@ -25,7 +25,7 @@ def __init__(self, domain: str): super().__init__(domain=domain) @property - def _trace_variable_name(self) -> str: + def _default_variable_name(self) -> str: return "velocity" @property @@ -34,7 +34,7 @@ def _name(self) -> str: t = Velocity2DPostStackTemplate("depth") assert t.name == "Velocity2DDepth" - assert t.trace_variable_name == "velocity" + assert t.default_variable_name == "velocity" dataset = t.build_dataset("Velocity 2D Depth Line 001", sizes=[2048, 4096], horizontal_coord_unit=_UNIT_METER) @@ -70,7 +70,7 @@ def test_all_templates_inherit_from_abstract(self) -> None: assert isinstance(template, AbstractDatasetTemplate) # That each template has the required properties and methods assert hasattr(template, "name") - assert hasattr(template, "trace_variable_name") + assert hasattr(template, "default_variable_name") assert hasattr(template, "trace_domain") assert hasattr(template, "dimension_names") assert hasattr(template, "coordinate_names") From 59d0e4e2129f6562a6d1c87f62d13be40a3afb31 Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 17:27:48 -0500 Subject: [PATCH 18/24] remove the guard for setting storage options to empty dictionary. new zarr is ok with None. --- src/mdio/core/factory.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/mdio/core/factory.py b/src/mdio/core/factory.py index c76095221..e84ea6793 100644 --- a/src/mdio/core/factory.py +++ b/src/mdio/core/factory.py @@ -121,8 +121,6 @@ def create_empty( """ zarr.config.set({"default_zarr_format": 2, "write_empty_chunks": False}) - storage_options = storage_options or {} - url = process_url(url=config.path, disk_cache=False) root_group = open_group(url, mode="w", storage_options=storage_options) root_group = create_zarr_hierarchy(root_group, overwrite) @@ -206,9 +204,6 @@ def create_empty_like( storage_options_input: Options for storage backend of the source dataset. storage_options_output: Options for storage backend of the destination dataset. """ - storage_options_input = storage_options_input or {} - storage_options_output = storage_options_output or {} - source_root = zarr.open_consolidated( source_path, mode="r", From 8246b9c1cc4cf1791bc43148045a4dfe4a48cd4d Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 17:28:18 -0500 Subject: [PATCH 19/24] update lockfile --- uv.lock | 542 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 272 insertions(+), 270 deletions(-) diff --git a/uv.lock b/uv.lock index b4d7ae134..c172a301c 100644 --- a/uv.lock +++ b/uv.lock @@ -21,7 +21,7 @@ wheels = [ [[package]] name = "adlfs" -version = "2024.12.0" +version = "2025.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -31,9 +31,9 @@ dependencies = [ { name = "azure-storage-blob" }, { name = "fsspec" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/82/e30891af574fb358449fb9436aac53569814452cb88b0cba4f488171b8dc/adlfs-2024.12.0.tar.gz", hash = "sha256:04582bf7461a57365766d01a295a0a88b2b8c42c4fea06e2d673f62675cac5c6", size = 49189, upload-time = "2024-12-15T19:06:30.939Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/af/4d74c92254fdeabc19e54df4c9146855c2c1027bd4052477e3a27b05de54/adlfs-2025.8.0.tar.gz", hash = "sha256:6fe5857866c18990f632598273e6a8b15edc6baf8614272ede25624057b83e64", size = 52007, upload-time = "2025-08-30T12:07:40.936Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/ed/d1bf75c089857d38332cf45416e419b47382b345ba5dfc4fae69397830d9/adlfs-2024.12.0-py3-none-any.whl", hash = "sha256:00aab061ddec0413b2039487e656b62e01ece8ef1ca0493f76034a596cf069e3", size = 41792, upload-time = "2024-12-15T19:06:27.718Z" }, + { url = "https://files.pythonhosted.org/packages/59/e0/e98227759d88184406f757d149843fa8f3d8bab71f5a2ebd3f6f3b3970b1/adlfs-2025.8.0-py3-none-any.whl", hash = "sha256:c12a9203a31485cad19599bf2ad30d8efcf4cf0fd2446c60fcdc18604fae07b1", size = 44076, upload-time = "2025-08-30T12:07:39.694Z" }, ] [[package]] @@ -295,7 +295,7 @@ wheels = [ [[package]] name = "bokeh" -version = "3.7.3" +version = "3.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "contourpy" }, @@ -309,9 +309,9 @@ dependencies = [ { name = "tornado", marker = "sys_platform != 'emscripten'" }, { name = "xyzservices" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/75/18/12d0d6024177ad18ba65deffc363046d0cbafe116f8b964a9efa85d2800f/bokeh-3.7.3.tar.gz", hash = "sha256:70a89a9f797b103d5ee6ad15fb7944adda115cf0da996ed0b75cfba61cb12f2b", size = 6366610, upload-time = "2025-05-12T12:13:29.318Z" } +sdist = { url = "https://files.pythonhosted.org/packages/78/bd/8455ecfaa8100dbfbb2af40061c689a7a9c808f4f8c9582f0efd0c8c9a19/bokeh-3.8.0.tar.gz", hash = "sha256:bfdf5e9df910653b097f70cd38f4c2399d91af6e54a618126e2387cc33c9ec03", size = 6529746, upload-time = "2025-08-29T12:16:55.005Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/48/08b2382e739236aa3360b7976360ba3e0c043b6234e25951c18c1eb6fa06/bokeh-3.7.3-py3-none-any.whl", hash = "sha256:b0e79dd737f088865212e4fdcb0f3b95d087f0f088bf8ca186a300ab1641e2c7", size = 7031447, upload-time = "2025-05-12T12:13:27.47Z" }, + { url = "https://files.pythonhosted.org/packages/96/9a/8e641b5415e12036d8a206147b8229d917a767b7d939521458d90feddcf5/bokeh-3.8.0-py3-none-any.whl", hash = "sha256:117c5e559231ad39fef87891a1a1b62b3bfefbaa47d536023537338f46015841", size = 7205343, upload-time = "2025-08-29T12:16:52.77Z" }, ] [[package]] @@ -557,55 +557,55 @@ wheels = [ [[package]] name = "coverage" -version = "7.10.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/61/83/153f54356c7c200013a752ce1ed5448573dca546ce125801afca9e1ac1a4/coverage-7.10.5.tar.gz", hash = "sha256:f2e57716a78bc3ae80b2207be0709a3b2b63b9f2dcf9740ee6ac03588a2015b6", size = 821662, upload-time = "2025-08-23T14:42:44.78Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/f2/336d34d2fc1291ca7c18eeb46f64985e6cef5a1a7ef6d9c23720c6527289/coverage-7.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c177e6ffe2ebc7c410785307758ee21258aa8e8092b44d09a2da767834f075f2", size = 216890, upload-time = "2025-08-23T14:40:43.627Z" }, - { url = "https://files.pythonhosted.org/packages/39/ea/92448b07cc1cf2b429d0ce635f59cf0c626a5d8de21358f11e92174ff2a6/coverage-7.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:14d6071c51ad0f703d6440827eaa46386169b5fdced42631d5a5ac419616046f", size = 217287, upload-time = "2025-08-23T14:40:45.214Z" }, - { url = "https://files.pythonhosted.org/packages/96/ba/ad5b36537c5179c808d0ecdf6e4aa7630b311b3c12747ad624dcd43a9b6b/coverage-7.10.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:61f78c7c3bc272a410c5ae3fde7792b4ffb4acc03d35a7df73ca8978826bb7ab", size = 247683, upload-time = "2025-08-23T14:40:46.791Z" }, - { url = "https://files.pythonhosted.org/packages/28/e5/fe3bbc8d097029d284b5fb305b38bb3404895da48495f05bff025df62770/coverage-7.10.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f39071caa126f69d63f99b324fb08c7b1da2ec28cbb1fe7b5b1799926492f65c", size = 249614, upload-time = "2025-08-23T14:40:48.082Z" }, - { url = "https://files.pythonhosted.org/packages/69/9c/a1c89a8c8712799efccb32cd0a1ee88e452f0c13a006b65bb2271f1ac767/coverage-7.10.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343a023193f04d46edc46b2616cdbee68c94dd10208ecd3adc56fcc54ef2baa1", size = 251719, upload-time = "2025-08-23T14:40:49.349Z" }, - { url = "https://files.pythonhosted.org/packages/e9/be/5576b5625865aa95b5633315f8f4142b003a70c3d96e76f04487c3b5cc95/coverage-7.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:585ffe93ae5894d1ebdee69fc0b0d4b7c75d8007983692fb300ac98eed146f78", size = 249411, upload-time = "2025-08-23T14:40:50.624Z" }, - { url = "https://files.pythonhosted.org/packages/94/0a/e39a113d4209da0dbbc9385608cdb1b0726a4d25f78672dc51c97cfea80f/coverage-7.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b0ef4e66f006ed181df29b59921bd8fc7ed7cd6a9289295cd8b2824b49b570df", size = 247466, upload-time = "2025-08-23T14:40:52.362Z" }, - { url = "https://files.pythonhosted.org/packages/40/cb/aebb2d8c9e3533ee340bea19b71c5b76605a0268aa49808e26fe96ec0a07/coverage-7.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:eb7b0bbf7cc1d0453b843eca7b5fa017874735bef9bfdfa4121373d2cc885ed6", size = 248104, upload-time = "2025-08-23T14:40:54.064Z" }, - { url = "https://files.pythonhosted.org/packages/08/e6/26570d6ccce8ff5de912cbfd268e7f475f00597cb58da9991fa919c5e539/coverage-7.10.5-cp311-cp311-win32.whl", hash = "sha256:1d043a8a06987cc0c98516e57c4d3fc2c1591364831e9deb59c9e1b4937e8caf", size = 219327, upload-time = "2025-08-23T14:40:55.424Z" }, - { url = "https://files.pythonhosted.org/packages/79/79/5f48525e366e518b36e66167e3b6e5db6fd54f63982500c6a5abb9d3dfbd/coverage-7.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:fefafcca09c3ac56372ef64a40f5fe17c5592fab906e0fdffd09543f3012ba50", size = 220213, upload-time = "2025-08-23T14:40:56.724Z" }, - { url = "https://files.pythonhosted.org/packages/40/3c/9058128b7b0bf333130c320b1eb1ae485623014a21ee196d68f7737f8610/coverage-7.10.5-cp311-cp311-win_arm64.whl", hash = "sha256:7e78b767da8b5fc5b2faa69bb001edafcd6f3995b42a331c53ef9572c55ceb82", size = 218893, upload-time = "2025-08-23T14:40:58.011Z" }, - { url = "https://files.pythonhosted.org/packages/27/8e/40d75c7128f871ea0fd829d3e7e4a14460cad7c3826e3b472e6471ad05bd/coverage-7.10.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c2d05c7e73c60a4cecc7d9b60dbfd603b4ebc0adafaef371445b47d0f805c8a9", size = 217077, upload-time = "2025-08-23T14:40:59.329Z" }, - { url = "https://files.pythonhosted.org/packages/18/a8/f333f4cf3fb5477a7f727b4d603a2eb5c3c5611c7fe01329c2e13b23b678/coverage-7.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:32ddaa3b2c509778ed5373b177eb2bf5662405493baeff52278a0b4f9415188b", size = 217310, upload-time = "2025-08-23T14:41:00.628Z" }, - { url = "https://files.pythonhosted.org/packages/ec/2c/fbecd8381e0a07d1547922be819b4543a901402f63930313a519b937c668/coverage-7.10.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:dd382410039fe062097aa0292ab6335a3f1e7af7bba2ef8d27dcda484918f20c", size = 248802, upload-time = "2025-08-23T14:41:02.012Z" }, - { url = "https://files.pythonhosted.org/packages/3f/bc/1011da599b414fb6c9c0f34086736126f9ff71f841755786a6b87601b088/coverage-7.10.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7fa22800f3908df31cea6fb230f20ac49e343515d968cc3a42b30d5c3ebf9b5a", size = 251550, upload-time = "2025-08-23T14:41:03.438Z" }, - { url = "https://files.pythonhosted.org/packages/4c/6f/b5c03c0c721c067d21bc697accc3642f3cef9f087dac429c918c37a37437/coverage-7.10.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f366a57ac81f5e12797136552f5b7502fa053c861a009b91b80ed51f2ce651c6", size = 252684, upload-time = "2025-08-23T14:41:04.85Z" }, - { url = "https://files.pythonhosted.org/packages/f9/50/d474bc300ebcb6a38a1047d5c465a227605d6473e49b4e0d793102312bc5/coverage-7.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f1dc8f1980a272ad4a6c84cba7981792344dad33bf5869361576b7aef42733a", size = 250602, upload-time = "2025-08-23T14:41:06.719Z" }, - { url = "https://files.pythonhosted.org/packages/4a/2d/548c8e04249cbba3aba6bd799efdd11eee3941b70253733f5d355d689559/coverage-7.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2285c04ee8676f7938b02b4936d9b9b672064daab3187c20f73a55f3d70e6b4a", size = 248724, upload-time = "2025-08-23T14:41:08.429Z" }, - { url = "https://files.pythonhosted.org/packages/e2/96/a7c3c0562266ac39dcad271d0eec8fc20ab576e3e2f64130a845ad2a557b/coverage-7.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c2492e4dd9daab63f5f56286f8a04c51323d237631eb98505d87e4c4ff19ec34", size = 250158, upload-time = "2025-08-23T14:41:09.749Z" }, - { url = "https://files.pythonhosted.org/packages/f3/75/74d4be58c70c42ef0b352d597b022baf12dbe2b43e7cb1525f56a0fb1d4b/coverage-7.10.5-cp312-cp312-win32.whl", hash = "sha256:38a9109c4ee8135d5df5505384fc2f20287a47ccbe0b3f04c53c9a1989c2bbaf", size = 219493, upload-time = "2025-08-23T14:41:11.095Z" }, - { url = "https://files.pythonhosted.org/packages/4f/08/364e6012d1d4d09d1e27437382967efed971d7613f94bca9add25f0c1f2b/coverage-7.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:6b87f1ad60b30bc3c43c66afa7db6b22a3109902e28c5094957626a0143a001f", size = 220302, upload-time = "2025-08-23T14:41:12.449Z" }, - { url = "https://files.pythonhosted.org/packages/db/d5/7c8a365e1f7355c58af4fe5faf3f90cc8e587590f5854808d17ccb4e7077/coverage-7.10.5-cp312-cp312-win_arm64.whl", hash = "sha256:672a6c1da5aea6c629819a0e1461e89d244f78d7b60c424ecf4f1f2556c041d8", size = 218936, upload-time = "2025-08-23T14:41:13.872Z" }, - { url = "https://files.pythonhosted.org/packages/9f/08/4166ecfb60ba011444f38a5a6107814b80c34c717bc7a23be0d22e92ca09/coverage-7.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ef3b83594d933020f54cf65ea1f4405d1f4e41a009c46df629dd964fcb6e907c", size = 217106, upload-time = "2025-08-23T14:41:15.268Z" }, - { url = "https://files.pythonhosted.org/packages/25/d7/b71022408adbf040a680b8c64bf6ead3be37b553e5844f7465643979f7ca/coverage-7.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b96bfdf7c0ea9faebce088a3ecb2382819da4fbc05c7b80040dbc428df6af44", size = 217353, upload-time = "2025-08-23T14:41:16.656Z" }, - { url = "https://files.pythonhosted.org/packages/74/68/21e0d254dbf8972bb8dd95e3fe7038f4be037ff04ba47d6d1b12b37510ba/coverage-7.10.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:63df1fdaffa42d914d5c4d293e838937638bf75c794cf20bee12978fc8c4e3bc", size = 248350, upload-time = "2025-08-23T14:41:18.128Z" }, - { url = "https://files.pythonhosted.org/packages/90/65/28752c3a896566ec93e0219fc4f47ff71bd2b745f51554c93e8dcb659796/coverage-7.10.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8002dc6a049aac0e81ecec97abfb08c01ef0c1fbf962d0c98da3950ace89b869", size = 250955, upload-time = "2025-08-23T14:41:19.577Z" }, - { url = "https://files.pythonhosted.org/packages/a5/eb/ca6b7967f57f6fef31da8749ea20417790bb6723593c8cd98a987be20423/coverage-7.10.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:63d4bb2966d6f5f705a6b0c6784c8969c468dbc4bcf9d9ded8bff1c7e092451f", size = 252230, upload-time = "2025-08-23T14:41:20.959Z" }, - { url = "https://files.pythonhosted.org/packages/bc/29/17a411b2a2a18f8b8c952aa01c00f9284a1fbc677c68a0003b772ea89104/coverage-7.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1f672efc0731a6846b157389b6e6d5d5e9e59d1d1a23a5c66a99fd58339914d5", size = 250387, upload-time = "2025-08-23T14:41:22.644Z" }, - { url = "https://files.pythonhosted.org/packages/c7/89/97a9e271188c2fbb3db82235c33980bcbc733da7da6065afbaa1d685a169/coverage-7.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3f39cef43d08049e8afc1fde4a5da8510fc6be843f8dea350ee46e2a26b2f54c", size = 248280, upload-time = "2025-08-23T14:41:24.061Z" }, - { url = "https://files.pythonhosted.org/packages/d1/c6/0ad7d0137257553eb4706b4ad6180bec0a1b6a648b092c5bbda48d0e5b2c/coverage-7.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2968647e3ed5a6c019a419264386b013979ff1fb67dd11f5c9886c43d6a31fc2", size = 249894, upload-time = "2025-08-23T14:41:26.165Z" }, - { url = "https://files.pythonhosted.org/packages/84/56/fb3aba936addb4c9e5ea14f5979393f1c2466b4c89d10591fd05f2d6b2aa/coverage-7.10.5-cp313-cp313-win32.whl", hash = "sha256:0d511dda38595b2b6934c2b730a1fd57a3635c6aa2a04cb74714cdfdd53846f4", size = 219536, upload-time = "2025-08-23T14:41:27.694Z" }, - { url = "https://files.pythonhosted.org/packages/fc/54/baacb8f2f74431e3b175a9a2881feaa8feb6e2f187a0e7e3046f3c7742b2/coverage-7.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:9a86281794a393513cf117177fd39c796b3f8e3759bb2764259a2abba5cce54b", size = 220330, upload-time = "2025-08-23T14:41:29.081Z" }, - { url = "https://files.pythonhosted.org/packages/64/8a/82a3788f8e31dee51d350835b23d480548ea8621f3effd7c3ba3f7e5c006/coverage-7.10.5-cp313-cp313-win_arm64.whl", hash = "sha256:cebd8e906eb98bb09c10d1feed16096700b1198d482267f8bf0474e63a7b8d84", size = 218961, upload-time = "2025-08-23T14:41:30.511Z" }, - { url = "https://files.pythonhosted.org/packages/d8/a1/590154e6eae07beee3b111cc1f907c30da6fc8ce0a83ef756c72f3c7c748/coverage-7.10.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0520dff502da5e09d0d20781df74d8189ab334a1e40d5bafe2efaa4158e2d9e7", size = 217819, upload-time = "2025-08-23T14:41:31.962Z" }, - { url = "https://files.pythonhosted.org/packages/0d/ff/436ffa3cfc7741f0973c5c89405307fe39b78dcf201565b934e6616fc4ad/coverage-7.10.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d9cd64aca68f503ed3f1f18c7c9174cbb797baba02ca8ab5112f9d1c0328cd4b", size = 218040, upload-time = "2025-08-23T14:41:33.472Z" }, - { url = "https://files.pythonhosted.org/packages/a0/ca/5787fb3d7820e66273913affe8209c534ca11241eb34ee8c4fd2aaa9dd87/coverage-7.10.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0913dd1613a33b13c4f84aa6e3f4198c1a21ee28ccb4f674985c1f22109f0aae", size = 259374, upload-time = "2025-08-23T14:41:34.914Z" }, - { url = "https://files.pythonhosted.org/packages/b5/89/21af956843896adc2e64fc075eae3c1cadb97ee0a6960733e65e696f32dd/coverage-7.10.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1b7181c0feeb06ed8a02da02792f42f829a7b29990fef52eff257fef0885d760", size = 261551, upload-time = "2025-08-23T14:41:36.333Z" }, - { url = "https://files.pythonhosted.org/packages/e1/96/390a69244ab837e0ac137989277879a084c786cf036c3c4a3b9637d43a89/coverage-7.10.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36d42b7396b605f774d4372dd9c49bed71cbabce4ae1ccd074d155709dd8f235", size = 263776, upload-time = "2025-08-23T14:41:38.25Z" }, - { url = "https://files.pythonhosted.org/packages/00/32/cfd6ae1da0a521723349f3129b2455832fc27d3f8882c07e5b6fefdd0da2/coverage-7.10.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b4fdc777e05c4940b297bf47bf7eedd56a39a61dc23ba798e4b830d585486ca5", size = 261326, upload-time = "2025-08-23T14:41:40.343Z" }, - { url = "https://files.pythonhosted.org/packages/4c/c4/bf8d459fb4ce2201e9243ce6c015936ad283a668774430a3755f467b39d1/coverage-7.10.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:42144e8e346de44a6f1dbd0a56575dd8ab8dfa7e9007da02ea5b1c30ab33a7db", size = 259090, upload-time = "2025-08-23T14:41:42.106Z" }, - { url = "https://files.pythonhosted.org/packages/f4/5d/a234f7409896468e5539d42234016045e4015e857488b0b5b5f3f3fa5f2b/coverage-7.10.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:66c644cbd7aed8fe266d5917e2c9f65458a51cfe5eeff9c05f15b335f697066e", size = 260217, upload-time = "2025-08-23T14:41:43.591Z" }, - { url = "https://files.pythonhosted.org/packages/f3/ad/87560f036099f46c2ddd235be6476dd5c1d6be6bb57569a9348d43eeecea/coverage-7.10.5-cp313-cp313t-win32.whl", hash = "sha256:2d1b73023854068c44b0c554578a4e1ef1b050ed07cf8b431549e624a29a66ee", size = 220194, upload-time = "2025-08-23T14:41:45.051Z" }, - { url = "https://files.pythonhosted.org/packages/36/a8/04a482594fdd83dc677d4a6c7e2d62135fff5a1573059806b8383fad9071/coverage-7.10.5-cp313-cp313t-win_amd64.whl", hash = "sha256:54a1532c8a642d8cc0bd5a9a51f5a9dcc440294fd06e9dda55e743c5ec1a8f14", size = 221258, upload-time = "2025-08-23T14:41:46.44Z" }, - { url = "https://files.pythonhosted.org/packages/eb/ad/7da28594ab66fe2bc720f1bc9b131e62e9b4c6e39f044d9a48d18429cc21/coverage-7.10.5-cp313-cp313t-win_arm64.whl", hash = "sha256:74d5b63fe3f5f5d372253a4ef92492c11a4305f3550631beaa432fc9df16fcff", size = 219521, upload-time = "2025-08-23T14:41:47.882Z" }, - { url = "https://files.pythonhosted.org/packages/08/b6/fff6609354deba9aeec466e4bcaeb9d1ed3e5d60b14b57df2a36fb2273f2/coverage-7.10.5-py3-none-any.whl", hash = "sha256:0be24d35e4db1d23d0db5c0f6a74a962e2ec83c426b5cac09f4234aadef38e4a", size = 208736, upload-time = "2025-08-23T14:42:43.145Z" }, +version = "7.10.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/70/025b179c993f019105b79575ac6edb5e084fb0f0e63f15cdebef4e454fb5/coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90", size = 823736, upload-time = "2025-08-29T15:35:16.668Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/16/2bea27e212c4980753d6d563a0803c150edeaaddb0771a50d2afc410a261/coverage-7.10.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c706db3cabb7ceef779de68270150665e710b46d56372455cd741184f3868d8f", size = 217129, upload-time = "2025-08-29T15:33:13.575Z" }, + { url = "https://files.pythonhosted.org/packages/2a/51/e7159e068831ab37e31aac0969d47b8c5ee25b7d307b51e310ec34869315/coverage-7.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e0c38dc289e0508ef68ec95834cb5d2e96fdbe792eaccaa1bccac3966bbadcc", size = 217532, upload-time = "2025-08-29T15:33:14.872Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c0/246ccbea53d6099325d25cd208df94ea435cd55f0db38099dd721efc7a1f/coverage-7.10.6-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:752a3005a1ded28f2f3a6e8787e24f28d6abe176ca64677bcd8d53d6fe2ec08a", size = 247931, upload-time = "2025-08-29T15:33:16.142Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fb/7435ef8ab9b2594a6e3f58505cc30e98ae8b33265d844007737946c59389/coverage-7.10.6-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:689920ecfd60f992cafca4f5477d55720466ad2c7fa29bb56ac8d44a1ac2b47a", size = 249864, upload-time = "2025-08-29T15:33:17.434Z" }, + { url = "https://files.pythonhosted.org/packages/51/f8/d9d64e8da7bcddb094d511154824038833c81e3a039020a9d6539bf303e9/coverage-7.10.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec98435796d2624d6905820a42f82149ee9fc4f2d45c2c5bc5a44481cc50db62", size = 251969, upload-time = "2025-08-29T15:33:18.822Z" }, + { url = "https://files.pythonhosted.org/packages/43/28/c43ba0ef19f446d6463c751315140d8f2a521e04c3e79e5c5fe211bfa430/coverage-7.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b37201ce4a458c7a758ecc4efa92fa8ed783c66e0fa3c42ae19fc454a0792153", size = 249659, upload-time = "2025-08-29T15:33:20.407Z" }, + { url = "https://files.pythonhosted.org/packages/79/3e/53635bd0b72beaacf265784508a0b386defc9ab7fad99ff95f79ce9db555/coverage-7.10.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2904271c80898663c810a6b067920a61dd8d38341244a3605bd31ab55250dad5", size = 247714, upload-time = "2025-08-29T15:33:21.751Z" }, + { url = "https://files.pythonhosted.org/packages/4c/55/0964aa87126624e8c159e32b0bc4e84edef78c89a1a4b924d28dd8265625/coverage-7.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5aea98383463d6e1fa4e95416d8de66f2d0cb588774ee20ae1b28df826bcb619", size = 248351, upload-time = "2025-08-29T15:33:23.105Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ab/6cfa9dc518c6c8e14a691c54e53a9433ba67336c760607e299bfcf520cb1/coverage-7.10.6-cp311-cp311-win32.whl", hash = "sha256:e3fb1fa01d3598002777dd259c0c2e6d9d5e10e7222976fc8e03992f972a2cba", size = 219562, upload-time = "2025-08-29T15:33:24.717Z" }, + { url = "https://files.pythonhosted.org/packages/5b/18/99b25346690cbc55922e7cfef06d755d4abee803ef335baff0014268eff4/coverage-7.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:f35ed9d945bece26553d5b4c8630453169672bea0050a564456eb88bdffd927e", size = 220453, upload-time = "2025-08-29T15:33:26.482Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ed/81d86648a07ccb124a5cf1f1a7788712b8d7216b593562683cd5c9b0d2c1/coverage-7.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:99e1a305c7765631d74b98bf7dbf54eeea931f975e80f115437d23848ee8c27c", size = 219127, upload-time = "2025-08-29T15:33:27.777Z" }, + { url = "https://files.pythonhosted.org/packages/26/06/263f3305c97ad78aab066d116b52250dd316e74fcc20c197b61e07eb391a/coverage-7.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5b2dd6059938063a2c9fee1af729d4f2af28fd1a545e9b7652861f0d752ebcea", size = 217324, upload-time = "2025-08-29T15:33:29.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/60/1e1ded9a4fe80d843d7d53b3e395c1db3ff32d6c301e501f393b2e6c1c1f/coverage-7.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:388d80e56191bf846c485c14ae2bc8898aa3124d9d35903fef7d907780477634", size = 217560, upload-time = "2025-08-29T15:33:30.748Z" }, + { url = "https://files.pythonhosted.org/packages/b8/25/52136173c14e26dfed8b106ed725811bb53c30b896d04d28d74cb64318b3/coverage-7.10.6-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:90cb5b1a4670662719591aa92d0095bb41714970c0b065b02a2610172dbf0af6", size = 249053, upload-time = "2025-08-29T15:33:32.041Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1d/ae25a7dc58fcce8b172d42ffe5313fc267afe61c97fa872b80ee72d9515a/coverage-7.10.6-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:961834e2f2b863a0e14260a9a273aff07ff7818ab6e66d2addf5628590c628f9", size = 251802, upload-time = "2025-08-29T15:33:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/f5/7a/1f561d47743710fe996957ed7c124b421320f150f1d38523d8d9102d3e2a/coverage-7.10.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf9a19f5012dab774628491659646335b1928cfc931bf8d97b0d5918dd58033c", size = 252935, upload-time = "2025-08-29T15:33:34.909Z" }, + { url = "https://files.pythonhosted.org/packages/6c/ad/8b97cd5d28aecdfde792dcbf646bac141167a5cacae2cd775998b45fabb5/coverage-7.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99c4283e2a0e147b9c9cc6bc9c96124de9419d6044837e9799763a0e29a7321a", size = 250855, upload-time = "2025-08-29T15:33:36.922Z" }, + { url = "https://files.pythonhosted.org/packages/33/6a/95c32b558d9a61858ff9d79580d3877df3eb5bc9eed0941b1f187c89e143/coverage-7.10.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:282b1b20f45df57cc508c1e033403f02283adfb67d4c9c35a90281d81e5c52c5", size = 248974, upload-time = "2025-08-29T15:33:38.175Z" }, + { url = "https://files.pythonhosted.org/packages/0d/9c/8ce95dee640a38e760d5b747c10913e7a06554704d60b41e73fdea6a1ffd/coverage-7.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cdbe264f11afd69841bd8c0d83ca10b5b32853263ee62e6ac6a0ab63895f972", size = 250409, upload-time = "2025-08-29T15:33:39.447Z" }, + { url = "https://files.pythonhosted.org/packages/04/12/7a55b0bdde78a98e2eb2356771fd2dcddb96579e8342bb52aa5bc52e96f0/coverage-7.10.6-cp312-cp312-win32.whl", hash = "sha256:a517feaf3a0a3eca1ee985d8373135cfdedfbba3882a5eab4362bda7c7cf518d", size = 219724, upload-time = "2025-08-29T15:33:41.172Z" }, + { url = "https://files.pythonhosted.org/packages/36/4a/32b185b8b8e327802c9efce3d3108d2fe2d9d31f153a0f7ecfd59c773705/coverage-7.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:856986eadf41f52b214176d894a7de05331117f6035a28ac0016c0f63d887629", size = 220536, upload-time = "2025-08-29T15:33:42.524Z" }, + { url = "https://files.pythonhosted.org/packages/08/3a/d5d8dc703e4998038c3099eaf77adddb00536a3cec08c8dcd556a36a3eb4/coverage-7.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:acf36b8268785aad739443fa2780c16260ee3fa09d12b3a70f772ef100939d80", size = 219171, upload-time = "2025-08-29T15:33:43.974Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e7/917e5953ea29a28c1057729c1d5af9084ab6d9c66217523fd0e10f14d8f6/coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6", size = 217351, upload-time = "2025-08-29T15:33:45.438Z" }, + { url = "https://files.pythonhosted.org/packages/eb/86/2e161b93a4f11d0ea93f9bebb6a53f113d5d6e416d7561ca41bb0a29996b/coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80", size = 217600, upload-time = "2025-08-29T15:33:47.269Z" }, + { url = "https://files.pythonhosted.org/packages/0e/66/d03348fdd8df262b3a7fb4ee5727e6e4936e39e2f3a842e803196946f200/coverage-7.10.6-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003", size = 248600, upload-time = "2025-08-29T15:33:48.953Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/508420fb47d09d904d962f123221bc249f64b5e56aa93d5f5f7603be475f/coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27", size = 251206, upload-time = "2025-08-29T15:33:50.697Z" }, + { url = "https://files.pythonhosted.org/packages/e9/1f/9020135734184f439da85c70ea78194c2730e56c2d18aee6e8ff1719d50d/coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4", size = 252478, upload-time = "2025-08-29T15:33:52.303Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a4/3d228f3942bb5a2051fde28c136eea23a761177dc4ff4ef54533164ce255/coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d", size = 250637, upload-time = "2025-08-29T15:33:53.67Z" }, + { url = "https://files.pythonhosted.org/packages/36/e3/293dce8cdb9a83de971637afc59b7190faad60603b40e32635cbd15fbf61/coverage-7.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc", size = 248529, upload-time = "2025-08-29T15:33:55.022Z" }, + { url = "https://files.pythonhosted.org/packages/90/26/64eecfa214e80dd1d101e420cab2901827de0e49631d666543d0e53cf597/coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc", size = 250143, upload-time = "2025-08-29T15:33:56.386Z" }, + { url = "https://files.pythonhosted.org/packages/3e/70/bd80588338f65ea5b0d97e424b820fb4068b9cfb9597fbd91963086e004b/coverage-7.10.6-cp313-cp313-win32.whl", hash = "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e", size = 219770, upload-time = "2025-08-29T15:33:58.063Z" }, + { url = "https://files.pythonhosted.org/packages/a7/14/0b831122305abcc1060c008f6c97bbdc0a913ab47d65070a01dc50293c2b/coverage-7.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32", size = 220566, upload-time = "2025-08-29T15:33:59.766Z" }, + { url = "https://files.pythonhosted.org/packages/83/c6/81a83778c1f83f1a4a168ed6673eeedc205afb562d8500175292ca64b94e/coverage-7.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2", size = 219195, upload-time = "2025-08-29T15:34:01.191Z" }, + { url = "https://files.pythonhosted.org/packages/d7/1c/ccccf4bf116f9517275fa85047495515add43e41dfe8e0bef6e333c6b344/coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b", size = 218059, upload-time = "2025-08-29T15:34:02.91Z" }, + { url = "https://files.pythonhosted.org/packages/92/97/8a3ceff833d27c7492af4f39d5da6761e9ff624831db9e9f25b3886ddbca/coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393", size = 218287, upload-time = "2025-08-29T15:34:05.106Z" }, + { url = "https://files.pythonhosted.org/packages/92/d8/50b4a32580cf41ff0423777a2791aaf3269ab60c840b62009aec12d3970d/coverage-7.10.6-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27", size = 259625, upload-time = "2025-08-29T15:34:06.575Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7e/6a7df5a6fb440a0179d94a348eb6616ed4745e7df26bf2a02bc4db72c421/coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df", size = 261801, upload-time = "2025-08-29T15:34:08.006Z" }, + { url = "https://files.pythonhosted.org/packages/3a/4c/a270a414f4ed5d196b9d3d67922968e768cd971d1b251e1b4f75e9362f75/coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb", size = 264027, upload-time = "2025-08-29T15:34:09.806Z" }, + { url = "https://files.pythonhosted.org/packages/9c/8b/3210d663d594926c12f373c5370bf1e7c5c3a427519a8afa65b561b9a55c/coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282", size = 261576, upload-time = "2025-08-29T15:34:11.585Z" }, + { url = "https://files.pythonhosted.org/packages/72/d0/e1961eff67e9e1dba3fc5eb7a4caf726b35a5b03776892da8d79ec895775/coverage-7.10.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4", size = 259341, upload-time = "2025-08-29T15:34:13.159Z" }, + { url = "https://files.pythonhosted.org/packages/3a/06/d6478d152cd189b33eac691cba27a40704990ba95de49771285f34a5861e/coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21", size = 260468, upload-time = "2025-08-29T15:34:14.571Z" }, + { url = "https://files.pythonhosted.org/packages/ed/73/737440247c914a332f0b47f7598535b29965bf305e19bbc22d4c39615d2b/coverage-7.10.6-cp313-cp313t-win32.whl", hash = "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0", size = 220429, upload-time = "2025-08-29T15:34:16.394Z" }, + { url = "https://files.pythonhosted.org/packages/bd/76/b92d3214740f2357ef4a27c75a526eb6c28f79c402e9f20a922c295c05e2/coverage-7.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5", size = 221493, upload-time = "2025-08-29T15:34:17.835Z" }, + { url = "https://files.pythonhosted.org/packages/fc/8e/6dcb29c599c8a1f654ec6cb68d76644fe635513af16e932d2d4ad1e5ac6e/coverage-7.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b", size = 219757, upload-time = "2025-08-29T15:34:19.248Z" }, + { url = "https://files.pythonhosted.org/packages/44/0c/50db5379b615854b5cf89146f8f5bd1d5a9693d7f3a987e269693521c404/coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3", size = 208986, upload-time = "2025-08-29T15:35:14.506Z" }, ] [package.optional-dependencies] @@ -667,43 +667,43 @@ wheels = [ [[package]] name = "cryptography" -version = "45.0.6" +version = "45.0.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/0d/d13399c94234ee8f3df384819dc67e0c5ce215fb751d567a55a1f4b028c7/cryptography-45.0.6.tar.gz", hash = "sha256:5c966c732cf6e4a276ce83b6e4c729edda2df6929083a952cc7da973c539c719", size = 744949, upload-time = "2025-08-05T23:59:27.93Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/29/2793d178d0eda1ca4a09a7c4e09a5185e75738cc6d526433e8663b460ea6/cryptography-45.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:048e7ad9e08cf4c0ab07ff7f36cc3115924e22e2266e034450a890d9e312dd74", size = 7042702, upload-time = "2025-08-05T23:58:23.464Z" }, - { url = "https://files.pythonhosted.org/packages/b3/b6/cabd07410f222f32c8d55486c464f432808abaa1f12af9afcbe8f2f19030/cryptography-45.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:44647c5d796f5fc042bbc6d61307d04bf29bccb74d188f18051b635f20a9c75f", size = 4206483, upload-time = "2025-08-05T23:58:27.132Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9e/f9c7d36a38b1cfeb1cc74849aabe9bf817990f7603ff6eb485e0d70e0b27/cryptography-45.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e40b80ecf35ec265c452eea0ba94c9587ca763e739b8e559c128d23bff7ebbbf", size = 4429679, upload-time = "2025-08-05T23:58:29.152Z" }, - { url = "https://files.pythonhosted.org/packages/9c/2a/4434c17eb32ef30b254b9e8b9830cee4e516f08b47fdd291c5b1255b8101/cryptography-45.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:00e8724bdad672d75e6f069b27970883179bd472cd24a63f6e620ca7e41cc0c5", size = 4210553, upload-time = "2025-08-05T23:58:30.596Z" }, - { url = "https://files.pythonhosted.org/packages/ef/1d/09a5df8e0c4b7970f5d1f3aff1b640df6d4be28a64cae970d56c6cf1c772/cryptography-45.0.6-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a3085d1b319d35296176af31c90338eeb2ddac8104661df79f80e1d9787b8b2", size = 3894499, upload-time = "2025-08-05T23:58:32.03Z" }, - { url = "https://files.pythonhosted.org/packages/79/62/120842ab20d9150a9d3a6bdc07fe2870384e82f5266d41c53b08a3a96b34/cryptography-45.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1b7fa6a1c1188c7ee32e47590d16a5a0646270921f8020efc9a511648e1b2e08", size = 4458484, upload-time = "2025-08-05T23:58:33.526Z" }, - { url = "https://files.pythonhosted.org/packages/fd/80/1bc3634d45ddfed0871bfba52cf8f1ad724761662a0c792b97a951fb1b30/cryptography-45.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:275ba5cc0d9e320cd70f8e7b96d9e59903c815ca579ab96c1e37278d231fc402", size = 4210281, upload-time = "2025-08-05T23:58:35.445Z" }, - { url = "https://files.pythonhosted.org/packages/7d/fe/ffb12c2d83d0ee625f124880a1f023b5878f79da92e64c37962bbbe35f3f/cryptography-45.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f4028f29a9f38a2025abedb2e409973709c660d44319c61762202206ed577c42", size = 4456890, upload-time = "2025-08-05T23:58:36.923Z" }, - { url = "https://files.pythonhosted.org/packages/8c/8e/b3f3fe0dc82c77a0deb5f493b23311e09193f2268b77196ec0f7a36e3f3e/cryptography-45.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ee411a1b977f40bd075392c80c10b58025ee5c6b47a822a33c1198598a7a5f05", size = 4333247, upload-time = "2025-08-05T23:58:38.781Z" }, - { url = "https://files.pythonhosted.org/packages/b3/a6/c3ef2ab9e334da27a1d7b56af4a2417d77e7806b2e0f90d6267ce120d2e4/cryptography-45.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e2a21a8eda2d86bb604934b6b37691585bd095c1f788530c1fcefc53a82b3453", size = 4565045, upload-time = "2025-08-05T23:58:40.415Z" }, - { url = "https://files.pythonhosted.org/packages/31/c3/77722446b13fa71dddd820a5faab4ce6db49e7e0bf8312ef4192a3f78e2f/cryptography-45.0.6-cp311-abi3-win32.whl", hash = "sha256:d063341378d7ee9c91f9d23b431a3502fc8bfacd54ef0a27baa72a0843b29159", size = 2928923, upload-time = "2025-08-05T23:58:41.919Z" }, - { url = "https://files.pythonhosted.org/packages/38/63/a025c3225188a811b82932a4dcc8457a26c3729d81578ccecbcce2cb784e/cryptography-45.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:833dc32dfc1e39b7376a87b9a6a4288a10aae234631268486558920029b086ec", size = 3403805, upload-time = "2025-08-05T23:58:43.792Z" }, - { url = "https://files.pythonhosted.org/packages/5b/af/bcfbea93a30809f126d51c074ee0fac5bd9d57d068edf56c2a73abedbea4/cryptography-45.0.6-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:3436128a60a5e5490603ab2adbabc8763613f638513ffa7d311c900a8349a2a0", size = 7020111, upload-time = "2025-08-05T23:58:45.316Z" }, - { url = "https://files.pythonhosted.org/packages/98/c6/ea5173689e014f1a8470899cd5beeb358e22bb3cf5a876060f9d1ca78af4/cryptography-45.0.6-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0d9ef57b6768d9fa58e92f4947cea96ade1233c0e236db22ba44748ffedca394", size = 4198169, upload-time = "2025-08-05T23:58:47.121Z" }, - { url = "https://files.pythonhosted.org/packages/ba/73/b12995edc0c7e2311ffb57ebd3b351f6b268fed37d93bfc6f9856e01c473/cryptography-45.0.6-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea3c42f2016a5bbf71825537c2ad753f2870191134933196bee408aac397b3d9", size = 4421273, upload-time = "2025-08-05T23:58:48.557Z" }, - { url = "https://files.pythonhosted.org/packages/f7/6e/286894f6f71926bc0da67408c853dd9ba953f662dcb70993a59fd499f111/cryptography-45.0.6-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:20ae4906a13716139d6d762ceb3e0e7e110f7955f3bc3876e3a07f5daadec5f3", size = 4199211, upload-time = "2025-08-05T23:58:50.139Z" }, - { url = "https://files.pythonhosted.org/packages/de/34/a7f55e39b9623c5cb571d77a6a90387fe557908ffc44f6872f26ca8ae270/cryptography-45.0.6-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dac5ec199038b8e131365e2324c03d20e97fe214af051d20c49db129844e8b3", size = 3883732, upload-time = "2025-08-05T23:58:52.253Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b9/c6d32edbcba0cd9f5df90f29ed46a65c4631c4fbe11187feb9169c6ff506/cryptography-45.0.6-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:18f878a34b90d688982e43f4b700408b478102dd58b3e39de21b5ebf6509c301", size = 4450655, upload-time = "2025-08-05T23:58:53.848Z" }, - { url = "https://files.pythonhosted.org/packages/77/2d/09b097adfdee0227cfd4c699b3375a842080f065bab9014248933497c3f9/cryptography-45.0.6-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5bd6020c80c5b2b2242d6c48487d7b85700f5e0038e67b29d706f98440d66eb5", size = 4198956, upload-time = "2025-08-05T23:58:55.209Z" }, - { url = "https://files.pythonhosted.org/packages/55/66/061ec6689207d54effdff535bbdf85cc380d32dd5377173085812565cf38/cryptography-45.0.6-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:eccddbd986e43014263eda489abbddfbc287af5cddfd690477993dbb31e31016", size = 4449859, upload-time = "2025-08-05T23:58:56.639Z" }, - { url = "https://files.pythonhosted.org/packages/41/ff/e7d5a2ad2d035e5a2af116e1a3adb4d8fcd0be92a18032917a089c6e5028/cryptography-45.0.6-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:550ae02148206beb722cfe4ef0933f9352bab26b087af00e48fdfb9ade35c5b3", size = 4320254, upload-time = "2025-08-05T23:58:58.833Z" }, - { url = "https://files.pythonhosted.org/packages/82/27/092d311af22095d288f4db89fcaebadfb2f28944f3d790a4cf51fe5ddaeb/cryptography-45.0.6-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5b64e668fc3528e77efa51ca70fadcd6610e8ab231e3e06ae2bab3b31c2b8ed9", size = 4554815, upload-time = "2025-08-05T23:59:00.283Z" }, - { url = "https://files.pythonhosted.org/packages/7e/01/aa2f4940262d588a8fdf4edabe4cda45854d00ebc6eaac12568b3a491a16/cryptography-45.0.6-cp37-abi3-win32.whl", hash = "sha256:780c40fb751c7d2b0c6786ceee6b6f871e86e8718a8ff4bc35073ac353c7cd02", size = 2912147, upload-time = "2025-08-05T23:59:01.716Z" }, - { url = "https://files.pythonhosted.org/packages/0a/bc/16e0276078c2de3ceef6b5a34b965f4436215efac45313df90d55f0ba2d2/cryptography-45.0.6-cp37-abi3-win_amd64.whl", hash = "sha256:20d15aed3ee522faac1a39fbfdfee25d17b1284bafd808e1640a74846d7c4d1b", size = 3390459, upload-time = "2025-08-05T23:59:03.358Z" }, - { url = "https://files.pythonhosted.org/packages/61/69/c252de4ec047ba2f567ecb53149410219577d408c2aea9c989acae7eafce/cryptography-45.0.6-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fc022c1fa5acff6def2fc6d7819bbbd31ccddfe67d075331a65d9cfb28a20983", size = 3584669, upload-time = "2025-08-05T23:59:15.431Z" }, - { url = "https://files.pythonhosted.org/packages/e3/fe/deea71e9f310a31fe0a6bfee670955152128d309ea2d1c79e2a5ae0f0401/cryptography-45.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3de77e4df42ac8d4e4d6cdb342d989803ad37707cf8f3fbf7b088c9cbdd46427", size = 4153022, upload-time = "2025-08-05T23:59:16.954Z" }, - { url = "https://files.pythonhosted.org/packages/60/45/a77452f5e49cb580feedba6606d66ae7b82c128947aa754533b3d1bd44b0/cryptography-45.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:599c8d7df950aa68baa7e98f7b73f4f414c9f02d0e8104a30c0182a07732638b", size = 4386802, upload-time = "2025-08-05T23:59:18.55Z" }, - { url = "https://files.pythonhosted.org/packages/a3/b9/a2f747d2acd5e3075fdf5c145c7c3568895daaa38b3b0c960ef830db6cdc/cryptography-45.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:31a2b9a10530a1cb04ffd6aa1cd4d3be9ed49f7d77a4dafe198f3b382f41545c", size = 4152706, upload-time = "2025-08-05T23:59:20.044Z" }, - { url = "https://files.pythonhosted.org/packages/81/ec/381b3e8d0685a3f3f304a382aa3dfce36af2d76467da0fd4bb21ddccc7b2/cryptography-45.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:e5b3dda1b00fb41da3af4c5ef3f922a200e33ee5ba0f0bc9ecf0b0c173958385", size = 4386740, upload-time = "2025-08-05T23:59:21.525Z" }, - { url = "https://files.pythonhosted.org/packages/0a/76/cf8d69da8d0b5ecb0db406f24a63a3f69ba5e791a11b782aeeefef27ccbb/cryptography-45.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:629127cfdcdc6806dfe234734d7cb8ac54edaf572148274fa377a7d3405b0043", size = 3331874, upload-time = "2025-08-05T23:59:23.017Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/a7/35/c495bffc2056f2dadb32434f1feedd79abde2a7f8363e1974afa9c33c7e2/cryptography-45.0.7.tar.gz", hash = "sha256:4b1654dfc64ea479c242508eb8c724044f1e964a47d1d1cacc5132292d851971", size = 744980, upload-time = "2025-09-01T11:15:03.146Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/91/925c0ac74362172ae4516000fe877912e33b5983df735ff290c653de4913/cryptography-45.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3be4f21c6245930688bd9e162829480de027f8bf962ede33d4f8ba7d67a00cee", size = 7041105, upload-time = "2025-09-01T11:13:59.684Z" }, + { url = "https://files.pythonhosted.org/packages/fc/63/43641c5acce3a6105cf8bd5baeceeb1846bb63067d26dae3e5db59f1513a/cryptography-45.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:67285f8a611b0ebc0857ced2081e30302909f571a46bfa7a3cc0ad303fe015c6", size = 4205799, upload-time = "2025-09-01T11:14:02.517Z" }, + { url = "https://files.pythonhosted.org/packages/bc/29/c238dd9107f10bfde09a4d1c52fd38828b1aa353ced11f358b5dd2507d24/cryptography-45.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:577470e39e60a6cd7780793202e63536026d9b8641de011ed9d8174da9ca5339", size = 4430504, upload-time = "2025-09-01T11:14:04.522Z" }, + { url = "https://files.pythonhosted.org/packages/62/62/24203e7cbcc9bd7c94739428cd30680b18ae6b18377ae66075c8e4771b1b/cryptography-45.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4bd3e5c4b9682bc112d634f2c6ccc6736ed3635fc3319ac2bb11d768cc5a00d8", size = 4209542, upload-time = "2025-09-01T11:14:06.309Z" }, + { url = "https://files.pythonhosted.org/packages/cd/e3/e7de4771a08620eef2389b86cd87a2c50326827dea5528feb70595439ce4/cryptography-45.0.7-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:465ccac9d70115cd4de7186e60cfe989de73f7bb23e8a7aa45af18f7412e75bf", size = 3889244, upload-time = "2025-09-01T11:14:08.152Z" }, + { url = "https://files.pythonhosted.org/packages/96/b8/bca71059e79a0bb2f8e4ec61d9c205fbe97876318566cde3b5092529faa9/cryptography-45.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:16ede8a4f7929b4b7ff3642eba2bf79aa1d71f24ab6ee443935c0d269b6bc513", size = 4461975, upload-time = "2025-09-01T11:14:09.755Z" }, + { url = "https://files.pythonhosted.org/packages/58/67/3f5b26937fe1218c40e95ef4ff8d23c8dc05aa950d54200cc7ea5fb58d28/cryptography-45.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8978132287a9d3ad6b54fcd1e08548033cc09dc6aacacb6c004c73c3eb5d3ac3", size = 4209082, upload-time = "2025-09-01T11:14:11.229Z" }, + { url = "https://files.pythonhosted.org/packages/0e/e4/b3e68a4ac363406a56cf7b741eeb80d05284d8c60ee1a55cdc7587e2a553/cryptography-45.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b6a0e535baec27b528cb07a119f321ac024592388c5681a5ced167ae98e9fff3", size = 4460397, upload-time = "2025-09-01T11:14:12.924Z" }, + { url = "https://files.pythonhosted.org/packages/22/49/2c93f3cd4e3efc8cb22b02678c1fad691cff9dd71bb889e030d100acbfe0/cryptography-45.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a24ee598d10befaec178efdff6054bc4d7e883f615bfbcd08126a0f4931c83a6", size = 4337244, upload-time = "2025-09-01T11:14:14.431Z" }, + { url = "https://files.pythonhosted.org/packages/04/19/030f400de0bccccc09aa262706d90f2ec23d56bc4eb4f4e8268d0ddf3fb8/cryptography-45.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa26fa54c0a9384c27fcdc905a2fb7d60ac6e47d14bc2692145f2b3b1e2cfdbd", size = 4568862, upload-time = "2025-09-01T11:14:16.185Z" }, + { url = "https://files.pythonhosted.org/packages/29/56/3034a3a353efa65116fa20eb3c990a8c9f0d3db4085429040a7eef9ada5f/cryptography-45.0.7-cp311-abi3-win32.whl", hash = "sha256:bef32a5e327bd8e5af915d3416ffefdbe65ed975b646b3805be81b23580b57b8", size = 2936578, upload-time = "2025-09-01T11:14:17.638Z" }, + { url = "https://files.pythonhosted.org/packages/b3/61/0ab90f421c6194705a99d0fa9f6ee2045d916e4455fdbb095a9c2c9a520f/cryptography-45.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:3808e6b2e5f0b46d981c24d79648e5c25c35e59902ea4391a0dcb3e667bf7443", size = 3405400, upload-time = "2025-09-01T11:14:18.958Z" }, + { url = "https://files.pythonhosted.org/packages/63/e8/c436233ddf19c5f15b25ace33979a9dd2e7aa1a59209a0ee8554179f1cc0/cryptography-45.0.7-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bfb4c801f65dd61cedfc61a83732327fafbac55a47282e6f26f073ca7a41c3b2", size = 7021824, upload-time = "2025-09-01T11:14:20.954Z" }, + { url = "https://files.pythonhosted.org/packages/bc/4c/8f57f2500d0ccd2675c5d0cc462095adf3faa8c52294ba085c036befb901/cryptography-45.0.7-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:81823935e2f8d476707e85a78a405953a03ef7b7b4f55f93f7c2d9680e5e0691", size = 4202233, upload-time = "2025-09-01T11:14:22.454Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ac/59b7790b4ccaed739fc44775ce4645c9b8ce54cbec53edf16c74fd80cb2b/cryptography-45.0.7-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3994c809c17fc570c2af12c9b840d7cea85a9fd3e5c0e0491f4fa3c029216d59", size = 4423075, upload-time = "2025-09-01T11:14:24.287Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/d4f07ea21434bf891faa088a6ac15d6d98093a66e75e30ad08e88aa2b9ba/cryptography-45.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dad43797959a74103cb59c5dac71409f9c27d34c8a05921341fb64ea8ccb1dd4", size = 4204517, upload-time = "2025-09-01T11:14:25.679Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ac/924a723299848b4c741c1059752c7cfe09473b6fd77d2920398fc26bfb53/cryptography-45.0.7-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ce7a453385e4c4693985b4a4a3533e041558851eae061a58a5405363b098fcd3", size = 3882893, upload-time = "2025-09-01T11:14:27.1Z" }, + { url = "https://files.pythonhosted.org/packages/83/dc/4dab2ff0a871cc2d81d3ae6d780991c0192b259c35e4d83fe1de18b20c70/cryptography-45.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b04f85ac3a90c227b6e5890acb0edbaf3140938dbecf07bff618bf3638578cf1", size = 4450132, upload-time = "2025-09-01T11:14:28.58Z" }, + { url = "https://files.pythonhosted.org/packages/12/dd/b2882b65db8fc944585d7fb00d67cf84a9cef4e77d9ba8f69082e911d0de/cryptography-45.0.7-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:48c41a44ef8b8c2e80ca4527ee81daa4c527df3ecbc9423c41a420a9559d0e27", size = 4204086, upload-time = "2025-09-01T11:14:30.572Z" }, + { url = "https://files.pythonhosted.org/packages/5d/fa/1d5745d878048699b8eb87c984d4ccc5da4f5008dfd3ad7a94040caca23a/cryptography-45.0.7-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f3df7b3d0f91b88b2106031fd995802a2e9ae13e02c36c1fc075b43f420f3a17", size = 4449383, upload-time = "2025-09-01T11:14:32.046Z" }, + { url = "https://files.pythonhosted.org/packages/36/8b/fc61f87931bc030598e1876c45b936867bb72777eac693e905ab89832670/cryptography-45.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd342f085542f6eb894ca00ef70236ea46070c8a13824c6bde0dfdcd36065b9b", size = 4332186, upload-time = "2025-09-01T11:14:33.95Z" }, + { url = "https://files.pythonhosted.org/packages/0b/11/09700ddad7443ccb11d674efdbe9a832b4455dc1f16566d9bd3834922ce5/cryptography-45.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1993a1bb7e4eccfb922b6cd414f072e08ff5816702a0bdb8941c247a6b1b287c", size = 4561639, upload-time = "2025-09-01T11:14:35.343Z" }, + { url = "https://files.pythonhosted.org/packages/71/ed/8f4c1337e9d3b94d8e50ae0b08ad0304a5709d483bfcadfcc77a23dbcb52/cryptography-45.0.7-cp37-abi3-win32.whl", hash = "sha256:18fcf70f243fe07252dcb1b268a687f2358025ce32f9f88028ca5c364b123ef5", size = 2926552, upload-time = "2025-09-01T11:14:36.929Z" }, + { url = "https://files.pythonhosted.org/packages/bc/ff/026513ecad58dacd45d1d24ebe52b852165a26e287177de1d545325c0c25/cryptography-45.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:7285a89df4900ed3bfaad5679b1e668cb4b38a8de1ccbfc84b05f34512da0a90", size = 3392742, upload-time = "2025-09-01T11:14:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/99/4e/49199a4c82946938a3e05d2e8ad9482484ba48bbc1e809e3d506c686d051/cryptography-45.0.7-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a862753b36620af6fc54209264f92c716367f2f0ff4624952276a6bbd18cbde", size = 3584634, upload-time = "2025-09-01T11:14:50.593Z" }, + { url = "https://files.pythonhosted.org/packages/16/ce/5f6ff59ea9c7779dba51b84871c19962529bdcc12e1a6ea172664916c550/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:06ce84dc14df0bf6ea84666f958e6080cdb6fe1231be2a51f3fc1267d9f3fb34", size = 4149533, upload-time = "2025-09-01T11:14:52.091Z" }, + { url = "https://files.pythonhosted.org/packages/ce/13/b3cfbd257ac96da4b88b46372e662009b7a16833bfc5da33bb97dd5631ae/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d0c5c6bac22b177bf8da7435d9d27a6834ee130309749d162b26c3105c0795a9", size = 4385557, upload-time = "2025-09-01T11:14:53.551Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c5/8c59d6b7c7b439ba4fc8d0cab868027fd095f215031bc123c3a070962912/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:2f641b64acc00811da98df63df7d59fd4706c0df449da71cb7ac39a0732b40ae", size = 4149023, upload-time = "2025-09-01T11:14:55.022Z" }, + { url = "https://files.pythonhosted.org/packages/55/32/05385c86d6ca9ab0b4d5bb442d2e3d85e727939a11f3e163fc776ce5eb40/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:f5414a788ecc6ee6bc58560e85ca624258a55ca434884445440a810796ea0e0b", size = 4385722, upload-time = "2025-09-01T11:14:57.319Z" }, + { url = "https://files.pythonhosted.org/packages/23/87/7ce86f3fa14bc11a5a48c30d8103c26e09b6465f8d8e9d74cf7a0714f043/cryptography-45.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:1f3d56f73595376f4244646dd5c5870c14c196949807be39e79e7bd9bac3da63", size = 3332908, upload-time = "2025-09-01T11:14:58.78Z" }, ] [[package]] @@ -825,11 +825,11 @@ wheels = [ [[package]] name = "executing" -version = "2.2.0" +version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693, upload-time = "2025-01-22T15:41:29.403Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702, upload-time = "2025-01-22T15:41:25.929Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, ] [[package]] @@ -953,11 +953,11 @@ wheels = [ [[package]] name = "fsspec" -version = "2025.7.0" +version = "2025.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8b/02/0835e6ab9cfc03916fe3f78c0956cfcdb6ff2669ffa6651065d5ebf7fc98/fsspec-2025.7.0.tar.gz", hash = "sha256:786120687ffa54b8283d942929540d8bc5ccfa820deb555a2b5d0ed2b737bf58", size = 304432, upload-time = "2025-07-15T16:05:21.19Z" } +sdist = { url = "https://files.pythonhosted.org/packages/de/e0/bab50af11c2d75c9c4a2a26a5254573c0bd97cea152254401510950486fa/fsspec-2025.9.0.tar.gz", hash = "sha256:19fd429483d25d28b65ec68f9f4adc16c17ea2c7c7bf54ec61360d478fb19c19", size = 304847, upload-time = "2025-09-02T19:10:49.215Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/e0/014d5d9d7a4564cf1c40b5039bc882db69fd881111e03ab3657ac0b218e2/fsspec-2025.7.0-py3-none-any.whl", hash = "sha256:8b012e39f63c7d5f10474de957f3ab793b47b45ae7d39f2fb735f8bbe25c0e21", size = 199597, upload-time = "2025-07-15T16:05:19.529Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl", hash = "sha256:530dc2a2af60a414a832059574df4a6e10cce927f6f4a78209390fe38955cfb7", size = 199289, upload-time = "2025-09-02T19:10:47.708Z" }, ] [[package]] @@ -978,7 +978,7 @@ wheels = [ [[package]] name = "gcsfs" -version = "2025.7.0" +version = "2025.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -989,9 +989,9 @@ dependencies = [ { name = "google-cloud-storage" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/d7/5eafe9f09f1bb09433a473cef7984cd52c398592c8fd09974e0ad87cfea4/gcsfs-2025.7.0.tar.gz", hash = "sha256:ad3ff66cf189ae8fc375ac8a2af409003dbca02357621cb94a66e457e02ba420", size = 82659, upload-time = "2025-07-15T16:49:21.647Z" } +sdist = { url = "https://files.pythonhosted.org/packages/54/55/cd737f96929f9cf52666bd49e9b4d1aac697655b3ab17c49ab4fb587bb12/gcsfs-2025.9.0.tar.gz", hash = "sha256:36b8c379d9789d5332a45a3aa2840ec518ff73c6d21c1e962f53318d1cd65db9", size = 82843, upload-time = "2025-09-02T19:23:19.22Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/21/f5/54bccbee01efbc25581db6aafefb6f6c277d880930f7a083b10052382463/gcsfs-2025.7.0-py2.py3-none-any.whl", hash = "sha256:653503331d58cb02bb34e725d4595d166e93f7f2f3ff88e4c66ef535ae66eae5", size = 36815, upload-time = "2025-07-15T16:49:20.333Z" }, + { url = "https://files.pythonhosted.org/packages/4c/41/f793f3bae39f9fbaabf935d0afcf3aa99e417c92096f3e232ac085aaddbf/gcsfs-2025.9.0-py2.py3-none-any.whl", hash = "sha256:38208bc79af60c693e44ff2f0bd6fd3ca664fea1940fe6770ac1c6003aa0f559", size = 36893, upload-time = "2025-09-02T19:23:18.002Z" }, ] [[package]] @@ -1052,7 +1052,7 @@ wheels = [ [[package]] name = "google-cloud-storage" -version = "3.3.0" +version = "3.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core" }, @@ -1062,9 +1062,9 @@ dependencies = [ { name = "google-resumable-media" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1e/91/10b9ddd5baacde375dcd7e6716b5024b3f65a22366f74c26926b6aa84e4e/google_cloud_storage-3.3.0.tar.gz", hash = "sha256:ae9d891d53e17d9681d7c4ef1ffeea0cde9bdc53d5b64fa6ff6bf30d1911cf61", size = 7781974, upload-time = "2025-08-12T09:10:36.245Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/0d/6be1c7e10d1e186e22990fdc22e7ece79f7c622370793cfe88aa8c658316/google_cloud_storage-3.3.1.tar.gz", hash = "sha256:60f291b0881e5c72919b156d1ee276d1b69a2538fcdc35f4e87559ae11678f77", size = 17224623, upload-time = "2025-09-01T05:59:02.804Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/41/9d/2814a2c47429dc2e197e176de25a946d4538422b081ade8638e585e4006f/google_cloud_storage-3.3.0-py3-none-any.whl", hash = "sha256:0338ecd6621b3ecacb108f1cf7513ff0d1bca7f1ff4d58e0220b59f3a725ff23", size = 274270, upload-time = "2025-08-12T09:10:34.793Z" }, + { url = "https://files.pythonhosted.org/packages/80/67/68eee082fc77e718fa483893ac2463fe0ae8f28ccab334cea9dc5aba99b0/google_cloud_storage-3.3.1-py3-none-any.whl", hash = "sha256:8cace9359b85f315f21868cf771143d6dbb47dcc5a3a9317c8207accc4d10fd3", size = 275070, upload-time = "2025-09-01T05:59:00.633Z" }, ] [[package]] @@ -1236,7 +1236,7 @@ wheels = [ [[package]] name = "ipython" -version = "9.4.0" +version = "9.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1251,9 +1251,9 @@ dependencies = [ { name = "traitlets" }, { name = "typing-extensions", marker = "python_full_version < '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/54/80/406f9e3bde1c1fd9bf5a0be9d090f8ae623e401b7670d8f6fdf2ab679891/ipython-9.4.0.tar.gz", hash = "sha256:c033c6d4e7914c3d9768aabe76bbe87ba1dc66a92a05db6bfa1125d81f2ee270", size = 4385338, upload-time = "2025-07-01T11:11:30.606Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/71/a86262bf5a68bf211bcc71fe302af7e05f18a2852fdc610a854d20d085e6/ipython-9.5.0.tar.gz", hash = "sha256:129c44b941fe6d9b82d36fc7a7c18127ddb1d6f02f78f867f402e2e3adde3113", size = 4389137, upload-time = "2025-08-29T12:15:21.519Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/63/f8/0031ee2b906a15a33d6bfc12dd09c3dfa966b3cb5b284ecfb7549e6ac3c4/ipython-9.4.0-py3-none-any.whl", hash = "sha256:25850f025a446d9b359e8d296ba175a36aedd32e83ca9b5060430fe16801f066", size = 611021, upload-time = "2025-07-01T11:11:27.85Z" }, + { url = "https://files.pythonhosted.org/packages/08/2a/5628a99d04acb2d2f2e749cdf4ea571d2575e898df0528a090948018b726/ipython-9.5.0-py3-none-any.whl", hash = "sha256:88369ffa1d5817d609120daa523a6da06d02518e582347c29f8451732a9c5e72", size = 612426, upload-time = "2025-08-29T12:15:18.866Z" }, ] [[package]] @@ -1855,11 +1855,11 @@ wheels = [ [[package]] name = "narwhals" -version = "2.2.0" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/8f/6b3d8c19540eaaa50778a8bbbe54e025d3f93aca6cdd5a4de3044c36f83c/narwhals-2.2.0.tar.gz", hash = "sha256:f6a34f2699acabe2c17339c104f0bec28b9f7a55fbc7f8d485d49bea72d12b8a", size = 547070, upload-time = "2025-08-25T07:51:58.904Z" } +sdist = { url = "https://files.pythonhosted.org/packages/39/30/7a55d6b4345f0fa27e762754a30ca7d5ba52ee53a2f4878a9d5e641b1d5b/narwhals-2.3.0.tar.gz", hash = "sha256:b66bc4ab7b6746354f60c4b3941e3ce60c066588c35360e2dc6c063489000a16", size = 552774, upload-time = "2025-09-01T08:29:27.502Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/54/1ecca75e51d7da8ca53d1ffa8636ef9077a6eaa31f43ade71360b3e6449a/narwhals-2.2.0-py3-none-any.whl", hash = "sha256:2b5e3d61a486fa4328c286b0c8018b3e781a964947ff725d66ba12f6d5ca3d2a", size = 401021, upload-time = "2025-08-25T07:51:56.97Z" }, + { url = "https://files.pythonhosted.org/packages/7d/81/1cf7a468ef2f8e88266d5c3e5ab026a045aa76150e6640bfe9c5450a8c11/narwhals-2.3.0-py3-none-any.whl", hash = "sha256:5507b1a9a9c2b1c55a627fdf6cf722fef2e23498bd14362a332c8848a311c321", size = 404361, upload-time = "2025-09-01T08:29:25.863Z" }, ] [[package]] @@ -2198,11 +2198,11 @@ wheels = [ [[package]] name = "platformdirs" -version = "4.3.8" +version = "4.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } +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/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, + { 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]] @@ -2244,14 +2244,14 @@ wheels = [ [[package]] name = "prompt-toolkit" -version = "3.0.51" +version = "3.0.52" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bb/6e/9d084c929dfe9e3bfe0c6a47e31f78a25c54627d64a66e884a8bf5474f1c/prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed", size = 428940, upload-time = "2025-04-15T09:18:47.731Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", size = 387810, upload-time = "2025-04-15T09:18:44.753Z" }, + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, ] [[package]] @@ -2690,61 +2690,63 @@ wheels = [ [[package]] name = "rapidfuzz" -version = "3.13.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/6895abc3a3d056b9698da3199b04c0e56226d530ae44a470edabf8b664f0/rapidfuzz-3.13.0.tar.gz", hash = "sha256:d2eaf3839e52cbcc0accbe9817a67b4b0fcf70aaeb229cfddc1c28061f9ce5d8", size = 57904226, upload-time = "2025-04-03T20:38:51.226Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/17/9be9eff5a3c7dfc831c2511262082c6786dca2ce21aa8194eef1cb71d67a/rapidfuzz-3.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d395a5cad0c09c7f096433e5fd4224d83b53298d53499945a9b0e5a971a84f3a", size = 1999453, upload-time = "2025-04-03T20:35:40.804Z" }, - { url = "https://files.pythonhosted.org/packages/75/67/62e57896ecbabe363f027d24cc769d55dd49019e576533ec10e492fcd8a2/rapidfuzz-3.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7b3eda607a019169f7187328a8d1648fb9a90265087f6903d7ee3a8eee01805", size = 1450881, upload-time = "2025-04-03T20:35:42.734Z" }, - { url = "https://files.pythonhosted.org/packages/96/5c/691c5304857f3476a7b3df99e91efc32428cbe7d25d234e967cc08346c13/rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98e0bfa602e1942d542de077baf15d658bd9d5dcfe9b762aff791724c1c38b70", size = 1422990, upload-time = "2025-04-03T20:35:45.158Z" }, - { url = "https://files.pythonhosted.org/packages/46/81/7a7e78f977496ee2d613154b86b203d373376bcaae5de7bde92f3ad5a192/rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bef86df6d59667d9655905b02770a0c776d2853971c0773767d5ef8077acd624", size = 5342309, upload-time = "2025-04-03T20:35:46.952Z" }, - { url = "https://files.pythonhosted.org/packages/51/44/12fdd12a76b190fe94bf38d252bb28ddf0ab7a366b943e792803502901a2/rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fedd316c165beed6307bf754dee54d3faca2c47e1f3bcbd67595001dfa11e969", size = 1656881, upload-time = "2025-04-03T20:35:49.954Z" }, - { url = "https://files.pythonhosted.org/packages/27/ae/0d933e660c06fcfb087a0d2492f98322f9348a28b2cc3791a5dbadf6e6fb/rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5158da7f2ec02a930be13bac53bb5903527c073c90ee37804090614cab83c29e", size = 1608494, upload-time = "2025-04-03T20:35:51.646Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2c/4b2f8aafdf9400e5599b6ed2f14bc26ca75f5a923571926ccbc998d4246a/rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b6f913ee4618ddb6d6f3e387b76e8ec2fc5efee313a128809fbd44e65c2bbb2", size = 3072160, upload-time = "2025-04-03T20:35:53.472Z" }, - { url = "https://files.pythonhosted.org/packages/60/7d/030d68d9a653c301114101c3003b31ce01cf2c3224034cd26105224cd249/rapidfuzz-3.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d25fdbce6459ccbbbf23b4b044f56fbd1158b97ac50994eaae2a1c0baae78301", size = 2491549, upload-time = "2025-04-03T20:35:55.391Z" }, - { url = "https://files.pythonhosted.org/packages/8e/cd/7040ba538fc6a8ddc8816a05ecf46af9988b46c148ddd7f74fb0fb73d012/rapidfuzz-3.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25343ccc589a4579fbde832e6a1e27258bfdd7f2eb0f28cb836d6694ab8591fc", size = 7584142, upload-time = "2025-04-03T20:35:57.71Z" }, - { url = "https://files.pythonhosted.org/packages/c1/96/85f7536fbceb0aa92c04a1c37a3fc4fcd4e80649e9ed0fb585382df82edc/rapidfuzz-3.13.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a9ad1f37894e3ffb76bbab76256e8a8b789657183870be11aa64e306bb5228fd", size = 2896234, upload-time = "2025-04-03T20:35:59.969Z" }, - { url = "https://files.pythonhosted.org/packages/55/fd/460e78438e7019f2462fe9d4ecc880577ba340df7974c8a4cfe8d8d029df/rapidfuzz-3.13.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5dc71ef23845bb6b62d194c39a97bb30ff171389c9812d83030c1199f319098c", size = 3437420, upload-time = "2025-04-03T20:36:01.91Z" }, - { url = "https://files.pythonhosted.org/packages/cc/df/c3c308a106a0993befd140a414c5ea78789d201cf1dfffb8fd9749718d4f/rapidfuzz-3.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b7f4c65facdb94f44be759bbd9b6dda1fa54d0d6169cdf1a209a5ab97d311a75", size = 4410860, upload-time = "2025-04-03T20:36:04.352Z" }, - { url = "https://files.pythonhosted.org/packages/75/ee/9d4ece247f9b26936cdeaae600e494af587ce9bf8ddc47d88435f05cfd05/rapidfuzz-3.13.0-cp311-cp311-win32.whl", hash = "sha256:b5104b62711565e0ff6deab2a8f5dbf1fbe333c5155abe26d2cfd6f1849b6c87", size = 1843161, upload-time = "2025-04-03T20:36:06.802Z" }, - { url = "https://files.pythonhosted.org/packages/c9/5a/d00e1f63564050a20279015acb29ecaf41646adfacc6ce2e1e450f7f2633/rapidfuzz-3.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:9093cdeb926deb32a4887ebe6910f57fbcdbc9fbfa52252c10b56ef2efb0289f", size = 1629962, upload-time = "2025-04-03T20:36:09.133Z" }, - { url = "https://files.pythonhosted.org/packages/3b/74/0a3de18bc2576b794f41ccd07720b623e840fda219ab57091897f2320fdd/rapidfuzz-3.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:f70f646751b6aa9d05be1fb40372f006cc89d6aad54e9d79ae97bd1f5fce5203", size = 866631, upload-time = "2025-04-03T20:36:11.022Z" }, - { url = "https://files.pythonhosted.org/packages/13/4b/a326f57a4efed8f5505b25102797a58e37ee11d94afd9d9422cb7c76117e/rapidfuzz-3.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a1a6a906ba62f2556372282b1ef37b26bca67e3d2ea957277cfcefc6275cca7", size = 1989501, upload-time = "2025-04-03T20:36:13.43Z" }, - { url = "https://files.pythonhosted.org/packages/b7/53/1f7eb7ee83a06c400089ec7cb841cbd581c2edd7a4b21eb2f31030b88daa/rapidfuzz-3.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fd0975e015b05c79a97f38883a11236f5a24cca83aa992bd2558ceaa5652b26", size = 1445379, upload-time = "2025-04-03T20:36:16.439Z" }, - { url = "https://files.pythonhosted.org/packages/07/09/de8069a4599cc8e6d194e5fa1782c561151dea7d5e2741767137e2a8c1f0/rapidfuzz-3.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d4e13593d298c50c4f94ce453f757b4b398af3fa0fd2fde693c3e51195b7f69", size = 1405986, upload-time = "2025-04-03T20:36:18.447Z" }, - { url = "https://files.pythonhosted.org/packages/5d/77/d9a90b39c16eca20d70fec4ca377fbe9ea4c0d358c6e4736ab0e0e78aaf6/rapidfuzz-3.13.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed6f416bda1c9133000009d84d9409823eb2358df0950231cc936e4bf784eb97", size = 5310809, upload-time = "2025-04-03T20:36:20.324Z" }, - { url = "https://files.pythonhosted.org/packages/1e/7d/14da291b0d0f22262d19522afaf63bccf39fc027c981233fb2137a57b71f/rapidfuzz-3.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1dc82b6ed01acb536b94a43996a94471a218f4d89f3fdd9185ab496de4b2a981", size = 1629394, upload-time = "2025-04-03T20:36:22.256Z" }, - { url = "https://files.pythonhosted.org/packages/b7/e4/79ed7e4fa58f37c0f8b7c0a62361f7089b221fe85738ae2dbcfb815e985a/rapidfuzz-3.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9d824de871daa6e443b39ff495a884931970d567eb0dfa213d234337343835f", size = 1600544, upload-time = "2025-04-03T20:36:24.207Z" }, - { url = "https://files.pythonhosted.org/packages/4e/20/e62b4d13ba851b0f36370060025de50a264d625f6b4c32899085ed51f980/rapidfuzz-3.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d18228a2390375cf45726ce1af9d36ff3dc1f11dce9775eae1f1b13ac6ec50f", size = 3052796, upload-time = "2025-04-03T20:36:26.279Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8d/55fdf4387dec10aa177fe3df8dbb0d5022224d95f48664a21d6b62a5299d/rapidfuzz-3.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f5fe634c9482ec5d4a6692afb8c45d370ae86755e5f57aa6c50bfe4ca2bdd87", size = 2464016, upload-time = "2025-04-03T20:36:28.525Z" }, - { url = "https://files.pythonhosted.org/packages/9b/be/0872f6a56c0f473165d3b47d4170fa75263dc5f46985755aa9bf2bbcdea1/rapidfuzz-3.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:694eb531889f71022b2be86f625a4209c4049e74be9ca836919b9e395d5e33b3", size = 7556725, upload-time = "2025-04-03T20:36:30.629Z" }, - { url = "https://files.pythonhosted.org/packages/5d/f3/6c0750e484d885a14840c7a150926f425d524982aca989cdda0bb3bdfa57/rapidfuzz-3.13.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:11b47b40650e06147dee5e51a9c9ad73bb7b86968b6f7d30e503b9f8dd1292db", size = 2859052, upload-time = "2025-04-03T20:36:32.836Z" }, - { url = "https://files.pythonhosted.org/packages/6f/98/5a3a14701b5eb330f444f7883c9840b43fb29c575e292e09c90a270a6e07/rapidfuzz-3.13.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:98b8107ff14f5af0243f27d236bcc6e1ef8e7e3b3c25df114e91e3a99572da73", size = 3390219, upload-time = "2025-04-03T20:36:35.062Z" }, - { url = "https://files.pythonhosted.org/packages/e9/7d/f4642eaaeb474b19974332f2a58471803448be843033e5740965775760a5/rapidfuzz-3.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b836f486dba0aceb2551e838ff3f514a38ee72b015364f739e526d720fdb823a", size = 4377924, upload-time = "2025-04-03T20:36:37.363Z" }, - { url = "https://files.pythonhosted.org/packages/8e/83/fa33f61796731891c3e045d0cbca4436a5c436a170e7f04d42c2423652c3/rapidfuzz-3.13.0-cp312-cp312-win32.whl", hash = "sha256:4671ee300d1818d7bdfd8fa0608580d7778ba701817216f0c17fb29e6b972514", size = 1823915, upload-time = "2025-04-03T20:36:39.451Z" }, - { url = "https://files.pythonhosted.org/packages/03/25/5ee7ab6841ca668567d0897905eebc79c76f6297b73bf05957be887e9c74/rapidfuzz-3.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e2065f68fb1d0bf65adc289c1bdc45ba7e464e406b319d67bb54441a1b9da9e", size = 1616985, upload-time = "2025-04-03T20:36:41.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/5e/3f0fb88db396cb692aefd631e4805854e02120a2382723b90dcae720bcc6/rapidfuzz-3.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:65cc97c2fc2c2fe23586599686f3b1ceeedeca8e598cfcc1b7e56dc8ca7e2aa7", size = 860116, upload-time = "2025-04-03T20:36:43.915Z" }, - { url = "https://files.pythonhosted.org/packages/0a/76/606e71e4227790750f1646f3c5c873e18d6cfeb6f9a77b2b8c4dec8f0f66/rapidfuzz-3.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:09e908064d3684c541d312bd4c7b05acb99a2c764f6231bd507d4b4b65226c23", size = 1982282, upload-time = "2025-04-03T20:36:46.149Z" }, - { url = "https://files.pythonhosted.org/packages/0a/f5/d0b48c6b902607a59fd5932a54e3518dae8223814db8349b0176e6e9444b/rapidfuzz-3.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:57c390336cb50d5d3bfb0cfe1467478a15733703af61f6dffb14b1cd312a6fae", size = 1439274, upload-time = "2025-04-03T20:36:48.323Z" }, - { url = "https://files.pythonhosted.org/packages/59/cf/c3ac8c80d8ced6c1f99b5d9674d397ce5d0e9d0939d788d67c010e19c65f/rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0da54aa8547b3c2c188db3d1c7eb4d1bb6dd80baa8cdaeaec3d1da3346ec9caa", size = 1399854, upload-time = "2025-04-03T20:36:50.294Z" }, - { url = "https://files.pythonhosted.org/packages/09/5d/ca8698e452b349c8313faf07bfa84e7d1c2d2edf7ccc67bcfc49bee1259a/rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df8e8c21e67afb9d7fbe18f42c6111fe155e801ab103c81109a61312927cc611", size = 5308962, upload-time = "2025-04-03T20:36:52.421Z" }, - { url = "https://files.pythonhosted.org/packages/66/0a/bebada332854e78e68f3d6c05226b23faca79d71362509dbcf7b002e33b7/rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:461fd13250a2adf8e90ca9a0e1e166515cbcaa5e9c3b1f37545cbbeff9e77f6b", size = 1625016, upload-time = "2025-04-03T20:36:54.639Z" }, - { url = "https://files.pythonhosted.org/packages/de/0c/9e58d4887b86d7121d1c519f7050d1be5eb189d8a8075f5417df6492b4f5/rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2b3dd5d206a12deca16870acc0d6e5036abeb70e3cad6549c294eff15591527", size = 1600414, upload-time = "2025-04-03T20:36:56.669Z" }, - { url = "https://files.pythonhosted.org/packages/9b/df/6096bc669c1311568840bdcbb5a893edc972d1c8d2b4b4325c21d54da5b1/rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1343d745fbf4688e412d8f398c6e6d6f269db99a54456873f232ba2e7aeb4939", size = 3053179, upload-time = "2025-04-03T20:36:59.366Z" }, - { url = "https://files.pythonhosted.org/packages/f9/46/5179c583b75fce3e65a5cd79a3561bd19abd54518cb7c483a89b284bf2b9/rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b1b065f370d54551dcc785c6f9eeb5bd517ae14c983d2784c064b3aa525896df", size = 2456856, upload-time = "2025-04-03T20:37:01.708Z" }, - { url = "https://files.pythonhosted.org/packages/6b/64/e9804212e3286d027ac35bbb66603c9456c2bce23f823b67d2f5cabc05c1/rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:11b125d8edd67e767b2295eac6eb9afe0b1cdc82ea3d4b9257da4b8e06077798", size = 7567107, upload-time = "2025-04-03T20:37:04.521Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f2/7d69e7bf4daec62769b11757ffc31f69afb3ce248947aadbb109fefd9f65/rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c33f9c841630b2bb7e69a3fb5c84a854075bb812c47620978bddc591f764da3d", size = 2854192, upload-time = "2025-04-03T20:37:06.905Z" }, - { url = "https://files.pythonhosted.org/packages/05/21/ab4ad7d7d0f653e6fe2e4ccf11d0245092bef94cdff587a21e534e57bda8/rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ae4574cb66cf1e85d32bb7e9ec45af5409c5b3970b7ceb8dea90168024127566", size = 3398876, upload-time = "2025-04-03T20:37:09.692Z" }, - { url = "https://files.pythonhosted.org/packages/0f/a8/45bba94c2489cb1ee0130dcb46e1df4fa2c2b25269e21ffd15240a80322b/rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e05752418b24bbd411841b256344c26f57da1148c5509e34ea39c7eb5099ab72", size = 4377077, upload-time = "2025-04-03T20:37:11.929Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f3/5e0c6ae452cbb74e5436d3445467447e8c32f3021f48f93f15934b8cffc2/rapidfuzz-3.13.0-cp313-cp313-win32.whl", hash = "sha256:0e1d08cb884805a543f2de1f6744069495ef527e279e05370dd7c83416af83f8", size = 1822066, upload-time = "2025-04-03T20:37:14.425Z" }, - { url = "https://files.pythonhosted.org/packages/96/e3/a98c25c4f74051df4dcf2f393176b8663bfd93c7afc6692c84e96de147a2/rapidfuzz-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9a7c6232be5f809cd39da30ee5d24e6cadd919831e6020ec6c2391f4c3bc9264", size = 1615100, upload-time = "2025-04-03T20:37:16.611Z" }, - { url = "https://files.pythonhosted.org/packages/60/b1/05cd5e697c00cd46d7791915f571b38c8531f714832eff2c5e34537c49ee/rapidfuzz-3.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:3f32f15bacd1838c929b35c84b43618481e1b3d7a61b5ed2db0291b70ae88b53", size = 858976, upload-time = "2025-04-03T20:37:19.336Z" }, - { url = "https://files.pythonhosted.org/packages/88/df/6060c5a9c879b302bd47a73fc012d0db37abf6544c57591bcbc3459673bd/rapidfuzz-3.13.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1ba007f4d35a45ee68656b2eb83b8715e11d0f90e5b9f02d615a8a321ff00c27", size = 1905935, upload-time = "2025-04-03T20:38:18.07Z" }, - { url = "https://files.pythonhosted.org/packages/a2/6c/a0b819b829e20525ef1bd58fc776fb8d07a0c38d819e63ba2b7c311a2ed4/rapidfuzz-3.13.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d7a217310429b43be95b3b8ad7f8fc41aba341109dc91e978cd7c703f928c58f", size = 1383714, upload-time = "2025-04-03T20:38:20.628Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c1/3da3466cc8a9bfb9cd345ad221fac311143b6a9664b5af4adb95b5e6ce01/rapidfuzz-3.13.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:558bf526bcd777de32b7885790a95a9548ffdcce68f704a81207be4a286c1095", size = 1367329, upload-time = "2025-04-03T20:38:23.01Z" }, - { url = "https://files.pythonhosted.org/packages/da/f0/9f2a9043bfc4e66da256b15d728c5fc2d865edf0028824337f5edac36783/rapidfuzz-3.13.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:202a87760f5145140d56153b193a797ae9338f7939eb16652dd7ff96f8faf64c", size = 5251057, upload-time = "2025-04-03T20:38:25.52Z" }, - { url = "https://files.pythonhosted.org/packages/6a/ff/af2cb1d8acf9777d52487af5c6b34ce9d13381a753f991d95ecaca813407/rapidfuzz-3.13.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfcccc08f671646ccb1e413c773bb92e7bba789e3a1796fd49d23c12539fe2e4", size = 2992401, upload-time = "2025-04-03T20:38:28.196Z" }, - { url = "https://files.pythonhosted.org/packages/c1/c5/c243b05a15a27b946180db0d1e4c999bef3f4221505dff9748f1f6c917be/rapidfuzz-3.13.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:1f219f1e3c3194d7a7de222f54450ce12bc907862ff9a8962d83061c1f923c86", size = 1553782, upload-time = "2025-04-03T20:38:30.778Z" }, +version = "3.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/11/0de727b336f28e25101d923c9feeeb64adcf231607fe7e1b083795fa149a/rapidfuzz-3.14.0.tar.gz", hash = "sha256:672b6ba06150e53d7baf4e3d5f12ffe8c213d5088239a15b5ae586ab245ac8b2", size = 58073448, upload-time = "2025-08-27T13:41:31.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/66/6b4aa4c63d9b22a9851a83f3ed4b52e127a1f655f80ecc4894f807a82566/rapidfuzz-3.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6501e49395ad5cecf1623cb4801639faa1c833dbacc07c26fa7b8f7fa19fd1c0", size = 2011991, upload-time = "2025-08-27T13:39:02.27Z" }, + { url = "https://files.pythonhosted.org/packages/ae/b8/a79e997baf4f4467c8428feece5d7b9ac22ff0918ebf793ed247ba5a3f3a/rapidfuzz-3.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c3cd9b8d5e159c67d242f80cae1b9d9b1502779fc69fcd268a1eb7053f58048", size = 1458900, upload-time = "2025-08-27T13:39:03.777Z" }, + { url = "https://files.pythonhosted.org/packages/b5/82/6ca7ebc66d0dd1330e92d08a37412c705d7366216bddd46ca6afcabaa6a0/rapidfuzz-3.14.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a578cadbe61f738685ffa20e56e8346847e40ecb033bdc885373a070cfe4a351", size = 1484735, upload-time = "2025-08-27T13:39:05.502Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5d/26eb60bc8eea194a03b32fdd9a4f5866fa9859dcaedf8da1f256dc9a47fc/rapidfuzz-3.14.0-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b46340872a1736544b23f3c355f292935311623a0e63a271f284ffdbab05e4", size = 1806075, upload-time = "2025-08-27T13:39:07.109Z" }, + { url = "https://files.pythonhosted.org/packages/3a/9c/12f2af41750ae4f30c06d5de1e0f3c4a5f55cbea9dabf3940a096cd8580a/rapidfuzz-3.14.0-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:238422749da213c3dfe36397b746aeda8579682e93b723a1e77655182198e693", size = 2358269, upload-time = "2025-08-27T13:39:08.796Z" }, + { url = "https://files.pythonhosted.org/packages/e2/3b/3c1839d51d1dfa768c8274025a36eedc177ed5b43a9d12cc7d91201eca03/rapidfuzz-3.14.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83f3ad0e7ad3cf1138e36be26f4cacb7580ac0132b26528a89e8168a0875afd8", size = 3313513, upload-time = "2025-08-27T13:39:10.44Z" }, + { url = "https://files.pythonhosted.org/packages/e7/47/ed1384c7c8c39dc36de202860373085ee9c43493d6e9d7bab654d2099da0/rapidfuzz-3.14.0-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:7c34e34fb7e01aeea1e84192cf01daf1d56ccc8a0b34c0833f9799b341c6d539", size = 1320968, upload-time = "2025-08-27T13:39:12.024Z" }, + { url = "https://files.pythonhosted.org/packages/16/0b/3d7458160b5dfe230b05cf8bf62505bf4e2c6d73782dd37248149b43e130/rapidfuzz-3.14.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a58bbbbdd2a150c76c6b3af5ac2bbe9afcff26e6b17e1f60b6bd766cc7094fcf", size = 2507138, upload-time = "2025-08-27T13:39:13.584Z" }, + { url = "https://files.pythonhosted.org/packages/e7/e5/8df797e4f3df2cc308092c5437dda570aa75ea5e5cc3dc1180165fce2332/rapidfuzz-3.14.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d0e50b4bea57bfcda4afee993eef390fd8f0a64981c971ac4decd9452143892d", size = 2629575, upload-time = "2025-08-27T13:39:15.624Z" }, + { url = "https://files.pythonhosted.org/packages/89/f9/e87e94cd6fc22e19a21b44030161b9e9680b5127bcea97aba05be506b66f/rapidfuzz-3.14.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:357eb9d394bfc742d3528e8bb13afa9baebc7fbe863071975426b47fc21db220", size = 2919216, upload-time = "2025-08-27T13:39:17.313Z" }, + { url = "https://files.pythonhosted.org/packages/b5/6e/f20154e8cb7a7c9938241aff7ba0477521bee1f57a57c78706664390a558/rapidfuzz-3.14.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fb960ec526030077658764a309b60e907d86d898f8efbe959845ec2873e514eb", size = 3435208, upload-time = "2025-08-27T13:39:18.942Z" }, + { url = "https://files.pythonhosted.org/packages/43/43/c2d0e17f75ded0f36ee264fc719f67de3610628d983769179e9d8a44c7db/rapidfuzz-3.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6bedb19db81d8d723cc4d914cb079d89ff359364184cc3c3db7cef1fc7819444", size = 4428371, upload-time = "2025-08-27T13:39:20.628Z" }, + { url = "https://files.pythonhosted.org/packages/a6/d7/41f645ad06494a94bafb1be8871585d5723a1f93b34929022014f8f03fef/rapidfuzz-3.14.0-cp311-cp311-win32.whl", hash = "sha256:8dba3d6e10a34aa255a6f6922cf249f8d0b9829e6b00854e371d803040044f7f", size = 1839290, upload-time = "2025-08-27T13:39:22.396Z" }, + { url = "https://files.pythonhosted.org/packages/f3/96/c783107296403cf50acde118596b07aa1af4b0287ac4600b38b0673b1fd7/rapidfuzz-3.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:ce79e37b23c1cbf1dc557159c8f20f6d71e9d28aef63afcf87bcb58c8add096a", size = 1661571, upload-time = "2025-08-27T13:39:24.03Z" }, + { url = "https://files.pythonhosted.org/packages/00/9e/8c562c5d78e31085a07ff1332329711030dd2c25b84c02fb10dcf9be1f64/rapidfuzz-3.14.0-cp311-cp311-win_arm64.whl", hash = "sha256:e140ff4b5d0ea386b998137ddd1335a7bd4201ef987d4cb5a48c3e8c174f8aec", size = 875433, upload-time = "2025-08-27T13:39:26.25Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ca/80c1d697fe42d0caea8d08b0f323b2a4c65a9d057d4d33fe139fd0f1b7d0/rapidfuzz-3.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:93c8739f7bf7931d690aeb527c27e2a61fd578f076d542ddd37e29fa535546b6", size = 2000791, upload-time = "2025-08-27T13:39:28.375Z" }, + { url = "https://files.pythonhosted.org/packages/01/01/e980b8d2e85efb4ff1fca26c590d645186a70e51abd4323f29582d41ba9b/rapidfuzz-3.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7596e95ab03da6cff70f4ec9a5298b2802e8bdd443159d18180b186c80df1416", size = 1455837, upload-time = "2025-08-27T13:39:29.987Z" }, + { url = "https://files.pythonhosted.org/packages/03/35/3433345c659a4c6cf93b66963ef5ec2d5088d230cbca9f035a3e30d13e70/rapidfuzz-3.14.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cdd49e097ced3746eadb5fb87379f377c0b093f9aba1133ae4f311b574e2ed8", size = 1457107, upload-time = "2025-08-27T13:39:31.991Z" }, + { url = "https://files.pythonhosted.org/packages/2b/27/ac98741cd2696330feb462a37cc9b945cb333a1b39f90216fe1af0568cd6/rapidfuzz-3.14.0-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4cd4898f21686bb141e151ba920bcd1744cab339277f484c0f97fe7de2c45c8", size = 1767664, upload-time = "2025-08-27T13:39:33.604Z" }, + { url = "https://files.pythonhosted.org/packages/db/1c/1495395016c05fc5d6d0d2622c4854eab160812c4dbc60f5e076116921cf/rapidfuzz-3.14.0-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:83427518ad72050add47e2cf581080bde81df7f69882e508da3e08faad166b1f", size = 2329980, upload-time = "2025-08-27T13:39:35.204Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e6/587fe4d88eab2a4ea8660744bfebfd0a0d100e7d26fd3fde5062f02ccf84/rapidfuzz-3.14.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05435b4f2472cbf7aac8b837e2e84a165e595c60d79da851da7cfa85ed15895d", size = 3271666, upload-time = "2025-08-27T13:39:36.973Z" }, + { url = "https://files.pythonhosted.org/packages/b4/8e/9928afd7a4727c173de615a4b26e70814ccd9407d87c3c233a01a1b4fc9c/rapidfuzz-3.14.0-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:2dae744c1cdb8b1411ed511a719b505a0348da1970a652bfc735598e68779287", size = 1307744, upload-time = "2025-08-27T13:39:38.825Z" }, + { url = "https://files.pythonhosted.org/packages/e5/5c/03d95b1dc5916e43f505d8bd8da37788b972ccabf14bf3ee0e143b7151d4/rapidfuzz-3.14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9ca05daaca07232037014fc6ce2c2ef0a05c69712f6a5e77da6da5209fb04d7c", size = 2477512, upload-time = "2025-08-27T13:39:40.881Z" }, + { url = "https://files.pythonhosted.org/packages/96/30/a1da6a124e10fd201a75e68ebf0bdedcf47a3878910c2e05deebf08e9e40/rapidfuzz-3.14.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:2227f4b3742295f380adefef7b6338c30434f8a8e18a11895a1a7c9308b6635d", size = 2613793, upload-time = "2025-08-27T13:39:42.62Z" }, + { url = "https://files.pythonhosted.org/packages/76/56/4776943e4b4130e58ebaf2dbea3ce9f4cb3c6c6a5640dcacb0e84e926190/rapidfuzz-3.14.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:847ea42b5a6077bc796e1b99cd357a641207b20e3573917b0469b28b5a22238a", size = 2880096, upload-time = "2025-08-27T13:39:44.394Z" }, + { url = "https://files.pythonhosted.org/packages/60/cc/25d7faa947d159935cfb0cfc270620f250f033338055702d7e8cc1885e00/rapidfuzz-3.14.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:539506f13cf0dd6ef2f846571f8e116dba32a468e52d05a91161785ab7de2ed1", size = 3413927, upload-time = "2025-08-27T13:39:46.142Z" }, + { url = "https://files.pythonhosted.org/packages/2c/39/3090aeb1ca57a71715f5590a890e45097dbc4862f2c0a5a756e022d0f006/rapidfuzz-3.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03c4b4d4f45f846e4eae052ee18d39d6afe659d74f6d99df5a0d2c5d53930505", size = 4387126, upload-time = "2025-08-27T13:39:48.217Z" }, + { url = "https://files.pythonhosted.org/packages/d8/9b/1dd7bd2824ac7c7daeb6b79c5cf7504c5d2a31b564649457061cc3f8ce9a/rapidfuzz-3.14.0-cp312-cp312-win32.whl", hash = "sha256:aff0baa3980a8aeb2ce5e15930140146b5fe3fb2d63c8dc4cb08dfbd2051ceb2", size = 1804449, upload-time = "2025-08-27T13:39:49.971Z" }, + { url = "https://files.pythonhosted.org/packages/31/32/43074dade26b9a82c5d05262b9179b25ec5d665f18c54f66b64b00791fb4/rapidfuzz-3.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:d1eef7f0694fe4cf991f61adaa040955da1e0072c8c41d7db5eb60e83da9e61b", size = 1656931, upload-time = "2025-08-27T13:39:52.195Z" }, + { url = "https://files.pythonhosted.org/packages/ce/82/c78f0ab282acefab5a55cbbc7741165cad787fce7fbeb0bb5b3903d06749/rapidfuzz-3.14.0-cp312-cp312-win_arm64.whl", hash = "sha256:269d8d1fe5830eef46a165a5c6dd240a05ad44c281a77957461b79cede1ece0f", size = 878656, upload-time = "2025-08-27T13:39:53.816Z" }, + { url = "https://files.pythonhosted.org/packages/04/b1/e6875e32209b28a581d3b8ec1ffded8f674de4a27f4540ec312d0ecf4b83/rapidfuzz-3.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5cf3828b8cbac02686e1d5c499c58e43c5f613ad936fe19a2d092e53f3308ccd", size = 2015663, upload-time = "2025-08-27T13:39:55.815Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c7/702472c4f3c4e5f9985bb5143405a5c4aadf3b439193f4174944880c50a3/rapidfuzz-3.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68c3931c19c51c11654cf75f663f34c0c7ea04c456c84ccebfd52b2047121dba", size = 1472180, upload-time = "2025-08-27T13:39:57.663Z" }, + { url = "https://files.pythonhosted.org/packages/49/e1/c22fc941b8e506db9a6f051298e17edbae76e1be63e258e51f13791d5eb2/rapidfuzz-3.14.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b4232168959af46f2c0770769e7986ff6084d97bc4b6b2b16b2bfa34164421b", size = 1461676, upload-time = "2025-08-27T13:39:59.409Z" }, + { url = "https://files.pythonhosted.org/packages/97/4c/9dd58e4b4d2b1b7497c35c5280b4fa064bd6e6e3ed5fcf67513faaa2d4f4/rapidfuzz-3.14.0-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:174c784cecfafe22d783b5124ebffa2e02cc01e49ffe60a28ad86d217977f478", size = 1774563, upload-time = "2025-08-27T13:40:01.284Z" }, + { url = "https://files.pythonhosted.org/packages/96/8f/89a39ab5fbd971e6a25431edbbf66e255d271a0b67aadc340b8e8bf573e7/rapidfuzz-3.14.0-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b2dedf216f43a50f227eee841ef0480e29e26b2ce2d7ee680b28354ede18627", size = 2332659, upload-time = "2025-08-27T13:40:03.04Z" }, + { url = "https://files.pythonhosted.org/packages/34/b0/f30f9bae81a472182787641c9c2430da79431c260f7620899a105ee959d0/rapidfuzz-3.14.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5698239eecf5b759630450ef59521ad3637e5bd4afc2b124ae8af2ff73309c41", size = 3289626, upload-time = "2025-08-27T13:40:04.77Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b9/c9eb0bfb62972123a23b31811d4d345e8dd46cb3083d131dd3c1c97b70af/rapidfuzz-3.14.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:0acc9553fc26f1c291c381a6aa8d3c5625be23b5721f139528af40cc4119ae1d", size = 1324164, upload-time = "2025-08-27T13:40:06.642Z" }, + { url = "https://files.pythonhosted.org/packages/7f/a1/91bf79a76626bd0dae694ad9c57afdad2ca275f9808f69e570be39a99e71/rapidfuzz-3.14.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00141dfd3b8c9ae15fbb5fbd191a08bde63cdfb1f63095d8f5faf1698e30da93", size = 2480695, upload-time = "2025-08-27T13:40:08.459Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6a/bfab3575842d8ccc406c3fa8c618b476363e4218a0d01394543c741ef1bd/rapidfuzz-3.14.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:67f725c3f5713da6e0750dc23f65f0f822c6937c25e3fc9ee797aa6783bef8c1", size = 2628236, upload-time = "2025-08-27T13:40:10.27Z" }, + { url = "https://files.pythonhosted.org/packages/5d/10/e7e99ca1a6546645aa21d1b426f728edbfb7a3abcb1a7b7642353b79ae57/rapidfuzz-3.14.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ba351cf2678d40a23fb4cbfe82cc45ea338a57518dca62a823c5b6381aa20c68", size = 2893483, upload-time = "2025-08-27T13:40:12.079Z" }, + { url = "https://files.pythonhosted.org/packages/00/11/fb46a86659e2bb304764478a28810f36bb56f794087f34a5bd1b81dd0be5/rapidfuzz-3.14.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:558323dcd5fb38737226be84c78cafbe427706e47379f02c57c3e35ac3745061", size = 3411761, upload-time = "2025-08-27T13:40:14.051Z" }, + { url = "https://files.pythonhosted.org/packages/fc/76/89eabf1e7523f6dc996ea6b2bfcfd22565cdfa830c7c3af0ebc5b17e9ce7/rapidfuzz-3.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cb4e4ea174add5183c707d890a816a85e9330f93e5ded139dab182adc727930c", size = 4404126, upload-time = "2025-08-27T13:40:16.39Z" }, + { url = "https://files.pythonhosted.org/packages/c8/6c/ddc7ee86d392908efdf95a1242b87b94523f6feaa368b7a24efa39ecd9d9/rapidfuzz-3.14.0-cp313-cp313-win32.whl", hash = "sha256:ec379e1b407935d729c08da9641cfc5dfb2a7796f74cdd82158ce5986bb8ff88", size = 1828545, upload-time = "2025-08-27T13:40:19.069Z" }, + { url = "https://files.pythonhosted.org/packages/95/47/2a271455b602eef360cd5cc716d370d7ab47b9d57f00263821a217fd30f4/rapidfuzz-3.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:4b59ba48a909bdf7ec5dad6e3a5a0004aeec141ae5ddb205d0c5bd4389894cf9", size = 1658600, upload-time = "2025-08-27T13:40:21.278Z" }, + { url = "https://files.pythonhosted.org/packages/86/47/5acb5d160a091c3175c6f5e3f227ccdf03b201b05ceaad2b8b7f5009ebe9/rapidfuzz-3.14.0-cp313-cp313-win_arm64.whl", hash = "sha256:e688b0a98edea42da450fa6ba41736203ead652a78b558839916c10df855f545", size = 885686, upload-time = "2025-08-27T13:40:23.254Z" }, + { url = "https://files.pythonhosted.org/packages/dc/f2/203c44a06dfefbb580ad7b743333880d600d7bdff693af9d290bd2b09742/rapidfuzz-3.14.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:cb6c5a46444a2787e466acd77e162049f061304025ab24da02b59caedea66064", size = 2041214, upload-time = "2025-08-27T13:40:25.051Z" }, + { url = "https://files.pythonhosted.org/packages/ec/db/6571a5bbba38255ede8098b3b45c007242788e5a5c3cdbe7f6f03dd6daed/rapidfuzz-3.14.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:99ed7a9e9ff798157caf3c3d96ca7da6560878902d8f70fa7731acc94e0d293c", size = 1501621, upload-time = "2025-08-27T13:40:26.881Z" }, + { url = "https://files.pythonhosted.org/packages/0b/85/efbae42fe8ca2bdb967751da1df2e3ebb5be9ea68f22f980731e5c18ce25/rapidfuzz-3.14.0-cp313-cp313t-win32.whl", hash = "sha256:c8e954dd59291ff0cd51b9c0f425e5dc84731bb006dbd5b7846746fe873a0452", size = 1887956, upload-time = "2025-08-27T13:40:29.143Z" }, + { url = "https://files.pythonhosted.org/packages/c8/60/2bb44b5ecb7151093ed7e2020156f260bdd9a221837f57a0bc5938b2b6d1/rapidfuzz-3.14.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5754e3ca259667c46a2b58ca7d7568251d6e23d2f0e354ac1cc5564557f4a32d", size = 1702542, upload-time = "2025-08-27T13:40:31.103Z" }, + { url = "https://files.pythonhosted.org/packages/6f/b7/688e9ab091545ff8eed564994a01309d8a52718211f27af94743d55b3c80/rapidfuzz-3.14.0-cp313-cp313t-win_arm64.whl", hash = "sha256:558865f6825d27006e6ae2e1635cfe236d736c8f2c5c82db6db4b1b6df4478bc", size = 912891, upload-time = "2025-08-27T13:40:33.263Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ed/5b83587b6a6bfe7845ed36286fd5780c00ba93c56463bd501b44617f427b/rapidfuzz-3.14.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5d610a2c5efdb2a3f9eaecac4ecd6d849efb2522efa36000e006179062056dc", size = 1888611, upload-time = "2025-08-27T13:41:24.326Z" }, + { url = "https://files.pythonhosted.org/packages/e6/d9/9332a39587a2478470a54218d5f85b5a29b6b3eb02b2310689b59ad3da11/rapidfuzz-3.14.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:c053cad08ab872df4e201daacb66d7fd04b5b4c395baebb193b9910c63ed22ec", size = 1363908, upload-time = "2025-08-27T13:41:26.463Z" }, + { url = "https://files.pythonhosted.org/packages/21/7f/c90f55402b5b43fd5cff42a8dab60373345b8f2697a7b83515eb62666913/rapidfuzz-3.14.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7e52ac8a458b2f09291fa968b23192d6664c7568a43607de2a51a088d016152d", size = 1555592, upload-time = "2025-08-27T13:41:28.583Z" }, ] [[package]] @@ -2813,81 +2815,81 @@ wheels = [ [[package]] name = "rpds-py" -version = "0.27.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/d9/991a0dee12d9fc53ed027e26a26a64b151d77252ac477e22666b9688bc16/rpds_py-0.27.0.tar.gz", hash = "sha256:8b23cf252f180cda89220b378d917180f29d313cd6a07b2431c0d3b776aae86f", size = 27420, upload-time = "2025-08-07T08:26:39.624Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/c1/49d515434c1752e40f5e35b985260cf27af052593378580a2f139a5be6b8/rpds_py-0.27.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:dbc2ab5d10544eb485baa76c63c501303b716a5c405ff2469a1d8ceffaabf622", size = 371577, upload-time = "2025-08-07T08:23:25.379Z" }, - { url = "https://files.pythonhosted.org/packages/e1/6d/bf2715b2fee5087fa13b752b5fd573f1a93e4134c74d275f709e38e54fe7/rpds_py-0.27.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7ec85994f96a58cf7ed288caa344b7fe31fd1d503bdf13d7331ead5f70ab60d5", size = 354959, upload-time = "2025-08-07T08:23:26.767Z" }, - { url = "https://files.pythonhosted.org/packages/a3/5c/e7762808c746dd19733a81373c10da43926f6a6adcf4920a21119697a60a/rpds_py-0.27.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:190d7285cd3bb6d31d37a0534d7359c1ee191eb194c511c301f32a4afa5a1dd4", size = 381485, upload-time = "2025-08-07T08:23:27.869Z" }, - { url = "https://files.pythonhosted.org/packages/40/51/0d308eb0b558309ca0598bcba4243f52c4cd20e15fe991b5bd75824f2e61/rpds_py-0.27.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c10d92fb6d7fd827e44055fcd932ad93dac6a11e832d51534d77b97d1d85400f", size = 396816, upload-time = "2025-08-07T08:23:29.424Z" }, - { url = "https://files.pythonhosted.org/packages/5c/aa/2d585ec911d78f66458b2c91252134ca0c7c70f687a72c87283173dc0c96/rpds_py-0.27.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd2c1d27ebfe6a015cfa2005b7fe8c52d5019f7bbdd801bc6f7499aab9ae739e", size = 514950, upload-time = "2025-08-07T08:23:30.576Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ef/aced551cc1148179557aed84343073adadf252c91265263ee6203458a186/rpds_py-0.27.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4790c9d5dd565ddb3e9f656092f57268951398cef52e364c405ed3112dc7c7c1", size = 402132, upload-time = "2025-08-07T08:23:32.428Z" }, - { url = "https://files.pythonhosted.org/packages/4b/ac/cf644803d8d417653fe2b3604186861d62ea6afaef1b2284045741baef17/rpds_py-0.27.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4300e15e7d03660f04be84a125d1bdd0e6b2f674bc0723bc0fd0122f1a4585dc", size = 383660, upload-time = "2025-08-07T08:23:33.829Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ec/caf47c55ce02b76cbaeeb2d3b36a73da9ca2e14324e3d75cf72b59dcdac5/rpds_py-0.27.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:59195dc244fc183209cf8a93406889cadde47dfd2f0a6b137783aa9c56d67c85", size = 401730, upload-time = "2025-08-07T08:23:34.97Z" }, - { url = "https://files.pythonhosted.org/packages/0b/71/c1f355afdcd5b99ffc253422aa4bdcb04ccf1491dcd1bda3688a0c07fd61/rpds_py-0.27.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fae4a01ef8c4cb2bbe92ef2063149596907dc4a881a8d26743b3f6b304713171", size = 416122, upload-time = "2025-08-07T08:23:36.062Z" }, - { url = "https://files.pythonhosted.org/packages/38/0f/f4b5b1eda724ed0e04d2b26d8911cdc131451a7ee4c4c020a1387e5c6ded/rpds_py-0.27.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e3dc8d4ede2dbae6c0fc2b6c958bf51ce9fd7e9b40c0f5b8835c3fde44f5807d", size = 558771, upload-time = "2025-08-07T08:23:37.478Z" }, - { url = "https://files.pythonhosted.org/packages/93/c0/5f8b834db2289ab48d5cffbecbb75e35410103a77ac0b8da36bf9544ec1c/rpds_py-0.27.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c3782fb753aa825b4ccabc04292e07897e2fd941448eabf666856c5530277626", size = 587876, upload-time = "2025-08-07T08:23:38.662Z" }, - { url = "https://files.pythonhosted.org/packages/d2/dd/1a1df02ab8eb970115cff2ae31a6f73916609b900dc86961dc382b8c2e5e/rpds_py-0.27.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:887ab1f12b0d227e9260558a4a2320024b20102207ada65c43e1ffc4546df72e", size = 554359, upload-time = "2025-08-07T08:23:39.897Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e4/95a014ab0d51ab6e3bebbdb476a42d992d2bbf9c489d24cff9fda998e925/rpds_py-0.27.0-cp311-cp311-win32.whl", hash = "sha256:5d6790ff400254137b81b8053b34417e2c46921e302d655181d55ea46df58cf7", size = 218084, upload-time = "2025-08-07T08:23:41.086Z" }, - { url = "https://files.pythonhosted.org/packages/49/78/f8d5b71ec65a0376b0de31efcbb5528ce17a9b7fdd19c3763303ccfdedec/rpds_py-0.27.0-cp311-cp311-win_amd64.whl", hash = "sha256:e24d8031a2c62f34853756d9208eeafa6b940a1efcbfe36e8f57d99d52bb7261", size = 230085, upload-time = "2025-08-07T08:23:42.143Z" }, - { url = "https://files.pythonhosted.org/packages/e7/d3/84429745184091e06b4cc70f8597408e314c2d2f7f5e13249af9ffab9e3d/rpds_py-0.27.0-cp311-cp311-win_arm64.whl", hash = "sha256:08680820d23df1df0a0260f714d12966bc6c42d02e8055a91d61e03f0c47dda0", size = 222112, upload-time = "2025-08-07T08:23:43.233Z" }, - { url = "https://files.pythonhosted.org/packages/cd/17/e67309ca1ac993fa1888a0d9b2f5ccc1f67196ace32e76c9f8e1dbbbd50c/rpds_py-0.27.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:19c990fdf5acecbf0623e906ae2e09ce1c58947197f9bced6bbd7482662231c4", size = 362611, upload-time = "2025-08-07T08:23:44.773Z" }, - { url = "https://files.pythonhosted.org/packages/93/2e/28c2fb84aa7aa5d75933d1862d0f7de6198ea22dfd9a0cca06e8a4e7509e/rpds_py-0.27.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6c27a7054b5224710fcfb1a626ec3ff4f28bcb89b899148c72873b18210e446b", size = 347680, upload-time = "2025-08-07T08:23:46.014Z" }, - { url = "https://files.pythonhosted.org/packages/44/3e/9834b4c8f4f5fe936b479e623832468aa4bd6beb8d014fecaee9eac6cdb1/rpds_py-0.27.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09965b314091829b378b60607022048953e25f0b396c2b70e7c4c81bcecf932e", size = 384600, upload-time = "2025-08-07T08:23:48Z" }, - { url = "https://files.pythonhosted.org/packages/19/78/744123c7b38865a965cd9e6f691fde7ef989a00a256fa8bf15b75240d12f/rpds_py-0.27.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:14f028eb47f59e9169bfdf9f7ceafd29dd64902141840633683d0bad5b04ff34", size = 400697, upload-time = "2025-08-07T08:23:49.407Z" }, - { url = "https://files.pythonhosted.org/packages/32/97/3c3d32fe7daee0a1f1a678b6d4dfb8c4dcf88197fa2441f9da7cb54a8466/rpds_py-0.27.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6168af0be75bba990a39f9431cdfae5f0ad501f4af32ae62e8856307200517b8", size = 517781, upload-time = "2025-08-07T08:23:50.557Z" }, - { url = "https://files.pythonhosted.org/packages/b2/be/28f0e3e733680aa13ecec1212fc0f585928a206292f14f89c0b8a684cad1/rpds_py-0.27.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab47fe727c13c09d0e6f508e3a49e545008e23bf762a245b020391b621f5b726", size = 406449, upload-time = "2025-08-07T08:23:51.732Z" }, - { url = "https://files.pythonhosted.org/packages/95/ae/5d15c83e337c082d0367053baeb40bfba683f42459f6ebff63a2fd7e5518/rpds_py-0.27.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fa01b3d5e3b7d97efab65bd3d88f164e289ec323a8c033c5c38e53ee25c007e", size = 386150, upload-time = "2025-08-07T08:23:52.822Z" }, - { url = "https://files.pythonhosted.org/packages/bf/65/944e95f95d5931112829e040912b25a77b2e7ed913ea5fe5746aa5c1ce75/rpds_py-0.27.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:6c135708e987f46053e0a1246a206f53717f9fadfba27174a9769ad4befba5c3", size = 406100, upload-time = "2025-08-07T08:23:54.339Z" }, - { url = "https://files.pythonhosted.org/packages/21/a4/1664b83fae02894533cd11dc0b9f91d673797c2185b7be0f7496107ed6c5/rpds_py-0.27.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc327f4497b7087d06204235199daf208fd01c82d80465dc5efa4ec9df1c5b4e", size = 421345, upload-time = "2025-08-07T08:23:55.832Z" }, - { url = "https://files.pythonhosted.org/packages/7c/26/b7303941c2b0823bfb34c71378249f8beedce57301f400acb04bb345d025/rpds_py-0.27.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e57906e38583a2cba67046a09c2637e23297618dc1f3caddbc493f2be97c93f", size = 561891, upload-time = "2025-08-07T08:23:56.951Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c8/48623d64d4a5a028fa99576c768a6159db49ab907230edddc0b8468b998b/rpds_py-0.27.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f4f69d7a4300fbf91efb1fb4916421bd57804c01ab938ab50ac9c4aa2212f03", size = 591756, upload-time = "2025-08-07T08:23:58.146Z" }, - { url = "https://files.pythonhosted.org/packages/b3/51/18f62617e8e61cc66334c9fb44b1ad7baae3438662098efbc55fb3fda453/rpds_py-0.27.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b4c4fbbcff474e1e5f38be1bf04511c03d492d42eec0babda5d03af3b5589374", size = 557088, upload-time = "2025-08-07T08:23:59.6Z" }, - { url = "https://files.pythonhosted.org/packages/bd/4c/e84c3a276e2496a93d245516be6b49e20499aa8ca1c94d59fada0d79addc/rpds_py-0.27.0-cp312-cp312-win32.whl", hash = "sha256:27bac29bbbf39601b2aab474daf99dbc8e7176ca3389237a23944b17f8913d97", size = 221926, upload-time = "2025-08-07T08:24:00.695Z" }, - { url = "https://files.pythonhosted.org/packages/83/89/9d0fbcef64340db0605eb0a0044f258076f3ae0a3b108983b2c614d96212/rpds_py-0.27.0-cp312-cp312-win_amd64.whl", hash = "sha256:8a06aa1197ec0281eb1d7daf6073e199eb832fe591ffa329b88bae28f25f5fe5", size = 233235, upload-time = "2025-08-07T08:24:01.846Z" }, - { url = "https://files.pythonhosted.org/packages/c9/b0/e177aa9f39cbab060f96de4a09df77d494f0279604dc2f509263e21b05f9/rpds_py-0.27.0-cp312-cp312-win_arm64.whl", hash = "sha256:e14aab02258cb776a108107bd15f5b5e4a1bbaa61ef33b36693dfab6f89d54f9", size = 223315, upload-time = "2025-08-07T08:24:03.337Z" }, - { url = "https://files.pythonhosted.org/packages/81/d2/dfdfd42565a923b9e5a29f93501664f5b984a802967d48d49200ad71be36/rpds_py-0.27.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:443d239d02d9ae55b74015234f2cd8eb09e59fbba30bf60baeb3123ad4c6d5ff", size = 362133, upload-time = "2025-08-07T08:24:04.508Z" }, - { url = "https://files.pythonhosted.org/packages/ac/4a/0a2e2460c4b66021d349ce9f6331df1d6c75d7eea90df9785d333a49df04/rpds_py-0.27.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b8a7acf04fda1f30f1007f3cc96d29d8cf0a53e626e4e1655fdf4eabc082d367", size = 347128, upload-time = "2025-08-07T08:24:05.695Z" }, - { url = "https://files.pythonhosted.org/packages/35/8d/7d1e4390dfe09d4213b3175a3f5a817514355cb3524593380733204f20b9/rpds_py-0.27.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d0f92b78cfc3b74a42239fdd8c1266f4715b573204c234d2f9fc3fc7a24f185", size = 384027, upload-time = "2025-08-07T08:24:06.841Z" }, - { url = "https://files.pythonhosted.org/packages/c1/65/78499d1a62172891c8cd45de737b2a4b84a414b6ad8315ab3ac4945a5b61/rpds_py-0.27.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ce4ed8e0c7dbc5b19352b9c2c6131dd23b95fa8698b5cdd076307a33626b72dc", size = 399973, upload-time = "2025-08-07T08:24:08.143Z" }, - { url = "https://files.pythonhosted.org/packages/10/a1/1c67c1d8cc889107b19570bb01f75cf49852068e95e6aee80d22915406fc/rpds_py-0.27.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fde355b02934cc6b07200cc3b27ab0c15870a757d1a72fd401aa92e2ea3c6bfe", size = 515295, upload-time = "2025-08-07T08:24:09.711Z" }, - { url = "https://files.pythonhosted.org/packages/df/27/700ec88e748436b6c7c4a2262d66e80f8c21ab585d5e98c45e02f13f21c0/rpds_py-0.27.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13bbc4846ae4c993f07c93feb21a24d8ec637573d567a924b1001e81c8ae80f9", size = 406737, upload-time = "2025-08-07T08:24:11.182Z" }, - { url = "https://files.pythonhosted.org/packages/33/cc/6b0ee8f0ba3f2df2daac1beda17fde5cf10897a7d466f252bd184ef20162/rpds_py-0.27.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0744661afbc4099fef7f4e604e7f1ea1be1dd7284f357924af12a705cc7d5c", size = 385898, upload-time = "2025-08-07T08:24:12.798Z" }, - { url = "https://files.pythonhosted.org/packages/e8/7e/c927b37d7d33c0a0ebf249cc268dc2fcec52864c1b6309ecb960497f2285/rpds_py-0.27.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:069e0384a54f427bd65d7fda83b68a90606a3835901aaff42185fcd94f5a9295", size = 405785, upload-time = "2025-08-07T08:24:14.906Z" }, - { url = "https://files.pythonhosted.org/packages/5b/d2/8ed50746d909dcf402af3fa58b83d5a590ed43e07251d6b08fad1a535ba6/rpds_py-0.27.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4bc262ace5a1a7dc3e2eac2fa97b8257ae795389f688b5adf22c5db1e2431c43", size = 419760, upload-time = "2025-08-07T08:24:16.129Z" }, - { url = "https://files.pythonhosted.org/packages/d3/60/2b2071aee781cb3bd49f94d5d35686990b925e9b9f3e3d149235a6f5d5c1/rpds_py-0.27.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2fe6e18e5c8581f0361b35ae575043c7029d0a92cb3429e6e596c2cdde251432", size = 561201, upload-time = "2025-08-07T08:24:17.645Z" }, - { url = "https://files.pythonhosted.org/packages/98/1f/27b67304272521aaea02be293fecedce13fa351a4e41cdb9290576fc6d81/rpds_py-0.27.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d93ebdb82363d2e7bec64eecdc3632b59e84bd270d74fe5be1659f7787052f9b", size = 591021, upload-time = "2025-08-07T08:24:18.999Z" }, - { url = "https://files.pythonhosted.org/packages/db/9b/a2fadf823164dd085b1f894be6443b0762a54a7af6f36e98e8fcda69ee50/rpds_py-0.27.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0954e3a92e1d62e83a54ea7b3fdc9efa5d61acef8488a8a3d31fdafbfb00460d", size = 556368, upload-time = "2025-08-07T08:24:20.54Z" }, - { url = "https://files.pythonhosted.org/packages/24/f3/6d135d46a129cda2e3e6d4c5e91e2cc26ea0428c6cf152763f3f10b6dd05/rpds_py-0.27.0-cp313-cp313-win32.whl", hash = "sha256:2cff9bdd6c7b906cc562a505c04a57d92e82d37200027e8d362518df427f96cd", size = 221236, upload-time = "2025-08-07T08:24:22.144Z" }, - { url = "https://files.pythonhosted.org/packages/c5/44/65d7494f5448ecc755b545d78b188440f81da98b50ea0447ab5ebfdf9bd6/rpds_py-0.27.0-cp313-cp313-win_amd64.whl", hash = "sha256:dc79d192fb76fc0c84f2c58672c17bbbc383fd26c3cdc29daae16ce3d927e8b2", size = 232634, upload-time = "2025-08-07T08:24:23.642Z" }, - { url = "https://files.pythonhosted.org/packages/70/d9/23852410fadab2abb611733933401de42a1964ce6600a3badae35fbd573e/rpds_py-0.27.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b3a5c8089eed498a3af23ce87a80805ff98f6ef8f7bdb70bd1b7dae5105f6ac", size = 222783, upload-time = "2025-08-07T08:24:25.098Z" }, - { url = "https://files.pythonhosted.org/packages/15/75/03447917f78512b34463f4ef11066516067099a0c466545655503bed0c77/rpds_py-0.27.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:90fb790138c1a89a2e58c9282fe1089638401f2f3b8dddd758499041bc6e0774", size = 359154, upload-time = "2025-08-07T08:24:26.249Z" }, - { url = "https://files.pythonhosted.org/packages/6b/fc/4dac4fa756451f2122ddaf136e2c6aeb758dc6fdbe9ccc4bc95c98451d50/rpds_py-0.27.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:010c4843a3b92b54373e3d2291a7447d6c3fc29f591772cc2ea0e9f5c1da434b", size = 343909, upload-time = "2025-08-07T08:24:27.405Z" }, - { url = "https://files.pythonhosted.org/packages/7b/81/723c1ed8e6f57ed9d8c0c07578747a2d3d554aaefc1ab89f4e42cfeefa07/rpds_py-0.27.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9ce7a9e967afc0a2af7caa0d15a3e9c1054815f73d6a8cb9225b61921b419bd", size = 379340, upload-time = "2025-08-07T08:24:28.714Z" }, - { url = "https://files.pythonhosted.org/packages/98/16/7e3740413de71818ce1997df82ba5f94bae9fff90c0a578c0e24658e6201/rpds_py-0.27.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aa0bf113d15e8abdfee92aa4db86761b709a09954083afcb5bf0f952d6065fdb", size = 391655, upload-time = "2025-08-07T08:24:30.223Z" }, - { url = "https://files.pythonhosted.org/packages/e0/63/2a9f510e124d80660f60ecce07953f3f2d5f0b96192c1365443859b9c87f/rpds_py-0.27.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb91d252b35004a84670dfeafadb042528b19842a0080d8b53e5ec1128e8f433", size = 513017, upload-time = "2025-08-07T08:24:31.446Z" }, - { url = "https://files.pythonhosted.org/packages/2c/4e/cf6ff311d09776c53ea1b4f2e6700b9d43bb4e99551006817ade4bbd6f78/rpds_py-0.27.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db8a6313dbac934193fc17fe7610f70cd8181c542a91382531bef5ed785e5615", size = 402058, upload-time = "2025-08-07T08:24:32.613Z" }, - { url = "https://files.pythonhosted.org/packages/88/11/5e36096d474cb10f2a2d68b22af60a3bc4164fd8db15078769a568d9d3ac/rpds_py-0.27.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce96ab0bdfcef1b8c371ada2100767ace6804ea35aacce0aef3aeb4f3f499ca8", size = 383474, upload-time = "2025-08-07T08:24:33.767Z" }, - { url = "https://files.pythonhosted.org/packages/db/a2/3dff02805b06058760b5eaa6d8cb8db3eb3e46c9e452453ad5fc5b5ad9fe/rpds_py-0.27.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:7451ede3560086abe1aa27dcdcf55cd15c96b56f543fb12e5826eee6f721f858", size = 400067, upload-time = "2025-08-07T08:24:35.021Z" }, - { url = "https://files.pythonhosted.org/packages/67/87/eed7369b0b265518e21ea836456a4ed4a6744c8c12422ce05bce760bb3cf/rpds_py-0.27.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:32196b5a99821476537b3f7732432d64d93a58d680a52c5e12a190ee0135d8b5", size = 412085, upload-time = "2025-08-07T08:24:36.267Z" }, - { url = "https://files.pythonhosted.org/packages/8b/48/f50b2ab2fbb422fbb389fe296e70b7a6b5ea31b263ada5c61377e710a924/rpds_py-0.27.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a029be818059870664157194e46ce0e995082ac49926f1423c1f058534d2aaa9", size = 555928, upload-time = "2025-08-07T08:24:37.573Z" }, - { url = "https://files.pythonhosted.org/packages/98/41/b18eb51045d06887666c3560cd4bbb6819127b43d758f5adb82b5f56f7d1/rpds_py-0.27.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3841f66c1ffdc6cebce8aed64e36db71466f1dc23c0d9a5592e2a782a3042c79", size = 585527, upload-time = "2025-08-07T08:24:39.391Z" }, - { url = "https://files.pythonhosted.org/packages/be/03/a3dd6470fc76499959b00ae56295b76b4bdf7c6ffc60d62006b1217567e1/rpds_py-0.27.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:42894616da0fc0dcb2ec08a77896c3f56e9cb2f4b66acd76fc8992c3557ceb1c", size = 554211, upload-time = "2025-08-07T08:24:40.6Z" }, - { url = "https://files.pythonhosted.org/packages/bf/d1/ee5fd1be395a07423ac4ca0bcc05280bf95db2b155d03adefeb47d5ebf7e/rpds_py-0.27.0-cp313-cp313t-win32.whl", hash = "sha256:b1fef1f13c842a39a03409e30ca0bf87b39a1e2a305a9924deadb75a43105d23", size = 216624, upload-time = "2025-08-07T08:24:42.204Z" }, - { url = "https://files.pythonhosted.org/packages/1c/94/4814c4c858833bf46706f87349c37ca45e154da7dbbec9ff09f1abeb08cc/rpds_py-0.27.0-cp313-cp313t-win_amd64.whl", hash = "sha256:183f5e221ba3e283cd36fdfbe311d95cd87699a083330b4f792543987167eff1", size = 230007, upload-time = "2025-08-07T08:24:43.329Z" }, - { url = "https://files.pythonhosted.org/packages/59/64/72ab5b911fdcc48058359b0e786e5363e3fde885156116026f1a2ba9a5b5/rpds_py-0.27.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e6491658dd2569f05860bad645569145c8626ac231877b0fb2d5f9bcb7054089", size = 371658, upload-time = "2025-08-07T08:26:02.369Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4b/90ff04b4da055db53d8fea57640d8d5d55456343a1ec9a866c0ecfe10fd1/rpds_py-0.27.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:bec77545d188f8bdd29d42bccb9191682a46fb2e655e3d1fb446d47c55ac3b8d", size = 355529, upload-time = "2025-08-07T08:26:03.83Z" }, - { url = "https://files.pythonhosted.org/packages/a4/be/527491fb1afcd86fc5ce5812eb37bc70428ee017d77fee20de18155c3937/rpds_py-0.27.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a4aebf8ca02bbb90a9b3e7a463bbf3bee02ab1c446840ca07b1695a68ce424", size = 382822, upload-time = "2025-08-07T08:26:05.52Z" }, - { url = "https://files.pythonhosted.org/packages/e0/a5/dcdb8725ce11e6d0913e6fcf782a13f4b8a517e8acc70946031830b98441/rpds_py-0.27.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:44524b96481a4c9b8e6c46d6afe43fa1fb485c261e359fbe32b63ff60e3884d8", size = 397233, upload-time = "2025-08-07T08:26:07.179Z" }, - { url = "https://files.pythonhosted.org/packages/33/f9/0947920d1927e9f144660590cc38cadb0795d78fe0d9aae0ef71c1513b7c/rpds_py-0.27.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45d04a73c54b6a5fd2bab91a4b5bc8b426949586e61340e212a8484919183859", size = 514892, upload-time = "2025-08-07T08:26:08.622Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ed/d1343398c1417c68f8daa1afce56ef6ce5cc587daaf98e29347b00a80ff2/rpds_py-0.27.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:343cf24de9ed6c728abefc5d5c851d5de06497caa7ac37e5e65dd572921ed1b5", size = 402733, upload-time = "2025-08-07T08:26:10.433Z" }, - { url = "https://files.pythonhosted.org/packages/1d/0b/646f55442cd14014fb64d143428f25667a100f82092c90087b9ea7101c74/rpds_py-0.27.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7aed8118ae20515974650d08eb724150dc2e20c2814bcc307089569995e88a14", size = 384447, upload-time = "2025-08-07T08:26:11.847Z" }, - { url = "https://files.pythonhosted.org/packages/4b/15/0596ef7529828e33a6c81ecf5013d1dd33a511a3e0be0561f83079cda227/rpds_py-0.27.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:af9d4fd79ee1cc8e7caf693ee02737daabfc0fcf2773ca0a4735b356c8ad6f7c", size = 402502, upload-time = "2025-08-07T08:26:13.537Z" }, - { url = "https://files.pythonhosted.org/packages/c3/8d/986af3c42f8454a6cafff8729d99fb178ae9b08a9816325ac7a8fa57c0c0/rpds_py-0.27.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f0396e894bd1e66c74ecbc08b4f6a03dc331140942c4b1d345dd131b68574a60", size = 416651, upload-time = "2025-08-07T08:26:14.923Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9a/b4ec3629b7b447e896eec574469159b5b60b7781d3711c914748bf32de05/rpds_py-0.27.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:59714ab0a5af25d723d8e9816638faf7f4254234decb7d212715c1aa71eee7be", size = 559460, upload-time = "2025-08-07T08:26:16.295Z" }, - { url = "https://files.pythonhosted.org/packages/61/63/d1e127b40c3e4733b3a6f26ae7a063cdf2bc1caa5272c89075425c7d397a/rpds_py-0.27.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:88051c3b7d5325409f433c5a40328fcb0685fc04e5db49ff936e910901d10114", size = 588072, upload-time = "2025-08-07T08:26:17.776Z" }, - { url = "https://files.pythonhosted.org/packages/04/7e/8ffc71a8f6833d9c9fb999f5b0ee736b8b159fd66968e05c7afc2dbcd57e/rpds_py-0.27.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:181bc29e59e5e5e6e9d63b143ff4d5191224d355e246b5a48c88ce6b35c4e466", size = 555083, upload-time = "2025-08-07T08:26:19.301Z" }, +version = "0.27.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479, upload-time = "2025-08-27T12:16:36.024Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/c1/7907329fbef97cbd49db6f7303893bd1dd5a4a3eae415839ffdfb0762cae/rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881", size = 371063, upload-time = "2025-08-27T12:12:47.856Z" }, + { url = "https://files.pythonhosted.org/packages/11/94/2aab4bc86228bcf7c48760990273653a4900de89c7537ffe1b0d6097ed39/rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5", size = 353210, upload-time = "2025-08-27T12:12:49.187Z" }, + { url = "https://files.pythonhosted.org/packages/3a/57/f5eb3ecf434342f4f1a46009530e93fd201a0b5b83379034ebdb1d7c1a58/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e", size = 381636, upload-time = "2025-08-27T12:12:50.492Z" }, + { url = "https://files.pythonhosted.org/packages/ae/f4/ef95c5945e2ceb5119571b184dd5a1cc4b8541bbdf67461998cfeac9cb1e/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c", size = 394341, upload-time = "2025-08-27T12:12:52.024Z" }, + { url = "https://files.pythonhosted.org/packages/5a/7e/4bd610754bf492d398b61725eb9598ddd5eb86b07d7d9483dbcd810e20bc/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195", size = 523428, upload-time = "2025-08-27T12:12:53.779Z" }, + { url = "https://files.pythonhosted.org/packages/9f/e5/059b9f65a8c9149361a8b75094864ab83b94718344db511fd6117936ed2a/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52", size = 402923, upload-time = "2025-08-27T12:12:55.15Z" }, + { url = "https://files.pythonhosted.org/packages/f5/48/64cabb7daced2968dd08e8a1b7988bf358d7bd5bcd5dc89a652f4668543c/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed", size = 384094, upload-time = "2025-08-27T12:12:57.194Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e1/dc9094d6ff566bff87add8a510c89b9e158ad2ecd97ee26e677da29a9e1b/rpds_py-0.27.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a", size = 401093, upload-time = "2025-08-27T12:12:58.985Z" }, + { url = "https://files.pythonhosted.org/packages/37/8e/ac8577e3ecdd5593e283d46907d7011618994e1d7ab992711ae0f78b9937/rpds_py-0.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde", size = 417969, upload-time = "2025-08-27T12:13:00.367Z" }, + { url = "https://files.pythonhosted.org/packages/66/6d/87507430a8f74a93556fe55c6485ba9c259949a853ce407b1e23fea5ba31/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21", size = 558302, upload-time = "2025-08-27T12:13:01.737Z" }, + { url = "https://files.pythonhosted.org/packages/3a/bb/1db4781ce1dda3eecc735e3152659a27b90a02ca62bfeea17aee45cc0fbc/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9", size = 589259, upload-time = "2025-08-27T12:13:03.127Z" }, + { url = "https://files.pythonhosted.org/packages/7b/0e/ae1c8943d11a814d01b482e1f8da903f88047a962dff9bbdadf3bd6e6fd1/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948", size = 554983, upload-time = "2025-08-27T12:13:04.516Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d5/0b2a55415931db4f112bdab072443ff76131b5ac4f4dc98d10d2d357eb03/rpds_py-0.27.1-cp311-cp311-win32.whl", hash = "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39", size = 217154, upload-time = "2025-08-27T12:13:06.278Z" }, + { url = "https://files.pythonhosted.org/packages/24/75/3b7ffe0d50dc86a6a964af0d1cc3a4a2cdf437cb7b099a4747bbb96d1819/rpds_py-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15", size = 228627, upload-time = "2025-08-27T12:13:07.625Z" }, + { url = "https://files.pythonhosted.org/packages/8d/3f/4fd04c32abc02c710f09a72a30c9a55ea3cc154ef8099078fd50a0596f8e/rpds_py-0.27.1-cp311-cp311-win_arm64.whl", hash = "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746", size = 220998, upload-time = "2025-08-27T12:13:08.972Z" }, + { url = "https://files.pythonhosted.org/packages/bd/fe/38de28dee5df58b8198c743fe2bea0c785c6d40941b9950bac4cdb71a014/rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90", size = 361887, upload-time = "2025-08-27T12:13:10.233Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/4b6c7eedc7dd90986bf0fab6ea2a091ec11c01b15f8ba0a14d3f80450468/rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5", size = 345795, upload-time = "2025-08-27T12:13:11.65Z" }, + { url = "https://files.pythonhosted.org/packages/6f/0e/e650e1b81922847a09cca820237b0edee69416a01268b7754d506ade11ad/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e", size = 385121, upload-time = "2025-08-27T12:13:13.008Z" }, + { url = "https://files.pythonhosted.org/packages/1b/ea/b306067a712988e2bff00dcc7c8f31d26c29b6d5931b461aa4b60a013e33/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881", size = 398976, upload-time = "2025-08-27T12:13:14.368Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0a/26dc43c8840cb8fe239fe12dbc8d8de40f2365e838f3d395835dde72f0e5/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec", size = 525953, upload-time = "2025-08-27T12:13:15.774Z" }, + { url = "https://files.pythonhosted.org/packages/22/14/c85e8127b573aaf3a0cbd7fbb8c9c99e735a4a02180c84da2a463b766e9e/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb", size = 407915, upload-time = "2025-08-27T12:13:17.379Z" }, + { url = "https://files.pythonhosted.org/packages/ed/7b/8f4fee9ba1fb5ec856eb22d725a4efa3deb47f769597c809e03578b0f9d9/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5", size = 386883, upload-time = "2025-08-27T12:13:18.704Z" }, + { url = "https://files.pythonhosted.org/packages/86/47/28fa6d60f8b74fcdceba81b272f8d9836ac0340570f68f5df6b41838547b/rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a", size = 405699, upload-time = "2025-08-27T12:13:20.089Z" }, + { url = "https://files.pythonhosted.org/packages/d0/fd/c5987b5e054548df56953a21fe2ebed51fc1ec7c8f24fd41c067b68c4a0a/rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444", size = 423713, upload-time = "2025-08-27T12:13:21.436Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ba/3c4978b54a73ed19a7d74531be37a8bcc542d917c770e14d372b8daea186/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a", size = 562324, upload-time = "2025-08-27T12:13:22.789Z" }, + { url = "https://files.pythonhosted.org/packages/b5/6c/6943a91768fec16db09a42b08644b960cff540c66aab89b74be6d4a144ba/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1", size = 593646, upload-time = "2025-08-27T12:13:24.122Z" }, + { url = "https://files.pythonhosted.org/packages/11/73/9d7a8f4be5f4396f011a6bb7a19fe26303a0dac9064462f5651ced2f572f/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998", size = 558137, upload-time = "2025-08-27T12:13:25.557Z" }, + { url = "https://files.pythonhosted.org/packages/6e/96/6772cbfa0e2485bcceef8071de7821f81aeac8bb45fbfd5542a3e8108165/rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39", size = 221343, upload-time = "2025-08-27T12:13:26.967Z" }, + { url = "https://files.pythonhosted.org/packages/67/b6/c82f0faa9af1c6a64669f73a17ee0eeef25aff30bb9a1c318509efe45d84/rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594", size = 232497, upload-time = "2025-08-27T12:13:28.326Z" }, + { url = "https://files.pythonhosted.org/packages/e1/96/2817b44bd2ed11aebacc9251da03689d56109b9aba5e311297b6902136e2/rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502", size = 222790, upload-time = "2025-08-27T12:13:29.71Z" }, + { url = "https://files.pythonhosted.org/packages/cc/77/610aeee8d41e39080c7e14afa5387138e3c9fa9756ab893d09d99e7d8e98/rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b", size = 361741, upload-time = "2025-08-27T12:13:31.039Z" }, + { url = "https://files.pythonhosted.org/packages/3a/fc/c43765f201c6a1c60be2043cbdb664013def52460a4c7adace89d6682bf4/rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf", size = 345574, upload-time = "2025-08-27T12:13:32.902Z" }, + { url = "https://files.pythonhosted.org/packages/20/42/ee2b2ca114294cd9847d0ef9c26d2b0851b2e7e00bf14cc4c0b581df0fc3/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83", size = 385051, upload-time = "2025-08-27T12:13:34.228Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e8/1e430fe311e4799e02e2d1af7c765f024e95e17d651612425b226705f910/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf", size = 398395, upload-time = "2025-08-27T12:13:36.132Z" }, + { url = "https://files.pythonhosted.org/packages/82/95/9dc227d441ff2670651c27a739acb2535ccaf8b351a88d78c088965e5996/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2", size = 524334, upload-time = "2025-08-27T12:13:37.562Z" }, + { url = "https://files.pythonhosted.org/packages/87/01/a670c232f401d9ad461d9a332aa4080cd3cb1d1df18213dbd0d2a6a7ab51/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0", size = 407691, upload-time = "2025-08-27T12:13:38.94Z" }, + { url = "https://files.pythonhosted.org/packages/03/36/0a14aebbaa26fe7fab4780c76f2239e76cc95a0090bdb25e31d95c492fcd/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418", size = 386868, upload-time = "2025-08-27T12:13:40.192Z" }, + { url = "https://files.pythonhosted.org/packages/3b/03/8c897fb8b5347ff6c1cc31239b9611c5bf79d78c984430887a353e1409a1/rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d", size = 405469, upload-time = "2025-08-27T12:13:41.496Z" }, + { url = "https://files.pythonhosted.org/packages/da/07/88c60edc2df74850d496d78a1fdcdc7b54360a7f610a4d50008309d41b94/rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274", size = 422125, upload-time = "2025-08-27T12:13:42.802Z" }, + { url = "https://files.pythonhosted.org/packages/6b/86/5f4c707603e41b05f191a749984f390dabcbc467cf833769b47bf14ba04f/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd", size = 562341, upload-time = "2025-08-27T12:13:44.472Z" }, + { url = "https://files.pythonhosted.org/packages/b2/92/3c0cb2492094e3cd9baf9e49bbb7befeceb584ea0c1a8b5939dca4da12e5/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2", size = 592511, upload-time = "2025-08-27T12:13:45.898Z" }, + { url = "https://files.pythonhosted.org/packages/10/bb/82e64fbb0047c46a168faa28d0d45a7851cd0582f850b966811d30f67ad8/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002", size = 557736, upload-time = "2025-08-27T12:13:47.408Z" }, + { url = "https://files.pythonhosted.org/packages/00/95/3c863973d409210da7fb41958172c6b7dbe7fc34e04d3cc1f10bb85e979f/rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3", size = 221462, upload-time = "2025-08-27T12:13:48.742Z" }, + { url = "https://files.pythonhosted.org/packages/ce/2c/5867b14a81dc217b56d95a9f2a40fdbc56a1ab0181b80132beeecbd4b2d6/rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83", size = 232034, upload-time = "2025-08-27T12:13:50.11Z" }, + { url = "https://files.pythonhosted.org/packages/c7/78/3958f3f018c01923823f1e47f1cc338e398814b92d83cd278364446fac66/rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d", size = 222392, upload-time = "2025-08-27T12:13:52.587Z" }, + { url = "https://files.pythonhosted.org/packages/01/76/1cdf1f91aed5c3a7bf2eba1f1c4e4d6f57832d73003919a20118870ea659/rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228", size = 358355, upload-time = "2025-08-27T12:13:54.012Z" }, + { url = "https://files.pythonhosted.org/packages/c3/6f/bf142541229374287604caf3bb2a4ae17f0a580798fd72d3b009b532db4e/rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92", size = 342138, upload-time = "2025-08-27T12:13:55.791Z" }, + { url = "https://files.pythonhosted.org/packages/1a/77/355b1c041d6be40886c44ff5e798b4e2769e497b790f0f7fd1e78d17e9a8/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2", size = 380247, upload-time = "2025-08-27T12:13:57.683Z" }, + { url = "https://files.pythonhosted.org/packages/d6/a4/d9cef5c3946ea271ce2243c51481971cd6e34f21925af2783dd17b26e815/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723", size = 390699, upload-time = "2025-08-27T12:13:59.137Z" }, + { url = "https://files.pythonhosted.org/packages/3a/06/005106a7b8c6c1a7e91b73169e49870f4af5256119d34a361ae5240a0c1d/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802", size = 521852, upload-time = "2025-08-27T12:14:00.583Z" }, + { url = "https://files.pythonhosted.org/packages/e5/3e/50fb1dac0948e17a02eb05c24510a8fe12d5ce8561c6b7b7d1339ab7ab9c/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f", size = 402582, upload-time = "2025-08-27T12:14:02.034Z" }, + { url = "https://files.pythonhosted.org/packages/cb/b0/f4e224090dc5b0ec15f31a02d746ab24101dd430847c4d99123798661bfc/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2", size = 384126, upload-time = "2025-08-27T12:14:03.437Z" }, + { url = "https://files.pythonhosted.org/packages/54/77/ac339d5f82b6afff1df8f0fe0d2145cc827992cb5f8eeb90fc9f31ef7a63/rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21", size = 399486, upload-time = "2025-08-27T12:14:05.443Z" }, + { url = "https://files.pythonhosted.org/packages/d6/29/3e1c255eee6ac358c056a57d6d6869baa00a62fa32eea5ee0632039c50a3/rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef", size = 414832, upload-time = "2025-08-27T12:14:06.902Z" }, + { url = "https://files.pythonhosted.org/packages/3f/db/6d498b844342deb3fa1d030598db93937a9964fcf5cb4da4feb5f17be34b/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081", size = 557249, upload-time = "2025-08-27T12:14:08.37Z" }, + { url = "https://files.pythonhosted.org/packages/60/f3/690dd38e2310b6f68858a331399b4d6dbb9132c3e8ef8b4333b96caf403d/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd", size = 587356, upload-time = "2025-08-27T12:14:10.034Z" }, + { url = "https://files.pythonhosted.org/packages/86/e3/84507781cccd0145f35b1dc32c72675200c5ce8d5b30f813e49424ef68fc/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7", size = 555300, upload-time = "2025-08-27T12:14:11.783Z" }, + { url = "https://files.pythonhosted.org/packages/e5/ee/375469849e6b429b3516206b4580a79e9ef3eb12920ddbd4492b56eaacbe/rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688", size = 216714, upload-time = "2025-08-27T12:14:13.629Z" }, + { url = "https://files.pythonhosted.org/packages/21/87/3fc94e47c9bd0742660e84706c311a860dcae4374cf4a03c477e23ce605a/rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797", size = 228943, upload-time = "2025-08-27T12:14:14.937Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ed/e1fba02de17f4f76318b834425257c8ea297e415e12c68b4361f63e8ae92/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df", size = 371402, upload-time = "2025-08-27T12:15:51.561Z" }, + { url = "https://files.pythonhosted.org/packages/af/7c/e16b959b316048b55585a697e94add55a4ae0d984434d279ea83442e460d/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3", size = 354084, upload-time = "2025-08-27T12:15:53.219Z" }, + { url = "https://files.pythonhosted.org/packages/de/c1/ade645f55de76799fdd08682d51ae6724cb46f318573f18be49b1e040428/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9", size = 383090, upload-time = "2025-08-27T12:15:55.158Z" }, + { url = "https://files.pythonhosted.org/packages/1f/27/89070ca9b856e52960da1472efcb6c20ba27cfe902f4f23ed095b9cfc61d/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc", size = 394519, upload-time = "2025-08-27T12:15:57.238Z" }, + { url = "https://files.pythonhosted.org/packages/b3/28/be120586874ef906aa5aeeae95ae8df4184bc757e5b6bd1c729ccff45ed5/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4", size = 523817, upload-time = "2025-08-27T12:15:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ef/70cc197bc11cfcde02a86f36ac1eed15c56667c2ebddbdb76a47e90306da/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66", size = 403240, upload-time = "2025-08-27T12:16:00.923Z" }, + { url = "https://files.pythonhosted.org/packages/cf/35/46936cca449f7f518f2f4996e0e8344db4b57e2081e752441154089d2a5f/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e", size = 385194, upload-time = "2025-08-27T12:16:02.802Z" }, + { url = "https://files.pythonhosted.org/packages/e1/62/29c0d3e5125c3270b51415af7cbff1ec587379c84f55a5761cc9efa8cd06/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c", size = 402086, upload-time = "2025-08-27T12:16:04.806Z" }, + { url = "https://files.pythonhosted.org/packages/8f/66/03e1087679227785474466fdd04157fb793b3b76e3fcf01cbf4c693c1949/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf", size = 419272, upload-time = "2025-08-27T12:16:06.471Z" }, + { url = "https://files.pythonhosted.org/packages/6a/24/e3e72d265121e00b063aef3e3501e5b2473cf1b23511d56e529531acf01e/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf", size = 560003, upload-time = "2025-08-27T12:16:08.06Z" }, + { url = "https://files.pythonhosted.org/packages/26/ca/f5a344c534214cc2d41118c0699fffbdc2c1bc7046f2a2b9609765ab9c92/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6", size = 590482, upload-time = "2025-08-27T12:16:10.137Z" }, + { url = "https://files.pythonhosted.org/packages/ce/08/4349bdd5c64d9d193c360aa9db89adeee6f6682ab8825dca0a3f535f434f/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a", size = 556523, upload-time = "2025-08-27T12:16:12.188Z" }, ] [[package]] @@ -2951,42 +2953,42 @@ wheels = [ [[package]] name = "ruff" -version = "0.12.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/eb/8c073deb376e46ae767f4961390d17545e8535921d2f65101720ed8bd434/ruff-0.12.10.tar.gz", hash = "sha256:189ab65149d11ea69a2d775343adf5f49bb2426fc4780f65ee33b423ad2e47f9", size = 5310076, upload-time = "2025-08-21T18:23:22.595Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/24/e7/560d049d15585d6c201f9eeacd2fd130def3741323e5ccf123786e0e3c95/ruff-0.12.10-py3-none-linux_armv6l.whl", hash = "sha256:8b593cb0fb55cc8692dac7b06deb29afda78c721c7ccfed22db941201b7b8f7b", size = 11935161, upload-time = "2025-08-21T18:22:26.965Z" }, - { url = "https://files.pythonhosted.org/packages/d1/b0/ad2464922a1113c365d12b8f80ed70fcfb39764288ac77c995156080488d/ruff-0.12.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ebb7333a45d56efc7c110a46a69a1b32365d5c5161e7244aaf3aa20ce62399c1", size = 12660884, upload-time = "2025-08-21T18:22:30.925Z" }, - { url = "https://files.pythonhosted.org/packages/d7/f1/97f509b4108d7bae16c48389f54f005b62ce86712120fd8b2d8e88a7cb49/ruff-0.12.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d59e58586829f8e4a9920788f6efba97a13d1fa320b047814e8afede381c6839", size = 11872754, upload-time = "2025-08-21T18:22:34.035Z" }, - { url = "https://files.pythonhosted.org/packages/12/ad/44f606d243f744a75adc432275217296095101f83f966842063d78eee2d3/ruff-0.12.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:822d9677b560f1fdeab69b89d1f444bf5459da4aa04e06e766cf0121771ab844", size = 12092276, upload-time = "2025-08-21T18:22:36.764Z" }, - { url = "https://files.pythonhosted.org/packages/06/1f/ed6c265e199568010197909b25c896d66e4ef2c5e1c3808caf461f6f3579/ruff-0.12.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37b4a64f4062a50c75019c61c7017ff598cb444984b638511f48539d3a1c98db", size = 11734700, upload-time = "2025-08-21T18:22:39.822Z" }, - { url = "https://files.pythonhosted.org/packages/63/c5/b21cde720f54a1d1db71538c0bc9b73dee4b563a7dd7d2e404914904d7f5/ruff-0.12.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6f4064c69d2542029b2a61d39920c85240c39837599d7f2e32e80d36401d6e", size = 13468783, upload-time = "2025-08-21T18:22:42.559Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/39369e6ac7f2a1848f22fb0b00b690492f20811a1ac5c1fd1d2798329263/ruff-0.12.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:059e863ea3a9ade41407ad71c1de2badfbe01539117f38f763ba42a1206f7559", size = 14436642, upload-time = "2025-08-21T18:22:45.612Z" }, - { url = "https://files.pythonhosted.org/packages/e3/03/5da8cad4b0d5242a936eb203b58318016db44f5c5d351b07e3f5e211bb89/ruff-0.12.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bef6161e297c68908b7218fa6e0e93e99a286e5ed9653d4be71e687dff101cf", size = 13859107, upload-time = "2025-08-21T18:22:48.886Z" }, - { url = "https://files.pythonhosted.org/packages/19/19/dd7273b69bf7f93a070c9cec9494a94048325ad18fdcf50114f07e6bf417/ruff-0.12.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4f1345fbf8fb0531cd722285b5f15af49b2932742fc96b633e883da8d841896b", size = 12886521, upload-time = "2025-08-21T18:22:51.567Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1d/b4207ec35e7babaee62c462769e77457e26eb853fbdc877af29417033333/ruff-0.12.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f68433c4fbc63efbfa3ba5db31727db229fa4e61000f452c540474b03de52a9", size = 13097528, upload-time = "2025-08-21T18:22:54.609Z" }, - { url = "https://files.pythonhosted.org/packages/ff/00/58f7b873b21114456e880b75176af3490d7a2836033779ca42f50de3b47a/ruff-0.12.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:141ce3d88803c625257b8a6debf4a0473eb6eed9643a6189b68838b43e78165a", size = 13080443, upload-time = "2025-08-21T18:22:57.413Z" }, - { url = "https://files.pythonhosted.org/packages/12/8c/9e6660007fb10189ccb78a02b41691288038e51e4788bf49b0a60f740604/ruff-0.12.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f3fc21178cd44c98142ae7590f42ddcb587b8e09a3b849cbc84edb62ee95de60", size = 11896759, upload-time = "2025-08-21T18:23:00.473Z" }, - { url = "https://files.pythonhosted.org/packages/67/4c/6d092bb99ea9ea6ebda817a0e7ad886f42a58b4501a7e27cd97371d0ba54/ruff-0.12.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7d1a4e0bdfafcd2e3e235ecf50bf0176f74dd37902f241588ae1f6c827a36c56", size = 11701463, upload-time = "2025-08-21T18:23:03.211Z" }, - { url = "https://files.pythonhosted.org/packages/59/80/d982c55e91df981f3ab62559371380616c57ffd0172d96850280c2b04fa8/ruff-0.12.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e67d96827854f50b9e3e8327b031647e7bcc090dbe7bb11101a81a3a2cbf1cc9", size = 12691603, upload-time = "2025-08-21T18:23:06.935Z" }, - { url = "https://files.pythonhosted.org/packages/ad/37/63a9c788bbe0b0850611669ec6b8589838faf2f4f959647f2d3e320383ae/ruff-0.12.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ae479e1a18b439c59138f066ae79cc0f3ee250712a873d00dbafadaad9481e5b", size = 13164356, upload-time = "2025-08-21T18:23:10.225Z" }, - { url = "https://files.pythonhosted.org/packages/47/d4/1aaa7fb201a74181989970ebccd12f88c0fc074777027e2a21de5a90657e/ruff-0.12.10-py3-none-win32.whl", hash = "sha256:9de785e95dc2f09846c5e6e1d3a3d32ecd0b283a979898ad427a9be7be22b266", size = 11896089, upload-time = "2025-08-21T18:23:14.232Z" }, - { url = "https://files.pythonhosted.org/packages/ad/14/2ad38fd4037daab9e023456a4a40ed0154e9971f8d6aed41bdea390aabd9/ruff-0.12.10-py3-none-win_amd64.whl", hash = "sha256:7837eca8787f076f67aba2ca559cefd9c5cbc3a9852fd66186f4201b87c1563e", size = 13004616, upload-time = "2025-08-21T18:23:17.422Z" }, - { url = "https://files.pythonhosted.org/packages/24/3c/21cf283d67af33a8e6ed242396863af195a8a6134ec581524fd22b9811b6/ruff-0.12.10-py3-none-win_arm64.whl", hash = "sha256:cc138cc06ed9d4bfa9d667a65af7172b47840e1a98b02ce7011c391e54635ffc", size = 12074225, upload-time = "2025-08-21T18:23:20.137Z" }, +version = "0.12.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/55/16ab6a7d88d93001e1ae4c34cbdcfb376652d761799459ff27c1dc20f6fa/ruff-0.12.11.tar.gz", hash = "sha256:c6b09ae8426a65bbee5425b9d0b82796dbb07cb1af045743c79bfb163001165d", size = 5347103, upload-time = "2025-08-28T13:59:08.87Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/a2/3b3573e474de39a7a475f3fbaf36a25600bfeb238e1a90392799163b64a0/ruff-0.12.11-py3-none-linux_armv6l.whl", hash = "sha256:93fce71e1cac3a8bf9200e63a38ac5c078f3b6baebffb74ba5274fb2ab276065", size = 11979885, upload-time = "2025-08-28T13:58:26.654Z" }, + { url = "https://files.pythonhosted.org/packages/76/e4/235ad6d1785a2012d3ded2350fd9bc5c5af8c6f56820e696b0118dfe7d24/ruff-0.12.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8e33ac7b28c772440afa80cebb972ffd823621ded90404f29e5ab6d1e2d4b93", size = 12742364, upload-time = "2025-08-28T13:58:30.256Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0d/15b72c5fe6b1e402a543aa9d8960e0a7e19dfb079f5b0b424db48b7febab/ruff-0.12.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d69fb9d4937aa19adb2e9f058bc4fbfe986c2040acb1a4a9747734834eaa0bfd", size = 11920111, upload-time = "2025-08-28T13:58:33.677Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c0/f66339d7893798ad3e17fa5a1e587d6fd9806f7c1c062b63f8b09dda6702/ruff-0.12.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:411954eca8464595077a93e580e2918d0a01a19317af0a72132283e28ae21bee", size = 12160060, upload-time = "2025-08-28T13:58:35.74Z" }, + { url = "https://files.pythonhosted.org/packages/03/69/9870368326db26f20c946205fb2d0008988aea552dbaec35fbacbb46efaa/ruff-0.12.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a2c0a2e1a450f387bf2c6237c727dd22191ae8c00e448e0672d624b2bbd7fb0", size = 11799848, upload-time = "2025-08-28T13:58:38.051Z" }, + { url = "https://files.pythonhosted.org/packages/25/8c/dd2c7f990e9b3a8a55eee09d4e675027d31727ce33cdb29eab32d025bdc9/ruff-0.12.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ca4c3a7f937725fd2413c0e884b5248a19369ab9bdd850b5781348ba283f644", size = 13536288, upload-time = "2025-08-28T13:58:40.046Z" }, + { url = "https://files.pythonhosted.org/packages/7a/30/d5496fa09aba59b5e01ea76775a4c8897b13055884f56f1c35a4194c2297/ruff-0.12.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4d1df0098124006f6a66ecf3581a7f7e754c4df7644b2e6704cd7ca80ff95211", size = 14490633, upload-time = "2025-08-28T13:58:42.285Z" }, + { url = "https://files.pythonhosted.org/packages/9b/2f/81f998180ad53445d403c386549d6946d0748e536d58fce5b5e173511183/ruff-0.12.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a8dd5f230efc99a24ace3b77e3555d3fbc0343aeed3fc84c8d89e75ab2ff793", size = 13888430, upload-time = "2025-08-28T13:58:44.641Z" }, + { url = "https://files.pythonhosted.org/packages/87/71/23a0d1d5892a377478c61dbbcffe82a3476b050f38b5162171942a029ef3/ruff-0.12.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dc75533039d0ed04cd33fb8ca9ac9620b99672fe7ff1533b6402206901c34ee", size = 12913133, upload-time = "2025-08-28T13:58:47.039Z" }, + { url = "https://files.pythonhosted.org/packages/80/22/3c6cef96627f89b344c933781ed38329bfb87737aa438f15da95907cbfd5/ruff-0.12.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fc58f9266d62c6eccc75261a665f26b4ef64840887fc6cbc552ce5b29f96cc8", size = 13169082, upload-time = "2025-08-28T13:58:49.157Z" }, + { url = "https://files.pythonhosted.org/packages/05/b5/68b3ff96160d8b49e8dd10785ff3186be18fd650d356036a3770386e6c7f/ruff-0.12.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5a0113bd6eafd545146440225fe60b4e9489f59eb5f5f107acd715ba5f0b3d2f", size = 13139490, upload-time = "2025-08-28T13:58:51.593Z" }, + { url = "https://files.pythonhosted.org/packages/59/b9/050a3278ecd558f74f7ee016fbdf10591d50119df8d5f5da45a22c6afafc/ruff-0.12.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0d737b4059d66295c3ea5720e6efc152623bb83fde5444209b69cd33a53e2000", size = 11958928, upload-time = "2025-08-28T13:58:53.943Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bc/93be37347db854806904a43b0493af8d6873472dfb4b4b8cbb27786eb651/ruff-0.12.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:916fc5defee32dbc1fc1650b576a8fed68f5e8256e2180d4d9855aea43d6aab2", size = 11764513, upload-time = "2025-08-28T13:58:55.976Z" }, + { url = "https://files.pythonhosted.org/packages/7a/a1/1471751e2015a81fd8e166cd311456c11df74c7e8769d4aabfbc7584c7ac/ruff-0.12.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c984f07d7adb42d3ded5be894fb4007f30f82c87559438b4879fe7aa08c62b39", size = 12745154, upload-time = "2025-08-28T13:58:58.16Z" }, + { url = "https://files.pythonhosted.org/packages/68/ab/2542b14890d0f4872dd81b7b2a6aed3ac1786fae1ce9b17e11e6df9e31e3/ruff-0.12.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e07fbb89f2e9249f219d88331c833860489b49cdf4b032b8e4432e9b13e8a4b9", size = 13227653, upload-time = "2025-08-28T13:59:00.276Z" }, + { url = "https://files.pythonhosted.org/packages/22/16/2fbfc61047dbfd009c58a28369a693a1484ad15441723be1cd7fe69bb679/ruff-0.12.11-py3-none-win32.whl", hash = "sha256:c792e8f597c9c756e9bcd4d87cf407a00b60af77078c96f7b6366ea2ce9ba9d3", size = 11944270, upload-time = "2025-08-28T13:59:02.347Z" }, + { url = "https://files.pythonhosted.org/packages/08/a5/34276984705bfe069cd383101c45077ee029c3fe3b28225bf67aa35f0647/ruff-0.12.11-py3-none-win_amd64.whl", hash = "sha256:a3283325960307915b6deb3576b96919ee89432ebd9c48771ca12ee8afe4a0fd", size = 13046600, upload-time = "2025-08-28T13:59:04.751Z" }, + { url = "https://files.pythonhosted.org/packages/84/a8/001d4a7c2b37623a3fd7463208267fb906df40ff31db496157549cfd6e72/ruff-0.12.11-py3-none-win_arm64.whl", hash = "sha256:bae4d6e6a2676f8fb0f98b74594a048bae1b944aab17e9f5d504062303c6dbea", size = 12135290, upload-time = "2025-08-28T13:59:06.933Z" }, ] [[package]] name = "s3fs" -version = "2025.7.0" +version = "2025.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiobotocore" }, { name = "aiohttp" }, { name = "fsspec" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bf/13/37438c4672ba1d23ec46df0e4b57e98469e5c5f4f98313cf6842b631652b/s3fs-2025.7.0.tar.gz", hash = "sha256:5e7f9ec0cad7745155e3eb86fae15b1481fa29946bf5b3a4ce3a60701ce6022d", size = 77795, upload-time = "2025-07-15T16:35:22.177Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/f3/8e6371436666aedfd16e63ff68a51b8a8fcf5f33a0eee33c35e0b2476b27/s3fs-2025.9.0.tar.gz", hash = "sha256:6d44257ef19ea64968d0720744c4af7a063a05f5c1be0e17ce943bef7302bc30", size = 77823, upload-time = "2025-09-02T19:18:21.781Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/c7/30d13b7fd4f866ca3f30e9a6e7ae038f0c45226f6e26b3cc98d6d197f93b/s3fs-2025.7.0-py3-none-any.whl", hash = "sha256:b6b2d3f84b6aa1c2ba5e62e39dd9410cf54f10a2cce1ea6db1ba0d1a6bcce685", size = 30315, upload-time = "2025-07-15T16:35:20.734Z" }, + { url = "https://files.pythonhosted.org/packages/37/b3/ca7d58ca25b1bb6df57e6cbd0ca8d6437a4b9ce1cd35adc8a6b2949c113b/s3fs-2025.9.0-py3-none-any.whl", hash = "sha256:c33c93d48f66ed440dbaf6600be149cdf8beae4b6f8f0201a209c5801aeb7e30", size = 30319, upload-time = "2025-09-02T19:18:20.563Z" }, ] [[package]] @@ -3064,11 +3066,11 @@ wheels = [ [[package]] name = "soupsieve" -version = "2.7" +version = "2.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/f4/4a80cd6ef364b2e8b65b15816a843c0980f7a5a2b4dc701fc574952aa19f/soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a", size = 103418, upload-time = "2025-04-20T18:50:08.518Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472, upload-time = "2025-08-27T15:39:51.78Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", size = 36677, upload-time = "2025-04-20T18:50:07.196Z" }, + { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" }, ] [[package]] From a9df33744c2f2982ac44db20f264535889722667 Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 17:51:19 -0500 Subject: [PATCH 20/24] fix broken tests and inconsistent type hints --- tests/integration/test_segy_import_export.py | 25 ++++++++------------ tests/integration/testing_helpers.py | 11 ++++----- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/tests/integration/test_segy_import_export.py b/tests/integration/test_segy_import_export.py index 7a72db8ff..4e638b330 100644 --- a/tests/integration/test_segy_import_export.py +++ b/tests/integration/test_segy_import_export.py @@ -286,22 +286,17 @@ def test_meta_dataset(self, zarr_tmp: Path) -> None: attributes = ds.attrs["attributes"] assert attributes is not None - assert len(attributes) == 7 + assert len(attributes) == 6 # Validate all attribute provided by the abstract template assert attributes["default_variable_name"] == "amplitude" - # Validate attributes provided by the PostStack3DTime template assert attributes["surveyDimensionality"] == "3D" assert attributes["ensembleType"] == "line" assert attributes["processingStage"] == "post-stack" - # Validate text header assert attributes["textHeader"] == text_header_teapot_dome() - # Validate binary header assert attributes["binaryHeader"] == binary_header_teapot_dome() def test_meta_variable(self, zarr_tmp: Path) -> None: """Metadata reading tests.""" - # NOTE: If mask_and_scale is not set, - # Xarray will convert int to float and replace _FillValue with NaN ds = open_dataset(StorageLocation(str(zarr_tmp))) expected_attrs = { "count": 97354860, @@ -325,13 +320,13 @@ def test_grid(self, zarr_tmp: Path) -> None: # sample dimension is called "time" # Validate the dimension coordinate variables - validate_variable(ds, "inline", (345,), ["inline"], np.int32, range(1, 346), get_values) - validate_variable(ds, "crossline", (188,), ["crossline"], np.int32, range(1, 189), get_values) - validate_variable(ds, "time", (1501,), ["time"], np.int32, range(0, 3002, 2), get_values) + validate_variable(ds, "inline", (345,), ("inline",), np.int32, range(1, 346), get_values) + validate_variable(ds, "crossline", (188,), ("crossline",), np.int32, range(1, 189), get_values) + validate_variable(ds, "time", (1501,), ("time",), np.int32, range(0, 3002, 2), get_values) # Validate the non-dimensional coordinate variables - validate_variable(ds, "cdp_x", (345, 188), ["inline", "crossline"], np.float64, None, None) - validate_variable(ds, "cdp_y", (345, 188), ["inline", "crossline"], np.float64, None, None) + validate_variable(ds, "cdp_x", (345, 188), ("inline", "crossline"), np.float64, None, None) + validate_variable(ds, "cdp_y", (345, 188), ("inline", "crossline"), np.float64, None, None) # Validate the headers # We have a custom set of headers since we used customize_segy_specs() @@ -342,21 +337,21 @@ def test_grid(self, zarr_tmp: Path) -> None: ds, "headers", (345, 188), - ["inline", "crossline"], - data_type, + ("inline", "crossline"), + data_type.newbyteorder("native"), range(1, 346), get_inline_header_values, ) # Validate the trace mask - validate_variable(ds, "trace_mask", (345, 188), ["inline", "crossline"], np.bool, None, None) + validate_variable(ds, "trace_mask", (345, 188), ("inline", "crossline"), np.bool, None, None) # validate the amplitude data validate_variable( ds, "amplitude", (345, 188, 1501), - ["inline", "crossline", "time"], + ("inline", "crossline", "time"), np.float32, None, None, diff --git a/tests/integration/testing_helpers.py b/tests/integration/testing_helpers.py index 05cf49226..164004e3b 100644 --- a/tests/integration/testing_helpers.py +++ b/tests/integration/testing_helpers.py @@ -4,6 +4,7 @@ import numpy as np import xarray as xr +from numpy.typing import DTypeLike from segy.schema import HeaderField from segy.schema import SegySpec @@ -55,9 +56,9 @@ def get_inline_header_values(dataset: xr.Dataset) -> np.ndarray: def validate_variable( # noqa PLR0913 dataset: xr.Dataset, name: str, - shape: list[int], - dims: list[str], - data_type: np.dtype, + shape: tuple[int, ...], + dims: tuple[str, ...], + data_type: DTypeLike, expected_values: range | None, actual_value_generator: Callable[[xr.DataArray], np.ndarray] | None = None, ) -> None: @@ -78,10 +79,6 @@ def validate_variable( # noqa PLR0913 expected_types = [data_type[name] for name in data_type.names] actual_types = [arr.dtype[name] for name in arr.dtype.names] assert expected_types == actual_types - - # Compare field offsets fails. - # However, we believe this is acceptable and do not compare offsets - # name: 'shot_point' dt_exp: (dtype('>i4'), 196) dt_act: (dtype(' Date: Wed, 3 Sep 2025 17:54:27 -0500 Subject: [PATCH 21/24] clean up comments --- tests/integration/test_segy_import_export.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/tests/integration/test_segy_import_export.py b/tests/integration/test_segy_import_export.py index 4e638b330..6219732e8 100644 --- a/tests/integration/test_segy_import_export.py +++ b/tests/integration/test_segy_import_export.py @@ -267,8 +267,6 @@ class TestReader: def test_meta_dataset(self, zarr_tmp: Path) -> None: """Metadata reading tests.""" - # NOTE: If mask_and_scale is not set, - # Xarray will convert int to float and replace _FillValue with NaN ds = open_dataset(StorageLocation(str(zarr_tmp))) expected_attrs = { "apiVersion": "1.0.0a1", @@ -311,14 +309,8 @@ def test_meta_variable(self, zarr_tmp: Path) -> None: def test_grid(self, zarr_tmp: Path) -> None: """Test validating MDIO variables.""" - # Load Xarray dataset from the MDIO file - # NOTE: If mask_and_scale is not set, - # Xarray will convert int to float and replace _FillValue with NaN ds = open_dataset(StorageLocation(str(zarr_tmp))) - # Note: in order to create the dataset we used the Time template, so the - # sample dimension is called "time" - # Validate the dimension coordinate variables validate_variable(ds, "inline", (345,), ("inline",), np.int32, range(1, 346), get_values) validate_variable(ds, "crossline", (188,), ("crossline",), np.int32, range(1, 189), get_values) @@ -338,7 +330,7 @@ def test_grid(self, zarr_tmp: Path) -> None: "headers", (345, 188), ("inline", "crossline"), - data_type.newbyteorder("native"), + data_type.newbyteorder("native"), # mdio saves with machine endian, spec could be different endian range(1, 346), get_inline_header_values, ) @@ -359,8 +351,6 @@ def test_grid(self, zarr_tmp: Path) -> None: def test_inline(self, zarr_tmp: Path) -> None: """Read and compare every 75 inlines' mean and std. dev.""" - # NOTE: If mask_and_scale is not set, - # Xarray will convert int to float and replace _FillValue with NaN ds = open_dataset(StorageLocation(str(zarr_tmp))) inlines = ds["amplitude"][::75, :, :] mean, std = inlines.mean(), inlines.std() @@ -368,8 +358,6 @@ def test_inline(self, zarr_tmp: Path) -> None: def test_crossline(self, zarr_tmp: Path) -> None: """Read and compare every 75 crosslines' mean and std. dev.""" - # NOTE: If mask_and_scale is not set, - # Xarray will convert int to float and replace _FillValue with NaN ds = open_dataset(StorageLocation(str(zarr_tmp))) xlines = ds["amplitude"][:, ::75, :] mean, std = xlines.mean(), xlines.std() @@ -378,8 +366,6 @@ def test_crossline(self, zarr_tmp: Path) -> None: def test_zslice(self, zarr_tmp: Path) -> None: """Read and compare every 225 z-slices' mean and std. dev.""" - # NOTE: If mask_and_scale is not set, - # Xarray will convert int to float and replace _FillValue with NaN ds = open_dataset(StorageLocation(str(zarr_tmp))) slices = ds["amplitude"][:, :, ::225] mean, std = slices.mean(), slices.std() From 7712a0d38e07cd1589549e5c6c1477523f819c05 Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 18:13:00 -0500 Subject: [PATCH 22/24] clarify binary header scaling --- src/mdio/segy/creation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mdio/segy/creation.py b/src/mdio/segy/creation.py index c3161358c..774ebe060 100644 --- a/src/mdio/segy/creation.py +++ b/src/mdio/segy/creation.py @@ -33,7 +33,7 @@ def make_segy_factory(dataset: xr.Dataset, spec: SegySpec) -> SegyFactory: samples_per_trace = binary_header["samples_per_trace"] return SegyFactory( spec=spec, - sample_interval=sample_interval, # Sample Interval is already in milliseconds + sample_interval=sample_interval, # Sample interval is read in from binary header so no scaling here samples_per_trace=samples_per_trace, ) From 2d658d05093ab388607c12fba3b9663e0aad803d Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 18:16:30 -0500 Subject: [PATCH 23/24] make test names clearer --- tests/integration/test_segy_import_export.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/integration/test_segy_import_export.py b/tests/integration/test_segy_import_export.py index 6219732e8..fe49257e7 100644 --- a/tests/integration/test_segy_import_export.py +++ b/tests/integration/test_segy_import_export.py @@ -262,10 +262,10 @@ def test_3d_import(segy_input: Path, zarr_tmp: Path) -> None: class TestReader: """Test reader functionality. - NOTE: These tests must be executed after the 'test_3d_import' and before running 'TestExport' tests. + NOTE: These tests must be executed after the 'test_3d_import' and before running 'TestExport' tests. """ - def test_meta_dataset(self, zarr_tmp: Path) -> None: + def test_dataset_metadata(self, zarr_tmp: Path) -> None: """Metadata reading tests.""" ds = open_dataset(StorageLocation(str(zarr_tmp))) expected_attrs = { @@ -285,7 +285,7 @@ def test_meta_dataset(self, zarr_tmp: Path) -> None: attributes = ds.attrs["attributes"] assert attributes is not None assert len(attributes) == 6 - # Validate all attribute provided by the abstract template + # Validate all attributes provided by the abstract template assert attributes["default_variable_name"] == "amplitude" assert attributes["surveyDimensionality"] == "3D" assert attributes["ensembleType"] == "line" @@ -293,7 +293,7 @@ def test_meta_dataset(self, zarr_tmp: Path) -> None: assert attributes["textHeader"] == text_header_teapot_dome() assert attributes["binaryHeader"] == binary_header_teapot_dome() - def test_meta_variable(self, zarr_tmp: Path) -> None: + def test_variable_metadata(self, zarr_tmp: Path) -> None: """Metadata reading tests.""" ds = open_dataset(StorageLocation(str(zarr_tmp))) expected_attrs = { @@ -349,14 +349,14 @@ def test_grid(self, zarr_tmp: Path) -> None: None, ) - def test_inline(self, zarr_tmp: Path) -> None: + def test_inline_reads(self, zarr_tmp: Path) -> None: """Read and compare every 75 inlines' mean and std. dev.""" ds = open_dataset(StorageLocation(str(zarr_tmp))) inlines = ds["amplitude"][::75, :, :] mean, std = inlines.mean(), inlines.std() npt.assert_allclose([mean, std], [1.0555277e-04, 6.0027051e-01]) - def test_crossline(self, zarr_tmp: Path) -> None: + def test_crossline_reads(self, zarr_tmp: Path) -> None: """Read and compare every 75 crosslines' mean and std. dev.""" ds = open_dataset(StorageLocation(str(zarr_tmp))) xlines = ds["amplitude"][:, ::75, :] @@ -364,7 +364,7 @@ def test_crossline(self, zarr_tmp: Path) -> None: npt.assert_allclose([mean, std], [-5.0329847e-05, 5.9406823e-01]) - def test_zslice(self, zarr_tmp: Path) -> None: + def test_zslice_reads(self, zarr_tmp: Path) -> None: """Read and compare every 225 z-slices' mean and std. dev.""" ds = open_dataset(StorageLocation(str(zarr_tmp))) slices = ds["amplitude"][:, :, ::225] @@ -376,8 +376,7 @@ def test_zslice(self, zarr_tmp: Path) -> None: class TestExport: """Test SEG-Y exporting functionality. - NOTE: This test(s) must be executed after the 'test_3d_import' and 'TestReader' tests - successfully complete. + NOTE: This test(s) must be executed after the 'test_3d_import' and 'TestReader' tests successfully complete. """ def test_3d_export(self, segy_input: Path, zarr_tmp: Path, segy_export_tmp: Path) -> None: From 96c58b1afbfc2b010efa236eae0af241f5e9669e Mon Sep 17 00:00:00 2001 From: Altay Sansal Date: Wed, 3 Sep 2025 18:21:20 -0500 Subject: [PATCH 24/24] fix broken unit tests due to storage_options handling --- src/mdio/api/convenience.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/mdio/api/convenience.py b/src/mdio/api/convenience.py index 9a522a86b..1a47e199e 100644 --- a/src/mdio/api/convenience.py +++ b/src/mdio/api/convenience.py @@ -51,9 +51,6 @@ def copy_mdio( # noqa: PLR0913 storage_options_output: Storage options for output MDIO. """ - storage_options_input = storage_options_input or {} - storage_options_output = storage_options_output or {} - create_empty_like( source_path, target_path,