UncountablePythonSDK 0.0.8__py3-none-any.whl → 0.0.92__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 (312) hide show
  1. UncountablePythonSDK-0.0.92.dist-info/METADATA +61 -0
  2. UncountablePythonSDK-0.0.92.dist-info/RECORD +301 -0
  3. {UncountablePythonSDK-0.0.8.dist-info → UncountablePythonSDK-0.0.92.dist-info}/WHEEL +1 -1
  4. {UncountablePythonSDK-0.0.8.dist-info → UncountablePythonSDK-0.0.92.dist-info}/top_level.txt +1 -1
  5. docs/.gitignore +1 -0
  6. docs/conf.py +57 -0
  7. docs/index.md +13 -0
  8. docs/justfile +12 -0
  9. docs/quickstart.md +19 -0
  10. docs/requirements.txt +7 -0
  11. docs/static/favicons/android-chrome-192x192.png +0 -0
  12. docs/static/favicons/android-chrome-512x512.png +0 -0
  13. docs/static/favicons/apple-touch-icon.png +0 -0
  14. docs/static/favicons/browserconfig.xml +9 -0
  15. docs/static/favicons/favicon-16x16.png +0 -0
  16. docs/static/favicons/favicon-32x32.png +0 -0
  17. docs/static/favicons/manifest.json +18 -0
  18. docs/static/favicons/mstile-150x150.png +0 -0
  19. docs/static/favicons/safari-pinned-tab.svg +32 -0
  20. docs/static/logo_blue.png +0 -0
  21. examples/async_batch.py +35 -0
  22. examples/create_entity.py +22 -17
  23. examples/download_files.py +26 -0
  24. examples/edit_recipe_inputs.py +50 -0
  25. examples/integration-server/jobs/materials_auto/example_cron.py +18 -0
  26. examples/integration-server/jobs/materials_auto/example_wh.py +15 -0
  27. examples/integration-server/jobs/materials_auto/profile.yaml +43 -0
  28. examples/integration-server/pyproject.toml +224 -0
  29. examples/invoke_uploader.py +26 -0
  30. examples/set_recipe_metadata_file.py +40 -0
  31. examples/set_recipe_output_file_sdk.py +26 -0
  32. examples/upload_files.py +18 -0
  33. pkgs/argument_parser/__init__.py +5 -0
  34. pkgs/argument_parser/_is_enum.py +1 -6
  35. pkgs/argument_parser/argument_parser.py +232 -76
  36. pkgs/argument_parser/case_convert.py +4 -3
  37. pkgs/filesystem_utils/__init__.py +20 -0
  38. pkgs/filesystem_utils/_blob_session.py +137 -0
  39. pkgs/filesystem_utils/_gdrive_session.py +309 -0
  40. pkgs/filesystem_utils/_local_session.py +69 -0
  41. pkgs/filesystem_utils/_s3_session.py +117 -0
  42. pkgs/filesystem_utils/_sftp_session.py +147 -0
  43. pkgs/filesystem_utils/file_type_utils.py +91 -0
  44. pkgs/filesystem_utils/filesystem_session.py +39 -0
  45. pkgs/py.typed +0 -0
  46. pkgs/serialization/__init__.py +8 -1
  47. pkgs/serialization/annotation.py +64 -0
  48. pkgs/serialization/missing_sentry.py +1 -1
  49. pkgs/serialization/opaque_key.py +1 -1
  50. pkgs/serialization/serial_alias.py +47 -0
  51. pkgs/serialization/serial_class.py +65 -50
  52. pkgs/serialization/serial_generic.py +16 -0
  53. pkgs/serialization/serial_union.py +84 -0
  54. pkgs/serialization/yaml.py +57 -0
  55. pkgs/serialization_util/__init__.py +7 -7
  56. pkgs/serialization_util/_get_type_for_serialization.py +1 -3
  57. pkgs/serialization_util/convert_to_snakecase.py +27 -0
  58. pkgs/serialization_util/dataclasses.py +14 -0
  59. pkgs/serialization_util/serialization_helpers.py +116 -74
  60. pkgs/strenum_compat/strenum_compat.py +1 -9
  61. pkgs/type_spec/actions_registry/__init__.py +0 -0
  62. pkgs/type_spec/actions_registry/__main__.py +126 -0
  63. pkgs/type_spec/actions_registry/emit_typescript.py +182 -0
  64. pkgs/type_spec/builder.py +475 -89
  65. pkgs/type_spec/config.py +24 -19
  66. pkgs/type_spec/emit_io_ts.py +5 -2
  67. pkgs/type_spec/emit_open_api.py +266 -32
  68. pkgs/type_spec/emit_open_api_util.py +32 -13
  69. pkgs/type_spec/emit_python.py +599 -151
  70. pkgs/type_spec/emit_typescript.py +74 -273
  71. pkgs/type_spec/emit_typescript_util.py +239 -5
  72. pkgs/type_spec/load_types.py +55 -10
  73. pkgs/type_spec/open_api_util.py +30 -41
  74. pkgs/type_spec/parts/base.py.prepart +4 -3
  75. pkgs/type_spec/type_info/emit_type_info.py +178 -16
  76. pkgs/type_spec/util.py +11 -11
  77. pkgs/type_spec/value_spec/__main__.py +3 -3
  78. pkgs/type_spec/value_spec/convert_type.py +8 -1
  79. pkgs/type_spec/value_spec/emit_python.py +13 -4
  80. uncountable/__init__.py +1 -2
  81. uncountable/core/__init__.py +12 -2
  82. uncountable/core/async_batch.py +37 -0
  83. uncountable/core/client.py +293 -43
  84. uncountable/core/environment.py +41 -0
  85. uncountable/core/file_upload.py +135 -0
  86. uncountable/core/types.py +17 -0
  87. uncountable/integration/__init__.py +0 -0
  88. uncountable/integration/cli.py +49 -0
  89. uncountable/integration/construct_client.py +51 -0
  90. uncountable/integration/cron.py +29 -0
  91. uncountable/integration/db/__init__.py +0 -0
  92. uncountable/integration/db/connect.py +18 -0
  93. uncountable/integration/db/session.py +25 -0
  94. uncountable/integration/entrypoint.py +13 -0
  95. uncountable/integration/executors/__init__.py +0 -0
  96. uncountable/integration/executors/executors.py +148 -0
  97. uncountable/integration/executors/generic_upload_executor.py +284 -0
  98. uncountable/integration/executors/script_executor.py +25 -0
  99. uncountable/integration/job.py +87 -0
  100. uncountable/integration/queue_runner/__init__.py +0 -0
  101. uncountable/integration/queue_runner/command_server/__init__.py +24 -0
  102. uncountable/integration/queue_runner/command_server/command_client.py +68 -0
  103. uncountable/integration/queue_runner/command_server/command_server.py +64 -0
  104. uncountable/integration/queue_runner/command_server/protocol/__init__.py +0 -0
  105. uncountable/integration/queue_runner/command_server/protocol/command_server.proto +22 -0
  106. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.py +40 -0
  107. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.pyi +38 -0
  108. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2_grpc.py +129 -0
  109. uncountable/integration/queue_runner/command_server/types.py +52 -0
  110. uncountable/integration/queue_runner/datastore/__init__.py +3 -0
  111. uncountable/integration/queue_runner/datastore/datastore_sqlite.py +93 -0
  112. uncountable/integration/queue_runner/datastore/interface.py +19 -0
  113. uncountable/integration/queue_runner/datastore/model.py +17 -0
  114. uncountable/integration/queue_runner/job_scheduler.py +163 -0
  115. uncountable/integration/queue_runner/queue_runner.py +26 -0
  116. uncountable/integration/queue_runner/types.py +7 -0
  117. uncountable/integration/queue_runner/worker.py +119 -0
  118. uncountable/integration/scan_profiles.py +67 -0
  119. uncountable/integration/scheduler.py +150 -0
  120. uncountable/integration/secret_retrieval/__init__.py +3 -0
  121. uncountable/integration/secret_retrieval/retrieve_secret.py +93 -0
  122. uncountable/integration/server.py +117 -0
  123. uncountable/integration/telemetry.py +209 -0
  124. uncountable/integration/webhook_server/entrypoint.py +170 -0
  125. uncountable/types/__init__.py +136 -20
  126. uncountable/types/api/batch/execute_batch.py +15 -7
  127. uncountable/types/api/batch/execute_batch_load_async.py +42 -0
  128. uncountable/types/api/chemical/__init__.py +1 -0
  129. uncountable/types/api/chemical/convert_chemical_formats.py +63 -0
  130. uncountable/types/api/entity/create_entities.py +23 -11
  131. uncountable/types/api/entity/create_entity.py +21 -12
  132. uncountable/types/api/entity/get_entities_data.py +18 -8
  133. uncountable/types/api/entity/grant_entity_permissions.py +48 -0
  134. uncountable/types/api/entity/list_entities.py +27 -12
  135. uncountable/types/api/entity/lock_entity.py +45 -0
  136. uncountable/types/api/entity/resolve_entity_ids.py +17 -7
  137. uncountable/types/api/entity/set_entity_field_values.py +44 -0
  138. uncountable/types/api/entity/set_values.py +14 -7
  139. uncountable/types/api/entity/transition_entity_phase.py +80 -0
  140. uncountable/types/api/entity/unlock_entity.py +44 -0
  141. uncountable/types/api/equipment/__init__.py +1 -0
  142. uncountable/types/api/equipment/associate_equipment_input.py +44 -0
  143. uncountable/types/api/field_options/__init__.py +1 -0
  144. uncountable/types/api/field_options/upsert_field_options.py +55 -0
  145. uncountable/types/api/files/__init__.py +1 -0
  146. uncountable/types/api/files/download_file.py +77 -0
  147. uncountable/types/api/id_source/__init__.py +1 -0
  148. uncountable/types/api/id_source/list_id_source.py +56 -0
  149. uncountable/types/api/id_source/match_id_source.py +54 -0
  150. uncountable/types/api/input_groups/get_input_group_names.py +16 -6
  151. uncountable/types/api/inputs/create_inputs.py +24 -11
  152. uncountable/types/api/inputs/get_input_data.py +32 -13
  153. uncountable/types/api/inputs/get_input_names.py +18 -8
  154. uncountable/types/api/inputs/get_inputs_data.py +29 -10
  155. uncountable/types/api/inputs/set_input_attribute_values.py +16 -9
  156. uncountable/types/api/inputs/set_input_category.py +44 -0
  157. uncountable/types/api/inputs/set_input_subcategories.py +45 -0
  158. uncountable/types/api/inputs/set_intermediate_type.py +50 -0
  159. uncountable/types/api/material_families/__init__.py +1 -0
  160. uncountable/types/api/material_families/update_entity_material_families.py +48 -0
  161. uncountable/types/api/outputs/get_output_data.py +32 -16
  162. uncountable/types/api/outputs/get_output_names.py +18 -8
  163. uncountable/types/api/outputs/resolve_output_conditions.py +23 -10
  164. uncountable/types/api/permissions/__init__.py +1 -0
  165. uncountable/types/api/permissions/set_core_permissions.py +105 -0
  166. uncountable/types/api/project/get_projects.py +17 -7
  167. uncountable/types/api/project/get_projects_data.py +21 -11
  168. uncountable/types/api/recipe_links/__init__.py +1 -0
  169. uncountable/types/api/recipe_links/create_recipe_link.py +46 -0
  170. uncountable/types/api/recipe_links/remove_recipe_link.py +45 -0
  171. uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py +18 -8
  172. uncountable/types/api/recipes/add_recipe_to_project.py +42 -0
  173. uncountable/types/api/recipes/archive_recipes.py +42 -0
  174. uncountable/types/api/recipes/associate_recipe_as_input.py +44 -0
  175. uncountable/types/api/recipes/associate_recipe_as_lot.py +43 -0
  176. uncountable/types/api/recipes/clear_recipe_outputs.py +42 -0
  177. uncountable/types/api/recipes/create_recipe.py +51 -0
  178. uncountable/types/api/recipes/create_recipes.py +25 -12
  179. uncountable/types/api/recipes/disassociate_recipe_as_input.py +42 -0
  180. uncountable/types/api/recipes/edit_recipe_inputs.py +283 -0
  181. uncountable/types/api/recipes/get_column_calculation_values.py +58 -0
  182. uncountable/types/api/recipes/get_curve.py +15 -7
  183. uncountable/types/api/recipes/get_recipe_calculations.py +17 -10
  184. uncountable/types/api/recipes/get_recipe_links.py +13 -6
  185. uncountable/types/api/recipes/get_recipe_names.py +16 -6
  186. uncountable/types/api/recipes/get_recipe_output_metadata.py +14 -7
  187. uncountable/types/api/recipes/get_recipes_data.py +63 -38
  188. uncountable/types/api/recipes/lock_recipes.py +63 -0
  189. uncountable/types/api/recipes/remove_recipe_from_project.py +42 -0
  190. uncountable/types/api/recipes/set_recipe_inputs.py +19 -10
  191. uncountable/types/api/recipes/set_recipe_metadata.py +43 -0
  192. uncountable/types/api/recipes/set_recipe_output_annotations.py +115 -0
  193. uncountable/types/api/recipes/set_recipe_output_file.py +56 -0
  194. uncountable/types/api/recipes/set_recipe_outputs.py +26 -12
  195. uncountable/types/api/recipes/set_recipe_tags.py +109 -0
  196. uncountable/types/api/recipes/unarchive_recipes.py +41 -0
  197. uncountable/types/api/recipes/unlock_recipes.py +50 -0
  198. uncountable/types/api/triggers/__init__.py +1 -0
  199. uncountable/types/api/triggers/run_trigger.py +43 -0
  200. uncountable/types/api/uploader/__init__.py +1 -0
  201. uncountable/types/api/uploader/invoke_uploader.py +47 -0
  202. uncountable/types/async_batch.py +13 -0
  203. uncountable/types/async_batch_processor.py +384 -0
  204. uncountable/types/async_batch_t.py +97 -0
  205. uncountable/types/async_jobs.py +9 -0
  206. uncountable/types/async_jobs_t.py +53 -0
  207. uncountable/types/auth_retrieval.py +12 -0
  208. uncountable/types/auth_retrieval_t.py +75 -0
  209. uncountable/types/base.py +5 -78
  210. uncountable/types/base_t.py +85 -0
  211. uncountable/types/calculations.py +3 -18
  212. uncountable/types/calculations_t.py +27 -0
  213. uncountable/types/chemical_structure.py +8 -0
  214. uncountable/types/chemical_structure_t.py +28 -0
  215. uncountable/types/client_base.py +1093 -54
  216. uncountable/types/client_config.py +8 -0
  217. uncountable/types/client_config_t.py +26 -0
  218. uncountable/types/curves.py +5 -42
  219. uncountable/types/curves_t.py +51 -0
  220. uncountable/types/entity.py +8 -269
  221. uncountable/types/entity_t.py +393 -0
  222. uncountable/types/experiment_groups.py +3 -18
  223. uncountable/types/experiment_groups_t.py +27 -0
  224. uncountable/types/field_values.py +17 -60
  225. uncountable/types/field_values_t.py +204 -0
  226. uncountable/types/fields.py +3 -19
  227. uncountable/types/fields_t.py +28 -0
  228. uncountable/types/generic_upload.py +15 -0
  229. uncountable/types/generic_upload_t.py +119 -0
  230. uncountable/types/id_source.py +12 -0
  231. uncountable/types/id_source_t.py +68 -0
  232. uncountable/types/identifier.py +11 -0
  233. uncountable/types/identifier_t.py +63 -0
  234. uncountable/types/input_attributes.py +3 -24
  235. uncountable/types/input_attributes_t.py +30 -0
  236. uncountable/types/inputs.py +6 -56
  237. uncountable/types/inputs_t.py +83 -0
  238. uncountable/types/integration_server.py +9 -0
  239. uncountable/types/integration_server_t.py +42 -0
  240. uncountable/types/job_definition.py +27 -0
  241. uncountable/types/job_definition_t.py +260 -0
  242. uncountable/types/outputs.py +3 -21
  243. uncountable/types/outputs_t.py +30 -0
  244. uncountable/types/overrides.py +10 -0
  245. uncountable/types/overrides_t.py +49 -0
  246. uncountable/types/permissions.py +8 -0
  247. uncountable/types/permissions_t.py +46 -0
  248. uncountable/types/phases.py +3 -18
  249. uncountable/types/phases_t.py +27 -0
  250. uncountable/types/post_base.py +8 -0
  251. uncountable/types/post_base_t.py +30 -0
  252. uncountable/types/queued_job.py +16 -0
  253. uncountable/types/queued_job_t.py +123 -0
  254. uncountable/types/recipe_identifiers.py +12 -0
  255. uncountable/types/recipe_identifiers_t.py +76 -0
  256. uncountable/types/recipe_inputs.py +9 -0
  257. uncountable/types/recipe_inputs_t.py +30 -0
  258. uncountable/types/recipe_links.py +4 -45
  259. uncountable/types/recipe_links_t.py +54 -0
  260. uncountable/types/recipe_metadata.py +5 -45
  261. uncountable/types/recipe_metadata_t.py +58 -0
  262. uncountable/types/recipe_output_metadata.py +3 -19
  263. uncountable/types/recipe_output_metadata_t.py +28 -0
  264. uncountable/types/recipe_tags.py +3 -18
  265. uncountable/types/recipe_tags_t.py +27 -0
  266. uncountable/types/recipe_workflow_steps.py +14 -0
  267. uncountable/types/recipe_workflow_steps_t.py +95 -0
  268. uncountable/types/recipes.py +8 -0
  269. uncountable/types/recipes_t.py +25 -0
  270. uncountable/types/response.py +3 -20
  271. uncountable/types/response_t.py +26 -0
  272. uncountable/types/secret_retrieval.py +12 -0
  273. uncountable/types/secret_retrieval_t.py +75 -0
  274. uncountable/types/units.py +3 -18
  275. uncountable/types/units_t.py +27 -0
  276. uncountable/types/users.py +3 -19
  277. uncountable/types/users_t.py +28 -0
  278. uncountable/types/webhook_job.py +9 -0
  279. uncountable/types/webhook_job_t.py +37 -0
  280. uncountable/types/workflows.py +4 -27
  281. uncountable/types/workflows_t.py +39 -0
  282. UncountablePythonSDK-0.0.8.dist-info/METADATA +0 -27
  283. UncountablePythonSDK-0.0.8.dist-info/RECORD +0 -134
  284. examples/recipe-import/importer.py +0 -39
  285. type_spec/external/api/batch/execute_batch.yaml +0 -56
  286. type_spec/external/api/entity/create_entities.yaml +0 -33
  287. type_spec/external/api/entity/create_entity.yaml +0 -39
  288. type_spec/external/api/entity/get_entities_data.yaml +0 -29
  289. type_spec/external/api/entity/list_entities.yaml +0 -52
  290. type_spec/external/api/entity/resolve_entity_ids.yaml +0 -29
  291. type_spec/external/api/entity/set_values.yaml +0 -18
  292. type_spec/external/api/input_groups/get_input_group_names.yaml +0 -29
  293. type_spec/external/api/inputs/create_inputs.yaml +0 -48
  294. type_spec/external/api/inputs/get_input_data.yaml +0 -95
  295. type_spec/external/api/inputs/get_input_names.yaml +0 -38
  296. type_spec/external/api/inputs/get_inputs_data.yaml +0 -82
  297. type_spec/external/api/inputs/set_input_attribute_values.yaml +0 -33
  298. type_spec/external/api/outputs/get_output_data.yaml +0 -92
  299. type_spec/external/api/outputs/get_output_names.yaml +0 -35
  300. type_spec/external/api/outputs/resolve_output_conditions.yaml +0 -50
  301. type_spec/external/api/project/get_projects.yaml +0 -42
  302. type_spec/external/api/project/get_projects_data.yaml +0 -50
  303. type_spec/external/api/recipe_metadata/get_recipe_metadata_data.yaml +0 -41
  304. type_spec/external/api/recipes/create_recipes.yaml +0 -47
  305. type_spec/external/api/recipes/get_curve.yaml +0 -18
  306. type_spec/external/api/recipes/get_recipe_calculations.yaml +0 -39
  307. type_spec/external/api/recipes/get_recipe_links.yaml +0 -26
  308. type_spec/external/api/recipes/get_recipe_names.yaml +0 -29
  309. type_spec/external/api/recipes/get_recipe_output_metadata.yaml +0 -36
  310. type_spec/external/api/recipes/get_recipes_data.yaml +0 -238
  311. type_spec/external/api/recipes/set_recipe_inputs.yaml +0 -36
  312. type_spec/external/api/recipes/set_recipe_outputs.yaml +0 -52
@@ -3,23 +3,38 @@ import typing
3
3
  from dataclasses import dataclass, field
4
4
 
5
5
  from . import builder, util
6
- from .config import TypeScriptConfig
7
6
 
8
7
  INDENT = " "
9
8
 
10
9
  MODIFY_NOTICE = "// DO NOT MODIFY -- This file is generated by type_spec\n"
11
10
 
12
11
 
12
+ base_name_map = {
13
+ builder.BaseTypeName.s_boolean: "boolean",
14
+ builder.BaseTypeName.s_date: "string", # IMPROVE: Aliased DateStr
15
+ builder.BaseTypeName.s_date_time: "string", # IMPROVE: Aliased DateTimeStr
16
+ # Decimal's are marked as to_string_values thus are strings in the front-end
17
+ builder.BaseTypeName.s_decimal: "string",
18
+ builder.BaseTypeName.s_dict: "PartialRecord",
19
+ builder.BaseTypeName.s_integer: "number",
20
+ builder.BaseTypeName.s_lossy_decimal: "number",
21
+ builder.BaseTypeName.s_opaque_key: "string",
22
+ builder.BaseTypeName.s_none: "null",
23
+ builder.BaseTypeName.s_string: "string",
24
+ # UNC: global types
25
+ builder.BaseTypeName.s_json_value: "JsonValue",
26
+ }
27
+
28
+
13
29
  @dataclass(kw_only=True)
14
30
  class EmitTypescriptContext:
15
- config: TypeScriptConfig
16
31
  out: io.StringIO
17
32
  namespace: builder.SpecNamespace
18
- namespaces: typing.Set[builder.SpecNamespace] = field(default_factory=set)
33
+ namespaces: set[builder.SpecNamespace] = field(default_factory=set)
19
34
 
20
35
 
21
36
  def ts_type_name(name: str) -> str:
22
- return "".join([x.capitalize() for x in name.split("_")])
37
+ return "".join([x.title() for x in name.split("_")])
23
38
 
24
39
 
25
40
  def resolve_namespace_ref(namespace: builder.SpecNamespace) -> str:
@@ -30,4 +45,223 @@ def ts_name(name: str, name_case: builder.NameCase) -> str:
30
45
  if name_case == builder.NameCase.preserve:
31
46
  return name
32
47
  bits = util.split_any_name(name)
33
- return "".join([bits[0], *[x.capitalize() for x in bits[1:]]])
48
+ return "".join([bits[0], *[x.title() for x in bits[1:]]])
49
+
50
+
51
+ def emit_value_ts(
52
+ ctx: EmitTypescriptContext, stype: builder.SpecType, value: typing.Any
53
+ ) -> str:
54
+ """Mimics emit_python even if not all types are used in TypeScript yet"""
55
+ literal = builder.unwrap_literal_type(stype)
56
+ if literal is not None:
57
+ return emit_value_ts(ctx, literal.value_type, literal.value)
58
+
59
+ if stype.is_base_type(builder.BaseTypeName.s_string):
60
+ assert isinstance(value, str)
61
+ return util.encode_common_string(value)
62
+ elif stype.is_base_type(builder.BaseTypeName.s_integer):
63
+ assert isinstance(value, int)
64
+ return str(value)
65
+ elif stype.is_base_type(builder.BaseTypeName.s_boolean):
66
+ assert isinstance(value, bool)
67
+ return "true" if value else "false"
68
+ elif stype.is_base_type(builder.BaseTypeName.s_lossy_decimal):
69
+ return str(value)
70
+ elif stype.is_base_type(builder.BaseTypeName.s_decimal):
71
+ return f"'{value}'"
72
+ elif isinstance(stype, builder.SpecTypeInstance):
73
+ if stype.defn_type.is_base_type(builder.BaseTypeName.s_list):
74
+ sub_type = stype.parameters[0]
75
+ return (
76
+ "[" + ", ".join([emit_value_ts(ctx, sub_type, x) for x in value]) + "]"
77
+ )
78
+
79
+ if stype.defn_type.is_base_type(builder.BaseTypeName.s_dict):
80
+ key_type = stype.parameters[0]
81
+ value_type = stype.parameters[1]
82
+ return (
83
+ "{\n\t"
84
+ + ",\n\t".join(
85
+ (
86
+ f"[{emit_value_ts(ctx, key_type, dkey)}]: "
87
+ if not key_type.is_base_type(builder.BaseTypeName.s_string)
88
+ else f"{dkey}: "
89
+ )
90
+ + emit_value_ts(ctx, value_type, dvalue)
91
+ for dkey, dvalue in value.items()
92
+ )
93
+ + "\n}"
94
+ )
95
+
96
+ if stype.defn_type.is_base_type(builder.BaseTypeName.s_optional):
97
+ sub_type = stype.parameters[0]
98
+ if value is None:
99
+ return "null"
100
+ return emit_value_ts(ctx, sub_type, value)
101
+
102
+ elif isinstance(stype, builder.SpecTypeDefnStringEnum):
103
+ return f"{refer_to(ctx, stype)}.{ts_enum_name(value, stype.name_case)}"
104
+
105
+ raise Exception("invalid constant type", value, stype)
106
+
107
+
108
+ def emit_type_ts(ctx: EmitTypescriptContext, stype: builder.SpecType) -> None:
109
+ if not isinstance(stype, builder.SpecTypeDefn):
110
+ return
111
+
112
+ if stype.is_base or stype.is_predefined:
113
+ return
114
+
115
+ ctx.out.write("\n")
116
+ ctx.out.write(MODIFY_NOTICE)
117
+
118
+ if isinstance(stype, builder.SpecTypeDefnExternal):
119
+ assert not stype.is_exported, "expecting private names"
120
+ ctx.out.write(stype.external_map["ts"])
121
+ ctx.out.write("\n")
122
+ return
123
+
124
+ assert stype.is_exported, "expecting exported names"
125
+ if isinstance(stype, builder.SpecTypeDefnAlias):
126
+ ctx.out.write(f"export type {stype.name} = {refer_to(ctx, stype.alias)}\n")
127
+ return
128
+
129
+ if isinstance(stype, builder.SpecTypeDefnUnion):
130
+ ctx.out.write(
131
+ f"export type {stype.name} = {refer_to(ctx, stype.get_backing_type())}\n"
132
+ )
133
+ return
134
+
135
+ if isinstance(stype, builder.SpecTypeDefnStringEnum):
136
+ ctx.out.write(f"export enum {stype.name} {{\n")
137
+ assert stype.values
138
+ for name, entry in stype.values.items():
139
+ ctx.out.write(
140
+ f'{INDENT}{ts_enum_name(name, stype.name_case)} = "{entry.value}",\n'
141
+ )
142
+ ctx.out.write("}\n")
143
+ return
144
+
145
+ assert isinstance(stype, builder.SpecTypeDefnObject)
146
+ assert stype.base is not None
147
+
148
+ base_type = ""
149
+ if not stype.base.is_base:
150
+ base_type = f"{refer_to(ctx, stype.base)} & "
151
+
152
+ if stype.properties is None and base_type == "":
153
+ ctx.out.write(f"export type {stype.name} = TEmpty\n")
154
+ elif stype.properties is None:
155
+ ctx.out.write(f"export type {stype.name} = {base_type}{{}}\n")
156
+ else:
157
+ if isinstance(stype, builder.SpecTypeDefnObject) and len(stype.parameters) > 0:
158
+ full_type_name = f"{stype.name}<{', '.join(stype.parameters)}>"
159
+ else:
160
+ full_type_name = stype.name
161
+ ctx.out.write(f"export type {full_type_name} = {base_type}{{")
162
+ ctx.out.write("\n")
163
+ for prop in stype.properties.values():
164
+ ref_type = refer_to(ctx, prop.spec_type)
165
+ prop_name = ts_name(prop.name, prop.name_case)
166
+ if prop.has_default and not prop.parse_require:
167
+ # For now, we'll assume the generated types with defaults are meant as
168
+ # arguments, thus treat like extant==missing
169
+ # IMPROVE: if we can decide they are meant as output instead, then
170
+ # they should be marked as required
171
+ ctx.out.write(f"{INDENT}{prop_name}?: {ref_type}")
172
+ elif prop.extant == builder.PropertyExtant.missing:
173
+ # Unlike optional below, missing does not imply null is possible. They
174
+ # treated distinctly.
175
+ ctx.out.write(f"{INDENT}{prop_name}?: {ref_type}")
176
+ elif prop.extant == builder.PropertyExtant.optional:
177
+ # Need to add in |null since Python side can produce null's right now
178
+ # IMPROVE: It would be better if the serializer could instead omit the None's
179
+ # Dropping the null should be forward compatible
180
+ ctx.out.write(f"{INDENT}{prop_name}?: {ref_type} | null")
181
+ else:
182
+ ctx.out.write(f"{INDENT}{prop_name}: {ref_type}")
183
+ ctx.out.write("\n")
184
+ ctx.out.write("}\n")
185
+
186
+
187
+ def refer_to(ctx: EmitTypescriptContext, stype: builder.SpecType) -> str:
188
+ return refer_to_impl(ctx, stype)[0]
189
+
190
+
191
+ def refer_to_impl(
192
+ ctx: EmitTypescriptContext, stype: builder.SpecType
193
+ ) -> tuple[str, bool]:
194
+ """
195
+ @return (string-specific, multiple-types)
196
+ """
197
+ if isinstance(stype, builder.SpecTypeInstance):
198
+ if stype.defn_type.name == builder.BaseTypeName.s_list:
199
+ spec, multi = refer_to_impl(ctx, stype.parameters[0])
200
+ return f"({spec})[]" if multi else f"{spec}[]", False
201
+ if stype.defn_type.name == builder.BaseTypeName.s_readonly_array:
202
+ spec, multi = refer_to_impl(ctx, stype.parameters[0])
203
+ return f"readonly ({spec})[]" if multi else f"readonly {spec}[]", False
204
+ if stype.defn_type.name == builder.BaseTypeName.s_union:
205
+ return (
206
+ f"({' | '.join([refer_to(ctx, p) for p in stype.parameters])})",
207
+ False,
208
+ )
209
+ if stype.defn_type.name == builder.BaseTypeName.s_literal:
210
+ parts = []
211
+ for parameter in stype.parameters:
212
+ assert isinstance(parameter, builder.SpecTypeLiteralWrapper)
213
+ parts.append(refer_to(ctx, parameter))
214
+ return f"({' | '.join(parts)})", False
215
+ if stype.defn_type.name == builder.BaseTypeName.s_optional:
216
+ return f"{refer_to(ctx, stype.parameters[0])} | null", True
217
+ if stype.defn_type.name == builder.BaseTypeName.s_tuple:
218
+ return f"[{', '.join([refer_to(ctx, p) for p in stype.parameters])}]", False
219
+ params = ", ".join([refer_to(ctx, p) for p in stype.parameters])
220
+ return f"{refer_to(ctx, stype.defn_type)}<{params}>", False
221
+
222
+ if isinstance(stype, builder.SpecTypeLiteralWrapper):
223
+ return emit_value_ts(ctx, stype.value_type, stype.value), False
224
+
225
+ if isinstance(stype, builder.SpecTypeGenericParameter):
226
+ return stype.name, False
227
+
228
+ assert isinstance(stype, builder.SpecTypeDefn)
229
+ if stype.is_base: # assume correct namespace
230
+ if stype.name == builder.BaseTypeName.s_list:
231
+ return "any[]", False # TODO: generic type
232
+ return base_name_map[builder.BaseTypeName(stype.name)], False
233
+
234
+ if stype.namespace == ctx.namespace:
235
+ return stype.name, False
236
+
237
+ ctx.namespaces.add(stype.namespace)
238
+ return f"{resolve_namespace_ref(stype.namespace)}.{stype.name}", False
239
+
240
+
241
+ def ts_enum_name(name: str, name_case: builder.NameCase) -> str:
242
+ if name_case == builder.NameCase.js_upper:
243
+ return name.upper()
244
+ return ts_name(name, name_case)
245
+
246
+
247
+ def resolve_namespace_name(namespace: builder.SpecNamespace) -> str:
248
+ return namespace.name
249
+
250
+
251
+ def emit_namespace_imports_ts(
252
+ namespaces: set[builder.SpecNamespace],
253
+ out: io.StringIO,
254
+ current_namespace: builder.SpecNamespace,
255
+ ) -> None:
256
+ for ns in sorted(
257
+ namespaces,
258
+ key=lambda name: resolve_namespace_name(name),
259
+ ):
260
+ import_as = resolve_namespace_ref(ns)
261
+ import_path = (
262
+ "./"
263
+ if len(current_namespace.path) == 1
264
+ else "../" * (len(current_namespace.path) - 1)
265
+ )
266
+ import_from = f"{import_path}{resolve_namespace_name(ns)}"
267
+ out.write(f'import * as {import_as} from "{import_from}"\n') # noqa: E501
@@ -1,9 +1,11 @@
1
1
  import os
2
- from typing import Callable, Optional
2
+ from collections.abc import Callable
3
+ from io import StringIO
3
4
 
4
- import yaml
5
5
  from shelljob import fs
6
6
 
7
+ from pkgs.serialization import yaml
8
+
7
9
  from .builder import SpecBuilder
8
10
  from .config import Config
9
11
 
@@ -12,17 +14,36 @@ ext_map = {
12
14
  ".py": "python",
13
15
  }
14
16
 
17
+ _DOC_FILE_REFEX = ".*/docs/(examples|guides)/.*yaml"
18
+ _EXAMPLE_FILE_REGEX = ".*/docs/examples/.*yaml"
19
+ _GUIDE_FILE_REGEX = ".*/docs/guides/.*md"
20
+
15
21
 
16
22
  def find_and_handle_files(
17
- root_folder: str, name_regex: str, handler: Callable[[str, str], None]
23
+ *,
24
+ root_folder: str,
25
+ handler: Callable[[str, str], None],
26
+ name_regex: str | None = None,
27
+ not_name_regex: str | None = None,
28
+ whole_name_regex: str | None = None,
29
+ not_whole_name_regex: str | None = None,
18
30
  ) -> None:
19
- for file_name in fs.find(root_folder, name_regex=name_regex, relative=True):
31
+ for file_name in fs.find(
32
+ root_folder,
33
+ name_regex=name_regex,
34
+ not_name_regex=not_name_regex,
35
+ whole_name_regex=whole_name_regex,
36
+ not_whole_name_regex=not_whole_name_regex,
37
+ relative=True,
38
+ ):
20
39
  with open(os.path.join(root_folder, file_name), encoding="utf-8") as file:
21
40
  handler(file_name, file.read())
22
41
 
23
42
 
24
- def load_types(config: Config) -> Optional[SpecBuilder]:
25
- builder = SpecBuilder(api_endpoints=config.api_endpoint)
43
+ def load_types(config: Config) -> SpecBuilder | None:
44
+ builder = SpecBuilder(
45
+ api_endpoints=config.api_endpoint, top_namespace=config.top_namespace
46
+ )
26
47
 
27
48
  def handle_builder_add(
28
49
  file_name: str, file_content: str, handler: Callable[[str, str, str], None]
@@ -31,9 +52,16 @@ def load_types(config: Config) -> Optional[SpecBuilder]:
31
52
  name, ext = os.path.splitext(by_name)
32
53
  handler(ext_map[ext], name, file_content)
33
54
 
55
+ def handle_builder_example_add(file_name: str, file_content: str) -> None:
56
+ yaml_content = yaml.safe_load(StringIO(file_content))
57
+ builder.add_example_file(yaml_content)
58
+
59
+ def handle_builder_guide_add(file_name: str, file_content: str) -> None:
60
+ builder.add_guide_file(file_content)
61
+
34
62
  for folder in config.type_spec_types:
35
63
  find_and_handle_files(
36
- folder,
64
+ root_folder=folder,
37
65
  name_regex=".*\\.(ts|py)\\.part",
38
66
  handler=lambda file_name, file_content: handle_builder_add(
39
67
  file_name, file_content, builder.add_part_file
@@ -42,7 +70,7 @@ def load_types(config: Config) -> Optional[SpecBuilder]:
42
70
 
43
71
  for folder in config.type_spec_types:
44
72
  find_and_handle_files(
45
- folder,
73
+ root_folder=folder,
46
74
  name_regex=".*\\.(ts|py)\\.prepart",
47
75
  handler=lambda file_name, file_content: handle_builder_add(
48
76
  file_name, file_content, builder.add_prepart_file
@@ -63,11 +91,28 @@ def load_types(config: Config) -> Optional[SpecBuilder]:
63
91
 
64
92
  for folder in config.type_spec_types:
65
93
  find_and_handle_files(
66
- folder, name_regex=".*\\.yaml", handler=builder_prescan_file
94
+ root_folder=folder,
95
+ name_regex=".*\\.yaml",
96
+ not_whole_name_regex=_DOC_FILE_REFEX,
97
+ handler=builder_prescan_file,
67
98
  )
68
99
 
100
+ if config.open_api is not None:
101
+ for folder in config.type_spec_types:
102
+ find_and_handle_files(
103
+ root_folder=folder,
104
+ whole_name_regex=_EXAMPLE_FILE_REGEX,
105
+ handler=handle_builder_example_add,
106
+ )
107
+
108
+ for folder in config.type_spec_types:
109
+ find_and_handle_files(
110
+ root_folder=folder,
111
+ whole_name_regex=_GUIDE_FILE_REGEX,
112
+ handler=handle_builder_guide_add,
113
+ )
114
+
69
115
  if not builder.process():
70
116
  return None
71
117
 
72
118
  return builder
73
- return True
@@ -1,7 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
- from enum import Enum
3
- from io import UnsupportedOperation
4
- from typing import Optional
2
+ from enum import StrEnum
5
3
 
6
4
 
7
5
  class OpenAPIType(ABC):
@@ -13,10 +11,8 @@ class OpenAPIType(ABC):
13
11
  self.nullable = nullable
14
12
 
15
13
  @abstractmethod
16
- def asdict(self) -> dict[str, object]: ...
17
-
18
- def asarguments(self) -> list[dict[str, object]]:
19
- raise UnsupportedOperation
14
+ def asdict(self) -> dict[str, object]:
15
+ pass
20
16
 
21
17
  def add_addl_info(self, emitted: dict[str, object]) -> dict[str, object]:
22
18
  if self.description is not None:
@@ -40,7 +36,7 @@ class OpenAPIRefType(OpenAPIType):
40
36
  return {"$ref": self.source}
41
37
 
42
38
 
43
- class OpenAPIPrimitive(str, Enum):
39
+ class OpenAPIPrimitive(StrEnum):
44
40
  string = "string"
45
41
  boolean = "boolean"
46
42
  integer = "integer"
@@ -147,9 +143,6 @@ class OpenAPIFreeFormObjectType(OpenAPIType):
147
143
  def asdict(self) -> dict[str, object]:
148
144
  return self.add_addl_info({"type": "object"})
149
145
 
150
- def asarguments(self) -> list[dict[str, object]]:
151
- return []
152
-
153
146
 
154
147
  class OpenAPIObjectType(OpenAPIType):
155
148
  """
@@ -164,7 +157,7 @@ class OpenAPIObjectType(OpenAPIType):
164
157
  description: str | None = None,
165
158
  nullable: bool = False,
166
159
  *,
167
- property_desc: Optional[dict[str, str]] = None,
160
+ property_desc: dict[str, str] | None = None,
168
161
  ) -> None:
169
162
  self.properties = properties
170
163
  if property_desc is None:
@@ -173,31 +166,39 @@ class OpenAPIObjectType(OpenAPIType):
173
166
  self.property_desc = property_desc
174
167
  super().__init__(description=description, nullable=nullable)
175
168
 
169
+ def _emit_property_desc(self, property_name: str) -> dict[str, str]:
170
+ desc = self.property_desc.get(property_name)
171
+ if desc is None or desc.strip() == "":
172
+ return {}
173
+
174
+ return {"description": desc}
175
+
176
+ def _emit_property(
177
+ self, property_name: str, property_type: OpenAPIType
178
+ ) -> dict[str, object]:
179
+ property_info = {
180
+ **property_type.asdict(),
181
+ }
182
+ property_description = self._emit_property_desc(property_name)
183
+ if "$ref" in property_info and "description" in property_description:
184
+ return {"allOf": [property_info, property_description]}
185
+
186
+ return property_info | property_description
187
+
176
188
  def asdict(self) -> dict[str, object]:
177
189
  return self.add_addl_info({
178
190
  "type": "object",
191
+ "required": [
192
+ property_name
193
+ for property_name, property_type in self.properties.items()
194
+ if not property_type.nullable
195
+ ],
179
196
  "properties": {
180
- property_name: {
181
- **property_type.asdict(),
182
- "description": self.property_desc.get(property_name),
183
- }
197
+ property_name: self._emit_property(property_name, property_type)
184
198
  for property_name, property_type in self.properties.items()
185
199
  },
186
200
  })
187
201
 
188
- def asarguments(self) -> list[dict[str, object]]:
189
- argument_types: list[dict[str, object]] = []
190
- for property_name, property_type in self.properties.items():
191
- desc = self.property_desc.get(property_name)
192
- argument_types.append({
193
- "name": property_name,
194
- "in": "query",
195
- "schema": property_type.asdict(),
196
- "required": not property_type.nullable,
197
- "description": desc or "",
198
- })
199
- return argument_types
200
-
201
202
 
202
203
  class OpenAPIUnionType(OpenAPIType):
203
204
  """
@@ -219,12 +220,6 @@ class OpenAPIUnionType(OpenAPIType):
219
220
  # TODO: use parents description and nullable
220
221
  return {"oneOf": [base_type.asdict() for base_type in self.base_types]}
221
222
 
222
- def asarguments(self) -> list[dict[str, object]]:
223
- # TODO handle inheritence (allOf and refs); need to inline here...
224
- # for now skip this endpoint
225
-
226
- return []
227
-
228
223
 
229
224
  class OpenAPIIntersectionType(OpenAPIType):
230
225
  """
@@ -245,9 +240,3 @@ class OpenAPIIntersectionType(OpenAPIType):
245
240
  def asdict(self) -> dict[str, object]:
246
241
  # TODO: use parents description and nullable
247
242
  return {"allOf": [base_type.asdict() for base_type in self.base_types]}
248
-
249
- def asarguments(self) -> list[dict[str, object]]:
250
- # TODO handle inheritence (allOf and refs); need to inline here...
251
- # for now skip this endpoint
252
-
253
- return []
@@ -1,7 +1,8 @@
1
1
  """
2
2
  Types that type_spec will use in the emitted files.
3
3
  """
4
- from typing import Union, Mapping, Sequence, Any, TYPE_CHECKING
4
+ from typing import Union, Any, TYPE_CHECKING
5
+ from collections.abc import Mapping, Sequence
5
6
 
6
7
  # These two are part of the core output, thus don't duplicate here
7
8
  # from decimal import Decimal
@@ -21,8 +22,8 @@ PureJsonScalar = Union[str, float, bool, None]
21
22
  # Regular expressions for identifying ref names and IDs. Ref names should be
22
23
  # using this regular expression as a constriant in the database.
23
24
  REF_NAME_REGEX = r"^[a-zA-Z0-9_/-]+$"
24
- # Does not support nonpositive integers at the moment.
25
- ID_REGEX = r"[1-9][0-9]{0,20}"
25
+ # Ids matching a strict integer number are converted to integers
26
+ ID_REGEX = r"-?[1-9][0-9]{0,20}"
26
27
 
27
28
 
28
29
  if TYPE_CHECKING: