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,259 @@
1
+ """Bounded read-only query boundary over exact sealed Parquet bytes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ from collections.abc import Mapping, Sequence
8
+ from dataclasses import dataclass
9
+ from typing import Any
10
+
11
+ import pyarrow as pa
12
+ import pyarrow.compute as pc
13
+
14
+ from mostlyright.data_harness.plan_graph import HARD_MAX_OUTPUT_ROWS
15
+ from mostlyright.data_harness.rowset import open_sealed_parquet, render_filtered_window
16
+ from mostlyright.data_harness.visual_run.observations import (
17
+ safe_column_for_display,
18
+ safe_for_display,
19
+ )
20
+
21
+ MAX_QUERY_ROWS = 200
22
+ MAX_QUERY_BYTES = 1024 * 1024
23
+ MAX_PREDICATES = 8
24
+ QUERY_OPERATORS = frozenset({"eq", "ne", "lt", "le", "gt", "ge", "is_null", "is_not_null"})
25
+ QUERY_DISPLAY_DISPOSITIONS = frozenset({"full", "redacted", "aggregate_only", "schema_only"})
26
+ PUBLIC_RIGHTS = frozenset({"public_domain", "permissive_license"})
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class QueryEvidence:
31
+ """Exact verified evidence that authorizes and explains a row-query result."""
32
+
33
+ candidate_digest: str
34
+ table_sha256: str
35
+ display: str
36
+ rights_statuses: tuple[str, ...]
37
+ classifications: tuple[str, ...]
38
+ profiles: Mapping[str, Mapping[str, Any]]
39
+ findings: tuple[Mapping[str, Any], ...]
40
+ lineage: Mapping[str, Mapping[str, Any]]
41
+ evidence_sha256: Mapping[str, str]
42
+
43
+ def validate(self, parquet_raw: bytes, available: Sequence[str]) -> None:
44
+ digests = (self.candidate_digest, self.table_sha256, *self.evidence_sha256.values())
45
+ if any(
46
+ not isinstance(digest, str)
47
+ or len(digest) != 64
48
+ or any(character not in "0123456789abcdef" for character in digest)
49
+ for digest in digests
50
+ ):
51
+ raise QueryError("QUERY_EVIDENCE", "verified evidence digests are invalid")
52
+ if hashlib.sha256(parquet_raw).hexdigest() != self.table_sha256:
53
+ raise QueryError("QUERY_EVIDENCE", "row bytes differ from verified evidence")
54
+ if self.display not in QUERY_DISPLAY_DISPOSITIONS:
55
+ raise QueryError("QUERY_EVIDENCE", "display disposition is invalid")
56
+ if (
57
+ self.display != "full"
58
+ or not self.rights_statuses
59
+ or any(status not in PUBLIC_RIGHTS for status in self.rights_statuses)
60
+ or not self.classifications
61
+ or any(classification != "public" for classification in self.classifications)
62
+ ):
63
+ raise QueryError(
64
+ "QUERY_DISPLAY_POLICY", "verified source governance forbids row-level display"
65
+ )
66
+ if set(self.profiles) != set(available) or set(self.lineage) != set(available):
67
+ raise QueryError(
68
+ "QUERY_EVIDENCE", "verified profile or lineage does not cover the sealed schema"
69
+ )
70
+ if any(not safe_column_for_display(column) for column in available):
71
+ raise QueryError("QUERY_SENSITIVE_COLUMN", "sealed schema contains a secret coordinate")
72
+
73
+
74
+ class QueryError(ValueError):
75
+ def __init__(self, code: str, detail: str) -> None:
76
+ self.code = code
77
+ super().__init__(detail)
78
+
79
+
80
+ def _request(
81
+ columns: Sequence[str], predicates: Sequence[Mapping[str, Any]], offset: int, limit: int
82
+ ) -> None:
83
+ if (
84
+ not columns
85
+ or len(columns) > 256
86
+ or len(columns) != len(set(columns))
87
+ or any(not isinstance(column, str) for column in columns)
88
+ ):
89
+ raise QueryError("QUERY_COLUMNS", "selected columns are invalid")
90
+ if isinstance(offset, bool) or not isinstance(offset, int) or offset < 0:
91
+ raise QueryError("QUERY_WINDOW", "offset is invalid")
92
+ if isinstance(limit, bool) or not isinstance(limit, int) or not 1 <= limit <= MAX_QUERY_ROWS:
93
+ raise QueryError("QUERY_WINDOW", "limit is invalid")
94
+ if len(predicates) > MAX_PREDICATES:
95
+ raise QueryError("QUERY_FILTER_BOUNDS", "too many predicates")
96
+ for predicate in predicates:
97
+ if not isinstance(predicate, Mapping) or set(predicate) not in (
98
+ {"column", "op"},
99
+ {"column", "op", "value"},
100
+ ):
101
+ raise QueryError("QUERY_FILTER", "predicate fields are invalid")
102
+ if not isinstance(predicate["column"], str) or predicate["op"] not in QUERY_OPERATORS:
103
+ raise QueryError("QUERY_FILTER", "predicate coordinate is invalid")
104
+ nullary = predicate["op"] in {"is_null", "is_not_null"}
105
+ if nullary != ("value" not in predicate):
106
+ raise QueryError("QUERY_FILTER", "predicate value shape is invalid")
107
+ if "value" in predicate and type(predicate["value"]) not in {bool, int, float, str}:
108
+ raise QueryError("QUERY_FILTER", "predicate value is not a scalar")
109
+
110
+
111
+ def query_sealed_parquet(
112
+ parquet_raw: bytes,
113
+ *,
114
+ columns: Sequence[str],
115
+ predicates: Sequence[Mapping[str, Any]] = (),
116
+ offset: int = 0,
117
+ limit: int = 50,
118
+ evidence: QueryEvidence,
119
+ ) -> dict[str, Any]:
120
+ """Return one deterministic physical-order window; never SQL or arbitrary expressions."""
121
+
122
+ _request(columns, predicates, offset, limit)
123
+ parquet, available, total = open_sealed_parquet(
124
+ parquet_raw,
125
+ max_columns=256,
126
+ max_rows=HARD_MAX_OUTPUT_ROWS,
127
+ max_uncompressed_bytes=256 * 1024 * 1024,
128
+ )
129
+ evidence.validate(parquet_raw, available)
130
+ unknown = sorted(({*columns} | {str(item["column"]) for item in predicates}) - set(available))
131
+ if unknown:
132
+ raise QueryError("QUERY_COLUMN_UNKNOWN", f"columns are absent: {unknown}")
133
+ if any(
134
+ not safe_column_for_display(column)
135
+ for column in (*columns, *(str(item["column"]) for item in predicates))
136
+ ):
137
+ raise QueryError("QUERY_SENSITIVE_COLUMN", "selected column is a secret coordinate")
138
+ if any(
139
+ "value" in predicate and not safe_for_display(predicate["value"])
140
+ for predicate in predicates
141
+ ):
142
+ raise QueryError("QUERY_SENSITIVE_VALUE", "filter contains credential-bearing text")
143
+
144
+ inspected = {*columns, *(str(item["column"]) for item in predicates)}
145
+ for batch in parquet.iter_batches(batch_size=1_024, columns=sorted(inspected)):
146
+ for column in inspected:
147
+ array = batch.column(batch.schema.get_field_index(column))
148
+ if any(
149
+ scalar.is_valid
150
+ and isinstance(scalar.as_py(), str)
151
+ and not safe_for_display(scalar.as_py())
152
+ for scalar in array
153
+ ):
154
+ raise QueryError(
155
+ "QUERY_SENSITIVE_VALUE", "selected data contains credential-bearing text"
156
+ )
157
+
158
+ def select(batch: pa.RecordBatch) -> pa.RecordBatch | pa.Table:
159
+ mask: pa.Array | pa.ChunkedArray | None = None
160
+ for predicate in predicates:
161
+ array = batch.column(batch.schema.get_field_index(str(predicate["column"])))
162
+ op = str(predicate["op"])
163
+ if op == "is_null":
164
+ condition = pc.is_null(array)
165
+ elif op == "is_not_null":
166
+ condition = pc.invert(pc.is_null(array))
167
+ else:
168
+ functions = {
169
+ "eq": pc.equal,
170
+ "ne": pc.not_equal,
171
+ "lt": pc.less,
172
+ "le": pc.less_equal,
173
+ "gt": pc.greater,
174
+ "ge": pc.greater_equal,
175
+ }
176
+ try:
177
+ condition = functions[op](array, predicate["value"])
178
+ except (ArrowInvalid, ArrowNotImplementedError) as exc: # type: ignore[name-defined]
179
+ raise QueryError(
180
+ "QUERY_FILTER_TYPE", "filter value does not match column"
181
+ ) from exc
182
+ mask = condition if mask is None else pc.and_kleene(mask, condition)
183
+ retained: pa.RecordBatch | pa.Table = batch if mask is None else batch.filter(mask)
184
+ return retained.select(columns)
185
+
186
+ try:
187
+ matched, rows = render_filtered_window(
188
+ parquet,
189
+ columns,
190
+ select=select,
191
+ offset=offset,
192
+ limit=limit,
193
+ exact_match_count=total if not predicates else None,
194
+ )
195
+ except QueryError:
196
+ raise
197
+ payload = {
198
+ "schema_version": "sealed-row-query.v1",
199
+ "columns": list(columns),
200
+ "total_row_count": total,
201
+ "matched_row_count": matched,
202
+ "offset": offset,
203
+ "limit": limit,
204
+ "rows": rows,
205
+ "truncated": offset + len(rows) < matched,
206
+ "evidence": {
207
+ "candidate_digest": evidence.candidate_digest,
208
+ "table_sha256": evidence.table_sha256,
209
+ "display": evidence.display,
210
+ "rights_statuses": list(evidence.rights_statuses),
211
+ "classifications": list(evidence.classifications),
212
+ "profiles": [dict(evidence.profiles[column]) for column in columns],
213
+ "findings": [
214
+ dict(finding)
215
+ for finding in evidence.findings
216
+ if finding.get("column") in {None, *columns}
217
+ ],
218
+ "lineage": [dict(evidence.lineage[column]) for column in columns],
219
+ "member_sha256": dict(evidence.evidence_sha256),
220
+ },
221
+ }
222
+ while (
223
+ len(json.dumps(payload, ensure_ascii=False, separators=(",", ":")).encode())
224
+ > MAX_QUERY_BYTES
225
+ ):
226
+ if not payload["rows"]:
227
+ raise QueryError("QUERY_RESPONSE_BOUNDS", "query metadata exceeds response budget")
228
+ payload["rows"] = payload["rows"][:-1]
229
+ payload["truncated"] = True
230
+ return payload
231
+
232
+
233
+ def sealed_parquet_columns(parquet_raw: bytes, *, evidence: QueryEvidence) -> tuple[str, ...]:
234
+ """Return the bounded sealed schema for the viewer's initial explorer request."""
235
+
236
+ _parquet, available, _total = open_sealed_parquet(
237
+ parquet_raw,
238
+ max_columns=256,
239
+ max_rows=HARD_MAX_OUTPUT_ROWS,
240
+ max_uncompressed_bytes=256 * 1024 * 1024,
241
+ )
242
+ evidence.validate(parquet_raw, available)
243
+ return tuple(available)
244
+
245
+
246
+ # Local aliases keep pyarrow exceptions out of the public contract.
247
+ ArrowInvalid = pa.ArrowInvalid
248
+ ArrowNotImplementedError = pa.ArrowNotImplementedError
249
+
250
+ __all__ = [
251
+ "MAX_PREDICATES",
252
+ "MAX_QUERY_BYTES",
253
+ "MAX_QUERY_ROWS",
254
+ "QUERY_OPERATORS",
255
+ "QueryError",
256
+ "QueryEvidence",
257
+ "query_sealed_parquet",
258
+ "sealed_parquet_columns",
259
+ ]
@@ -0,0 +1,280 @@
1
+ """Pure deterministic reduction of visual-run events."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping
6
+ from dataclasses import dataclass, replace
7
+ from types import MappingProxyType
8
+
9
+ from mostlyright.data_harness.visual_run.contracts import (
10
+ EVENT_REGISTRY,
11
+ ArtifactReference,
12
+ TerminalError,
13
+ VisualRunError,
14
+ VisualRunEvent,
15
+ )
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class CellState:
20
+ cell_id: str
21
+ cell_type: str
22
+ version: int
23
+ phase: str
24
+ stage: str
25
+ status: str
26
+ source_length: int = 0
27
+ source: ArtifactReference | None = None
28
+ execution_count: int | None = None
29
+ outputs: tuple[ArtifactReference, ...] = ()
30
+ error: TerminalError | None = None
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class ObjectState:
35
+ """Complete current state for every non-cell registry object family."""
36
+
37
+ object_id: str
38
+ kind: str
39
+ version: int
40
+ phase: str
41
+ stage: str
42
+ status: str
43
+ payload: Mapping[str, object]
44
+ artifact: ArtifactReference | None = None
45
+ error: TerminalError | None = None
46
+
47
+
48
+ @dataclass(frozen=True)
49
+ class ReducedRun:
50
+ run_id: str | None = None
51
+ cursor: int = 0
52
+ lifecycle: str = "waiting"
53
+ active_object_id: str | None = None
54
+ cells: Mapping[str, CellState] = MappingProxyType({})
55
+ observations: tuple[ArtifactReference, ...] = ()
56
+ quality: tuple[int, int] | None = None
57
+ candidate_digest: str | None = None
58
+ table_ready: bool = False
59
+ terminal_error: TerminalError | None = None
60
+ terminal: bool = False
61
+ object_versions: Mapping[str, int] = MappingProxyType({})
62
+ object_kinds: Mapping[str, str] = MappingProxyType({})
63
+ objects: Mapping[str, ObjectState] = MappingProxyType({})
64
+
65
+
66
+ def _fail(code: str, message: str) -> None:
67
+ raise VisualRunError(code, message)
68
+
69
+
70
+ def apply_event(state: ReducedRun, event: VisualRunEvent) -> ReducedRun:
71
+ if event.sequence != state.cursor + 1:
72
+ _fail("VISUAL_RUN_SEQUENCE_GAP", "reducer requires the next durable sequence")
73
+ if state.run_id is not None and event.run_id != state.run_id:
74
+ _fail("VISUAL_RUN_RUN_MISMATCH", "event belongs to another run")
75
+ if state.terminal:
76
+ _fail("VISUAL_RUN_TERMINAL_APPEND", "events cannot follow a terminal run event")
77
+ cells = dict(state.cells)
78
+ observations = state.observations
79
+ lifecycle = state.lifecycle
80
+ active = state.active_object_id
81
+ quality = state.quality
82
+ candidate_digest = state.candidate_digest
83
+ table_ready = state.table_ready
84
+ terminal_error = state.terminal_error
85
+ terminal = state.terminal
86
+ object_versions = dict(state.object_versions)
87
+ object_kinds = dict(state.object_kinds)
88
+ objects = dict(state.objects)
89
+
90
+ event_type = event.event_type
91
+ spec = EVENT_REGISTRY[event_type]
92
+ event_family = event_type.split(".", 1)[0]
93
+ current_version = object_versions.get(event.object_id)
94
+ if spec.object_rule == "create":
95
+ if current_version is not None:
96
+ _fail("VISUAL_RUN_OBJECT_EXISTS", "object creation was already recorded")
97
+ if event.previous_object_version is not None or event.object_version != 1:
98
+ _fail("VISUAL_RUN_OBJECT_VERSION", "object creation must start at version one")
99
+ else:
100
+ if current_version is None:
101
+ _fail("VISUAL_RUN_OBJECT_MISSING", "object update has no creation event")
102
+ if (
103
+ event.previous_object_version != current_version
104
+ or event.object_version != current_version + 1
105
+ ):
106
+ _fail("VISUAL_RUN_OBJECT_VERSION", "object update does not follow current version")
107
+ if event_type != "run.started" and state.run_id is None:
108
+ _fail("VISUAL_RUN_TRANSITION", "run must start before other events")
109
+ established_kind = object_kinds.get(event.object_id)
110
+ compatible_kinds = {established_kind}
111
+ if established_kind == "transformation":
112
+ compatible_kinds.add("stage")
113
+ if current_version is not None and event_family not in compatible_kinds:
114
+ _fail("VISUAL_RUN_OBJECT_KIND", "object update changes registry family")
115
+ if event_type == "run.started":
116
+ lifecycle = "researching"
117
+ elif event_type in {"run.completed", "run.failed", "run.interrupted"}:
118
+ lifecycle = event.status or "failed"
119
+ terminal_error = event.error
120
+ terminal = True
121
+ active = None
122
+ elif event_type == "phase.started":
123
+ lifecycle = event.phase
124
+ terminal_error = None
125
+ active = event.object_id
126
+ elif event_type == "phase.completed":
127
+ active = None
128
+ elif event_type == "phase.failed":
129
+ lifecycle = "failed"
130
+ terminal_error = event.error
131
+ active = None
132
+ terminal_error = event.error
133
+ elif event_type == "cell.created":
134
+ cells[event.object_id] = CellState(
135
+ event.object_id,
136
+ str(event.payload["cell_type"]),
137
+ event.object_version,
138
+ event.phase,
139
+ event.stage,
140
+ "writing",
141
+ )
142
+ active = event.object_id
143
+ elif event_type.startswith("cell."):
144
+ moved = False
145
+ cell = cells.get(event.object_id)
146
+ if cell is None:
147
+ _fail("VISUAL_RUN_OBJECT_MISSING", "cell update has no creation event")
148
+ if cell.status in {"complete", "failed"} and event_type not in {
149
+ "cell.source_revised",
150
+ "cell.moved",
151
+ }:
152
+ _fail("VISUAL_RUN_OBJECT_TERMINAL", "terminal cell cannot be changed")
153
+ if event_type in {"cell.source_extended", "cell.source_revised"}:
154
+ length = int(event.payload["source_length"])
155
+ if event_type == "cell.source_extended" and length < cell.source_length:
156
+ _fail("VISUAL_RUN_TRANSITION", "source length cannot shrink")
157
+ if event.artifact is None:
158
+ _fail("VISUAL_RUN_ARTIFACT_REQUIRED", "source update requires artifact")
159
+ cell = replace(
160
+ cell,
161
+ source_length=length,
162
+ source=event.artifact,
163
+ status="writing",
164
+ execution_count=None,
165
+ outputs=(),
166
+ error=None,
167
+ )
168
+ active = event.object_id
169
+ elif event_type == "cell.moved":
170
+ after = str(event.payload["after_cell_id"])
171
+ if after == event.object_id or after not in cells:
172
+ _fail("VISUAL_RUN_TRANSITION", "cell move anchor is invalid")
173
+ moving = replace(cells.pop(event.object_id), version=event.object_version)
174
+ reordered: dict[str, CellState] = {}
175
+ for identifier, existing in cells.items():
176
+ reordered[identifier] = existing
177
+ if identifier == after:
178
+ reordered[event.object_id] = moving
179
+ cells = reordered
180
+ cell = moving
181
+ moved = True
182
+ elif event_type == "cell.execution_started":
183
+ cell = replace(
184
+ cell, execution_count=int(event.payload["execution_count"]), status="executing"
185
+ )
186
+ elif event_type == "cell.output_appended":
187
+ if event.artifact is None:
188
+ _fail("VISUAL_RUN_ARTIFACT_REQUIRED", "output requires artifact")
189
+ index = int(event.payload["output_index"])
190
+ if index != len(cell.outputs):
191
+ _fail("VISUAL_RUN_TRANSITION", "appended output index must be next")
192
+ cell = replace(cell, outputs=(*cell.outputs, event.artifact), status="executing")
193
+ elif event_type == "cell.output_replaced":
194
+ if event.artifact is None:
195
+ _fail("VISUAL_RUN_ARTIFACT_REQUIRED", "output requires artifact")
196
+ index = int(event.payload["output_index"])
197
+ if not 0 <= index < len(cell.outputs):
198
+ _fail("VISUAL_RUN_TRANSITION", "replacement output index is absent")
199
+ outputs = list(cell.outputs)
200
+ outputs[index] = event.artifact
201
+ cell = replace(cell, outputs=tuple(outputs), status="executing")
202
+ elif event_type == "cell.completed":
203
+ cell = replace(cell, status="complete")
204
+ active = None
205
+ elif event_type == "cell.failed":
206
+ cell = replace(cell, status="failed", error=event.error)
207
+ active = None
208
+ if not moved:
209
+ cells[event.object_id] = replace(cell, version=event.object_version)
210
+ elif event_type in {
211
+ "source.schema_observed",
212
+ "source.observation_ready",
213
+ "stage.observation_ready",
214
+ }:
215
+ if event.artifact is not None:
216
+ observations = (*observations, event.artifact)
217
+ elif event_type == "quality.completed":
218
+ quality = (int(event.payload["checks_passed"]), int(event.payload["checks_total"]))
219
+ elif event_type == "build.sealed":
220
+ candidate_digest = str(event.payload["candidate_digest"])
221
+ lifecycle = "building"
222
+ elif event_type == "build.verified":
223
+ digest = str(event.payload["candidate_digest"])
224
+ if candidate_digest is None:
225
+ _fail("VISUAL_RUN_TRANSITION", "build must be sealed before verification")
226
+ if digest != candidate_digest:
227
+ _fail("VISUAL_RUN_BUILD_MISMATCH", "verified digest differs from sealed digest")
228
+ candidate_digest = digest
229
+ table_ready = True
230
+ lifecycle = "table-ready"
231
+
232
+ if event.status is not None:
233
+ status = event.status
234
+ elif event_type.endswith(".failed"):
235
+ status = "failed"
236
+ elif event_type == "source.rejected":
237
+ status = "rejected"
238
+ elif event_type.endswith((".completed", ".verified", ".written", ".materialized")) or (
239
+ event_type.endswith((".observation_ready", ".schema_observed"))
240
+ ):
241
+ status = "complete"
242
+ else:
243
+ status = "active"
244
+ objects[event.object_id] = ObjectState(
245
+ object_id=event.object_id,
246
+ kind=established_kind or event_family,
247
+ version=event.object_version,
248
+ phase=event.phase,
249
+ stage=event.stage,
250
+ status=status,
251
+ payload=event.payload,
252
+ artifact=event.artifact,
253
+ error=event.error,
254
+ )
255
+
256
+ object_versions[event.object_id] = event.object_version
257
+ object_kinds.setdefault(event.object_id, event_family)
258
+ return ReducedRun(
259
+ run_id=event.run_id,
260
+ cursor=event.sequence,
261
+ lifecycle=lifecycle,
262
+ active_object_id=active,
263
+ cells=MappingProxyType(cells),
264
+ observations=observations,
265
+ quality=quality,
266
+ candidate_digest=candidate_digest,
267
+ table_ready=table_ready,
268
+ terminal_error=terminal_error,
269
+ terminal=terminal,
270
+ object_versions=MappingProxyType(object_versions),
271
+ object_kinds=MappingProxyType(object_kinds),
272
+ objects=MappingProxyType(objects),
273
+ )
274
+
275
+
276
+ def reduce_events(events: tuple[VisualRunEvent, ...] | list[VisualRunEvent]) -> ReducedRun:
277
+ state = ReducedRun()
278
+ for event in events:
279
+ state = apply_event(state, event)
280
+ return state