UncountablePythonSDK 0.0.24__py3-none-any.whl → 0.0.131__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.

Potentially problematic release.


This version of UncountablePythonSDK might be problematic. Click here for more details.

Files changed (373) hide show
  1. docs/conf.py +60 -8
  2. docs/index.md +107 -4
  3. docs/integration_examples/create_ingredient.md +43 -0
  4. docs/integration_examples/create_output.md +56 -0
  5. docs/integration_examples/index.md +6 -0
  6. docs/justfile +2 -2
  7. docs/requirements.txt +7 -5
  8. examples/async_batch.py +5 -6
  9. examples/basic_auth.py +7 -0
  10. examples/create_entity.py +4 -6
  11. examples/create_ingredient_sdk.py +34 -0
  12. examples/download_files.py +26 -0
  13. examples/edit_recipe_inputs.py +50 -0
  14. examples/integration-server/jobs/materials_auto/concurrent_cron.py +11 -0
  15. examples/integration-server/jobs/materials_auto/example_cron.py +21 -0
  16. examples/integration-server/jobs/materials_auto/example_http.py +47 -0
  17. examples/integration-server/jobs/materials_auto/example_instrument.py +100 -0
  18. examples/integration-server/jobs/materials_auto/example_parse.py +140 -0
  19. examples/integration-server/jobs/materials_auto/example_predictions.py +61 -0
  20. examples/integration-server/jobs/materials_auto/example_runsheet_wh.py +39 -0
  21. examples/integration-server/jobs/materials_auto/example_wh.py +23 -0
  22. examples/integration-server/jobs/materials_auto/profile.yaml +104 -0
  23. examples/integration-server/pyproject.toml +224 -0
  24. examples/invoke_uploader.py +26 -0
  25. examples/oauth.py +7 -0
  26. examples/set_recipe_metadata_file.py +40 -0
  27. examples/set_recipe_output_file_sdk.py +26 -0
  28. examples/upload_files.py +2 -3
  29. pkgs/argument_parser/__init__.py +9 -0
  30. pkgs/argument_parser/_is_namedtuple.py +3 -0
  31. pkgs/argument_parser/argument_parser.py +295 -74
  32. pkgs/argument_parser/case_convert.py +4 -3
  33. pkgs/filesystem_utils/__init__.py +20 -0
  34. pkgs/filesystem_utils/_blob_session.py +144 -0
  35. pkgs/filesystem_utils/_gdrive_session.py +309 -0
  36. pkgs/filesystem_utils/_local_session.py +69 -0
  37. pkgs/filesystem_utils/_s3_session.py +118 -0
  38. pkgs/filesystem_utils/_sftp_session.py +151 -0
  39. pkgs/filesystem_utils/file_type_utils.py +91 -0
  40. pkgs/filesystem_utils/filesystem_session.py +39 -0
  41. pkgs/py.typed +0 -0
  42. pkgs/serialization/__init__.py +8 -1
  43. pkgs/serialization/annotation.py +64 -0
  44. pkgs/serialization/missing_sentry.py +1 -1
  45. pkgs/serialization/opaque_key.py +1 -1
  46. pkgs/serialization/serial_alias.py +47 -0
  47. pkgs/serialization/serial_class.py +69 -54
  48. pkgs/serialization/serial_generic.py +16 -0
  49. pkgs/serialization/serial_union.py +84 -0
  50. pkgs/serialization/yaml.py +57 -0
  51. pkgs/serialization_util/__init__.py +7 -7
  52. pkgs/serialization_util/convert_to_snakecase.py +27 -0
  53. pkgs/serialization_util/dataclasses.py +14 -0
  54. pkgs/serialization_util/serialization_helpers.py +117 -71
  55. pkgs/type_spec/actions_registry/__main__.py +0 -4
  56. pkgs/type_spec/actions_registry/emit_typescript.py +5 -5
  57. pkgs/type_spec/builder.py +438 -109
  58. pkgs/type_spec/builder_types.py +9 -0
  59. pkgs/type_spec/config.py +52 -24
  60. pkgs/type_spec/cross_output_links.py +99 -0
  61. pkgs/type_spec/emit_io_ts.py +1 -1
  62. pkgs/type_spec/emit_open_api.py +160 -41
  63. pkgs/type_spec/emit_open_api_util.py +13 -7
  64. pkgs/type_spec/emit_python.py +450 -136
  65. pkgs/type_spec/emit_typescript.py +117 -250
  66. pkgs/type_spec/emit_typescript_util.py +293 -4
  67. pkgs/type_spec/load_types.py +20 -5
  68. pkgs/type_spec/non_discriminated_union_exceptions.py +14 -0
  69. pkgs/type_spec/open_api_util.py +29 -4
  70. pkgs/type_spec/parts/base.py.prepart +13 -10
  71. pkgs/type_spec/parts/base.ts.prepart +4 -0
  72. pkgs/type_spec/type_info/__main__.py +3 -1
  73. pkgs/type_spec/type_info/emit_type_info.py +161 -32
  74. pkgs/type_spec/ui_entry_actions/__init__.py +4 -0
  75. pkgs/type_spec/ui_entry_actions/generate_ui_entry_actions.py +308 -0
  76. pkgs/type_spec/util.py +4 -4
  77. pkgs/type_spec/value_spec/__main__.py +27 -10
  78. pkgs/type_spec/value_spec/convert_type.py +21 -1
  79. pkgs/type_spec/value_spec/emit_python.py +25 -7
  80. pkgs/type_spec/value_spec/types.py +1 -1
  81. uncountable/__init__.py +1 -2
  82. uncountable/core/__init__.py +11 -3
  83. uncountable/core/async_batch.py +16 -1
  84. uncountable/core/client.py +247 -52
  85. uncountable/core/environment.py +41 -0
  86. uncountable/core/file_upload.py +67 -22
  87. uncountable/core/types.py +8 -13
  88. uncountable/integration/cli.py +142 -0
  89. uncountable/integration/construct_client.py +43 -27
  90. uncountable/integration/cron.py +12 -11
  91. uncountable/integration/db/connect.py +12 -2
  92. uncountable/integration/db/session.py +25 -0
  93. uncountable/integration/entrypoint.py +4 -34
  94. uncountable/integration/executors/executors.py +147 -0
  95. uncountable/integration/executors/generic_upload_executor.py +336 -0
  96. uncountable/integration/executors/script_executor.py +15 -9
  97. uncountable/integration/http_server/__init__.py +5 -0
  98. uncountable/integration/http_server/types.py +69 -0
  99. uncountable/integration/job.py +246 -19
  100. uncountable/integration/queue_runner/__init__.py +0 -0
  101. uncountable/integration/queue_runner/command_server/__init__.py +28 -0
  102. uncountable/integration/queue_runner/command_server/command_client.py +133 -0
  103. uncountable/integration/queue_runner/command_server/command_server.py +142 -0
  104. uncountable/integration/queue_runner/command_server/constants.py +4 -0
  105. uncountable/integration/queue_runner/command_server/protocol/__init__.py +0 -0
  106. uncountable/integration/queue_runner/command_server/protocol/command_server.proto +58 -0
  107. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.py +57 -0
  108. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.pyi +114 -0
  109. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2_grpc.py +264 -0
  110. uncountable/integration/queue_runner/command_server/types.py +75 -0
  111. uncountable/integration/queue_runner/datastore/__init__.py +3 -0
  112. uncountable/integration/queue_runner/datastore/datastore_sqlite.py +250 -0
  113. uncountable/integration/queue_runner/datastore/interface.py +29 -0
  114. uncountable/integration/queue_runner/datastore/model.py +24 -0
  115. uncountable/integration/queue_runner/job_scheduler.py +200 -0
  116. uncountable/integration/queue_runner/queue_runner.py +34 -0
  117. uncountable/integration/queue_runner/types.py +7 -0
  118. uncountable/integration/queue_runner/worker.py +116 -0
  119. uncountable/integration/scan_profiles.py +67 -0
  120. uncountable/integration/scheduler.py +199 -0
  121. uncountable/integration/secret_retrieval/__init__.py +3 -0
  122. uncountable/integration/secret_retrieval/retrieve_secret.py +93 -0
  123. uncountable/integration/server.py +103 -54
  124. uncountable/integration/telemetry.py +251 -0
  125. uncountable/integration/webhook_server/entrypoint.py +97 -0
  126. uncountable/types/__init__.py +149 -30
  127. uncountable/types/api/batch/execute_batch.py +16 -9
  128. uncountable/types/api/batch/execute_batch_load_async.py +13 -7
  129. uncountable/types/api/chemical/convert_chemical_formats.py +20 -8
  130. uncountable/types/api/condition_parameters/__init__.py +1 -0
  131. uncountable/types/api/condition_parameters/upsert_condition_match.py +72 -0
  132. uncountable/types/api/entity/create_entities.py +24 -12
  133. uncountable/types/api/entity/create_entity.py +22 -13
  134. uncountable/types/api/entity/create_or_update_entity.py +48 -0
  135. uncountable/types/api/entity/export_entities.py +59 -0
  136. uncountable/types/api/entity/get_entities_data.py +18 -9
  137. uncountable/types/api/entity/grant_entity_permissions.py +48 -0
  138. uncountable/types/api/entity/list_aggregate.py +79 -0
  139. uncountable/types/api/entity/list_entities.py +53 -14
  140. uncountable/types/api/entity/lock_entity.py +45 -0
  141. uncountable/types/api/entity/lookup_entity.py +116 -0
  142. uncountable/types/api/entity/resolve_entity_ids.py +19 -10
  143. uncountable/types/api/entity/set_entity_field_values.py +44 -0
  144. uncountable/types/api/entity/set_values.py +15 -8
  145. uncountable/types/api/entity/transition_entity_phase.py +27 -12
  146. uncountable/types/api/entity/unlock_entity.py +44 -0
  147. uncountable/types/api/equipment/__init__.py +1 -0
  148. uncountable/types/api/equipment/associate_equipment_input.py +43 -0
  149. uncountable/types/api/field_options/__init__.py +1 -0
  150. uncountable/types/api/field_options/upsert_field_options.py +55 -0
  151. uncountable/types/api/files/__init__.py +1 -0
  152. uncountable/types/api/files/download_file.py +77 -0
  153. uncountable/types/api/id_source/list_id_source.py +20 -11
  154. uncountable/types/api/id_source/match_id_source.py +15 -10
  155. uncountable/types/api/input_groups/get_input_group_names.py +16 -7
  156. uncountable/types/api/inputs/create_inputs.py +28 -14
  157. uncountable/types/api/inputs/get_input_data.py +34 -16
  158. uncountable/types/api/inputs/get_input_names.py +19 -10
  159. uncountable/types/api/inputs/get_inputs_data.py +29 -11
  160. uncountable/types/api/inputs/set_input_attribute_values.py +16 -10
  161. uncountable/types/api/inputs/set_input_category.py +44 -0
  162. uncountable/types/api/inputs/set_input_subcategories.py +45 -0
  163. uncountable/types/api/inputs/set_intermediate_type.py +50 -0
  164. uncountable/types/api/integrations/__init__.py +1 -0
  165. uncountable/types/api/integrations/publish_realtime_data.py +41 -0
  166. uncountable/types/api/integrations/push_notification.py +49 -0
  167. uncountable/types/api/integrations/register_sockets_token.py +41 -0
  168. uncountable/types/api/listing/__init__.py +1 -0
  169. uncountable/types/api/listing/fetch_listing.py +58 -0
  170. uncountable/types/api/material_families/__init__.py +1 -0
  171. uncountable/types/api/material_families/update_entity_material_families.py +47 -0
  172. uncountable/types/api/notebooks/__init__.py +1 -0
  173. uncountable/types/api/notebooks/add_notebook_content.py +119 -0
  174. uncountable/types/api/outputs/get_output_data.py +32 -17
  175. uncountable/types/api/outputs/get_output_names.py +18 -9
  176. uncountable/types/api/outputs/get_output_organization.py +173 -0
  177. uncountable/types/api/outputs/resolve_output_conditions.py +23 -11
  178. uncountable/types/api/permissions/set_core_permissions.py +31 -15
  179. uncountable/types/api/project/get_projects.py +20 -11
  180. uncountable/types/api/project/get_projects_data.py +23 -14
  181. uncountable/types/api/recipe_links/create_recipe_link.py +17 -10
  182. uncountable/types/api/recipe_links/remove_recipe_link.py +45 -0
  183. uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py +19 -10
  184. uncountable/types/api/recipes/add_recipe_to_project.py +42 -0
  185. uncountable/types/api/recipes/add_time_series_data.py +64 -0
  186. uncountable/types/api/recipes/archive_recipes.py +14 -7
  187. uncountable/types/api/recipes/associate_recipe_as_input.py +16 -8
  188. uncountable/types/api/recipes/associate_recipe_as_lot.py +14 -7
  189. uncountable/types/api/recipes/clear_recipe_outputs.py +42 -0
  190. uncountable/types/api/recipes/create_mix_order.py +44 -0
  191. uncountable/types/api/recipes/create_recipe.py +21 -14
  192. uncountable/types/api/recipes/create_recipes.py +25 -13
  193. uncountable/types/api/recipes/disassociate_recipe_as_input.py +14 -7
  194. uncountable/types/api/recipes/edit_recipe_inputs.py +208 -19
  195. uncountable/types/api/recipes/get_column_calculation_values.py +57 -0
  196. uncountable/types/api/recipes/get_curve.py +15 -9
  197. uncountable/types/api/recipes/get_recipe_calculations.py +17 -11
  198. uncountable/types/api/recipes/get_recipe_links.py +14 -8
  199. uncountable/types/api/recipes/get_recipe_names.py +16 -7
  200. uncountable/types/api/recipes/get_recipe_output_metadata.py +16 -10
  201. uncountable/types/api/recipes/get_recipes_data.py +96 -45
  202. uncountable/types/api/recipes/lock_recipes.py +64 -0
  203. uncountable/types/api/recipes/remove_recipe_from_project.py +42 -0
  204. uncountable/types/api/recipes/set_recipe_inputs.py +19 -13
  205. uncountable/types/api/recipes/set_recipe_metadata.py +14 -7
  206. uncountable/types/api/recipes/set_recipe_output_annotations.py +114 -0
  207. uncountable/types/api/recipes/set_recipe_output_file.py +55 -0
  208. uncountable/types/api/recipes/set_recipe_outputs.py +40 -15
  209. uncountable/types/api/recipes/set_recipe_tags.py +30 -13
  210. uncountable/types/api/recipes/set_recipe_total.py +59 -0
  211. uncountable/types/api/recipes/unarchive_recipes.py +41 -0
  212. uncountable/types/api/recipes/unlock_recipes.py +51 -0
  213. uncountable/types/api/runsheet/__init__.py +1 -0
  214. uncountable/types/api/runsheet/complete_async_upload.py +41 -0
  215. uncountable/types/api/triggers/run_trigger.py +15 -8
  216. uncountable/types/api/uploader/__init__.py +1 -0
  217. uncountable/types/api/uploader/complete_async_parse.py +46 -0
  218. uncountable/types/api/uploader/invoke_uploader.py +46 -0
  219. uncountable/types/api/user/__init__.py +1 -0
  220. uncountable/types/api/user/get_current_user_info.py +40 -0
  221. uncountable/types/async_batch.py +8 -52
  222. uncountable/types/async_batch_processor.py +694 -18
  223. uncountable/types/async_batch_t.py +108 -0
  224. uncountable/types/async_jobs.py +8 -0
  225. uncountable/types/async_jobs_t.py +52 -0
  226. uncountable/types/auth_retrieval.py +11 -0
  227. uncountable/types/auth_retrieval_t.py +75 -0
  228. uncountable/types/base.py +5 -80
  229. uncountable/types/base_t.py +87 -0
  230. uncountable/types/calculations.py +3 -19
  231. uncountable/types/calculations_t.py +26 -0
  232. uncountable/types/chemical_structure.py +3 -23
  233. uncountable/types/chemical_structure_t.py +28 -0
  234. uncountable/types/client_base.py +1170 -88
  235. uncountable/types/client_config.py +8 -0
  236. uncountable/types/client_config_t.py +36 -0
  237. uncountable/types/curves.py +5 -43
  238. uncountable/types/curves_t.py +50 -0
  239. uncountable/types/data.py +12 -0
  240. uncountable/types/data_t.py +103 -0
  241. uncountable/types/entity.py +8 -270
  242. uncountable/types/entity_t.py +446 -0
  243. uncountable/types/experiment_groups.py +3 -19
  244. uncountable/types/experiment_groups_t.py +26 -0
  245. uncountable/types/exports.py +8 -0
  246. uncountable/types/exports_t.py +34 -0
  247. uncountable/types/field_values.py +25 -61
  248. uncountable/types/field_values_t.py +302 -0
  249. uncountable/types/fields.py +3 -20
  250. uncountable/types/fields_t.py +27 -0
  251. uncountable/types/generic_upload.py +14 -0
  252. uncountable/types/generic_upload_t.py +119 -0
  253. uncountable/types/id_source.py +7 -45
  254. uncountable/types/id_source_t.py +68 -0
  255. uncountable/types/identifier.py +6 -50
  256. uncountable/types/identifier_t.py +62 -0
  257. uncountable/types/input_attributes.py +3 -25
  258. uncountable/types/input_attributes_t.py +29 -0
  259. uncountable/types/inputs.py +6 -57
  260. uncountable/types/inputs_t.py +82 -0
  261. uncountable/types/integration_server.py +8 -0
  262. uncountable/types/integration_server_t.py +46 -0
  263. uncountable/types/integration_session.py +10 -0
  264. uncountable/types/integration_session_t.py +60 -0
  265. uncountable/types/integrations.py +10 -0
  266. uncountable/types/integrations_t.py +62 -0
  267. uncountable/types/job_definition.py +28 -0
  268. uncountable/types/job_definition_t.py +285 -0
  269. uncountable/types/listing.py +9 -0
  270. uncountable/types/listing_t.py +51 -0
  271. uncountable/types/notices.py +8 -0
  272. uncountable/types/notices_t.py +37 -0
  273. uncountable/types/notifications.py +11 -0
  274. uncountable/types/notifications_t.py +74 -0
  275. uncountable/types/outputs.py +3 -22
  276. uncountable/types/outputs_t.py +29 -0
  277. uncountable/types/overrides.py +9 -0
  278. uncountable/types/overrides_t.py +49 -0
  279. uncountable/types/permissions.py +3 -42
  280. uncountable/types/permissions_t.py +45 -0
  281. uncountable/types/phases.py +3 -19
  282. uncountable/types/phases_t.py +26 -0
  283. uncountable/types/post_base.py +3 -26
  284. uncountable/types/post_base_t.py +29 -0
  285. uncountable/types/queued_job.py +17 -0
  286. uncountable/types/queued_job_t.py +140 -0
  287. uncountable/types/recipe_identifiers.py +7 -58
  288. uncountable/types/recipe_identifiers_t.py +75 -0
  289. uncountable/types/recipe_inputs.py +4 -26
  290. uncountable/types/recipe_inputs_t.py +29 -0
  291. uncountable/types/recipe_links.py +4 -46
  292. uncountable/types/recipe_links_t.py +53 -0
  293. uncountable/types/recipe_metadata.py +5 -48
  294. uncountable/types/recipe_metadata_t.py +57 -0
  295. uncountable/types/recipe_output_metadata.py +3 -20
  296. uncountable/types/recipe_output_metadata_t.py +27 -0
  297. uncountable/types/recipe_tags.py +3 -19
  298. uncountable/types/recipe_tags_t.py +26 -0
  299. uncountable/types/recipe_workflow_steps.py +9 -73
  300. uncountable/types/recipe_workflow_steps_t.py +95 -0
  301. uncountable/types/recipes.py +7 -0
  302. uncountable/types/recipes_t.py +25 -0
  303. uncountable/types/response.py +3 -21
  304. uncountable/types/response_t.py +26 -0
  305. uncountable/types/secret_retrieval.py +11 -0
  306. uncountable/types/secret_retrieval_t.py +75 -0
  307. uncountable/types/sockets.py +20 -0
  308. uncountable/types/sockets_t.py +169 -0
  309. uncountable/types/structured_filters.py +25 -0
  310. uncountable/types/structured_filters_t.py +248 -0
  311. uncountable/types/units.py +3 -19
  312. uncountable/types/units_t.py +26 -0
  313. uncountable/types/uploader.py +24 -0
  314. uncountable/types/uploader_t.py +222 -0
  315. uncountable/types/users.py +3 -20
  316. uncountable/types/users_t.py +27 -0
  317. uncountable/types/webhook_job.py +9 -0
  318. uncountable/types/webhook_job_t.py +48 -0
  319. uncountable/types/workflows.py +4 -28
  320. uncountable/types/workflows_t.py +38 -0
  321. uncountablepythonsdk-0.0.131.dist-info/METADATA +64 -0
  322. uncountablepythonsdk-0.0.131.dist-info/RECORD +363 -0
  323. {UncountablePythonSDK-0.0.24.dist-info → uncountablepythonsdk-0.0.131.dist-info}/WHEEL +1 -1
  324. {UncountablePythonSDK-0.0.24.dist-info → uncountablepythonsdk-0.0.131.dist-info}/top_level.txt +0 -1
  325. UncountablePythonSDK-0.0.24.dist-info/METADATA +0 -47
  326. UncountablePythonSDK-0.0.24.dist-info/RECORD +0 -216
  327. docs/quickstart.md +0 -19
  328. examples/recipe-import/importer.py +0 -39
  329. type_spec/external/api/batch/execute_batch.yaml +0 -56
  330. type_spec/external/api/batch/execute_batch_load_async.yaml +0 -18
  331. type_spec/external/api/chemical/convert_chemical_formats.yaml +0 -33
  332. type_spec/external/api/entity/create_entities.yaml +0 -45
  333. type_spec/external/api/entity/create_entity.yaml +0 -51
  334. type_spec/external/api/entity/get_entities_data.yaml +0 -29
  335. type_spec/external/api/entity/list_entities.yaml +0 -52
  336. type_spec/external/api/entity/resolve_entity_ids.yaml +0 -29
  337. type_spec/external/api/entity/set_values.yaml +0 -18
  338. type_spec/external/api/entity/transition_entity_phase.yaml +0 -44
  339. type_spec/external/api/id_source/list_id_source.yaml +0 -35
  340. type_spec/external/api/id_source/match_id_source.yaml +0 -32
  341. type_spec/external/api/input_groups/get_input_group_names.yaml +0 -29
  342. type_spec/external/api/inputs/create_inputs.yaml +0 -48
  343. type_spec/external/api/inputs/get_input_data.yaml +0 -95
  344. type_spec/external/api/inputs/get_input_names.yaml +0 -38
  345. type_spec/external/api/inputs/get_inputs_data.yaml +0 -82
  346. type_spec/external/api/inputs/set_input_attribute_values.yaml +0 -33
  347. type_spec/external/api/outputs/get_output_data.yaml +0 -92
  348. type_spec/external/api/outputs/get_output_names.yaml +0 -35
  349. type_spec/external/api/outputs/resolve_output_conditions.yaml +0 -50
  350. type_spec/external/api/permissions/set_core_permissions.yaml +0 -69
  351. type_spec/external/api/project/get_projects.yaml +0 -42
  352. type_spec/external/api/project/get_projects_data.yaml +0 -50
  353. type_spec/external/api/recipe_links/create_recipe_link.yaml +0 -25
  354. type_spec/external/api/recipe_metadata/get_recipe_metadata_data.yaml +0 -41
  355. type_spec/external/api/recipes/archive_recipes.yaml +0 -20
  356. type_spec/external/api/recipes/associate_recipe_as_input.yaml +0 -19
  357. type_spec/external/api/recipes/associate_recipe_as_lot.yaml +0 -19
  358. type_spec/external/api/recipes/create_recipe.yaml +0 -39
  359. type_spec/external/api/recipes/create_recipes.yaml +0 -47
  360. type_spec/external/api/recipes/disassociate_recipe_as_input.yaml +0 -16
  361. type_spec/external/api/recipes/edit_recipe_inputs.yaml +0 -85
  362. type_spec/external/api/recipes/get_curve.yaml +0 -21
  363. type_spec/external/api/recipes/get_recipe_calculations.yaml +0 -39
  364. type_spec/external/api/recipes/get_recipe_links.yaml +0 -26
  365. type_spec/external/api/recipes/get_recipe_names.yaml +0 -29
  366. type_spec/external/api/recipes/get_recipe_output_metadata.yaml +0 -36
  367. type_spec/external/api/recipes/get_recipes_data.yaml +0 -244
  368. type_spec/external/api/recipes/set_recipe_inputs.yaml +0 -42
  369. type_spec/external/api/recipes/set_recipe_metadata.yaml +0 -20
  370. type_spec/external/api/recipes/set_recipe_outputs.yaml +0 -52
  371. type_spec/external/api/recipes/set_recipe_tags.yaml +0 -62
  372. type_spec/external/api/triggers/run_trigger.yaml +0 -18
  373. uncountable/integration/types.py +0 -89
@@ -0,0 +1,84 @@
1
+ import dataclasses
2
+ import typing
3
+
4
+ from .annotation import SerialBase, SerialInspector, get_serial_annotation
5
+
6
+ T = typing.TypeVar("T")
7
+
8
+
9
+ class IdentityHashWrapper(typing.Generic[T]):
10
+ """This allows unhashable types to be used in the SerialUnion, like dict.
11
+ Since we have only one copy of the types themselves, we rely on
12
+ object identity for the hashing."""
13
+
14
+ def __init__(self, inner: T) -> None:
15
+ self.inner = inner
16
+
17
+ def __hash__(self) -> int:
18
+ return id(self.inner)
19
+
20
+
21
+ @dataclasses.dataclass(kw_only=True, frozen=True, eq=True)
22
+ class _SerialUnion(SerialBase):
23
+ """
24
+ This class is to be kept private, to provide flexibility in registration/lookup.
25
+ Places that need the data should access it via help classes/methods.
26
+ """
27
+
28
+ # If specified, indicates the Union has a discriminator which should be used to
29
+ # determine which type to parse.
30
+ discriminator: str | None = None
31
+ discriminator_map: IdentityHashWrapper[dict[str, type]] | None = None
32
+
33
+
34
+ def serial_union_annotation(
35
+ *,
36
+ discriminator: str | None = None,
37
+ discriminator_map: dict[str, type] | None = None,
38
+ named_type_path: str | None = None,
39
+ is_dynamic_allowed: bool = False,
40
+ ) -> _SerialUnion:
41
+ return _SerialUnion(
42
+ discriminator=discriminator,
43
+ discriminator_map=IdentityHashWrapper(discriminator_map)
44
+ if discriminator_map is not None
45
+ else None,
46
+ named_type_path=named_type_path,
47
+ from_decorator=True,
48
+ is_dynamic_allowed=is_dynamic_allowed,
49
+ )
50
+
51
+
52
+ def _get_serial_union(parsed_type: type[T]) -> _SerialUnion | None:
53
+ serial = get_serial_annotation(parsed_type)
54
+ if not isinstance(serial, _SerialUnion):
55
+ return None
56
+ return serial
57
+
58
+
59
+ class SerialClassInspector(SerialInspector[T]):
60
+ def __init__(self, parsed_type: type[T], serial_union: _SerialUnion) -> None:
61
+ super().__init__(parsed_type, serial_union)
62
+ self._parsed_type = parsed_type
63
+ self._serial_union = serial_union
64
+
65
+ def get_union_underlying(self) -> type[T]:
66
+ return typing.get_args(self._parsed_type)[0] # type:ignore[no-any-return]
67
+
68
+ @property
69
+ def discriminator(self) -> str | None:
70
+ return self._serial_union.discriminator
71
+
72
+ @property
73
+ def discriminator_map(self) -> dict[str, type] | None:
74
+ if self._serial_union.discriminator_map is None:
75
+ return None
76
+ return self._serial_union.discriminator_map.inner
77
+
78
+
79
+ def get_serial_union_data(parsed_type: type[T]) -> SerialClassInspector[T] | None:
80
+ serial = _get_serial_union(parsed_type)
81
+ if serial is None:
82
+ return None
83
+
84
+ return SerialClassInspector(parsed_type, serial)
@@ -0,0 +1,57 @@
1
+ from decimal import Decimal
2
+ from typing import TYPE_CHECKING, Any
3
+
4
+ import yaml
5
+
6
+ if TYPE_CHECKING:
7
+ from _typeshed import SupportsRead as SupportsReadT
8
+ from _typeshed import SupportsWrite as SupportsWriteT
9
+
10
+ SupportsRead = SupportsReadT[Any]
11
+ SupportsWrite = SupportsWriteT[Any]
12
+ else:
13
+ SupportsRead = object
14
+ SupportsWrite = object
15
+
16
+
17
+ def _decimal_constructor(loader, node): # type:ignore
18
+ value = loader.construct_scalar(node)
19
+ return Decimal(value)
20
+
21
+
22
+ # A semi-acceptable patch to force a number to be parsed as a decimal, since pyyaml
23
+ # parses them as lossy floats otherwise. Though a bit ugly, at least this way we have
24
+ # support for decimal constants
25
+ yaml.SafeLoader.add_constructor("!decimal", _decimal_constructor)
26
+
27
+
28
+ class YAMLError(BaseException):
29
+ pass
30
+
31
+
32
+ def dumps(obj: Any, sort_keys: bool = False) -> str:
33
+ return yaml.dump(obj, sort_keys=sort_keys)
34
+
35
+
36
+ def dump(obj: Any, f: SupportsWrite, sort_keys: bool = False) -> None:
37
+ yaml.dump(obj, f, sort_keys=sort_keys)
38
+
39
+
40
+ def safe_load(src: str | bytes | SupportsRead) -> Any:
41
+ try:
42
+ return yaml.safe_load(src)
43
+ except yaml.YAMLError as e:
44
+ raise YAMLError() from e
45
+
46
+
47
+ def safe_dump(
48
+ obj: Any,
49
+ sort_keys: bool = False,
50
+ indent: int | None = None,
51
+ width: int | None = None,
52
+ ) -> str:
53
+ return yaml.safe_dump(obj, sort_keys=sort_keys, indent=indent, width=width)
54
+
55
+
56
+ def c_load(f: SupportsRead) -> Any:
57
+ return yaml.load(f, Loader=yaml.CLoader)
@@ -1,8 +1,8 @@
1
+ from .convert_to_snakecase import convert_dict_to_snake_case
2
+ from .dataclasses import dict_fields as dict_fields
3
+ from .dataclasses import iterate_fields as iterate_fields
1
4
  from .serialization_helpers import (
2
- convert_dict_to_snake_case,
3
- convert_to_camelcase,
4
- resolve_missing_to_none,
5
- serialize,
5
+ JsonValue,
6
6
  serialize_for_api,
7
7
  serialize_for_storage,
8
8
  serialize_for_storage_dict,
@@ -10,10 +10,10 @@ from .serialization_helpers import (
10
10
 
11
11
  __all__: list[str] = [
12
12
  "convert_dict_to_snake_case",
13
- "convert_to_camelcase",
14
- "resolve_missing_to_none",
15
- "serialize",
16
13
  "serialize_for_api",
17
14
  "serialize_for_storage",
18
15
  "serialize_for_storage_dict",
16
+ "iterate_fields",
17
+ "dict_fields",
18
+ "JsonValue",
19
19
  ]
@@ -0,0 +1,27 @@
1
+ from typing import (
2
+ Any,
3
+ )
4
+
5
+ from pkgs.argument_parser import camel_to_snake_case
6
+ from pkgs.serialization import (
7
+ MISSING_SENTRY,
8
+ OpaqueKey,
9
+ )
10
+
11
+
12
+ def _key_convert_to_snake_case(o: Any) -> Any:
13
+ if isinstance(o, OpaqueKey):
14
+ return o
15
+ if isinstance(o, str):
16
+ return camel_to_snake_case(o)
17
+ return o
18
+
19
+
20
+ def convert_dict_to_snake_case(data: Any) -> Any:
21
+ return {
22
+ _key_convert_to_snake_case(k): convert_dict_to_snake_case(v)
23
+ if isinstance(v, dict)
24
+ else v
25
+ for k, v in data.items()
26
+ if v != MISSING_SENTRY
27
+ }
@@ -0,0 +1,14 @@
1
+ from dataclasses import fields
2
+ from typing import TYPE_CHECKING, Any, Iterator
3
+
4
+ if TYPE_CHECKING:
5
+ from _typeshed import DataclassInstance
6
+
7
+
8
+ def iterate_fields(d: "DataclassInstance") -> Iterator[tuple[str, Any]]:
9
+ for field in fields(d):
10
+ yield field.name, getattr(d, field.name)
11
+
12
+
13
+ def dict_fields(d: "DataclassInstance") -> dict[str, Any]:
14
+ return dict(iterate_fields(d))
@@ -1,25 +1,31 @@
1
+ # warnings -- types here assume that keys to dictionaries are strings
2
+ # this is true most of the time, but there are cases where we have integer indexes
3
+
4
+ import dataclasses
1
5
  import datetime
2
6
  import enum
7
+ import functools
3
8
  from collections.abc import Callable, Mapping, Sequence
4
9
  from decimal import Decimal
5
10
  from typing import (
6
11
  TYPE_CHECKING,
7
12
  Any,
8
- Optional,
13
+ ClassVar,
14
+ Protocol,
9
15
  TypeVar,
10
16
  Union,
17
+ overload,
11
18
  )
12
19
 
13
- from pkgs.argument_parser import camel_to_snake_case, snake_to_camel_case
20
+ from pkgs.argument_parser import snake_to_camel_case
14
21
  from pkgs.serialization import (
15
22
  MISSING_SENTRY,
16
- MissingSentryType,
17
- MissingType,
18
23
  OpaqueKey,
19
24
  get_serial_class_data,
20
25
  )
21
26
 
22
27
  from ._get_type_for_serialization import SerializationType, get_serialization_type
28
+ from .dataclasses import iterate_fields
23
29
 
24
30
  # Inlined types which otherwise would import from types/base.py
25
31
  JsonScalar = Union[str, float, bool, Decimal, None, datetime.datetime, datetime.date]
@@ -28,53 +34,103 @@ if TYPE_CHECKING:
28
34
  else:
29
35
  JsonValue = Union[JsonScalar, dict[str, Any], list[Any]]
30
36
 
37
+ T = TypeVar("T")
38
+
39
+
40
+ class Dataclass(Protocol):
41
+ __dataclass_fields__: ClassVar[dict] # type: ignore[type-arg,unused-ignore]
42
+
43
+
44
+ def identity(x: T) -> T:
45
+ return x
31
46
 
32
- def key_convert_to_camelcase(o: Any) -> Any:
47
+
48
+ @overload
49
+ def key_convert_to_camelcase(o: str) -> str: ...
50
+
51
+
52
+ @overload
53
+ def key_convert_to_camelcase(o: int) -> int: ...
54
+
55
+
56
+ def key_convert_to_camelcase(o: Any) -> str | int:
33
57
  if isinstance(o, OpaqueKey):
34
58
  return o
35
- if isinstance(o, enum.Enum):
59
+ if isinstance(o, enum.StrEnum):
36
60
  return o.value
37
61
  if isinstance(o, str):
38
62
  return snake_to_camel_case(o)
39
- return o
63
+ if isinstance(o, int):
64
+ # we allow dictionaries to use integer keys
65
+ return o
66
+ raise ValueError("Unexpected key type", o)
40
67
 
41
68
 
42
- def _convert_dict(d: Any) -> Any:
69
+ def _convert_dict(d: dict[str, Any]) -> dict[str, JsonValue]:
43
70
  return {
44
- key_convert_to_camelcase(k): convert_to_camelcase(v)
71
+ key_convert_to_camelcase(k): serialize_for_api(v)
45
72
  for k, v in d.items()
46
73
  if v != MISSING_SENTRY
47
74
  }
48
75
 
49
76
 
50
- def _serialize_dict(d: Any) -> dict[str, Any]:
51
- return {k: serialize(v) for k, v in d.items() if v != MISSING_SENTRY}
77
+ def _serialize_dict(d: dict[str, Any]) -> dict[str, JsonValue]:
78
+ return {k: serialize_for_storage(v) for k, v in d.items() if v != MISSING_SENTRY}
52
79
 
53
80
 
54
- def _convert_dataclass(d: Any) -> Any:
55
- dct = type(d)
56
- scd = get_serial_class_data(dct)
81
+ def _serialize_dataclass(d: Any) -> dict[str, JsonValue]:
82
+ return {
83
+ k: serialize_for_storage(v) for k, v in iterate_fields(d) if v != MISSING_SENTRY
84
+ }
85
+
86
+
87
+ def _to_string_value(value: Any) -> str:
88
+ assert isinstance(value, (Decimal, int)), (
89
+ f"Expecting decimal or int, received: {value} (type={type(value)})"
90
+ )
91
+ return str(value)
92
+
93
+
94
+ @dataclasses.dataclass(kw_only=True)
95
+ class DataclassConversions:
96
+ key_conversions: dict[str, str]
97
+ value_conversion_functions: dict[str, Callable[[Any], JsonValue]]
57
98
 
58
- def key_convert(key: Any) -> Any:
99
+
100
+ @functools.lru_cache(maxsize=10000)
101
+ def _get_dataclass_conversion_lookups(dataclass_type: Any) -> DataclassConversions:
102
+ scd = get_serial_class_data(dataclass_type)
103
+
104
+ key_conversions: dict[str, str] = {}
105
+ value_conversion_functions: dict[str, Callable[[Any], JsonValue]] = {}
106
+
107
+ for field in dataclasses.fields(dataclass_type):
108
+ key = field.name
59
109
  if scd.has_unconverted_key(key):
60
- return key
61
- return key_convert_to_camelcase(key)
110
+ key_conversions[key] = key
111
+ else:
112
+ key_conversions[key] = key_convert_to_camelcase(key)
62
113
 
63
- def value_convert(key: Any, value: Any) -> Any:
64
- if value is None:
65
- return None
66
114
  if scd.has_to_string_value(key):
67
- # Limit to types we know we need to support to avoid surprises
68
- # Generics, like List/Dict would need to be per-value stringified
69
- assert isinstance(value, (Decimal, int))
70
- return str(value)
71
- if scd.has_unconverted_value(key):
72
- return value
73
- return convert_to_camelcase(value)
115
+ value_conversion_functions[key] = _to_string_value
116
+ elif scd.has_unconverted_value(key):
117
+ value_conversion_functions[key] = serialize_for_storage
118
+ else:
119
+ value_conversion_functions[key] = serialize_for_api
74
120
 
121
+ return DataclassConversions(
122
+ key_conversions=key_conversions,
123
+ value_conversion_functions=value_conversion_functions,
124
+ )
125
+
126
+
127
+ def _convert_dataclass(d: Any) -> dict[str, JsonValue]:
128
+ conversions = _get_dataclass_conversion_lookups(type(d)) # type: ignore[arg-type]
75
129
  return {
76
- key_convert(k): value_convert(k, v)
77
- for k, v in d.__dict__.items()
130
+ conversions.key_conversions[k]: (
131
+ conversions.value_conversion_functions[k](v) if v is not None else None
132
+ )
133
+ for k, v in iterate_fields(d)
78
134
  if v != MISSING_SENTRY
79
135
  }
80
136
 
@@ -83,38 +139,54 @@ _SERIALIZATION_FUNCS_STANDARD = {
83
139
  SerializationType.ENUM: lambda x: str(x.value),
84
140
  SerializationType.DATE: lambda x: x.isoformat(),
85
141
  SerializationType.TIMEDELTA: lambda x: x.total_seconds(),
86
- SerializationType.UNKNOWN: lambda x: x,
142
+ SerializationType.UNKNOWN: identity,
87
143
  }
88
144
 
89
- _CONVERSION_SERIALIZATION_FUNCS = {
145
+ _CONVERSION_SERIALIZATION_FUNCS: dict[SerializationType, Callable[[Any], JsonValue]] = {
90
146
  **_SERIALIZATION_FUNCS_STANDARD,
91
147
  SerializationType.NAMED_TUPLE: lambda x: _convert_dict(x._asdict()),
92
- SerializationType.ITERABLE: lambda x: [convert_to_camelcase(v) for v in x],
148
+ SerializationType.ITERABLE: lambda x: [serialize_for_api(v) for v in x],
93
149
  SerializationType.DICT: _convert_dict,
94
150
  SerializationType.DATACLASS: _convert_dataclass,
95
151
  }
96
152
 
97
153
 
98
- def convert_to_camelcase(obj: Any) -> Any:
99
- """@DEPRECATED prefer serialize_for_api"""
100
- return serialize_for_api(obj)
154
+ @overload
155
+ def serialize_for_api(obj: None) -> None: ...
156
+
101
157
 
158
+ @overload
159
+ def serialize_for_api(obj: dict[str, Any]) -> dict[str, JsonValue]: ...
102
160
 
103
- def serialize_for_api(obj: Any) -> Any:
161
+
162
+ @overload
163
+ def serialize_for_api(obj: Dataclass) -> dict[str, JsonValue]: ...
164
+
165
+
166
+ @overload
167
+ def serialize_for_api(obj: Any) -> JsonValue: ...
168
+
169
+
170
+ def serialize_for_api(obj: Any) -> JsonValue:
104
171
  """
105
172
  Serialize to a parsed-JSON format suitably encoded for API output.
106
173
 
107
174
  Use the CachedParser.parse_api to parse this data.
108
175
  """
109
176
  serialization_type = get_serialization_type(type(obj)) # type: ignore
110
- return _CONVERSION_SERIALIZATION_FUNCS[serialization_type](obj)
177
+ if (
178
+ serialization_type == SerializationType.UNKNOWN
179
+ ): # performance optimization to not do function lookup
180
+ return obj # type: ignore
181
+ r = _CONVERSION_SERIALIZATION_FUNCS[serialization_type](obj)
182
+ return r
111
183
 
112
184
 
113
185
  _SERIALIZATION_FUNCS_DICT: dict[
114
186
  SerializationType, Callable[[Any], dict[str, JsonValue]]
115
187
  ] = {
116
188
  SerializationType.DICT: _serialize_dict,
117
- SerializationType.DATACLASS: lambda x: _serialize_dict(x.__dict__),
189
+ SerializationType.DATACLASS: _serialize_dataclass,
118
190
  }
119
191
 
120
192
 
@@ -122,15 +194,10 @@ _SERIALIZATION_FUNCS: dict[SerializationType, Callable[[Any], JsonValue]] = {
122
194
  **_SERIALIZATION_FUNCS_STANDARD,
123
195
  **_SERIALIZATION_FUNCS_DICT,
124
196
  SerializationType.NAMED_TUPLE: lambda x: _serialize_dict(x._asdict()),
125
- SerializationType.ITERABLE: lambda x: [serialize(v) for v in x],
197
+ SerializationType.ITERABLE: lambda x: [serialize_for_storage(v) for v in x],
126
198
  }
127
199
 
128
200
 
129
- def serialize(obj: Any) -> Any:
130
- """@DEPRECATED: prefer serialize_for_storage"""
131
- return serialize_for_storage(obj)
132
-
133
-
134
201
  def serialize_for_storage(obj: Any) -> JsonValue:
135
202
  """
136
203
  Convert a value into the pseudo-JSON form for
@@ -139,37 +206,16 @@ def serialize_for_storage(obj: Any) -> JsonValue:
139
206
  Use the CachedParser.parse_storage to parse this data.
140
207
  """
141
208
  serialization_type = get_serialization_type(type(obj)) # type: ignore
209
+ if (
210
+ serialization_type == SerializationType.UNKNOWN
211
+ ): # performance optimization to not do function lookup
212
+ return obj # type: ignore
142
213
  return _SERIALIZATION_FUNCS[serialization_type](obj)
143
214
 
144
215
 
145
- def serialize_for_storage_dict(obj: Any) -> dict[str, JsonValue]:
216
+ def serialize_for_storage_dict(obj: dict | Dataclass) -> dict[str, JsonValue]: # type: ignore[type-arg]
146
217
  """
147
218
  Same as serialize for storage but guarantees outer object is a dictionary
148
219
  """
149
- serialization_type = get_serialization_type(type(obj)) # type: ignore
220
+ serialization_type = get_serialization_type(type(obj))
150
221
  return _SERIALIZATION_FUNCS_DICT[serialization_type](obj)
151
-
152
-
153
- def key_convert_to_snake_case(o: Any) -> Any:
154
- if isinstance(o, OpaqueKey):
155
- return o
156
- if isinstance(o, str):
157
- return camel_to_snake_case(o)
158
- return o
159
-
160
-
161
- def convert_dict_to_snake_case(data: Any) -> Any:
162
- return {
163
- key_convert_to_snake_case(k): convert_dict_to_snake_case(v)
164
- if isinstance(v, dict)
165
- else v
166
- for k, v in data.items()
167
- if v != MISSING_SENTRY
168
- }
169
-
170
-
171
- T = TypeVar("T")
172
-
173
-
174
- def resolve_missing_to_none(val: MissingType[T]) -> Optional[T]:
175
- return val if not isinstance(val, MissingSentryType) else None
@@ -6,7 +6,6 @@ import os
6
6
  import sys
7
7
  from collections import defaultdict
8
8
  from dataclasses import dataclass
9
- from typing import TypeVar
10
9
 
11
10
  from main.base.types import actions_registry_t
12
11
  from pkgs.type_spec import builder
@@ -22,9 +21,6 @@ key_short_description = "short_description"
22
21
  key_description = "description"
23
22
 
24
23
 
25
- TypeT = TypeVar("TypeT")
26
-
27
-
28
24
  class InvalidSpecException(Exception):
29
25
  pass
30
26
 
@@ -64,7 +64,7 @@ def _emit_imports(
64
64
  sorted([f"type {_action_namespace_name(item)}" for item in list(namespaces)])
65
65
  )
66
66
  out.write(
67
- f'import {{ ActionsRegistryT, {namespaces_to_import} }} from "unc_mat/types"\n\n'
67
+ f'import {{ ActionsRegistryT, {namespaces_to_import} }} from "unc_types"\n\n'
68
68
  )
69
69
  out.write(MODIFY_NOTICE)
70
70
  return out.getvalue()
@@ -111,9 +111,7 @@ def _get_argument_type(arguments: str | None) -> str | None:
111
111
  argument_parts = arguments.split(".")
112
112
  namespace = argument_parts[0]
113
113
  type_name = argument_parts[1]
114
- if namespace is not None and type_name is not None:
115
- return f"{_action_namespace_name(namespace)}.{type_name}"
116
- return None
114
+ return f"{_action_namespace_name(namespace)}.{type_name}"
117
115
 
118
116
 
119
117
  def _emit_action_definition(
@@ -129,7 +127,9 @@ def _emit_action_definition(
129
127
  out.write(f"{indent}{_action_symbol_name(action_definition)}: {{\n")
130
128
  out.write(f"{sub_indent}name: {encode_common_string(action_definition.name)},\n")
131
129
  if action_definition.icon is not None:
132
- out.write(f"{sub_indent}icon: {encode_common_string(action_definition.icon)},\n")
130
+ out.write(
131
+ f"{sub_indent}icon: {encode_common_string(action_definition.icon)},\n"
132
+ )
133
133
  out.write(
134
134
  f"{sub_indent}shortDescription: {encode_common_string(action_definition.short_description)},\n"
135
135
  )