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
docs/conf.py CHANGED
@@ -6,11 +6,15 @@
6
6
  # -- Project information -----------------------------------------------------
7
7
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8
8
 
9
- from datetime import date
10
- import uncountable
9
+ import datetime
10
+
11
+ from docutils import nodes # type: ignore[import-untyped]
12
+ from sphinx.addnodes import pending_xref # type: ignore[import-not-found]
13
+ from sphinx.application import Sphinx # type: ignore[import-not-found]
14
+ from sphinx.environment import BuildEnvironment # type: ignore[import-not-found]
11
15
 
12
16
  project = "Uncountable SDK"
13
- copyright = f"{date.today().year}, Uncountable Inc"
17
+ copyright = f"{datetime.datetime.now(tz=datetime.UTC).date().year}, Uncountable Inc"
14
18
  author = "Uncountable Inc"
15
19
 
16
20
  # -- General configuration ---------------------------------------------------
@@ -23,16 +27,23 @@ extensions = [
23
27
  "sphinx_copybutton",
24
28
  "sphinx_favicon",
25
29
  ]
26
- myst_enable_extensions = ["fieldlist", "deflist"]
30
+ myst_enable_extensions = ["fieldlist", "deflist", "colon_fence"]
27
31
 
28
32
  autoapi_dirs = ["../uncountable"]
29
- autoapi_options = [ 'members', 'undoc-members', 'show-inheritance', 'show-module-summary', 'imported-members' ]
30
- autoapi_ignore = ['*integration*']
31
- autodoc_typehints = 'description'
33
+ autoapi_options = [
34
+ "members",
35
+ "inherited-members",
36
+ "undoc-members",
37
+ ]
38
+ autoapi_root = "api"
39
+ autoapi_ignore = ["*integration*"]
40
+ autodoc_typehints = "description"
41
+ autoapi_member_order = "groupwise"
42
+ autoapi_own_page_level = "class"
32
43
 
33
44
  exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
34
45
 
35
-
46
+ python_use_unqualified_type_names = True
36
47
  # -- Options for HTML output -------------------------------------------------
37
48
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
38
49
 
@@ -50,3 +61,44 @@ favicons = [
50
61
  "favicons/mstile-150x150.png",
51
62
  "favicons/safari-pinned-tab.svg",
52
63
  ]
64
+
65
+
66
+ def _hook_missing_reference(
67
+ _app: Sphinx, _env: BuildEnvironment, node: pending_xref, contnode: nodes.Text
68
+ ) -> nodes.reference | None:
69
+ """
70
+ Manually resolve reference when autoapi reference resolution fails.
71
+ This is necessary because autoapi does not fully support type aliases.
72
+ """
73
+ # example reftarget value: uncountable.types.identifier_t.IdentifierKey
74
+ target = node.get("reftarget", "")
75
+
76
+ # example refdoc value: api/uncountable/types/generic_upload_t/GenericUploadStrategy
77
+ current_doc = node.get("refdoc", "")
78
+
79
+ if not target.startswith("uncountable"):
80
+ return None
81
+
82
+ target_module, target_name = target.rsplit(".", 1)
83
+
84
+ # construct relative path from current doc page to target page
85
+ relative_segments_to_root = [".." for _ in current_doc.split("/")]
86
+ relative_segments_to_target = target_module.split(".")
87
+
88
+ # example full relative path: ../../../../../api/uncountable/types/identifier_t/#uncountable.types.identifier_t.IdentifierKey
89
+ full_relative_path = "/".join([
90
+ *relative_segments_to_root,
91
+ autoapi_root,
92
+ *relative_segments_to_target,
93
+ f"#{target}",
94
+ ])
95
+
96
+ return nodes.reference(
97
+ text=target_name if python_use_unqualified_type_names else target,
98
+ children=[contnode],
99
+ refuri=full_relative_path,
100
+ )
101
+
102
+
103
+ def setup(app: Sphinx) -> None:
104
+ app.connect("missing-reference", _hook_missing_reference)
docs/index.md CHANGED
@@ -3,11 +3,114 @@
3
3
  ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/UncountablePythonSDK)
4
4
 
5
5
 
6
- `Uncountable Python SDK` is a python package that allows interacting with the uncountable platform
6
+ The Uncountable Python SDK is a python package that provides a wrapper around the Uncountable REST API.
7
7
 
8
- ```{toctree}
9
- :maxdepth: 2
8
+ Using this SDK provides the following advantages:
9
+
10
+ - In-editor parameter/type safety
11
+ - Automatic parsing of response data.
12
+ - Reduced code boilerplate
13
+ - Helper methods
14
+
15
+ ## Getting Started
16
+ The first step in any integration is to create a [Client](uncountable.core.client.Client) object. The client provides access to all available SDK methods, and includes built-in request authentication & error propagation.
17
+
18
+ ### Creating a Client
19
+ Create a client using one of the supported authentication mechanisms. API credentials can be generated by a member of Uncountable staff.
20
+
21
+ ::::{tab-set}
22
+ :::{tab-item} Basic Auth
23
+ ```{literalinclude} ../examples/basic_auth.py
24
+ ```
25
+ :::
26
+
27
+ :::{tab-item} OAuth
28
+ ```{literalinclude} ../examples/oauth.py
29
+ ```
30
+ :::
31
+ ::::
32
+
33
+ The provided code examples assume that a Client has been created and stored in the `client` variable
34
+
35
+
36
+ ### Basic Usage
37
+
38
+ :::{dropdown} List Ingredient Names & IDs
39
+ ```{code-block} python
40
+ from uncountable.types import entity_t, id_source_t
41
+
42
+ client.list_id_source(
43
+ spec=id_source_t.IdSourceSpecEntity(entity_type=entity_t.EntityType.INGREDIENT),
44
+ search_label="",
45
+ )
46
+ ```
47
+ Example Response:
48
+ ```code
49
+ Data(
50
+ results=[
51
+ IdName(id=1, name='Filler'),
52
+ IdName(id=2, name='Calcium Oxide 2'),
53
+ IdName(id=3, name='Carbon Black'),
54
+ ]
55
+ )
56
+ ```
57
+ :::
58
+
59
+ :::{dropdown} Create an Experiment
60
+ ```{code-block} python
61
+ client.create_recipe(material_family_id=1, workflow_id=1, name="Example Recipe")
62
+ ```
63
+ Example Response:
64
+ ```code
65
+ Data(result_id=52271)
66
+ ```
67
+ :::
68
+
69
+ :::{dropdown} Upload a file
70
+ ```{code-block} python
71
+ from uncountable.core.file_upload import MediaFileUpload
72
+
73
+ client.upload_files(file_uploads=[MediaFileUpload(path="/path/to/local/example_file.pdf")])
74
+ ```
75
+ Example Response:
76
+ ```code
77
+ [
78
+ UploadedFile(name='example_file.pdf', file_id=718)
79
+ ]
80
+ ```
81
+ :::
82
+
83
+ [More examples](integration_examples/index)
84
+
85
+ ## Errors
86
+ Client methods will raise Exceptions when the API returns codes in the `3xx`, `4xx` or `5xx` ranges. Ensure all method calls are wrapped in Exception handling logic.
87
+
88
+ ## Pagination
89
+ Many of the Uncountable APIs require pagination to fetch more than 100 results at once. The following code snippet implements pagination to fetch the Names & IDs of all Projects:
90
+ :::{dropdown} Pagination Example
91
+ ```{code-block} python
92
+ from uncountable.types import entity_t, id_source_t
93
+ from uncountable.types.api.id_source.list_id_source import IdName
10
94
 
11
- quickstart
95
+ def fetch_all_projects(client: Client) -> list[IdName]:
96
+ projects: list[IdName] = []
97
+ while True:
98
+ response = client.list_id_source(
99
+ spec=IdSourceSpecEntity(entity_type=entity_t.EntityType.PROJECT),
100
+ search_label="",
101
+ offset=len(projects),
102
+ )
103
+ projects.extend(response.results)
104
+ if len(response.results) < 100:
105
+ return projects
12
106
  ```
107
+ :::
13
108
 
109
+
110
+ ```{toctree}
111
+ :hidden:
112
+ Overview <self>
113
+ Available SDK Methods <api/uncountable/core/client/Client>
114
+ integration_examples/index
115
+ SDK Reference <api/uncountable/index>
116
+ ```
@@ -0,0 +1,43 @@
1
+ # Create an Ingredient
2
+
3
+ Use the `create_or_update_entity` method to create Ingredients.
4
+
5
+ The following fields are required when creating an Ingredient:
6
+ - `name`: The name of the Ingredient
7
+ - `core_ingredient_ingredientMaterialFamilies`: The list of material families in which to include the Ingredient
8
+
9
+ The reference name of the default definition of Ingredients is `uncIngredient`
10
+
11
+ This is an example of a minimal ingredient creation call
12
+
13
+ ```{code-block} python
14
+ from uncountable.types import entity_t, field_values_t, identifier_t
15
+
16
+ client.create_or_update_entity(
17
+ entity_type=entity_t.EntityType.INGREDIENT,
18
+ definition_key=identifier_t.IdentifierKeyRefName(ref_name="uncIngredient"),
19
+ field_values=[
20
+ field_values_t.FieldArgumentValue(
21
+ field_key=identifier_t.IdentifierKeyRefName(
22
+ ref_name="core_ingredient_ingredientMaterialFamilies"
23
+ ),
24
+ value=field_values_t.FieldValueIds(
25
+ entity_type=entity_t.EntityType.MATERIAL_FAMILY,
26
+ identifier_keys=[identifier_t.IdentifierKeyId(id=1)],
27
+ ),
28
+ ),
29
+ field_values_t.FieldArgumentValue(
30
+ field_key=identifier_t.IdentifierKeyRefName(ref_name="name"),
31
+ value=field_values_t.FieldValueText(value="Example Ingredient"),
32
+ ),
33
+ ],
34
+ )
35
+ ```
36
+
37
+ Example Response:
38
+ ```{code}
39
+ Data(modification_made=True, result_id=3124, entity=None, result_values=None)
40
+ ```
41
+
42
+ Optional fields:
43
+ - `core_ingredient_quantityType`: The quantity type of the ingredient (default is `numeric`)
@@ -0,0 +1,56 @@
1
+ # Create an Output
2
+
3
+ Use the `create_or_update_entity` method to create Outputs.
4
+
5
+ The following fields are required when creating an Output:
6
+ - `name`: The name of the Output
7
+ - `core_output_unitsId`: The unit the output is measured in
8
+ - `core_output_outputMaterialFamilies`: The list of material families in which to include the Output
9
+ - `core_output_quantityType`: The quantity type of the output
10
+
11
+ The reference name of the default definition of Ingredients is `unc_output_definition`
12
+
13
+ This is an example of a minimal output creation call
14
+
15
+ ```{code-block} python
16
+ from uncountable.types import entity_t, field_values_t, identifier_t
17
+
18
+ client.create_or_update_entity(
19
+ entity_type=entity_t.EntityType.OUTPUT,
20
+ definition_key=identifier_t.IdentifierKeyRefName(ref_name="unc_output_definition"),
21
+ field_values=[
22
+ field_values_t.FieldArgumentValue(
23
+ field_key=identifier_t.IdentifierKeyRefName(ref_name="name"),
24
+ value=field_values_t.FieldValueText(value="Example Output"),
25
+ ),
26
+ field_values_t.FieldArgumentValue(
27
+ field_key=identifier_t.IdentifierKeyRefName(ref_name="core_output_unitsId"),
28
+ value=field_values_t.FieldValueId(
29
+ entity_type=entity_t.EntityType.UNITS,
30
+ identifier_key=identifier_t.IdentifierKeyId(id=1),
31
+ ),
32
+ ),
33
+ field_values_t.FieldArgumentValue(
34
+ field_key=identifier_t.IdentifierKeyRefName(
35
+ ref_name="core_output_outputMaterialFamilies"
36
+ ),
37
+ value=field_values_t.FieldValueIds(
38
+ entity_type=entity_t.EntityType.MATERIAL_FAMILY,
39
+ identifier_keys=[identifier_t.IdentifierKeyId(id=1)],
40
+ ),
41
+ ),
42
+ field_values_t.FieldArgumentValue(
43
+ field_key=identifier_t.IdentifierKeyRefName(
44
+ ref_name="core_output_quantityType"
45
+ ),
46
+ value=field_values_t.FieldValueFieldOption(value="numeric"),
47
+ ),
48
+ ],
49
+ )
50
+ ```
51
+
52
+ Example Response:
53
+
54
+ ```{code}
55
+ Data(modification_made=True, result_id=653, entity=None, result_values=None)
56
+ ```
@@ -0,0 +1,6 @@
1
+ # Integration Examples
2
+
3
+ ```{toctree}
4
+ create_ingredient
5
+ create_output
6
+ ```
docs/justfile CHANGED
@@ -1,5 +1,6 @@
1
1
  docs-setup-python:
2
- pip install -r requirements.txt
2
+ curl -LsSf https://astral.sh/uv/0.8.4/install.sh | sh
3
+ uv pip install -r requirements.txt
3
4
 
4
5
  docs-clean:
5
6
  rm -rf _build/
@@ -9,4 +10,3 @@ docs-build: docs-clean
9
10
 
10
11
  docs-serve: docs-build
11
12
  npx serve _build/dirhtml
12
-
docs/requirements.txt CHANGED
@@ -1,7 +1,9 @@
1
- furo==2024.1.29
2
- myst-parser==2.0.0
3
- sphinx-autoapi==3.0.0
1
+ furo==2025.9.25
2
+ myst-parser==4.0.1
3
+ sphinx-autoapi==3.6.0
4
4
  sphinx-copybutton==0.5.2
5
- Sphinx==7.2.6
6
- sphinx_design==0.5.0
5
+ Sphinx==8.2.0
6
+ sphinx_design==0.6.1
7
7
  sphinx-favicon==1.0.1
8
+ astroid==3.3.8
9
+ docutils==0.21.2
examples/async_batch.py CHANGED
@@ -1,6 +1,7 @@
1
+ import os
1
2
  from decimal import Decimal
2
- from uncountable.core import AuthDetailsApiKey, Client
3
- from uncountable.core import AsyncBatchProcessor
3
+
4
+ from uncountable.core import AsyncBatchProcessor, AuthDetailsApiKey, Client
4
5
  from uncountable.types import (
5
6
  recipe_metadata,
6
7
  )
@@ -10,12 +11,10 @@ from uncountable.types.recipe_identifiers import (
10
11
  RecipeIdentifiers,
11
12
  )
12
13
 
13
-
14
14
  client = Client(
15
- base_url="https://app.uncountable.com",
15
+ base_url=os.environ["UNC_BASE_URL"],
16
16
  auth_details=AuthDetailsApiKey(
17
- api_id="X",
18
- api_secret_key="X",
17
+ api_id=os.environ["UNC_API_ID"], api_secret_key=os.environ["UNC_API_SECRET_KEY"]
19
18
  ),
20
19
  )
21
20
  batch_loader = AsyncBatchProcessor(client=client)
examples/basic_auth.py ADDED
@@ -0,0 +1,7 @@
1
+ from uncountable.core.client import Client
2
+ from uncountable.core.types import AuthDetailsApiKey
3
+
4
+ client = Client(
5
+ base_url="https://app.uncountable.com",
6
+ auth_details=AuthDetailsApiKey(api_id="x", api_secret_key="x"),
7
+ )
examples/create_entity.py CHANGED
@@ -1,16 +1,14 @@
1
- from uncountable.core import AuthDetailsApiKey, Client
1
+ from uncountable.core import AuthDetailsOAuth, Client
2
+ from uncountable.core.client import ClientConfig
2
3
  from uncountable.types import (
3
4
  entity_t,
4
5
  field_values_t,
5
6
  )
6
7
 
7
-
8
8
  client = Client(
9
9
  base_url="https://app.uncountable.com",
10
- auth_details=AuthDetailsApiKey(
11
- api_id="X",
12
- api_secret_key="X",
13
- ),
10
+ auth_details=AuthDetailsOAuth(refresh_token="x"),
11
+ config=ClientConfig(allow_insecure_tls=False),
14
12
  )
15
13
  entities = client.create_entity(
16
14
  definition_id=24,
@@ -0,0 +1,34 @@
1
+ import os
2
+
3
+ import uncountable.types.api.inputs.create_inputs as create_inputs
4
+ from uncountable.core import AuthDetailsApiKey, Client
5
+ from uncountable.types import field_values_t, inputs_t
6
+
7
+ client = Client(
8
+ base_url="http://localhost:5000",
9
+ auth_details=AuthDetailsApiKey(
10
+ api_id=os.environ["UNC_API_ID"],
11
+ api_secret_key=os.environ["UNC_API_SECRET_KEY"],
12
+ ),
13
+ )
14
+
15
+ client.external_create_inputs(
16
+ inputs_to_create=[
17
+ create_inputs.InputToCreate(
18
+ name="sdk test ing",
19
+ material_family_ids=[1],
20
+ quantity_type=inputs_t.IngredientQuantityType.NUMERIC,
21
+ type=inputs_t.IngredientType.INGREDIENT,
22
+ field_values=[
23
+ field_values_t.FieldRefNameValue(
24
+ field_ref_name="carrieTestNumericField",
25
+ value="10",
26
+ ),
27
+ field_values_t.FieldRefNameValue(
28
+ field_ref_name="carrieTestCheckboxField",
29
+ value=True,
30
+ ),
31
+ ],
32
+ )
33
+ ]
34
+ )
@@ -0,0 +1,26 @@
1
+ import os
2
+ from pprint import pprint
3
+
4
+ from uncountable.core import AuthDetailsApiKey, Client
5
+ from uncountable.types import download_file_t, entity_t, identifier_t
6
+
7
+ client = Client(
8
+ base_url="http://localhost:5000",
9
+ auth_details=AuthDetailsApiKey(
10
+ api_id=os.environ["UNC_API_ID"],
11
+ api_secret_key=os.environ["UNC_API_SECRET_KEY"],
12
+ ),
13
+ )
14
+
15
+ file_query = download_file_t.FileDownloadQueryEntityField(
16
+ entity=entity_t.EntityIdentifier(
17
+ type=entity_t.EntityType.LAB_REQUEST,
18
+ identifier_key=identifier_t.IdentifierKeyId(id=2375),
19
+ ),
20
+ field_key=identifier_t.IdentifierKeyRefName(ref_name="attachments"),
21
+ )
22
+
23
+ downloaded = client.download_files(
24
+ file_query=file_query,
25
+ )
26
+ pprint(downloaded)
@@ -0,0 +1,50 @@
1
+ import os
2
+ from decimal import Decimal
3
+
4
+ from uncountable.core import AsyncBatchProcessor, AuthDetailsApiKey, Client
5
+ from uncountable.types import (
6
+ edit_recipe_inputs_t,
7
+ recipe_workflow_steps_t,
8
+ )
9
+ from uncountable.types.identifier import IdentifierKeyBatchReference, IdentifierKeyId
10
+ from uncountable.types.recipe_identifiers import (
11
+ RecipeIdentifiers,
12
+ )
13
+ from uncountable.types.recipe_inputs import QuantityBasis
14
+
15
+ client = Client(
16
+ base_url=os.environ["UNC_BASE_URL"],
17
+ auth_details=AuthDetailsApiKey(
18
+ api_id=os.environ["UNC_API_ID"], api_secret_key=os.environ["UNC_API_SECRET_KEY"]
19
+ ),
20
+ )
21
+ batch_loader = AsyncBatchProcessor(client=client)
22
+ recipe_identifiers: RecipeIdentifiers = []
23
+ req = batch_loader.create_recipe(
24
+ material_family_id=1, workflow_id=1, identifiers=recipe_identifiers
25
+ )
26
+ created_recipe_reference = req.batch_reference
27
+ edits: list[edit_recipe_inputs_t.RecipeInputEdit] = []
28
+ edits.append(
29
+ edit_recipe_inputs_t.RecipeInputEditAddInput(
30
+ quantity_basis=QuantityBasis.MASS,
31
+ ingredient_key=IdentifierKeyId(id=1),
32
+ value_numeric=Decimal("56.7"),
33
+ )
34
+ )
35
+ edits.append(
36
+ edit_recipe_inputs_t.RecipeInputEditChangeBasisViewed(
37
+ quantity_basis=QuantityBasis.VOLUME, ingredient_key=IdentifierKeyId(id=1)
38
+ )
39
+ )
40
+ edits.append(
41
+ edit_recipe_inputs_t.RecipeInputEditAddInstructions(
42
+ instructions="Mix for 3 minutes"
43
+ )
44
+ )
45
+ batch_loader.edit_recipe_inputs(
46
+ recipe_key=IdentifierKeyBatchReference(reference=created_recipe_reference),
47
+ edits=edits,
48
+ recipe_workflow_step_identifier=recipe_workflow_steps_t.RecipeWorkflowStepIdentifierDefault(),
49
+ )
50
+ job_id = batch_loader.send()
@@ -0,0 +1,11 @@
1
+ import time
2
+
3
+ from uncountable.integration.job import CronJob, JobArguments, register_job
4
+ from uncountable.types.job_definition_t import JobResult
5
+
6
+
7
+ @register_job
8
+ class MyConcurrentCronJob(CronJob):
9
+ def run(self, args: JobArguments) -> JobResult:
10
+ time.sleep(10)
11
+ return JobResult(success=True)
@@ -0,0 +1,21 @@
1
+ import time
2
+
3
+ from uncountable.integration.job import CronJob, JobArguments, register_job
4
+ from uncountable.types import entity_t
5
+ from uncountable.types.job_definition_t import JobResult
6
+
7
+
8
+ @register_job
9
+ class MyCronJob(CronJob):
10
+ def run(self, args: JobArguments) -> JobResult:
11
+ matfam = args.client.get_entities_data(
12
+ entity_ids=[1],
13
+ entity_type=entity_t.EntityType.MATERIAL_FAMILY,
14
+ ).entity_details[0]
15
+ name = None
16
+ for field_val in matfam.field_values:
17
+ if field_val.field_ref_name == "name":
18
+ name = field_val.value
19
+ args.logger.log_info(f"material family found with name: {name}")
20
+ time.sleep(1.5)
21
+ return JobResult(success=True)
@@ -0,0 +1,47 @@
1
+ from dataclasses import dataclass
2
+
3
+ from uncountable.integration.http_server import (
4
+ GenericHttpRequest,
5
+ GenericHttpResponse,
6
+ )
7
+ from uncountable.integration.http_server.types import HttpException
8
+ from uncountable.integration.job import CustomHttpJob, register_job
9
+ from uncountable.types import job_definition_t
10
+
11
+
12
+ @dataclass(kw_only=True)
13
+ class ExampleWebhookPayload:
14
+ id: int
15
+ message: str
16
+
17
+
18
+ _EXPECTED_USER_ID = 1
19
+
20
+
21
+ @register_job
22
+ class HttpExample(CustomHttpJob):
23
+ @staticmethod
24
+ def validate_request(
25
+ *,
26
+ request: GenericHttpRequest, # noqa: ARG004
27
+ job_definition: job_definition_t.HttpJobDefinitionBase, # noqa: ARG004
28
+ profile_meta: job_definition_t.ProfileMetadata, # noqa: ARG004
29
+ ) -> None:
30
+ if (
31
+ CustomHttpJob.get_validated_oauth_request_user_id(
32
+ request=request, profile_metadata=profile_meta
33
+ )
34
+ != _EXPECTED_USER_ID
35
+ ):
36
+ raise HttpException(
37
+ message="unauthorized; invalid oauth token", error_code=401
38
+ )
39
+
40
+ @staticmethod
41
+ def handle_request(
42
+ *,
43
+ request: GenericHttpRequest, # noqa: ARG004
44
+ job_definition: job_definition_t.HttpJobDefinitionBase, # noqa: ARG004
45
+ profile_meta: job_definition_t.ProfileMetadata, # noqa: ARG004
46
+ ) -> GenericHttpResponse:
47
+ return GenericHttpResponse(response="OK", status_code=200)