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,551 @@
1
+ """Zero-third-party native protected-store adapters.
2
+
3
+ The adapter selected for an operating system is final. A missing, locked, timed-out, or refusing
4
+ native facility is a typed error; this module never catches one and substitutes secure-file.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import sys
10
+ from collections.abc import Callable
11
+ from pathlib import Path
12
+ from typing import Protocol
13
+
14
+ from mostlyright.data_harness.ux.credential_store import (
15
+ MAX_CREDENTIAL_ENVELOPE_BYTES,
16
+ CredentialBackend,
17
+ CredentialStoreError,
18
+ SecureFileBackend,
19
+ )
20
+
21
+ _SERVICE = "md.mostlyright.cli"
22
+ _ACCOUNT = "default"
23
+ _SECRET_TOOL_TIMEOUT_SECONDS = 8.0
24
+ _SECRET_TOOL_ATTRIBUTES = ("application", "mostlyright-cli", "profile", "default")
25
+ _DPAPI_FLAGS = 0x1 # CRYPTPROTECT_UI_FORBIDDEN; intentionally excludes LOCAL_MACHINE (0x4).
26
+
27
+
28
+ class KeychainFFI(Protocol):
29
+ def read(self, *, service: str, account: str) -> bytes | None: ...
30
+
31
+ def write(self, payload: bytearray, *, service: str, account: str) -> None: ...
32
+
33
+ def delete(self, *, service: str, account: str) -> None: ...
34
+
35
+
36
+ class DPAPIFFI(Protocol):
37
+ def protect(self, plaintext: bytearray, *, flags: int) -> bytes: ...
38
+
39
+ def unprotect(self, ciphertext: bytes, *, flags: int) -> bytearray: ...
40
+
41
+
42
+ class MacOSKeychainBackend:
43
+ """One Keychain generic-password item through Security.framework item APIs."""
44
+
45
+ name = "keychain"
46
+
47
+ def __init__(self, *, ffi: KeychainFFI | None = None) -> None:
48
+ try:
49
+ self._ffi = ffi or _MacOSSecurityFramework()
50
+ except Exception:
51
+ raise _native_refusal("Keychain", "initialization") from None
52
+
53
+ def read(self) -> bytes | None:
54
+ try:
55
+ payload = self._ffi.read(service=_SERVICE, account=_ACCOUNT)
56
+ if payload is not None and len(payload) > MAX_CREDENTIAL_ENVELOPE_BYTES:
57
+ raise ValueError("oversized item")
58
+ return payload
59
+ except Exception:
60
+ raise _native_refusal("Keychain", "copy") from None
61
+
62
+ def write(self, payload: bytes) -> None:
63
+ if len(payload) > MAX_CREDENTIAL_ENVELOPE_BYTES:
64
+ raise CredentialStoreError(
65
+ "CREDENTIAL_STORE_CORRUPT", "the protected credential envelope exceeds its limit"
66
+ )
67
+ mutable = bytearray(payload)
68
+ try:
69
+ self._ffi.write(mutable, service=_SERVICE, account=_ACCOUNT)
70
+ except Exception:
71
+ raise _native_refusal("Keychain", "store") from None
72
+ finally:
73
+ _zero(mutable)
74
+
75
+ def delete(self) -> None:
76
+ try:
77
+ self._ffi.delete(service=_SERVICE, account=_ACCOUNT)
78
+ except Exception:
79
+ raise _native_refusal("Keychain", "delete") from None
80
+
81
+
82
+ class DPAPIBackend:
83
+ """Current-user DPAPI protection with only the encrypted blob stored atomically."""
84
+
85
+ name = "dpapi"
86
+
87
+ def __init__(self, config_dir: Path, *, ffi: DPAPIFFI | None = None) -> None:
88
+ try:
89
+ self._ffi = ffi or _WindowsDPAPI()
90
+ # This file contains only DPAPI ciphertext. Reusing the atomic private-file writer
91
+ # keeps replace/fsync/link checks identical without making secure-file a fallback.
92
+ self._blob = SecureFileBackend(
93
+ config_dir,
94
+ explicit=True,
95
+ filename="credentials.dpapi",
96
+ )
97
+ except Exception:
98
+ raise _native_refusal("DPAPI", "initialization") from None
99
+
100
+ def read(self) -> bytes | None:
101
+ plaintext: bytearray | None = None
102
+ try:
103
+ ciphertext = self._blob.read()
104
+ if ciphertext is None:
105
+ return None
106
+ plaintext = self._ffi.unprotect(ciphertext, flags=_DPAPI_FLAGS)
107
+ if len(plaintext) > MAX_CREDENTIAL_ENVELOPE_BYTES:
108
+ raise ValueError("oversized plaintext")
109
+ return bytes(plaintext)
110
+ except Exception:
111
+ raise _native_refusal("DPAPI", "unprotect") from None
112
+ finally:
113
+ if plaintext is not None:
114
+ _zero(plaintext)
115
+
116
+ def write(self, payload: bytes) -> None:
117
+ if len(payload) > MAX_CREDENTIAL_ENVELOPE_BYTES:
118
+ raise CredentialStoreError(
119
+ "CREDENTIAL_STORE_CORRUPT", "the protected credential envelope exceeds its limit"
120
+ )
121
+ plaintext = bytearray(payload)
122
+ try:
123
+ ciphertext = self._ffi.protect(plaintext, flags=_DPAPI_FLAGS)
124
+ if not ciphertext or len(ciphertext) > MAX_CREDENTIAL_ENVELOPE_BYTES:
125
+ raise ValueError("invalid DPAPI ciphertext")
126
+ self._blob.write(ciphertext)
127
+ except Exception:
128
+ raise _native_refusal("DPAPI", "protect") from None
129
+ finally:
130
+ _zero(plaintext)
131
+
132
+ def delete(self) -> None:
133
+ try:
134
+ self._blob.delete()
135
+ except Exception:
136
+ raise _native_refusal("DPAPI", "delete") from None
137
+
138
+
139
+ class _ProcessResult(Protocol):
140
+ returncode: int
141
+ stdout: bytes
142
+ stderr: bytes
143
+
144
+
145
+ _ProcessRunner = Callable[..., _ProcessResult]
146
+
147
+
148
+ class SecretServiceBackend:
149
+ """Linux Secret Service through bounded ``secret-tool`` subprocesses."""
150
+
151
+ name = "secret-service"
152
+
153
+ def __init__(
154
+ self,
155
+ *,
156
+ executable: str,
157
+ process_runner: _ProcessRunner | None = None,
158
+ ) -> None:
159
+ if not executable:
160
+ raise _native_refusal("Secret Service", "probe")
161
+ if process_runner is None:
162
+ import subprocess
163
+
164
+ process_runner = subprocess.run
165
+ self._executable = executable
166
+ self._run = process_runner
167
+
168
+ def _invoke(self, operation: str, *, payload: bytes | None) -> _ProcessResult:
169
+ import subprocess
170
+
171
+ if operation == "store":
172
+ args = [
173
+ self._executable,
174
+ "store",
175
+ "--label=Mostly Right CLI",
176
+ *_SECRET_TOOL_ATTRIBUTES,
177
+ ]
178
+ else:
179
+ args = [self._executable, operation, *_SECRET_TOOL_ATTRIBUTES]
180
+ try:
181
+ completed = self._run(
182
+ args,
183
+ input=payload,
184
+ capture_output=True,
185
+ timeout=_SECRET_TOOL_TIMEOUT_SECONDS,
186
+ check=False,
187
+ )
188
+ except (OSError, subprocess.TimeoutExpired):
189
+ raise _native_refusal("Secret Service", operation) from None
190
+ # Never retain or render subprocess diagnostics. They may contain the item value, D-Bus
191
+ # details, a user name, or a hostile canary emitted by an injected executable.
192
+ if len(completed.stdout or b"") > MAX_CREDENTIAL_ENVELOPE_BYTES:
193
+ raise _native_refusal("Secret Service", operation)
194
+ if len(completed.stderr or b"") > MAX_CREDENTIAL_ENVELOPE_BYTES:
195
+ raise _native_refusal("Secret Service", operation)
196
+ return completed
197
+
198
+ def read(self) -> bytes | None:
199
+ completed = self._invoke("lookup", payload=None)
200
+ if completed.returncode == 1 and not completed.stdout and not completed.stderr:
201
+ return None
202
+ if completed.returncode != 0:
203
+ raise _native_refusal("Secret Service", "lookup")
204
+ if not completed.stdout:
205
+ raise _native_refusal("Secret Service", "lookup")
206
+ # ``secret-tool lookup`` appends a newline to its printed secret. Canonical envelopes do
207
+ # not contain trailing whitespace, so remove exactly that transport newline and no other
208
+ # byte.
209
+ return completed.stdout[:-1] if completed.stdout.endswith(b"\n") else completed.stdout
210
+
211
+ def write(self, payload: bytes) -> None:
212
+ if len(payload) > MAX_CREDENTIAL_ENVELOPE_BYTES:
213
+ raise CredentialStoreError(
214
+ "CREDENTIAL_STORE_CORRUPT", "the protected credential envelope exceeds its limit"
215
+ )
216
+ completed = self._invoke("store", payload=payload)
217
+ if completed.returncode != 0:
218
+ raise _native_refusal("Secret Service", "store")
219
+
220
+ def delete(self) -> None:
221
+ completed = self._invoke("clear", payload=None)
222
+ if completed.returncode == 1 and not completed.stdout and not completed.stderr:
223
+ return
224
+ if completed.returncode != 0:
225
+ raise _native_refusal("Secret Service", "clear")
226
+
227
+
228
+ def native_backend(
229
+ config_dir: Path,
230
+ *,
231
+ platform_name: str | None = None,
232
+ executable_finder: Callable[[str], str | None] | None = None,
233
+ ) -> CredentialBackend:
234
+ """Select the current OS protected backend, with no fallback on absence or failure."""
235
+
236
+ selected = sys.platform if platform_name is None else platform_name
237
+ try:
238
+ if selected == "darwin":
239
+ return MacOSKeychainBackend()
240
+ if selected == "win32":
241
+ return DPAPIBackend(config_dir)
242
+ if selected.startswith("linux"):
243
+ if executable_finder is None:
244
+ import shutil
245
+
246
+ executable_finder = shutil.which
247
+ executable = executable_finder("secret-tool")
248
+ if executable is None:
249
+ raise CredentialStoreError(
250
+ "CREDENTIAL_STORE_UNAVAILABLE",
251
+ "Secret Service probe failed; explicitly select "
252
+ "--credential-store secure-file to use an owner-controlled file",
253
+ )
254
+ return SecretServiceBackend(executable=executable)
255
+ except CredentialStoreError:
256
+ raise
257
+ except Exception:
258
+ pass
259
+ raise CredentialStoreError(
260
+ "CREDENTIAL_STORE_UNAVAILABLE",
261
+ "native protected storage is unavailable; explicitly select "
262
+ "--credential-store secure-file to use an owner-controlled file",
263
+ )
264
+
265
+
266
+ def _native_refusal(adapter: str, outcome: str) -> CredentialStoreError:
267
+ return CredentialStoreError(
268
+ "CREDENTIAL_STORE_UNAVAILABLE",
269
+ f"{adapter} {outcome} failed",
270
+ )
271
+
272
+
273
+ def _zero(value: bytearray) -> None:
274
+ for index in range(len(value)):
275
+ value[index] = 0
276
+
277
+
278
+ class _WindowsDPAPI:
279
+ def __init__(self) -> None:
280
+ import ctypes
281
+
282
+ if sys.platform != "win32":
283
+ raise OSError("DPAPI is not available")
284
+ self._ctypes = ctypes
285
+
286
+ class DataBlob(ctypes.Structure):
287
+ _fields_ = [
288
+ ("cbData", ctypes.c_uint32),
289
+ ("pbData", ctypes.POINTER(ctypes.c_ubyte)),
290
+ ]
291
+
292
+ self._blob_type = DataBlob
293
+ self._crypt32 = ctypes.WinDLL("crypt32", use_last_error=True)
294
+ self._kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
295
+ self._crypt32.CryptProtectData.argtypes = [
296
+ ctypes.POINTER(DataBlob),
297
+ ctypes.c_wchar_p,
298
+ ctypes.POINTER(DataBlob),
299
+ ctypes.c_void_p,
300
+ ctypes.c_void_p,
301
+ ctypes.c_uint32,
302
+ ctypes.POINTER(DataBlob),
303
+ ]
304
+ self._crypt32.CryptProtectData.restype = ctypes.c_int
305
+ self._crypt32.CryptUnprotectData.argtypes = [
306
+ ctypes.POINTER(DataBlob),
307
+ ctypes.c_void_p,
308
+ ctypes.POINTER(DataBlob),
309
+ ctypes.c_void_p,
310
+ ctypes.c_void_p,
311
+ ctypes.c_uint32,
312
+ ctypes.POINTER(DataBlob),
313
+ ]
314
+ self._crypt32.CryptUnprotectData.restype = ctypes.c_int
315
+ self._kernel32.LocalFree.argtypes = [ctypes.c_void_p]
316
+ self._kernel32.LocalFree.restype = ctypes.c_void_p
317
+
318
+ def _input_blob(self, value: bytearray | bytes) -> tuple[object, object]:
319
+ ctypes = self._ctypes
320
+ if isinstance(value, bytearray):
321
+ buffer = (ctypes.c_ubyte * len(value)).from_buffer(value)
322
+ else:
323
+ buffer = (ctypes.c_ubyte * len(value)).from_buffer_copy(value)
324
+ return (
325
+ self._blob_type(len(value), ctypes.cast(buffer, ctypes.POINTER(ctypes.c_ubyte))),
326
+ buffer,
327
+ )
328
+
329
+ def protect(self, plaintext: bytearray, *, flags: int) -> bytes:
330
+ ctypes = self._ctypes
331
+ source, keepalive = self._input_blob(plaintext)
332
+ result = self._blob_type()
333
+ if not self._crypt32.CryptProtectData(
334
+ ctypes.byref(source), None, None, None, None, flags, ctypes.byref(result)
335
+ ):
336
+ raise OSError("CryptProtectData failed")
337
+ try:
338
+ return ctypes.string_at(result.pbData, result.cbData)
339
+ finally:
340
+ del keepalive
341
+ self._kernel32.LocalFree(result.pbData)
342
+
343
+ def unprotect(self, ciphertext: bytes, *, flags: int) -> bytearray:
344
+ ctypes = self._ctypes
345
+ source, keepalive = self._input_blob(ciphertext)
346
+ result = self._blob_type()
347
+ if not self._crypt32.CryptUnprotectData(
348
+ ctypes.byref(source), None, None, None, None, flags, ctypes.byref(result)
349
+ ):
350
+ raise OSError("CryptUnprotectData failed")
351
+ try:
352
+ plaintext = bytearray(ctypes.string_at(result.pbData, result.cbData))
353
+ ctypes.memset(result.pbData, 0, result.cbData)
354
+ return plaintext
355
+ finally:
356
+ del keepalive
357
+ self._kernel32.LocalFree(result.pbData)
358
+
359
+
360
+ class _MacOSSecurityFramework:
361
+ """Small CoreFoundation bridge for four Security.framework item operations."""
362
+
363
+ _SUCCESS = 0
364
+ _DUPLICATE = -25299
365
+ _NOT_FOUND = -25300
366
+ _UTF8 = 0x08000100
367
+
368
+ def __init__(self) -> None:
369
+ import ctypes
370
+ import ctypes.util
371
+
372
+ if sys.platform != "darwin":
373
+ raise OSError("Security.framework is not available")
374
+ self._ctypes = ctypes
375
+ security_path = ctypes.util.find_library("Security")
376
+ core_path = ctypes.util.find_library("CoreFoundation")
377
+ if not security_path or not core_path:
378
+ raise OSError("framework unavailable")
379
+ self._security = ctypes.CDLL(security_path)
380
+ self._core = ctypes.CDLL(core_path)
381
+ self._configure()
382
+ self._class = self._constant(self._security, "kSecClass")
383
+ self._generic = self._constant(self._security, "kSecClassGenericPassword")
384
+ self._service = self._constant(self._security, "kSecAttrService")
385
+ self._account = self._constant(self._security, "kSecAttrAccount")
386
+ self._value = self._constant(self._security, "kSecValueData")
387
+ self._return_data = self._constant(self._security, "kSecReturnData")
388
+ self._match_limit = self._constant(self._security, "kSecMatchLimit")
389
+ self._match_one = self._constant(self._security, "kSecMatchLimitOne")
390
+ self._true = self._constant(self._core, "kCFBooleanTrue")
391
+
392
+ def _constant(self, library: object, name: str) -> int:
393
+ ctypes = self._ctypes
394
+ value = ctypes.c_void_p.in_dll(library, name).value
395
+ if value is None:
396
+ raise OSError("framework constant unavailable")
397
+ return value
398
+
399
+ def _configure(self) -> None:
400
+ ctypes = self._ctypes
401
+ self._core.CFStringCreateWithBytes.argtypes = [
402
+ ctypes.c_void_p,
403
+ ctypes.c_void_p,
404
+ ctypes.c_long,
405
+ ctypes.c_uint32,
406
+ ctypes.c_bool,
407
+ ]
408
+ self._core.CFStringCreateWithBytes.restype = ctypes.c_void_p
409
+ self._core.CFDataCreate.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_long]
410
+ self._core.CFDataCreate.restype = ctypes.c_void_p
411
+ self._core.CFDataGetLength.argtypes = [ctypes.c_void_p]
412
+ self._core.CFDataGetLength.restype = ctypes.c_long
413
+ self._core.CFDataGetBytePtr.argtypes = [ctypes.c_void_p]
414
+ self._core.CFDataGetBytePtr.restype = ctypes.POINTER(ctypes.c_ubyte)
415
+ self._core.CFDictionaryCreate.argtypes = [
416
+ ctypes.c_void_p,
417
+ ctypes.c_void_p,
418
+ ctypes.c_void_p,
419
+ ctypes.c_long,
420
+ ctypes.c_void_p,
421
+ ctypes.c_void_p,
422
+ ]
423
+ self._core.CFDictionaryCreate.restype = ctypes.c_void_p
424
+ self._core.CFRelease.argtypes = [ctypes.c_void_p]
425
+ self._security.SecItemCopyMatching.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
426
+ self._security.SecItemCopyMatching.restype = ctypes.c_int32
427
+ self._security.SecItemAdd.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
428
+ self._security.SecItemAdd.restype = ctypes.c_int32
429
+ self._security.SecItemUpdate.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
430
+ self._security.SecItemUpdate.restype = ctypes.c_int32
431
+ self._security.SecItemDelete.argtypes = [ctypes.c_void_p]
432
+ self._security.SecItemDelete.restype = ctypes.c_int32
433
+
434
+ def _string(self, value: str) -> int:
435
+ ctypes = self._ctypes
436
+ raw = value.encode("utf-8")
437
+ buffer = ctypes.create_string_buffer(raw)
438
+ result = self._core.CFStringCreateWithBytes(None, buffer, len(raw), self._UTF8, False)
439
+ if not result:
440
+ raise OSError("CFString allocation failed")
441
+ return result
442
+
443
+ def _data(self, value: bytearray) -> int:
444
+ ctypes = self._ctypes
445
+ buffer = (ctypes.c_ubyte * len(value)).from_buffer(value)
446
+ result = self._core.CFDataCreate(None, buffer, len(value))
447
+ if not result:
448
+ raise OSError("CFData allocation failed")
449
+ return result
450
+
451
+ def _dictionary(self, items: list[tuple[int, int]]) -> int:
452
+ ctypes = self._ctypes
453
+ keys = (ctypes.c_void_p * len(items))(*(key for key, _ in items))
454
+ values = (ctypes.c_void_p * len(items))(*(value for _, value in items))
455
+ result = self._core.CFDictionaryCreate(None, keys, values, len(items), None, None)
456
+ if not result:
457
+ raise OSError("CFDictionary allocation failed")
458
+ return result
459
+
460
+ def _query(self, service: str, account: str) -> tuple[int, list[int]]:
461
+ service_ref = self._string(service)
462
+ account_ref = self._string(account)
463
+ query = self._dictionary(
464
+ [
465
+ (self._class, self._generic),
466
+ (self._service, service_ref),
467
+ (self._account, account_ref),
468
+ ]
469
+ )
470
+ return query, [service_ref, account_ref]
471
+
472
+ def _release_all(self, values: list[int]) -> None:
473
+ for value in reversed(values):
474
+ self._core.CFRelease(value)
475
+
476
+ def read(self, *, service: str, account: str) -> bytes | None:
477
+ ctypes = self._ctypes
478
+ query, owned = self._query(service, account)
479
+ rich_query = self._dictionary(
480
+ [
481
+ (self._class, self._generic),
482
+ (self._service, owned[0]),
483
+ (self._account, owned[1]),
484
+ (self._return_data, self._true),
485
+ (self._match_limit, self._match_one),
486
+ ]
487
+ )
488
+ self._core.CFRelease(query)
489
+ result = ctypes.c_void_p()
490
+ try:
491
+ status = self._security.SecItemCopyMatching(rich_query, ctypes.byref(result))
492
+ if status == self._NOT_FOUND:
493
+ return None
494
+ if status != self._SUCCESS or not result.value:
495
+ raise OSError("SecItemCopyMatching failed")
496
+ length = self._core.CFDataGetLength(result)
497
+ if length < 0 or length > MAX_CREDENTIAL_ENVELOPE_BYTES:
498
+ raise OSError("invalid Keychain item size")
499
+ return ctypes.string_at(self._core.CFDataGetBytePtr(result), length)
500
+ finally:
501
+ if result.value:
502
+ self._core.CFRelease(result)
503
+ self._core.CFRelease(rich_query)
504
+ self._release_all(owned)
505
+
506
+ def write(self, payload: bytearray, *, service: str, account: str) -> None:
507
+ query, owned = self._query(service, account)
508
+ data = self._data(payload)
509
+ attributes = self._dictionary([(self._value, data)])
510
+ try:
511
+ status = self._security.SecItemUpdate(query, attributes)
512
+ if status == self._NOT_FOUND:
513
+ add = self._dictionary(
514
+ [
515
+ (self._class, self._generic),
516
+ (self._service, owned[0]),
517
+ (self._account, owned[1]),
518
+ (self._value, data),
519
+ ]
520
+ )
521
+ try:
522
+ status = self._security.SecItemAdd(add, None)
523
+ finally:
524
+ self._core.CFRelease(add)
525
+ if status == self._DUPLICATE:
526
+ status = self._security.SecItemUpdate(query, attributes)
527
+ if status != self._SUCCESS:
528
+ raise OSError("Keychain write failed")
529
+ finally:
530
+ self._core.CFRelease(attributes)
531
+ self._core.CFRelease(data)
532
+ self._core.CFRelease(query)
533
+ self._release_all(owned)
534
+
535
+ def delete(self, *, service: str, account: str) -> None:
536
+ query, owned = self._query(service, account)
537
+ try:
538
+ status = self._security.SecItemDelete(query)
539
+ if status not in {self._SUCCESS, self._NOT_FOUND}:
540
+ raise OSError("SecItemDelete failed")
541
+ finally:
542
+ self._core.CFRelease(query)
543
+ self._release_all(owned)
544
+
545
+
546
+ __all__ = [
547
+ "DPAPIBackend",
548
+ "MacOSKeychainBackend",
549
+ "SecretServiceBackend",
550
+ "native_backend",
551
+ ]