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,501 @@
1
+ """Trigonometry that answers the same on every machine, computed here rather than asked of C.
2
+
3
+ Why this module exists
4
+ ----------------------
5
+ IEEE-754 requires addition, subtraction, multiplication, division and square root to be
6
+ correctly rounded: two conforming machines given the same doubles hand back the same double,
7
+ bit for bit. It requires nothing of the kind for sine, cosine, tangent, logarithm, arc tangent
8
+ or raising to a fractional power. Every C library approximates those its own way, and two of
9
+ them may differ in the last place with neither of them wrong.
10
+
11
+ That is not a theoretical difference here. The flagship weather model declares both of its
12
+ standard parallels at 38.5 degrees, so the whole cone constant of its projection is one sine::
13
+
14
+ math.sin(math.radians(38.5))
15
+
16
+ glibc on x86_64 0x1.3eba3d0b47a0ep-1
17
+ macOS on arm64 0x1.3eba3d0b47a0fp-1
18
+
19
+ One unit in the last place. Carried through the projection it moved the reported latitude of a
20
+ grid cell over Chicago from ``41.87656533464987`` to ``41.87656533464984``, which moved the
21
+ digest of a known-good sample, which failed the sample gate on one architecture and passed it on
22
+ the other. A dataset this harness builds is meant to be reproducible from its recipe and its
23
+ bytes; a coordinate that also depends on which C library the machine was built against is not
24
+ that, and it fails in the quiet way: the number verifies, it plots, and it disagrees with the
25
+ same number computed next door.
26
+
27
+ What this module guarantees, and what it does not
28
+ -------------------------------------------------
29
+ **Guaranteed: one float64 argument produces one float64 answer, the same on every machine.**
30
+ Every step between them is a decimal operation the decimal arithmetic specification requires to
31
+ be correctly rounded -- add, subtract, multiply, divide, square root -- carried at a fixed
32
+ precision, in a fixed order, from constants this module computes for itself. Nothing on that
33
+ path is left to the machine.
34
+
35
+ **Not claimed: correct rounding.** Each series below stops when a further term no longer moves
36
+ the running total at fifty significant digits, and the result is rounded to float64 once, at the
37
+ end. Claiming correct rounding would need a proof that no true value here lands within
38
+ 10^-33 of the midpoint between two doubles, and no such proof is offered. What is offered is
39
+ that whatever answer comes out, the same answer comes out everywhere, which is the property a
40
+ reproducible receipt actually needs.
41
+
42
+ **Accuracy is graded rather than asserted.** The tests compare every function here against the
43
+ platform's own C library across the range this Reader uses, and against the identities that
44
+ relate them. Beyond that, the projection built on this module is compared against the
45
+ publisher's own declared grid corner on every message read -- so an error here does not wait for
46
+ a test to be noticed, it stops a decode.
47
+
48
+ What is not computed in decimal, and why
49
+ ----------------------------------------
50
+ ``PI``, ``radians`` and ``degrees`` are exact float64 work: a constant, and one multiplication
51
+ by a factor derived with one correctly rounded division. They live here so the projection has a
52
+ single place to reach for its arithmetic, not because they were ever in doubt. A correctly
53
+ rounded multiplication is a correctly rounded multiplication everywhere, and running them
54
+ through decimal would cost time to buy nothing.
55
+
56
+ The method, in the order the code runs it
57
+ -----------------------------------------
58
+ 1. The float64 argument converts to ``Decimal`` exactly. A binary64 value is a terminating
59
+ decimal, so this step loses nothing and invents nothing.
60
+ 2. The argument is reduced to the interval where its series converges quickly: sine and cosine
61
+ modulo a quarter turn, the logarithm to a mantissa near one, the exponential modulo the
62
+ logarithm of ten, the arc tangent by repeated halving. Reduction uses constants carried to
63
+ seventy-five digits so the subtraction that does it cannot lose the digits that matter.
64
+ 3. The reduced argument goes into a plain series, summed until a term no longer changes the
65
+ total at the working precision.
66
+ 4. The result is rounded to float64 exactly once, on the way out.
67
+
68
+ The constants
69
+ -------------
70
+ ``pi`` comes from Machin's formula, ``pi = 16 atan(1/5) - 4 atan(1/239)``; the natural
71
+ logarithms of two and ten come from the ``atanh`` series. They are computed at import from
72
+ those formulae rather than pasted in as digits, so there is no transcription to get wrong. The
73
+ tests compare their leading digits against published values, which is the check a pasted
74
+ constant would have needed anyway.
75
+
76
+ Import discipline: ``decimal`` and nothing else, ever. A module whose entire purpose is to
77
+ stop depending on the machine's arithmetic has no business importing anything that might.
78
+ """
79
+
80
+ from __future__ import annotations
81
+
82
+ import decimal
83
+ from decimal import Decimal
84
+
85
+ __all__ = [
86
+ "CONSTANT_DIGITS",
87
+ "PI",
88
+ "WORKING_DIGITS",
89
+ "atan",
90
+ "atan2",
91
+ "cos",
92
+ "degrees",
93
+ "hypot",
94
+ "log",
95
+ "power",
96
+ "radians",
97
+ "sin",
98
+ "tan",
99
+ ]
100
+
101
+ # Fifty digits to work in, seventy-five to reduce with. A float64 needs seventeen to be written
102
+ # down exactly, so the working precision carries thirty-three digits of headroom over what the
103
+ # answer can express, and the reduction carries twenty-five more than that so the cancelling
104
+ # subtraction inside it cannot eat the digits the series then works from.
105
+ WORKING_DIGITS = 50
106
+ CONSTANT_DIGITS = 75
107
+
108
+ # The reduction subtracts a multiple of a constant known to CONSTANT_DIGITS digits, so a very
109
+ # large argument would spend that headroom on its integer part. Nothing on this Reader's path
110
+ # reaches an angle of a thousand million radians -- every one of them is bounded by half a turn
111
+ # or by a latitude -- so the bound is stated and enforced rather than assumed.
112
+ _LARGEST_REDUCIBLE_ANGLE = Decimal(10) ** 10
113
+
114
+
115
+ def _context(digits: int) -> decimal.Context:
116
+ """A context that decides everything: precision, rounding, range, and what is fatal.
117
+
118
+ Constructed rather than inherited. A caller who has set a precision of five digits and
119
+ rounding towards zero must not be able to change this module's answer, and a caller's flags
120
+ must not be disturbed by having called it, so every function here runs inside one of these
121
+ and never inside whatever context it was called from.
122
+ """
123
+
124
+ return decimal.Context(
125
+ prec=digits,
126
+ rounding=decimal.ROUND_HALF_EVEN,
127
+ Emin=-999999999,
128
+ Emax=999999999,
129
+ capitals=1,
130
+ clamp=0,
131
+ flags=[],
132
+ # An operation with no answer, and a division by zero, are faults to raise rather than
133
+ # quiet NaNs to propagate. Overflow is deliberately not fatal: it becomes an infinity,
134
+ # which is what float64 arithmetic would have produced at the same point.
135
+ traps=[decimal.InvalidOperation, decimal.DivisionByZero],
136
+ )
137
+
138
+
139
+ _WORKING = _context(WORKING_DIGITS)
140
+ _REDUCING = _context(CONSTANT_DIGITS)
141
+
142
+
143
+ # --- The constants, computed from their own formulae ----------------------------------------
144
+
145
+
146
+ def _arc_tangent_of_reciprocal(whole: int) -> Decimal:
147
+ """``atan(1/whole)`` by its alternating series, for whole numbers of two and up.
148
+
149
+ Used only to build pi, where the two reciprocals are a fifth and a two hundred and
150
+ thirty-ninth and the series converges in a few dozen terms.
151
+ """
152
+
153
+ x = Decimal(1) / Decimal(whole)
154
+ square = x * x
155
+ signed = x
156
+ total = x
157
+ term = 1
158
+ while True:
159
+ signed = -signed * square
160
+ addend = signed / (2 * term + 1)
161
+ if total + addend == total:
162
+ return total
163
+ total += addend
164
+ term += 1
165
+
166
+
167
+ def _logarithm_series(mantissa: Decimal) -> Decimal:
168
+ """``log(mantissa)`` by the ``atanh`` series, for a mantissa the caller has already reduced.
169
+
170
+ ``log(m) = 2 (z + z^3/3 + z^5/5 + ...)`` with ``z = (m - 1) / (m + 1)``. Every caller here
171
+ hands over a mantissa between the square roots of a half and of two, where ``|z|`` is under
172
+ 0.18 and the series settles in about forty terms.
173
+ """
174
+
175
+ z = (mantissa - 1) / (mantissa + 1)
176
+ square = z * z
177
+ power_of_z = z
178
+ total = z
179
+ term = 1
180
+ while True:
181
+ power_of_z = power_of_z * square
182
+ addend = power_of_z / (2 * term + 1)
183
+ if total + addend == total:
184
+ return 2 * total
185
+ total += addend
186
+ term += 1
187
+
188
+
189
+ with decimal.localcontext(_REDUCING):
190
+ _PI = 16 * _arc_tangent_of_reciprocal(5) - 4 * _arc_tangent_of_reciprocal(239)
191
+ _HALF_PI = _PI / 2
192
+ _LOG_TWO = _logarithm_series(Decimal(2))
193
+ # ten is eight times a quarter more than one, so the logarithm of ten is three logarithms of
194
+ # two plus one more series, all of them reduced enough to converge quickly.
195
+ _LOG_TEN = 3 * _LOG_TWO + _logarithm_series(Decimal("1.25"))
196
+ _ROOT_TWO = Decimal(2).sqrt()
197
+
198
+ #: Pi as a float64. Equal to ``math.pi``, and the tests assert that it is: this module computes
199
+ #: the constant it needs rather than importing one, and the agreement is the evidence that both
200
+ #: are the correctly rounded double.
201
+ PI = float(_PI)
202
+
203
+ #: One degree in radians, and one radian in degrees. Each is a single IEEE-754 division of two
204
+ #: doubles, which every conforming machine rounds the same way, so a conversion is one exactly
205
+ #: reproducible multiplication.
206
+ _DEGREES_TO_RADIANS = PI / 180.0
207
+ _RADIANS_TO_DEGREES = 180.0 / PI
208
+
209
+ # The arc tangent series is halved down to here before it is summed. Below a twentieth the
210
+ # terms fall by a factor of four hundred each time and thirty of them are more than enough.
211
+ _ARC_TANGENT_THRESHOLD = Decimal("0.05")
212
+
213
+
214
+ # --- What a caller may hand in ---------------------------------------------------------------
215
+
216
+
217
+ def _argument(value: float, function: str) -> Decimal:
218
+ """One argument, exactly, or a refusal that names the function and what arrived.
219
+
220
+ A bool is a whole number to Python and an argument nobody meant to pass here, so it is
221
+ turned away with everything else that is not a real number.
222
+ """
223
+
224
+ if isinstance(value, bool) or not isinstance(value, (int, float)):
225
+ raise TypeError(f"{function} takes a real number, not {value!r}")
226
+ number = Decimal(value)
227
+ if not number.is_finite():
228
+ raise ValueError(f"{function} takes a finite number, not {value!r}")
229
+ return number
230
+
231
+
232
+ # --- Sine, cosine and tangent ----------------------------------------------------------------
233
+
234
+
235
+ def _quarter_turns(angle: Decimal) -> tuple[int, Decimal]:
236
+ """An angle as a whole number of quarter turns and a remainder of at most an eighth turn.
237
+
238
+ The remainder is what the series are given, and it is computed at the reducing precision so
239
+ that the subtraction -- which cancels almost everything when the angle is near a quarter
240
+ turn -- still leaves fifty good digits behind.
241
+ """
242
+
243
+ if abs(angle) > _LARGEST_REDUCIBLE_ANGLE:
244
+ raise ValueError(
245
+ f"an angle of {angle} radians is larger than the {_LARGEST_REDUCIBLE_ANGLE} this "
246
+ f"module reduces exactly; every angle on this Reader's path is bounded by half a "
247
+ f"turn and this one is not one of them"
248
+ )
249
+ with decimal.localcontext(_REDUCING):
250
+ turns = (angle / _HALF_PI).to_integral_value(rounding=decimal.ROUND_HALF_EVEN)
251
+ remainder = angle - turns * _HALF_PI
252
+ return int(turns) % 4, +remainder
253
+
254
+
255
+ def _sine_series(remainder: Decimal) -> Decimal:
256
+ """``sin`` of an angle already reduced to an eighth of a turn or less."""
257
+
258
+ square = remainder * remainder
259
+ term = remainder
260
+ total = remainder
261
+ index = 1
262
+ while True:
263
+ term = -term * square / ((2 * index) * (2 * index + 1))
264
+ if total + term == total:
265
+ return total
266
+ total += term
267
+ index += 1
268
+
269
+
270
+ def _cosine_series(remainder: Decimal) -> Decimal:
271
+ """``cos`` of an angle already reduced to an eighth of a turn or less."""
272
+
273
+ square = remainder * remainder
274
+ term = Decimal(1)
275
+ total = Decimal(1)
276
+ index = 1
277
+ while True:
278
+ term = -term * square / ((2 * index - 1) * (2 * index))
279
+ if total + term == total:
280
+ return total
281
+ total += term
282
+ index += 1
283
+
284
+
285
+ def _sine(angle: Decimal) -> Decimal:
286
+ quarter, remainder = _quarter_turns(angle)
287
+ if quarter == 0:
288
+ return _sine_series(remainder)
289
+ if quarter == 1:
290
+ return _cosine_series(remainder)
291
+ if quarter == 2:
292
+ return -_sine_series(remainder)
293
+ return -_cosine_series(remainder)
294
+
295
+
296
+ def _cosine(angle: Decimal) -> Decimal:
297
+ quarter, remainder = _quarter_turns(angle)
298
+ if quarter == 0:
299
+ return _cosine_series(remainder)
300
+ if quarter == 1:
301
+ return -_sine_series(remainder)
302
+ if quarter == 2:
303
+ return -_cosine_series(remainder)
304
+ return _sine_series(remainder)
305
+
306
+
307
+ def sin(x: float) -> float:
308
+ """The sine of an angle in radians."""
309
+
310
+ with decimal.localcontext(_WORKING):
311
+ return float(_sine(_argument(x, "sin")))
312
+
313
+
314
+ def cos(x: float) -> float:
315
+ """The cosine of an angle in radians."""
316
+
317
+ with decimal.localcontext(_WORKING):
318
+ return float(_cosine(_argument(x, "cos")))
319
+
320
+
321
+ def tan(x: float) -> float:
322
+ """The tangent of an angle in radians, as the quotient of the two computed above.
323
+
324
+ The quarter-turn reduction leaves the cosine of a remainder no larger than an eighth of a
325
+ turn, which is never nearer to zero than about 0.707, so the divisor here is zero only if the
326
+ reduction itself changed. The guard is for that day rather than for any argument.
327
+ """
328
+
329
+ with decimal.localcontext(_WORKING):
330
+ angle = _argument(x, "tan")
331
+ divisor = _cosine(angle)
332
+ if divisor == 0:
333
+ raise ValueError(f"tan has no value at {x!r}, where the cosine is exactly zero")
334
+ return float(_sine(angle) / divisor)
335
+
336
+
337
+ # --- Logarithm, exponential and a fractional power -------------------------------------------
338
+
339
+
340
+ def _logarithm(number: Decimal) -> Decimal:
341
+ """The natural logarithm, reduced to a mantissa the series converges quickly on.
342
+
343
+ A decimal already carries its own power of ten, so the first reduction is free: shift it out
344
+ and the mantissa is between one and ten. Halving at most three more times brings it between
345
+ the square roots of a half and of two. Each halving is exact -- a mantissa that came from a
346
+ float64 has seventeen digits and there are fifty to hold it -- so nothing is lost getting
347
+ there.
348
+ """
349
+
350
+ exponent = number.adjusted()
351
+ mantissa = number.scaleb(Decimal(-exponent))
352
+ halvings = 0
353
+ while mantissa >= _ROOT_TWO:
354
+ mantissa = mantissa / 2
355
+ halvings += 1
356
+ return exponent * _LOG_TEN + halvings * _LOG_TWO + _logarithm_series(mantissa)
357
+
358
+
359
+ def _exponential(power_of_e: Decimal) -> Decimal:
360
+ """``e`` to a power, reduced modulo the logarithm of ten so the scaling back is exact.
361
+
362
+ Reducing by ten rather than by two is deliberate: a decimal scales by powers of ten for
363
+ nothing, so the last step of this function moves an exponent rather than performing a
364
+ multiplication that would have to be rounded.
365
+ """
366
+
367
+ with decimal.localcontext(_REDUCING):
368
+ scale = int((power_of_e / _LOG_TEN).to_integral_value(rounding=decimal.ROUND_HALF_EVEN))
369
+ remainder = +(power_of_e - scale * _LOG_TEN)
370
+ term = Decimal(1)
371
+ total = Decimal(1)
372
+ index = 1
373
+ while True:
374
+ term = term * remainder / index
375
+ if total + term == total:
376
+ return total.scaleb(Decimal(scale))
377
+ total += term
378
+ index += 1
379
+
380
+
381
+ def log(x: float) -> float:
382
+ """The natural logarithm of a positive number."""
383
+
384
+ with decimal.localcontext(_WORKING):
385
+ number = _argument(x, "log")
386
+ if number <= 0:
387
+ raise ValueError(f"log takes a positive number, not {x!r}")
388
+ return float(_logarithm(number))
389
+
390
+
391
+ def power(base: float, exponent: float) -> float:
392
+ """A positive base raised to any power, as ``exp(exponent * log(base))``.
393
+
394
+ Written as one function rather than left to ``**`` because the operator on two floats is the
395
+ C library's ``pow``, which is exactly the kind of call this module exists to remove. The
396
+ base is required to be positive: every use of it here raises a tangent or a radius, both of
397
+ which are checked positive by their caller before they arrive.
398
+ """
399
+
400
+ with decimal.localcontext(_WORKING):
401
+ bottom = _argument(base, "power")
402
+ top = _argument(exponent, "power")
403
+ if bottom <= 0:
404
+ raise ValueError(f"power takes a positive base, not {base!r}")
405
+ if top == 0 or bottom == 1:
406
+ return 1.0
407
+ return float(_exponential(_logarithm(bottom) * top))
408
+
409
+
410
+ # --- Arc tangent, in one and in two arguments ------------------------------------------------
411
+
412
+
413
+ def _arc_tangent_series(x: Decimal) -> Decimal:
414
+ """``atan`` of an argument the caller has already halved down below a twentieth."""
415
+
416
+ square = x * x
417
+ signed = x
418
+ total = x
419
+ index = 1
420
+ while True:
421
+ signed = -signed * square
422
+ addend = signed / (2 * index + 1)
423
+ if total + addend == total:
424
+ return total
425
+ total += addend
426
+ index += 1
427
+
428
+
429
+ def _arc_tangent(x: Decimal) -> Decimal:
430
+ """The arc tangent, by reciprocal above one and by repeated halving below it.
431
+
432
+ ``atan(x) = 2 atan(x / (1 + sqrt(1 + x^2)))`` is the halving identity, and four or five of
433
+ them carry any argument in the unit interval below the series threshold.
434
+ """
435
+
436
+ if x < 0:
437
+ return -_arc_tangent(-x)
438
+ if x > 1:
439
+ return _HALF_PI - _arc_tangent(1 / x)
440
+ halvings = 0
441
+ while x > _ARC_TANGENT_THRESHOLD:
442
+ x = x / (1 + (1 + x * x).sqrt())
443
+ halvings += 1
444
+ return (2**halvings) * _arc_tangent_series(x)
445
+
446
+
447
+ def atan(x: float) -> float:
448
+ """The arc tangent of a number, in radians, between minus and plus a quarter turn."""
449
+
450
+ with decimal.localcontext(_WORKING):
451
+ return float(_arc_tangent(_argument(x, "atan")))
452
+
453
+
454
+ def atan2(y: float, x: float) -> float:
455
+ """The angle of the point ``(x, y)`` from the positive x axis, in radians.
456
+
457
+ Signed zeros are not distinguished: a zero is a zero here, so ``atan2(0.0, -1.0)`` is half a
458
+ turn and never minus half a turn. The one caller in this Reader guards against both
459
+ arguments being zero before it calls, because a point at the apex of the cone has every
460
+ longitude and is refused there by name rather than answered here by convention.
461
+ """
462
+
463
+ with decimal.localcontext(_WORKING):
464
+ across = _argument(x, "atan2")
465
+ up = _argument(y, "atan2")
466
+ if across == 0:
467
+ if up == 0:
468
+ return 0.0
469
+ return float(_HALF_PI if up > 0 else -_HALF_PI)
470
+ angle = _arc_tangent(up / across)
471
+ if across > 0:
472
+ return float(angle)
473
+ return float(angle + _PI if up >= 0 else angle - _PI)
474
+
475
+
476
+ # --- The rest: exact float64 work, kept here so the projection has one place to look ---------
477
+
478
+
479
+ def hypot(x: float, y: float) -> float:
480
+ """The length of the hypotenuse, from a square root the specification rounds correctly."""
481
+
482
+ with decimal.localcontext(_WORKING):
483
+ across = _argument(x, "hypot")
484
+ up = _argument(y, "hypot")
485
+ return float((across * across + up * up).sqrt())
486
+
487
+
488
+ def radians(degrees_of_arc: float) -> float:
489
+ """Degrees to radians: one multiplication, correctly rounded on every machine."""
490
+
491
+ if isinstance(degrees_of_arc, bool) or not isinstance(degrees_of_arc, (int, float)):
492
+ raise TypeError(f"radians takes a real number, not {degrees_of_arc!r}")
493
+ return degrees_of_arc * _DEGREES_TO_RADIANS
494
+
495
+
496
+ def degrees(radians_of_arc: float) -> float:
497
+ """Radians to degrees: one multiplication, correctly rounded on every machine."""
498
+
499
+ if isinstance(radians_of_arc, bool) or not isinstance(radians_of_arc, (int, float)):
500
+ raise TypeError(f"degrees takes a real number, not {radians_of_arc!r}")
501
+ return radians_of_arc * _RADIANS_TO_DEGREES