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,351 @@
1
+ """Linux image smoke for the real crawler seccomp, normalizer exec boundary, and decoder.
2
+
3
+ Two things are checked here that cannot be checked anywhere else, because both are properties
4
+ of the confined child rather than of the code that runs in it.
5
+
6
+ The first is the crawler's own boundary: normalization happens in a child that can reach no
7
+ network, and a deliberately malicious probe confirms every escape it might try returns EPERM.
8
+
9
+ The second is the weather decoder. The filter installed in that child denies thread
10
+ creation outright -- ``clone``, ``fork``, ``vfork`` and ``clone3`` are all in
11
+ ``_PROCESS_ESCAPE_SYSCALLS_X86_64`` -- so a compiled decoder that spawned a worker pool would not
12
+ run slowly there, it would die. That makes this the one environment where "single-threaded" is
13
+ enforced rather than merely expected, which is why the claim is tested here and not in a unit
14
+ test that would pass on a decoder with a thread pool in it.
15
+
16
+ The thread count is sampled **during** the decode, from the parent, while the child decodes in a
17
+ loop. A library that spawned a pool for the duration of a call and joined it afterwards would
18
+ pass a before-and-after check and die under the filter, so a before-and-after check would be the
19
+ wrong check to write down as green.
20
+
21
+ The decoded levels are compared against digests that were computed on both target platforms and
22
+ found identical, so this doubles as the cross-platform determinism check for the decoder: a wrong
23
+ answer here fails the image, exactly as a wrong warm-up answer halts a refresh.
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import subprocess
29
+ import sys
30
+ import time
31
+ from types import SimpleNamespace
32
+
33
+ from mostlyright.data_harness.canonical import parse_canonical_json, sha256_bytes
34
+ from mostlyright.data_harness.hosted_crawler import encode_normalization_input
35
+ from mostlyright.data_harness.hosted_crawler_job import (
36
+ _disable_process_dumpability,
37
+ _install_networkless_seccomp,
38
+ _run_crawler_child,
39
+ )
40
+ from mostlyright.data_harness.hosted_crawler_protocol import (
41
+ OPENLIGADB_EGRESS_POLICY_ATTESTATION,
42
+ HostedCrawlerRequest,
43
+ parse_crawler_result,
44
+ )
45
+ from mostlyright.data_harness.readers.grib2 import MESSAGES_ROOT
46
+
47
+ _RAW_FIXTURE = b"""[{
48
+ "matchID":1,
49
+ "matchDateTimeUTC":"2025-08-22T18:30:00Z",
50
+ "lastUpdateDateTime":"2025-08-23T01:00:54.147",
51
+ "leagueShortcut":"bl1",
52
+ "leagueSeason":2025,
53
+ "group":{"groupOrderID":1},
54
+ "team1":{"teamId":40,"teamName":"FC Bayern Muenchen"},
55
+ "team2":{"teamId":1635,"teamName":"RB Leipzig"},
56
+ "matchIsFinished":true,
57
+ "matchResults":[{
58
+ "resultTypeID":2,
59
+ "resultOrderID":1,
60
+ "resultID":1,
61
+ "pointsTeam1":6,
62
+ "pointsTeam2":0
63
+ }]
64
+ }]"""
65
+
66
+ _MALICIOUS_PROBE = r"""
67
+ import ctypes
68
+ import errno
69
+ import os
70
+
71
+ links = []
72
+ for name in os.listdir("/proc/self/fd"):
73
+ try:
74
+ links.append(os.readlink("/proc/self/fd/" + name))
75
+ except OSError:
76
+ pass
77
+ if any(link.startswith("socket:[") for link in links):
78
+ raise SystemExit(20)
79
+ status = open("/proc/self/status", encoding="ascii").read()
80
+ if "NoNewPrivs:\t1" not in status or "Seccomp:\t2" not in status:
81
+ raise SystemExit(21)
82
+ try:
83
+ open(f"/proc/{os.getppid()}/environ", "rb").read()
84
+ except PermissionError:
85
+ pass
86
+ else:
87
+ raise SystemExit(22)
88
+ libc = ctypes.CDLL(None, use_errno=True)
89
+ for number, arguments in (
90
+ (41, (2, 1, 0)),
91
+ (42, (-1, 0, 0)),
92
+ (0x40000000 | 41, (2, 1, 0)),
93
+ (0x40000000 | 42, (-1, 0, 0)),
94
+ (56, (0, 0, 0, 0, 0)),
95
+ (101, (0, 0, 0, 0)),
96
+ (310, (os.getppid(), 0, 0, 0, 0, 0)),
97
+ (434, (os.getppid(), 0)),
98
+ ):
99
+ ctypes.set_errno(0)
100
+ outcome = libc.syscall(number, *arguments)
101
+ if outcome != -1 or ctypes.get_errno() != errno.EPERM:
102
+ raise SystemExit(23)
103
+ print("networkless-seccomp-ok")
104
+ """
105
+
106
+
107
+ # Every admitted template pair, with the point count and the digest of the levels the decoder
108
+ # returns for it. The digests were computed on macOS arm64 and on Linux x86_64 and are identical:
109
+ # the levels are exact integers recovered from a deterministic bit unpacking, so they have no
110
+ # licence to differ, and pinning them here is what turns that from an expectation into a check.
111
+ _DECODER_SAMPLES = (
112
+ (
113
+ "gfs_2m_temperature/gfs-2m-temperature.grib2",
114
+ 0,
115
+ 3,
116
+ 65160,
117
+ "e6597dbf68fcf31ad983491122484bf6391d5ace74daf56cb5fa76d1c9b217db",
118
+ ),
119
+ (
120
+ "hrrr_2m_temperature/hrrr-2m-temperature.grib2",
121
+ 30,
122
+ 3,
123
+ 1905141,
124
+ "2e2a2468d687c082c4ec00341e2dd0822d77b1be08246272279ad6ea7e8a7307",
125
+ ),
126
+ (
127
+ "hrrr_categorical_rain/hrrr-categorical-rain.grib2",
128
+ 30,
129
+ 0,
130
+ 1905141,
131
+ "b0379dce013d12446af5c48bf41b14a3f7571c8c5e20cecacd48f2026881d61c",
132
+ ),
133
+ )
134
+
135
+ # Environment variables that a library with a thread pool would expect to be told to stand down
136
+ # with. None of them is set here, and the probe refuses to run if one is. A decoder that is
137
+ # single-threaded only when configured to be is single-threaded nowhere it is not configured, and
138
+ # recording "single-threaded" on the strength of a variable somebody set would be recording the
139
+ # variable rather than the property.
140
+ _FORCING_VARIABLES = (
141
+ "RAYON_NUM_THREADS",
142
+ "OMP_NUM_THREADS",
143
+ "MKL_NUM_THREADS",
144
+ "OPENBLAS_NUM_THREADS",
145
+ "NUMEXPR_NUM_THREADS",
146
+ "TOKIO_WORKER_THREADS",
147
+ "MOSTLYRIGHT_GRIB_THREADS",
148
+ )
149
+
150
+ _DECODER_SECONDS = 1.5
151
+ _DECODER_SAMPLE_INTERVAL = 0.005
152
+ _DECODER_LEAST_SAMPLES = 20
153
+
154
+
155
+ def _decoder_probe() -> str:
156
+ """The child's whole program, with the sample paths resolved by the parent.
157
+
158
+ The paths are resolved here rather than imported there on purpose, and the reason is a
159
+ measurement. Importing the decoder alone leaves the process on one thread. Importing this
160
+ harness package takes it to nine, because the analytics engines it loads start pools of their
161
+ own at import time -- nothing to do with the decoder, and pre-existing, but enough to drown
162
+ the signal this probe exists to read. So the child imports the decoder and the standard
163
+ library, and nothing else, and the thread count it reports is a statement about the decoder.
164
+ """
165
+
166
+ resolved = tuple(
167
+ (str((MESSAGES_ROOT / relative).resolve()), grid, packing, count, digest)
168
+ for relative, grid, packing, count, digest in _DECODER_SAMPLES
169
+ )
170
+ return f"""
171
+ import hashlib
172
+ import os
173
+ import sys
174
+ import time
175
+
176
+ for name in {_FORCING_VARIABLES!r}:
177
+ if name in os.environ:
178
+ raise SystemExit(40)
179
+
180
+ status = open("/proc/self/status", encoding="ascii").read()
181
+ if "NoNewPrivs:\\t1" not in status or "Seccomp:\\t2" not in status:
182
+ raise SystemExit(41)
183
+
184
+
185
+ def threads():
186
+ for line in open("/proc/self/status", encoding="ascii"):
187
+ if line.startswith("Threads:"):
188
+ return int(line.split()[1])
189
+ raise SystemExit(42)
190
+
191
+
192
+ if threads() != 1:
193
+ raise SystemExit(43)
194
+
195
+ import mostlyright_grib
196
+
197
+ if threads() != 1:
198
+ raise SystemExit(44)
199
+
200
+ samples = []
201
+ for path, grid, packing, count, digest in {resolved!r}:
202
+ samples.append((open(path, "rb").read(), grid, packing, count, digest))
203
+
204
+ if threads() != 1:
205
+ raise SystemExit(48)
206
+
207
+ sys.stdout.write("decoder-ready\\n")
208
+ sys.stdout.flush()
209
+
210
+ rounds = 0
211
+ deadline = time.monotonic() + {_DECODER_SECONDS!r}
212
+ while True:
213
+ for content, grid, packing, count, digest in samples:
214
+ unpacked = mostlyright_grib.unpack(
215
+ content, grid_template=grid, packing_template=packing
216
+ )
217
+ if unpacked.point_count != count:
218
+ raise SystemExit(45)
219
+ if hashlib.sha256(memoryview(unpacked.levels).cast("B")).hexdigest() != digest:
220
+ raise SystemExit(46)
221
+ rounds += 1
222
+ if time.monotonic() >= deadline:
223
+ break
224
+
225
+ if threads() != 1:
226
+ raise SystemExit(47)
227
+
228
+ sys.stdout.write("decoder-ok %d %s\\n" % (rounds, mostlyright_grib.decoder_version()))
229
+ """
230
+
231
+
232
+ def _thread_count(pid: int) -> int | None:
233
+ """How many threads the child has right now, read from outside it.
234
+
235
+ From outside, because from inside there is no way to look: the decode is one blocking call
236
+ into compiled code, a Python signal handler would not run until it returned, and a watcher
237
+ thread is exactly the thing the filter denies. The parent is not confined, so it can watch."""
238
+
239
+ try:
240
+ with open(f"/proc/{pid}/status", encoding="ascii") as handle:
241
+ for line in handle:
242
+ if line.startswith("Threads:"):
243
+ return int(line.split()[1])
244
+ except (FileNotFoundError, ProcessLookupError):
245
+ return None
246
+ return None
247
+
248
+
249
+ def _exercise_decoder_under_the_filter() -> None:
250
+ """Import the decoder and decode every admitted sample inside the confined child.
251
+
252
+ Succeeding at all is the strongest half of the claim, because the filter denies thread
253
+ creation: a decoder with a worker pool does not reach the second line. The sampling below is
254
+ the explicit half, so the count is asserted rather than inferred from a survival."""
255
+
256
+ probe = subprocess.Popen(
257
+ [sys.executable, "-c", _decoder_probe()],
258
+ stdin=subprocess.DEVNULL,
259
+ stdout=subprocess.PIPE,
260
+ stderr=subprocess.PIPE,
261
+ env={},
262
+ close_fds=True,
263
+ preexec_fn=_install_networkless_seccomp,
264
+ )
265
+ assert probe.stdout is not None
266
+ if probe.stdout.readline() != b"decoder-ready\n":
267
+ probe.kill()
268
+ raise RuntimeError("weather decoder did not start under the crawler seccomp filter")
269
+
270
+ counts: list[int] = []
271
+ while probe.poll() is None:
272
+ observed = _thread_count(probe.pid)
273
+ if observed is None:
274
+ break
275
+ counts.append(observed)
276
+ time.sleep(_DECODER_SAMPLE_INTERVAL)
277
+
278
+ stdout, stderr = probe.communicate(timeout=30.0)
279
+ if probe.returncode != 0 or stderr:
280
+ raise RuntimeError(
281
+ f"weather decoder failed under the crawler seccomp filter with status "
282
+ f"{probe.returncode}"
283
+ )
284
+ if not stdout.startswith(b"decoder-ok "):
285
+ raise RuntimeError("weather decoder did not complete every admitted sample")
286
+ if len(counts) < _DECODER_LEAST_SAMPLES:
287
+ raise RuntimeError(
288
+ f"only {len(counts)} thread counts were taken while the decoder ran, which is too "
289
+ f"few to say anything about what it did during a decode"
290
+ )
291
+ if set(counts) != {1}:
292
+ raise RuntimeError(
293
+ f"the weather decoder ran on more than one thread during a decode: observed "
294
+ f"{sorted(set(counts))}"
295
+ )
296
+ print(stdout.decode("ascii").strip())
297
+
298
+
299
+ def main() -> int:
300
+ """Exercise normalization plus a malicious network probe under the production launcher."""
301
+
302
+ _disable_process_dumpability()
303
+ request = HostedCrawlerRequest(
304
+ request_id="12121212-1212-4212-8212-121212121212",
305
+ source_id="openligadb",
306
+ adapter_id="external.openligadb",
307
+ adapter_version="1.0.0",
308
+ requested_at="2026-07-20T12:00:00Z",
309
+ query={"league_shortcut": "bl1", "league_season": 2025},
310
+ egress_policy_attestation=OPENLIGADB_EGRESS_POLICY_ATTESTATION,
311
+ )
312
+ source_url = "https://api.openligadb.de/getmatchdata/bl1/2025"
313
+ normalization_input = encode_normalization_input(
314
+ request,
315
+ SimpleNamespace(
316
+ source_url=source_url,
317
+ final_url=source_url,
318
+ media_type="application/json",
319
+ content=_RAW_FIXTURE,
320
+ content_sha256=sha256_bytes(_RAW_FIXTURE),
321
+ transport_evidence_digest="f" * 64,
322
+ hops=(SimpleNamespace(status=200, etag=None, last_modified=None),),
323
+ ),
324
+ )
325
+ result = parse_crawler_result(
326
+ parse_canonical_json(_run_crawler_child(request, normalization_input))
327
+ )
328
+ if result.request_digest != request.digest or b"FC Bayern Muenchen" not in (
329
+ result.normalized_content
330
+ ):
331
+ raise RuntimeError("sandboxed normalizer result binding failed")
332
+
333
+ probe = subprocess.run(
334
+ [sys.executable, "-c", _MALICIOUS_PROBE],
335
+ input=b"",
336
+ capture_output=True,
337
+ env={},
338
+ close_fds=True,
339
+ preexec_fn=_install_networkless_seccomp,
340
+ check=False,
341
+ timeout=10.0,
342
+ )
343
+ if probe.returncode != 0 or probe.stdout != b"networkless-seccomp-ok\n" or probe.stderr:
344
+ raise RuntimeError("crawler seccomp malicious-network probe failed")
345
+ print("networkless-seccomp-ok")
346
+ _exercise_decoder_under_the_filter()
347
+ return 0
348
+
349
+
350
+ if __name__ == "__main__":
351
+ raise SystemExit(main())