UncountablePythonSDK 0.0.52__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 (316) hide show
  1. docs/conf.py +54 -7
  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 +6 -4
  8. examples/async_batch.py +3 -3
  9. examples/basic_auth.py +7 -0
  10. examples/create_entity.py +3 -1
  11. examples/create_ingredient_sdk.py +34 -0
  12. examples/download_files.py +26 -0
  13. examples/edit_recipe_inputs.py +4 -2
  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 +4 -1
  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 +1 -2
  29. pkgs/argument_parser/__init__.py +9 -0
  30. pkgs/argument_parser/_is_namedtuple.py +3 -0
  31. pkgs/argument_parser/argument_parser.py +217 -70
  32. pkgs/filesystem_utils/__init__.py +1 -0
  33. pkgs/filesystem_utils/_blob_session.py +144 -0
  34. pkgs/filesystem_utils/_gdrive_session.py +10 -7
  35. pkgs/filesystem_utils/_s3_session.py +15 -13
  36. pkgs/filesystem_utils/_sftp_session.py +11 -7
  37. pkgs/filesystem_utils/file_type_utils.py +30 -10
  38. pkgs/py.typed +0 -0
  39. pkgs/serialization/__init__.py +7 -2
  40. pkgs/serialization/annotation.py +64 -0
  41. pkgs/serialization/missing_sentry.py +1 -1
  42. pkgs/serialization/opaque_key.py +1 -1
  43. pkgs/serialization/serial_alias.py +47 -0
  44. pkgs/serialization/serial_class.py +47 -26
  45. pkgs/serialization/serial_generic.py +16 -0
  46. pkgs/serialization/serial_union.py +17 -14
  47. pkgs/serialization/yaml.py +4 -1
  48. pkgs/serialization_util/__init__.py +6 -0
  49. pkgs/serialization_util/dataclasses.py +14 -0
  50. pkgs/serialization_util/serialization_helpers.py +15 -5
  51. pkgs/type_spec/actions_registry/__main__.py +0 -4
  52. pkgs/type_spec/actions_registry/emit_typescript.py +5 -5
  53. pkgs/type_spec/builder.py +354 -119
  54. pkgs/type_spec/builder_types.py +9 -0
  55. pkgs/type_spec/config.py +51 -11
  56. pkgs/type_spec/cross_output_links.py +99 -0
  57. pkgs/type_spec/emit_io_ts.py +1 -1
  58. pkgs/type_spec/emit_open_api.py +127 -36
  59. pkgs/type_spec/emit_open_api_util.py +5 -6
  60. pkgs/type_spec/emit_python.py +329 -121
  61. pkgs/type_spec/emit_typescript.py +117 -256
  62. pkgs/type_spec/emit_typescript_util.py +291 -2
  63. pkgs/type_spec/load_types.py +18 -4
  64. pkgs/type_spec/non_discriminated_union_exceptions.py +14 -0
  65. pkgs/type_spec/open_api_util.py +29 -4
  66. pkgs/type_spec/parts/base.py.prepart +13 -10
  67. pkgs/type_spec/parts/base.ts.prepart +4 -0
  68. pkgs/type_spec/type_info/__main__.py +3 -1
  69. pkgs/type_spec/type_info/emit_type_info.py +124 -29
  70. pkgs/type_spec/ui_entry_actions/__init__.py +4 -0
  71. pkgs/type_spec/ui_entry_actions/generate_ui_entry_actions.py +308 -0
  72. pkgs/type_spec/util.py +4 -4
  73. pkgs/type_spec/value_spec/__main__.py +26 -9
  74. pkgs/type_spec/value_spec/convert_type.py +21 -1
  75. pkgs/type_spec/value_spec/emit_python.py +25 -7
  76. pkgs/type_spec/value_spec/types.py +1 -1
  77. uncountable/core/async_batch.py +1 -1
  78. uncountable/core/client.py +142 -39
  79. uncountable/core/environment.py +41 -0
  80. uncountable/core/file_upload.py +52 -18
  81. uncountable/integration/cli.py +142 -0
  82. uncountable/integration/construct_client.py +8 -8
  83. uncountable/integration/cron.py +11 -37
  84. uncountable/integration/db/connect.py +12 -2
  85. uncountable/integration/db/session.py +25 -0
  86. uncountable/integration/entrypoint.py +8 -37
  87. uncountable/integration/executors/executors.py +125 -2
  88. uncountable/integration/executors/generic_upload_executor.py +87 -29
  89. uncountable/integration/executors/script_executor.py +3 -3
  90. uncountable/integration/http_server/__init__.py +5 -0
  91. uncountable/integration/http_server/types.py +69 -0
  92. uncountable/integration/job.py +242 -12
  93. uncountable/integration/queue_runner/__init__.py +0 -0
  94. uncountable/integration/queue_runner/command_server/__init__.py +28 -0
  95. uncountable/integration/queue_runner/command_server/command_client.py +133 -0
  96. uncountable/integration/queue_runner/command_server/command_server.py +142 -0
  97. uncountable/integration/queue_runner/command_server/constants.py +4 -0
  98. uncountable/integration/queue_runner/command_server/protocol/__init__.py +0 -0
  99. uncountable/integration/queue_runner/command_server/protocol/command_server.proto +58 -0
  100. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.py +57 -0
  101. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.pyi +114 -0
  102. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2_grpc.py +264 -0
  103. uncountable/integration/queue_runner/command_server/types.py +75 -0
  104. uncountable/integration/queue_runner/datastore/__init__.py +3 -0
  105. uncountable/integration/queue_runner/datastore/datastore_sqlite.py +250 -0
  106. uncountable/integration/queue_runner/datastore/interface.py +29 -0
  107. uncountable/integration/queue_runner/datastore/model.py +24 -0
  108. uncountable/integration/queue_runner/job_scheduler.py +200 -0
  109. uncountable/integration/queue_runner/queue_runner.py +34 -0
  110. uncountable/integration/queue_runner/types.py +7 -0
  111. uncountable/integration/queue_runner/worker.py +116 -0
  112. uncountable/integration/scan_profiles.py +67 -0
  113. uncountable/integration/scheduler.py +199 -0
  114. uncountable/integration/secret_retrieval/retrieve_secret.py +26 -4
  115. uncountable/integration/server.py +94 -69
  116. uncountable/integration/telemetry.py +150 -34
  117. uncountable/integration/webhook_server/entrypoint.py +97 -0
  118. uncountable/types/__init__.py +78 -1
  119. uncountable/types/api/batch/execute_batch.py +13 -6
  120. uncountable/types/api/batch/execute_batch_load_async.py +9 -3
  121. uncountable/types/api/chemical/convert_chemical_formats.py +17 -5
  122. uncountable/types/api/condition_parameters/__init__.py +1 -0
  123. uncountable/types/api/condition_parameters/upsert_condition_match.py +72 -0
  124. uncountable/types/api/entity/create_entities.py +19 -7
  125. uncountable/types/api/entity/create_entity.py +17 -8
  126. uncountable/types/api/entity/create_or_update_entity.py +48 -0
  127. uncountable/types/api/entity/export_entities.py +59 -0
  128. uncountable/types/api/entity/get_entities_data.py +13 -4
  129. uncountable/types/api/entity/grant_entity_permissions.py +48 -0
  130. uncountable/types/api/entity/list_aggregate.py +79 -0
  131. uncountable/types/api/entity/list_entities.py +42 -10
  132. uncountable/types/api/entity/lock_entity.py +11 -4
  133. uncountable/types/api/entity/lookup_entity.py +116 -0
  134. uncountable/types/api/entity/resolve_entity_ids.py +15 -6
  135. uncountable/types/api/entity/set_entity_field_values.py +44 -0
  136. uncountable/types/api/entity/set_values.py +10 -3
  137. uncountable/types/api/entity/transition_entity_phase.py +22 -7
  138. uncountable/types/api/entity/unlock_entity.py +10 -3
  139. uncountable/types/api/equipment/associate_equipment_input.py +9 -3
  140. uncountable/types/api/field_options/upsert_field_options.py +17 -7
  141. uncountable/types/api/files/__init__.py +1 -0
  142. uncountable/types/api/files/download_file.py +77 -0
  143. uncountable/types/api/id_source/list_id_source.py +16 -7
  144. uncountable/types/api/id_source/match_id_source.py +14 -5
  145. uncountable/types/api/input_groups/get_input_group_names.py +13 -4
  146. uncountable/types/api/inputs/create_inputs.py +23 -9
  147. uncountable/types/api/inputs/get_input_data.py +30 -12
  148. uncountable/types/api/inputs/get_input_names.py +16 -7
  149. uncountable/types/api/inputs/get_inputs_data.py +25 -7
  150. uncountable/types/api/inputs/set_input_attribute_values.py +12 -6
  151. uncountable/types/api/inputs/set_input_category.py +12 -5
  152. uncountable/types/api/inputs/set_input_subcategories.py +10 -3
  153. uncountable/types/api/inputs/set_intermediate_type.py +11 -4
  154. uncountable/types/api/integrations/__init__.py +1 -0
  155. uncountable/types/api/integrations/publish_realtime_data.py +41 -0
  156. uncountable/types/api/integrations/push_notification.py +49 -0
  157. uncountable/types/api/integrations/register_sockets_token.py +41 -0
  158. uncountable/types/api/listing/__init__.py +1 -0
  159. uncountable/types/api/listing/fetch_listing.py +58 -0
  160. uncountable/types/api/material_families/update_entity_material_families.py +10 -4
  161. uncountable/types/api/notebooks/__init__.py +1 -0
  162. uncountable/types/api/notebooks/add_notebook_content.py +119 -0
  163. uncountable/types/api/outputs/get_output_data.py +28 -13
  164. uncountable/types/api/outputs/get_output_names.py +15 -6
  165. uncountable/types/api/outputs/get_output_organization.py +173 -0
  166. uncountable/types/api/outputs/resolve_output_conditions.py +20 -8
  167. uncountable/types/api/permissions/set_core_permissions.py +26 -10
  168. uncountable/types/api/project/get_projects.py +16 -7
  169. uncountable/types/api/project/get_projects_data.py +17 -8
  170. uncountable/types/api/recipe_links/create_recipe_link.py +12 -5
  171. uncountable/types/api/recipe_links/remove_recipe_link.py +11 -4
  172. uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py +16 -7
  173. uncountable/types/api/recipes/add_recipe_to_project.py +10 -3
  174. uncountable/types/api/recipes/add_time_series_data.py +64 -0
  175. uncountable/types/api/recipes/archive_recipes.py +11 -4
  176. uncountable/types/api/recipes/associate_recipe_as_input.py +12 -5
  177. uncountable/types/api/recipes/associate_recipe_as_lot.py +10 -3
  178. uncountable/types/api/recipes/clear_recipe_outputs.py +42 -0
  179. uncountable/types/api/recipes/create_mix_order.py +44 -0
  180. uncountable/types/api/recipes/create_recipe.py +15 -9
  181. uncountable/types/api/recipes/create_recipes.py +21 -9
  182. uncountable/types/api/recipes/disassociate_recipe_as_input.py +10 -3
  183. uncountable/types/api/recipes/edit_recipe_inputs.py +134 -22
  184. uncountable/types/api/recipes/get_column_calculation_values.py +57 -0
  185. uncountable/types/api/recipes/get_curve.py +11 -5
  186. uncountable/types/api/recipes/get_recipe_calculations.py +13 -7
  187. uncountable/types/api/recipes/get_recipe_links.py +10 -4
  188. uncountable/types/api/recipes/get_recipe_names.py +13 -4
  189. uncountable/types/api/recipes/get_recipe_output_metadata.py +12 -6
  190. uncountable/types/api/recipes/get_recipes_data.py +87 -33
  191. uncountable/types/api/recipes/lock_recipes.py +19 -8
  192. uncountable/types/api/recipes/remove_recipe_from_project.py +10 -3
  193. uncountable/types/api/recipes/set_recipe_inputs.py +16 -10
  194. uncountable/types/api/recipes/set_recipe_metadata.py +10 -3
  195. uncountable/types/api/recipes/set_recipe_output_annotations.py +24 -12
  196. uncountable/types/api/recipes/set_recipe_output_file.py +55 -0
  197. uncountable/types/api/recipes/set_recipe_outputs.py +35 -12
  198. uncountable/types/api/recipes/set_recipe_tags.py +26 -9
  199. uncountable/types/api/recipes/set_recipe_total.py +59 -0
  200. uncountable/types/api/recipes/unarchive_recipes.py +10 -3
  201. uncountable/types/api/recipes/unlock_recipes.py +14 -6
  202. uncountable/types/api/runsheet/__init__.py +1 -0
  203. uncountable/types/api/runsheet/complete_async_upload.py +41 -0
  204. uncountable/types/api/triggers/run_trigger.py +11 -4
  205. uncountable/types/api/uploader/complete_async_parse.py +46 -0
  206. uncountable/types/api/uploader/invoke_uploader.py +13 -6
  207. uncountable/types/api/user/__init__.py +1 -0
  208. uncountable/types/api/user/get_current_user_info.py +40 -0
  209. uncountable/types/async_batch.py +2 -1
  210. uncountable/types/async_batch_processor.py +618 -18
  211. uncountable/types/async_batch_t.py +54 -7
  212. uncountable/types/async_jobs.py +8 -0
  213. uncountable/types/async_jobs_t.py +52 -0
  214. uncountable/types/auth_retrieval.py +11 -0
  215. uncountable/types/auth_retrieval_t.py +75 -0
  216. uncountable/types/base.py +0 -1
  217. uncountable/types/base_t.py +13 -11
  218. uncountable/types/calculations.py +0 -1
  219. uncountable/types/calculations_t.py +5 -2
  220. uncountable/types/chemical_structure.py +0 -1
  221. uncountable/types/chemical_structure_t.py +6 -5
  222. uncountable/types/client_base.py +751 -70
  223. uncountable/types/client_config.py +1 -1
  224. uncountable/types/client_config_t.py +17 -3
  225. uncountable/types/curves.py +0 -1
  226. uncountable/types/curves_t.py +10 -7
  227. uncountable/types/data.py +12 -0
  228. uncountable/types/data_t.py +103 -0
  229. uncountable/types/entity.py +4 -1
  230. uncountable/types/entity_t.py +125 -7
  231. uncountable/types/experiment_groups.py +0 -1
  232. uncountable/types/experiment_groups_t.py +5 -2
  233. uncountable/types/exports.py +8 -0
  234. uncountable/types/exports_t.py +34 -0
  235. uncountable/types/field_values.py +19 -1
  236. uncountable/types/field_values_t.py +246 -9
  237. uncountable/types/fields.py +0 -1
  238. uncountable/types/fields_t.py +5 -2
  239. uncountable/types/generic_upload.py +6 -1
  240. uncountable/types/generic_upload_t.py +88 -9
  241. uncountable/types/id_source.py +0 -1
  242. uncountable/types/id_source_t.py +26 -7
  243. uncountable/types/identifier.py +0 -1
  244. uncountable/types/identifier_t.py +13 -5
  245. uncountable/types/input_attributes.py +0 -1
  246. uncountable/types/input_attributes_t.py +4 -4
  247. uncountable/types/inputs.py +1 -1
  248. uncountable/types/inputs_t.py +24 -4
  249. uncountable/types/integration_server.py +8 -0
  250. uncountable/types/integration_server_t.py +46 -0
  251. uncountable/types/integration_session.py +10 -0
  252. uncountable/types/integration_session_t.py +60 -0
  253. uncountable/types/integrations.py +10 -0
  254. uncountable/types/integrations_t.py +62 -0
  255. uncountable/types/job_definition.py +4 -6
  256. uncountable/types/job_definition_t.py +96 -65
  257. uncountable/types/listing.py +9 -0
  258. uncountable/types/listing_t.py +51 -0
  259. uncountable/types/notices.py +8 -0
  260. uncountable/types/notices_t.py +37 -0
  261. uncountable/types/notifications.py +11 -0
  262. uncountable/types/notifications_t.py +74 -0
  263. uncountable/types/outputs.py +0 -1
  264. uncountable/types/outputs_t.py +6 -3
  265. uncountable/types/overrides.py +9 -0
  266. uncountable/types/overrides_t.py +49 -0
  267. uncountable/types/permissions.py +0 -1
  268. uncountable/types/permissions_t.py +1 -2
  269. uncountable/types/phases.py +0 -1
  270. uncountable/types/phases_t.py +5 -2
  271. uncountable/types/post_base.py +0 -1
  272. uncountable/types/post_base_t.py +1 -2
  273. uncountable/types/queued_job.py +17 -0
  274. uncountable/types/queued_job_t.py +140 -0
  275. uncountable/types/recipe_identifiers.py +0 -1
  276. uncountable/types/recipe_identifiers_t.py +21 -8
  277. uncountable/types/recipe_inputs.py +0 -1
  278. uncountable/types/recipe_inputs_t.py +1 -2
  279. uncountable/types/recipe_links.py +0 -1
  280. uncountable/types/recipe_links_t.py +7 -4
  281. uncountable/types/recipe_metadata.py +0 -1
  282. uncountable/types/recipe_metadata_t.py +14 -9
  283. uncountable/types/recipe_output_metadata.py +0 -1
  284. uncountable/types/recipe_output_metadata_t.py +5 -2
  285. uncountable/types/recipe_tags.py +0 -1
  286. uncountable/types/recipe_tags_t.py +5 -2
  287. uncountable/types/recipe_workflow_steps.py +0 -1
  288. uncountable/types/recipe_workflow_steps_t.py +14 -7
  289. uncountable/types/recipes.py +0 -1
  290. uncountable/types/recipes_t.py +6 -2
  291. uncountable/types/response.py +0 -1
  292. uncountable/types/response_t.py +3 -2
  293. uncountable/types/secret_retrieval.py +0 -1
  294. uncountable/types/secret_retrieval_t.py +13 -7
  295. uncountable/types/sockets.py +20 -0
  296. uncountable/types/sockets_t.py +169 -0
  297. uncountable/types/structured_filters.py +25 -0
  298. uncountable/types/structured_filters_t.py +248 -0
  299. uncountable/types/units.py +0 -1
  300. uncountable/types/units_t.py +5 -2
  301. uncountable/types/uploader.py +24 -0
  302. uncountable/types/uploader_t.py +222 -0
  303. uncountable/types/users.py +0 -1
  304. uncountable/types/users_t.py +5 -2
  305. uncountable/types/webhook_job.py +9 -0
  306. uncountable/types/webhook_job_t.py +48 -0
  307. uncountable/types/workflows.py +0 -1
  308. uncountable/types/workflows_t.py +10 -4
  309. uncountablepythonsdk-0.0.131.dist-info/METADATA +64 -0
  310. uncountablepythonsdk-0.0.131.dist-info/RECORD +363 -0
  311. {UncountablePythonSDK-0.0.52.dist-info → uncountablepythonsdk-0.0.131.dist-info}/WHEEL +1 -1
  312. UncountablePythonSDK-0.0.52.dist-info/METADATA +0 -56
  313. UncountablePythonSDK-0.0.52.dist-info/RECORD +0 -246
  314. docs/quickstart.md +0 -19
  315. uncountable/core/version.py +0 -11
  316. {UncountablePythonSDK-0.0.52.dist-info → uncountablepythonsdk-0.0.131.dist-info}/top_level.txt +0 -0
@@ -1,82 +1,24 @@
1
1
  import io
2
2
  import os
3
- from typing import Any
3
+ from typing import assert_never
4
4
 
5
5
  from . import builder, util
6
- from .builder import SpecTypeDefnObject
6
+ from .builder import EndpointKey, EndpointSpecificPath, PathMapping
7
7
  from .config import TypeScriptConfig
8
+ from .cross_output_links import get_path_links
8
9
  from .emit_io_ts import emit_type_io_ts
9
10
  from .emit_typescript_util import (
10
- INDENT,
11
11
  MODIFY_NOTICE,
12
12
  EmitTypescriptContext,
13
+ emit_constant_ts,
14
+ emit_namespace_imports_ts,
15
+ emit_type_ts,
16
+ resolve_namespace_name,
13
17
  resolve_namespace_ref,
14
- ts_name,
15
18
  ts_type_name,
16
19
  )
17
20
 
18
21
 
19
- def ts_enum_name(name: str, name_case: builder.NameCase) -> str:
20
- if name_case == builder.NameCase.js_upper:
21
- return name.upper()
22
- return ts_name(name, name_case)
23
-
24
-
25
- def _resolve_namespace_name(namespace: builder.SpecNamespace) -> str:
26
- return namespace.name
27
-
28
-
29
- def _emit_value(ctx: EmitTypescriptContext, stype: builder.SpecType, value: Any) -> str:
30
- """Mimics emit_python even if not all types are used in TypeScript yet"""
31
- literal = builder.unwrap_literal_type(stype)
32
- if literal is not None:
33
- return _emit_value(ctx, literal.value_type, literal.value)
34
-
35
- if stype.is_base_type(builder.BaseTypeName.s_string):
36
- assert isinstance(value, str)
37
- return util.encode_common_string(value)
38
- elif stype.is_base_type(builder.BaseTypeName.s_integer):
39
- assert isinstance(value, int)
40
- return str(value)
41
- elif stype.is_base_type(builder.BaseTypeName.s_boolean):
42
- assert isinstance(value, bool)
43
- return "true" if value else "false"
44
- elif stype.is_base_type(builder.BaseTypeName.s_lossy_decimal):
45
- return str(value)
46
- elif stype.is_base_type(builder.BaseTypeName.s_decimal):
47
- return f"'{value}'"
48
- elif isinstance(stype, builder.SpecTypeInstance):
49
- if stype.defn_type.is_base_type(builder.BaseTypeName.s_list):
50
- sub_type = stype.parameters[0]
51
- return "[" + ", ".join([_emit_value(ctx, sub_type, x) for x in value]) + "]"
52
-
53
- if stype.defn_type.is_base_type(builder.BaseTypeName.s_dict):
54
- key_type = stype.parameters[0]
55
- value_type = stype.parameters[1]
56
- return (
57
- "{\n\t"
58
- + ",\n\t".join(
59
- "["
60
- + _emit_value(ctx, key_type, dkey)
61
- + "]: "
62
- + _emit_value(ctx, value_type, dvalue)
63
- for dkey, dvalue in value.items()
64
- )
65
- + "\n}"
66
- )
67
-
68
- if stype.defn_type.is_base_type(builder.BaseTypeName.s_optional):
69
- sub_type = stype.parameters[0]
70
- if value is None:
71
- return "null"
72
- return _emit_value(ctx, sub_type, value)
73
-
74
- elif isinstance(stype, builder.SpecTypeDefnStringEnum):
75
- return f"{refer_to(ctx, stype)}.{ts_enum_name(value, stype.name_case)}"
76
-
77
- raise Exception("invalid constant type", value, stype)
78
-
79
-
80
22
  def emit_typescript(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
81
23
  _emit_types(builder, config)
82
24
  _emit_id_source(builder, config)
@@ -90,11 +32,16 @@ def _emit_types(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
90
32
 
91
33
  for namespace in sorted(
92
34
  builder.namespaces.values(),
93
- key=lambda ns: _resolve_namespace_name(ns),
35
+ key=lambda ns: resolve_namespace_name(ns),
94
36
  ):
95
- ctx = EmitTypescriptContext(out=io.StringIO(), namespace=namespace, config=config)
37
+ ctx = EmitTypescriptContext(
38
+ out=io.StringIO(),
39
+ namespace=namespace,
40
+ cross_output_paths=builder.cross_output_paths,
41
+ api_endpoints=builder.api_endpoints,
42
+ )
96
43
 
97
- _emit_namespace(ctx, namespace)
44
+ _emit_namespace(ctx, config, namespace)
98
45
 
99
46
  prepart = builder.preparts["typescript"].get(namespace.name)
100
47
  part = builder.parts["typescript"].get(namespace.name)
@@ -107,7 +54,10 @@ def _emit_types(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
107
54
  and len(namespace.constants) == 0
108
55
  ):
109
56
  # Try to capture some common incompleteness errors
110
- if namespace.endpoint is None or namespace.endpoint.function is None:
57
+ if namespace.endpoint is None or any(
58
+ endpoint_specific_path.function is None
59
+ for endpoint_specific_path in namespace.endpoint.path_per_api_endpoint.values()
60
+ ):
111
61
  raise Exception(
112
62
  f"Namespace {'/'.join(namespace.path)} is incomplete. It should have an endpoint with function, types, and/or constants"
113
63
  )
@@ -121,16 +71,7 @@ def _emit_types(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
121
71
  full.write(f"// === END section from {namespace.name}.ts.prepart ===\n")
122
72
  full.write("\n")
123
73
 
124
- for ns in sorted(
125
- ctx.namespaces,
126
- key=lambda name: _resolve_namespace_name(name),
127
- ):
128
- import_as = resolve_namespace_ref(ns)
129
- import_path = (
130
- "./" if len(namespace.path) == 1 else "../" * (len(namespace.path) - 1)
131
- )
132
- import_from = f"{import_path}{_resolve_namespace_name(ns)}"
133
- full.write(f'import * as {import_as} from "{import_from}"\n') # noqa: E501
74
+ emit_namespace_imports_ts(ctx.namespaces, out=full, current_namespace=namespace)
134
75
  if namespace.emit_io_ts:
135
76
  full.write("import * as IO from 'io-ts';")
136
77
  full.write(ctx.out.getvalue())
@@ -139,6 +80,7 @@ def _emit_types(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
139
80
  full.write("\n")
140
81
  full.write(MODIFY_NOTICE)
141
82
  full.write(f"// === START section from {namespace.name}.ts.part ===\n")
83
+ full.write("\n")
142
84
  full.write(part)
143
85
  full.write(f"// === END section from {namespace.name}.ts.part ===\n")
144
86
 
@@ -150,7 +92,7 @@ def _emit_types(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
150
92
 
151
93
  if len(namespace.path) == 1:
152
94
  index_out.write(
153
- f"import * as {resolve_namespace_ref(namespace)} from './{_resolve_namespace_name(namespace)}'\n"
95
+ f"import * as {resolve_namespace_ref(namespace)} from './{resolve_namespace_name(namespace)}'\n"
154
96
  ) # noqa: E501
155
97
  index_out_end.write(f"export {{{resolve_namespace_ref(namespace)}}}\n")
156
98
 
@@ -161,22 +103,27 @@ def _emit_types(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
161
103
  util.rewrite_file(f"{config.types_output}/index.ts", index_out.getvalue())
162
104
 
163
105
 
164
- def _emit_namespace(ctx: EmitTypescriptContext, namespace: builder.SpecNamespace) -> None:
106
+ def _emit_namespace(
107
+ ctx: EmitTypescriptContext,
108
+ config: TypeScriptConfig,
109
+ namespace: builder.SpecNamespace,
110
+ ) -> None:
165
111
  for stype in namespace.types.values():
166
112
  if namespace.emit_io_ts:
167
113
  emit_type_io_ts(ctx, stype, namespace.derive_types_from_io_ts)
168
114
  if not namespace.emit_io_ts or not namespace.derive_types_from_io_ts:
169
- _emit_type(ctx, stype)
115
+ emit_type_ts(ctx, stype)
170
116
 
171
117
  for sconst in namespace.constants.values():
172
- _emit_constant(ctx, sconst)
118
+ emit_constant_ts(ctx, sconst)
173
119
 
174
120
  if namespace.endpoint is not None:
175
- _emit_endpoint(ctx, namespace, namespace.endpoint)
121
+ _emit_endpoint(ctx, config, namespace, namespace.endpoint)
176
122
 
177
123
 
178
124
  def _emit_endpoint(
179
125
  ctx: EmitTypescriptContext,
126
+ config: TypeScriptConfig,
180
127
  namespace: builder.SpecNamespace,
181
128
  endpoint: builder.SpecEndpoint,
182
129
  ) -> None:
@@ -188,6 +135,7 @@ def _emit_endpoint(
188
135
  has_data = "Data" in namespace.types
189
136
  has_deprecated_result = "DeprecatedResult" in namespace.types
190
137
  is_binary = endpoint.result_type == builder.ResultType.binary
138
+ has_multiple_endpoints = len(endpoint.path_per_api_endpoint) > 1
191
139
 
192
140
  result_type_count = sum([has_data, has_deprecated_result, is_binary])
193
141
  assert result_type_count < 2
@@ -199,6 +147,13 @@ def _emit_endpoint(
199
147
  if not is_binary:
200
148
  assert endpoint.result_type == builder.ResultType.json
201
149
 
150
+ paths_string = get_path_links(
151
+ ctx.cross_output_paths,
152
+ namespace,
153
+ current_path_type="TypeScript",
154
+ endpoint=endpoint,
155
+ )
156
+
202
157
  data_loader_head = ""
203
158
  data_loader_body = ""
204
159
  if endpoint.data_loader:
@@ -206,7 +161,7 @@ def _emit_endpoint(
206
161
  assert has_data
207
162
 
208
163
  data_loader_head = (
209
- 'import { buildApiDataLoader, argsKey } from "unc_base/data_manager"\n'
164
+ 'import { argsKey, buildApiDataLoader } from "unc_base/data_manager"\n'
210
165
  )
211
166
  data_loader_body = (
212
167
  "\nexport const data = buildApiDataLoader(argsKey(), apiCall)\n"
@@ -222,24 +177,49 @@ def _emit_endpoint(
222
177
  wrap_call = (
223
178
  f"{wrap_name}<Arguments>" if is_binary else f"{wrap_name}<Arguments, Response>"
224
179
  )
225
- type_path = f"unc_mat/types/{'/'.join(namespace.path)}"
180
+
181
+ unc_base_api_imports = (
182
+ f"appSpecificApiPath, {wrap_name}" if has_multiple_endpoints else wrap_name
183
+ )
184
+ path_mapping = ctx.api_endpoints[endpoint.default_endpoint_key].path_mapping
185
+
186
+ match path_mapping:
187
+ case PathMapping.NO_MAPPING:
188
+ path_mapping_part = (
189
+ "\n { pathMapping: ApplicationT.APIPathMapping.noMapping },"
190
+ )
191
+ case PathMapping.DEFAULT_MAPPING:
192
+ path_mapping_part = ""
193
+ case _:
194
+ assert_never(path_mapping)
195
+
196
+ unc_types_imports = (
197
+ 'import { ApplicationT } from "unc_types"\n'
198
+ if has_multiple_endpoints or path_mapping_part != ""
199
+ else ""
200
+ )
201
+
202
+ type_path = f"unc_types/{'/'.join(namespace.path)}"
226
203
 
227
204
  if is_binary:
228
- tsx_response_part = f"""import {{ {wrap_name} }} from "unc_base/api"
229
- import type {{ Arguments }} from "{type_path}"
205
+ tsx_response_head = f"""import {{ {unc_base_api_imports} }} from "unc_base/api"
206
+ """
207
+ tsx_response_part = f"""import type {{ Arguments }} from "{type_path}"
230
208
 
231
209
  export type {{ Arguments }}
232
210
  """
233
211
  elif has_data and endpoint.has_attachment:
234
- tsx_response_part = f"""import {{ {wrap_name}, type AttachmentResponse }} from "unc_base/api"
235
- import type {{ Arguments, Data }} from "{type_path}"
212
+ tsx_response_head = f"""import {{ type AttachmentResponse, {unc_base_api_imports} }} from "unc_base/api"
213
+ """
214
+ tsx_response_part = f"""import type {{ Arguments, Data }} from "{type_path}"
236
215
 
237
216
  export type {{ Arguments, Data }}
238
217
  export type Response = AttachmentResponse<Data>
239
218
  """
240
219
  elif has_data:
241
- tsx_response_part = f"""import {{ {wrap_name}, type JsonResponse }} from "unc_base/api"
242
- import type {{ Arguments, Data }} from "{type_path}"
220
+ tsx_response_head = f"""import {{ {unc_base_api_imports}, type JsonResponse }} from "unc_base/api"
221
+ """
222
+ tsx_response_part = f"""import type {{ Arguments, Data }} from "{type_path}"
243
223
 
244
224
  export type {{ Arguments, Data }}
245
225
  export type Response = JsonResponse<Data>
@@ -247,196 +227,77 @@ export type Response = JsonResponse<Data>
247
227
 
248
228
  else:
249
229
  assert has_deprecated_result
250
- tsx_response_part = f"""import {{ {wrap_name} }} from "unc_base/api"
251
- import type {{ Arguments, DeprecatedResult }} from "{type_path}"
230
+ tsx_response_head = f"""import {{ {unc_base_api_imports} }} from "unc_base/api"
231
+ """
232
+ tsx_response_part = f"""import type {{ Arguments, DeprecatedResult }} from "{type_path}"
252
233
 
253
234
  export type {{ Arguments }}
254
235
  export type Response = DeprecatedResult
255
236
  """
256
237
 
257
- tsx_api = f"""{MODIFY_NOTICE}
258
- {data_loader_head}{tsx_response_part}
238
+ """
239
+
240
+ export const apiCall = buildWrappedGetCall<Arguments, Response>(
241
+ appSpecificApiPath({
242
+ [ApplicationT.FrontendApplication.materials]: "api/materials/common/list_id_source",
243
+ }),
244
+ )
245
+
246
+
247
+ """
248
+
249
+ if not has_multiple_endpoints:
250
+ default_endpoint_path = endpoint.path_per_api_endpoint[
251
+ endpoint.default_endpoint_key
252
+ ]
253
+ endpoint_path_part = f'"{default_endpoint_path.path_root}/{default_endpoint_path.path_dirname}/{default_endpoint_path.path_basename}",'
254
+ else:
255
+ path_lookup_map = ""
256
+ api_endpoint_key: EndpointKey
257
+ endpoint_specific_path: EndpointSpecificPath
258
+ for (
259
+ api_endpoint_key,
260
+ endpoint_specific_path,
261
+ ) in endpoint.path_per_api_endpoint.items():
262
+ full_path = f"{endpoint_specific_path.path_root}/{endpoint_specific_path.path_dirname}/{endpoint_specific_path.path_basename}"
263
+ frontend_app_value = config.endpoint_to_frontend_app_type[api_endpoint_key]
264
+
265
+ path_lookup_map += (
266
+ f'\n [ApplicationT.{frontend_app_value}]: "{full_path}",'
267
+ )
268
+
269
+ endpoint_path_part = f"""appSpecificApiPath({{{path_lookup_map}
270
+ }}),"""
271
+
272
+ # tsx_api = f"""{MODIFY_NOTICE}
273
+ tsx_api = f"""{MODIFY_NOTICE}{paths_string}
274
+ {tsx_response_head}{data_loader_head}{unc_types_imports}{tsx_response_part}
259
275
  export const apiCall = {wrap_call}(
260
- "{endpoint.path_root}/{endpoint.path_dirname}/{endpoint.path_basename}",
276
+ {endpoint_path_part}{path_mapping_part}
261
277
  )
262
278
  {data_loader_body}"""
263
279
 
264
- output = f"{ctx.config.routes_output}/{'/'.join(namespace.path)}.tsx"
280
+ output = f"{config.endpoint_to_routes_output[endpoint.default_endpoint_key]}/{'/'.join(namespace.path)}.tsx"
265
281
  util.rewrite_file(output, tsx_api)
266
282
 
267
283
  # Hacky index support, until enough is migrated to regen entirely
268
284
  # Emits the import into the UI API index file
269
- index_path = f"{ctx.config.routes_output}/{'/'.join(namespace.path[0:-1])}/index.tsx"
285
+ index_path = f"{config.endpoint_to_routes_output[endpoint.default_endpoint_key]}/{'/'.join(namespace.path[0:-1])}/index.tsx"
270
286
  api_name = f"Api{ts_type_name(namespace.path[0 - 1])}"
271
287
  if os.path.exists(index_path):
272
- with open(index_path) as index:
288
+ with open(index_path, encoding="utf-8") as index:
273
289
  index_data = index.read()
274
290
  need_index = index_data.find(api_name) == -1
275
291
  else:
276
292
  need_index = True
277
293
 
278
294
  if need_index:
279
- with open(index_path, "a") as index:
295
+ with open(index_path, "a", encoding="utf-8") as index:
280
296
  print(f"Updated API Index {index_path}")
281
297
  index.write(f'import * as {api_name} from "./{namespace.path[-1]}"\n\n')
282
298
  index.write(f"export {{ {api_name} }}\n")
283
299
 
284
300
 
285
- def _emit_type(ctx: EmitTypescriptContext, stype: builder.SpecType) -> None:
286
- if not isinstance(stype, builder.SpecTypeDefn):
287
- return
288
-
289
- if stype.is_base or stype.is_predefined:
290
- return
291
-
292
- ctx.out.write("\n")
293
- ctx.out.write(MODIFY_NOTICE)
294
-
295
- if isinstance(stype, builder.SpecTypeDefnExternal):
296
- assert not stype.is_exported, "expecting private names"
297
- ctx.out.write(stype.external_map["ts"])
298
- ctx.out.write("\n")
299
- return
300
-
301
- assert stype.is_exported, "expecting exported names"
302
- if isinstance(stype, builder.SpecTypeDefnAlias):
303
- ctx.out.write(f"export type {stype.name} = {refer_to(ctx, stype.alias)}\n")
304
- return
305
-
306
- if isinstance(stype, builder.SpecTypeDefnUnion):
307
- ctx.out.write(
308
- f"export type {stype.name} = {refer_to(ctx, stype.get_backing_type())}\n"
309
- )
310
- return
311
-
312
- if isinstance(stype, builder.SpecTypeDefnStringEnum):
313
- ctx.out.write(f"export enum {stype.name} {{\n")
314
- assert stype.values
315
- for name, entry in stype.values.items():
316
- ctx.out.write(
317
- f'{INDENT}{ts_enum_name(name, stype.name_case)} = "{entry.value}",\n'
318
- )
319
- ctx.out.write("}\n")
320
- return
321
-
322
- assert isinstance(stype, builder.SpecTypeDefnObject)
323
- assert stype.base is not None
324
-
325
- base_type = ""
326
- if not stype.base.is_base:
327
- base_type = f"{refer_to(ctx, stype.base)} & "
328
-
329
- if stype.properties is None and base_type == "":
330
- ctx.out.write(f"export type {stype.name} = TEmpty\n")
331
- elif stype.properties is None:
332
- ctx.out.write(f"export type {stype.name} = {base_type}{{}}\n")
333
- else:
334
- if isinstance(stype, SpecTypeDefnObject) and len(stype.parameters) > 0:
335
- full_type_name = f'{stype.name}<{", ".join(stype.parameters)}>'
336
- else:
337
- full_type_name = stype.name
338
- ctx.out.write(f"export type {full_type_name} = {base_type}{{")
339
- ctx.out.write("\n")
340
- for prop in stype.properties.values():
341
- ref_type = refer_to(ctx, prop.spec_type)
342
- prop_name = ts_name(prop.name, prop.name_case)
343
- if prop.has_default and not prop.parse_require:
344
- # For now, we'll assume the generated types with defaults are meant as
345
- # arguments, thus treat like extant==missing
346
- # IMPROVE: if we can decide they are meant as output instead, then
347
- # they should be marked as required
348
- ctx.out.write(f"{INDENT}{prop_name}?: {ref_type}")
349
- elif prop.extant == builder.PropertyExtant.missing:
350
- # Unlike optional below, missing does not imply null is possible. They
351
- # treated distinctly.
352
- ctx.out.write(f"{INDENT}{prop_name}?: {ref_type}")
353
- elif prop.extant == builder.PropertyExtant.optional:
354
- # Need to add in |null since Python side can produce null's right now
355
- # IMPROVE: It would be better if the serializer could instead omit the None's
356
- # Dropping the null should be forward compatible
357
- ctx.out.write(f"{INDENT}{prop_name}?: {ref_type} | null")
358
- else:
359
- ctx.out.write(f"{INDENT}{prop_name}: {ref_type}")
360
- ctx.out.write("\n")
361
- ctx.out.write("}\n")
362
-
363
-
364
- def _emit_constant(ctx: EmitTypescriptContext, sconst: builder.SpecConstant) -> None:
365
- ctx.out.write("\n\n")
366
- ctx.out.write(MODIFY_NOTICE)
367
- value = _emit_value(ctx, sconst.value_type, sconst.value)
368
- const_name = sconst.name.upper()
369
- ctx.out.write(f"export const {const_name} = {value}\n")
370
-
371
-
372
- base_name_map = {
373
- builder.BaseTypeName.s_boolean: "boolean",
374
- builder.BaseTypeName.s_date: "string", # IMPROVE: Aliased DateStr
375
- builder.BaseTypeName.s_date_time: "string", # IMPROVE: Aliased DateTimeStr
376
- # Decimal's are marked as to_string_values thus are strings in the front-end
377
- builder.BaseTypeName.s_decimal: "string",
378
- builder.BaseTypeName.s_dict: "PartialRecord",
379
- builder.BaseTypeName.s_integer: "number",
380
- builder.BaseTypeName.s_lossy_decimal: "number",
381
- builder.BaseTypeName.s_opaque_key: "string",
382
- builder.BaseTypeName.s_none: "null",
383
- builder.BaseTypeName.s_string: "string",
384
- # UNC: global types
385
- builder.BaseTypeName.s_json_value: "JsonValue",
386
- }
387
-
388
-
389
- def refer_to(ctx: EmitTypescriptContext, stype: builder.SpecType) -> str:
390
- return refer_to_impl(ctx, stype)[0]
391
-
392
-
393
- def refer_to_impl(
394
- ctx: EmitTypescriptContext, stype: builder.SpecType
395
- ) -> tuple[str, bool]:
396
- """
397
- @return (string-specific, multiple-types)
398
- """
399
- if isinstance(stype, builder.SpecTypeInstance):
400
- if stype.defn_type.name == builder.BaseTypeName.s_list:
401
- spec, multi = refer_to_impl(ctx, stype.parameters[0])
402
- return f"({spec})[]" if multi else f"{spec}[]", False
403
- if stype.defn_type.name == builder.BaseTypeName.s_readonly_array:
404
- spec, multi = refer_to_impl(ctx, stype.parameters[0])
405
- return f"readonly ({spec})[]" if multi else f"readonly {spec}[]", False
406
- if stype.defn_type.name == builder.BaseTypeName.s_union:
407
- return f'({" | ".join([refer_to(ctx, p) for p in stype.parameters])})', False
408
- if stype.defn_type.name == builder.BaseTypeName.s_literal:
409
- parts = []
410
- for parameter in stype.parameters:
411
- assert isinstance(parameter, builder.SpecTypeLiteralWrapper)
412
- parts.append(refer_to(ctx, parameter))
413
- return f'({" | ".join(parts)})', False
414
- if stype.defn_type.name == builder.BaseTypeName.s_optional:
415
- return f"{refer_to(ctx, stype.parameters[0])} | null", True
416
- if stype.defn_type.name == builder.BaseTypeName.s_tuple:
417
- return f"[{', '.join([refer_to(ctx, p) for p in stype.parameters])}]", False
418
- params = ", ".join([refer_to(ctx, p) for p in stype.parameters])
419
- return f"{refer_to(ctx, stype.defn_type)}<{params}>", False
420
-
421
- if isinstance(stype, builder.SpecTypeLiteralWrapper):
422
- return _emit_value(ctx, stype.value_type, stype.value), False
423
-
424
- if isinstance(stype, builder.SpecTypeGenericParameter):
425
- return stype.name, False
426
-
427
- assert isinstance(stype, builder.SpecTypeDefn)
428
- if stype.is_base: # assume correct namespace
429
- if stype.name == builder.BaseTypeName.s_list:
430
- return "any[]", False # TODO: generic type
431
- return base_name_map[builder.BaseTypeName(stype.name)], False
432
-
433
- if stype.namespace == ctx.namespace:
434
- return stype.name, False
435
-
436
- ctx.namespaces.add(stype.namespace)
437
- return f"{resolve_namespace_ref(stype.namespace)}.{stype.name}", False
438
-
439
-
440
301
  def _emit_id_source(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
441
302
  id_source_output = config.id_source_output
442
303
  if id_source_output is None: