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.
Files changed (314) hide show
  1. mostlyright/data_harness/__init__.py +158 -0
  2. mostlyright/data_harness/acquisition/__init__.py +55 -0
  3. mostlyright/data_harness/acquisition/http.py +2773 -0
  4. mostlyright/data_harness/acquisition/parsing.py +809 -0
  5. mostlyright/data_harness/acquisition/ranges.py +495 -0
  6. mostlyright/data_harness/acquisition/result_download.py +360 -0
  7. mostlyright/data_harness/acquisition/retention_admission.py +248 -0
  8. mostlyright/data_harness/acquisition/sandbox.py +4888 -0
  9. mostlyright/data_harness/acquisition/url_policy.py +530 -0
  10. mostlyright/data_harness/agent_runtime.py +2743 -0
  11. mostlyright/data_harness/assets/logo-ink.svg +31 -0
  12. mostlyright/data_harness/backends/__init__.py +28 -0
  13. mostlyright/data_harness/backends/pandas_backend.py +350 -0
  14. mostlyright/data_harness/backends/polars_backend.py +366 -0
  15. mostlyright/data_harness/backends/protocol.py +124 -0
  16. mostlyright/data_harness/backends/reference.py +83 -0
  17. mostlyright/data_harness/backends/registry.py +55 -0
  18. mostlyright/data_harness/backends/restrictions.py +126 -0
  19. mostlyright/data_harness/canonical.py +333 -0
  20. mostlyright/data_harness/catalog_job.py +625 -0
  21. mostlyright/data_harness/cli.py +5398 -0
  22. mostlyright/data_harness/contracts.py +53 -0
  23. mostlyright/data_harness/coordinator.py +1307 -0
  24. mostlyright/data_harness/deploy.py +924 -0
  25. mostlyright/data_harness/deploy_target.py +312 -0
  26. mostlyright/data_harness/deployment_evidence.py +1067 -0
  27. mostlyright/data_harness/event_presentation.py +576 -0
  28. mostlyright/data_harness/events.py +2152 -0
  29. mostlyright/data_harness/fast_delimited.py +239 -0
  30. mostlyright/data_harness/fleet.py +237 -0
  31. mostlyright/data_harness/formats.py +236 -0
  32. mostlyright/data_harness/governors.py +1163 -0
  33. mostlyright/data_harness/hosted_bootstrap.py +972 -0
  34. mostlyright/data_harness/hosted_crawler.py +1115 -0
  35. mostlyright/data_harness/hosted_crawler_container_smoke.py +351 -0
  36. mostlyright/data_harness/hosted_crawler_fetch.py +423 -0
  37. mostlyright/data_harness/hosted_crawler_job.py +1277 -0
  38. mostlyright/data_harness/hosted_crawler_protocol.py +676 -0
  39. mostlyright/data_harness/hosted_dataset.py +1500 -0
  40. mostlyright/data_harness/hosted_deploy.py +3037 -0
  41. mostlyright/data_harness/hosted_handoff.py +62 -0
  42. mostlyright/data_harness/hosted_ingestion_contract.py +504 -0
  43. mostlyright/data_harness/hosted_ingestion_job.py +356 -0
  44. mostlyright/data_harness/hosted_ingestion_job_smoke.py +40 -0
  45. mostlyright/data_harness/hosted_session_container_smoke.py +194 -0
  46. mostlyright/data_harness/hosted_session_worker.py +3554 -0
  47. mostlyright/data_harness/hosted_session_worker_job_smoke.py +46 -0
  48. mostlyright/data_harness/hosted_worker.py +6784 -0
  49. mostlyright/data_harness/ingestion/__init__.py +56 -0
  50. mostlyright/data_harness/ingestion/contracts.py +461 -0
  51. mostlyright/data_harness/ingestion/faults.py +42 -0
  52. mostlyright/data_harness/ingestion/gcs_store.py +1162 -0
  53. mostlyright/data_harness/ingestion/spool.py +130 -0
  54. mostlyright/data_harness/ingestion/store.py +885 -0
  55. mostlyright/data_harness/key_seam.py +434 -0
  56. mostlyright/data_harness/linux_process_boundary.py +262 -0
  57. mostlyright/data_harness/local_contracts.py +2880 -0
  58. mostlyright/data_harness/local_search/__init__.py +5 -0
  59. mostlyright/data_harness/local_search/build_index.py +1087 -0
  60. mostlyright/data_harness/local_search/contracts.py +920 -0
  61. mostlyright/data_harness/local_search/query_trace.py +266 -0
  62. mostlyright/data_harness/local_search/retrieval.py +700 -0
  63. mostlyright/data_harness/local_search/sealed.py +474 -0
  64. mostlyright/data_harness/local_search/service.py +784 -0
  65. mostlyright/data_harness/nbrender/CONTRACT.md +212 -0
  66. mostlyright/data_harness/nbrender/__init__.py +12 -0
  67. mostlyright/data_harness/nbrender/chrome.py +359 -0
  68. mostlyright/data_harness/nbrender/code_body.py +266 -0
  69. mostlyright/data_harness/nbrender/document.py +407 -0
  70. mostlyright/data_harness/nbrender/frame.py +275 -0
  71. mostlyright/data_harness/nbrender/interactive.py +337 -0
  72. mostlyright/data_harness/nbrender/markdown_body.py +477 -0
  73. mostlyright/data_harness/nbrender/mr_components.py +134 -0
  74. mostlyright/data_harness/nbrender/outputs_data.py +595 -0
  75. mostlyright/data_harness/nbrender/outputs_rich.py +906 -0
  76. mostlyright/data_harness/nbrender/outputs_source.py +260 -0
  77. mostlyright/data_harness/nbrender/outputs_stage.py +176 -0
  78. mostlyright/data_harness/nbrender/outputs_text.py +400 -0
  79. mostlyright/data_harness/nbrender/parse.py +394 -0
  80. mostlyright/data_harness/nbrender/status.py +40 -0
  81. mostlyright/data_harness/nbrender/tokens.py +1295 -0
  82. mostlyright/data_harness/notebook.py +1710 -0
  83. mostlyright/data_harness/offline.py +2049 -0
  84. mostlyright/data_harness/operation_registry.py +1007 -0
  85. mostlyright/data_harness/operator_setup.py +239 -0
  86. mostlyright/data_harness/pipeline.py +6428 -0
  87. mostlyright/data_harness/plan_graph.py +2026 -0
  88. mostlyright/data_harness/preparation/__init__.py +104 -0
  89. mostlyright/data_harness/preparation/contracts.py +1017 -0
  90. mostlyright/data_harness/preparation/engine.py +221 -0
  91. mostlyright/data_harness/preparation/errors.py +14 -0
  92. mostlyright/data_harness/preparation/gates.py +751 -0
  93. mostlyright/data_harness/preparation/joins.py +574 -0
  94. mostlyright/data_harness/preparation/profile.py +384 -0
  95. mostlyright/data_harness/preparation/table.py +217 -0
  96. mostlyright/data_harness/preparation/transforms.py +568 -0
  97. mostlyright/data_harness/progress_events.py +534 -0
  98. mostlyright/data_harness/readers/__init__.py +46 -0
  99. mostlyright/data_harness/readers/containers.py +963 -0
  100. mostlyright/data_harness/readers/contracts.py +542 -0
  101. mostlyright/data_harness/readers/delimited.py +257 -0
  102. mostlyright/data_harness/readers/grib2/__init__.py +33 -0
  103. mostlyright/data_harness/readers/grib2/admission.py +722 -0
  104. mostlyright/data_harness/readers/grib2/decode.py +1009 -0
  105. mostlyright/data_harness/readers/grib2/geometry.py +1133 -0
  106. mostlyright/data_harness/readers/grib2/portable_math.py +501 -0
  107. mostlyright/data_harness/readers/json_tabular.py +485 -0
  108. mostlyright/data_harness/readers/registry.py +514 -0
  109. mostlyright/data_harness/readers/samples/README.md +110 -0
  110. mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/cities.csv.gz +0 -0
  111. mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/expected.json +24 -0
  112. mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/cities.csv.gz +0 -0
  113. mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/expected.json +24 -0
  114. mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/cities.tar +0 -0
  115. mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/expected.json +24 -0
  116. mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/cities.tar +0 -0
  117. mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/expected.json +24 -0
  118. mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/cities.zip +0 -0
  119. mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/expected.json +25 -0
  120. mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
  121. mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/expected.json +25 -0
  122. mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
  123. mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/expected.json +25 -0
  124. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/cities.csv +3 -0
  125. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/expected.json +23 -0
  126. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/cities.tsv +5 -0
  127. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/expected.json +25 -0
  128. mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/expected.json +30 -0
  129. mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/observations.csv +5 -0
  130. mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/expected.json +44 -0
  131. mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/stations.json +1 -0
  132. mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/expected.json +48 -0
  133. mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/observations.ndjson +4 -0
  134. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/cities.xlsx +0 -0
  135. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/expected.json +24 -0
  136. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/expected.json +27 -0
  137. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
  138. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/expected.json +27 -0
  139. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
  140. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/README.md +20 -0
  141. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/expected.json +55 -0
  142. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/gfs-2m-temperature.grib2 +0 -0
  143. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/expected.json +54 -0
  144. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
  145. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/expected.json +54 -0
  146. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/hrrr-categorical-rain.grib2 +0 -0
  147. mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/expected.json +54 -0
  148. mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
  149. mostlyright/data_harness/readers/samples.py +582 -0
  150. mostlyright/data_harness/readers/spreadsheet.py +803 -0
  151. mostlyright/data_harness/readers/tabular.py +510 -0
  152. mostlyright/data_harness/recipe.py +5321 -0
  153. mostlyright/data_harness/repair/__init__.py +78 -0
  154. mostlyright/data_harness/repair/adapters.py +274 -0
  155. mostlyright/data_harness/repair/contracts.py +872 -0
  156. mostlyright/data_harness/repair/coordinator.py +1099 -0
  157. mostlyright/data_harness/repair/errors.py +16 -0
  158. mostlyright/data_harness/review.py +2533 -0
  159. mostlyright/data_harness/rowset.py +283 -0
  160. mostlyright/data_harness/serving.py +1975 -0
  161. mostlyright/data_harness/serving_edge.py +590 -0
  162. mostlyright/data_harness/serving_http.py +1031 -0
  163. mostlyright/data_harness/session_probes.py +759 -0
  164. mostlyright/data_harness/signing.py +101 -0
  165. mostlyright/data_harness/source_discovery.py +898 -0
  166. mostlyright/data_harness/sources/__init__.py +209 -0
  167. mostlyright/data_harness/sources/_adapter_steps.py +213 -0
  168. mostlyright/data_harness/sources/adapters.py +1214 -0
  169. mostlyright/data_harness/sources/cadence.py +1428 -0
  170. mostlyright/data_harness/sources/cadence_emission.py +453 -0
  171. mostlyright/data_harness/sources/cadence_history.py +546 -0
  172. mostlyright/data_harness/sources/catalog/__init__.py +17 -0
  173. mostlyright/data_harness/sources/catalog/admission.py +477 -0
  174. mostlyright/data_harness/sources/catalog/authoring.py +1701 -0
  175. mostlyright/data_harness/sources/catalog/authoring_policy.py +701 -0
  176. mostlyright/data_harness/sources/catalog/authoring_shards.py +1217 -0
  177. mostlyright/data_harness/sources/catalog/bounded_io.py +231 -0
  178. mostlyright/data_harness/sources/catalog/channel.py +523 -0
  179. mostlyright/data_harness/sources/catalog/channel_client.py +296 -0
  180. mostlyright/data_harness/sources/catalog/contracts.py +825 -0
  181. mostlyright/data_harness/sources/catalog/coverage.py +137 -0
  182. mostlyright/data_harness/sources/catalog/delta.py +1340 -0
  183. mostlyright/data_harness/sources/catalog/embedding.py +532 -0
  184. mostlyright/data_harness/sources/catalog/entry_v2.py +1182 -0
  185. mostlyright/data_harness/sources/catalog/fill.py +3889 -0
  186. mostlyright/data_harness/sources/catalog/fill_partitions.py +459 -0
  187. mostlyright/data_harness/sources/catalog/fill_staging.py +1105 -0
  188. mostlyright/data_harness/sources/catalog/gating.py +374 -0
  189. mostlyright/data_harness/sources/catalog/generation_receipt.py +1607 -0
  190. mostlyright/data_harness/sources/catalog/harvest/__init__.py +7 -0
  191. mostlyright/data_harness/sources/catalog/harvest/ckan.py +384 -0
  192. mostlyright/data_harness/sources/catalog/harvest/datagov_v4.py +798 -0
  193. mostlyright/data_harness/sources/catalog/harvest/protocol.py +964 -0
  194. mostlyright/data_harness/sources/catalog/harvest/sdmx.py +445 -0
  195. mostlyright/data_harness/sources/catalog/harvest/stac.py +384 -0
  196. mostlyright/data_harness/sources/catalog/health.py +447 -0
  197. mostlyright/data_harness/sources/catalog/hosted_catalog.py +105 -0
  198. mostlyright/data_harness/sources/catalog/identity_history.py +1549 -0
  199. mostlyright/data_harness/sources/catalog/neural.py +1618 -0
  200. mostlyright/data_harness/sources/catalog/packed_catalog.py +2345 -0
  201. mostlyright/data_harness/sources/catalog/packed_retrieval.py +1517 -0
  202. mostlyright/data_harness/sources/catalog/packed_writer.py +2802 -0
  203. mostlyright/data_harness/sources/catalog/query_trace.py +1037 -0
  204. mostlyright/data_harness/sources/catalog/recommend.py +171 -0
  205. mostlyright/data_harness/sources/catalog/retrieval.py +230 -0
  206. mostlyright/data_harness/sources/catalog/retrieval_manifest.py +995 -0
  207. mostlyright/data_harness/sources/catalog/rights_decisions.py +254 -0
  208. mostlyright/data_harness/sources/catalog/sealed.py +560 -0
  209. mostlyright/data_harness/sources/catalog/search.py +230 -0
  210. mostlyright/data_harness/sources/catalog/streaming_delta.py +1097 -0
  211. mostlyright/data_harness/sources/catalog/update.py +891 -0
  212. mostlyright/data_harness/sources/collections.py +815 -0
  213. mostlyright/data_harness/sources/contracts.py +2223 -0
  214. mostlyright/data_harness/sources/deletion.py +761 -0
  215. mostlyright/data_harness/sources/fitness.py +162 -0
  216. mostlyright/data_harness/sources/governance.py +163 -0
  217. mostlyright/data_harness/sources/hosted.py +173 -0
  218. mostlyright/data_harness/sources/integration.py +218 -0
  219. mostlyright/data_harness/sources/range_reader.py +418 -0
  220. mostlyright/data_harness/sources/registry.py +514 -0
  221. mostlyright/data_harness/sources/rights_rule.py +59 -0
  222. mostlyright/data_harness/sources/source_cadence_vectors.v1.json +1 -0
  223. mostlyright/data_harness/sources/sports.py +521 -0
  224. mostlyright/data_harness/sources/stream.py +524 -0
  225. mostlyright/data_harness/sources/stream_connector.py +418 -0
  226. mostlyright/data_harness/sources/stream_recorder.py +1404 -0
  227. mostlyright/data_harness/studio_boundary.py +2019 -0
  228. mostlyright/data_harness/thin/__init__.py +37 -0
  229. mostlyright/data_harness/thin/acquire.py +1137 -0
  230. mostlyright/data_harness/thin/acquire_cancel.py +579 -0
  231. mostlyright/data_harness/thin/approvals.py +617 -0
  232. mostlyright/data_harness/thin/commands.py +406 -0
  233. mostlyright/data_harness/thin/download.py +194 -0
  234. mostlyright/data_harness/thin/narrative.py +589 -0
  235. mostlyright/data_harness/thin/parity.py +1070 -0
  236. mostlyright/data_harness/thin/propose.py +2759 -0
  237. mostlyright/data_harness/thin/research.py +1663 -0
  238. mostlyright/data_harness/thin/router.py +924 -0
  239. mostlyright/data_harness/thin/runs.py +519 -0
  240. mostlyright/data_harness/thin/session.py +281 -0
  241. mostlyright/data_harness/thin/stream.py +501 -0
  242. mostlyright/data_harness/thin/transport.py +187 -0
  243. mostlyright/data_harness/thin/vocabulary.py +368 -0
  244. mostlyright/data_harness/thin/workers.py +164 -0
  245. mostlyright/data_harness/ucum/TABLE-PIN.json +40 -0
  246. mostlyright/data_harness/ucum/ucum-subset.v1.json +632 -0
  247. mostlyright/data_harness/unit_flow.py +927 -0
  248. mostlyright/data_harness/units.py +572 -0
  249. mostlyright/data_harness/ux/__init__.py +9 -0
  250. mostlyright/data_harness/ux/approve.py +485 -0
  251. mostlyright/data_harness/ux/author_yaml.py +597 -0
  252. mostlyright/data_harness/ux/cloud_auth.py +447 -0
  253. mostlyright/data_harness/ux/commands/__init__.py +260 -0
  254. mostlyright/data_harness/ux/commands/approve.py +136 -0
  255. mostlyright/data_harness/ux/commands/auth.py +744 -0
  256. mostlyright/data_harness/ux/commands/author.py +79 -0
  257. mostlyright/data_harness/ux/commands/catalog_author.py +403 -0
  258. mostlyright/data_harness/ux/commands/catalog_fill.py +523 -0
  259. mostlyright/data_harness/ux/commands/catalog_harvest.py +545 -0
  260. mostlyright/data_harness/ux/commands/catalog_publish.py +1838 -0
  261. mostlyright/data_harness/ux/commands/catalog_search.py +71 -0
  262. mostlyright/data_harness/ux/commands/catalog_update.py +437 -0
  263. mostlyright/data_harness/ux/commands/deploy.py +134 -0
  264. mostlyright/data_harness/ux/commands/deploy_dataset.py +98 -0
  265. mostlyright/data_harness/ux/commands/deploy_plan.py +105 -0
  266. mostlyright/data_harness/ux/commands/deploy_status.py +104 -0
  267. mostlyright/data_harness/ux/commands/diff.py +74 -0
  268. mostlyright/data_harness/ux/commands/index.py +84 -0
  269. mostlyright/data_harness/ux/commands/inventory.py +47 -0
  270. mostlyright/data_harness/ux/commands/list_builds.py +143 -0
  271. mostlyright/data_harness/ux/commands/login.py +63 -0
  272. mostlyright/data_harness/ux/commands/peek.py +236 -0
  273. mostlyright/data_harness/ux/commands/plan_check.py +90 -0
  274. mostlyright/data_harness/ux/commands/preflight.py +97 -0
  275. mostlyright/data_harness/ux/commands/record.py +107 -0
  276. mostlyright/data_harness/ux/commands/review_setup.py +47 -0
  277. mostlyright/data_harness/ux/commands/search.py +440 -0
  278. mostlyright/data_harness/ux/commands/show.py +61 -0
  279. mostlyright/data_harness/ux/commands/whoami.py +37 -0
  280. mostlyright/data_harness/ux/credential_native.py +551 -0
  281. mostlyright/data_harness/ux/credential_store.py +1055 -0
  282. mostlyright/data_harness/ux/credentials.py +631 -0
  283. mostlyright/data_harness/ux/diffing.py +444 -0
  284. mostlyright/data_harness/ux/headline.py +671 -0
  285. mostlyright/data_harness/ux/hosted_acquisition.py +974 -0
  286. mostlyright/data_harness/ux/hosted_run_status.py +619 -0
  287. mostlyright/data_harness/ux/inventory.py +427 -0
  288. mostlyright/data_harness/ux/local_review.py +375 -0
  289. mostlyright/data_harness/ux/login.py +691 -0
  290. mostlyright/data_harness/ux/path_kind.py +147 -0
  291. mostlyright/data_harness/ux/peek.py +1000 -0
  292. mostlyright/data_harness/ux/plain_file.py +178 -0
  293. mostlyright/data_harness/ux/plan_check.py +311 -0
  294. mostlyright/data_harness/ux/preflight.py +918 -0
  295. mostlyright/data_harness/ux/readers.py +1124 -0
  296. mostlyright/data_harness/ux/remediation.py +2195 -0
  297. mostlyright/data_harness/ux/render.py +657 -0
  298. mostlyright/data_harness/ux/workload.py +1077 -0
  299. mostlyright/data_harness/viewer.py +3713 -0
  300. mostlyright/data_harness/visual_run/__init__.py +83 -0
  301. mostlyright/data_harness/visual_run/authoring.py +235 -0
  302. mostlyright/data_harness/visual_run/contracts.py +673 -0
  303. mostlyright/data_harness/visual_run/materialize.py +486 -0
  304. mostlyright/data_harness/visual_run/observations.py +874 -0
  305. mostlyright/data_harness/visual_run/query.py +259 -0
  306. mostlyright/data_harness/visual_run/reducer.py +280 -0
  307. mostlyright/data_harness/visual_run/sdk.py +892 -0
  308. mostlyright/data_harness/visual_run/store.py +584 -0
  309. mostlyright/data_harness/visual_run/transport.py +239 -0
  310. mostlyright/data_harness/watch.py +2999 -0
  311. mostlyright_data-0.9.0.dist-info/METADATA +607 -0
  312. mostlyright_data-0.9.0.dist-info/RECORD +314 -0
  313. mostlyright_data-0.9.0.dist-info/WHEEL +4 -0
  314. 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
+ ]