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,474 @@
1
+ """Descriptor-rooted, bounded capture of one private Build-index snapshot."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import stat
7
+ from collections.abc import Mapping
8
+ from dataclasses import dataclass
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ from mostlyright.data_harness.canonical import (
13
+ CanonicalJSONError,
14
+ parse_canonical_json,
15
+ sha256_bytes,
16
+ )
17
+ from mostlyright.data_harness.local_search.contracts import (
18
+ MAX_ENTRIES,
19
+ BuildDiagnosticDocument,
20
+ BuildIndexEntry,
21
+ BuildIndexManifest,
22
+ BuildLocatorDocument,
23
+ LocalSearchContractError,
24
+ SnapshotMember,
25
+ build_diagnostic_document_from_dict,
26
+ build_index_entry_from_dict,
27
+ build_index_manifest_from_dict,
28
+ build_locator_document_from_dict,
29
+ )
30
+
31
+ _OPEN_DIRECTORY = os.O_RDONLY | os.O_DIRECTORY | getattr(os, "O_CLOEXEC", 0)
32
+ _OPEN_MEMBER = (
33
+ os.O_RDONLY
34
+ | getattr(os, "O_CLOEXEC", 0)
35
+ | getattr(os, "O_NOFOLLOW", 0)
36
+ | getattr(os, "O_NONBLOCK", 0)
37
+ )
38
+ _NOFOLLOW = getattr(os, "O_NOFOLLOW", 0)
39
+ _READ_CHUNK = 1024 * 1024
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class SnapshotLimits:
44
+ """Closed capture budgets applied before unbounded allocation."""
45
+
46
+ max_member_bytes: int = 16 * 1024 * 1024
47
+ max_total_bytes: int = 256 * 1024 * 1024
48
+ max_members: int = MAX_ENTRIES + 2
49
+
50
+ def __post_init__(self) -> None:
51
+ for name, value, maximum in (
52
+ ("max_member_bytes", self.max_member_bytes, 256 * 1024 * 1024),
53
+ ("max_total_bytes", self.max_total_bytes, 1024 * 1024 * 1024),
54
+ ("max_members", self.max_members, MAX_ENTRIES + 2),
55
+ ):
56
+ if type(value) is not int or not 1 <= value <= maximum:
57
+ raise LocalSearchContractError(
58
+ "LOCAL_SEARCH_SNAPSHOT_LIMIT",
59
+ f"limits.{name}",
60
+ f"must be an integer in [1, {maximum}]",
61
+ )
62
+
63
+
64
+ @dataclass(frozen=True)
65
+ class CapturedBuildIndexSnapshot:
66
+ """A fully verified private index plus the exact bytes that produced it."""
67
+
68
+ manifest: BuildIndexManifest
69
+ manifest_sha256: str
70
+ entries: tuple[BuildIndexEntry, ...]
71
+ locators: BuildLocatorDocument
72
+ diagnostics: BuildDiagnosticDocument
73
+ manifest_bytes: bytes
74
+ member_bytes: tuple[tuple[str, bytes], ...]
75
+
76
+
77
+ def capture_build_index_snapshot(
78
+ directory: Path,
79
+ *,
80
+ expected_manifest_sha256: str,
81
+ limits: SnapshotLimits | None = None,
82
+ ) -> CapturedBuildIndexSnapshot:
83
+ """Capture and verify a private index from one retained root identity.
84
+
85
+ ``expected_manifest_sha256`` is deliberately mandatory: the live directory cannot nominate
86
+ its own authority coordinate merely by replacing all of its files consistently.
87
+ """
88
+
89
+ _expected_digest(expected_manifest_sha256)
90
+ if not isinstance(directory, Path):
91
+ raise LocalSearchContractError("LOCAL_SEARCH_TYPE", "directory", "must be a pathlib.Path")
92
+ selected_limits = limits or SnapshotLimits()
93
+ if not isinstance(selected_limits, SnapshotLimits):
94
+ raise LocalSearchContractError("LOCAL_SEARCH_TYPE", "limits", "must be SnapshotLimits")
95
+
96
+ root_fd = _open_root(directory)
97
+ try:
98
+ root_identity = _directory_identity(root_fd, "root")
99
+ root_names_before = _directory_names(root_fd, "root", maximum=5)
100
+ expected_root_names = {"manifest.json", "entries", "locators.json", "diagnostics.json"}
101
+ _exact_membership(root_names_before, expected_root_names, "root")
102
+
103
+ manifest_bytes = _read_plain_file(
104
+ root_fd,
105
+ "manifest.json",
106
+ maximum=min(selected_limits.max_member_bytes, selected_limits.max_total_bytes),
107
+ )
108
+ manifest_sha256 = sha256_bytes(manifest_bytes)
109
+ if manifest_sha256 != expected_manifest_sha256:
110
+ raise LocalSearchContractError(
111
+ "LOCAL_SEARCH_MANIFEST_MISMATCH",
112
+ "manifest.json",
113
+ "does not match the caller's expected manifest digest",
114
+ )
115
+ manifest = build_index_manifest_from_dict(_canonical_mapping(manifest_bytes, "manifest"))
116
+ if len(manifest.members) > selected_limits.max_members:
117
+ raise LocalSearchContractError(
118
+ "LOCAL_SEARCH_SNAPSHOT_LIMIT",
119
+ "manifest.entries",
120
+ "snapshot exceeds the member-count limit",
121
+ )
122
+ if manifest.total_bytes + len(manifest_bytes) > selected_limits.max_total_bytes:
123
+ raise LocalSearchContractError(
124
+ "LOCAL_SEARCH_SNAPSHOT_LIMIT",
125
+ "manifest.total_bytes",
126
+ "snapshot exceeds the aggregate byte limit",
127
+ )
128
+ for member in manifest.members:
129
+ if member.bytes > selected_limits.max_member_bytes:
130
+ raise LocalSearchContractError(
131
+ "LOCAL_SEARCH_SNAPSHOT_LIMIT",
132
+ member.path,
133
+ "member exceeds the byte limit",
134
+ )
135
+
136
+ entries_fd = _open_child_directory(root_fd, "entries")
137
+ try:
138
+ entries_identity = _directory_identity(entries_fd, "entries")
139
+ expected_entry_names = {
140
+ member.path.removeprefix("entries/") for member in manifest.entries
141
+ }
142
+ _exact_membership(
143
+ _directory_names(
144
+ entries_fd,
145
+ "entries",
146
+ maximum=selected_limits.max_members + 1,
147
+ ),
148
+ expected_entry_names,
149
+ "entries",
150
+ )
151
+
152
+ captured_members: list[tuple[str, bytes]] = []
153
+ parsed_entries: list[BuildIndexEntry] = []
154
+ for member in manifest.entries:
155
+ name = member.path.removeprefix("entries/")
156
+ raw = _capture_bound_member(entries_fd, name, member, selected_limits)
157
+ entry = build_index_entry_from_dict(_canonical_mapping(raw, member.path))
158
+ if entry.facts_sha256 != member.sha256 or name != f"{entry.facts_sha256}.json":
159
+ raise LocalSearchContractError(
160
+ "LOCAL_SEARCH_ENTRY_BINDING",
161
+ member.path,
162
+ "filename and member digest must equal the entry facts digest",
163
+ )
164
+ parsed_entries.append(entry)
165
+ captured_members.append((member.path, raw))
166
+
167
+ _validate_directory(entries_fd, entries_identity, "entries")
168
+ _validate_named_directory(root_fd, "entries", entries_identity)
169
+ _exact_membership(
170
+ _directory_names(
171
+ entries_fd,
172
+ "entries",
173
+ maximum=selected_limits.max_members + 1,
174
+ ),
175
+ expected_entry_names,
176
+ "entries",
177
+ )
178
+ finally:
179
+ os.close(entries_fd)
180
+
181
+ locator_bytes = _capture_bound_member(
182
+ root_fd, "locators.json", manifest.locators, selected_limits
183
+ )
184
+ diagnostic_bytes = _capture_bound_member(
185
+ root_fd, "diagnostics.json", manifest.diagnostics, selected_limits
186
+ )
187
+ locators = build_locator_document_from_dict(
188
+ _canonical_mapping(locator_bytes, "locators.json")
189
+ )
190
+ diagnostics = build_diagnostic_document_from_dict(
191
+ _canonical_mapping(diagnostic_bytes, "diagnostics.json")
192
+ )
193
+ if len(locators.locators) != manifest.locator_count:
194
+ raise LocalSearchContractError(
195
+ "LOCAL_SEARCH_MANIFEST",
196
+ "manifest.locator_count",
197
+ "does not match locators.json",
198
+ )
199
+ if len(diagnostics.diagnostics) != manifest.diagnostic_count:
200
+ raise LocalSearchContractError(
201
+ "LOCAL_SEARCH_MANIFEST",
202
+ "manifest.diagnostic_count",
203
+ "does not match diagnostics.json",
204
+ )
205
+ entry_digests = {entry.facts_sha256 for entry in parsed_entries}
206
+ dangling = sorted(
207
+ locator.facts_sha256
208
+ for locator in locators.locators
209
+ if locator.facts_sha256 not in entry_digests
210
+ )
211
+ if dangling:
212
+ raise LocalSearchContractError(
213
+ "LOCAL_SEARCH_LOCATOR_BINDING",
214
+ "locators.json",
215
+ "a locator names facts absent from this snapshot",
216
+ )
217
+
218
+ captured_members.extend(
219
+ (("locators.json", locator_bytes), ("diagnostics.json", diagnostic_bytes))
220
+ )
221
+ _validate_directory(root_fd, root_identity, "root")
222
+ _exact_membership(_directory_names(root_fd, "root", maximum=5), expected_root_names, "root")
223
+ return CapturedBuildIndexSnapshot(
224
+ manifest=manifest,
225
+ manifest_sha256=manifest_sha256,
226
+ entries=tuple(parsed_entries),
227
+ locators=locators,
228
+ diagnostics=diagnostics,
229
+ manifest_bytes=manifest_bytes,
230
+ member_bytes=tuple(captured_members),
231
+ )
232
+ finally:
233
+ os.close(root_fd)
234
+
235
+
236
+ def _open_root(directory: Path) -> int:
237
+ try:
238
+ descriptor = os.open(directory, _OPEN_DIRECTORY | _NOFOLLOW)
239
+ except OSError as error:
240
+ raise LocalSearchContractError(
241
+ "LOCAL_SEARCH_SNAPSHOT_ROOT",
242
+ "root",
243
+ "cannot open a retained no-follow directory",
244
+ ) from error
245
+ try:
246
+ _directory_identity(descriptor, "root")
247
+ except BaseException:
248
+ os.close(descriptor)
249
+ raise
250
+ return descriptor
251
+
252
+
253
+ def _open_child_directory(parent_fd: int, name: str) -> int:
254
+ try:
255
+ named = os.stat(name, dir_fd=parent_fd, follow_symlinks=False)
256
+ descriptor = os.open(name, _OPEN_DIRECTORY | _NOFOLLOW, dir_fd=parent_fd)
257
+ except OSError as error:
258
+ raise LocalSearchContractError(
259
+ "LOCAL_SEARCH_SNAPSHOT_MEMBER",
260
+ name,
261
+ "cannot open a retained no-follow directory",
262
+ ) from error
263
+ try:
264
+ opened = os.fstat(descriptor)
265
+ if not stat.S_ISDIR(named.st_mode) or not stat.S_ISDIR(opened.st_mode):
266
+ raise LocalSearchContractError(
267
+ "LOCAL_SEARCH_SNAPSHOT_MEMBER", name, "must be a directory"
268
+ )
269
+ if _identity(named) != _identity(opened):
270
+ raise LocalSearchContractError(
271
+ "LOCAL_SEARCH_SNAPSHOT_RACE", name, "named and opened identities differ"
272
+ )
273
+ except BaseException:
274
+ os.close(descriptor)
275
+ raise
276
+ return descriptor
277
+
278
+
279
+ def _capture_bound_member(
280
+ parent_fd: int,
281
+ name: str,
282
+ expected: SnapshotMember,
283
+ limits: SnapshotLimits,
284
+ ) -> bytes:
285
+ raw = _read_plain_file(parent_fd, name, maximum=limits.max_member_bytes)
286
+ if len(raw) != expected.bytes or sha256_bytes(raw) != expected.sha256:
287
+ raise LocalSearchContractError(
288
+ "LOCAL_SEARCH_SNAPSHOT_MEMBER",
289
+ expected.path,
290
+ "size or digest differs from the manifest",
291
+ )
292
+ return raw
293
+
294
+
295
+ def _read_plain_file(parent_fd: int, name: str, *, maximum: int) -> bytes:
296
+ """Read one named file once, checking opened and named identity on both sides."""
297
+
298
+ descriptor = -1
299
+ try:
300
+ named_before = os.stat(name, dir_fd=parent_fd, follow_symlinks=False)
301
+ descriptor = os.open(name, _OPEN_MEMBER, dir_fd=parent_fd)
302
+ opened_before = os.fstat(descriptor)
303
+ except OSError as error:
304
+ if descriptor >= 0:
305
+ os.close(descriptor)
306
+ raise LocalSearchContractError(
307
+ "LOCAL_SEARCH_SNAPSHOT_MEMBER", name, "cannot open a plain no-follow member"
308
+ ) from error
309
+ try:
310
+ if (
311
+ not stat.S_ISREG(named_before.st_mode)
312
+ or not stat.S_ISREG(opened_before.st_mode)
313
+ or named_before.st_nlink != 1
314
+ or opened_before.st_nlink != 1
315
+ ):
316
+ raise LocalSearchContractError(
317
+ "LOCAL_SEARCH_SNAPSHOT_MEMBER",
318
+ name,
319
+ "must be a single-link regular file",
320
+ )
321
+ if _identity(named_before) != _identity(opened_before):
322
+ raise LocalSearchContractError(
323
+ "LOCAL_SEARCH_SNAPSHOT_RACE", name, "named and opened identities differ"
324
+ )
325
+ if opened_before.st_size > maximum:
326
+ raise LocalSearchContractError(
327
+ "LOCAL_SEARCH_SNAPSHOT_LIMIT", name, "member exceeds the byte limit"
328
+ )
329
+ chunks: list[bytes] = []
330
+ observed = 0
331
+ while True:
332
+ chunk = os.read(descriptor, min(_READ_CHUNK, maximum - observed + 1))
333
+ if not chunk:
334
+ break
335
+ observed += len(chunk)
336
+ if observed > maximum:
337
+ raise LocalSearchContractError(
338
+ "LOCAL_SEARCH_SNAPSHOT_LIMIT", name, "member exceeds the byte limit"
339
+ )
340
+ chunks.append(chunk)
341
+ opened_after = os.fstat(descriptor)
342
+ try:
343
+ named_after = os.stat(name, dir_fd=parent_fd, follow_symlinks=False)
344
+ except OSError as error:
345
+ raise LocalSearchContractError(
346
+ "LOCAL_SEARCH_SNAPSHOT_RACE", name, "named member disappeared during capture"
347
+ ) from error
348
+ raw = b"".join(chunks)
349
+ if (
350
+ _identity(opened_before) != _identity(opened_after)
351
+ or _identity(opened_after) != _identity(named_after)
352
+ or opened_after.st_nlink != 1
353
+ or len(raw) != opened_after.st_size
354
+ ):
355
+ raise LocalSearchContractError(
356
+ "LOCAL_SEARCH_SNAPSHOT_RACE", name, "member changed during capture"
357
+ )
358
+ return raw
359
+ finally:
360
+ os.close(descriptor)
361
+
362
+
363
+ def _canonical_mapping(raw: bytes, path: str) -> Mapping[str, Any]:
364
+ try:
365
+ value = parse_canonical_json(raw)
366
+ except CanonicalJSONError as error:
367
+ raise LocalSearchContractError(
368
+ "LOCAL_SEARCH_CANONICAL", path, "member is not exact canonical JSON"
369
+ ) from error
370
+ if not isinstance(value, Mapping):
371
+ raise LocalSearchContractError("LOCAL_SEARCH_TYPE", path, "must contain an object")
372
+ return value
373
+
374
+
375
+ def _expected_digest(value: Any) -> str:
376
+ if (
377
+ not isinstance(value, str)
378
+ or len(value) != 64
379
+ or any(char not in "0123456789abcdef" for char in value)
380
+ ):
381
+ raise LocalSearchContractError(
382
+ "LOCAL_SEARCH_SHA256",
383
+ "expected_manifest_sha256",
384
+ "must be a lowercase 64-character SHA-256",
385
+ )
386
+ return value
387
+
388
+
389
+ def _directory_names(descriptor: int, path: str, *, maximum: int) -> set[str]:
390
+ """Read at most ``maximum`` names, refusing before an attacker-sized allocation."""
391
+
392
+ names: set[str] = set()
393
+ try:
394
+ with os.scandir(descriptor) as entries:
395
+ for entry in entries:
396
+ if len(names) >= maximum:
397
+ raise LocalSearchContractError(
398
+ "LOCAL_SEARCH_SNAPSHOT_MEMBERSHIP",
399
+ path,
400
+ "directory contains more names than the bounded snapshot permits",
401
+ )
402
+ if entry.name in names:
403
+ raise LocalSearchContractError(
404
+ "LOCAL_SEARCH_SNAPSHOT_MEMBERSHIP",
405
+ path,
406
+ "directory contains a duplicate name",
407
+ )
408
+ names.add(entry.name)
409
+ except LocalSearchContractError:
410
+ raise
411
+ except OSError as error:
412
+ raise LocalSearchContractError(
413
+ "LOCAL_SEARCH_SNAPSHOT_RACE", path, "directory cannot be listed"
414
+ ) from error
415
+ return names
416
+
417
+
418
+ def _exact_membership(actual: set[str], expected: set[str], path: str) -> None:
419
+ if actual != expected:
420
+ raise LocalSearchContractError(
421
+ "LOCAL_SEARCH_SNAPSHOT_MEMBERSHIP",
422
+ path,
423
+ "membership is not exact; "
424
+ f"missing_count={len(expected - actual)}, extra_count={len(actual - expected)}",
425
+ )
426
+
427
+
428
+ def _directory_identity(descriptor: int, path: str) -> tuple[int, int, int, int, int, int]:
429
+ try:
430
+ observed = os.fstat(descriptor)
431
+ except OSError as error:
432
+ raise LocalSearchContractError(
433
+ "LOCAL_SEARCH_SNAPSHOT_RACE", path, "directory identity cannot be read"
434
+ ) from error
435
+ if not stat.S_ISDIR(observed.st_mode):
436
+ raise LocalSearchContractError("LOCAL_SEARCH_SNAPSHOT_MEMBER", path, "must be a directory")
437
+ return _identity(observed)
438
+
439
+
440
+ def _validate_directory(
441
+ descriptor: int, expected: tuple[int, int, int, int, int, int], path: str
442
+ ) -> None:
443
+ if _directory_identity(descriptor, path) != expected:
444
+ raise LocalSearchContractError(
445
+ "LOCAL_SEARCH_SNAPSHOT_RACE", path, "directory changed during capture"
446
+ )
447
+
448
+
449
+ def _validate_named_directory(
450
+ parent_fd: int,
451
+ name: str,
452
+ expected: tuple[int, int, int, int, int, int],
453
+ ) -> None:
454
+ try:
455
+ named = os.stat(name, dir_fd=parent_fd, follow_symlinks=False)
456
+ except OSError as error:
457
+ raise LocalSearchContractError(
458
+ "LOCAL_SEARCH_SNAPSHOT_RACE", name, "directory disappeared during capture"
459
+ ) from error
460
+ if not stat.S_ISDIR(named.st_mode) or _identity(named) != expected:
461
+ raise LocalSearchContractError(
462
+ "LOCAL_SEARCH_SNAPSHOT_RACE", name, "named directory changed during capture"
463
+ )
464
+
465
+
466
+ def _identity(value: os.stat_result) -> tuple[int, int, int, int, int, int]:
467
+ return (
468
+ value.st_dev,
469
+ value.st_ino,
470
+ value.st_mode,
471
+ value.st_size,
472
+ value.st_mtime_ns,
473
+ value.st_ctime_ns,
474
+ )