UncountablePythonSDK 0.0.21__tar.gz → 0.0.23__tar.gz

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 (223) hide show
  1. {uncountablepythonsdk-0.0.21/UncountablePythonSDK.egg-info → uncountablepythonsdk-0.0.23}/PKG-INFO +1 -1
  2. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23/UncountablePythonSDK.egg-info}/PKG-INFO +1 -1
  3. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/UncountablePythonSDK.egg-info/SOURCES.txt +2 -0
  4. uncountablepythonsdk-0.0.23/examples/async_batch.py +36 -0
  5. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/builder.py +113 -8
  6. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/emit_io_ts.py +4 -1
  7. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/emit_open_api.py +192 -18
  8. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/emit_open_api_util.py +17 -0
  9. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/emit_python.py +7 -1
  10. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/emit_typescript.py +3 -0
  11. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/load_types.py +48 -5
  12. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/open_api_util.py +13 -33
  13. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/entity/create_entities.yaml +1 -0
  14. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/entity/create_entity.yaml +1 -0
  15. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/edit_recipe_inputs.yaml +1 -4
  16. uncountablepythonsdk-0.0.23/uncountable/core/__init__.py +5 -0
  17. uncountablepythonsdk-0.0.23/uncountable/core/async_batch.py +22 -0
  18. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/core/client.py +78 -6
  19. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/entity/create_entities.py +1 -1
  20. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/entity/create_entity.py +1 -1
  21. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/edit_recipe_inputs.py +2 -3
  22. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/async_batch.py +1 -0
  23. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/async_batch_processor.py +7 -7
  24. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/client_base.py +27 -2
  25. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/identifier.py +3 -3
  26. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/recipe_workflow_steps.py +1 -1
  27. uncountablepythonsdk-0.0.21/uncountable/core/__init__.py +0 -4
  28. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/.github/workflows/documentation.yml +0 -0
  29. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/.github/workflows/publish.yml +0 -0
  30. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/.gitignore +0 -0
  31. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/README.md +0 -0
  32. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/UncountablePythonSDK.egg-info/dependency_links.txt +0 -0
  33. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/UncountablePythonSDK.egg-info/requires.txt +0 -0
  34. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/UncountablePythonSDK.egg-info/top_level.txt +0 -0
  35. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/.gitignore +0 -0
  36. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/conf.py +0 -0
  37. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/index.md +0 -0
  38. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/justfile +0 -0
  39. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/quickstart.md +0 -0
  40. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/requirements.txt +0 -0
  41. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/static/favicons/android-chrome-192x192.png +0 -0
  42. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/static/favicons/android-chrome-512x512.png +0 -0
  43. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/static/favicons/apple-touch-icon.png +0 -0
  44. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/static/favicons/browserconfig.xml +0 -0
  45. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/static/favicons/favicon-16x16.png +0 -0
  46. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/static/favicons/favicon-32x32.png +0 -0
  47. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/static/favicons/manifest.json +0 -0
  48. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/static/favicons/mstile-150x150.png +0 -0
  49. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/static/favicons/safari-pinned-tab.svg +0 -0
  50. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/docs/static/logo_blue.png +0 -0
  51. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/examples/create_entity.py +0 -0
  52. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/examples/recipe-import/importer.py +0 -0
  53. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/examples/upload_files.py +0 -0
  54. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/__init__.py +0 -0
  55. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/argument_parser/__init__.py +0 -0
  56. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/argument_parser/_is_enum.py +0 -0
  57. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/argument_parser/_is_namedtuple.py +0 -0
  58. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/argument_parser/argument_parser.py +0 -0
  59. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/argument_parser/case_convert.py +0 -0
  60. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/serialization/__init__.py +0 -0
  61. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/serialization/missing_sentry.py +0 -0
  62. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/serialization/opaque_key.py +0 -0
  63. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/serialization/serial_class.py +0 -0
  64. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/serialization_util/__init__.py +0 -0
  65. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/serialization_util/_get_type_for_serialization.py +0 -0
  66. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/serialization_util/serialization_helpers.py +0 -0
  67. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/strenum_compat/__init__.py +0 -0
  68. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/strenum_compat/strenum_compat.py +0 -0
  69. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/__init__.py +0 -0
  70. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/__main__.py +0 -0
  71. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/actions_registry/__init__.py +0 -0
  72. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/actions_registry/__main__.py +0 -0
  73. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/actions_registry/emit_typescript.py +0 -0
  74. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/config.py +0 -0
  75. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/emit_typescript_util.py +0 -0
  76. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/parts/base.py.prepart +0 -0
  77. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/parts/base.ts.prepart +0 -0
  78. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/test.py +0 -0
  79. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/type_info/__main__.py +0 -0
  80. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/type_info/emit_type_info.py +0 -0
  81. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/util.py +0 -0
  82. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/value_spec/__init__.py +0 -0
  83. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/value_spec/__main__.py +0 -0
  84. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/value_spec/convert_type.py +0 -0
  85. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/value_spec/emit_python.py +0 -0
  86. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pkgs/type_spec/value_spec/types.py +0 -0
  87. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/pyproject.toml +0 -0
  88. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/setup.cfg +0 -0
  89. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/batch/execute_batch.yaml +0 -0
  90. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/batch/execute_batch_load_async.yaml +0 -0
  91. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/chemical/convert_chemical_formats.yaml +0 -0
  92. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/entity/get_entities_data.yaml +0 -0
  93. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/entity/list_entities.yaml +0 -0
  94. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/entity/resolve_entity_ids.yaml +0 -0
  95. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/entity/set_values.yaml +0 -0
  96. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/entity/transition_entity_phase.yaml +0 -0
  97. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/id_source/list_id_source.yaml +0 -0
  98. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/id_source/match_id_source.yaml +0 -0
  99. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/input_groups/get_input_group_names.yaml +0 -0
  100. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/inputs/create_inputs.yaml +0 -0
  101. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/inputs/get_input_data.yaml +0 -0
  102. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/inputs/get_input_names.yaml +0 -0
  103. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/inputs/get_inputs_data.yaml +0 -0
  104. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/inputs/set_input_attribute_values.yaml +0 -0
  105. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/outputs/get_output_data.yaml +0 -0
  106. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/outputs/get_output_names.yaml +0 -0
  107. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/outputs/resolve_output_conditions.yaml +0 -0
  108. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/permissions/set_core_permissions.yaml +0 -0
  109. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/project/get_projects.yaml +0 -0
  110. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/project/get_projects_data.yaml +0 -0
  111. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipe_links/create_recipe_link.yaml +0 -0
  112. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipe_metadata/get_recipe_metadata_data.yaml +0 -0
  113. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/associate_recipe_as_input.yaml +0 -0
  114. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/associate_recipe_as_lot.yaml +0 -0
  115. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/create_recipe.yaml +0 -0
  116. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/create_recipes.yaml +0 -0
  117. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/disassociate_recipe_as_input.yaml +0 -0
  118. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/get_curve.yaml +0 -0
  119. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/get_recipe_calculations.yaml +0 -0
  120. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/get_recipe_links.yaml +0 -0
  121. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/get_recipe_names.yaml +0 -0
  122. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/get_recipe_output_metadata.yaml +0 -0
  123. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/get_recipes_data.yaml +0 -0
  124. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/set_recipe_inputs.yaml +0 -0
  125. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/set_recipe_metadata.yaml +0 -0
  126. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/set_recipe_outputs.yaml +0 -0
  127. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/recipes/set_recipe_tags.yaml +0 -0
  128. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/type_spec/external/api/triggers/run_trigger.yaml +0 -0
  129. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/__init__.py +0 -0
  130. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/core/file_upload.py +0 -0
  131. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/core/types.py +0 -0
  132. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/integration/__init__.py +0 -0
  133. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/integration/construct_client.py +0 -0
  134. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/integration/cron.py +0 -0
  135. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/integration/db/__init__.py +0 -0
  136. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/integration/db/connect.py +0 -0
  137. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/integration/entrypoint.py +0 -0
  138. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/integration/executors/__init__.py +0 -0
  139. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/integration/executors/script_executor.py +0 -0
  140. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/integration/job.py +0 -0
  141. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/integration/server.py +0 -0
  142. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/integration/types.py +0 -0
  143. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/py.typed +0 -0
  144. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/__init__.py +0 -0
  145. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/__init__.py +0 -0
  146. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/batch/__init__.py +0 -0
  147. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/batch/execute_batch.py +0 -0
  148. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/batch/execute_batch_load_async.py +0 -0
  149. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/chemical/__init__.py +0 -0
  150. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/chemical/convert_chemical_formats.py +0 -0
  151. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/entity/__init__.py +0 -0
  152. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/entity/get_entities_data.py +0 -0
  153. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/entity/list_entities.py +0 -0
  154. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/entity/resolve_entity_ids.py +0 -0
  155. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/entity/set_values.py +0 -0
  156. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/entity/transition_entity_phase.py +0 -0
  157. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/id_source/__init__.py +0 -0
  158. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/id_source/list_id_source.py +0 -0
  159. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/id_source/match_id_source.py +0 -0
  160. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/input_groups/__init__.py +0 -0
  161. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/input_groups/get_input_group_names.py +0 -0
  162. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/inputs/__init__.py +0 -0
  163. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/inputs/create_inputs.py +0 -0
  164. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/inputs/get_input_data.py +0 -0
  165. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/inputs/get_input_names.py +0 -0
  166. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/inputs/get_inputs_data.py +0 -0
  167. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/inputs/set_input_attribute_values.py +0 -0
  168. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/outputs/__init__.py +0 -0
  169. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/outputs/get_output_data.py +0 -0
  170. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/outputs/get_output_names.py +0 -0
  171. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/outputs/resolve_output_conditions.py +0 -0
  172. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/permissions/__init__.py +0 -0
  173. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/permissions/set_core_permissions.py +0 -0
  174. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/project/__init__.py +0 -0
  175. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/project/get_projects.py +0 -0
  176. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/project/get_projects_data.py +0 -0
  177. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipe_links/__init__.py +0 -0
  178. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipe_links/create_recipe_link.py +0 -0
  179. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipe_metadata/__init__.py +0 -0
  180. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py +0 -0
  181. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/__init__.py +0 -0
  182. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/associate_recipe_as_input.py +0 -0
  183. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/associate_recipe_as_lot.py +0 -0
  184. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/create_recipe.py +0 -0
  185. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/create_recipes.py +0 -0
  186. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/disassociate_recipe_as_input.py +0 -0
  187. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/get_curve.py +0 -0
  188. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/get_recipe_calculations.py +0 -0
  189. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/get_recipe_links.py +0 -0
  190. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/get_recipe_names.py +0 -0
  191. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/get_recipe_output_metadata.py +0 -0
  192. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/get_recipes_data.py +0 -0
  193. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/set_recipe_inputs.py +0 -0
  194. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/set_recipe_metadata.py +0 -0
  195. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/set_recipe_outputs.py +0 -0
  196. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/recipes/set_recipe_tags.py +0 -0
  197. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/triggers/__init__.py +0 -0
  198. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/api/triggers/run_trigger.py +0 -0
  199. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/base.py +0 -0
  200. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/calculations.py +0 -0
  201. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/chemical_structure.py +0 -0
  202. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/curves.py +0 -0
  203. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/entity.py +0 -0
  204. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/experiment_groups.py +0 -0
  205. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/field_values.py +0 -0
  206. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/fields.py +0 -0
  207. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/id_source.py +0 -0
  208. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/input_attributes.py +0 -0
  209. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/inputs.py +0 -0
  210. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/outputs.py +0 -0
  211. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/permissions.py +0 -0
  212. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/phases.py +0 -0
  213. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/post_base.py +0 -0
  214. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/recipe_identifiers.py +0 -0
  215. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/recipe_inputs.py +0 -0
  216. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/recipe_links.py +0 -0
  217. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/recipe_metadata.py +0 -0
  218. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/recipe_output_metadata.py +0 -0
  219. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/recipe_tags.py +0 -0
  220. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/response.py +0 -0
  221. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/units.py +0 -0
  222. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/users.py +0 -0
  223. {uncountablepythonsdk-0.0.21 → uncountablepythonsdk-0.0.23}/uncountable/types/workflows.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: UncountablePythonSDK
3
- Version: 0.0.21
3
+ Version: 0.0.23
4
4
  Summary: Uncountable SDK
5
5
  Project-URL: Homepage, https://github.com/uncountableinc/uncountable-python-sdk
6
6
  Project-URL: Repository, https://github.com/uncountableinc/uncountable-python-sdk.git
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: UncountablePythonSDK
3
- Version: 0.0.21
3
+ Version: 0.0.23
4
4
  Summary: Uncountable SDK
5
5
  Project-URL: Homepage, https://github.com/uncountableinc/uncountable-python-sdk
6
6
  Project-URL: Repository, https://github.com/uncountableinc/uncountable-python-sdk.git
@@ -24,6 +24,7 @@ docs/static/favicons/favicon-32x32.png
24
24
  docs/static/favicons/manifest.json
25
25
  docs/static/favicons/mstile-150x150.png
26
26
  docs/static/favicons/safari-pinned-tab.svg
27
+ examples/async_batch.py
27
28
  examples/create_entity.py
28
29
  examples/upload_files.py
29
30
  examples/recipe-import/importer.py
@@ -114,6 +115,7 @@ type_spec/external/api/triggers/run_trigger.yaml
114
115
  uncountable/__init__.py
115
116
  uncountable/py.typed
116
117
  uncountable/core/__init__.py
118
+ uncountable/core/async_batch.py
117
119
  uncountable/core/client.py
118
120
  uncountable/core/file_upload.py
119
121
  uncountable/core/types.py
@@ -0,0 +1,36 @@
1
+ from decimal import Decimal
2
+ from uncountable.core import AuthDetailsApiKey, Client
3
+ from uncountable.core import AsyncBatchProcessor
4
+ from uncountable.types import (
5
+ recipe_metadata,
6
+ )
7
+ from uncountable.types.identifier import IdentifierKeyBatchReference
8
+ from uncountable.types.recipe_identifiers import (
9
+ RecipeIdentifierEditableName,
10
+ RecipeIdentifiers,
11
+ )
12
+
13
+
14
+ client = Client(
15
+ base_url="https://app.uncountable.com",
16
+ auth_details=AuthDetailsApiKey(
17
+ api_id="X",
18
+ api_secret_key="X",
19
+ ),
20
+ )
21
+ batch_loader = AsyncBatchProcessor(client=client)
22
+ recipe_identifiers: RecipeIdentifiers = []
23
+ recipe_identifiers.append(
24
+ RecipeIdentifierEditableName(editable_name="My recipe from API")
25
+ )
26
+ req = batch_loader.create_recipe(
27
+ material_family_id=1, workflow_id=1, identifiers=recipe_identifiers
28
+ )
29
+ created_recipe_reference = req.batch_reference
30
+ batch_loader.set_recipe_metadata(
31
+ recipe_key=IdentifierKeyBatchReference(reference=created_recipe_reference),
32
+ recipe_metadata=[
33
+ recipe_metadata.MetadataValue(metadata_id=7, value_numeric=Decimal(38))
34
+ ],
35
+ )
36
+ job_id = batch_loader.send()
@@ -10,7 +10,7 @@ import re
10
10
  from collections import defaultdict
11
11
  from dataclasses import MISSING, dataclass
12
12
  from enum import Enum, StrEnum, auto
13
- from typing import Any, Optional
13
+ from typing import Any, Optional, Self
14
14
 
15
15
  from . import util
16
16
  from .util import parse_type_str, unused
@@ -86,6 +86,7 @@ class BaseTypeName(StrEnum):
86
86
  s_optional = "Optional"
87
87
  s_string = "String"
88
88
  s_tuple = "Tuple"
89
+ s_readonly_array = "ReadonlyArray"
89
90
  s_union = "Union"
90
91
 
91
92
  # For a root class that defines properties
@@ -184,6 +185,34 @@ class SpecTypeInstance(SpecType):
184
185
  return defn_type + self.parameters
185
186
 
186
187
 
188
+ @dataclass(kw_only=True)
189
+ class SpecEndpointExample:
190
+ summary: str
191
+ description: str
192
+ arguments: dict[str, object]
193
+ data: dict[str, object]
194
+
195
+
196
+ @dataclass(kw_only=True)
197
+ class SpecGuide:
198
+ title: str
199
+ markdown_content: str
200
+ html_content: str
201
+
202
+
203
+ @dataclass(kw_only=True, frozen=True)
204
+ class RootGuideKey:
205
+ pass
206
+
207
+
208
+ @dataclass(kw_only=True, frozen=True)
209
+ class EndpointGuideKey:
210
+ path: str
211
+
212
+
213
+ SpecGuideKey = RootGuideKey | EndpointGuideKey
214
+
215
+
187
216
  class SpecTypeLiteralWrapper(SpecType):
188
217
  def __init__(
189
218
  self,
@@ -672,6 +701,32 @@ class ResultType(StrEnum):
672
701
  RE_ENDPOINT_ROOT = re.compile(r"\${([_a-z]+)}")
673
702
 
674
703
 
704
+ @dataclass(kw_only=True, frozen=True)
705
+ class _EndpointPathDetails:
706
+ root: str
707
+ root_path: str
708
+ resolved_path: str
709
+
710
+
711
+ def _resolve_endpoint_path(
712
+ path: str, api_endpoints: dict[str, str]
713
+ ) -> _EndpointPathDetails:
714
+ root_path_source = path.split("/")[0]
715
+ root_match = RE_ENDPOINT_ROOT.fullmatch(root_path_source)
716
+ if root_match is None:
717
+ raise Exception(f"invalid-api-path-root:{root_path_source}")
718
+
719
+ root_var = root_match.group(1)
720
+ root_path = api_endpoints[root_var]
721
+
722
+ _, *rest_path = path.split("/", 1)
723
+ resolved_path = "/".join([root_path] + rest_path)
724
+
725
+ return _EndpointPathDetails(
726
+ root=root_var, root_path=root_path, resolved_path=resolved_path
727
+ )
728
+
729
+
675
730
  class SpecEndpoint:
676
731
  method: RouteMethod
677
732
  root: str
@@ -748,14 +803,10 @@ class SpecEndpoint:
748
803
 
749
804
  self.result_type = ResultType(data.get("result_type", ResultType.json.value))
750
805
 
806
+ path_details = _resolve_endpoint_path(data["path"], builder.api_endpoints)
807
+ self.root = path_details.root
808
+ self.path_root = path_details.root_path
751
809
  self.desc = data.get("desc")
752
-
753
- root_match = RE_ENDPOINT_ROOT.fullmatch(path[0])
754
- if root_match is None:
755
- raise Exception(f"invalid-api-path-root:{path[0]}")
756
-
757
- self.root = root_match.group(1)
758
- self.path_root = builder.api_endpoints[self.root]
759
810
  # IMPROVE: remove need for is_external flag
760
811
  self.is_external = self.path_root == "api/external"
761
812
  self.has_attachment = data.get("has_attachment", False)
@@ -764,6 +815,10 @@ class SpecEndpoint:
764
815
  not is_sdk or self.desc is not None
765
816
  ), f"Endpoint description required for SDK endpoints, missing: {path}"
766
817
 
818
+ @property
819
+ def resolved_path(self: Self) -> str:
820
+ return f"{self.path_root}/{self.path_dirname}/{self.path_basename}"
821
+
767
822
 
768
823
  def _parse_const(
769
824
  builder: SpecBuilder,
@@ -1014,6 +1069,8 @@ class SpecBuilder:
1014
1069
  self.pending: list[NamespaceDataPair] = []
1015
1070
  self.parts: dict[str, dict[str, str]] = defaultdict(dict)
1016
1071
  self.preparts: dict[str, dict[str, str]] = defaultdict(dict)
1072
+ self.examples: dict[str, list[SpecEndpointExample]] = defaultdict(list)
1073
+ self.guides: dict[SpecGuideKey, list[SpecGuide]] = defaultdict(list)
1017
1074
  self.api_endpoints = api_endpoints
1018
1075
  base_namespace = SpecNamespace(name=base_namespace_name)
1019
1076
  for base_type in BaseTypeName:
@@ -1198,5 +1255,53 @@ class SpecBuilder:
1198
1255
  def add_prepart_file(self, target: str, name: str, data: str) -> None:
1199
1256
  self.preparts[target][name] = data
1200
1257
 
1258
+ def add_example_file(self, data: dict[str, object]) -> None:
1259
+ path_details = _resolve_endpoint_path(str(data["path"]), self.api_endpoints)
1260
+
1261
+ examples_data = data["examples"]
1262
+ if not isinstance(examples_data, list):
1263
+ raise Exception(
1264
+ f"'examples' in example files are expected to be a list, endpoint_path={path_details.resolved_path}"
1265
+ )
1266
+ for example in examples_data:
1267
+ arguments = example["arguments"]
1268
+ data_example = example["data"]
1269
+ if not isinstance(arguments, dict) or not isinstance(data_example, dict):
1270
+ raise Exception(
1271
+ f"'arguments' and 'data' fields must be dictionaries for each endpoint example, endpoint={path_details.resolved_path}"
1272
+ )
1273
+ self.examples[path_details.resolved_path].append(
1274
+ SpecEndpointExample(
1275
+ summary=str(example["summary"]),
1276
+ description=str(example["description"]),
1277
+ arguments=arguments,
1278
+ data=data_example,
1279
+ )
1280
+ )
1281
+
1282
+ def add_guide_file(self, file_content: str) -> None:
1283
+ import markdown
1284
+
1285
+ md = markdown.Markdown(extensions=["meta"])
1286
+ html = md.convert(file_content)
1287
+ meta: dict[str, list[str]] = md.Meta # type: ignore[attr-defined]
1288
+ title_meta: list[str] | None = meta.get("title")
1289
+ if title_meta is None:
1290
+ raise Exception("guides requier a title in the meta section")
1291
+
1292
+ path_meta: list[str] | None = meta.get("path")
1293
+ guide_key: SpecGuideKey = RootGuideKey()
1294
+ if path_meta is not None:
1295
+ path_details = _resolve_endpoint_path("".join(path_meta), self.api_endpoints)
1296
+ guide_key = EndpointGuideKey(path=path_details.resolved_path)
1297
+
1298
+ self.guides[guide_key].append(
1299
+ SpecGuide(
1300
+ title="".join(title_meta),
1301
+ html_content=html,
1302
+ markdown_content=file_content,
1303
+ )
1304
+ )
1305
+
1201
1306
  def resolve_proper_name(self, stype: SpecTypeDefn) -> str:
1202
1307
  return f"{'.'.join(stype.namespace.path)}.{stype.name}"
@@ -118,7 +118,10 @@ def refer_to_io_ts(
118
118
  stype: builder.SpecType,
119
119
  ) -> str:
120
120
  if isinstance(stype, builder.SpecTypeInstance):
121
- if stype.defn_type.name == builder.BaseTypeName.s_list:
121
+ if (
122
+ stype.defn_type.name == builder.BaseTypeName.s_list
123
+ or stype.defn_type.name == builder.BaseTypeName.s_readonly_array
124
+ ):
122
125
  spec = refer_to_io_ts(ctx, stype.parameters[0])
123
126
  return f"IO.array({spec})"
124
127
  if stype.defn_type.name == builder.BaseTypeName.s_union:
@@ -5,18 +5,22 @@ WORK-IN-PROGRESS, DON'T USE!
5
5
  """
6
6
 
7
7
  import dataclasses
8
+ import json
8
9
  import re
9
- from typing import cast
10
+ from typing import Collection, cast
10
11
 
11
12
  import yaml
12
13
 
13
14
  from . import builder, util
15
+ from .builder import EndpointGuideKey, RootGuideKey
14
16
  from .config import OpenAPIConfig
15
17
  from .emit_open_api_util import (
16
18
  MODIFY_NOTICE,
17
19
  EmitOpenAPIContext,
18
20
  EmitOpenAPIEndpoint,
21
+ EmitOpenAPIEndpointExample,
19
22
  EmitOpenAPIGlobalContext,
23
+ EmitOpenAPIGuide,
20
24
  EmitOpenAPIPath,
21
25
  EmitOpenAPIServer,
22
26
  EmitOpenAPITag,
@@ -74,12 +78,23 @@ def _rewrite_with_notice(
74
78
  return util.rewrite_file(file_path, f"{notice}\n{modified_file_content}")
75
79
 
76
80
 
77
- def _open_api_info(config: OpenAPIConfig) -> GlobalContextInfo:
78
- description = config.description
81
+ def _write_guide_as_html(guide: EmitOpenAPIGuide) -> str:
82
+ return f"""
83
+ <details>
84
+ <summary>{guide.title}</summary>
85
+ {guide.html_content}
86
+ </details>"""
87
+
88
+
89
+ def _open_api_info(
90
+ config: OpenAPIConfig, guides: list[EmitOpenAPIGuide]
91
+ ) -> GlobalContextInfo:
92
+ full_guides = "<br/>".join([_write_guide_as_html(guide) for guide in guides])
93
+ full_description = f"{config.description}<br/>{full_guides}"
79
94
  info: GlobalContextInfo = dict()
80
95
  info["version"] = "1.0.0"
81
96
  info["title"] = "Uncountable API Documentation"
82
- info["description"] = description
97
+ info["description"] = full_description
83
98
  info["x-logo"] = {"url": "../static/images/logo_blue.png", "altText": "Logo"}
84
99
  return info
85
100
 
@@ -90,9 +105,14 @@ def _open_api_servers(config: OpenAPIConfig) -> list[EmitOpenAPIServer]:
90
105
 
91
106
 
92
107
  def emit_open_api(builder: builder.SpecBuilder, *, config: OpenAPIConfig) -> None:
108
+ root_guides = builder.guides.get(RootGuideKey(), [])
109
+ openapi_guides = [
110
+ EmitOpenAPIGuide(title=guide.title, html_content=guide.html_content)
111
+ for guide in root_guides
112
+ ]
93
113
  gctx = EmitOpenAPIGlobalContext(
94
114
  version="3.0.0",
95
- info=_open_api_info(config),
115
+ info=_open_api_info(config, openapi_guides),
96
116
  servers=_open_api_servers(config),
97
117
  )
98
118
 
@@ -112,6 +132,8 @@ def emit_open_api(builder: builder.SpecBuilder, *, config: OpenAPIConfig) -> Non
112
132
  ctx,
113
133
  namespace=namespace,
114
134
  config=config,
135
+ examples=builder.examples,
136
+ guides=builder.guides,
115
137
  )
116
138
 
117
139
  _rewrite_with_notice(
@@ -144,52 +166,183 @@ def _serialize_global_context(ctx: EmitOpenAPIGlobalContext) -> str:
144
166
  return yaml.dump(oa_root, sort_keys=False)
145
167
 
146
168
 
147
- def _emit_endpoint_parameters(typ: OpenAPIType | None) -> dict[str, list[dict[str, str]]]:
169
+ def _is_empty_object_type(typ: OpenAPIType) -> bool:
148
170
  if not isinstance(typ, OpenAPIObjectType):
171
+ return False
172
+ return len(typ.properties) == 0
173
+
174
+
175
+ _QUERY_PARM_METHODS = ("get", "head", "options")
176
+ _REQUEST_BODY_METHODS = ("put", "post", "patch", "delete")
177
+
178
+ ApiSchema = dict[str, "ApiSchema"] | Collection["ApiSchema"] | str | bool
179
+ DictApiSchema = dict[str, ApiSchema]
180
+
181
+
182
+ def _emit_endpoint_argument_examples(
183
+ examples: list[EmitOpenAPIEndpointExample],
184
+ ) -> DictApiSchema:
185
+ if len(examples) == 0:
186
+ return {}
187
+
188
+ response_examples = {}
189
+ for example in examples:
190
+ response_examples[example.ref_name] = {
191
+ "summary": example.summary,
192
+ "description": example.description,
193
+ "value": example.arguments,
194
+ }
195
+ return {"examples": response_examples}
196
+
197
+
198
+ def _emit_endpoint_parameter_examples(
199
+ examples: list[EmitOpenAPIEndpointExample],
200
+ ) -> DictApiSchema:
201
+ if len(examples) == 0:
202
+ return {}
203
+
204
+ paramater_examples = []
205
+ comment_new_line = "\n// "
206
+ new_line = "\n"
207
+ for example in examples:
208
+ javascript_description = (
209
+ f"// {comment_new_line.join(example.description.split(new_line))}"
210
+ )
211
+ javascript_json_payload = f"{json.dumps(example.arguments, indent=2)}"
212
+ paramater_examples.append({
213
+ "lang": "JavaScript",
214
+ "label": f"Payload - {example.summary}",
215
+ "source": f"{javascript_description}\n{javascript_json_payload}",
216
+ })
217
+ return {"x-codeSamples": paramater_examples}
218
+
219
+
220
+ def _emit_endpoint_parameters(
221
+ endpoint: EmitOpenAPIEndpoint,
222
+ argument_type: OpenAPIType | None,
223
+ examples: list[EmitOpenAPIEndpointExample],
224
+ ) -> DictApiSchema:
225
+ if (
226
+ endpoint.method.lower() not in _QUERY_PARM_METHODS
227
+ or argument_type is None
228
+ or _is_empty_object_type(argument_type)
229
+ ):
149
230
  return {}
150
231
 
151
232
  return {
152
233
  "parameters": [
153
- {"$ref": f"#/components/schema/Arguments/{prop_name}"}
154
- for prop_name in typ.properties
234
+ {
235
+ "name": "data",
236
+ "required": True,
237
+ "in": "query",
238
+ "content": {
239
+ "application/json": {
240
+ "schema": {"$ref": "#/components/schema/Arguments"}
241
+ }
242
+ },
243
+ }
155
244
  ]
156
- }
245
+ } | _emit_endpoint_parameter_examples(examples)
157
246
 
158
247
 
159
- def _emit_is_beta(is_beta: bool) -> dict[str, bool]:
248
+ def _emit_is_beta(is_beta: bool) -> DictApiSchema:
160
249
  if is_beta:
161
250
  return {"x-beta": True}
162
251
  return {}
163
252
 
164
253
 
254
+ def _emit_endpoint_request_body(
255
+ endpoint: EmitOpenAPIEndpoint,
256
+ arguments_type: OpenAPIType | None,
257
+ examples: list[EmitOpenAPIEndpointExample],
258
+ ) -> DictApiSchema:
259
+ if (
260
+ endpoint.method.lower() not in _REQUEST_BODY_METHODS
261
+ or arguments_type is None
262
+ or _is_empty_object_type(arguments_type)
263
+ ):
264
+ return {}
265
+
266
+ return {
267
+ "requestBody": {
268
+ "content": {
269
+ "application/json": {
270
+ "schema": {
271
+ "type": "object",
272
+ "title": "Body",
273
+ "required": ["data"],
274
+ "properties": {"data": {"$ref": "#/components/schema/Arguments"}},
275
+ }
276
+ }
277
+ | _emit_endpoint_argument_examples(examples)
278
+ },
279
+ }
280
+ }
281
+
282
+
283
+ def _emit_endpoint_response_examples(
284
+ examples: list[EmitOpenAPIEndpointExample],
285
+ ) -> dict[str, dict[str, object]]:
286
+ if len(examples) == 0:
287
+ return {}
288
+
289
+ response_examples: dict[str, object] = {}
290
+ for example in examples:
291
+ response_examples[example.ref_name] = {
292
+ "summary": example.summary,
293
+ "description": example.description,
294
+ "value": example.data,
295
+ }
296
+ return {"examples": response_examples}
297
+
298
+
299
+ def _emit_endpoint_description(
300
+ description: str, guides: list[EmitOpenAPIGuide]
301
+ ) -> dict[str, str]:
302
+ full_guides = "<br/>".join([_write_guide_as_html(guide) for guide in guides])
303
+ return {
304
+ "description": description
305
+ if len(guides) == 0
306
+ else f"{description}<br/>{full_guides}"
307
+ }
308
+
309
+
165
310
  def _emit_namespace(
166
311
  gctx: EmitOpenAPIGlobalContext,
167
312
  ctx: EmitOpenAPIContext,
168
313
  namespace: builder.SpecNamespace,
169
314
  *,
170
315
  config: OpenAPIConfig,
316
+ examples: dict[str, list[builder.SpecEndpointExample]],
317
+ guides: dict[builder.SpecGuideKey, list[builder.SpecGuide]],
171
318
  ) -> None:
172
319
  for stype in namespace.types.values():
173
320
  _emit_type(ctx, stype, config=config)
174
321
 
175
322
  if namespace.endpoint is not None:
176
- _emit_endpoint(gctx, ctx, namespace, namespace.endpoint)
323
+ endpoint_examples = examples.get(namespace.endpoint.resolved_path, [])
324
+ endpoint_guides = guides.get(
325
+ EndpointGuideKey(path=namespace.endpoint.resolved_path), []
326
+ )
327
+ _emit_endpoint(
328
+ gctx, ctx, namespace, namespace.endpoint, endpoint_examples, endpoint_guides
329
+ )
177
330
 
178
331
  oa_components: dict[str, object] = dict()
179
332
 
180
333
  if ctx.endpoint is not None:
181
334
  endpoint = ctx.endpoint
182
-
183
335
  argument_type = ctx.types.get("Arguments")
184
336
  oa_endpoint = dict()
185
337
  oa_endpoint[endpoint.method] = (
186
338
  {
187
339
  "tags": endpoint.tags,
188
340
  "summary": endpoint.summary,
189
- "description": endpoint.description,
190
341
  }
342
+ | _emit_endpoint_description(endpoint.description, ctx.endpoint.guides)
191
343
  | _emit_is_beta(endpoint.is_beta)
192
- | _emit_endpoint_parameters(argument_type)
344
+ | _emit_endpoint_parameters(endpoint, argument_type, ctx.endpoint.examples)
345
+ | _emit_endpoint_request_body(endpoint, argument_type, ctx.endpoint.examples)
193
346
  | {
194
347
  "responses": {
195
348
  "200": {
@@ -198,6 +351,7 @@ def _emit_namespace(
198
351
  "application/json": {
199
352
  "schema": {"$ref": "#/components/schema/Data"}
200
353
  }
354
+ | _emit_endpoint_response_examples(ctx.endpoint.examples)
201
355
  },
202
356
  }
203
357
  },
@@ -236,10 +390,7 @@ def _emit_namespace(
236
390
 
237
391
  oa_components["schema"] = cast(
238
392
  object,
239
- {
240
- name: (value.asdict() if name != "Arguments" else value.asarguments())
241
- for name, value in types.items()
242
- },
393
+ {name: value.asdict() for name, value in types.items()},
243
394
  )
244
395
 
245
396
  path = f"{config.types_output}/common/{'/'.join(namespace.path)}.yaml"
@@ -348,6 +499,8 @@ def _emit_endpoint(
348
499
  ctx: EmitOpenAPIContext,
349
500
  namespace: builder.SpecNamespace,
350
501
  endpoint: builder.SpecEndpoint,
502
+ endpoint_examples: list[builder.SpecEndpointExample],
503
+ endpoint_guides: list[builder.SpecGuide],
351
504
  ) -> None:
352
505
  assert namespace.endpoint is not None
353
506
  assert namespace.path[0] == "api"
@@ -397,6 +550,23 @@ def _emit_endpoint(
397
550
  summary=f"{'/'.join(namespace.path[path_cutoff:])}",
398
551
  description=description,
399
552
  is_beta=namespace.endpoint.is_beta,
553
+ examples=[
554
+ EmitOpenAPIEndpointExample(
555
+ ref_name=f"ex_{i}",
556
+ summary=example.summary,
557
+ description=example.description,
558
+ arguments=example.arguments,
559
+ data=example.data,
560
+ )
561
+ for i, example in enumerate(endpoint_examples)
562
+ ],
563
+ guides=[
564
+ EmitOpenAPIGuide(
565
+ title=guide.title,
566
+ html_content=guide.html_content,
567
+ )
568
+ for guide in endpoint_guides
569
+ ],
400
570
  )
401
571
 
402
572
 
@@ -464,6 +634,10 @@ def open_api_type(
464
634
  [open_api_type(ctx, p, config=config) for p in stype.parameters],
465
635
  description="TupleType",
466
636
  )
637
+ if stype.defn_type.name == builder.BaseTypeName.s_readonly_array:
638
+ return OpenAPIArrayType(
639
+ open_api_type(ctx, stype.parameters[0], config=config)
640
+ )
467
641
 
468
642
  # TODO: generics are not supported by OpenAPI
469
643
  # map to Free-Form Object and add description
@@ -43,6 +43,12 @@ class EmitOpenAPIServer:
43
43
  url: str
44
44
 
45
45
 
46
+ @dataclass(kw_only=True)
47
+ class EmitOpenAPIGuide:
48
+ title: str
49
+ html_content: str
50
+
51
+
46
52
  @dataclass
47
53
  class EmitOpenAPIGlobalContext:
48
54
  version: str
@@ -56,6 +62,15 @@ class EmitOpenAPIGlobalContext:
56
62
  paths: list[EmitOpenAPIPath] = field(default_factory=list)
57
63
 
58
64
 
65
+ @dataclass(kw_only=True)
66
+ class EmitOpenAPIEndpointExample:
67
+ ref_name: str
68
+ summary: str
69
+ description: str
70
+ arguments: dict[str, object]
71
+ data: dict[str, object]
72
+
73
+
59
74
  @dataclass
60
75
  class EmitOpenAPIEndpoint:
61
76
  method: str
@@ -63,6 +78,8 @@ class EmitOpenAPIEndpoint:
63
78
  summary: str
64
79
  description: str
65
80
  is_beta: bool
81
+ examples: list[EmitOpenAPIEndpointExample]
82
+ guides: list[EmitOpenAPIGuide]
66
83
 
67
84
 
68
85
  @dataclass
@@ -29,7 +29,7 @@ ASYNC_BATCH_REQUEST_STYPE = builder.SpecTypeDefnObject(
29
29
  namespace=ASYNC_BATCH_TYPE_NAMESPACE, name="AsyncBatchRequest"
30
30
  )
31
31
  QUEUED_BATCH_REQUEST_STYPE = builder.SpecTypeDefnObject(
32
- namespace=ASYNC_BATCH_TYPE_NAMESPACE, name="QueuedBatchRequest"
32
+ namespace=ASYNC_BATCH_TYPE_NAMESPACE, name="QueuedAsyncBatchRequest"
33
33
  )
34
34
 
35
35
 
@@ -819,6 +819,7 @@ base_name_map = {
819
819
  builder.BaseTypeName.s_opaque_key: "OpaqueKey",
820
820
  builder.BaseTypeName.s_string: "str",
821
821
  builder.BaseTypeName.s_tuple: "tuple",
822
+ builder.BaseTypeName.s_readonly_array: "tuple",
822
823
  builder.BaseTypeName.s_union: "typing.Union",
823
824
  builder.BaseTypeName.s_literal: "typing.Literal",
824
825
  }
@@ -827,6 +828,11 @@ base_name_map = {
827
828
  def refer_to(ctx: TrackingContext, stype: builder.SpecType) -> str:
828
829
  if isinstance(stype, builder.SpecTypeInstance):
829
830
  params = ", ".join([refer_to(ctx, p) for p in stype.parameters])
831
+
832
+ if stype.defn_type.is_base_type(builder.BaseTypeName.s_readonly_array):
833
+ assert len(stype.parameters) == 1, "Read Only Array takes one parameter"
834
+ params = f"{params}, ..."
835
+
830
836
  return f"{refer_to(ctx, stype.defn_type)}[{params}]"
831
837
 
832
838
  if isinstance(stype, builder.SpecTypeLiteralWrapper):
@@ -394,6 +394,9 @@ def refer_to_impl(
394
394
  if stype.defn_type.name == builder.BaseTypeName.s_list:
395
395
  spec, multi = refer_to_impl(ctx, stype.parameters[0])
396
396
  return f"({spec})[]" if multi else f"{spec}[]", False
397
+ if stype.defn_type.name == builder.BaseTypeName.s_readonly_array:
398
+ spec, multi = refer_to_impl(ctx, stype.parameters[0])
399
+ return f"readonly ({spec})[]" if multi else f"readonly {spec}[]", False
397
400
  if stype.defn_type.name == builder.BaseTypeName.s_union:
398
401
  return f'({" | ".join([refer_to(ctx, p) for p in stype.parameters])})', False
399
402
  if stype.defn_type.name == builder.BaseTypeName.s_literal: