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,126 @@
1
+ """Typed engine-restriction contract: which operations an engine may not run.
2
+
3
+ An operation that provably cannot reach cross-engine byte parity on some engine must be
4
+ listed here so that engine gives a typed refusal instead of a silently divergent digest.
5
+
6
+ The table is currently empty. Every operation in the closed set reaches byte parity across
7
+ reference / pandas / polars, proven by the sealed goldens and the conformance matrix, so
8
+ this module is the mechanism with no data: a closed per-operation by per-engine table plus
9
+ a typed refusal.
10
+
11
+ ``_ENGINE_RESTRICTED_OPS`` maps an operation identity to the set of excluded engine names.
12
+ An entry is a claim that the operation provably cannot reach byte parity on those engines,
13
+ so adding one is a contract change: it needs a golden that establishes the divergence
14
+ before the entry is trusted.
15
+
16
+ Two states, not one
17
+ -------------------
18
+ "Cannot run this operation" has two distinct causes and they oblige different answers:
19
+
20
+ ``RESTRICTED`` -- an implementation exists but provably cannot reach cross-engine byte
21
+ parity, so it must refuse rather than diverge. Admitting a restriction needs evidence: a
22
+ golden that establishes the divergence comes first, then the table entry.
23
+
24
+ ``UNIMPLEMENTED`` -- no implementation exists for that engine at all. The obligation is an
25
+ implementation or an honest refusal, not a restriction entry. Recording an unimplemented
26
+ engine as restricted would claim evidence of divergence that was never gathered.
27
+
28
+ ``engine_operation_support`` returns an ``EngineOperationSupport`` member rather than a
29
+ bool so a caller cannot collapse the two into "unsupported". An enum is the right shape
30
+ here because the states are a closed set with no payload; a dataclass would invite fields
31
+ that drift from the table.
32
+
33
+ ``assert_engine_supports_plan`` is called once from ``pipeline.build_candidate`` after the
34
+ selected engine is resolved. With an empty table it is a no-op on every build; it raises the
35
+ instant a restricted operation meets an excluded engine.
36
+
37
+ The operation-identity vocabulary is the plan's operation surface: each cleaning step's
38
+ ``operation`` name (``trim`` / ``empty_to_null`` / ``rename`` / ``cast``), plus ``"join"``
39
+ and ``"select"``.
40
+
41
+ This module is deliberately dependency-light and dynamic-import-free: it lives under
42
+ ``backends/`` and is scanned by that package's static dynamic-import ban.
43
+ """
44
+
45
+ from __future__ import annotations
46
+
47
+ from enum import Enum
48
+ from typing import TYPE_CHECKING
49
+
50
+ if TYPE_CHECKING:
51
+ from mostlyright.data_harness.local_contracts import TablePlan
52
+
53
+ # Closed per-operation by per-engine restriction table; see the module docstring for the
54
+ # rule an entry has to meet. Deliberately empty: no divergence golden exists, so no claim
55
+ # of divergence is admissible.
56
+ _ENGINE_RESTRICTED_OPS: dict[str, frozenset[str]] = {}
57
+
58
+
59
+ class EngineOperationSupport(Enum):
60
+ """Whether an engine runs an operation, refuses it, or does not implement it."""
61
+
62
+ SUPPORTED = "supported"
63
+ RESTRICTED = "restricted"
64
+ UNIMPLEMENTED = "unimplemented"
65
+
66
+
67
+ def engine_operation_support(engine: str, operation: str) -> EngineOperationSupport:
68
+ """Report how ``engine`` stands with respect to ``operation``.
69
+
70
+ The engine set is read from the registry rather than re-listed here; a second list of
71
+ engine names would be exactly the duplication the format table just removed. The
72
+ registry is resolved lazily inside this function, matching the lazy ``BuildError``
73
+ import below, so this module stays dependency-light and the package's static
74
+ dynamic-import ban keeps passing.
75
+ """
76
+
77
+ from mostlyright.data_harness.backends.registry import registered_backends
78
+
79
+ if engine not in registered_backends():
80
+ return EngineOperationSupport.UNIMPLEMENTED
81
+ excluded = _ENGINE_RESTRICTED_OPS.get(operation)
82
+ if excluded is not None and engine in excluded:
83
+ return EngineOperationSupport.RESTRICTED
84
+ return EngineOperationSupport.SUPPORTED
85
+
86
+
87
+ def _plan_operations(plan: TablePlan) -> frozenset[str]:
88
+ """The set of op-identities a plan uses: each cleaning step's ``operation`` name, plus
89
+ ``"join"`` and ``"select"``. This is the surface the restriction table keys on."""
90
+
91
+ operations: set[str] = set()
92
+ for step in getattr(plan, "cleaning", ()):
93
+ operations.add(step.operation)
94
+ if getattr(plan, "join", None) is not None:
95
+ operations.add("join")
96
+ if getattr(plan, "select", None):
97
+ operations.add("select")
98
+ return frozenset(operations)
99
+
100
+
101
+ def assert_engine_supports_plan(engine: str, plan: TablePlan) -> None:
102
+ """Fail closed if ``plan`` uses an operation that is restricted on ``engine``.
103
+
104
+ For any operation the plan uses that appears in ``_ENGINE_RESTRICTED_OPS`` with
105
+ ``engine`` in its excluded set, raise a typed ``BuildError("ENGINE_RESTRICTED", ...)``
106
+ rather than let the build produce a divergent digest. With the table empty this is a
107
+ no-op on every plan.
108
+ """
109
+
110
+ if not _ENGINE_RESTRICTED_OPS:
111
+ # Fast path while the table is empty: the mechanism never fires.
112
+ return
113
+
114
+ for operation in _plan_operations(plan):
115
+ excluded = _ENGINE_RESTRICTED_OPS.get(operation)
116
+ if excluded is not None and engine in excluded:
117
+ # Lazy import keeps ``import backends`` free of a module-load dependency on
118
+ # ``pipeline`` (the same cycle-avoidance the registry uses). Only this failure
119
+ # path needs ``BuildError``.
120
+ from mostlyright.data_harness.pipeline import BuildError
121
+
122
+ raise BuildError(
123
+ "ENGINE_RESTRICTED",
124
+ f"operation {operation!r} is engine-restricted on {engine!r} "
125
+ "(it cannot reach cross-engine byte parity on that engine)",
126
+ )
@@ -0,0 +1,333 @@
1
+ """Strict JSON parsing, canonical bytes, and content digests.
2
+
3
+ This module is intentionally small and dependency-free. It is the Harness-private
4
+ canonicalization authority; Studio wire canonicalization remains owned by Studio's generated
5
+ client and contract set.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+ import json
12
+ import math
13
+ from typing import Any, NoReturn
14
+
15
+ MAX_CANONICAL_DEPTH = 64
16
+ MAX_CANONICAL_MEMBERS = 100_000
17
+ MAX_CANONICAL_STRING_LENGTH = 1_000_000
18
+ MAX_CANONICAL_INTEGER = (1 << 63) - 1
19
+
20
+
21
+ class CanonicalJSONError(ValueError):
22
+ """A typed strict-JSON or canonicalization failure."""
23
+
24
+ def __init__(self, code: str, path: str, detail: str) -> None:
25
+ self.code = code
26
+ self.path = path
27
+ self.detail = detail
28
+ super().__init__(f"{path}: {detail} [{code}]")
29
+
30
+
31
+ def parse_json(value: str | bytes | bytearray | memoryview) -> Any:
32
+ """Parse strict UTF-8 JSON while rejecting duplicate keys and non-integer numbers."""
33
+
34
+ return _parse(value, fractions="reject")
35
+
36
+
37
+ def parse_foreign_json(value: str | bytes | bytearray | memoryview) -> Any:
38
+ """Parse a third-party document with the same hardening, but accept fractional numbers.
39
+
40
+ The Harness data model is integer-only, and :func:`parse_json` enforces that because every
41
+ value it returns may end up inside a digested artifact. A document somebody else published is a
42
+ different problem. A STAC Collection is required by its own specification to carry
43
+ ``extent.spatial.bbox``, and real bboxes are fractional (``-178.1333``); so are ``gsd``,
44
+ ``center_wavelength`` and most ``summaries`` entries. Refusing them would mean refusing every
45
+ real ``/collections`` response, which is not a hardening decision but an outage.
46
+
47
+ Everything else stays exactly as strict: duplicate object keys, byte-order marks, non-strict
48
+ UTF-8, ``NaN``/``Infinity``, out-of-range integers, unpaired surrogates, depth, member count and
49
+ string length are all refused identically. Only finite fractional numbers become admissible, and
50
+ only as *read* values.
51
+
52
+ Floats still cannot reach an artifact. :func:`canonical_json_bytes` -- the sole input to every
53
+ digest in the repository -- refuses a float unchanged, so a float read here can be inspected by
54
+ a parser but can never be sealed, digested or written into a canonical document. That is the
55
+ boundary this pair of functions draws: read what the world publishes, write only what the model
56
+ admits.
57
+ """
58
+
59
+ return _parse(value, fractions="accept")
60
+
61
+
62
+ def _parse(value: str | bytes | bytearray | memoryview, *, fractions: str) -> Any:
63
+ if isinstance(value, str):
64
+ text = value
65
+ elif isinstance(value, (bytes, bytearray, memoryview)):
66
+ raw = bytes(value)
67
+ if raw.startswith(b"\xef\xbb\xbf"):
68
+ raise CanonicalJSONError("JSON_BOM", "$", "UTF-8 byte-order marks are not allowed")
69
+ try:
70
+ text = raw.decode("utf-8", errors="strict")
71
+ except UnicodeDecodeError as exc:
72
+ raise CanonicalJSONError(
73
+ "JSON_UTF8",
74
+ "$",
75
+ f"input is not strict UTF-8 at byte {exc.start}",
76
+ ) from None
77
+ else:
78
+ raise CanonicalJSONError(
79
+ "JSON_INPUT_TYPE",
80
+ "$",
81
+ "JSON input must be text or a bytes-like value",
82
+ )
83
+
84
+ if text.startswith("\ufeff"):
85
+ raise CanonicalJSONError("JSON_BOM", "$", "Unicode byte-order marks are not allowed")
86
+ accept_fractions = fractions == "accept"
87
+ try:
88
+ parsed = json.loads(
89
+ text,
90
+ object_pairs_hook=_reject_duplicate_pairs,
91
+ parse_int=_parse_integer,
92
+ parse_float=_parse_fraction if accept_fractions else _reject_fraction,
93
+ parse_constant=_reject_constant,
94
+ )
95
+ except CanonicalJSONError:
96
+ raise
97
+ except json.JSONDecodeError as exc:
98
+ raise CanonicalJSONError(
99
+ "JSON_SYNTAX",
100
+ "$",
101
+ f"invalid JSON at line {exc.lineno}, column {exc.colno}",
102
+ ) from None
103
+ return _normalize_json(
104
+ parsed,
105
+ "$",
106
+ depth=0,
107
+ members=[0],
108
+ accept_fractions=accept_fractions,
109
+ )
110
+
111
+
112
+ def canonical_json_bytes(value: Any) -> bytes:
113
+ """Return deterministic UTF-8 JSON bytes for the strict Harness data model."""
114
+
115
+ normalized = _normalize_json(value, "$", depth=0, members=[0])
116
+ return json.dumps(
117
+ normalized,
118
+ ensure_ascii=False,
119
+ allow_nan=False,
120
+ sort_keys=True,
121
+ separators=(",", ":"),
122
+ ).encode("utf-8")
123
+
124
+
125
+ def parse_canonical_json(value: str | bytes | bytearray | memoryview) -> Any:
126
+ """Parse JSON and require that its source encoding is already canonical."""
127
+
128
+ if isinstance(value, str):
129
+ try:
130
+ raw = value.encode("utf-8", errors="strict")
131
+ except UnicodeEncodeError as exc:
132
+ raise CanonicalJSONError(
133
+ "JSON_UNICODE",
134
+ "$",
135
+ f"text contains an invalid Unicode code point at character {exc.start}",
136
+ ) from None
137
+ elif isinstance(value, (bytes, bytearray, memoryview)):
138
+ raw = bytes(value)
139
+ else:
140
+ raise CanonicalJSONError(
141
+ "JSON_INPUT_TYPE",
142
+ "$",
143
+ "JSON input must be text or a bytes-like value",
144
+ )
145
+ parsed = parse_json(raw)
146
+ if raw != canonical_json_bytes(parsed):
147
+ raise CanonicalJSONError(
148
+ "JSON_NONCANONICAL",
149
+ "$",
150
+ "JSON bytes are not the canonical UTF-8 representation",
151
+ )
152
+ return parsed
153
+
154
+
155
+ def canonical_sha256(value: Any) -> str:
156
+ """Return lowercase SHA-256 over :func:`canonical_json_bytes`."""
157
+
158
+ return hashlib.sha256(canonical_json_bytes(value)).hexdigest()
159
+
160
+
161
+ def sha256_bytes(value: bytes | bytearray | memoryview) -> str:
162
+ """Return lowercase SHA-256 over exact bytes."""
163
+
164
+ return hashlib.sha256(bytes(value)).hexdigest()
165
+
166
+
167
+ def _normalize_json(
168
+ value: Any,
169
+ path: str,
170
+ *,
171
+ depth: int,
172
+ members: list[int],
173
+ accept_fractions: bool = False,
174
+ ) -> Any:
175
+ if depth > MAX_CANONICAL_DEPTH:
176
+ raise CanonicalJSONError(
177
+ "JSON_DEPTH",
178
+ path,
179
+ f"nesting exceeds {MAX_CANONICAL_DEPTH} levels",
180
+ )
181
+ members[0] += 1
182
+ if members[0] > MAX_CANONICAL_MEMBERS:
183
+ raise CanonicalJSONError(
184
+ "JSON_MEMBERS",
185
+ path,
186
+ f"value exceeds {MAX_CANONICAL_MEMBERS} members",
187
+ )
188
+
189
+ if value is None or type(value) is bool:
190
+ return value
191
+ if type(value) is int:
192
+ if not -MAX_CANONICAL_INTEGER - 1 <= value <= MAX_CANONICAL_INTEGER:
193
+ raise CanonicalJSONError(
194
+ "JSON_INTEGER_RANGE",
195
+ path,
196
+ "integer is outside the signed 64-bit range",
197
+ )
198
+ return value
199
+ if type(value) is float:
200
+ if not math.isfinite(value):
201
+ raise CanonicalJSONError(
202
+ "JSON_NONFINITE",
203
+ path,
204
+ "non-finite JSON numbers are unsupported",
205
+ )
206
+ if accept_fractions:
207
+ return value
208
+ raise CanonicalJSONError(
209
+ "JSON_NONINTEGER_NUMBER",
210
+ path,
211
+ "non-integer JSON numbers are unsupported",
212
+ )
213
+ if isinstance(value, str):
214
+ if len(value) > MAX_CANONICAL_STRING_LENGTH:
215
+ raise CanonicalJSONError(
216
+ "JSON_STRING_LENGTH",
217
+ path,
218
+ f"string exceeds {MAX_CANONICAL_STRING_LENGTH} code points",
219
+ )
220
+ _validate_unicode(value, path)
221
+ return value
222
+ if isinstance(value, dict):
223
+ if any(not isinstance(key, str) for key in value):
224
+ raise CanonicalJSONError(
225
+ "JSON_OBJECT_KEY",
226
+ path,
227
+ "object keys must all be strings",
228
+ )
229
+ result: dict[str, Any] = {}
230
+ for key, item in value.items():
231
+ _validate_unicode(key, f"{path}.<key>")
232
+ child = f"{path}.{key}" if key else f"{path}['']"
233
+ result[key] = _normalize_json(
234
+ item,
235
+ child,
236
+ depth=depth + 1,
237
+ members=members,
238
+ accept_fractions=accept_fractions,
239
+ )
240
+ return result
241
+ if isinstance(value, (list, tuple)):
242
+ return [
243
+ _normalize_json(
244
+ item,
245
+ f"{path}[{index}]",
246
+ depth=depth + 1,
247
+ members=members,
248
+ accept_fractions=accept_fractions,
249
+ )
250
+ for index, item in enumerate(value)
251
+ ]
252
+ raise CanonicalJSONError(
253
+ "JSON_VALUE_TYPE",
254
+ path,
255
+ f"unsupported value type {type(value).__name__}",
256
+ )
257
+
258
+
259
+ def _validate_unicode(value: str, path: str) -> None:
260
+ """Refuse a string carrying a lone surrogate, in one step rather than one step per character.
261
+
262
+ UTF-8 can encode every code point except a surrogate, so ``str.encode`` *is* the surrogate
263
+ check: it raises :class:`UnicodeEncodeError` on exactly the code points ``0xD800..0xDFFF`` and
264
+ on nothing else, and ``start`` is the character index the walk used to report. The two forms
265
+ refuse the same strings with the same code, path and character; this one does it in C.
266
+
267
+ The ASCII fast path costs one C-level scan and no allocation. ASCII is a strict subset of the
268
+ non-surrogate code points, so a string that passes it cannot contain one.
269
+ """
270
+
271
+ if value.isascii():
272
+ return
273
+ try:
274
+ value.encode("utf-8", errors="strict")
275
+ except UnicodeEncodeError as error:
276
+ raise CanonicalJSONError(
277
+ "JSON_UNICODE",
278
+ path,
279
+ f"unpaired surrogate at character {error.start}",
280
+ ) from None
281
+
282
+
283
+ def _reject_duplicate_pairs(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
284
+ result: dict[str, Any] = {}
285
+ for key, value in pairs:
286
+ if key in result:
287
+ raise CanonicalJSONError(
288
+ "JSON_DUPLICATE_KEY",
289
+ f"$.{key}",
290
+ f"duplicate object key {key!r}",
291
+ )
292
+ result[key] = value
293
+ return result
294
+
295
+
296
+ def _parse_integer(value: str) -> int:
297
+ parsed = int(value)
298
+ if not -MAX_CANONICAL_INTEGER - 1 <= parsed <= MAX_CANONICAL_INTEGER:
299
+ raise CanonicalJSONError(
300
+ "JSON_INTEGER_RANGE",
301
+ "$",
302
+ "integer is outside the signed 64-bit range",
303
+ )
304
+ return parsed
305
+
306
+
307
+ def _parse_fraction(value: str) -> float:
308
+ """Read a fractional literal from a foreign document, refusing a non-finite one."""
309
+
310
+ parsed = float(value)
311
+ if not math.isfinite(parsed):
312
+ raise CanonicalJSONError(
313
+ "JSON_NONFINITE",
314
+ "$",
315
+ f"non-finite JSON number {value!r} is unsupported",
316
+ )
317
+ return parsed
318
+
319
+
320
+ def _reject_fraction(value: str) -> NoReturn:
321
+ raise CanonicalJSONError(
322
+ "JSON_NONINTEGER_NUMBER",
323
+ "$",
324
+ f"non-integer JSON number {value!r} is unsupported",
325
+ )
326
+
327
+
328
+ def _reject_constant(value: str) -> NoReturn:
329
+ raise CanonicalJSONError(
330
+ "JSON_NONFINITE",
331
+ "$",
332
+ f"non-finite JSON number {value!r} is unsupported",
333
+ )