mostlyright-data 0.9.0__py3-none-any.whl
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_harness/__init__.py +158 -0
- mostlyright/data_harness/acquisition/__init__.py +55 -0
- mostlyright/data_harness/acquisition/http.py +2773 -0
- mostlyright/data_harness/acquisition/parsing.py +809 -0
- mostlyright/data_harness/acquisition/ranges.py +495 -0
- mostlyright/data_harness/acquisition/result_download.py +360 -0
- mostlyright/data_harness/acquisition/retention_admission.py +248 -0
- mostlyright/data_harness/acquisition/sandbox.py +4888 -0
- mostlyright/data_harness/acquisition/url_policy.py +530 -0
- mostlyright/data_harness/agent_runtime.py +2743 -0
- mostlyright/data_harness/assets/logo-ink.svg +31 -0
- mostlyright/data_harness/backends/__init__.py +28 -0
- mostlyright/data_harness/backends/pandas_backend.py +350 -0
- mostlyright/data_harness/backends/polars_backend.py +366 -0
- mostlyright/data_harness/backends/protocol.py +124 -0
- mostlyright/data_harness/backends/reference.py +83 -0
- mostlyright/data_harness/backends/registry.py +55 -0
- mostlyright/data_harness/backends/restrictions.py +126 -0
- mostlyright/data_harness/canonical.py +333 -0
- mostlyright/data_harness/catalog_job.py +625 -0
- mostlyright/data_harness/cli.py +5398 -0
- mostlyright/data_harness/contracts.py +53 -0
- mostlyright/data_harness/coordinator.py +1307 -0
- mostlyright/data_harness/deploy.py +924 -0
- mostlyright/data_harness/deploy_target.py +312 -0
- mostlyright/data_harness/deployment_evidence.py +1067 -0
- mostlyright/data_harness/event_presentation.py +576 -0
- mostlyright/data_harness/events.py +2152 -0
- mostlyright/data_harness/fast_delimited.py +239 -0
- mostlyright/data_harness/fleet.py +237 -0
- mostlyright/data_harness/formats.py +236 -0
- mostlyright/data_harness/governors.py +1163 -0
- mostlyright/data_harness/hosted_bootstrap.py +972 -0
- mostlyright/data_harness/hosted_crawler.py +1115 -0
- mostlyright/data_harness/hosted_crawler_container_smoke.py +351 -0
- mostlyright/data_harness/hosted_crawler_fetch.py +423 -0
- mostlyright/data_harness/hosted_crawler_job.py +1277 -0
- mostlyright/data_harness/hosted_crawler_protocol.py +676 -0
- mostlyright/data_harness/hosted_dataset.py +1500 -0
- mostlyright/data_harness/hosted_deploy.py +3037 -0
- mostlyright/data_harness/hosted_handoff.py +62 -0
- mostlyright/data_harness/hosted_ingestion_contract.py +504 -0
- mostlyright/data_harness/hosted_ingestion_job.py +356 -0
- mostlyright/data_harness/hosted_ingestion_job_smoke.py +40 -0
- mostlyright/data_harness/hosted_session_container_smoke.py +194 -0
- mostlyright/data_harness/hosted_session_worker.py +3554 -0
- mostlyright/data_harness/hosted_session_worker_job_smoke.py +46 -0
- mostlyright/data_harness/hosted_worker.py +6784 -0
- mostlyright/data_harness/ingestion/__init__.py +56 -0
- mostlyright/data_harness/ingestion/contracts.py +461 -0
- mostlyright/data_harness/ingestion/faults.py +42 -0
- mostlyright/data_harness/ingestion/gcs_store.py +1162 -0
- mostlyright/data_harness/ingestion/spool.py +130 -0
- mostlyright/data_harness/ingestion/store.py +885 -0
- mostlyright/data_harness/key_seam.py +434 -0
- mostlyright/data_harness/linux_process_boundary.py +262 -0
- mostlyright/data_harness/local_contracts.py +2880 -0
- mostlyright/data_harness/local_search/__init__.py +5 -0
- mostlyright/data_harness/local_search/build_index.py +1087 -0
- mostlyright/data_harness/local_search/contracts.py +920 -0
- mostlyright/data_harness/local_search/query_trace.py +266 -0
- mostlyright/data_harness/local_search/retrieval.py +700 -0
- mostlyright/data_harness/local_search/sealed.py +474 -0
- mostlyright/data_harness/local_search/service.py +784 -0
- mostlyright/data_harness/nbrender/CONTRACT.md +212 -0
- mostlyright/data_harness/nbrender/__init__.py +12 -0
- mostlyright/data_harness/nbrender/chrome.py +359 -0
- mostlyright/data_harness/nbrender/code_body.py +266 -0
- mostlyright/data_harness/nbrender/document.py +407 -0
- mostlyright/data_harness/nbrender/frame.py +275 -0
- mostlyright/data_harness/nbrender/interactive.py +337 -0
- mostlyright/data_harness/nbrender/markdown_body.py +477 -0
- mostlyright/data_harness/nbrender/mr_components.py +134 -0
- mostlyright/data_harness/nbrender/outputs_data.py +595 -0
- mostlyright/data_harness/nbrender/outputs_rich.py +906 -0
- mostlyright/data_harness/nbrender/outputs_source.py +260 -0
- mostlyright/data_harness/nbrender/outputs_stage.py +176 -0
- mostlyright/data_harness/nbrender/outputs_text.py +400 -0
- mostlyright/data_harness/nbrender/parse.py +394 -0
- mostlyright/data_harness/nbrender/status.py +40 -0
- mostlyright/data_harness/nbrender/tokens.py +1295 -0
- mostlyright/data_harness/notebook.py +1710 -0
- mostlyright/data_harness/offline.py +2049 -0
- mostlyright/data_harness/operation_registry.py +1007 -0
- mostlyright/data_harness/operator_setup.py +239 -0
- mostlyright/data_harness/pipeline.py +6428 -0
- mostlyright/data_harness/plan_graph.py +2026 -0
- mostlyright/data_harness/preparation/__init__.py +104 -0
- mostlyright/data_harness/preparation/contracts.py +1017 -0
- mostlyright/data_harness/preparation/engine.py +221 -0
- mostlyright/data_harness/preparation/errors.py +14 -0
- mostlyright/data_harness/preparation/gates.py +751 -0
- mostlyright/data_harness/preparation/joins.py +574 -0
- mostlyright/data_harness/preparation/profile.py +384 -0
- mostlyright/data_harness/preparation/table.py +217 -0
- mostlyright/data_harness/preparation/transforms.py +568 -0
- mostlyright/data_harness/progress_events.py +534 -0
- mostlyright/data_harness/readers/__init__.py +46 -0
- mostlyright/data_harness/readers/containers.py +963 -0
- mostlyright/data_harness/readers/contracts.py +542 -0
- mostlyright/data_harness/readers/delimited.py +257 -0
- mostlyright/data_harness/readers/grib2/__init__.py +33 -0
- mostlyright/data_harness/readers/grib2/admission.py +722 -0
- mostlyright/data_harness/readers/grib2/decode.py +1009 -0
- mostlyright/data_harness/readers/grib2/geometry.py +1133 -0
- mostlyright/data_harness/readers/grib2/portable_math.py +501 -0
- mostlyright/data_harness/readers/json_tabular.py +485 -0
- mostlyright/data_harness/readers/registry.py +514 -0
- mostlyright/data_harness/readers/samples/README.md +110 -0
- mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/cities.csv.gz +0 -0
- mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/expected.json +24 -0
- mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/cities.csv.gz +0 -0
- mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/expected.json +24 -0
- mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/cities.tar +0 -0
- mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/expected.json +24 -0
- mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/cities.tar +0 -0
- mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/expected.json +24 -0
- mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/cities.zip +0 -0
- mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/expected.json +25 -0
- mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
- mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/expected.json +25 -0
- mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
- mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/expected.json +25 -0
- mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/cities.csv +3 -0
- mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/expected.json +23 -0
- mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/cities.tsv +5 -0
- mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/expected.json +25 -0
- mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/expected.json +30 -0
- mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/observations.csv +5 -0
- mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/expected.json +44 -0
- mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/stations.json +1 -0
- mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/expected.json +48 -0
- mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/observations.ndjson +4 -0
- mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/cities.xlsx +0 -0
- mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/expected.json +24 -0
- mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/expected.json +27 -0
- mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
- mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/expected.json +27 -0
- mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
- mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/README.md +20 -0
- mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/expected.json +55 -0
- mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/gfs-2m-temperature.grib2 +0 -0
- mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/expected.json +54 -0
- mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
- mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/expected.json +54 -0
- mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/hrrr-categorical-rain.grib2 +0 -0
- mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/expected.json +54 -0
- mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
- mostlyright/data_harness/readers/samples.py +582 -0
- mostlyright/data_harness/readers/spreadsheet.py +803 -0
- mostlyright/data_harness/readers/tabular.py +510 -0
- mostlyright/data_harness/recipe.py +5321 -0
- mostlyright/data_harness/repair/__init__.py +78 -0
- mostlyright/data_harness/repair/adapters.py +274 -0
- mostlyright/data_harness/repair/contracts.py +872 -0
- mostlyright/data_harness/repair/coordinator.py +1099 -0
- mostlyright/data_harness/repair/errors.py +16 -0
- mostlyright/data_harness/review.py +2533 -0
- mostlyright/data_harness/rowset.py +283 -0
- mostlyright/data_harness/serving.py +1975 -0
- mostlyright/data_harness/serving_edge.py +590 -0
- mostlyright/data_harness/serving_http.py +1031 -0
- mostlyright/data_harness/session_probes.py +759 -0
- mostlyright/data_harness/signing.py +101 -0
- mostlyright/data_harness/source_discovery.py +898 -0
- mostlyright/data_harness/sources/__init__.py +209 -0
- mostlyright/data_harness/sources/_adapter_steps.py +213 -0
- mostlyright/data_harness/sources/adapters.py +1214 -0
- mostlyright/data_harness/sources/cadence.py +1428 -0
- mostlyright/data_harness/sources/cadence_emission.py +453 -0
- mostlyright/data_harness/sources/cadence_history.py +546 -0
- mostlyright/data_harness/sources/catalog/__init__.py +17 -0
- mostlyright/data_harness/sources/catalog/admission.py +477 -0
- mostlyright/data_harness/sources/catalog/authoring.py +1701 -0
- mostlyright/data_harness/sources/catalog/authoring_policy.py +701 -0
- mostlyright/data_harness/sources/catalog/authoring_shards.py +1217 -0
- mostlyright/data_harness/sources/catalog/bounded_io.py +231 -0
- mostlyright/data_harness/sources/catalog/channel.py +523 -0
- mostlyright/data_harness/sources/catalog/channel_client.py +296 -0
- mostlyright/data_harness/sources/catalog/contracts.py +825 -0
- mostlyright/data_harness/sources/catalog/coverage.py +137 -0
- mostlyright/data_harness/sources/catalog/delta.py +1340 -0
- mostlyright/data_harness/sources/catalog/embedding.py +532 -0
- mostlyright/data_harness/sources/catalog/entry_v2.py +1182 -0
- mostlyright/data_harness/sources/catalog/fill.py +3889 -0
- mostlyright/data_harness/sources/catalog/fill_partitions.py +459 -0
- mostlyright/data_harness/sources/catalog/fill_staging.py +1105 -0
- mostlyright/data_harness/sources/catalog/gating.py +374 -0
- mostlyright/data_harness/sources/catalog/generation_receipt.py +1607 -0
- mostlyright/data_harness/sources/catalog/harvest/__init__.py +7 -0
- mostlyright/data_harness/sources/catalog/harvest/ckan.py +384 -0
- mostlyright/data_harness/sources/catalog/harvest/datagov_v4.py +798 -0
- mostlyright/data_harness/sources/catalog/harvest/protocol.py +964 -0
- mostlyright/data_harness/sources/catalog/harvest/sdmx.py +445 -0
- mostlyright/data_harness/sources/catalog/harvest/stac.py +384 -0
- mostlyright/data_harness/sources/catalog/health.py +447 -0
- mostlyright/data_harness/sources/catalog/hosted_catalog.py +105 -0
- mostlyright/data_harness/sources/catalog/identity_history.py +1549 -0
- mostlyright/data_harness/sources/catalog/neural.py +1618 -0
- mostlyright/data_harness/sources/catalog/packed_catalog.py +2345 -0
- mostlyright/data_harness/sources/catalog/packed_retrieval.py +1517 -0
- mostlyright/data_harness/sources/catalog/packed_writer.py +2802 -0
- mostlyright/data_harness/sources/catalog/query_trace.py +1037 -0
- mostlyright/data_harness/sources/catalog/recommend.py +171 -0
- mostlyright/data_harness/sources/catalog/retrieval.py +230 -0
- mostlyright/data_harness/sources/catalog/retrieval_manifest.py +995 -0
- mostlyright/data_harness/sources/catalog/rights_decisions.py +254 -0
- mostlyright/data_harness/sources/catalog/sealed.py +560 -0
- mostlyright/data_harness/sources/catalog/search.py +230 -0
- mostlyright/data_harness/sources/catalog/streaming_delta.py +1097 -0
- mostlyright/data_harness/sources/catalog/update.py +891 -0
- mostlyright/data_harness/sources/collections.py +815 -0
- mostlyright/data_harness/sources/contracts.py +2223 -0
- mostlyright/data_harness/sources/deletion.py +761 -0
- mostlyright/data_harness/sources/fitness.py +162 -0
- mostlyright/data_harness/sources/governance.py +163 -0
- mostlyright/data_harness/sources/hosted.py +173 -0
- mostlyright/data_harness/sources/integration.py +218 -0
- mostlyright/data_harness/sources/range_reader.py +418 -0
- mostlyright/data_harness/sources/registry.py +514 -0
- mostlyright/data_harness/sources/rights_rule.py +59 -0
- mostlyright/data_harness/sources/source_cadence_vectors.v1.json +1 -0
- mostlyright/data_harness/sources/sports.py +521 -0
- mostlyright/data_harness/sources/stream.py +524 -0
- mostlyright/data_harness/sources/stream_connector.py +418 -0
- mostlyright/data_harness/sources/stream_recorder.py +1404 -0
- mostlyright/data_harness/studio_boundary.py +2019 -0
- mostlyright/data_harness/thin/__init__.py +37 -0
- mostlyright/data_harness/thin/acquire.py +1137 -0
- mostlyright/data_harness/thin/acquire_cancel.py +579 -0
- mostlyright/data_harness/thin/approvals.py +617 -0
- mostlyright/data_harness/thin/commands.py +406 -0
- mostlyright/data_harness/thin/download.py +194 -0
- mostlyright/data_harness/thin/narrative.py +589 -0
- mostlyright/data_harness/thin/parity.py +1070 -0
- mostlyright/data_harness/thin/propose.py +2759 -0
- mostlyright/data_harness/thin/research.py +1663 -0
- mostlyright/data_harness/thin/router.py +924 -0
- mostlyright/data_harness/thin/runs.py +519 -0
- mostlyright/data_harness/thin/session.py +281 -0
- mostlyright/data_harness/thin/stream.py +501 -0
- mostlyright/data_harness/thin/transport.py +187 -0
- mostlyright/data_harness/thin/vocabulary.py +368 -0
- mostlyright/data_harness/thin/workers.py +164 -0
- mostlyright/data_harness/ucum/TABLE-PIN.json +40 -0
- mostlyright/data_harness/ucum/ucum-subset.v1.json +632 -0
- mostlyright/data_harness/unit_flow.py +927 -0
- mostlyright/data_harness/units.py +572 -0
- mostlyright/data_harness/ux/__init__.py +9 -0
- mostlyright/data_harness/ux/approve.py +485 -0
- mostlyright/data_harness/ux/author_yaml.py +597 -0
- mostlyright/data_harness/ux/cloud_auth.py +447 -0
- mostlyright/data_harness/ux/commands/__init__.py +260 -0
- mostlyright/data_harness/ux/commands/approve.py +136 -0
- mostlyright/data_harness/ux/commands/auth.py +744 -0
- mostlyright/data_harness/ux/commands/author.py +79 -0
- mostlyright/data_harness/ux/commands/catalog_author.py +403 -0
- mostlyright/data_harness/ux/commands/catalog_fill.py +523 -0
- mostlyright/data_harness/ux/commands/catalog_harvest.py +545 -0
- mostlyright/data_harness/ux/commands/catalog_publish.py +1838 -0
- mostlyright/data_harness/ux/commands/catalog_search.py +71 -0
- mostlyright/data_harness/ux/commands/catalog_update.py +437 -0
- mostlyright/data_harness/ux/commands/deploy.py +134 -0
- mostlyright/data_harness/ux/commands/deploy_dataset.py +98 -0
- mostlyright/data_harness/ux/commands/deploy_plan.py +105 -0
- mostlyright/data_harness/ux/commands/deploy_status.py +104 -0
- mostlyright/data_harness/ux/commands/diff.py +74 -0
- mostlyright/data_harness/ux/commands/index.py +84 -0
- mostlyright/data_harness/ux/commands/inventory.py +47 -0
- mostlyright/data_harness/ux/commands/list_builds.py +143 -0
- mostlyright/data_harness/ux/commands/login.py +63 -0
- mostlyright/data_harness/ux/commands/peek.py +236 -0
- mostlyright/data_harness/ux/commands/plan_check.py +90 -0
- mostlyright/data_harness/ux/commands/preflight.py +97 -0
- mostlyright/data_harness/ux/commands/record.py +107 -0
- mostlyright/data_harness/ux/commands/review_setup.py +47 -0
- mostlyright/data_harness/ux/commands/search.py +440 -0
- mostlyright/data_harness/ux/commands/show.py +61 -0
- mostlyright/data_harness/ux/commands/whoami.py +37 -0
- mostlyright/data_harness/ux/credential_native.py +551 -0
- mostlyright/data_harness/ux/credential_store.py +1055 -0
- mostlyright/data_harness/ux/credentials.py +631 -0
- mostlyright/data_harness/ux/diffing.py +444 -0
- mostlyright/data_harness/ux/headline.py +671 -0
- mostlyright/data_harness/ux/hosted_acquisition.py +974 -0
- mostlyright/data_harness/ux/hosted_run_status.py +619 -0
- mostlyright/data_harness/ux/inventory.py +427 -0
- mostlyright/data_harness/ux/local_review.py +375 -0
- mostlyright/data_harness/ux/login.py +691 -0
- mostlyright/data_harness/ux/path_kind.py +147 -0
- mostlyright/data_harness/ux/peek.py +1000 -0
- mostlyright/data_harness/ux/plain_file.py +178 -0
- mostlyright/data_harness/ux/plan_check.py +311 -0
- mostlyright/data_harness/ux/preflight.py +918 -0
- mostlyright/data_harness/ux/readers.py +1124 -0
- mostlyright/data_harness/ux/remediation.py +2195 -0
- mostlyright/data_harness/ux/render.py +657 -0
- mostlyright/data_harness/ux/workload.py +1077 -0
- mostlyright/data_harness/viewer.py +3713 -0
- mostlyright/data_harness/visual_run/__init__.py +83 -0
- mostlyright/data_harness/visual_run/authoring.py +235 -0
- mostlyright/data_harness/visual_run/contracts.py +673 -0
- mostlyright/data_harness/visual_run/materialize.py +486 -0
- mostlyright/data_harness/visual_run/observations.py +874 -0
- mostlyright/data_harness/visual_run/query.py +259 -0
- mostlyright/data_harness/visual_run/reducer.py +280 -0
- mostlyright/data_harness/visual_run/sdk.py +892 -0
- mostlyright/data_harness/visual_run/store.py +584 -0
- mostlyright/data_harness/visual_run/transport.py +239 -0
- mostlyright/data_harness/watch.py +2999 -0
- mostlyright_data-0.9.0.dist-info/METADATA +607 -0
- mostlyright_data-0.9.0.dist-info/RECORD +314 -0
- mostlyright_data-0.9.0.dist-info/WHEEL +4 -0
- mostlyright_data-0.9.0.dist-info/entry_points.txt +12 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"""The one empirical-observation emitter both governed producers share.
|
|
2
|
+
|
|
3
|
+
Two producers probe the same kind of source. The trusted Builder drives a hosted Courier
|
|
4
|
+
session and reads Studio's append coordinate; ``mr-data acquire`` drives a local Clean room and
|
|
5
|
+
reads an on-disk history. What they may state about the publisher is identical, so the rule
|
|
6
|
+
that turns one probe into one :class:`~mostlyright.data_harness.sources.cadence.
|
|
7
|
+
EmpiricalSourceObservation` lives here once rather than twice.
|
|
8
|
+
|
|
9
|
+
That is not tidiness. The closure bar for empirical cadence is that local and hosted inference
|
|
10
|
+
replay to the same result, and two restatements of the same mapping are two things that can
|
|
11
|
+
drift apart between releases while every test on either side still passes. One implementation
|
|
12
|
+
makes byte-identity a property of the code rather than a coincidence a test has to keep
|
|
13
|
+
re-checking.
|
|
14
|
+
|
|
15
|
+
This module records evidence and infers nothing. It reads no clock, no network, no environment,
|
|
16
|
+
and no file: every timestamp, digest, and coordinate arrives from the caller that authenticated
|
|
17
|
+
it. Deciding *what to probe next* remains Studio's, and releasing anything remains Studio's.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import re
|
|
23
|
+
from collections.abc import Mapping
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from types import MappingProxyType
|
|
26
|
+
from typing import Any
|
|
27
|
+
|
|
28
|
+
from mostlyright.data_harness.sources.cadence import (
|
|
29
|
+
SOURCE_CADENCE_ALGORITHM_DIGEST,
|
|
30
|
+
CadenceContractError,
|
|
31
|
+
EmpiricalSourceObservation,
|
|
32
|
+
SourceFrontier,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
#: The exact key set of a cadence authority document, whichever coordinator states it.
|
|
36
|
+
#: ``schema_version`` names the stating authority -- Studio's contract hosted, the local
|
|
37
|
+
#: acquisition contract locally -- and the remaining eleven fields are the epoch identity the
|
|
38
|
+
#: observation contract fixes. A producer that accepted a different key set could bind an
|
|
39
|
+
#: observation to an identity its coordinator never stated.
|
|
40
|
+
SOURCE_CADENCE_AUTHORITY_FIELDS = frozenset(
|
|
41
|
+
{
|
|
42
|
+
"schema_version",
|
|
43
|
+
"inference_epoch",
|
|
44
|
+
"connector_configuration_digest",
|
|
45
|
+
"locator_digest",
|
|
46
|
+
"recipe_digest",
|
|
47
|
+
"source_authority_digest",
|
|
48
|
+
"reader_family_id",
|
|
49
|
+
"reader_family_version",
|
|
50
|
+
"reader_options_digest",
|
|
51
|
+
"frontier_policy_digest",
|
|
52
|
+
"time_interpretation_digest",
|
|
53
|
+
"inference_algorithm_digest",
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# A probe that reached the source and refused reports one closed failure code. These are the
|
|
58
|
+
# only cadence outcomes that code alone can prove. The map is deliberately narrow: a refusal
|
|
59
|
+
# carries no HTTP status, so nothing here may claim ``http_4xx``, ``http_5xx``, ``rate_limited``,
|
|
60
|
+
# or ``source_unavailable`` -- each asserts a response no producer on this path saw.
|
|
61
|
+
# ``HTTP_NOT_FOUND`` is the one code the acquisition layer defines as "this address is not
|
|
62
|
+
# published" (``acquisition/http.py``), so it is the only code that becomes evidence about the
|
|
63
|
+
# publisher rather than about the probe. Every other code stays a ``failed`` probe, which the
|
|
64
|
+
# inference is required to treat as proving nothing about whether an edition exists.
|
|
65
|
+
SOURCE_REACHED_FAILURE_OUTCOMES: Mapping[str, tuple[str, str | None]] = MappingProxyType(
|
|
66
|
+
{
|
|
67
|
+
"HTTP_NOT_FOUND": ("not_published", None),
|
|
68
|
+
"HTTP_STATUS": ("failed", "unknown_failure"),
|
|
69
|
+
"TOTAL_TIMEOUT": ("failed", "timeout"),
|
|
70
|
+
"CONNECT_TIMEOUT": ("failed", "timeout"),
|
|
71
|
+
"READ_TIMEOUT": ("failed", "timeout"),
|
|
72
|
+
"TLS_FAILED": ("failed", "tls_failure"),
|
|
73
|
+
"CONNECT_FAILED": ("failed", "transport_failure"),
|
|
74
|
+
"DNS_FAILED": ("failed", "transport_failure"),
|
|
75
|
+
"MEDIA_TYPE": ("failed", "contract_refused"),
|
|
76
|
+
"BYTE_LIMIT": ("failed", "contract_refused"),
|
|
77
|
+
"REDIRECT_LIMIT": ("failed", "contract_refused"),
|
|
78
|
+
"REDIRECT_POLICY": ("failed", "contract_refused"),
|
|
79
|
+
"CRAWLER_ENDPOINT_MISMATCH": ("failed", "contract_refused"),
|
|
80
|
+
"CRAWLER_MEDIA_TYPE_MISMATCH": ("failed", "contract_refused"),
|
|
81
|
+
"CRAWLER_RESPONSE_INVALID": ("failed", "contract_refused"),
|
|
82
|
+
"CRAWLER_NORMALIZATION_INPUT_INVALID": ("failed", "contract_refused"),
|
|
83
|
+
"READER_FAMILY_UNKNOWN": ("failed", "reader_failure"),
|
|
84
|
+
"READER_DEPENDENCY_UNAVAILABLE": ("failed", "reader_failure"),
|
|
85
|
+
"READER_CONTRACT": ("failed", "reader_failure"),
|
|
86
|
+
"READER_OPTIONS": ("failed", "reader_failure"),
|
|
87
|
+
"READER_ADMISSION": ("failed", "reader_failure"),
|
|
88
|
+
"READER_BUDGET": ("failed", "reader_failure"),
|
|
89
|
+
"READER_MEMBER_MISSING": ("failed", "reader_failure"),
|
|
90
|
+
"READER_MEMBER_AMBIGUOUS": ("failed", "reader_failure"),
|
|
91
|
+
"READER_MEMBER_TYPE": ("failed", "reader_failure"),
|
|
92
|
+
"READER_NESTED_CONTAINER": ("failed", "reader_failure"),
|
|
93
|
+
"READER_SHEET_MISSING": ("failed", "reader_failure"),
|
|
94
|
+
"READER_RANGE": ("failed", "reader_failure"),
|
|
95
|
+
"READER_GRID_UNKNOWN": ("failed", "reader_failure"),
|
|
96
|
+
"READER_PACKING_UNKNOWN": ("failed", "reader_failure"),
|
|
97
|
+
"READER_DECODE": ("failed", "reader_failure"),
|
|
98
|
+
"READER_OUTPUT": ("failed", "reader_failure"),
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
UNCLASSIFIED_FAILURE_OUTCOME: tuple[str, str | None] = ("failed", "unknown_failure")
|
|
102
|
+
# These two refusals provably precede any source byte. Both producers run the Reader
|
|
103
|
+
# memory-boundary assertion and the packaged-sample warm-up before the fetch, and the warm-up
|
|
104
|
+
# decodes the packaged known sample rather than the source. An observation built from either
|
|
105
|
+
# would charge the publisher for a probe of this source that never happened, so they record
|
|
106
|
+
# nothing at all.
|
|
107
|
+
PRE_FETCH_FAILURE_CODES = frozenset({"CRAWLER_MEMORY_BOUNDARY", "READER_WARMUP"})
|
|
108
|
+
|
|
109
|
+
_DIGEST = re.compile(r"^[0-9a-f]{64}$")
|
|
110
|
+
# The frontier every whole-document HTTPS probe records. Neither producer's query contract
|
|
111
|
+
# declares an approved typed event-time field or partition key, so there is no deterministic
|
|
112
|
+
# derivation to record and the contract refuses a frontier on an absent result outright.
|
|
113
|
+
_NO_FRONTIER = SourceFrontier("none", None, "none")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class SourceCadenceEmissionError(ValueError):
|
|
117
|
+
"""Typed refusal raised when a probe cannot be recorded as an exact observation."""
|
|
118
|
+
|
|
119
|
+
def __init__(self, code: str, detail: str) -> None:
|
|
120
|
+
self.code = code
|
|
121
|
+
self.detail = detail
|
|
122
|
+
super().__init__(f"{detail} [{code}]")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def sealed_refusal_absent_outcome(failure_code: str) -> tuple[str, str | None] | None:
|
|
126
|
+
"""Return the absent result a *sealed* refusal supports, or ``None`` for none.
|
|
127
|
+
|
|
128
|
+
A sealed refusal is a completed result: the Courier reached the source, refused, and bound
|
|
129
|
+
that refusal into a document it returned. Reaching the source is therefore proven even for
|
|
130
|
+
a code this map does not classify, so an unclassified code is still a failed probe and
|
|
131
|
+
records ``unknown_failure`` rather than a classification the producer cannot support. It
|
|
132
|
+
can never become ``not_published``.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
if failure_code in PRE_FETCH_FAILURE_CODES:
|
|
136
|
+
return None
|
|
137
|
+
return SOURCE_REACHED_FAILURE_OUTCOMES.get(failure_code, UNCLASSIFIED_FAILURE_OUTCOME)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def local_refusal_absent_outcome(failure_code: str) -> tuple[str, str | None] | None:
|
|
141
|
+
"""Return the absent result a *local* refusal supports, or ``None`` for none.
|
|
142
|
+
|
|
143
|
+
The map and the pre-fetch set are the hosted path's own; one line differs, and the reason is
|
|
144
|
+
evidence rather than preference. A hosted refusal arrives inside a document the Courier
|
|
145
|
+
sealed around its own failure, which is what proves the source was reached. A local
|
|
146
|
+
acquisition raises its refusal as an exception with no such seal, so an unclassified code --
|
|
147
|
+
a mismatched attestation, a malformed acquisition document, a snapshot that could not be
|
|
148
|
+
sealed -- proves only that this machine stopped, and says nothing about the publisher.
|
|
149
|
+
Recording it would charge the source for a probe it never answered, so it records nothing.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
if failure_code in PRE_FETCH_FAILURE_CODES:
|
|
153
|
+
return None
|
|
154
|
+
return SOURCE_REACHED_FAILURE_OUTCOMES.get(failure_code)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@dataclass(frozen=True)
|
|
158
|
+
class SourceCadenceAuthority:
|
|
159
|
+
"""The epoch identity one coordinator fixed for one source, as stated by that coordinator."""
|
|
160
|
+
|
|
161
|
+
inference_epoch: str
|
|
162
|
+
connector_configuration_digest: str
|
|
163
|
+
locator_digest: str
|
|
164
|
+
recipe_digest: str
|
|
165
|
+
source_authority_digest: str
|
|
166
|
+
reader_family_id: str
|
|
167
|
+
reader_family_version: str
|
|
168
|
+
reader_options_digest: str
|
|
169
|
+
frontier_policy_digest: str
|
|
170
|
+
time_interpretation_digest: str
|
|
171
|
+
inference_algorithm_digest: str
|
|
172
|
+
schema_version: str
|
|
173
|
+
|
|
174
|
+
@classmethod
|
|
175
|
+
def from_mapping(cls, value: Any, *, schema_version: str) -> SourceCadenceAuthority:
|
|
176
|
+
"""Admit exactly the stated authority contract, or refuse it whole.
|
|
177
|
+
|
|
178
|
+
``inference_algorithm_digest`` must be this build's algorithm digest. A coordinator
|
|
179
|
+
naming any other algorithm is naming an inference this producer cannot replay, and an
|
|
180
|
+
observation bound to it would enter a history no consumer here can validate.
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
if not isinstance(value, Mapping) or set(value) != SOURCE_CADENCE_AUTHORITY_FIELDS:
|
|
184
|
+
raise SourceCadenceEmissionError(
|
|
185
|
+
"SOURCE_CADENCE_AUTHORITY_INVALID",
|
|
186
|
+
"the cadence authority does not have the exact versioned key set",
|
|
187
|
+
)
|
|
188
|
+
payload = dict(value)
|
|
189
|
+
if payload["schema_version"] != schema_version:
|
|
190
|
+
raise SourceCadenceEmissionError(
|
|
191
|
+
"SOURCE_CADENCE_AUTHORITY_INVALID",
|
|
192
|
+
"the cadence authority states another contract version",
|
|
193
|
+
)
|
|
194
|
+
if payload["inference_algorithm_digest"] != SOURCE_CADENCE_ALGORITHM_DIGEST:
|
|
195
|
+
raise SourceCadenceEmissionError(
|
|
196
|
+
"SOURCE_CADENCE_AUTHORITY_INVALID",
|
|
197
|
+
"the cadence authority names an inference algorithm this build cannot replay",
|
|
198
|
+
)
|
|
199
|
+
try:
|
|
200
|
+
return cls(**payload)
|
|
201
|
+
except TypeError as error: # pragma: no cover - key set already checked above
|
|
202
|
+
raise SourceCadenceEmissionError(
|
|
203
|
+
"SOURCE_CADENCE_AUTHORITY_INVALID",
|
|
204
|
+
"the cadence authority is not the exact authority document",
|
|
205
|
+
) from error
|
|
206
|
+
|
|
207
|
+
def to_dict(self) -> dict[str, Any]:
|
|
208
|
+
return {
|
|
209
|
+
"schema_version": self.schema_version,
|
|
210
|
+
"inference_epoch": self.inference_epoch,
|
|
211
|
+
"connector_configuration_digest": self.connector_configuration_digest,
|
|
212
|
+
"locator_digest": self.locator_digest,
|
|
213
|
+
"recipe_digest": self.recipe_digest,
|
|
214
|
+
"source_authority_digest": self.source_authority_digest,
|
|
215
|
+
"reader_family_id": self.reader_family_id,
|
|
216
|
+
"reader_family_version": self.reader_family_version,
|
|
217
|
+
"reader_options_digest": self.reader_options_digest,
|
|
218
|
+
"frontier_policy_digest": self.frontier_policy_digest,
|
|
219
|
+
"time_interpretation_digest": self.time_interpretation_digest,
|
|
220
|
+
"inference_algorithm_digest": self.inference_algorithm_digest,
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@dataclass(frozen=True)
|
|
225
|
+
class SourceProbeCoordinate:
|
|
226
|
+
"""Where in one epoch's chain this probe lands, and what bounded it in time."""
|
|
227
|
+
|
|
228
|
+
sequence: int
|
|
229
|
+
previous_observation_digest: str | None
|
|
230
|
+
latest_acquired: EmpiricalSourceObservation | None
|
|
231
|
+
started_at: str
|
|
232
|
+
completed_at: str
|
|
233
|
+
probe_receipt_digest: str
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@dataclass(frozen=True)
|
|
237
|
+
class AcquiredProbeFacts:
|
|
238
|
+
"""What one probe that acquired bytes independently proved about them.
|
|
239
|
+
|
|
240
|
+
Every field is a fact about the acquired artifact or the response that carried it. The
|
|
241
|
+
validator headers are digests rather than values because they are untrusted publisher bytes;
|
|
242
|
+
the contract stores them hashed for the same reason.
|
|
243
|
+
"""
|
|
244
|
+
|
|
245
|
+
acquired_size_bytes: int
|
|
246
|
+
content_digest: str
|
|
247
|
+
row_digest: str
|
|
248
|
+
schema_digest: str
|
|
249
|
+
row_count: int
|
|
250
|
+
http_status_code: int | None
|
|
251
|
+
etag_digest: str | None
|
|
252
|
+
last_modified_digest: str | None
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def acquired_observation(
|
|
256
|
+
*,
|
|
257
|
+
source_id: str,
|
|
258
|
+
authority: SourceCadenceAuthority,
|
|
259
|
+
coordinate: SourceProbeCoordinate,
|
|
260
|
+
facts: AcquiredProbeFacts,
|
|
261
|
+
) -> EmpiricalSourceObservation:
|
|
262
|
+
"""Build the exact observation for one probe that acquired bytes.
|
|
263
|
+
|
|
264
|
+
The result kind comes from one comparison and nothing else: the acquired content digest
|
|
265
|
+
against the predecessor acquired observation's. Equal content that arrives with any other
|
|
266
|
+
parsed fact changed is not an unchanged edition and is not a changed one either -- it is a
|
|
267
|
+
contradiction, and it is refused rather than recorded as either.
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
latest_acquired = coordinate.latest_acquired
|
|
271
|
+
if latest_acquired is None or latest_acquired.content_digest != facts.content_digest:
|
|
272
|
+
result_kind = "changed"
|
|
273
|
+
else:
|
|
274
|
+
if (
|
|
275
|
+
facts.acquired_size_bytes != latest_acquired.acquired_size_bytes
|
|
276
|
+
or facts.row_digest != latest_acquired.row_digest
|
|
277
|
+
or facts.schema_digest != latest_acquired.schema_digest
|
|
278
|
+
or facts.row_count != latest_acquired.row_count
|
|
279
|
+
or latest_acquired.frontier != _NO_FRONTIER
|
|
280
|
+
or latest_acquired.max_event_time is not None
|
|
281
|
+
or latest_acquired.partition_digests
|
|
282
|
+
or latest_acquired.next_watermark_candidate_digest is not None
|
|
283
|
+
or latest_acquired.publication_reference_time is not None
|
|
284
|
+
or latest_acquired.edition_sequence is not None
|
|
285
|
+
):
|
|
286
|
+
raise SourceCadenceEmissionError(
|
|
287
|
+
"SOURCE_CADENCE_RESULT_INCONSISTENT",
|
|
288
|
+
"identical acquired content has contradictory parsed evidence",
|
|
289
|
+
)
|
|
290
|
+
result_kind = "unchanged"
|
|
291
|
+
return _observation(
|
|
292
|
+
source_id=source_id,
|
|
293
|
+
authority=authority,
|
|
294
|
+
coordinate=coordinate,
|
|
295
|
+
result_kind=result_kind,
|
|
296
|
+
failure_code=None,
|
|
297
|
+
http_status_code=facts.http_status_code,
|
|
298
|
+
etag_digest=facts.etag_digest,
|
|
299
|
+
last_modified_digest=facts.last_modified_digest,
|
|
300
|
+
acquired_size_bytes=facts.acquired_size_bytes,
|
|
301
|
+
content_digest=facts.content_digest,
|
|
302
|
+
row_digest=facts.row_digest,
|
|
303
|
+
schema_digest=facts.schema_digest,
|
|
304
|
+
row_count=facts.row_count,
|
|
305
|
+
acquisition_receipt_digest=coordinate.probe_receipt_digest,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def absent_observation(
|
|
310
|
+
*,
|
|
311
|
+
source_id: str,
|
|
312
|
+
authority: SourceCadenceAuthority,
|
|
313
|
+
coordinate: SourceProbeCoordinate,
|
|
314
|
+
outcome: tuple[str, str | None],
|
|
315
|
+
) -> EmpiricalSourceObservation:
|
|
316
|
+
"""Build the exact observation for one probe that reached the source and acquired nothing.
|
|
317
|
+
|
|
318
|
+
The result kind comes only from the closed failure code the probe reported. No bytes were
|
|
319
|
+
acquired, so the observation reports no status code, validator digest, acquired fact,
|
|
320
|
+
frontier, or publication coordinate -- it holds none of them, and the contract refuses an
|
|
321
|
+
absent result that claims any of them.
|
|
322
|
+
"""
|
|
323
|
+
|
|
324
|
+
result_kind, failure_code = outcome
|
|
325
|
+
return _observation(
|
|
326
|
+
source_id=source_id,
|
|
327
|
+
authority=authority,
|
|
328
|
+
coordinate=coordinate,
|
|
329
|
+
result_kind=result_kind,
|
|
330
|
+
failure_code=failure_code,
|
|
331
|
+
# A refusal carries no HTTP status, so the observation claims none. The contract
|
|
332
|
+
# admits a null status for both absent result kinds.
|
|
333
|
+
http_status_code=None,
|
|
334
|
+
etag_digest=None,
|
|
335
|
+
last_modified_digest=None,
|
|
336
|
+
acquired_size_bytes=None,
|
|
337
|
+
content_digest=None,
|
|
338
|
+
row_digest=None,
|
|
339
|
+
schema_digest=None,
|
|
340
|
+
row_count=None,
|
|
341
|
+
acquisition_receipt_digest=None,
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def _observation(
|
|
346
|
+
*,
|
|
347
|
+
source_id: str,
|
|
348
|
+
authority: SourceCadenceAuthority,
|
|
349
|
+
coordinate: SourceProbeCoordinate,
|
|
350
|
+
result_kind: str,
|
|
351
|
+
failure_code: str | None,
|
|
352
|
+
http_status_code: int | None,
|
|
353
|
+
etag_digest: str | None,
|
|
354
|
+
last_modified_digest: str | None,
|
|
355
|
+
acquired_size_bytes: int | None,
|
|
356
|
+
content_digest: str | None,
|
|
357
|
+
row_digest: str | None,
|
|
358
|
+
schema_digest: str | None,
|
|
359
|
+
row_count: int | None,
|
|
360
|
+
acquisition_receipt_digest: str | None,
|
|
361
|
+
) -> EmpiricalSourceObservation:
|
|
362
|
+
"""Assemble one observation from an authority, a coordinate, and one probe's facts.
|
|
363
|
+
|
|
364
|
+
Every field either comes from the coordinator that authenticated it or is absent, and the
|
|
365
|
+
contract validates the assembled result. A refusal here is typed rather than a raw
|
|
366
|
+
``ValueError`` so both producers can report it with their own code without inspecting the
|
|
367
|
+
exception's text.
|
|
368
|
+
"""
|
|
369
|
+
|
|
370
|
+
try:
|
|
371
|
+
return EmpiricalSourceObservation(
|
|
372
|
+
source_id=source_id,
|
|
373
|
+
sequence=coordinate.sequence,
|
|
374
|
+
inference_epoch=authority.inference_epoch,
|
|
375
|
+
previous_observation_digest=coordinate.previous_observation_digest,
|
|
376
|
+
connector_configuration_digest=authority.connector_configuration_digest,
|
|
377
|
+
locator_digest=authority.locator_digest,
|
|
378
|
+
recipe_digest=authority.recipe_digest,
|
|
379
|
+
source_authority_digest=authority.source_authority_digest,
|
|
380
|
+
reader_family_id=authority.reader_family_id,
|
|
381
|
+
reader_family_version=authority.reader_family_version,
|
|
382
|
+
reader_options_digest=authority.reader_options_digest,
|
|
383
|
+
frontier_policy_digest=authority.frontier_policy_digest,
|
|
384
|
+
time_interpretation_digest=authority.time_interpretation_digest,
|
|
385
|
+
inference_algorithm_digest=authority.inference_algorithm_digest,
|
|
386
|
+
started_at=coordinate.started_at,
|
|
387
|
+
completed_at=coordinate.completed_at,
|
|
388
|
+
result_kind=result_kind,
|
|
389
|
+
failure_code=failure_code,
|
|
390
|
+
http_status_class=(
|
|
391
|
+
"none" if http_status_code is None else f"{http_status_code // 100}xx"
|
|
392
|
+
),
|
|
393
|
+
http_status_code=http_status_code,
|
|
394
|
+
# Neither producer issues a conditional request on this transport, so no probe here
|
|
395
|
+
# can report the no-body HTTP 304 shape the contract also admits.
|
|
396
|
+
conditional_request_digest=None,
|
|
397
|
+
probe_receipt_digest=coordinate.probe_receipt_digest,
|
|
398
|
+
etag_digest=etag_digest,
|
|
399
|
+
last_modified_digest=last_modified_digest,
|
|
400
|
+
acquired_size_bytes=acquired_size_bytes,
|
|
401
|
+
content_digest=content_digest,
|
|
402
|
+
row_digest=row_digest,
|
|
403
|
+
schema_digest=schema_digest,
|
|
404
|
+
row_count=row_count,
|
|
405
|
+
# A whole-document HTTPS fetch observes no approved typed frontier field, so this
|
|
406
|
+
# path records no frontier, no maximum event time, and no partition digests rather
|
|
407
|
+
# than deriving them from byte order. The publication coordinate is absent for two
|
|
408
|
+
# independent reasons. The receipt seals only ``last_modified_digest``, a one-way
|
|
409
|
+
# hash, so there is no publication reference time here to record. And even with the
|
|
410
|
+
# value, the contract admits that time only paired with an edition sequence, which
|
|
411
|
+
# this transport cannot prove: it cannot tell how many editions a publisher released
|
|
412
|
+
# between two probes, and counting observed changes would assert exactly that.
|
|
413
|
+
frontier=_NO_FRONTIER,
|
|
414
|
+
max_event_time=None,
|
|
415
|
+
partition_digests=(),
|
|
416
|
+
acquisition_receipt_digest=acquisition_receipt_digest,
|
|
417
|
+
next_watermark_candidate_digest=None,
|
|
418
|
+
publication_reference_time=None,
|
|
419
|
+
edition_sequence=None,
|
|
420
|
+
)
|
|
421
|
+
except CadenceContractError as error:
|
|
422
|
+
raise SourceCadenceEmissionError(
|
|
423
|
+
"SOURCE_CADENCE_OBSERVATION_INVALID",
|
|
424
|
+
f"the probe does not form an exact observation: {error.detail}",
|
|
425
|
+
) from error
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def require_probe_receipt_digest(value: Any) -> str:
|
|
429
|
+
"""Admit one bare lowercase SHA-256 probe receipt digest."""
|
|
430
|
+
|
|
431
|
+
if not isinstance(value, str) or _DIGEST.fullmatch(value) is None:
|
|
432
|
+
raise SourceCadenceEmissionError(
|
|
433
|
+
"SOURCE_CADENCE_CONTEXT_INVALID",
|
|
434
|
+
"the probe receipt digest is not a bare lowercase SHA-256 digest",
|
|
435
|
+
)
|
|
436
|
+
return value
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
__all__ = [
|
|
440
|
+
"PRE_FETCH_FAILURE_CODES",
|
|
441
|
+
"SOURCE_CADENCE_AUTHORITY_FIELDS",
|
|
442
|
+
"SOURCE_REACHED_FAILURE_OUTCOMES",
|
|
443
|
+
"UNCLASSIFIED_FAILURE_OUTCOME",
|
|
444
|
+
"AcquiredProbeFacts",
|
|
445
|
+
"SourceCadenceAuthority",
|
|
446
|
+
"SourceCadenceEmissionError",
|
|
447
|
+
"SourceProbeCoordinate",
|
|
448
|
+
"absent_observation",
|
|
449
|
+
"acquired_observation",
|
|
450
|
+
"local_refusal_absent_outcome",
|
|
451
|
+
"require_probe_receipt_digest",
|
|
452
|
+
"sealed_refusal_absent_outcome",
|
|
453
|
+
]
|