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,375 @@
|
|
|
1
|
+
"""Run local deterministic checks without creating a review decision.
|
|
2
|
+
|
|
3
|
+
**It writes nothing.** No file, no folder, no permission, no recorded state. The tests prove that
|
|
4
|
+
by digesting the entire tree before and after the call rather than by reading the code and
|
|
5
|
+
believing it, and they do the same for a Workbench folder's control folder, because a recorded
|
|
6
|
+
state file is what an accidental write would touch first.
|
|
7
|
+
|
|
8
|
+
**It cannot say a decision.** :data:`LOCAL_REVIEW_STATUS` is a module constant and is not a
|
|
9
|
+
parameter of anything, so no code path can produce another value;
|
|
10
|
+
:attr:`LocalReview.human_review_pending` is a property returning the literal ``True`` rather than a
|
|
11
|
+
field, so it cannot be constructed false. Releasing a dataset needs two independent reviewers and a
|
|
12
|
+
signed decision, and :data:`PENDING_SENTENCE` says so on every run, passing or failing, in both
|
|
13
|
+
output modes.
|
|
14
|
+
|
|
15
|
+
**It cannot seal one.** This module does not import sealing or signing code.
|
|
16
|
+
|
|
17
|
+
**A broken build is a result, not a crash.** The seal check runs through
|
|
18
|
+
:func:`pipeline.inspect_candidate`, which replays the whole sealed tree before reporting a single
|
|
19
|
+
fact. When it refuses, its refusal is captured as a failed check carrying its finding identifier:
|
|
20
|
+
"this build does not verify" is exactly the answer a reviewer asked for. Only the refusal for a
|
|
21
|
+
folder with no build in it is left to raise, because that is a typo rather than a review result.
|
|
22
|
+
|
|
23
|
+
One reading, not two: every fact below comes from the single :func:`inspect_candidate` call. A
|
|
24
|
+
second open of the same folder would be a second race window, and closing that window is the entire
|
|
25
|
+
reason that reader exists.
|
|
26
|
+
|
|
27
|
+
**Which route the rights check takes, and why.** ``pipeline._validate_rights`` takes a parsed
|
|
28
|
+
``TablePlan`` -- not a sealed tree -- and raises on the first source it objects to. It can
|
|
29
|
+
therefore neither be handed a verified reading nor report per source, which is what a review needs,
|
|
30
|
+
so the rights facts are read from the ``evidence/sources.json`` member of that same verified
|
|
31
|
+
reading and reported one source at a time. The values are the ones the build recorded and sealed;
|
|
32
|
+
the gate that acted on them at build time is named ``rights_permission`` in the recorded checks
|
|
33
|
+
fingerprint.
|
|
34
|
+
|
|
35
|
+
Nothing here prints. Every function returns facts or text for terminal or browser rendering.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
from collections.abc import Mapping, Sequence
|
|
41
|
+
from dataclasses import dataclass
|
|
42
|
+
from pathlib import Path
|
|
43
|
+
from typing import Any
|
|
44
|
+
|
|
45
|
+
from mostlyright.data_harness.pipeline import (
|
|
46
|
+
GRAPH_MANIFEST_VERSION,
|
|
47
|
+
GRAPH_RECIPE_MANIFEST_VERSION,
|
|
48
|
+
BuildError,
|
|
49
|
+
inspect_candidate,
|
|
50
|
+
)
|
|
51
|
+
from mostlyright.data_harness.ux.readers import require_a_build
|
|
52
|
+
from mostlyright.data_harness.ux.render import render_human
|
|
53
|
+
|
|
54
|
+
# The only status this module can report. It cannot be constructed as a sealed decision.
|
|
55
|
+
LOCAL_REVIEW_STATUS = "local_checks_completed"
|
|
56
|
+
|
|
57
|
+
# Printed on every run, whether the checks passed or failed, in both output modes. This is the
|
|
58
|
+
# honest line: the checks below are what one machine can decide alone, and that is not a release.
|
|
59
|
+
PENDING_SENTENCE = (
|
|
60
|
+
"Human review is still pending. These checks are what this machine can decide on its own; "
|
|
61
|
+
"releasing a dataset needs two independent reviewers and a signed decision."
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# The names of the checks this module contributes. The quality checks keep the names the build
|
|
65
|
+
# recorded for them (MIN_ROWS, NOT_NULL_<column>, UNIQUE_GRAIN), so a check reads the same here as
|
|
66
|
+
# it does in the receipt.
|
|
67
|
+
SEAL_CHECK = "SEAL_REPLAY"
|
|
68
|
+
JOIN_CHECK = "JOIN_ROW_MULTIPLIER"
|
|
69
|
+
LINEAGE_CHECK = "LINEAGE_COMPLETE"
|
|
70
|
+
RIGHTS_CHECK_PREFIX = "RIGHTS_"
|
|
71
|
+
|
|
72
|
+
# Where the rendered check lines sit in the payload both renderings are built from, and where the
|
|
73
|
+
# closing sentence sits. Both are keys a person reads; the check names inside are values, never
|
|
74
|
+
# keys, because a name taken from the data would be relabelled into a name the data does not have.
|
|
75
|
+
CHECKS_KEY = "checks"
|
|
76
|
+
PENDING_KEY = "what happens next"
|
|
77
|
+
|
|
78
|
+
# Where the one fact a script branches on sits in the payload.
|
|
79
|
+
ALL_CHECKS_PASSED_KEY = "all_checks_passed"
|
|
80
|
+
|
|
81
|
+
_PASSED = "[pass]"
|
|
82
|
+
_FAILED = "[fail]"
|
|
83
|
+
|
|
84
|
+
# What the build's own recorded checks fingerprint requires of a join: every left row matched, and
|
|
85
|
+
# exactly one row out per row in. Read from ``pipeline.validation_policy_digest``'s mandatory gates
|
|
86
|
+
# rather than restated here, so local review applies the Build's rule.
|
|
87
|
+
_REQUIRED_ROW_MULTIPLIER = 1.0
|
|
88
|
+
|
|
89
|
+
# Rights statuses the build refuses outright.
|
|
90
|
+
_REFUSED_RIGHTS = frozenset({"unknown", "prohibited"})
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass(frozen=True)
|
|
94
|
+
class LocalCheck:
|
|
95
|
+
"""One check, its outcome, and the recorded value that decided it.
|
|
96
|
+
|
|
97
|
+
``evidence`` is not a restatement of the outcome: it is the number, the name, or the identifier
|
|
98
|
+
a person would ask for next. A check that says only "failed" makes its reader open the folder,
|
|
99
|
+
which is the thing this command exists to save them.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
name: str
|
|
103
|
+
passed: bool
|
|
104
|
+
evidence: str
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def line(self) -> str:
|
|
108
|
+
"""This check as one plain line: outcome first, then what it was, then why."""
|
|
109
|
+
|
|
110
|
+
return f"{_PASSED if self.passed else _FAILED} {self.name}: {self.evidence}"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@dataclass(frozen=True)
|
|
114
|
+
class LocalReview:
|
|
115
|
+
"""The result of running every deterministic check on one build.
|
|
116
|
+
|
|
117
|
+
``status`` and ``human_review_pending`` are properties rather than fields on purpose. A field
|
|
118
|
+
is something a caller sets; these two are the whole safety argument of the module, so they are
|
|
119
|
+
not settable at all -- constructing this with either as a keyword is a ``TypeError``.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
run_dir: Path
|
|
123
|
+
candidate_digest: str | None
|
|
124
|
+
table_sha256: str | None
|
|
125
|
+
checks: tuple[LocalCheck, ...]
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def status(self) -> str:
|
|
129
|
+
return LOCAL_REVIEW_STATUS
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def human_review_pending(self) -> bool:
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def passed(self) -> bool:
|
|
137
|
+
"""Whether every check passed. Never a verdict on the build -- only on the checks."""
|
|
138
|
+
|
|
139
|
+
return all(check.passed for check in self.checks)
|
|
140
|
+
|
|
141
|
+
def to_dict(self) -> dict[str, Any]:
|
|
142
|
+
"""The one payload both renderings are built from."""
|
|
143
|
+
|
|
144
|
+
width = len(str(max(len(self.checks), 1)))
|
|
145
|
+
return {
|
|
146
|
+
"status": LOCAL_REVIEW_STATUS,
|
|
147
|
+
"run_dir": str(self.run_dir),
|
|
148
|
+
"candidate_digest": self.candidate_digest,
|
|
149
|
+
"table_sha256": self.table_sha256,
|
|
150
|
+
ALL_CHECKS_PASSED_KEY: self.passed,
|
|
151
|
+
"human_review_pending": self.human_review_pending,
|
|
152
|
+
CHECKS_KEY: {
|
|
153
|
+
f"check {index:0{width}d}": check.line
|
|
154
|
+
for index, check in enumerate(self.checks, start=1)
|
|
155
|
+
},
|
|
156
|
+
PENDING_KEY: PENDING_SENTENCE,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
def lines(self) -> list[str]:
|
|
160
|
+
"""Exactly the plain lines the command line prints, from exactly the same payload."""
|
|
161
|
+
|
|
162
|
+
return render_human(self.to_dict()).splitlines()
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def local_review_exit_code(payload: Mapping[str, Any]) -> int:
|
|
166
|
+
"""1 when a check failed, so this reads as a gate in a script.
|
|
167
|
+
|
|
168
|
+
Derived from the emitted payload rather than a second Build read. The
|
|
169
|
+
number a script branches on and the line a person reads are the same fact.
|
|
170
|
+
|
|
171
|
+
A failed seal replay exits 1. Passing checks exit 0 and leave human review pending; this
|
|
172
|
+
command does not approve a build.
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
return 0 if payload.get(ALL_CHECKS_PASSED_KEY) is True else 1
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def run_local_review(run_dir: Path | str) -> LocalReview:
|
|
179
|
+
"""Run every deterministic check on the build at ``run_dir`` and report, writing nothing.
|
|
180
|
+
|
|
181
|
+
A folder with no build in it is named as such before anything else runs, because the reader's
|
|
182
|
+
own refusal for a missing tree reads like a break-in rather than the typo it usually is. Every
|
|
183
|
+
other refusal from the sealed reading becomes a failed seal check, so a build that does not
|
|
184
|
+
verify comes back as a review rather than a traceback.
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
target = Path(run_dir)
|
|
188
|
+
require_a_build(target)
|
|
189
|
+
try:
|
|
190
|
+
inspection = inspect_candidate(target)
|
|
191
|
+
except BuildError as error:
|
|
192
|
+
return LocalReview(
|
|
193
|
+
run_dir=target,
|
|
194
|
+
candidate_digest=None,
|
|
195
|
+
table_sha256=None,
|
|
196
|
+
checks=(
|
|
197
|
+
LocalCheck(
|
|
198
|
+
name=SEAL_CHECK,
|
|
199
|
+
passed=False,
|
|
200
|
+
evidence=f"this build does not replay as sealed ({error.finding_id})",
|
|
201
|
+
),
|
|
202
|
+
),
|
|
203
|
+
)
|
|
204
|
+
checks: list[LocalCheck] = [
|
|
205
|
+
LocalCheck(
|
|
206
|
+
name=SEAL_CHECK,
|
|
207
|
+
passed=True,
|
|
208
|
+
evidence=f"every file replays to the fingerprint {inspection.candidate_digest}",
|
|
209
|
+
),
|
|
210
|
+
*_quality_checks(inspection.quality),
|
|
211
|
+
_join_check(
|
|
212
|
+
inspection.join,
|
|
213
|
+
graph=inspection.manifest_version
|
|
214
|
+
in {GRAPH_MANIFEST_VERSION, GRAPH_RECIPE_MANIFEST_VERSION},
|
|
215
|
+
),
|
|
216
|
+
_lineage_check(
|
|
217
|
+
inspection.columns,
|
|
218
|
+
inspection.lineage,
|
|
219
|
+
graph=inspection.manifest_version
|
|
220
|
+
in {GRAPH_MANIFEST_VERSION, GRAPH_RECIPE_MANIFEST_VERSION},
|
|
221
|
+
),
|
|
222
|
+
*_rights_checks(inspection.sources),
|
|
223
|
+
]
|
|
224
|
+
return LocalReview(
|
|
225
|
+
run_dir=target,
|
|
226
|
+
candidate_digest=inspection.candidate_digest,
|
|
227
|
+
table_sha256=inspection.table_sha256,
|
|
228
|
+
checks=tuple(checks),
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _quality_checks(quality: Mapping[str, Any]) -> list[LocalCheck]:
|
|
233
|
+
"""Every check the build recorded, under the name the build recorded it."""
|
|
234
|
+
|
|
235
|
+
recorded = quality.get("checks") if isinstance(quality, Mapping) else None
|
|
236
|
+
if not isinstance(recorded, list):
|
|
237
|
+
return [
|
|
238
|
+
LocalCheck(
|
|
239
|
+
name="QUALITY_CHECKS",
|
|
240
|
+
passed=False,
|
|
241
|
+
evidence="this build recorded no list of checks",
|
|
242
|
+
)
|
|
243
|
+
]
|
|
244
|
+
return [
|
|
245
|
+
LocalCheck(
|
|
246
|
+
name=str(check.get("check_id", "an unnamed check")),
|
|
247
|
+
passed=bool(check.get("passed")),
|
|
248
|
+
evidence=f"wanted {check.get('expected')}, found {check.get('observed')}",
|
|
249
|
+
)
|
|
250
|
+
for check in recorded
|
|
251
|
+
if isinstance(check, Mapping)
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _join_check(join: Mapping[str, Any], *, graph: bool = False) -> LocalCheck:
|
|
256
|
+
"""The declared relationship against the rows that actually came out of it."""
|
|
257
|
+
|
|
258
|
+
if graph:
|
|
259
|
+
nodes = join.get("nodes") if isinstance(join, Mapping) else None
|
|
260
|
+
valid = isinstance(nodes, list) and all(isinstance(node, Mapping) for node in nodes)
|
|
261
|
+
joins = sum(
|
|
262
|
+
node.get("operation") == "join" for node in nodes or () if isinstance(node, Mapping)
|
|
263
|
+
)
|
|
264
|
+
return LocalCheck(
|
|
265
|
+
name=JOIN_CHECK,
|
|
266
|
+
passed=valid,
|
|
267
|
+
evidence=f"the verified graph records {joins} join operation(s)",
|
|
268
|
+
)
|
|
269
|
+
if not join:
|
|
270
|
+
return LocalCheck(
|
|
271
|
+
name=JOIN_CHECK,
|
|
272
|
+
passed=True,
|
|
273
|
+
evidence="this build joins nothing, so there is no relationship to hold to",
|
|
274
|
+
)
|
|
275
|
+
cardinality = join.get("cardinality")
|
|
276
|
+
multiplier = join.get("row_multiplier")
|
|
277
|
+
unmatched = join.get("unmatched_left_rows")
|
|
278
|
+
evidence = (
|
|
279
|
+
f"declared {cardinality}; {join.get('left_rows')} rows in, {join.get('output_rows')} out "
|
|
280
|
+
f"({multiplier} out per row in), {unmatched} did not match"
|
|
281
|
+
)
|
|
282
|
+
passed = multiplier == _REQUIRED_ROW_MULTIPLIER and unmatched == 0
|
|
283
|
+
return LocalCheck(name=JOIN_CHECK, passed=passed, evidence=evidence)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _lineage_check(
|
|
287
|
+
columns: Sequence[str], lineage: Mapping[str, Any], *, graph: bool = False
|
|
288
|
+
) -> LocalCheck:
|
|
289
|
+
"""Whether every column that shipped can be traced back to a column that arrived."""
|
|
290
|
+
|
|
291
|
+
entries = lineage.get("columns", {}) if graph and isinstance(lineage, Mapping) else lineage
|
|
292
|
+
untraced = [
|
|
293
|
+
str(column)
|
|
294
|
+
for column in columns
|
|
295
|
+
if not _traces_to_a_source(entries.get(column) if isinstance(entries, Mapping) else None)
|
|
296
|
+
]
|
|
297
|
+
if untraced:
|
|
298
|
+
return LocalCheck(
|
|
299
|
+
name=LINEAGE_CHECK,
|
|
300
|
+
passed=False,
|
|
301
|
+
evidence=(
|
|
302
|
+
f"{len(columns) - len(untraced)} of {len(columns)} columns trace to a column of a "
|
|
303
|
+
f"source; no source recorded for: {', '.join(untraced)}"
|
|
304
|
+
),
|
|
305
|
+
)
|
|
306
|
+
return LocalCheck(
|
|
307
|
+
name=LINEAGE_CHECK,
|
|
308
|
+
passed=True,
|
|
309
|
+
evidence=f"all {len(columns)} columns trace to a column of a source",
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _traces_to_a_source(entry: Any) -> bool:
|
|
314
|
+
if isinstance(entry, Mapping) and isinstance(entry.get("inputs"), list):
|
|
315
|
+
return bool(entry["inputs"]) and all(
|
|
316
|
+
isinstance(item, Mapping)
|
|
317
|
+
and bool(item.get("source_id"))
|
|
318
|
+
and bool(item.get("source_column"))
|
|
319
|
+
for item in entry["inputs"]
|
|
320
|
+
)
|
|
321
|
+
return (
|
|
322
|
+
isinstance(entry, Mapping)
|
|
323
|
+
and bool(entry.get("source_id"))
|
|
324
|
+
and bool(entry.get("source_column"))
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def _rights_checks(sources: Sequence[Mapping[str, Any]]) -> list[LocalCheck]:
|
|
329
|
+
"""What each source is recorded as allowing, one check per source.
|
|
330
|
+
|
|
331
|
+
One check per source rather than one for all of them: a reviewer who has to act on this needs
|
|
332
|
+
the name of the file that is the problem, not the sentence "the rights do not permit this".
|
|
333
|
+
"""
|
|
334
|
+
|
|
335
|
+
if not sources:
|
|
336
|
+
return [
|
|
337
|
+
LocalCheck(
|
|
338
|
+
name=f"{RIGHTS_CHECK_PREFIX}NONE",
|
|
339
|
+
passed=False,
|
|
340
|
+
evidence="this build recorded no source to check the rights of",
|
|
341
|
+
)
|
|
342
|
+
]
|
|
343
|
+
checks: list[LocalCheck] = []
|
|
344
|
+
for source in sources:
|
|
345
|
+
name = str(source.get("source_id", "an unnamed source"))
|
|
346
|
+
rights = source.get("rights")
|
|
347
|
+
rights = rights if isinstance(rights, Mapping) else {}
|
|
348
|
+
status = str(rights.get("status", "not recorded"))
|
|
349
|
+
permissions = [str(item) for item in rights.get("permissions", ()) or ()]
|
|
350
|
+
allowed = ", ".join(permissions) if permissions else "nothing"
|
|
351
|
+
checks.append(
|
|
352
|
+
LocalCheck(
|
|
353
|
+
name=f"{RIGHTS_CHECK_PREFIX}{name}",
|
|
354
|
+
passed=status not in _REFUSED_RIGHTS and bool(permissions),
|
|
355
|
+
evidence=f"recorded as {status}; may {allowed}",
|
|
356
|
+
)
|
|
357
|
+
)
|
|
358
|
+
return checks
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
__all__ = [
|
|
362
|
+
"ALL_CHECKS_PASSED_KEY",
|
|
363
|
+
"CHECKS_KEY",
|
|
364
|
+
"JOIN_CHECK",
|
|
365
|
+
"LINEAGE_CHECK",
|
|
366
|
+
"LOCAL_REVIEW_STATUS",
|
|
367
|
+
"PENDING_KEY",
|
|
368
|
+
"PENDING_SENTENCE",
|
|
369
|
+
"RIGHTS_CHECK_PREFIX",
|
|
370
|
+
"SEAL_CHECK",
|
|
371
|
+
"LocalCheck",
|
|
372
|
+
"LocalReview",
|
|
373
|
+
"local_review_exit_code",
|
|
374
|
+
"run_local_review",
|
|
375
|
+
]
|