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,459 @@
1
+ """Fixed identifier partitions and bounded external merge for Data.gov v4 passes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sqlite3
6
+ import tempfile
7
+ from collections.abc import Iterable
8
+ from dataclasses import dataclass
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ from mostlyright.data_harness.canonical import canonical_sha256, sha256_bytes
13
+ from mostlyright.data_harness.sources.catalog.fill_staging import CatalogFillRefused, FillStaging
14
+
15
+ PARTITION_COUNT = 256
16
+ RUN_MAX_ITEMS = 128
17
+ MERGE_FAN_IN = 16
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class PassMapResult:
22
+ root_sha256: str
23
+ manifest_sha256: str
24
+ unique_records: int
25
+ conflicting_identifiers: int
26
+
27
+
28
+ def write_partition_runs(staging: FillStaging, records: tuple[Any, ...]) -> list[dict[str, Any]]:
29
+ """Route exact identifier bytes by SHA-256 prefix into immutable bounded sorted runs."""
30
+
31
+ descriptors: list[dict[str, Any]] = []
32
+ for partition, entries, conflicting_identifiers in plan_partition_runs(records):
33
+ digest = _write_run(
34
+ staging,
35
+ partition=partition,
36
+ entries=entries,
37
+ conflicting_identifiers=conflicting_identifiers,
38
+ )
39
+ descriptors.append({"partition": partition, "run_sha256": digest})
40
+ return descriptors
41
+
42
+
43
+ def plan_partition_runs(
44
+ records: Iterable[Any],
45
+ ) -> Iterable[tuple[str, list[tuple[str, str]], set[str]]]:
46
+ """Yield the exact bounded page runs used by both installation and validation."""
47
+
48
+ groups: dict[str, list[tuple[str, str]]] = {}
49
+ for record in records:
50
+ identifier = getattr(record, "record_id", None)
51
+ semantic_sha256 = getattr(record, "digest", None)
52
+ if not isinstance(identifier, str) or not _is_digest(semantic_sha256):
53
+ raise CatalogFillRefused(
54
+ "DATAGOV_PARTITION", "fill.records", "record identity/digest is invalid"
55
+ )
56
+ partition = sha256_bytes(identifier.encode("utf-8", errors="strict"))[:2]
57
+ groups.setdefault(partition, []).append((identifier, semantic_sha256))
58
+ for partition in sorted(groups):
59
+ entries, conflicting_identifiers = _deduplicate_sorted(groups[partition])
60
+ for offset in range(0, len(entries), RUN_MAX_ITEMS):
61
+ chunk = entries[offset : offset + RUN_MAX_ITEMS]
62
+ yield (
63
+ partition,
64
+ chunk,
65
+ conflicting_identifiers & {identifier for identifier, _semantic in chunk},
66
+ )
67
+
68
+
69
+ def external_merge_pass(
70
+ staging: FillStaging,
71
+ run_descriptors: Iterable[dict[str, Any]],
72
+ ) -> PassMapResult:
73
+ """Merge a descriptor stream through bounded disk-backed fixed-size batches."""
74
+
75
+ try:
76
+ return _external_merge_pass(staging, run_descriptors)
77
+ except CatalogFillRefused:
78
+ raise
79
+ except (OSError, sqlite3.Error) as error:
80
+ raise CatalogFillRefused(
81
+ "DATAGOV_EXTERNAL_MERGE",
82
+ "fill.partition_runs",
83
+ "bounded external-merge scratch storage failed",
84
+ ) from error
85
+
86
+
87
+ def _external_merge_pass(
88
+ staging: FillStaging,
89
+ run_descriptors: Iterable[dict[str, Any]],
90
+ ) -> PassMapResult:
91
+
92
+ with tempfile.TemporaryDirectory(
93
+ prefix="mostlyright-datagov-pass-", dir=staging.root.parent
94
+ ) as directory:
95
+ database_path = Path(directory) / "merge.sqlite3"
96
+ connection = sqlite3.connect(database_path)
97
+ try:
98
+ connection.execute("PRAGMA journal_mode=OFF")
99
+ connection.execute("PRAGMA synchronous=OFF")
100
+ connection.execute("PRAGMA temp_store=FILE")
101
+ connection.execute(
102
+ "CREATE TABLE merged ("
103
+ "partition TEXT NOT NULL, identifier TEXT NOT NULL, semantic TEXT NOT NULL, "
104
+ "conflicting INTEGER NOT NULL, PRIMARY KEY (partition, identifier)"
105
+ ") WITHOUT ROWID"
106
+ )
107
+ descriptor_count = 0
108
+ for descriptor in run_descriptors:
109
+ if (
110
+ not isinstance(descriptor, dict)
111
+ or set(descriptor) != {"partition", "run_sha256"}
112
+ or not _valid_partition(descriptor["partition"])
113
+ or not _is_digest(descriptor["run_sha256"])
114
+ ):
115
+ raise CatalogFillRefused(
116
+ "DATAGOV_PARTITION", "fill.partition_runs", "run descriptor is invalid"
117
+ )
118
+ entries, inherited_conflicts = _validate_run(
119
+ staging.read_shard("partition_run", descriptor["run_sha256"]),
120
+ descriptor["partition"],
121
+ )
122
+ connection.executemany(
123
+ "INSERT INTO merged(partition, identifier, semantic, conflicting) "
124
+ "VALUES (?, ?, ?, ?) ON CONFLICT(partition, identifier) DO UPDATE SET "
125
+ "conflicting = merged.conflicting OR excluded.conflicting "
126
+ "OR merged.semantic <> excluded.semantic, "
127
+ "semantic = min(merged.semantic, excluded.semantic)",
128
+ (
129
+ (
130
+ descriptor["partition"],
131
+ identifier,
132
+ semantic,
133
+ int(identifier in inherited_conflicts),
134
+ )
135
+ for identifier, semantic in entries
136
+ ),
137
+ )
138
+ descriptor_count += 1
139
+ if descriptor_count % MERGE_FAN_IN == 0:
140
+ connection.commit()
141
+ connection.commit()
142
+ partition_manifests, unique_records, conflicts = _write_spooled_partitions(
143
+ staging, connection
144
+ )
145
+ finally:
146
+ connection.close()
147
+ manifest = {
148
+ "partition_count": PARTITION_COUNT,
149
+ "partitions": partition_manifests,
150
+ "unique_records": unique_records,
151
+ "conflicting_identifiers": conflicts,
152
+ }
153
+ root = canonical_sha256(manifest)
154
+ manifest_sha256 = staging.write_shard("pass_map", {**manifest, "root_sha256": root})
155
+ return PassMapResult(
156
+ root_sha256=root,
157
+ manifest_sha256=manifest_sha256,
158
+ unique_records=unique_records,
159
+ conflicting_identifiers=conflicts,
160
+ )
161
+
162
+
163
+ def _write_spooled_partitions(
164
+ staging: FillStaging, connection: sqlite3.Connection
165
+ ) -> tuple[list[dict[str, Any]], int, int]:
166
+ partition_manifests: list[dict[str, Any]] = []
167
+ unique_records = 0
168
+ conflicts = 0
169
+ partitions = connection.execute("SELECT DISTINCT partition FROM merged ORDER BY partition")
170
+ for (partition,) in partitions:
171
+ rows = connection.execute(
172
+ "SELECT identifier, semantic, conflicting FROM merged "
173
+ "WHERE partition = ? ORDER BY identifier",
174
+ (partition,),
175
+ )
176
+ run_sha256s: list[str] = []
177
+ partition_records = 0
178
+ partition_conflicts = 0
179
+ while chunk := rows.fetchmany(RUN_MAX_ITEMS):
180
+ entries = [(identifier, semantic) for identifier, semantic, _conflict in chunk]
181
+ conflicting_identifiers = {
182
+ identifier for identifier, _semantic, conflict in chunk if conflict
183
+ }
184
+ run_sha256s.append(
185
+ _write_run(
186
+ staging,
187
+ partition=partition,
188
+ entries=entries,
189
+ conflicting_identifiers=conflicting_identifiers,
190
+ )
191
+ )
192
+ partition_records += len(entries)
193
+ partition_conflicts += len(conflicting_identifiers)
194
+ unique_records += partition_records
195
+ conflicts += partition_conflicts
196
+ partition_manifests.append(
197
+ {
198
+ "partition": partition,
199
+ "run_sha256s": run_sha256s,
200
+ "record_count": partition_records,
201
+ "conflicting_identifiers": partition_conflicts,
202
+ }
203
+ )
204
+ return partition_manifests, unique_records, conflicts
205
+
206
+
207
+ def dry_run_external_merge_pass(
208
+ staging: FillStaging,
209
+ run_descriptors: Iterable[dict[str, Any]],
210
+ ) -> PassMapResult:
211
+ """Recompute a pass in bounded scratch space without mutating authenticated staging."""
212
+
213
+ with tempfile.TemporaryDirectory(
214
+ prefix="mostlyright-datagov-merge-", dir=staging.root.parent
215
+ ) as directory:
216
+ scratch = FillStaging(Path(directory))
217
+ with scratch.locked():
218
+ adapter = _DryRunStaging(staging, scratch)
219
+ return external_merge_pass(adapter, run_descriptors) # type: ignore[arg-type]
220
+
221
+
222
+ class _DryRunStaging:
223
+ def __init__(self, source: FillStaging, scratch: FillStaging) -> None:
224
+ self._source = source
225
+ self._scratch = scratch
226
+ self._written: set[tuple[str, str]] = set()
227
+
228
+ @property
229
+ def root(self) -> Path:
230
+ return self._scratch.root
231
+
232
+ def write_shard(self, kind: str, payload: dict[str, Any]) -> str:
233
+ digest = self._scratch.write_shard(kind, payload)
234
+ self._written.add((kind, digest))
235
+ return digest
236
+
237
+ def read_shard(self, kind: str, digest: str) -> dict[str, Any]:
238
+ if (kind, digest) in self._written:
239
+ return self._scratch.read_shard(kind, digest)
240
+ return self._source.read_shard(kind, digest)
241
+
242
+
243
+ def validate_partition_run(staging: FillStaging, descriptor: dict[str, Any]) -> tuple[int, int]:
244
+ """Authenticate one indexed run descriptor and its exact immutable shard."""
245
+
246
+ if (
247
+ not isinstance(descriptor, dict)
248
+ or set(descriptor) != {"partition", "run_sha256"}
249
+ or not _valid_partition(descriptor["partition"])
250
+ or not _is_digest(descriptor["run_sha256"])
251
+ ):
252
+ raise CatalogFillRefused(
253
+ "DATAGOV_PARTITION", "fill.partition_runs", "run descriptor is invalid"
254
+ )
255
+ entries, conflicts = _validate_run(
256
+ staging.read_shard("partition_run", descriptor["run_sha256"]),
257
+ descriptor["partition"],
258
+ )
259
+ return len(entries), len(conflicts)
260
+
261
+
262
+ def read_partition_run(
263
+ staging: FillStaging, descriptor: dict[str, Any]
264
+ ) -> tuple[str, list[tuple[str, str]], set[str]]:
265
+ """Authenticate and return one bounded run for exact cross-stream reconciliation."""
266
+
267
+ if (
268
+ not isinstance(descriptor, dict)
269
+ or set(descriptor) != {"partition", "run_sha256"}
270
+ or not _valid_partition(descriptor["partition"])
271
+ or not _is_digest(descriptor["run_sha256"])
272
+ ):
273
+ raise CatalogFillRefused(
274
+ "DATAGOV_PARTITION", "fill.partition_runs", "run descriptor is invalid"
275
+ )
276
+ entries, conflicts = _validate_run(
277
+ staging.read_shard("partition_run", descriptor["run_sha256"]),
278
+ descriptor["partition"],
279
+ )
280
+ return descriptor["partition"], entries, set(conflicts)
281
+
282
+
283
+ def validate_pass_map(
284
+ staging: FillStaging, manifest_sha256: str, expected_root_sha256: str
285
+ ) -> PassMapResult:
286
+ """Stream-authenticate the terminal pass manifest and every referenced final run."""
287
+
288
+ payload = staging.read_shard("pass_map", manifest_sha256)
289
+ if (
290
+ set(payload)
291
+ != {
292
+ "partition_count",
293
+ "partitions",
294
+ "unique_records",
295
+ "conflicting_identifiers",
296
+ "root_sha256",
297
+ }
298
+ or payload["partition_count"] != PARTITION_COUNT
299
+ or not isinstance(payload["partitions"], list)
300
+ or type(payload["unique_records"]) is not int
301
+ or type(payload["conflicting_identifiers"]) is not int
302
+ or payload["root_sha256"] != expected_root_sha256
303
+ ):
304
+ raise CatalogFillRefused("DATAGOV_PASS_MAP", "fill.pass_map", "pass manifest is invalid")
305
+ manifest = {key: value for key, value in payload.items() if key != "root_sha256"}
306
+ if canonical_sha256(manifest) != expected_root_sha256:
307
+ raise CatalogFillRefused("DATAGOV_PASS_MAP", "fill.pass_map", "pass manifest root differs")
308
+ prior_partition: str | None = None
309
+ records = 0
310
+ conflicts = 0
311
+ for partition in payload["partitions"]:
312
+ if (
313
+ not isinstance(partition, dict)
314
+ or set(partition)
315
+ != {
316
+ "partition",
317
+ "run_sha256s",
318
+ "record_count",
319
+ "conflicting_identifiers",
320
+ }
321
+ or not _valid_partition(partition["partition"])
322
+ or (prior_partition is not None and partition["partition"] <= prior_partition)
323
+ or not isinstance(partition["run_sha256s"], list)
324
+ or not partition["run_sha256s"]
325
+ or type(partition["record_count"]) is not int
326
+ or type(partition["conflicting_identifiers"]) is not int
327
+ ):
328
+ raise CatalogFillRefused(
329
+ "DATAGOV_PASS_MAP", "fill.pass_map", "partition manifest is invalid"
330
+ )
331
+ run_records = 0
332
+ run_conflicts = 0
333
+ for digest in partition["run_sha256s"]:
334
+ count, conflict_count = validate_partition_run(
335
+ staging,
336
+ {"partition": partition["partition"], "run_sha256": digest},
337
+ )
338
+ run_records += count
339
+ run_conflicts += conflict_count
340
+ if (
341
+ run_records != partition["record_count"]
342
+ or run_conflicts != partition["conflicting_identifiers"]
343
+ ):
344
+ raise CatalogFillRefused("DATAGOV_PASS_MAP", "fill.pass_map", "partition totals differ")
345
+ records += run_records
346
+ conflicts += run_conflicts
347
+ prior_partition = partition["partition"]
348
+ if records != payload["unique_records"] or conflicts != payload["conflicting_identifiers"]:
349
+ raise CatalogFillRefused("DATAGOV_PASS_MAP", "fill.pass_map", "pass totals differ")
350
+ return PassMapResult(
351
+ root_sha256=expected_root_sha256,
352
+ manifest_sha256=manifest_sha256,
353
+ unique_records=records,
354
+ conflicting_identifiers=conflicts,
355
+ )
356
+
357
+
358
+ def _write_run(
359
+ staging: FillStaging,
360
+ *,
361
+ partition: str,
362
+ entries: list[tuple[str, str]],
363
+ conflicting_identifiers: set[str],
364
+ ) -> str:
365
+ if not _valid_partition(partition) or not 1 <= len(entries) <= RUN_MAX_ITEMS:
366
+ raise CatalogFillRefused(
367
+ "DATAGOV_PARTITION", "fill.partition_run", "bounded run is invalid"
368
+ )
369
+ return staging.write_shard(
370
+ "partition_run",
371
+ {
372
+ "partition": partition,
373
+ "entries": [
374
+ {"identifier": identifier, "semantic_sha256": semantic}
375
+ for identifier, semantic in entries
376
+ ],
377
+ "conflicting_identifiers": sorted(conflicting_identifiers),
378
+ },
379
+ )
380
+
381
+
382
+ def _validate_run(
383
+ payload: dict[str, Any], expected_partition: str
384
+ ) -> tuple[list[tuple[str, str]], set[str]]:
385
+ if (
386
+ set(payload) != {"partition", "entries", "conflicting_identifiers"}
387
+ or payload["partition"] != expected_partition
388
+ or not isinstance(payload["entries"], list)
389
+ or not 1 <= len(payload["entries"]) <= RUN_MAX_ITEMS
390
+ or not isinstance(payload["conflicting_identifiers"], list)
391
+ ):
392
+ raise CatalogFillRefused(
393
+ "DATAGOV_PARTITION", "fill.partition_run", "run payload is invalid"
394
+ )
395
+ result: list[tuple[str, str]] = []
396
+ for item in payload["entries"]:
397
+ if (
398
+ not isinstance(item, dict)
399
+ or set(item) != {"identifier", "semantic_sha256"}
400
+ or not isinstance(item["identifier"], str)
401
+ or not _is_digest(item["semantic_sha256"])
402
+ ):
403
+ raise CatalogFillRefused(
404
+ "DATAGOV_PARTITION", "fill.partition_run", "run entry is invalid"
405
+ )
406
+ if (
407
+ sha256_bytes(item["identifier"].encode("utf-8", errors="strict"))[:2]
408
+ != expected_partition
409
+ ):
410
+ raise CatalogFillRefused(
411
+ "DATAGOV_PARTITION", "fill.partition_run", "entry is in the wrong partition"
412
+ )
413
+ result.append((item["identifier"], item["semantic_sha256"]))
414
+ if result != sorted(result) or len({identifier for identifier, _ in result}) != len(result):
415
+ raise CatalogFillRefused(
416
+ "DATAGOV_PARTITION", "fill.partition_run", "run is not unique and sorted"
417
+ )
418
+ conflicts = payload["conflicting_identifiers"]
419
+ identifiers = {identifier for identifier, _semantic in result}
420
+ if (
421
+ any(not isinstance(identifier, str) for identifier in conflicts)
422
+ or conflicts != sorted(set(conflicts))
423
+ or not set(conflicts) <= identifiers
424
+ ):
425
+ raise CatalogFillRefused(
426
+ "DATAGOV_PARTITION", "fill.partition_run", "conflict membership is invalid"
427
+ )
428
+ return result, set(conflicts)
429
+
430
+
431
+ def _deduplicate_sorted(
432
+ entries: list[tuple[str, str]],
433
+ ) -> tuple[list[tuple[str, str]], set[str]]:
434
+ result: list[tuple[str, str]] = []
435
+ conflicts: set[str] = set()
436
+ for identifier, semantic in sorted(entries):
437
+ if result and result[-1][0] == identifier:
438
+ if result[-1][1] != semantic:
439
+ conflicts.add(identifier)
440
+ result[-1] = (identifier, min(result[-1][1], semantic))
441
+ continue
442
+ result.append((identifier, semantic))
443
+ return result, conflicts
444
+
445
+
446
+ def _valid_partition(value: Any) -> bool:
447
+ return (
448
+ isinstance(value, str)
449
+ and len(value) == 2
450
+ and all(character in "0123456789abcdef" for character in value)
451
+ )
452
+
453
+
454
+ def _is_digest(value: Any) -> bool:
455
+ return (
456
+ isinstance(value, str)
457
+ and len(value) == 64
458
+ and all(character in "0123456789abcdef" for character in value)
459
+ )