UncountablePythonSDK 0.0.82__py3-none-any.whl → 0.0.132__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.
- docs/conf.py +54 -7
- docs/index.md +107 -4
- docs/integration_examples/create_ingredient.md +43 -0
- docs/integration_examples/create_output.md +56 -0
- docs/integration_examples/index.md +6 -0
- docs/justfile +2 -2
- docs/requirements.txt +6 -4
- examples/basic_auth.py +7 -0
- examples/create_ingredient_sdk.py +34 -0
- examples/download_files.py +26 -0
- examples/integration-server/jobs/materials_auto/concurrent_cron.py +11 -0
- examples/integration-server/jobs/materials_auto/example_cron.py +3 -0
- examples/integration-server/jobs/materials_auto/example_http.py +47 -0
- examples/integration-server/jobs/materials_auto/example_instrument.py +100 -0
- examples/integration-server/jobs/materials_auto/example_parse.py +140 -0
- examples/integration-server/jobs/materials_auto/example_predictions.py +61 -0
- examples/integration-server/jobs/materials_auto/example_runsheet_wh.py +39 -0
- examples/integration-server/jobs/materials_auto/example_wh.py +17 -9
- examples/integration-server/jobs/materials_auto/profile.yaml +61 -0
- examples/integration-server/pyproject.toml +10 -10
- examples/oauth.py +7 -0
- examples/set_recipe_metadata_file.py +1 -1
- examples/upload_files.py +1 -2
- pkgs/argument_parser/__init__.py +8 -0
- pkgs/argument_parser/_is_namedtuple.py +3 -0
- pkgs/argument_parser/argument_parser.py +196 -63
- pkgs/filesystem_utils/__init__.py +1 -0
- pkgs/filesystem_utils/_blob_session.py +144 -0
- pkgs/filesystem_utils/_gdrive_session.py +5 -5
- pkgs/filesystem_utils/_s3_session.py +2 -1
- pkgs/filesystem_utils/_sftp_session.py +6 -3
- pkgs/filesystem_utils/file_type_utils.py +30 -10
- pkgs/serialization/__init__.py +7 -2
- pkgs/serialization/annotation.py +64 -0
- pkgs/serialization/missing_sentry.py +1 -1
- pkgs/serialization/opaque_key.py +1 -1
- pkgs/serialization/serial_alias.py +47 -0
- pkgs/serialization/serial_class.py +40 -48
- pkgs/serialization/serial_generic.py +16 -0
- pkgs/serialization/serial_union.py +16 -16
- pkgs/serialization_util/__init__.py +6 -0
- pkgs/serialization_util/dataclasses.py +14 -0
- pkgs/serialization_util/serialization_helpers.py +15 -5
- pkgs/type_spec/actions_registry/__main__.py +0 -4
- pkgs/type_spec/actions_registry/emit_typescript.py +2 -4
- pkgs/type_spec/builder.py +248 -70
- pkgs/type_spec/builder_types.py +9 -0
- pkgs/type_spec/config.py +40 -7
- pkgs/type_spec/cross_output_links.py +99 -0
- pkgs/type_spec/emit_open_api.py +121 -34
- pkgs/type_spec/emit_open_api_util.py +5 -5
- pkgs/type_spec/emit_python.py +277 -86
- pkgs/type_spec/emit_typescript.py +102 -29
- pkgs/type_spec/emit_typescript_util.py +66 -10
- pkgs/type_spec/load_types.py +16 -3
- pkgs/type_spec/non_discriminated_union_exceptions.py +14 -0
- pkgs/type_spec/open_api_util.py +29 -4
- pkgs/type_spec/parts/base.py.prepart +11 -8
- pkgs/type_spec/parts/base.ts.prepart +4 -0
- pkgs/type_spec/type_info/__main__.py +3 -1
- pkgs/type_spec/type_info/emit_type_info.py +115 -22
- pkgs/type_spec/ui_entry_actions/__init__.py +4 -0
- pkgs/type_spec/ui_entry_actions/generate_ui_entry_actions.py +308 -0
- pkgs/type_spec/util.py +3 -3
- pkgs/type_spec/value_spec/__main__.py +26 -9
- pkgs/type_spec/value_spec/convert_type.py +18 -0
- pkgs/type_spec/value_spec/emit_python.py +13 -3
- pkgs/type_spec/value_spec/types.py +1 -1
- uncountable/core/async_batch.py +1 -1
- uncountable/core/client.py +133 -34
- uncountable/core/environment.py +3 -3
- uncountable/core/file_upload.py +39 -15
- uncountable/integration/cli.py +116 -23
- uncountable/integration/construct_client.py +3 -3
- uncountable/integration/executors/executors.py +12 -2
- uncountable/integration/executors/generic_upload_executor.py +66 -14
- uncountable/integration/http_server/__init__.py +5 -0
- uncountable/integration/http_server/types.py +69 -0
- uncountable/integration/job.py +192 -7
- uncountable/integration/queue_runner/command_server/__init__.py +4 -0
- uncountable/integration/queue_runner/command_server/command_client.py +65 -0
- uncountable/integration/queue_runner/command_server/command_server.py +83 -5
- uncountable/integration/queue_runner/command_server/constants.py +4 -0
- uncountable/integration/queue_runner/command_server/protocol/command_server.proto +36 -0
- uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.py +28 -11
- uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.pyi +77 -1
- uncountable/integration/queue_runner/command_server/protocol/command_server_pb2_grpc.py +135 -0
- uncountable/integration/queue_runner/command_server/types.py +25 -2
- uncountable/integration/queue_runner/datastore/datastore_sqlite.py +168 -11
- uncountable/integration/queue_runner/datastore/interface.py +10 -0
- uncountable/integration/queue_runner/datastore/model.py +8 -1
- uncountable/integration/queue_runner/job_scheduler.py +63 -23
- uncountable/integration/queue_runner/queue_runner.py +10 -2
- uncountable/integration/queue_runner/worker.py +22 -17
- uncountable/integration/scan_profiles.py +1 -1
- uncountable/integration/scheduler.py +74 -25
- uncountable/integration/secret_retrieval/retrieve_secret.py +1 -1
- uncountable/integration/server.py +42 -12
- uncountable/integration/telemetry.py +63 -10
- uncountable/integration/webhook_server/entrypoint.py +39 -112
- uncountable/types/__init__.py +58 -1
- uncountable/types/api/batch/execute_batch.py +5 -6
- uncountable/types/api/batch/execute_batch_load_async.py +2 -3
- uncountable/types/api/chemical/convert_chemical_formats.py +10 -5
- uncountable/types/api/condition_parameters/__init__.py +1 -0
- uncountable/types/api/condition_parameters/upsert_condition_match.py +72 -0
- uncountable/types/api/entity/create_entities.py +7 -7
- uncountable/types/api/entity/create_entity.py +8 -8
- uncountable/types/api/entity/create_or_update_entity.py +48 -0
- uncountable/types/api/entity/export_entities.py +59 -0
- uncountable/types/api/entity/get_entities_data.py +3 -4
- uncountable/types/api/entity/grant_entity_permissions.py +6 -6
- uncountable/types/api/entity/list_aggregate.py +79 -0
- uncountable/types/api/entity/list_entities.py +34 -10
- uncountable/types/api/entity/lock_entity.py +4 -4
- uncountable/types/api/entity/lookup_entity.py +116 -0
- uncountable/types/api/entity/resolve_entity_ids.py +5 -6
- uncountable/types/api/entity/set_entity_field_values.py +44 -0
- uncountable/types/api/entity/set_values.py +3 -3
- uncountable/types/api/entity/transition_entity_phase.py +14 -7
- uncountable/types/api/entity/unlock_entity.py +3 -3
- uncountable/types/api/equipment/associate_equipment_input.py +2 -3
- uncountable/types/api/field_options/upsert_field_options.py +7 -7
- uncountable/types/api/files/__init__.py +1 -0
- uncountable/types/api/files/download_file.py +77 -0
- uncountable/types/api/id_source/list_id_source.py +6 -7
- uncountable/types/api/id_source/match_id_source.py +4 -5
- uncountable/types/api/input_groups/get_input_group_names.py +3 -4
- uncountable/types/api/inputs/create_inputs.py +10 -9
- uncountable/types/api/inputs/get_input_data.py +11 -12
- uncountable/types/api/inputs/get_input_names.py +6 -7
- uncountable/types/api/inputs/get_inputs_data.py +6 -7
- uncountable/types/api/inputs/set_input_attribute_values.py +5 -6
- uncountable/types/api/inputs/set_input_category.py +5 -5
- uncountable/types/api/inputs/set_input_subcategories.py +3 -3
- uncountable/types/api/inputs/set_intermediate_type.py +4 -4
- uncountable/types/api/integrations/__init__.py +1 -0
- uncountable/types/api/integrations/publish_realtime_data.py +41 -0
- uncountable/types/api/integrations/push_notification.py +49 -0
- uncountable/types/api/integrations/register_sockets_token.py +41 -0
- uncountable/types/api/listing/__init__.py +1 -0
- uncountable/types/api/listing/fetch_listing.py +58 -0
- uncountable/types/api/material_families/update_entity_material_families.py +3 -4
- uncountable/types/api/notebooks/__init__.py +1 -0
- uncountable/types/api/notebooks/add_notebook_content.py +119 -0
- uncountable/types/api/outputs/get_output_data.py +12 -13
- uncountable/types/api/outputs/get_output_names.py +5 -6
- uncountable/types/api/outputs/get_output_organization.py +173 -0
- uncountable/types/api/outputs/resolve_output_conditions.py +7 -8
- uncountable/types/api/permissions/set_core_permissions.py +16 -10
- uncountable/types/api/project/get_projects.py +6 -7
- uncountable/types/api/project/get_projects_data.py +7 -8
- uncountable/types/api/recipe_links/create_recipe_link.py +5 -5
- uncountable/types/api/recipe_links/remove_recipe_link.py +4 -4
- uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py +6 -7
- uncountable/types/api/recipes/add_recipe_to_project.py +3 -3
- uncountable/types/api/recipes/add_time_series_data.py +64 -0
- uncountable/types/api/recipes/archive_recipes.py +4 -4
- uncountable/types/api/recipes/associate_recipe_as_input.py +5 -5
- uncountable/types/api/recipes/associate_recipe_as_lot.py +3 -3
- uncountable/types/api/recipes/clear_recipe_outputs.py +3 -3
- uncountable/types/api/recipes/create_mix_order.py +44 -0
- uncountable/types/api/recipes/create_recipe.py +8 -9
- uncountable/types/api/recipes/create_recipes.py +8 -9
- uncountable/types/api/recipes/disassociate_recipe_as_input.py +3 -3
- uncountable/types/api/recipes/edit_recipe_inputs.py +101 -24
- uncountable/types/api/recipes/get_column_calculation_values.py +4 -5
- uncountable/types/api/recipes/get_curve.py +4 -5
- uncountable/types/api/recipes/get_recipe_calculations.py +6 -7
- uncountable/types/api/recipes/get_recipe_links.py +3 -4
- uncountable/types/api/recipes/get_recipe_names.py +3 -4
- uncountable/types/api/recipes/get_recipe_output_metadata.py +5 -6
- uncountable/types/api/recipes/get_recipes_data.py +62 -34
- uncountable/types/api/recipes/lock_recipes.py +9 -8
- uncountable/types/api/recipes/remove_recipe_from_project.py +3 -3
- uncountable/types/api/recipes/set_recipe_inputs.py +9 -10
- uncountable/types/api/recipes/set_recipe_metadata.py +3 -3
- uncountable/types/api/recipes/set_recipe_output_annotations.py +11 -12
- uncountable/types/api/recipes/set_recipe_output_file.py +5 -6
- uncountable/types/api/recipes/set_recipe_outputs.py +24 -13
- uncountable/types/api/recipes/set_recipe_tags.py +14 -9
- uncountable/types/api/recipes/set_recipe_total.py +59 -0
- uncountable/types/api/recipes/unarchive_recipes.py +3 -3
- uncountable/types/api/recipes/unlock_recipes.py +7 -6
- uncountable/types/api/runsheet/__init__.py +1 -0
- uncountable/types/api/runsheet/complete_async_upload.py +41 -0
- uncountable/types/api/triggers/run_trigger.py +4 -4
- uncountable/types/api/uploader/complete_async_parse.py +46 -0
- uncountable/types/api/uploader/invoke_uploader.py +4 -5
- uncountable/types/api/user/__init__.py +1 -0
- uncountable/types/api/user/get_current_user_info.py +40 -0
- uncountable/types/async_batch.py +1 -1
- uncountable/types/async_batch_processor.py +506 -23
- uncountable/types/async_batch_t.py +35 -8
- uncountable/types/async_jobs.py +0 -1
- uncountable/types/async_jobs_t.py +1 -2
- uncountable/types/auth_retrieval.py +0 -1
- uncountable/types/auth_retrieval_t.py +6 -6
- uncountable/types/base.py +0 -1
- uncountable/types/base_t.py +11 -9
- uncountable/types/calculations.py +0 -1
- uncountable/types/calculations_t.py +1 -2
- uncountable/types/chemical_structure.py +0 -1
- uncountable/types/chemical_structure_t.py +5 -5
- uncountable/types/client_base.py +614 -69
- uncountable/types/client_config.py +1 -1
- uncountable/types/client_config_t.py +13 -3
- uncountable/types/curves.py +0 -1
- uncountable/types/curves_t.py +6 -7
- uncountable/types/data.py +12 -0
- uncountable/types/data_t.py +103 -0
- uncountable/types/entity.py +1 -1
- uncountable/types/entity_t.py +90 -10
- uncountable/types/experiment_groups.py +0 -1
- uncountable/types/experiment_groups_t.py +1 -2
- uncountable/types/exports.py +8 -0
- uncountable/types/exports_t.py +34 -0
- uncountable/types/field_values.py +19 -1
- uncountable/types/field_values_t.py +242 -9
- uncountable/types/fields.py +0 -1
- uncountable/types/fields_t.py +1 -2
- uncountable/types/generic_upload.py +0 -1
- uncountable/types/generic_upload_t.py +14 -14
- uncountable/types/id_source.py +0 -1
- uncountable/types/id_source_t.py +13 -7
- uncountable/types/identifier.py +0 -1
- uncountable/types/identifier_t.py +10 -5
- uncountable/types/input_attributes.py +0 -1
- uncountable/types/input_attributes_t.py +3 -4
- uncountable/types/inputs.py +0 -1
- uncountable/types/inputs_t.py +3 -4
- uncountable/types/integration_server.py +0 -1
- uncountable/types/integration_server_t.py +13 -4
- uncountable/types/integration_session.py +10 -0
- uncountable/types/integration_session_t.py +60 -0
- uncountable/types/integrations.py +10 -0
- uncountable/types/integrations_t.py +62 -0
- uncountable/types/job_definition.py +2 -1
- uncountable/types/job_definition_t.py +57 -32
- uncountable/types/listing.py +9 -0
- uncountable/types/listing_t.py +51 -0
- uncountable/types/notices.py +8 -0
- uncountable/types/notices_t.py +37 -0
- uncountable/types/notifications.py +11 -0
- uncountable/types/notifications_t.py +74 -0
- uncountable/types/outputs.py +0 -1
- uncountable/types/outputs_t.py +2 -3
- uncountable/types/overrides.py +0 -1
- uncountable/types/overrides_t.py +10 -4
- uncountable/types/permissions.py +0 -1
- uncountable/types/permissions_t.py +1 -2
- uncountable/types/phases.py +0 -1
- uncountable/types/phases_t.py +1 -2
- uncountable/types/post_base.py +0 -1
- uncountable/types/post_base_t.py +1 -2
- uncountable/types/queued_job.py +2 -1
- uncountable/types/queued_job_t.py +29 -12
- uncountable/types/recipe_identifiers.py +0 -1
- uncountable/types/recipe_identifiers_t.py +18 -8
- uncountable/types/recipe_inputs.py +0 -1
- uncountable/types/recipe_inputs_t.py +1 -2
- uncountable/types/recipe_links.py +0 -1
- uncountable/types/recipe_links_t.py +3 -4
- uncountable/types/recipe_metadata.py +0 -1
- uncountable/types/recipe_metadata_t.py +9 -10
- uncountable/types/recipe_output_metadata.py +0 -1
- uncountable/types/recipe_output_metadata_t.py +1 -2
- uncountable/types/recipe_tags.py +0 -1
- uncountable/types/recipe_tags_t.py +1 -2
- uncountable/types/recipe_workflow_steps.py +0 -1
- uncountable/types/recipe_workflow_steps_t.py +7 -7
- uncountable/types/recipes.py +0 -1
- uncountable/types/recipes_t.py +2 -2
- uncountable/types/response.py +0 -1
- uncountable/types/response_t.py +2 -2
- uncountable/types/secret_retrieval.py +0 -1
- uncountable/types/secret_retrieval_t.py +7 -7
- uncountable/types/sockets.py +20 -0
- uncountable/types/sockets_t.py +169 -0
- uncountable/types/structured_filters.py +25 -0
- uncountable/types/structured_filters_t.py +248 -0
- uncountable/types/units.py +0 -1
- uncountable/types/units_t.py +1 -2
- uncountable/types/uploader.py +24 -0
- uncountable/types/uploader_t.py +222 -0
- uncountable/types/users.py +0 -1
- uncountable/types/users_t.py +1 -2
- uncountable/types/webhook_job.py +1 -1
- uncountable/types/webhook_job_t.py +14 -3
- uncountable/types/workflows.py +0 -1
- uncountable/types/workflows_t.py +3 -4
- uncountablepythonsdk-0.0.132.dist-info/METADATA +64 -0
- uncountablepythonsdk-0.0.132.dist-info/RECORD +363 -0
- {UncountablePythonSDK-0.0.82.dist-info → uncountablepythonsdk-0.0.132.dist-info}/WHEEL +1 -1
- UncountablePythonSDK-0.0.82.dist-info/METADATA +0 -60
- UncountablePythonSDK-0.0.82.dist-info/RECORD +0 -292
- docs/quickstart.md +0 -19
- {UncountablePythonSDK-0.0.82.dist-info → uncountablepythonsdk-0.0.132.dist-info}/top_level.txt +0 -0
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import io
|
|
2
2
|
import os
|
|
3
|
+
from typing import assert_never
|
|
3
4
|
|
|
4
5
|
from . import builder, util
|
|
6
|
+
from .builder import EndpointKey, EndpointSpecificPath, PathMapping
|
|
5
7
|
from .config import TypeScriptConfig
|
|
8
|
+
from .cross_output_links import get_path_links
|
|
6
9
|
from .emit_io_ts import emit_type_io_ts
|
|
7
10
|
from .emit_typescript_util import (
|
|
8
11
|
MODIFY_NOTICE,
|
|
9
12
|
EmitTypescriptContext,
|
|
13
|
+
emit_constant_ts,
|
|
10
14
|
emit_namespace_imports_ts,
|
|
11
15
|
emit_type_ts,
|
|
12
|
-
emit_value_ts,
|
|
13
16
|
resolve_namespace_name,
|
|
14
17
|
resolve_namespace_ref,
|
|
15
18
|
ts_type_name,
|
|
@@ -31,7 +34,12 @@ def _emit_types(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
|
|
|
31
34
|
builder.namespaces.values(),
|
|
32
35
|
key=lambda ns: resolve_namespace_name(ns),
|
|
33
36
|
):
|
|
34
|
-
ctx = EmitTypescriptContext(
|
|
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
|
+
)
|
|
35
43
|
|
|
36
44
|
_emit_namespace(ctx, config, namespace)
|
|
37
45
|
|
|
@@ -46,7 +54,10 @@ def _emit_types(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
|
|
|
46
54
|
and len(namespace.constants) == 0
|
|
47
55
|
):
|
|
48
56
|
# Try to capture some common incompleteness errors
|
|
49
|
-
if namespace.endpoint is None or
|
|
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
|
+
):
|
|
50
61
|
raise Exception(
|
|
51
62
|
f"Namespace {'/'.join(namespace.path)} is incomplete. It should have an endpoint with function, types, and/or constants"
|
|
52
63
|
)
|
|
@@ -69,6 +80,7 @@ def _emit_types(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
|
|
|
69
80
|
full.write("\n")
|
|
70
81
|
full.write(MODIFY_NOTICE)
|
|
71
82
|
full.write(f"// === START section from {namespace.name}.ts.part ===\n")
|
|
83
|
+
full.write("\n")
|
|
72
84
|
full.write(part)
|
|
73
85
|
full.write(f"// === END section from {namespace.name}.ts.part ===\n")
|
|
74
86
|
|
|
@@ -103,7 +115,7 @@ def _emit_namespace(
|
|
|
103
115
|
emit_type_ts(ctx, stype)
|
|
104
116
|
|
|
105
117
|
for sconst in namespace.constants.values():
|
|
106
|
-
|
|
118
|
+
emit_constant_ts(ctx, sconst)
|
|
107
119
|
|
|
108
120
|
if namespace.endpoint is not None:
|
|
109
121
|
_emit_endpoint(ctx, config, namespace, namespace.endpoint)
|
|
@@ -123,6 +135,7 @@ def _emit_endpoint(
|
|
|
123
135
|
has_data = "Data" in namespace.types
|
|
124
136
|
has_deprecated_result = "DeprecatedResult" in namespace.types
|
|
125
137
|
is_binary = endpoint.result_type == builder.ResultType.binary
|
|
138
|
+
has_multiple_endpoints = len(endpoint.path_per_api_endpoint) > 1
|
|
126
139
|
|
|
127
140
|
result_type_count = sum([has_data, has_deprecated_result, is_binary])
|
|
128
141
|
assert result_type_count < 2
|
|
@@ -134,6 +147,13 @@ def _emit_endpoint(
|
|
|
134
147
|
if not is_binary:
|
|
135
148
|
assert endpoint.result_type == builder.ResultType.json
|
|
136
149
|
|
|
150
|
+
paths_string = get_path_links(
|
|
151
|
+
ctx.cross_output_paths,
|
|
152
|
+
namespace,
|
|
153
|
+
current_path_type="TypeScript",
|
|
154
|
+
endpoint=endpoint,
|
|
155
|
+
)
|
|
156
|
+
|
|
137
157
|
data_loader_head = ""
|
|
138
158
|
data_loader_body = ""
|
|
139
159
|
if endpoint.data_loader:
|
|
@@ -141,7 +161,7 @@ def _emit_endpoint(
|
|
|
141
161
|
assert has_data
|
|
142
162
|
|
|
143
163
|
data_loader_head = (
|
|
144
|
-
'import {
|
|
164
|
+
'import { argsKey, buildApiDataLoader } from "unc_base/data_manager"\n'
|
|
145
165
|
)
|
|
146
166
|
data_loader_body = (
|
|
147
167
|
"\nexport const data = buildApiDataLoader(argsKey(), apiCall)\n"
|
|
@@ -157,24 +177,49 @@ def _emit_endpoint(
|
|
|
157
177
|
wrap_call = (
|
|
158
178
|
f"{wrap_name}<Arguments>" if is_binary else f"{wrap_name}<Arguments, Response>"
|
|
159
179
|
)
|
|
160
|
-
|
|
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)}"
|
|
161
203
|
|
|
162
204
|
if is_binary:
|
|
163
|
-
|
|
164
|
-
|
|
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}"
|
|
165
208
|
|
|
166
209
|
export type {{ Arguments }}
|
|
167
210
|
"""
|
|
168
211
|
elif has_data and endpoint.has_attachment:
|
|
169
|
-
|
|
170
|
-
|
|
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}"
|
|
171
215
|
|
|
172
216
|
export type {{ Arguments, Data }}
|
|
173
217
|
export type Response = AttachmentResponse<Data>
|
|
174
218
|
"""
|
|
175
219
|
elif has_data:
|
|
176
|
-
|
|
177
|
-
|
|
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}"
|
|
178
223
|
|
|
179
224
|
export type {{ Arguments, Data }}
|
|
180
225
|
export type Response = JsonResponse<Data>
|
|
@@ -182,49 +227,77 @@ export type Response = JsonResponse<Data>
|
|
|
182
227
|
|
|
183
228
|
else:
|
|
184
229
|
assert has_deprecated_result
|
|
185
|
-
|
|
186
|
-
|
|
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}"
|
|
187
233
|
|
|
188
234
|
export type {{ Arguments }}
|
|
189
235
|
export type Response = DeprecatedResult
|
|
190
236
|
"""
|
|
191
237
|
|
|
192
|
-
|
|
193
|
-
|
|
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}
|
|
194
275
|
export const apiCall = {wrap_call}(
|
|
195
|
-
|
|
276
|
+
{endpoint_path_part}{path_mapping_part}
|
|
196
277
|
)
|
|
197
278
|
{data_loader_body}"""
|
|
198
279
|
|
|
199
|
-
output = f"{config.
|
|
280
|
+
output = f"{config.endpoint_to_routes_output[endpoint.default_endpoint_key]}/{'/'.join(namespace.path)}.tsx"
|
|
200
281
|
util.rewrite_file(output, tsx_api)
|
|
201
282
|
|
|
202
283
|
# Hacky index support, until enough is migrated to regen entirely
|
|
203
284
|
# Emits the import into the UI API index file
|
|
204
|
-
index_path = f"{config.
|
|
285
|
+
index_path = f"{config.endpoint_to_routes_output[endpoint.default_endpoint_key]}/{'/'.join(namespace.path[0:-1])}/index.tsx"
|
|
205
286
|
api_name = f"Api{ts_type_name(namespace.path[0 - 1])}"
|
|
206
287
|
if os.path.exists(index_path):
|
|
207
|
-
with open(index_path) as index:
|
|
288
|
+
with open(index_path, encoding="utf-8") as index:
|
|
208
289
|
index_data = index.read()
|
|
209
290
|
need_index = index_data.find(api_name) == -1
|
|
210
291
|
else:
|
|
211
292
|
need_index = True
|
|
212
293
|
|
|
213
294
|
if need_index:
|
|
214
|
-
with open(index_path, "a") as index:
|
|
295
|
+
with open(index_path, "a", encoding="utf-8") as index:
|
|
215
296
|
print(f"Updated API Index {index_path}")
|
|
216
297
|
index.write(f'import * as {api_name} from "./{namespace.path[-1]}"\n\n')
|
|
217
298
|
index.write(f"export {{ {api_name} }}\n")
|
|
218
299
|
|
|
219
300
|
|
|
220
|
-
def _emit_constant(ctx: EmitTypescriptContext, sconst: builder.SpecConstant) -> None:
|
|
221
|
-
ctx.out.write("\n\n")
|
|
222
|
-
ctx.out.write(MODIFY_NOTICE)
|
|
223
|
-
value = emit_value_ts(ctx, sconst.value_type, sconst.value)
|
|
224
|
-
const_name = sconst.name.upper()
|
|
225
|
-
ctx.out.write(f"export const {const_name} = {value}\n")
|
|
226
|
-
|
|
227
|
-
|
|
228
301
|
def _emit_id_source(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
|
|
229
302
|
id_source_output = config.id_source_output
|
|
230
303
|
if id_source_output is None:
|
|
@@ -3,12 +3,12 @@ import typing
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
4
|
|
|
5
5
|
from . import builder, util
|
|
6
|
+
from .builder_types import CrossOutputPaths
|
|
6
7
|
|
|
7
8
|
INDENT = " "
|
|
8
9
|
|
|
9
10
|
MODIFY_NOTICE = "// DO NOT MODIFY -- This file is generated by type_spec\n"
|
|
10
11
|
|
|
11
|
-
|
|
12
12
|
base_name_map = {
|
|
13
13
|
builder.BaseTypeName.s_boolean: "boolean",
|
|
14
14
|
builder.BaseTypeName.s_date: "string", # IMPROVE: Aliased DateStr
|
|
@@ -31,6 +31,8 @@ class EmitTypescriptContext:
|
|
|
31
31
|
out: io.StringIO
|
|
32
32
|
namespace: builder.SpecNamespace
|
|
33
33
|
namespaces: set[builder.SpecNamespace] = field(default_factory=set)
|
|
34
|
+
cross_output_paths: CrossOutputPaths | None = None
|
|
35
|
+
api_endpoints: dict[builder.EndpointKey, builder.APIEndpointInfo]
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
def ts_type_name(name: str) -> str:
|
|
@@ -49,7 +51,10 @@ def ts_name(name: str, name_case: builder.NameCase) -> str:
|
|
|
49
51
|
|
|
50
52
|
|
|
51
53
|
def emit_value_ts(
|
|
52
|
-
ctx: EmitTypescriptContext,
|
|
54
|
+
ctx: EmitTypescriptContext,
|
|
55
|
+
stype: builder.SpecType,
|
|
56
|
+
value: typing.Any,
|
|
57
|
+
indent: int = 0,
|
|
53
58
|
) -> str:
|
|
54
59
|
"""Mimics emit_python even if not all types are used in TypeScript yet"""
|
|
55
60
|
literal = builder.unwrap_literal_type(stype)
|
|
@@ -79,16 +84,24 @@ def emit_value_ts(
|
|
|
79
84
|
if stype.defn_type.is_base_type(builder.BaseTypeName.s_dict):
|
|
80
85
|
key_type = stype.parameters[0]
|
|
81
86
|
value_type = stype.parameters[1]
|
|
87
|
+
|
|
88
|
+
if not key_type.is_base_type(
|
|
89
|
+
builder.BaseTypeName.s_string
|
|
90
|
+
) and not isinstance(key_type, builder.SpecTypeDefnStringEnum):
|
|
91
|
+
raise Exception("invalid dict keys -- dict keys must be string or enum")
|
|
92
|
+
|
|
82
93
|
return (
|
|
83
|
-
"{\n
|
|
84
|
-
+ ",\n
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
94
|
+
f"{{\n{INDENT * (indent + 1)}"
|
|
95
|
+
+ f",\n{INDENT * (indent + 1)}".join(
|
|
96
|
+
(
|
|
97
|
+
f"[{emit_value_ts(ctx, key_type, dkey)}]: "
|
|
98
|
+
if not key_type.is_base_type(builder.BaseTypeName.s_string)
|
|
99
|
+
else f"{dkey}: "
|
|
100
|
+
)
|
|
101
|
+
+ emit_value_ts(ctx, value_type, dvalue, indent=indent + 1)
|
|
89
102
|
for dkey, dvalue in value.items()
|
|
90
103
|
)
|
|
91
|
-
+ "\n}"
|
|
104
|
+
+ f"\n{INDENT * (indent)}}}"
|
|
92
105
|
)
|
|
93
106
|
|
|
94
107
|
if stype.defn_type.is_base_type(builder.BaseTypeName.s_optional):
|
|
@@ -99,8 +112,35 @@ def emit_value_ts(
|
|
|
99
112
|
|
|
100
113
|
elif isinstance(stype, builder.SpecTypeDefnStringEnum):
|
|
101
114
|
return f"{refer_to(ctx, stype)}.{ts_enum_name(value, stype.name_case)}"
|
|
115
|
+
elif isinstance(stype, builder.SpecTypeDefnObject):
|
|
116
|
+
assert isinstance(value, dict), (
|
|
117
|
+
f"Expected dict value for {stype.name} but got {value}"
|
|
118
|
+
)
|
|
119
|
+
obj_out = "{"
|
|
120
|
+
did_emit = False
|
|
121
|
+
for prop_name, prop in (stype.properties or {}).items():
|
|
122
|
+
if prop_name not in value and prop.has_default:
|
|
123
|
+
value_to_emit = prop.default
|
|
124
|
+
elif prop_name not in value:
|
|
125
|
+
continue
|
|
126
|
+
else:
|
|
127
|
+
value_to_emit = value[prop_name]
|
|
128
|
+
did_emit = True
|
|
129
|
+
typescript_name = ts_name(prop.name, prop.name_case)
|
|
130
|
+
obj_out += f"\n{INDENT * (indent + 1)}{typescript_name}: {emit_value_ts(ctx, prop.spec_type, value_to_emit, indent=indent + 1)},"
|
|
131
|
+
whitespace = f"\n{INDENT * indent}" if did_emit else ""
|
|
132
|
+
obj_out += f"{whitespace}}} as const"
|
|
133
|
+
return obj_out
|
|
102
134
|
|
|
103
|
-
raise Exception("invalid constant type", value, stype)
|
|
135
|
+
raise Exception("invalid constant type", value, stype, type(stype))
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def emit_constant_ts(ctx: EmitTypescriptContext, sconst: builder.SpecConstant) -> None:
|
|
139
|
+
ctx.out.write("\n\n")
|
|
140
|
+
ctx.out.write(MODIFY_NOTICE)
|
|
141
|
+
value = emit_value_ts(ctx, sconst.value_type, sconst.value)
|
|
142
|
+
const_name = sconst.name.upper()
|
|
143
|
+
ctx.out.write(f"export const {const_name} = {value}\n")
|
|
104
144
|
|
|
105
145
|
|
|
106
146
|
def emit_type_ts(ctx: EmitTypescriptContext, stype: builder.SpecType) -> None:
|
|
@@ -115,6 +155,7 @@ def emit_type_ts(ctx: EmitTypescriptContext, stype: builder.SpecType) -> None:
|
|
|
115
155
|
|
|
116
156
|
if isinstance(stype, builder.SpecTypeDefnExternal):
|
|
117
157
|
assert not stype.is_exported, "expecting private names"
|
|
158
|
+
ctx.out.write("\n")
|
|
118
159
|
ctx.out.write(stype.external_map["ts"])
|
|
119
160
|
ctx.out.write("\n")
|
|
120
161
|
return
|
|
@@ -263,3 +304,18 @@ def emit_namespace_imports_ts(
|
|
|
263
304
|
)
|
|
264
305
|
import_from = f"{import_path}{resolve_namespace_name(ns)}"
|
|
265
306
|
out.write(f'import * as {import_as} from "{import_from}"\n') # noqa: E501
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def emit_namespace_imports_from_root_ts(
|
|
310
|
+
namespaces: set[builder.SpecNamespace],
|
|
311
|
+
out: io.StringIO,
|
|
312
|
+
root: str,
|
|
313
|
+
) -> None:
|
|
314
|
+
for ns in sorted(
|
|
315
|
+
namespaces,
|
|
316
|
+
key=lambda name: resolve_namespace_name(name),
|
|
317
|
+
):
|
|
318
|
+
import_as = resolve_namespace_ref(ns)
|
|
319
|
+
out.write(
|
|
320
|
+
f'import * as {import_as} from "{root}/{resolve_namespace_name(ns)}"\n'
|
|
321
|
+
) # noqa: E501
|
pkgs/type_spec/load_types.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from collections.abc import Callable
|
|
3
3
|
from io import StringIO
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
from shelljob import fs
|
|
7
6
|
|
|
8
7
|
from pkgs.serialization import yaml
|
|
9
8
|
|
|
10
9
|
from .builder import SpecBuilder
|
|
10
|
+
from .builder_types import CrossOutputPaths
|
|
11
11
|
from .config import Config
|
|
12
12
|
|
|
13
13
|
ext_map = {
|
|
@@ -41,9 +41,22 @@ def find_and_handle_files(
|
|
|
41
41
|
handler(file_name, file.read())
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
def load_types(config: Config) ->
|
|
44
|
+
def load_types(config: Config) -> SpecBuilder | None:
|
|
45
|
+
if config.typescript is not None and config.python is not None:
|
|
46
|
+
cross_output_paths = CrossOutputPaths(
|
|
47
|
+
python_types_output=config.python.types_output,
|
|
48
|
+
typescript_types_output=config.typescript.types_output,
|
|
49
|
+
typescript_routes_output_by_endpoint=config.typescript.endpoint_to_routes_output,
|
|
50
|
+
typespec_files_input=config.type_spec_types,
|
|
51
|
+
# IMPROVE not sure how to know which one is the correct one in emit_typescript
|
|
52
|
+
)
|
|
53
|
+
else:
|
|
54
|
+
cross_output_paths = None
|
|
55
|
+
|
|
45
56
|
builder = SpecBuilder(
|
|
46
|
-
api_endpoints=config.api_endpoint,
|
|
57
|
+
api_endpoints=config.api_endpoint,
|
|
58
|
+
top_namespace=config.top_namespace,
|
|
59
|
+
cross_output_paths=cross_output_paths,
|
|
47
60
|
)
|
|
48
61
|
|
|
49
62
|
def handle_builder_add(
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
NON_DISCRIMINATED_UNION_EXCEPTIONS = [
|
|
2
|
+
"generate_tool_parameters.UnionWithoutDiscrim",
|
|
3
|
+
"output_calculation_entities.ConditionParameterFilterCondition",
|
|
4
|
+
"output_parameters.AnalyticalMethodParameterOptions",
|
|
5
|
+
"output_parameters.AnalyticalMethodLinkedOptionValue",
|
|
6
|
+
"recipes_redirect.RecipesRedirectResult",
|
|
7
|
+
"value_spec.ResolvedPathAll",
|
|
8
|
+
"weighted_sum.WeightedSumEntities",
|
|
9
|
+
"workflows.WorkflowTotalDisplay",
|
|
10
|
+
"type_info.TypeFormActionConstraint",
|
|
11
|
+
"structured_loading.CompatibleFilterNode",
|
|
12
|
+
"structured_display_element.DisplayElementColumn",
|
|
13
|
+
"field_values.FieldValue",
|
|
14
|
+
]
|
pkgs/type_spec/open_api_util.py
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from enum import StrEnum
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
from pkgs.serialization_util import JsonValue
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class OpenAPIType(ABC):
|
|
7
8
|
description: str | None = None
|
|
8
9
|
nullable: bool = False
|
|
10
|
+
default: JsonValue
|
|
9
11
|
|
|
10
|
-
def __init__(
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
description: str | None = None,
|
|
15
|
+
nullable: bool = False,
|
|
16
|
+
default: JsonValue = None,
|
|
17
|
+
) -> None:
|
|
11
18
|
self.description = description
|
|
12
19
|
self.nullable = nullable
|
|
20
|
+
self.default = default
|
|
13
21
|
|
|
14
22
|
@abstractmethod
|
|
15
23
|
def asdict(self) -> dict[str, object]:
|
|
@@ -20,6 +28,8 @@ class OpenAPIType(ABC):
|
|
|
20
28
|
emitted["description"] = self.description
|
|
21
29
|
if self.nullable:
|
|
22
30
|
emitted["nullable"] = self.nullable
|
|
31
|
+
if self.default is not None:
|
|
32
|
+
emitted["default"] = self.default
|
|
23
33
|
return emitted
|
|
24
34
|
|
|
25
35
|
|
|
@@ -158,7 +168,7 @@ class OpenAPIObjectType(OpenAPIType):
|
|
|
158
168
|
description: str | None = None,
|
|
159
169
|
nullable: bool = False,
|
|
160
170
|
*,
|
|
161
|
-
property_desc:
|
|
171
|
+
property_desc: dict[str, str] | None = None,
|
|
162
172
|
) -> None:
|
|
163
173
|
self.properties = properties
|
|
164
174
|
if property_desc is None:
|
|
@@ -213,13 +223,28 @@ class OpenAPIUnionType(OpenAPIType):
|
|
|
213
223
|
base_types: list[OpenAPIType],
|
|
214
224
|
description: str | None = None,
|
|
215
225
|
nullable: bool = False,
|
|
226
|
+
discriminator: str | None = None,
|
|
227
|
+
discriminator_map: dict[str, OpenAPIRefType] | None = None,
|
|
216
228
|
) -> None:
|
|
217
229
|
self.base_types = base_types
|
|
230
|
+
self._discriminator = discriminator
|
|
231
|
+
self._discriminator_map = discriminator_map
|
|
218
232
|
super().__init__(description=description, nullable=nullable)
|
|
219
233
|
|
|
220
234
|
def asdict(self) -> dict[str, object]:
|
|
221
235
|
# TODO: use parents description and nullable
|
|
222
|
-
return {
|
|
236
|
+
return {
|
|
237
|
+
"oneOf": [base_type.asdict() for base_type in self.base_types],
|
|
238
|
+
"discriminator": {
|
|
239
|
+
"propertyName": self._discriminator,
|
|
240
|
+
"mapping": {
|
|
241
|
+
discriminator_value: base_type.source
|
|
242
|
+
for discriminator_value, base_type in self._discriminator_map.items()
|
|
243
|
+
},
|
|
244
|
+
}
|
|
245
|
+
if self._discriminator is not None and self._discriminator_map is not None
|
|
246
|
+
else None,
|
|
247
|
+
}
|
|
223
248
|
|
|
224
249
|
|
|
225
250
|
class OpenAPIIntersectionType(OpenAPIType):
|
|
@@ -22,10 +22,16 @@ PureJsonScalar = Union[str, float, bool, None]
|
|
|
22
22
|
# Regular expressions for identifying ref names and IDs. Ref names should be
|
|
23
23
|
# using this regular expression as a constriant in the database.
|
|
24
24
|
REF_NAME_REGEX = r"^[a-zA-Z0-9_/-]+$"
|
|
25
|
+
REF_NAME_STRICT_REGEX_STRING = "^[a-zA-Z_][a-zA-Z0-9_]*$"
|
|
26
|
+
REF_NAME_STRICT_REGEX = rf"{REF_NAME_STRICT_REGEX_STRING}"
|
|
25
27
|
# Ids matching a strict integer number are converted to integers
|
|
26
28
|
ID_REGEX = r"-?[1-9][0-9]{0,20}"
|
|
27
29
|
|
|
28
30
|
|
|
31
|
+
# ENABLE_SLOTS should be removed after slots have been tested locally
|
|
32
|
+
import os
|
|
33
|
+
ENABLE_SLOTS = os.environ.get("UNC_ENABLE_DATACLASS_SLOTS") == "true"
|
|
34
|
+
|
|
29
35
|
if TYPE_CHECKING:
|
|
30
36
|
JsonValue = Union[JsonScalar, Mapping[str, "JsonValue"], Sequence["JsonValue"]]
|
|
31
37
|
ExtJsonValue = JsonValue
|
|
@@ -52,15 +58,12 @@ def is_pure_json_value(value: ExtJsonValue) -> bool:
|
|
|
52
58
|
return True
|
|
53
59
|
|
|
54
60
|
if isinstance(value, list):
|
|
55
|
-
for item in value
|
|
56
|
-
if not is_pure_json_value(item):
|
|
57
|
-
return False
|
|
58
|
-
return True
|
|
61
|
+
return all(is_pure_json_value(item) for item in value)
|
|
59
62
|
|
|
60
63
|
if isinstance(value, dict):
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
return all(
|
|
65
|
+
is_pure_json_value(key) and is_pure_json_value(item)
|
|
66
|
+
for key, item in value.items()
|
|
67
|
+
)
|
|
65
68
|
|
|
66
69
|
return False
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// doesn't allow referring explicitly to global names (thus cannot override here)
|
|
4
4
|
// IMPROVE: invert relationship for global.d.ts looks here instead
|
|
5
5
|
import * as IO from 'io-ts';
|
|
6
|
+
|
|
6
7
|
type localJsonScalar = JsonScalar
|
|
7
8
|
type localJsonValue = JsonValue
|
|
8
9
|
type localObjectId = ObjectId
|
|
@@ -28,3 +29,6 @@ export const IOJsonValue: IO.Type<JsonValue> = IO.recursion('JsonValue', () =>
|
|
|
28
29
|
export interface nominal<T> {
|
|
29
30
|
"nominal structural brand": T
|
|
30
31
|
}
|
|
32
|
+
|
|
33
|
+
// Ids matching a strict integer number are converted to integers
|
|
34
|
+
export const ID_REGEX = /^-?[1-9][0-9]{0,20}$/
|
|
@@ -9,7 +9,7 @@ import sys
|
|
|
9
9
|
|
|
10
10
|
from ..config import parse_yaml_config
|
|
11
11
|
from ..load_types import load_types
|
|
12
|
-
from .emit_type_info import emit_type_info
|
|
12
|
+
from .emit_type_info import emit_type_info, emit_type_info_python
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def main() -> bool:
|
|
@@ -26,6 +26,8 @@ def main() -> bool:
|
|
|
26
26
|
|
|
27
27
|
assert config.typescript is not None
|
|
28
28
|
emit_type_info(builder, config.typescript.type_info_output)
|
|
29
|
+
if config.python.type_info_output is not None:
|
|
30
|
+
emit_type_info_python(builder, config.python.type_info_output)
|
|
29
31
|
|
|
30
32
|
return True
|
|
31
33
|
|