mostlyright-data 0.9.2__tar.gz → 0.9.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/PKG-INFO +5 -5
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/README.md +3 -3
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/pyproject.toml +3 -2
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/acquisition/http.py +458 -12
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_bootstrap.py +63 -10
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_crawler_job.py +188 -88
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_session_worker.py +137 -6
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_worker.py +1806 -94
- mostlyright_data-0.9.4/src/mostlyright/data_harness/readers/parquet_tabular.py +158 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/registry.py +9 -0
- mostlyright_data-0.9.4/src/mostlyright/data_harness/readers/samples/parquet.tabular/1.0.0/two_columns/expected.json +25 -0
- mostlyright_data-0.9.4/src/mostlyright/data_harness/readers/samples/parquet.tabular/1.0.0/two_columns/prices.parquet +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/recipe.py +92 -0
- mostlyright_data-0.9.4/src/mostlyright/data_harness/sources/authenticated_https.py +367 -0
- mostlyright_data-0.9.4/src/mostlyright/data_harness/sources/authenticated_https_v2.py +1186 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/admission.py +5 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/authoring_shards.py +36 -2
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/governance.py +98 -8
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/studio_boundary.py +1 -1
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/research.py +34 -12
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/router.py +5 -4
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/vocabulary.py +6 -1
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/workers.py +6 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/catalog_author.py +29 -1
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/catalog_publish.py +25 -4
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/remediation.py +4 -1
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/render.py +1 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/.gitignore +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/scripts/hatch_build.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/acquisition/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/acquisition/parsing.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/acquisition/ranges.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/acquisition/result_download.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/acquisition/retention_admission.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/acquisition/sandbox.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/acquisition/url_policy.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/agent_runtime.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/assets/logo-ink.svg +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/backends/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/backends/pandas_backend.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/backends/polars_backend.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/backends/protocol.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/backends/reference.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/backends/registry.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/backends/restrictions.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/canonical.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/catalog_job.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/cli.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/contracts.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/coordinator.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/deploy.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/deploy_target.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/deployment_evidence.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/event_presentation.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/events.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/fast_delimited.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/fleet.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/formats.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/governors.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_crawler.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_crawler_container_smoke.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_crawler_fetch.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_crawler_protocol.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_dataset.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_deploy.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_handoff.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_ingestion_contract.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_ingestion_job.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_ingestion_job_smoke.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_session_container_smoke.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/hosted_session_worker_job_smoke.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ingestion/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ingestion/contracts.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ingestion/faults.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ingestion/gcs_store.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ingestion/spool.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ingestion/store.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/key_seam.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/linux_process_boundary.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/local_contracts.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/local_search/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/local_search/build_index.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/local_search/contracts.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/local_search/query_trace.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/local_search/retrieval.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/local_search/sealed.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/local_search/service.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/CONTRACT.md +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/chrome.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/code_body.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/document.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/frame.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/interactive.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/markdown_body.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/mr_components.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/outputs_data.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/outputs_rich.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/outputs_source.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/outputs_stage.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/outputs_text.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/parse.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/status.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/nbrender/tokens.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/notebook.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/offline.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/operation_registry.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/operator_setup.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/pipeline.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/plan_graph.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/preparation/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/preparation/contracts.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/preparation/engine.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/preparation/errors.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/preparation/gates.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/preparation/joins.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/preparation/profile.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/preparation/table.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/preparation/transforms.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/progress_events.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/containers.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/contracts.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/delimited.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/grib2/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/grib2/admission.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/grib2/decode.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/grib2/geometry.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/grib2/portable_math.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/json_tabular.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/README.md +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/cities.csv.gz +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/cities.csv.gz +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/cities.tar +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/cities.tar +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/cities.zip +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/cities.csv +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/cities.tsv +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/observations.csv +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/stations.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/observations.ndjson +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/cities.xlsx +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/README.md +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/gfs-2m-temperature.grib2 +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/hrrr-categorical-rain.grib2 +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/expected.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/samples.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/spreadsheet.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/readers/tabular.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/repair/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/repair/adapters.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/repair/contracts.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/repair/coordinator.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/repair/errors.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/review.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/rowset.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/serving.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/serving_edge.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/serving_http.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/session_probes.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/signing.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/source_discovery.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/_adapter_steps.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/adapters.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/cadence.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/cadence_emission.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/cadence_history.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/authoring.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/authoring_policy.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/bounded_io.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/channel.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/channel_client.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/contracts.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/coverage.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/delta.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/embedding.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/entry_v2.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/fill.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/fill_partitions.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/fill_staging.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/gating.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/generation_receipt.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/harvest/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/harvest/ckan.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/harvest/datagov_v4.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/harvest/protocol.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/harvest/sdmx.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/harvest/stac.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/health.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/hosted_catalog.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/identity_history.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/neural.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/packed_catalog.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/packed_retrieval.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/packed_writer.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/query_trace.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/recommend.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/retrieval.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/retrieval_manifest.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/rights_decisions.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/sealed.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/search.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/streaming_delta.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/catalog/update.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/collections.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/contracts.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/deletion.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/fitness.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/hosted.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/integration.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/range_reader.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/registry.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/rights_rule.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/source_cadence_vectors.v1.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/sports.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/stream.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/stream_connector.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/sources/stream_recorder.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/acquire.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/acquire_cancel.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/approvals.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/commands.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/download.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/narrative.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/parity.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/propose.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/runs.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/session.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/stream.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/thin/transport.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ucum/TABLE-PIN.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ucum/ucum-subset.v1.json +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/unit_flow.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/units.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/approve.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/author_yaml.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/cloud_auth.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/approve.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/auth.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/author.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/catalog_fill.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/catalog_harvest.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/catalog_search.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/catalog_update.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/deploy.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/deploy_dataset.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/deploy_plan.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/deploy_status.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/diff.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/index.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/inventory.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/list_builds.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/login.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/peek.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/plan_check.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/preflight.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/record.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/review_setup.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/search.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/show.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/commands/whoami.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/credential_native.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/credential_store.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/credentials.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/diffing.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/headline.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/hosted_acquisition.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/hosted_run_status.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/inventory.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/local_review.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/login.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/path_kind.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/peek.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/plain_file.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/plan_check.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/preflight.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/readers.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/ux/workload.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/viewer.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/visual_run/__init__.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/visual_run/authoring.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/visual_run/contracts.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/visual_run/materialize.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/visual_run/observations.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/visual_run/query.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/visual_run/reducer.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/visual_run/sdk.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/visual_run/store.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/visual_run/transport.py +0 -0
- {mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/watch.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mostlyright-data
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.4
|
|
4
4
|
Summary: Mostly Right CLI for reproducible, reviewed Parquet datasets
|
|
5
5
|
Project-URL: Homepage, https://mostlyright.md/
|
|
6
6
|
Project-URL: Documentation, https://mostlyright.md/docs/guides/cli/
|
|
7
7
|
Requires-Python: <3.14,>=3.13
|
|
8
8
|
Provides-Extra: build
|
|
9
9
|
Requires-Dist: hatchling==1.28.0; extra == 'build'
|
|
10
|
-
Requires-Dist: uv-build==0.
|
|
10
|
+
Requires-Dist: uv-build==0.11.3; extra == 'build'
|
|
11
11
|
Provides-Extra: dev
|
|
12
12
|
Requires-Dist: cmarkgfm>=2025.10.22; extra == 'dev'
|
|
13
13
|
Requires-Dist: croniter==6.2.4; extra == 'dev'
|
|
@@ -81,9 +81,9 @@ Description-Content-Type: text/markdown
|
|
|
81
81
|
|
|
82
82
|
# mostlyright-data
|
|
83
83
|
|
|
84
|
-
Mostly Right turns public weather, economic, financial,
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
Mostly Right turns historical and live public data—weather, economic releases, financial reports,
|
|
85
|
+
and satellite data—into consistent datasets for analysis and prediction. Each dataset includes
|
|
86
|
+
source links, timestamps, and check results.
|
|
87
87
|
|
|
88
88
|
`mostlyright-data` is the command-line client. It starts hosted research and build work, follows
|
|
89
89
|
run status, and downloads completed Parquet datasets.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# mostlyright-data
|
|
2
2
|
|
|
3
|
-
Mostly Right turns public weather, economic, financial,
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Mostly Right turns historical and live public data—weather, economic releases, financial reports,
|
|
4
|
+
and satellite data—into consistent datasets for analysis and prediction. Each dataset includes
|
|
5
|
+
source links, timestamps, and check results.
|
|
6
6
|
|
|
7
7
|
`mostlyright-data` is the command-line client. It starts hosted research and build work, follows
|
|
8
8
|
run status, and downloads completed Parquet datasets.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mostlyright-data"
|
|
3
|
-
version = "0.9.
|
|
3
|
+
version = "0.9.4"
|
|
4
4
|
description = "Mostly Right CLI for reproducible, reviewed Parquet datasets"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13,<3.14"
|
|
@@ -69,6 +69,7 @@ mr-data-catalog-job = "mostlyright.data_harness.thin.workers:catalog_job_main"
|
|
|
69
69
|
mr-data-crawler = "mostlyright.data_harness.thin.workers:crawler_main"
|
|
70
70
|
mr-data-crawler-job = "mostlyright.data_harness.thin.workers:crawler_job_main"
|
|
71
71
|
mr-data-producer = "mostlyright.data_harness.thin.workers:producer_main"
|
|
72
|
+
mr-data-generic-api = "mostlyright.data_harness.thin.workers:generic_api_probe_main"
|
|
72
73
|
mr-data-verifier = "mostlyright.data_harness.thin.workers:verifier_main"
|
|
73
74
|
# The legacy Cloud Run service entrypoint remains available for rollback. The Job entrypoint is a
|
|
74
75
|
# separate executable so an image revision cannot accidentally start the retired HTTP service.
|
|
@@ -80,7 +81,7 @@ mr-data-ingestion-job = "mostlyright.data_harness.thin.workers:ingestion_job_mai
|
|
|
80
81
|
[project.optional-dependencies]
|
|
81
82
|
build = [
|
|
82
83
|
"hatchling==1.28.0",
|
|
83
|
-
"uv-build==0.
|
|
84
|
+
"uv-build==0.11.3",
|
|
84
85
|
]
|
|
85
86
|
# Everything the local execution lane needs, and nothing the hosted lane does. Installing this
|
|
86
87
|
# extra is what turns the thin client back into today's full product; `mr-data` detects its
|
{mostlyright_data-0.9.2 → mostlyright_data-0.9.4}/src/mostlyright/data_harness/acquisition/http.py
RENAMED
|
@@ -17,8 +17,8 @@ import time
|
|
|
17
17
|
from collections.abc import Callable, Iterator, Mapping
|
|
18
18
|
from dataclasses import dataclass, replace
|
|
19
19
|
from pathlib import Path, PurePosixPath
|
|
20
|
-
from typing import Protocol
|
|
21
|
-
from urllib.parse import urljoin, urlsplit
|
|
20
|
+
from typing import TYPE_CHECKING, Protocol
|
|
21
|
+
from urllib.parse import unquote_to_bytes, urljoin, urlsplit
|
|
22
22
|
|
|
23
23
|
try:
|
|
24
24
|
import fcntl
|
|
@@ -44,6 +44,11 @@ from mostlyright.data_harness.formats import (
|
|
|
44
44
|
)
|
|
45
45
|
from mostlyright.data_harness.readers.registry import TOOLBOX, ReaderRegistry
|
|
46
46
|
|
|
47
|
+
if TYPE_CHECKING:
|
|
48
|
+
from mostlyright.data_harness.sources.authenticated_https import (
|
|
49
|
+
AuthenticatedApiKeyConfiguration,
|
|
50
|
+
)
|
|
51
|
+
|
|
47
52
|
MAX_HEADER_COUNT = 128
|
|
48
53
|
MAX_HEADER_BYTES = 32_768
|
|
49
54
|
REDIRECT_STATUSES = frozenset({301, 302, 303, 307, 308})
|
|
@@ -373,6 +378,276 @@ class _DatagovEchoObject(list[tuple[str, object]]):
|
|
|
373
378
|
"""JSON object retaining duplicate keys for authorization echo inspection."""
|
|
374
379
|
|
|
375
380
|
|
|
381
|
+
class AuthenticatedApiKeyAuthorization:
|
|
382
|
+
"""One-use API-key authorization with one owned mutable secret buffer.
|
|
383
|
+
|
|
384
|
+
Python's HTTP stack ultimately requires one immutable header string. That runtime-owned
|
|
385
|
+
rendering cannot be zeroed by Python code; this carrier narrows it to the transport call,
|
|
386
|
+
drops every Harness reference immediately afterwards, and zeroes every mutable buffer it owns.
|
|
387
|
+
"""
|
|
388
|
+
|
|
389
|
+
__slots__ = ("_closed", "_configuration", "_token", "_validity_check")
|
|
390
|
+
|
|
391
|
+
def __init__(
|
|
392
|
+
self,
|
|
393
|
+
*,
|
|
394
|
+
configuration: AuthenticatedApiKeyConfiguration,
|
|
395
|
+
token: bytes | bytearray | memoryview,
|
|
396
|
+
validity_check: Callable[[], None] | None = None,
|
|
397
|
+
) -> None:
|
|
398
|
+
# Imported lazily because ``sources.__init__`` exports adapters that depend on this
|
|
399
|
+
# transport module. Runtime import at module load would close that established cycle.
|
|
400
|
+
from mostlyright.data_harness.sources.authenticated_https import (
|
|
401
|
+
AUTHENTICATED_API_KEY_MAX_TOKEN_BYTES,
|
|
402
|
+
AuthenticatedApiKeyConfiguration,
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
if not isinstance(configuration, AuthenticatedApiKeyConfiguration):
|
|
406
|
+
raise AcquisitionSecurityError(
|
|
407
|
+
"AUTHENTICATED_CONFIG",
|
|
408
|
+
"validated authenticated API-key configuration is required",
|
|
409
|
+
)
|
|
410
|
+
if not isinstance(token, (bytes, bytearray, memoryview)):
|
|
411
|
+
raise AcquisitionSecurityError(
|
|
412
|
+
"AUTHENTICATED_TOKEN",
|
|
413
|
+
"token must be bounded printable ASCII bytes",
|
|
414
|
+
)
|
|
415
|
+
borrowed = memoryview(token).cast("B")
|
|
416
|
+
try:
|
|
417
|
+
if (
|
|
418
|
+
not 1 <= len(borrowed) <= AUTHENTICATED_API_KEY_MAX_TOKEN_BYTES
|
|
419
|
+
or borrowed[0] == ord(" ")
|
|
420
|
+
or borrowed[-1] == ord(" ")
|
|
421
|
+
or any(byte < 0x20 or byte > 0x7E for byte in borrowed)
|
|
422
|
+
):
|
|
423
|
+
raise AcquisitionSecurityError(
|
|
424
|
+
"AUTHENTICATED_TOKEN",
|
|
425
|
+
"token must be 1..4096 printable ASCII bytes without edge whitespace",
|
|
426
|
+
)
|
|
427
|
+
self._token = bytearray(borrowed)
|
|
428
|
+
finally:
|
|
429
|
+
borrowed.release()
|
|
430
|
+
self._configuration = configuration
|
|
431
|
+
self._closed = False
|
|
432
|
+
self._validity_check = validity_check
|
|
433
|
+
|
|
434
|
+
def __repr__(self) -> str:
|
|
435
|
+
return (
|
|
436
|
+
"AuthenticatedApiKeyAuthorization("
|
|
437
|
+
f"configuration_id={self._configuration.configuration_id!r}, token=<redacted>)"
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
__str__ = __repr__
|
|
441
|
+
|
|
442
|
+
def __reduce_ex__(self, _protocol: int) -> object:
|
|
443
|
+
raise TypeError("AuthenticatedApiKeyAuthorization is not serializable")
|
|
444
|
+
|
|
445
|
+
def __copy__(self) -> object:
|
|
446
|
+
raise TypeError("AuthenticatedApiKeyAuthorization is not copyable")
|
|
447
|
+
|
|
448
|
+
def __deepcopy__(self, _memo: object) -> object:
|
|
449
|
+
raise TypeError("AuthenticatedApiKeyAuthorization is not copyable")
|
|
450
|
+
|
|
451
|
+
@property
|
|
452
|
+
def closed(self) -> bool:
|
|
453
|
+
return self._closed
|
|
454
|
+
|
|
455
|
+
def close(self) -> None:
|
|
456
|
+
_zero_mutable_buffer(self._token)
|
|
457
|
+
self._closed = True
|
|
458
|
+
|
|
459
|
+
def validate_target(self, url: str) -> None:
|
|
460
|
+
"""Check current authority and destination without rendering a header value."""
|
|
461
|
+
|
|
462
|
+
from mostlyright.data_harness.sources.authenticated_https import (
|
|
463
|
+
parse_authenticated_api_key_query,
|
|
464
|
+
)
|
|
465
|
+
from mostlyright.data_harness.sources.contracts import SourceContractError
|
|
466
|
+
|
|
467
|
+
if self._closed:
|
|
468
|
+
raise AcquisitionSecurityError(
|
|
469
|
+
"AUTHENTICATED_TOKEN",
|
|
470
|
+
"authorization has already been consumed",
|
|
471
|
+
)
|
|
472
|
+
if self._validity_check is not None:
|
|
473
|
+
self._validity_check()
|
|
474
|
+
token = memoryview(self._token).toreadonly()
|
|
475
|
+
try:
|
|
476
|
+
decoded_url = unquote_to_bytes(url)
|
|
477
|
+
except Exception:
|
|
478
|
+
decoded_url = b""
|
|
479
|
+
try:
|
|
480
|
+
if _contains_octets(url.encode("utf-8", errors="ignore"), token) or _contains_octets(
|
|
481
|
+
decoded_url, token
|
|
482
|
+
):
|
|
483
|
+
raise AcquisitionSecurityError(
|
|
484
|
+
"AUTHENTICATED_SECRET",
|
|
485
|
+
"request target contains credential material",
|
|
486
|
+
)
|
|
487
|
+
finally:
|
|
488
|
+
token.release()
|
|
489
|
+
try:
|
|
490
|
+
parse_authenticated_api_key_query(
|
|
491
|
+
{"url": url, "data_format": "csv", "filename": "source.csv"},
|
|
492
|
+
configuration=self._configuration,
|
|
493
|
+
)
|
|
494
|
+
except SourceContractError as error:
|
|
495
|
+
code = (
|
|
496
|
+
"AUTHENTICATED_SECRET"
|
|
497
|
+
if error.code == "AUTHENTICATED_SECRET"
|
|
498
|
+
else "AUTHENTICATED_ORIGIN"
|
|
499
|
+
)
|
|
500
|
+
raise AcquisitionSecurityError(
|
|
501
|
+
code,
|
|
502
|
+
"request is outside the configured authenticated HTTPS origin",
|
|
503
|
+
) from None
|
|
504
|
+
|
|
505
|
+
@contextlib.contextmanager
|
|
506
|
+
def render_header(self, url: str) -> Iterator[tuple[str, str]]:
|
|
507
|
+
"""Render the unavoidable immutable header only for the surrounding transport call."""
|
|
508
|
+
|
|
509
|
+
self.validate_target(url)
|
|
510
|
+
rendered_octets = bytearray(b"Bearer " if self._configuration.scheme == "bearer" else b"")
|
|
511
|
+
rendered_octets.extend(self._token)
|
|
512
|
+
rendered = rendered_octets.decode("ascii", errors="strict")
|
|
513
|
+
name = "Authorization" if self._configuration.scheme == "bearer" else "X-API-Key"
|
|
514
|
+
try:
|
|
515
|
+
yield name, rendered
|
|
516
|
+
finally:
|
|
517
|
+
rendered = ""
|
|
518
|
+
_zero_mutable_buffer(rendered_octets)
|
|
519
|
+
|
|
520
|
+
def reject_echoed_headers(self, headers: tuple[tuple[str, str], ...]) -> None:
|
|
521
|
+
token = memoryview(self._token).toreadonly()
|
|
522
|
+
try:
|
|
523
|
+
for name, value in headers:
|
|
524
|
+
for text in (name, value):
|
|
525
|
+
candidate = bytearray(text, "utf-8")
|
|
526
|
+
try:
|
|
527
|
+
if _contains_json_equivalent_token(candidate, token):
|
|
528
|
+
raise AcquisitionSecurityError(
|
|
529
|
+
"AUTHENTICATED_SECRET_ECHO",
|
|
530
|
+
"authenticated response echoed credential material "
|
|
531
|
+
"and was not retained",
|
|
532
|
+
)
|
|
533
|
+
finally:
|
|
534
|
+
_zero_mutable_buffer(candidate)
|
|
535
|
+
finally:
|
|
536
|
+
token.release()
|
|
537
|
+
|
|
538
|
+
def reject_echoed_response(self, payload: bytes) -> None:
|
|
539
|
+
token = memoryview(self._token).toreadonly()
|
|
540
|
+
try:
|
|
541
|
+
if _contains_json_equivalent_token(payload, token):
|
|
542
|
+
raise AcquisitionSecurityError(
|
|
543
|
+
"AUTHENTICATED_SECRET_ECHO",
|
|
544
|
+
"authenticated response echoed credential material and was not retained",
|
|
545
|
+
)
|
|
546
|
+
finally:
|
|
547
|
+
token.release()
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
_JSON_SHORT_ESCAPES = {
|
|
551
|
+
ord('"'): ord('"'),
|
|
552
|
+
ord("/"): ord("/"),
|
|
553
|
+
ord("\\"): ord("\\"),
|
|
554
|
+
ord("b"): 0x08,
|
|
555
|
+
ord("f"): 0x0C,
|
|
556
|
+
ord("n"): 0x0A,
|
|
557
|
+
ord("r"): 0x0D,
|
|
558
|
+
ord("t"): 0x09,
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
def _zero_mutable_buffer(value: bytearray) -> None:
|
|
563
|
+
for index in range(len(value)):
|
|
564
|
+
value[index] = 0
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def _contains_octets(
|
|
568
|
+
value: bytes | bytearray,
|
|
569
|
+
token: bytes | bytearray | memoryview,
|
|
570
|
+
) -> bool:
|
|
571
|
+
"""Linear-time subsequence search without materializing an immutable token copy."""
|
|
572
|
+
|
|
573
|
+
if not token or len(token) > len(value):
|
|
574
|
+
return False
|
|
575
|
+
prefix = [0] * len(token)
|
|
576
|
+
matched = 0
|
|
577
|
+
for index in range(1, len(token)):
|
|
578
|
+
while matched and token[index] != token[matched]:
|
|
579
|
+
matched = prefix[matched - 1]
|
|
580
|
+
if token[index] == token[matched]:
|
|
581
|
+
matched += 1
|
|
582
|
+
prefix[index] = matched
|
|
583
|
+
matched = 0
|
|
584
|
+
for byte in value:
|
|
585
|
+
while matched and byte != token[matched]:
|
|
586
|
+
matched = prefix[matched - 1]
|
|
587
|
+
if byte == token[matched]:
|
|
588
|
+
matched += 1
|
|
589
|
+
if matched == len(token):
|
|
590
|
+
return True
|
|
591
|
+
return False
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def _contains_json_equivalent_token(
|
|
595
|
+
value: bytes | bytearray,
|
|
596
|
+
token: bytes | bytearray | memoryview,
|
|
597
|
+
) -> bool:
|
|
598
|
+
"""Find a token through every bounded reversible ASCII JSON-escape layer.
|
|
599
|
+
|
|
600
|
+
Each successful pass strictly shortens the already transport-bounded input. An additional
|
|
601
|
+
JSON layer must at least double the escaping backslashes, so ``bit_length + 1`` passes reach a
|
|
602
|
+
fixed point for every representable nesting depth without an attacker-controlled loop count.
|
|
603
|
+
Unknown and non-ASCII escapes remain literal. If that mathematical bound is ever exhausted
|
|
604
|
+
before a fixed point, fail closed and classify the value as an echo.
|
|
605
|
+
"""
|
|
606
|
+
|
|
607
|
+
current: bytes | bytearray = value
|
|
608
|
+
owned: list[bytearray] = []
|
|
609
|
+
maximum_passes = max(1, len(value).bit_length() + 1)
|
|
610
|
+
try:
|
|
611
|
+
for _ in range(maximum_passes):
|
|
612
|
+
if _contains_octets(current, token):
|
|
613
|
+
return True
|
|
614
|
+
normalized = bytearray()
|
|
615
|
+
owned.append(normalized)
|
|
616
|
+
changed = False
|
|
617
|
+
index = 0
|
|
618
|
+
while index < len(current):
|
|
619
|
+
byte = current[index]
|
|
620
|
+
if byte != ord("\\") or index + 1 >= len(current):
|
|
621
|
+
normalized.append(byte)
|
|
622
|
+
index += 1
|
|
623
|
+
continue
|
|
624
|
+
escaped = current[index + 1]
|
|
625
|
+
short = _JSON_SHORT_ESCAPES.get(escaped)
|
|
626
|
+
if short is not None:
|
|
627
|
+
normalized.append(short)
|
|
628
|
+
index += 2
|
|
629
|
+
changed = True
|
|
630
|
+
continue
|
|
631
|
+
if escaped == ord("u") and index + 6 <= len(current):
|
|
632
|
+
digits = current[index + 2 : index + 6]
|
|
633
|
+
if all(digit in b"0123456789abcdefABCDEF" for digit in digits):
|
|
634
|
+
codepoint = int(digits, 16)
|
|
635
|
+
if codepoint <= 0x7F:
|
|
636
|
+
normalized.append(codepoint)
|
|
637
|
+
index += 6
|
|
638
|
+
changed = True
|
|
639
|
+
continue
|
|
640
|
+
normalized.append(byte)
|
|
641
|
+
index += 1
|
|
642
|
+
if not changed:
|
|
643
|
+
return False
|
|
644
|
+
current = normalized
|
|
645
|
+
return True
|
|
646
|
+
finally:
|
|
647
|
+
for buffer in owned:
|
|
648
|
+
_zero_mutable_buffer(buffer)
|
|
649
|
+
|
|
650
|
+
|
|
376
651
|
def _require_datagov_key_stat(info: os.stat_result) -> None:
|
|
377
652
|
if (
|
|
378
653
|
not stat.S_ISREG(info.st_mode)
|
|
@@ -675,6 +950,8 @@ class PinnedTransport(Protocol):
|
|
|
675
950
|
headers: Mapping[str, str],
|
|
676
951
|
limits: RetrievalLimits,
|
|
677
952
|
byte_range: ByteRange | None = None,
|
|
953
|
+
method: str = "GET",
|
|
954
|
+
body: bytes | None = None,
|
|
678
955
|
) -> TransportResponse:
|
|
679
956
|
"""Connect only to approved_ip while verifying target.hostname in TLS.
|
|
680
957
|
|
|
@@ -791,6 +1068,11 @@ class RetrievalHop:
|
|
|
791
1068
|
etag: str | None = None
|
|
792
1069
|
last_modified: str | None = None
|
|
793
1070
|
peer_attempts: tuple[PeerAttempt, ...] = ()
|
|
1071
|
+
# The ordinary transport digest intentionally remains stable for every existing public
|
|
1072
|
+
# connector. Authenticated verification additionally needs the complete resolver-approved
|
|
1073
|
+
# set to rederive ``resolution_digest`` without either repeating the request or receiving the
|
|
1074
|
+
# credential, so the pinned retriever retains it alongside (but not inside) ``to_dict``.
|
|
1075
|
+
approved_ips: tuple[str, ...] = ()
|
|
794
1076
|
|
|
795
1077
|
def to_dict(self) -> dict[str, object]:
|
|
796
1078
|
# Both range keys are always present, ``None`` when the hop ordered no slice, so a hop
|
|
@@ -811,6 +1093,23 @@ class RetrievalHop:
|
|
|
811
1093
|
"peer_attempts": [attempt.to_dict() for attempt in self.peer_attempts],
|
|
812
1094
|
}
|
|
813
1095
|
|
|
1096
|
+
def authenticated_attestation_dict(self) -> dict[str, object]:
|
|
1097
|
+
"""Return the complete secret-free facts proved by the pinned HTTPS boundary."""
|
|
1098
|
+
|
|
1099
|
+
if not self.approved_ips:
|
|
1100
|
+
raise AcquisitionSecurityError(
|
|
1101
|
+
"TRANSPORT_EVIDENCE",
|
|
1102
|
+
"authenticated hop omitted its resolver-approved address set",
|
|
1103
|
+
)
|
|
1104
|
+
return {
|
|
1105
|
+
**self.to_dict(),
|
|
1106
|
+
"approved_ips": list(self.approved_ips),
|
|
1107
|
+
# A hop is constructed only after both checks pass. Retaining the predicates makes
|
|
1108
|
+
# that invariant explicit and independently parseable in the verifier evidence.
|
|
1109
|
+
"tls_verified": True,
|
|
1110
|
+
"hostname_verified": True,
|
|
1111
|
+
}
|
|
1112
|
+
|
|
814
1113
|
|
|
815
1114
|
@dataclass(frozen=True)
|
|
816
1115
|
class ProbeTransport:
|
|
@@ -884,6 +1183,10 @@ class RetrievedBytes:
|
|
|
884
1183
|
transport_evidence_digest: str
|
|
885
1184
|
complete_size_bytes: int | None = None
|
|
886
1185
|
rate_observation: DatagovRateObservation | None = None
|
|
1186
|
+
# Link pagination needs the terminal headers while it is in flight. They deliberately
|
|
1187
|
+
# remain outside ``RetrievalHop`` and its durable digest: publisher-controlled header text
|
|
1188
|
+
# is not receipt evidence and may never be persisted by the adapter.
|
|
1189
|
+
response_headers: tuple[tuple[str, str], ...] = ()
|
|
887
1190
|
|
|
888
1191
|
@property
|
|
889
1192
|
def total_response_body_size_bytes(self) -> int:
|
|
@@ -1189,6 +1492,67 @@ class PinnedHttpsRetriever:
|
|
|
1189
1492
|
datagov_response_observed=on_response_observed,
|
|
1190
1493
|
)
|
|
1191
1494
|
|
|
1495
|
+
def retrieve_authenticated(
|
|
1496
|
+
self,
|
|
1497
|
+
url: str,
|
|
1498
|
+
authorization: AuthenticatedApiKeyAuthorization,
|
|
1499
|
+
) -> RetrievedBytes:
|
|
1500
|
+
"""Perform one exact-origin CSV GET and consume ``authorization`` on every outcome."""
|
|
1501
|
+
|
|
1502
|
+
if not isinstance(authorization, AuthenticatedApiKeyAuthorization):
|
|
1503
|
+
raise AcquisitionSecurityError(
|
|
1504
|
+
"AUTHENTICATED_TOKEN",
|
|
1505
|
+
"strict authenticated API-key authorization is required",
|
|
1506
|
+
)
|
|
1507
|
+
try:
|
|
1508
|
+
authorization.validate_target(url)
|
|
1509
|
+
try:
|
|
1510
|
+
retrieved = self._fetch(
|
|
1511
|
+
url,
|
|
1512
|
+
byte_range=None,
|
|
1513
|
+
presence_probe=False,
|
|
1514
|
+
datagov_authorization=None,
|
|
1515
|
+
authenticated_authorization=authorization,
|
|
1516
|
+
)
|
|
1517
|
+
except AcquisitionSecurityError as error:
|
|
1518
|
+
if error.code.startswith("AUTHENTICATED_"):
|
|
1519
|
+
raise
|
|
1520
|
+
raise AcquisitionSecurityError(
|
|
1521
|
+
"AUTHENTICATED_RETRIEVAL",
|
|
1522
|
+
"authenticated retrieval failed before content could be retained",
|
|
1523
|
+
) from None
|
|
1524
|
+
except Exception:
|
|
1525
|
+
raise AcquisitionSecurityError(
|
|
1526
|
+
"AUTHENTICATED_TRANSPORT",
|
|
1527
|
+
"authenticated transport failed before content could be retained",
|
|
1528
|
+
) from None
|
|
1529
|
+
if retrieved.media_type not in DIRECT_FETCH_MEDIA_TYPES["csv"]:
|
|
1530
|
+
raise AcquisitionSecurityError(
|
|
1531
|
+
"AUTHENTICATED_MEDIA_TYPE",
|
|
1532
|
+
"authenticated response is not an admitted CSV media type",
|
|
1533
|
+
)
|
|
1534
|
+
return retrieved
|
|
1535
|
+
finally:
|
|
1536
|
+
authorization.close()
|
|
1537
|
+
|
|
1538
|
+
def retrieve_frozen_authenticated_request(
|
|
1539
|
+
self, url: str, *, method: str, headers: Mapping[str, str], body: bytes | None
|
|
1540
|
+
) -> RetrievedBytes:
|
|
1541
|
+
"""Run a prevalidated generic adapter request through the pinned HTTPS machinery."""
|
|
1542
|
+
if method not in {"GET", "POST"} or (method == "GET" and body is not None):
|
|
1543
|
+
raise AcquisitionSecurityError(
|
|
1544
|
+
"AUTHENTICATED_REQUEST", "frozen request method is invalid"
|
|
1545
|
+
)
|
|
1546
|
+
return self._fetch(
|
|
1547
|
+
url,
|
|
1548
|
+
byte_range=None,
|
|
1549
|
+
presence_probe=False,
|
|
1550
|
+
datagov_authorization=None,
|
|
1551
|
+
frozen_headers=headers,
|
|
1552
|
+
frozen_method=method,
|
|
1553
|
+
frozen_body=body,
|
|
1554
|
+
)
|
|
1555
|
+
|
|
1192
1556
|
def probe_presence(self, url: str) -> bool:
|
|
1193
1557
|
"""Ask one address whether it is published, keep nothing, and answer yes or no.
|
|
1194
1558
|
|
|
@@ -1246,9 +1610,13 @@ class PinnedHttpsRetriever:
|
|
|
1246
1610
|
byte_range: ByteRange | None,
|
|
1247
1611
|
presence_probe: bool,
|
|
1248
1612
|
datagov_authorization: DatagovV4Authorization | None,
|
|
1613
|
+
authenticated_authorization: AuthenticatedApiKeyAuthorization | None = None,
|
|
1249
1614
|
datagov_response_observed: (
|
|
1250
1615
|
Callable[[int, int, DatagovRateObservation | None], None] | None
|
|
1251
1616
|
) = None,
|
|
1617
|
+
frozen_headers: Mapping[str, str] | None = None,
|
|
1618
|
+
frozen_method: str = "GET",
|
|
1619
|
+
frozen_body: bytes | None = None,
|
|
1252
1620
|
) -> RetrievedBytes:
|
|
1253
1621
|
started = float(self._clock())
|
|
1254
1622
|
current_url = url
|
|
@@ -1256,7 +1624,11 @@ class PinnedHttpsRetriever:
|
|
|
1256
1624
|
aggregate_response_bytes = 0
|
|
1257
1625
|
requests = 0
|
|
1258
1626
|
deadline = started + self._limits.total_timeout_seconds
|
|
1259
|
-
redirect_budget =
|
|
1627
|
+
redirect_budget = (
|
|
1628
|
+
0
|
|
1629
|
+
if datagov_authorization is not None or authenticated_authorization is not None
|
|
1630
|
+
else self._limits.max_redirects
|
|
1631
|
+
)
|
|
1260
1632
|
for redirect_count in range(redirect_budget + 1):
|
|
1261
1633
|
self._check_total_timeout(started)
|
|
1262
1634
|
target = validate_public_https_url(
|
|
@@ -1268,12 +1640,22 @@ class PinnedHttpsRetriever:
|
|
|
1268
1640
|
# A presence probe asks for anything, because it admits anything: sending the
|
|
1269
1641
|
# acquisition allowlist would invite a 406 for a run that is published and
|
|
1270
1642
|
# fine, which is the same false failure the admission exemption removes.
|
|
1271
|
-
"Accept":
|
|
1643
|
+
"Accept": (
|
|
1644
|
+
"*/*"
|
|
1645
|
+
if presence_probe
|
|
1646
|
+
else (
|
|
1647
|
+
", ".join(sorted(DIRECT_FETCH_MEDIA_TYPES["csv"]))
|
|
1648
|
+
if authenticated_authorization is not None
|
|
1649
|
+
else _accept_header(self._limits)
|
|
1650
|
+
)
|
|
1651
|
+
),
|
|
1272
1652
|
"Accept-Encoding": "identity",
|
|
1273
1653
|
"Connection": "close",
|
|
1274
1654
|
"Host": urlsplit(target.normalized_url).netloc,
|
|
1275
1655
|
"User-Agent": self._limits.user_agent,
|
|
1276
1656
|
}
|
|
1657
|
+
if frozen_headers is not None:
|
|
1658
|
+
headers.update(frozen_headers)
|
|
1277
1659
|
if datagov_authorization is not None:
|
|
1278
1660
|
name, value = datagov_authorization.header_for(target.normalized_url)
|
|
1279
1661
|
headers[name] = value
|
|
@@ -1296,7 +1678,18 @@ class PinnedHttpsRetriever:
|
|
|
1296
1678
|
deadline,
|
|
1297
1679
|
remaining_aggregate_bytes=remaining_aggregate_bytes,
|
|
1298
1680
|
)
|
|
1681
|
+
if authenticated_authorization is not None:
|
|
1682
|
+
attempt_limits = replace(
|
|
1683
|
+
attempt_limits,
|
|
1684
|
+
max_redirects=0,
|
|
1685
|
+
max_requests=1,
|
|
1686
|
+
)
|
|
1299
1687
|
except AcquisitionSecurityError as exc:
|
|
1688
|
+
if authenticated_authorization is not None:
|
|
1689
|
+
raise AcquisitionSecurityError(
|
|
1690
|
+
"AUTHENTICATED_TRANSPORT",
|
|
1691
|
+
"authenticated transport failed before content could be retained",
|
|
1692
|
+
) from None
|
|
1300
1693
|
raise PeerAttemptError(
|
|
1301
1694
|
exc.code,
|
|
1302
1695
|
exc.detail,
|
|
@@ -1307,14 +1700,47 @@ class PinnedHttpsRetriever:
|
|
|
1307
1700
|
# One budget, one clock: every peer and limiter wait is bounded by the
|
|
1308
1701
|
# same absolute instant. A fallback never starts a second DNS epoch.
|
|
1309
1702
|
with self._limiter.acquire(target.hostname, deadline=deadline):
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1703
|
+
request_headers = headers
|
|
1704
|
+
if authenticated_authorization is None:
|
|
1705
|
+
request_kwargs = {"byte_range": byte_range}
|
|
1706
|
+
if (
|
|
1707
|
+
frozen_method != "GET"
|
|
1708
|
+
or frozen_body is not None
|
|
1709
|
+
or frozen_headers is not None
|
|
1710
|
+
):
|
|
1711
|
+
request_kwargs.update(
|
|
1712
|
+
{"method": frozen_method, "body": frozen_body}
|
|
1713
|
+
)
|
|
1714
|
+
response = self._transport.request(
|
|
1715
|
+
target,
|
|
1716
|
+
approved_ip=approved_ip,
|
|
1717
|
+
headers=headers,
|
|
1718
|
+
limits=attempt_limits,
|
|
1719
|
+
**request_kwargs,
|
|
1720
|
+
)
|
|
1721
|
+
else:
|
|
1722
|
+
request_headers = dict(headers)
|
|
1723
|
+
try:
|
|
1724
|
+
with authenticated_authorization.render_header(
|
|
1725
|
+
target.normalized_url
|
|
1726
|
+
) as (name, value):
|
|
1727
|
+
request_headers[name] = value
|
|
1728
|
+
response = self._transport.request(
|
|
1729
|
+
target,
|
|
1730
|
+
approved_ip=approved_ip,
|
|
1731
|
+
headers=request_headers,
|
|
1732
|
+
limits=attempt_limits,
|
|
1733
|
+
byte_range=byte_range,
|
|
1734
|
+
)
|
|
1735
|
+
finally:
|
|
1736
|
+
request_headers.clear()
|
|
1737
|
+
name = value = ""
|
|
1317
1738
|
except AcquisitionSecurityError as exc:
|
|
1739
|
+
if authenticated_authorization is not None:
|
|
1740
|
+
raise AcquisitionSecurityError(
|
|
1741
|
+
"AUTHENTICATED_TRANSPORT",
|
|
1742
|
+
"authenticated transport failed before content could be retained",
|
|
1743
|
+
) from None
|
|
1318
1744
|
if exc.code == "TRANSPORT_FAILURE" and not isinstance(
|
|
1319
1745
|
exc, TransportFailureError
|
|
1320
1746
|
):
|
|
@@ -1370,6 +1796,13 @@ class PinnedHttpsRetriever:
|
|
|
1370
1796
|
raise PeerAttemptError(
|
|
1371
1797
|
failure_code, detail, tuple(peer_attempts), hops=tuple(hops)
|
|
1372
1798
|
) from None
|
|
1799
|
+
except Exception:
|
|
1800
|
+
if authenticated_authorization is not None:
|
|
1801
|
+
raise AcquisitionSecurityError(
|
|
1802
|
+
"AUTHENTICATED_TRANSPORT",
|
|
1803
|
+
"authenticated transport failed before content could be retained",
|
|
1804
|
+
) from None
|
|
1805
|
+
raise
|
|
1373
1806
|
peer_attempts.append(
|
|
1374
1807
|
PeerAttempt(
|
|
1375
1808
|
approved_ip=approved_ip,
|
|
@@ -1447,6 +1880,15 @@ class PinnedHttpsRetriever:
|
|
|
1447
1880
|
aggregate_response_bytes,
|
|
1448
1881
|
datagov_rate_observation,
|
|
1449
1882
|
)
|
|
1883
|
+
elif authenticated_authorization is not None:
|
|
1884
|
+
authenticated_authorization.reject_echoed_headers(normalized_headers)
|
|
1885
|
+
if response.status in REDIRECT_STATUSES:
|
|
1886
|
+
raise AcquisitionSecurityError(
|
|
1887
|
+
"AUTHENTICATED_REDIRECT",
|
|
1888
|
+
"authenticated responses never follow redirects",
|
|
1889
|
+
)
|
|
1890
|
+
authenticated_authorization.reject_echoed_response(response.body)
|
|
1891
|
+
datagov_rate_observation = None
|
|
1450
1892
|
else:
|
|
1451
1893
|
datagov_rate_observation = None
|
|
1452
1894
|
if len(response.body) > attempt_limits.max_response_bytes:
|
|
@@ -1520,6 +1962,7 @@ class PinnedHttpsRetriever:
|
|
|
1520
1962
|
etag=self._response_header(response, "etag", peer_attempts, hops),
|
|
1521
1963
|
last_modified=self._response_header(response, "last-modified", peer_attempts, hops),
|
|
1522
1964
|
peer_attempts=tuple(peer_attempts),
|
|
1965
|
+
approved_ips=target.approved_ips,
|
|
1523
1966
|
)
|
|
1524
1967
|
hops.append(hop)
|
|
1525
1968
|
# The delivery check. A slice is delivered only when the server answered 206,
|
|
@@ -1672,6 +2115,7 @@ class PinnedHttpsRetriever:
|
|
|
1672
2115
|
transport_evidence_digest=evidence_digest,
|
|
1673
2116
|
complete_size_bytes=complete_size_bytes,
|
|
1674
2117
|
rate_observation=datagov_rate_observation,
|
|
2118
|
+
response_headers=tuple(normalized_headers),
|
|
1675
2119
|
)
|
|
1676
2120
|
raise AcquisitionSecurityError("REDIRECT_LIMIT", "redirect loop did not terminate")
|
|
1677
2121
|
|
|
@@ -1805,6 +2249,8 @@ class StdlibPinnedTransport:
|
|
|
1805
2249
|
headers: Mapping[str, str],
|
|
1806
2250
|
limits: RetrievalLimits,
|
|
1807
2251
|
byte_range: ByteRange | None = None,
|
|
2252
|
+
method: str = "GET",
|
|
2253
|
+
body: bytes | None = None,
|
|
1808
2254
|
) -> TransportResponse:
|
|
1809
2255
|
outgoing = dict(headers)
|
|
1810
2256
|
if byte_range is not None:
|
|
@@ -1819,7 +2265,7 @@ class StdlibPinnedTransport:
|
|
|
1819
2265
|
)
|
|
1820
2266
|
total = 0
|
|
1821
2267
|
try:
|
|
1822
|
-
connection.request(
|
|
2268
|
+
connection.request(method, target.request_target, body=body, headers=outgoing)
|
|
1823
2269
|
if connection.sock is not None:
|
|
1824
2270
|
connection.sock.settimeout(
|
|
1825
2271
|
_remaining_socket_timeout(
|