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,56 @@
1
+ """Core contracts and local durable state for resumable engine-wide ingestion."""
2
+
3
+ from mostlyright.data_harness.ingestion.contracts import (
4
+ CHUNK_REF_SCHEMA,
5
+ INGESTION_MANIFEST_SCHEMA,
6
+ SOURCE_VERSION_PIN_SCHEMA,
7
+ ChunkRef,
8
+ IngestionConflict,
9
+ IngestionCorrupt,
10
+ IngestionError,
11
+ IngestionManifestV1,
12
+ SourceVersionPinV1,
13
+ merkle_root_sha256,
14
+ )
15
+ from mostlyright.data_harness.ingestion.faults import DeterministicFaultInjector, FaultInjected
16
+ from mostlyright.data_harness.ingestion.gcs_store import (
17
+ GCSIngestionStore,
18
+ GCSObjectNotFound,
19
+ GCSObjectRead,
20
+ GCSObjectTransport,
21
+ GCSObjectVersion,
22
+ GCSPreconditionFailed,
23
+ GCSStorageError,
24
+ GoogleCloudStorageJSONTransport,
25
+ workload_identity_access_token,
26
+ )
27
+ from mostlyright.data_harness.ingestion.spool import SpooledChunk, spool_hash_stream
28
+ from mostlyright.data_harness.ingestion.store import IngestionStore, LocalIngestionStore
29
+
30
+ __all__ = [
31
+ "CHUNK_REF_SCHEMA",
32
+ "INGESTION_MANIFEST_SCHEMA",
33
+ "SOURCE_VERSION_PIN_SCHEMA",
34
+ "ChunkRef",
35
+ "DeterministicFaultInjector",
36
+ "FaultInjected",
37
+ "GCSIngestionStore",
38
+ "GCSObjectNotFound",
39
+ "GCSObjectRead",
40
+ "GCSObjectTransport",
41
+ "GCSObjectVersion",
42
+ "GCSPreconditionFailed",
43
+ "GCSStorageError",
44
+ "GoogleCloudStorageJSONTransport",
45
+ "IngestionConflict",
46
+ "IngestionCorrupt",
47
+ "IngestionError",
48
+ "IngestionManifestV1",
49
+ "IngestionStore",
50
+ "LocalIngestionStore",
51
+ "SourceVersionPinV1",
52
+ "SpooledChunk",
53
+ "merkle_root_sha256",
54
+ "spool_hash_stream",
55
+ "workload_identity_access_token",
56
+ ]
@@ -0,0 +1,461 @@
1
+ """Immutable contracts for the resumable ingestion engine.
2
+
3
+ The engine deliberately commits one source version at a time. A source is pinned before
4
+ the first byte is accepted, chunks are admitted in one gap-free ordinal sequence, and a
5
+ finalized manifest is an immutable, order-sensitive Merkle commitment. This leaves a
6
+ small, portable contract for remote stores while keeping the local store implementation
7
+ free to decide how it makes the individual state transitions durable.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import hashlib
13
+ import re
14
+ from collections.abc import Mapping
15
+ from dataclasses import dataclass, replace
16
+ from typing import Any, Self
17
+
18
+ from mostlyright.data_harness.canonical import canonical_json_bytes, canonical_sha256
19
+
20
+ SOURCE_VERSION_PIN_SCHEMA = "harness-source-version-pin.v1"
21
+ CHUNK_REF_SCHEMA = "harness-ingestion-chunk-ref.v1"
22
+ INGESTION_MANIFEST_SCHEMA = "harness-ingestion-manifest.v1"
23
+
24
+ MAX_SOURCE_ID_LENGTH = 128
25
+ MAX_SOURCE_VERSION_LENGTH = 512
26
+ MAX_RUN_ID_LENGTH = 128
27
+ MAX_CHUNK_BYTES = 1 << 40
28
+ MAX_RECORDS_PER_CHUNK = (1 << 63) - 1
29
+ MAX_MANIFEST_CHUNKS = 10_000
30
+
31
+ _SOURCE_ID = re.compile(r"[a-z][a-z0-9._-]{0,127}\Z")
32
+ _RUN_ID = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]{0,127}\Z")
33
+ _DIGEST = re.compile(r"[0-9a-f]{64}\Z")
34
+ # A source version crosses the durable checkpoint boundary. It is not an opaque connector header:
35
+ # ETags, cursors, signed URLs and bearer values have all appeared in such headers in practice.
36
+ # Admit only a public immutable content identity or a monotonically assigned public generation.
37
+ # The connector-specific canonical evidence remains in ``fence_sha256``; no raw response value is
38
+ # needed in a manifest.
39
+ _PUBLIC_SOURCE_VERSION = re.compile(
40
+ r"(?:generation:(?:0|[1-9][0-9]{0,31})|(?:etag|version)-sha256:[0-9a-f]{64})\Z"
41
+ )
42
+
43
+
44
+ class IngestionError(ValueError):
45
+ """A stable refusal or corruption error from the ingestion core."""
46
+
47
+ def __init__(self, code: str, path: str, detail: str) -> None:
48
+ self.code = code
49
+ self.path = path
50
+ self.detail = detail
51
+ super().__init__(f"{path}: {detail} [{code}]")
52
+
53
+
54
+ class IngestionConflict(IngestionError):
55
+ """A fenced transition did not apply to the state the caller observed."""
56
+
57
+
58
+ class IngestionCorrupt(IngestionError):
59
+ """Durable bytes no longer satisfy the ingestion contract."""
60
+
61
+
62
+ def _require_digest(value: object, path: str) -> str:
63
+ if not isinstance(value, str) or _DIGEST.fullmatch(value) is None:
64
+ raise IngestionError("INGESTION_DIGEST", path, "must be a lowercase SHA-256 digest")
65
+ return value
66
+
67
+
68
+ def _require_text(value: object, path: str, *, maximum: int) -> str:
69
+ if (
70
+ not isinstance(value, str)
71
+ or not value
72
+ or len(value) > maximum
73
+ or any(ord(character) < 0x20 or 0xD800 <= ord(character) <= 0xDFFF for character in value)
74
+ ):
75
+ raise IngestionError("INGESTION_TEXT", path, "must be a bounded non-control string")
76
+ return value
77
+
78
+
79
+ def _require_exact_mapping(value: object, keys: frozenset[str], path: str) -> Mapping[str, Any]:
80
+ if not isinstance(value, Mapping) or set(value) != keys:
81
+ raise IngestionError("INGESTION_DOCUMENT", path, "has unsupported or missing members")
82
+ return value
83
+
84
+
85
+ @dataclass(frozen=True)
86
+ class SourceVersionPinV1:
87
+ """The exact source revision and acquisition policy a run is allowed to consume.
88
+
89
+ ``source_version`` is one non-secret immutable identity: a public generation or a digest of
90
+ a public ETag/version. ``fence_sha256`` binds the connector-specific version evidence after
91
+ canonicalization. ``acquisition_policy_sha256`` binds the reviewed connector policy. Neither
92
+ permits a raw header/cursor or a later source version to enter durable resume state.
93
+ """
94
+
95
+ source_id: str
96
+ source_version: str
97
+ fence_sha256: str
98
+ acquisition_policy_sha256: str
99
+ schema_version: str = SOURCE_VERSION_PIN_SCHEMA
100
+
101
+ def __post_init__(self) -> None:
102
+ if self.schema_version != SOURCE_VERSION_PIN_SCHEMA:
103
+ raise IngestionError(
104
+ "INGESTION_SOURCE_SCHEMA",
105
+ "source_version_pin.schema_version",
106
+ "is unsupported",
107
+ )
108
+ if not isinstance(self.source_id, str) or _SOURCE_ID.fullmatch(self.source_id) is None:
109
+ raise IngestionError(
110
+ "INGESTION_SOURCE_ID",
111
+ "source_version_pin.source_id",
112
+ "is invalid",
113
+ )
114
+ if (
115
+ not isinstance(self.source_version, str)
116
+ or len(self.source_version) > MAX_SOURCE_VERSION_LENGTH
117
+ or _PUBLIC_SOURCE_VERSION.fullmatch(self.source_version) is None
118
+ ):
119
+ raise IngestionError(
120
+ "INGESTION_SOURCE_VERSION",
121
+ "source_version_pin.source_version",
122
+ "must be a public generation or a SHA-256-pinned public ETag/version",
123
+ )
124
+ _require_digest(self.fence_sha256, "source_version_pin.fence_sha256")
125
+ _require_digest(
126
+ self.acquisition_policy_sha256,
127
+ "source_version_pin.acquisition_policy_sha256",
128
+ )
129
+
130
+ @property
131
+ def digest(self) -> str:
132
+ return canonical_sha256(self.to_dict())
133
+
134
+ def to_dict(self) -> dict[str, str]:
135
+ return {
136
+ "schema_version": self.schema_version,
137
+ "source_id": self.source_id,
138
+ "source_version": self.source_version,
139
+ "fence_sha256": self.fence_sha256,
140
+ "acquisition_policy_sha256": self.acquisition_policy_sha256,
141
+ }
142
+
143
+ @classmethod
144
+ def from_dict(cls, value: object) -> Self:
145
+ document = _require_exact_mapping(
146
+ value,
147
+ frozenset(
148
+ {
149
+ "schema_version",
150
+ "source_id",
151
+ "source_version",
152
+ "fence_sha256",
153
+ "acquisition_policy_sha256",
154
+ }
155
+ ),
156
+ "source_version_pin",
157
+ )
158
+ return cls(
159
+ schema_version=document["schema_version"],
160
+ source_id=document["source_id"],
161
+ source_version=document["source_version"],
162
+ fence_sha256=document["fence_sha256"],
163
+ acquisition_policy_sha256=document["acquisition_policy_sha256"],
164
+ )
165
+
166
+
167
+ @dataclass(frozen=True)
168
+ class ChunkRef:
169
+ """One immutable byte object, assigned exactly one position in a pinned run."""
170
+
171
+ ordinal: int
172
+ content_sha256: str
173
+ byte_length: int
174
+ record_count: int
175
+ source_pin_sha256: str
176
+ schema_version: str = CHUNK_REF_SCHEMA
177
+
178
+ def __post_init__(self) -> None:
179
+ if self.schema_version != CHUNK_REF_SCHEMA:
180
+ raise IngestionError(
181
+ "INGESTION_CHUNK_SCHEMA", "chunk_ref.schema_version", "is unsupported"
182
+ )
183
+ if isinstance(self.ordinal, bool) or not isinstance(self.ordinal, int) or self.ordinal < 0:
184
+ raise IngestionError(
185
+ "INGESTION_ORDINAL", "chunk_ref.ordinal", "must be a non-negative integer"
186
+ )
187
+ _require_digest(self.content_sha256, "chunk_ref.content_sha256")
188
+ _require_digest(self.source_pin_sha256, "chunk_ref.source_pin_sha256")
189
+ if (
190
+ isinstance(self.byte_length, bool)
191
+ or not isinstance(self.byte_length, int)
192
+ or not 0 <= self.byte_length <= MAX_CHUNK_BYTES
193
+ ):
194
+ raise IngestionError(
195
+ "INGESTION_CHUNK_BYTES",
196
+ "chunk_ref.byte_length",
197
+ "is outside the supported range",
198
+ )
199
+ if (
200
+ isinstance(self.record_count, bool)
201
+ or not isinstance(self.record_count, int)
202
+ or not 0 <= self.record_count <= MAX_RECORDS_PER_CHUNK
203
+ ):
204
+ raise IngestionError(
205
+ "INGESTION_RECORD_COUNT",
206
+ "chunk_ref.record_count",
207
+ "is outside the supported range",
208
+ )
209
+
210
+ @property
211
+ def digest(self) -> str:
212
+ return canonical_sha256(self.to_dict())
213
+
214
+ def to_dict(self) -> dict[str, str | int]:
215
+ return {
216
+ "schema_version": self.schema_version,
217
+ "ordinal": self.ordinal,
218
+ "content_sha256": self.content_sha256,
219
+ "byte_length": self.byte_length,
220
+ "record_count": self.record_count,
221
+ "source_pin_sha256": self.source_pin_sha256,
222
+ }
223
+
224
+ @classmethod
225
+ def from_dict(cls, value: object) -> Self:
226
+ document = _require_exact_mapping(
227
+ value,
228
+ frozenset(
229
+ {
230
+ "schema_version",
231
+ "ordinal",
232
+ "content_sha256",
233
+ "byte_length",
234
+ "record_count",
235
+ "source_pin_sha256",
236
+ }
237
+ ),
238
+ "chunk_ref",
239
+ )
240
+ return cls(
241
+ schema_version=document["schema_version"],
242
+ ordinal=document["ordinal"],
243
+ content_sha256=document["content_sha256"],
244
+ byte_length=document["byte_length"],
245
+ record_count=document["record_count"],
246
+ source_pin_sha256=document["source_pin_sha256"],
247
+ )
248
+
249
+
250
+ def merkle_root_sha256(chunks: tuple[ChunkRef, ...]) -> str:
251
+ """Return an order-sensitive, domain-separated Merkle root for ``chunks``."""
252
+
253
+ if not chunks:
254
+ return hashlib.sha256(b"mostlyright-ingestion-merkle-v1\x00empty").hexdigest()
255
+ level = [
256
+ hashlib.sha256(
257
+ b"mostlyright-ingestion-merkle-v1\x00leaf\x00" + canonical_json_bytes(chunk.to_dict())
258
+ ).digest()
259
+ for chunk in chunks
260
+ ]
261
+ while len(level) > 1:
262
+ if len(level) % 2:
263
+ level.append(level[-1])
264
+ level = [
265
+ hashlib.sha256(b"mostlyright-ingestion-merkle-v1\x00node\x00" + left + right).digest()
266
+ for left, right in zip(level[::2], level[1::2], strict=True)
267
+ ]
268
+ return level[0].hex()
269
+
270
+
271
+ @dataclass(frozen=True)
272
+ class IngestionManifestV1:
273
+ """An append-only source-pinned state machine.
274
+
275
+ Each persisted state points at its immediate predecessor digest. That turns an
276
+ ambiguous successful replacement into a safe idempotent retry without allowing a
277
+ caller holding an arbitrary stale fence to overwrite unrelated work.
278
+ """
279
+
280
+ run_id: str
281
+ source_pin: SourceVersionPinV1
282
+ chunks: tuple[ChunkRef, ...]
283
+ state: str
284
+ previous_manifest_sha256: str | None
285
+ merkle_root_sha256: str | None
286
+ schema_version: str = INGESTION_MANIFEST_SCHEMA
287
+
288
+ def __post_init__(self) -> None:
289
+ if self.schema_version != INGESTION_MANIFEST_SCHEMA:
290
+ raise IngestionError(
291
+ "INGESTION_MANIFEST_SCHEMA", "ingestion_manifest.schema_version", "is unsupported"
292
+ )
293
+ if not isinstance(self.run_id, str) or _RUN_ID.fullmatch(self.run_id) is None:
294
+ raise IngestionError("INGESTION_RUN_ID", "ingestion_manifest.run_id", "is invalid")
295
+ if not isinstance(self.source_pin, SourceVersionPinV1):
296
+ raise IngestionError(
297
+ "INGESTION_SOURCE_PIN",
298
+ "ingestion_manifest.source_pin",
299
+ "must be SourceVersionPinV1",
300
+ )
301
+ if not isinstance(self.chunks, tuple) or len(self.chunks) > MAX_MANIFEST_CHUNKS:
302
+ raise IngestionError(
303
+ "INGESTION_CHUNK_LIMIT",
304
+ "ingestion_manifest.chunks",
305
+ "must be a bounded tuple",
306
+ )
307
+ expected_pin = self.source_pin.digest
308
+ for ordinal, chunk in enumerate(self.chunks):
309
+ if not isinstance(chunk, ChunkRef):
310
+ raise IngestionError(
311
+ "INGESTION_CHUNK", "ingestion_manifest.chunks", "must contain ChunkRef values"
312
+ )
313
+ if chunk.ordinal != ordinal:
314
+ raise IngestionError(
315
+ "INGESTION_GAP",
316
+ "ingestion_manifest.chunks",
317
+ "ordinals must be ordered and gap-free",
318
+ )
319
+ if chunk.source_pin_sha256 != expected_pin:
320
+ raise IngestionError(
321
+ "INGESTION_SOURCE_DRIFT",
322
+ "ingestion_manifest.chunks",
323
+ "chunk belongs to a different source pin",
324
+ )
325
+ if self.previous_manifest_sha256 is not None:
326
+ _require_digest(
327
+ self.previous_manifest_sha256, "ingestion_manifest.previous_manifest_sha256"
328
+ )
329
+ if not self.chunks and self.state == "open" and self.previous_manifest_sha256 is not None:
330
+ raise IngestionError(
331
+ "INGESTION_INITIAL_PREDECESSOR",
332
+ "ingestion_manifest.previous_manifest_sha256",
333
+ "initial open state must not have a predecessor",
334
+ )
335
+ if self.chunks or self.state == "finalized":
336
+ if self.previous_manifest_sha256 is None:
337
+ raise IngestionError(
338
+ "INGESTION_PREDECESSOR",
339
+ "ingestion_manifest.previous_manifest_sha256",
340
+ "non-initial state must name its predecessor",
341
+ )
342
+ if self.state == "open":
343
+ if self.merkle_root_sha256 is not None:
344
+ raise IngestionError(
345
+ "INGESTION_OPEN_ROOT",
346
+ "ingestion_manifest.merkle_root_sha256",
347
+ "open state must not have a Merkle root",
348
+ )
349
+ elif self.state == "finalized":
350
+ if self.merkle_root_sha256 != merkle_root_sha256(self.chunks):
351
+ raise IngestionError(
352
+ "INGESTION_MERKLE",
353
+ "ingestion_manifest.merkle_root_sha256",
354
+ "does not commit exactly the ordered chunk sequence",
355
+ )
356
+ else:
357
+ raise IngestionError("INGESTION_STATE", "ingestion_manifest.state", "is unsupported")
358
+
359
+ @classmethod
360
+ def initial(cls, *, run_id: str, source_pin: SourceVersionPinV1) -> Self:
361
+ return cls(
362
+ run_id=run_id,
363
+ source_pin=source_pin,
364
+ chunks=(),
365
+ state="open",
366
+ previous_manifest_sha256=None,
367
+ merkle_root_sha256=None,
368
+ )
369
+
370
+ @property
371
+ def digest(self) -> str:
372
+ return canonical_sha256(self.to_dict())
373
+
374
+ @property
375
+ def is_finalized(self) -> bool:
376
+ return self.state == "finalized"
377
+
378
+ def append(self, chunk: ChunkRef) -> Self:
379
+ if self.is_finalized:
380
+ raise IngestionConflict(
381
+ "INGESTION_FINALIZED",
382
+ "ingestion_manifest.state",
383
+ "cannot append to a finalized run",
384
+ )
385
+ if chunk.source_pin_sha256 != self.source_pin.digest:
386
+ raise IngestionConflict(
387
+ "INGESTION_SOURCE_DRIFT",
388
+ "chunk_ref.source_pin_sha256",
389
+ "does not equal the manifest source pin",
390
+ )
391
+ if chunk.ordinal != len(self.chunks):
392
+ raise IngestionConflict(
393
+ "INGESTION_ORDINAL_FENCE",
394
+ "chunk_ref.ordinal",
395
+ "must equal the next gap-free manifest ordinal",
396
+ )
397
+ if len(self.chunks) >= MAX_MANIFEST_CHUNKS:
398
+ raise IngestionConflict(
399
+ "INGESTION_CHUNK_LIMIT", "ingestion_manifest.chunks", "cannot accept another chunk"
400
+ )
401
+ return replace(
402
+ self,
403
+ chunks=(*self.chunks, chunk),
404
+ previous_manifest_sha256=self.digest,
405
+ )
406
+
407
+ def finalize(self) -> Self:
408
+ if self.is_finalized:
409
+ return self
410
+ return replace(
411
+ self,
412
+ state="finalized",
413
+ previous_manifest_sha256=self.digest,
414
+ merkle_root_sha256=merkle_root_sha256(self.chunks),
415
+ )
416
+
417
+ def is_direct_successor_of(self, predecessor_digest: str) -> bool:
418
+ return self.previous_manifest_sha256 == predecessor_digest
419
+
420
+ def to_dict(self) -> dict[str, Any]:
421
+ return {
422
+ "schema_version": self.schema_version,
423
+ "run_id": self.run_id,
424
+ "source_pin": self.source_pin.to_dict(),
425
+ "chunks": [chunk.to_dict() for chunk in self.chunks],
426
+ "state": self.state,
427
+ "previous_manifest_sha256": self.previous_manifest_sha256,
428
+ "merkle_root_sha256": self.merkle_root_sha256,
429
+ }
430
+
431
+ @classmethod
432
+ def from_dict(cls, value: object) -> Self:
433
+ document = _require_exact_mapping(
434
+ value,
435
+ frozenset(
436
+ {
437
+ "schema_version",
438
+ "run_id",
439
+ "source_pin",
440
+ "chunks",
441
+ "state",
442
+ "previous_manifest_sha256",
443
+ "merkle_root_sha256",
444
+ }
445
+ ),
446
+ "ingestion_manifest",
447
+ )
448
+ raw_chunks = document["chunks"]
449
+ if not isinstance(raw_chunks, list):
450
+ raise IngestionError(
451
+ "INGESTION_DOCUMENT", "ingestion_manifest.chunks", "must be a list"
452
+ )
453
+ return cls(
454
+ schema_version=document["schema_version"],
455
+ run_id=document["run_id"],
456
+ source_pin=SourceVersionPinV1.from_dict(document["source_pin"]),
457
+ chunks=tuple(ChunkRef.from_dict(item) for item in raw_chunks),
458
+ state=document["state"],
459
+ previous_manifest_sha256=document["previous_manifest_sha256"],
460
+ merkle_root_sha256=document["merkle_root_sha256"],
461
+ )
@@ -0,0 +1,42 @@
1
+ """Deterministic crash simulation for ingestion durability tests."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections import Counter
6
+ from collections.abc import Mapping
7
+ from dataclasses import dataclass, field
8
+
9
+
10
+ class FaultInjected(RuntimeError):
11
+ """Raised exactly at a configured durability checkpoint."""
12
+
13
+ def __init__(self, checkpoint: str, occurrence: int) -> None:
14
+ self.checkpoint = checkpoint
15
+ self.occurrence = occurrence
16
+ super().__init__(f"fault injected at {checkpoint!r} occurrence {occurrence}")
17
+
18
+
19
+ @dataclass
20
+ class DeterministicFaultInjector:
21
+ """Fail a named checkpoint on a configured occurrence, once per injector.
22
+
23
+ It is intentionally stateful and explicit rather than probabilistic so every crash
24
+ window can be regression-tested and replayed exactly.
25
+ """
26
+
27
+ fail_on: Mapping[str, int] = field(default_factory=dict)
28
+ occurrences: Counter[str] = field(default_factory=Counter, init=False)
29
+ _fired: set[str] = field(default_factory=set, init=False)
30
+
31
+ def __post_init__(self) -> None:
32
+ for checkpoint, occurrence in self.fail_on.items():
33
+ if not isinstance(checkpoint, str) or not checkpoint:
34
+ raise ValueError("fault checkpoint must be a non-empty string")
35
+ if isinstance(occurrence, bool) or not isinstance(occurrence, int) or occurrence <= 0:
36
+ raise ValueError("fault occurrence must be a positive integer")
37
+
38
+ def checkpoint(self, name: str) -> None:
39
+ self.occurrences[name] += 1
40
+ if self.fail_on.get(name) == self.occurrences[name] and name not in self._fired:
41
+ self._fired.add(name)
42
+ raise FaultInjected(name, self.occurrences[name])