UncountablePythonSDK 0.0.24__py3-none-any.whl → 0.0.131__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of UncountablePythonSDK might be problematic. Click here for more details.
- docs/conf.py +60 -8
- 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 +7 -5
- examples/async_batch.py +5 -6
- examples/basic_auth.py +7 -0
- examples/create_entity.py +4 -6
- examples/create_ingredient_sdk.py +34 -0
- examples/download_files.py +26 -0
- examples/edit_recipe_inputs.py +50 -0
- examples/integration-server/jobs/materials_auto/concurrent_cron.py +11 -0
- examples/integration-server/jobs/materials_auto/example_cron.py +21 -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 +23 -0
- examples/integration-server/jobs/materials_auto/profile.yaml +104 -0
- examples/integration-server/pyproject.toml +224 -0
- examples/invoke_uploader.py +26 -0
- examples/oauth.py +7 -0
- examples/set_recipe_metadata_file.py +40 -0
- examples/set_recipe_output_file_sdk.py +26 -0
- examples/upload_files.py +2 -3
- pkgs/argument_parser/__init__.py +9 -0
- pkgs/argument_parser/_is_namedtuple.py +3 -0
- pkgs/argument_parser/argument_parser.py +295 -74
- pkgs/argument_parser/case_convert.py +4 -3
- pkgs/filesystem_utils/__init__.py +20 -0
- pkgs/filesystem_utils/_blob_session.py +144 -0
- pkgs/filesystem_utils/_gdrive_session.py +309 -0
- pkgs/filesystem_utils/_local_session.py +69 -0
- pkgs/filesystem_utils/_s3_session.py +118 -0
- pkgs/filesystem_utils/_sftp_session.py +151 -0
- pkgs/filesystem_utils/file_type_utils.py +91 -0
- pkgs/filesystem_utils/filesystem_session.py +39 -0
- pkgs/py.typed +0 -0
- pkgs/serialization/__init__.py +8 -1
- 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 +69 -54
- pkgs/serialization/serial_generic.py +16 -0
- pkgs/serialization/serial_union.py +84 -0
- pkgs/serialization/yaml.py +57 -0
- pkgs/serialization_util/__init__.py +7 -7
- pkgs/serialization_util/convert_to_snakecase.py +27 -0
- pkgs/serialization_util/dataclasses.py +14 -0
- pkgs/serialization_util/serialization_helpers.py +117 -71
- pkgs/type_spec/actions_registry/__main__.py +0 -4
- pkgs/type_spec/actions_registry/emit_typescript.py +5 -5
- pkgs/type_spec/builder.py +438 -109
- pkgs/type_spec/builder_types.py +9 -0
- pkgs/type_spec/config.py +52 -24
- pkgs/type_spec/cross_output_links.py +99 -0
- pkgs/type_spec/emit_io_ts.py +1 -1
- pkgs/type_spec/emit_open_api.py +160 -41
- pkgs/type_spec/emit_open_api_util.py +13 -7
- pkgs/type_spec/emit_python.py +450 -136
- pkgs/type_spec/emit_typescript.py +117 -250
- pkgs/type_spec/emit_typescript_util.py +293 -4
- pkgs/type_spec/load_types.py +20 -5
- 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 +13 -10
- 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 +161 -32
- 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 +4 -4
- pkgs/type_spec/value_spec/__main__.py +27 -10
- pkgs/type_spec/value_spec/convert_type.py +21 -1
- pkgs/type_spec/value_spec/emit_python.py +25 -7
- pkgs/type_spec/value_spec/types.py +1 -1
- uncountable/__init__.py +1 -2
- uncountable/core/__init__.py +11 -3
- uncountable/core/async_batch.py +16 -1
- uncountable/core/client.py +247 -52
- uncountable/core/environment.py +41 -0
- uncountable/core/file_upload.py +67 -22
- uncountable/core/types.py +8 -13
- uncountable/integration/cli.py +142 -0
- uncountable/integration/construct_client.py +43 -27
- uncountable/integration/cron.py +12 -11
- uncountable/integration/db/connect.py +12 -2
- uncountable/integration/db/session.py +25 -0
- uncountable/integration/entrypoint.py +4 -34
- uncountable/integration/executors/executors.py +147 -0
- uncountable/integration/executors/generic_upload_executor.py +336 -0
- uncountable/integration/executors/script_executor.py +15 -9
- uncountable/integration/http_server/__init__.py +5 -0
- uncountable/integration/http_server/types.py +69 -0
- uncountable/integration/job.py +246 -19
- uncountable/integration/queue_runner/__init__.py +0 -0
- uncountable/integration/queue_runner/command_server/__init__.py +28 -0
- uncountable/integration/queue_runner/command_server/command_client.py +133 -0
- uncountable/integration/queue_runner/command_server/command_server.py +142 -0
- uncountable/integration/queue_runner/command_server/constants.py +4 -0
- uncountable/integration/queue_runner/command_server/protocol/__init__.py +0 -0
- uncountable/integration/queue_runner/command_server/protocol/command_server.proto +58 -0
- uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.py +57 -0
- uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.pyi +114 -0
- uncountable/integration/queue_runner/command_server/protocol/command_server_pb2_grpc.py +264 -0
- uncountable/integration/queue_runner/command_server/types.py +75 -0
- uncountable/integration/queue_runner/datastore/__init__.py +3 -0
- uncountable/integration/queue_runner/datastore/datastore_sqlite.py +250 -0
- uncountable/integration/queue_runner/datastore/interface.py +29 -0
- uncountable/integration/queue_runner/datastore/model.py +24 -0
- uncountable/integration/queue_runner/job_scheduler.py +200 -0
- uncountable/integration/queue_runner/queue_runner.py +34 -0
- uncountable/integration/queue_runner/types.py +7 -0
- uncountable/integration/queue_runner/worker.py +116 -0
- uncountable/integration/scan_profiles.py +67 -0
- uncountable/integration/scheduler.py +199 -0
- uncountable/integration/secret_retrieval/__init__.py +3 -0
- uncountable/integration/secret_retrieval/retrieve_secret.py +93 -0
- uncountable/integration/server.py +103 -54
- uncountable/integration/telemetry.py +251 -0
- uncountable/integration/webhook_server/entrypoint.py +97 -0
- uncountable/types/__init__.py +149 -30
- uncountable/types/api/batch/execute_batch.py +16 -9
- uncountable/types/api/batch/execute_batch_load_async.py +13 -7
- uncountable/types/api/chemical/convert_chemical_formats.py +20 -8
- 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 +24 -12
- uncountable/types/api/entity/create_entity.py +22 -13
- 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 +18 -9
- uncountable/types/api/entity/grant_entity_permissions.py +48 -0
- uncountable/types/api/entity/list_aggregate.py +79 -0
- uncountable/types/api/entity/list_entities.py +53 -14
- uncountable/types/api/entity/lock_entity.py +45 -0
- uncountable/types/api/entity/lookup_entity.py +116 -0
- uncountable/types/api/entity/resolve_entity_ids.py +19 -10
- uncountable/types/api/entity/set_entity_field_values.py +44 -0
- uncountable/types/api/entity/set_values.py +15 -8
- uncountable/types/api/entity/transition_entity_phase.py +27 -12
- uncountable/types/api/entity/unlock_entity.py +44 -0
- uncountable/types/api/equipment/__init__.py +1 -0
- uncountable/types/api/equipment/associate_equipment_input.py +43 -0
- uncountable/types/api/field_options/__init__.py +1 -0
- uncountable/types/api/field_options/upsert_field_options.py +55 -0
- 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 +20 -11
- uncountable/types/api/id_source/match_id_source.py +15 -10
- uncountable/types/api/input_groups/get_input_group_names.py +16 -7
- uncountable/types/api/inputs/create_inputs.py +28 -14
- uncountable/types/api/inputs/get_input_data.py +34 -16
- uncountable/types/api/inputs/get_input_names.py +19 -10
- uncountable/types/api/inputs/get_inputs_data.py +29 -11
- uncountable/types/api/inputs/set_input_attribute_values.py +16 -10
- uncountable/types/api/inputs/set_input_category.py +44 -0
- uncountable/types/api/inputs/set_input_subcategories.py +45 -0
- uncountable/types/api/inputs/set_intermediate_type.py +50 -0
- 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/__init__.py +1 -0
- uncountable/types/api/material_families/update_entity_material_families.py +47 -0
- 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 +32 -17
- uncountable/types/api/outputs/get_output_names.py +18 -9
- uncountable/types/api/outputs/get_output_organization.py +173 -0
- uncountable/types/api/outputs/resolve_output_conditions.py +23 -11
- uncountable/types/api/permissions/set_core_permissions.py +31 -15
- uncountable/types/api/project/get_projects.py +20 -11
- uncountable/types/api/project/get_projects_data.py +23 -14
- uncountable/types/api/recipe_links/create_recipe_link.py +17 -10
- uncountable/types/api/recipe_links/remove_recipe_link.py +45 -0
- uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py +19 -10
- uncountable/types/api/recipes/add_recipe_to_project.py +42 -0
- uncountable/types/api/recipes/add_time_series_data.py +64 -0
- uncountable/types/api/recipes/archive_recipes.py +14 -7
- uncountable/types/api/recipes/associate_recipe_as_input.py +16 -8
- uncountable/types/api/recipes/associate_recipe_as_lot.py +14 -7
- uncountable/types/api/recipes/clear_recipe_outputs.py +42 -0
- uncountable/types/api/recipes/create_mix_order.py +44 -0
- uncountable/types/api/recipes/create_recipe.py +21 -14
- uncountable/types/api/recipes/create_recipes.py +25 -13
- uncountable/types/api/recipes/disassociate_recipe_as_input.py +14 -7
- uncountable/types/api/recipes/edit_recipe_inputs.py +208 -19
- uncountable/types/api/recipes/get_column_calculation_values.py +57 -0
- uncountable/types/api/recipes/get_curve.py +15 -9
- uncountable/types/api/recipes/get_recipe_calculations.py +17 -11
- uncountable/types/api/recipes/get_recipe_links.py +14 -8
- uncountable/types/api/recipes/get_recipe_names.py +16 -7
- uncountable/types/api/recipes/get_recipe_output_metadata.py +16 -10
- uncountable/types/api/recipes/get_recipes_data.py +96 -45
- uncountable/types/api/recipes/lock_recipes.py +64 -0
- uncountable/types/api/recipes/remove_recipe_from_project.py +42 -0
- uncountable/types/api/recipes/set_recipe_inputs.py +19 -13
- uncountable/types/api/recipes/set_recipe_metadata.py +14 -7
- uncountable/types/api/recipes/set_recipe_output_annotations.py +114 -0
- uncountable/types/api/recipes/set_recipe_output_file.py +55 -0
- uncountable/types/api/recipes/set_recipe_outputs.py +40 -15
- uncountable/types/api/recipes/set_recipe_tags.py +30 -13
- uncountable/types/api/recipes/set_recipe_total.py +59 -0
- uncountable/types/api/recipes/unarchive_recipes.py +41 -0
- uncountable/types/api/recipes/unlock_recipes.py +51 -0
- 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 +15 -8
- uncountable/types/api/uploader/__init__.py +1 -0
- uncountable/types/api/uploader/complete_async_parse.py +46 -0
- uncountable/types/api/uploader/invoke_uploader.py +46 -0
- uncountable/types/api/user/__init__.py +1 -0
- uncountable/types/api/user/get_current_user_info.py +40 -0
- uncountable/types/async_batch.py +8 -52
- uncountable/types/async_batch_processor.py +694 -18
- uncountable/types/async_batch_t.py +108 -0
- uncountable/types/async_jobs.py +8 -0
- uncountable/types/async_jobs_t.py +52 -0
- uncountable/types/auth_retrieval.py +11 -0
- uncountable/types/auth_retrieval_t.py +75 -0
- uncountable/types/base.py +5 -80
- uncountable/types/base_t.py +87 -0
- uncountable/types/calculations.py +3 -19
- uncountable/types/calculations_t.py +26 -0
- uncountable/types/chemical_structure.py +3 -23
- uncountable/types/chemical_structure_t.py +28 -0
- uncountable/types/client_base.py +1170 -88
- uncountable/types/client_config.py +8 -0
- uncountable/types/client_config_t.py +36 -0
- uncountable/types/curves.py +5 -43
- uncountable/types/curves_t.py +50 -0
- uncountable/types/data.py +12 -0
- uncountable/types/data_t.py +103 -0
- uncountable/types/entity.py +8 -270
- uncountable/types/entity_t.py +446 -0
- uncountable/types/experiment_groups.py +3 -19
- uncountable/types/experiment_groups_t.py +26 -0
- uncountable/types/exports.py +8 -0
- uncountable/types/exports_t.py +34 -0
- uncountable/types/field_values.py +25 -61
- uncountable/types/field_values_t.py +302 -0
- uncountable/types/fields.py +3 -20
- uncountable/types/fields_t.py +27 -0
- uncountable/types/generic_upload.py +14 -0
- uncountable/types/generic_upload_t.py +119 -0
- uncountable/types/id_source.py +7 -45
- uncountable/types/id_source_t.py +68 -0
- uncountable/types/identifier.py +6 -50
- uncountable/types/identifier_t.py +62 -0
- uncountable/types/input_attributes.py +3 -25
- uncountable/types/input_attributes_t.py +29 -0
- uncountable/types/inputs.py +6 -57
- uncountable/types/inputs_t.py +82 -0
- uncountable/types/integration_server.py +8 -0
- uncountable/types/integration_server_t.py +46 -0
- 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 +28 -0
- uncountable/types/job_definition_t.py +285 -0
- 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 +3 -22
- uncountable/types/outputs_t.py +29 -0
- uncountable/types/overrides.py +9 -0
- uncountable/types/overrides_t.py +49 -0
- uncountable/types/permissions.py +3 -42
- uncountable/types/permissions_t.py +45 -0
- uncountable/types/phases.py +3 -19
- uncountable/types/phases_t.py +26 -0
- uncountable/types/post_base.py +3 -26
- uncountable/types/post_base_t.py +29 -0
- uncountable/types/queued_job.py +17 -0
- uncountable/types/queued_job_t.py +140 -0
- uncountable/types/recipe_identifiers.py +7 -58
- uncountable/types/recipe_identifiers_t.py +75 -0
- uncountable/types/recipe_inputs.py +4 -26
- uncountable/types/recipe_inputs_t.py +29 -0
- uncountable/types/recipe_links.py +4 -46
- uncountable/types/recipe_links_t.py +53 -0
- uncountable/types/recipe_metadata.py +5 -48
- uncountable/types/recipe_metadata_t.py +57 -0
- uncountable/types/recipe_output_metadata.py +3 -20
- uncountable/types/recipe_output_metadata_t.py +27 -0
- uncountable/types/recipe_tags.py +3 -19
- uncountable/types/recipe_tags_t.py +26 -0
- uncountable/types/recipe_workflow_steps.py +9 -73
- uncountable/types/recipe_workflow_steps_t.py +95 -0
- uncountable/types/recipes.py +7 -0
- uncountable/types/recipes_t.py +25 -0
- uncountable/types/response.py +3 -21
- uncountable/types/response_t.py +26 -0
- uncountable/types/secret_retrieval.py +11 -0
- uncountable/types/secret_retrieval_t.py +75 -0
- 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 +3 -19
- uncountable/types/units_t.py +26 -0
- uncountable/types/uploader.py +24 -0
- uncountable/types/uploader_t.py +222 -0
- uncountable/types/users.py +3 -20
- uncountable/types/users_t.py +27 -0
- uncountable/types/webhook_job.py +9 -0
- uncountable/types/webhook_job_t.py +48 -0
- uncountable/types/workflows.py +4 -28
- uncountable/types/workflows_t.py +38 -0
- uncountablepythonsdk-0.0.131.dist-info/METADATA +64 -0
- uncountablepythonsdk-0.0.131.dist-info/RECORD +363 -0
- {UncountablePythonSDK-0.0.24.dist-info → uncountablepythonsdk-0.0.131.dist-info}/WHEEL +1 -1
- {UncountablePythonSDK-0.0.24.dist-info → uncountablepythonsdk-0.0.131.dist-info}/top_level.txt +0 -1
- UncountablePythonSDK-0.0.24.dist-info/METADATA +0 -47
- UncountablePythonSDK-0.0.24.dist-info/RECORD +0 -216
- docs/quickstart.md +0 -19
- examples/recipe-import/importer.py +0 -39
- type_spec/external/api/batch/execute_batch.yaml +0 -56
- type_spec/external/api/batch/execute_batch_load_async.yaml +0 -18
- type_spec/external/api/chemical/convert_chemical_formats.yaml +0 -33
- type_spec/external/api/entity/create_entities.yaml +0 -45
- type_spec/external/api/entity/create_entity.yaml +0 -51
- type_spec/external/api/entity/get_entities_data.yaml +0 -29
- type_spec/external/api/entity/list_entities.yaml +0 -52
- type_spec/external/api/entity/resolve_entity_ids.yaml +0 -29
- type_spec/external/api/entity/set_values.yaml +0 -18
- type_spec/external/api/entity/transition_entity_phase.yaml +0 -44
- type_spec/external/api/id_source/list_id_source.yaml +0 -35
- type_spec/external/api/id_source/match_id_source.yaml +0 -32
- type_spec/external/api/input_groups/get_input_group_names.yaml +0 -29
- type_spec/external/api/inputs/create_inputs.yaml +0 -48
- type_spec/external/api/inputs/get_input_data.yaml +0 -95
- type_spec/external/api/inputs/get_input_names.yaml +0 -38
- type_spec/external/api/inputs/get_inputs_data.yaml +0 -82
- type_spec/external/api/inputs/set_input_attribute_values.yaml +0 -33
- type_spec/external/api/outputs/get_output_data.yaml +0 -92
- type_spec/external/api/outputs/get_output_names.yaml +0 -35
- type_spec/external/api/outputs/resolve_output_conditions.yaml +0 -50
- type_spec/external/api/permissions/set_core_permissions.yaml +0 -69
- type_spec/external/api/project/get_projects.yaml +0 -42
- type_spec/external/api/project/get_projects_data.yaml +0 -50
- type_spec/external/api/recipe_links/create_recipe_link.yaml +0 -25
- type_spec/external/api/recipe_metadata/get_recipe_metadata_data.yaml +0 -41
- type_spec/external/api/recipes/archive_recipes.yaml +0 -20
- type_spec/external/api/recipes/associate_recipe_as_input.yaml +0 -19
- type_spec/external/api/recipes/associate_recipe_as_lot.yaml +0 -19
- type_spec/external/api/recipes/create_recipe.yaml +0 -39
- type_spec/external/api/recipes/create_recipes.yaml +0 -47
- type_spec/external/api/recipes/disassociate_recipe_as_input.yaml +0 -16
- type_spec/external/api/recipes/edit_recipe_inputs.yaml +0 -85
- type_spec/external/api/recipes/get_curve.yaml +0 -21
- type_spec/external/api/recipes/get_recipe_calculations.yaml +0 -39
- type_spec/external/api/recipes/get_recipe_links.yaml +0 -26
- type_spec/external/api/recipes/get_recipe_names.yaml +0 -29
- type_spec/external/api/recipes/get_recipe_output_metadata.yaml +0 -36
- type_spec/external/api/recipes/get_recipes_data.yaml +0 -244
- type_spec/external/api/recipes/set_recipe_inputs.yaml +0 -42
- type_spec/external/api/recipes/set_recipe_metadata.yaml +0 -20
- type_spec/external/api/recipes/set_recipe_outputs.yaml +0 -52
- type_spec/external/api/recipes/set_recipe_tags.yaml +0 -62
- type_spec/external/api/triggers/run_trigger.yaml +0 -18
- uncountable/integration/types.py +0 -89
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import dataclasses
|
|
3
|
+
import decimal
|
|
3
4
|
import io
|
|
4
5
|
import json
|
|
5
|
-
from
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Any, cast
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
import yaml
|
|
10
|
+
|
|
11
|
+
from main.base.types import data_t, type_info_t
|
|
12
|
+
from main.base.types.base_t import PureJsonValue
|
|
9
13
|
from pkgs.argument_parser import CachedParser
|
|
10
14
|
from pkgs.serialization_util import serialize_for_api, serialize_for_storage
|
|
11
15
|
|
|
@@ -13,7 +17,7 @@ from .. import builder, util
|
|
|
13
17
|
from ..emit_typescript_util import MODIFY_NOTICE, ts_name
|
|
14
18
|
from ..value_spec import convert_to_value_spec_type
|
|
15
19
|
|
|
16
|
-
ext_info_parser = CachedParser(
|
|
20
|
+
ext_info_parser = CachedParser(type_info_t.ExtInfo, strict_property_parsing=True)
|
|
17
21
|
|
|
18
22
|
|
|
19
23
|
def type_path_of(stype: builder.SpecType) -> object: # NamePath
|
|
@@ -37,10 +41,23 @@ def type_path_of(stype: builder.SpecType) -> object: # NamePath
|
|
|
37
41
|
parts: list[object] = ["$literal"]
|
|
38
42
|
for parameter in stype.parameters:
|
|
39
43
|
assert isinstance(parameter, builder.SpecTypeLiteralWrapper)
|
|
44
|
+
emit_value = parameter.value
|
|
45
|
+
if isinstance(parameter.value_type, builder.SpecTypeDefnObject):
|
|
46
|
+
emit_value = parameter.value
|
|
47
|
+
assert isinstance(emit_value, (str, bool)), (
|
|
48
|
+
f"invalid-literal-value:{emit_value}"
|
|
49
|
+
)
|
|
50
|
+
elif isinstance(parameter.value_type, builder.SpecTypeDefnStringEnum):
|
|
51
|
+
key = parameter.value
|
|
52
|
+
assert isinstance(key, str)
|
|
53
|
+
emit_value = parameter.value_type.values[key].value
|
|
54
|
+
else:
|
|
55
|
+
raise Exception("unhandled-literal-type")
|
|
56
|
+
|
|
40
57
|
# This allows expansion to enum literal values later
|
|
41
58
|
parts.append([
|
|
42
59
|
"$value",
|
|
43
|
-
|
|
60
|
+
emit_value,
|
|
44
61
|
type_path_of(parameter.value_type),
|
|
45
62
|
])
|
|
46
63
|
return parts
|
|
@@ -69,12 +86,21 @@ def _dict_null_strip(data: dict[str, object]) -> dict[str, object]:
|
|
|
69
86
|
}
|
|
70
87
|
|
|
71
88
|
|
|
89
|
+
class JsonEncoder(json.JSONEncoder):
|
|
90
|
+
"""We have some defaults of special types that we need to emit"""
|
|
91
|
+
|
|
92
|
+
def default(self, obj: object) -> object:
|
|
93
|
+
if isinstance(obj, decimal.Decimal):
|
|
94
|
+
return str(obj)
|
|
95
|
+
return json.JSONEncoder.default(self, obj)
|
|
96
|
+
|
|
97
|
+
|
|
72
98
|
def emit_type_info(build: builder.SpecBuilder, output: str) -> None:
|
|
73
99
|
type_map = _build_map_all(build)
|
|
74
100
|
|
|
75
101
|
# sort for stability, indent for smaller diffs
|
|
76
102
|
stripped = _dict_null_strip(dataclasses.asdict(type_map))
|
|
77
|
-
serial = json.dumps(stripped, sort_keys=True, indent=2)
|
|
103
|
+
serial = json.dumps(stripped, sort_keys=True, indent=2, cls=JsonEncoder)
|
|
78
104
|
type_map_out = io.StringIO()
|
|
79
105
|
type_map_out.write(MODIFY_NOTICE)
|
|
80
106
|
type_map_out.write(f"export const TYPE_MAP = {serial}")
|
|
@@ -82,6 +108,35 @@ def emit_type_info(build: builder.SpecBuilder, output: str) -> None:
|
|
|
82
108
|
util.rewrite_file(f"{output}/type_map.ts", type_map_out.getvalue())
|
|
83
109
|
|
|
84
110
|
|
|
111
|
+
def _convert_value_for_yaml_dump(value: Any) -> Any:
|
|
112
|
+
if isinstance(value, Enum):
|
|
113
|
+
return value.value
|
|
114
|
+
elif isinstance(value, list):
|
|
115
|
+
return [_convert_value_for_yaml_dump(item) for item in value]
|
|
116
|
+
elif isinstance(value, dict):
|
|
117
|
+
return {k: _convert_value_for_yaml_dump(v) for k, v in value.items()}
|
|
118
|
+
elif isinstance(value, decimal.Decimal):
|
|
119
|
+
return str(value)
|
|
120
|
+
else:
|
|
121
|
+
return value
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def asdict_for_yaml_dump(dataclass_instance: Any) -> Any:
|
|
125
|
+
return {
|
|
126
|
+
k: _convert_value_for_yaml_dump(v)
|
|
127
|
+
for k, v in dataclasses.asdict(dataclass_instance).items()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def emit_type_info_python(build: builder.SpecBuilder, output: str) -> None:
|
|
132
|
+
type_map = _build_map_all(build, python=True)
|
|
133
|
+
|
|
134
|
+
stripped = _dict_null_strip(asdict_for_yaml_dump(type_map))
|
|
135
|
+
|
|
136
|
+
yaml_content = yaml.dump(stripped, default_flow_style=False, sort_keys=True)
|
|
137
|
+
util.rewrite_file(f"{output}/type_map.yaml", yaml_content)
|
|
138
|
+
|
|
139
|
+
|
|
85
140
|
@dataclasses.dataclass
|
|
86
141
|
class MapProperty:
|
|
87
142
|
api_name: str
|
|
@@ -93,7 +148,7 @@ class MapProperty:
|
|
|
93
148
|
desc: str | None
|
|
94
149
|
# We don't have typing on defaults yet, relying on emitters to check it. Limit
|
|
95
150
|
# use of this field, as it'll necessarily change when adding type info
|
|
96
|
-
default:
|
|
151
|
+
default: PureJsonValue
|
|
97
152
|
|
|
98
153
|
|
|
99
154
|
@dataclasses.dataclass
|
|
@@ -116,12 +171,19 @@ class MapTypeAlias(MapTypeBase):
|
|
|
116
171
|
discriminator: str | None
|
|
117
172
|
|
|
118
173
|
|
|
174
|
+
@dataclasses.dataclass
|
|
175
|
+
class StringEnumValue:
|
|
176
|
+
value: str
|
|
177
|
+
label: str
|
|
178
|
+
deprecated: bool = False
|
|
179
|
+
|
|
180
|
+
|
|
119
181
|
@dataclasses.dataclass
|
|
120
182
|
class MapStringEnum(MapTypeBase):
|
|
121
|
-
values: dict[str,
|
|
183
|
+
values: dict[str, StringEnumValue]
|
|
122
184
|
|
|
123
185
|
|
|
124
|
-
MapType
|
|
186
|
+
MapType = MapTypeObject | MapTypeAlias | MapStringEnum
|
|
125
187
|
|
|
126
188
|
|
|
127
189
|
@dataclasses.dataclass
|
|
@@ -134,11 +196,14 @@ class MapAll:
|
|
|
134
196
|
namespaces: dict[str, MapNamespace]
|
|
135
197
|
|
|
136
198
|
|
|
137
|
-
def _build_map_all(build: builder.SpecBuilder) -> MapAll:
|
|
199
|
+
def _build_map_all(build: builder.SpecBuilder, *, python: bool = False) -> MapAll:
|
|
138
200
|
map_all = MapAll(namespaces={})
|
|
139
201
|
|
|
140
202
|
for namespace in build.namespaces.values():
|
|
141
|
-
if not namespace.emit_type_info:
|
|
203
|
+
if not python and not namespace.emit_type_info:
|
|
204
|
+
continue
|
|
205
|
+
|
|
206
|
+
if python and not namespace.emit_type_info_python:
|
|
142
207
|
continue
|
|
143
208
|
|
|
144
209
|
map_namespace = MapNamespace(types={})
|
|
@@ -159,9 +224,9 @@ class InheritablePropertyParts:
|
|
|
159
224
|
at that level, but that needs to be done in builder. When that is done, the
|
|
160
225
|
"label" and "desc" could probably be removed from this list."""
|
|
161
226
|
|
|
162
|
-
label:
|
|
163
|
-
desc:
|
|
164
|
-
ext_info:
|
|
227
|
+
label: str | None = None
|
|
228
|
+
desc: str | None = None
|
|
229
|
+
ext_info: type_info_t.ExtInfo | None = None
|
|
165
230
|
|
|
166
231
|
|
|
167
232
|
def _extract_inheritable_property_parts(
|
|
@@ -188,8 +253,13 @@ def _extract_inheritable_property_parts(
|
|
|
188
253
|
elif base_parts.ext_info is None:
|
|
189
254
|
ext_info = local_ext_info
|
|
190
255
|
else:
|
|
191
|
-
ext_info =
|
|
192
|
-
|
|
256
|
+
ext_info = dataclasses.replace(
|
|
257
|
+
local_ext_info,
|
|
258
|
+
**{
|
|
259
|
+
field.name: getattr(base_parts.ext_info, field.name)
|
|
260
|
+
for field in dataclasses.fields(type_info_t.ExtInfo)
|
|
261
|
+
if getattr(base_parts.ext_info, field.name) is not None
|
|
262
|
+
},
|
|
193
263
|
)
|
|
194
264
|
|
|
195
265
|
return InheritablePropertyParts(label=label, desc=desc, ext_info=ext_info)
|
|
@@ -201,7 +271,7 @@ ALL_FIELDS_GROUP = "*all_fields"
|
|
|
201
271
|
|
|
202
272
|
def _extract_and_validate_layout(
|
|
203
273
|
stype: builder.SpecTypeDefnObject,
|
|
204
|
-
ext_info:
|
|
274
|
+
ext_info: type_info_t.ExtInfo,
|
|
205
275
|
base_layout: ExtInfoLayout | None,
|
|
206
276
|
) -> ExtInfoLayout:
|
|
207
277
|
"""
|
|
@@ -217,13 +287,15 @@ def _extract_and_validate_layout(
|
|
|
217
287
|
for group in ext_info.layout.groups:
|
|
218
288
|
fields = set(group.fields or [])
|
|
219
289
|
for field in fields:
|
|
220
|
-
assert field in stype.properties,
|
|
290
|
+
assert field in stype.properties or field == DISCRIMINATOR_COMMON_NAME, (
|
|
291
|
+
f"layout-refers-to-missing-field:{field}"
|
|
292
|
+
)
|
|
221
293
|
|
|
222
294
|
local_ref_name = None
|
|
223
295
|
if group.ref_name is not None:
|
|
224
|
-
assert (
|
|
225
|
-
|
|
226
|
-
)
|
|
296
|
+
assert base_layout is None or base_layout.get(group.ref_name) is None, (
|
|
297
|
+
f"group-name-duplicate-in-base:{group.ref_name}"
|
|
298
|
+
)
|
|
227
299
|
local_ref_name = group.ref_name
|
|
228
300
|
|
|
229
301
|
if group.extends:
|
|
@@ -242,22 +314,60 @@ def _extract_and_validate_layout(
|
|
|
242
314
|
assert group_ref_name in layout, f"missing-base-group:{group_ref_name}"
|
|
243
315
|
|
|
244
316
|
for prop_ref_name in stype.properties:
|
|
245
|
-
assert prop_ref_name in all_fields_group,
|
|
317
|
+
assert prop_ref_name in all_fields_group, (
|
|
318
|
+
f"layout-missing-field:{prop_ref_name}"
|
|
319
|
+
)
|
|
246
320
|
|
|
247
321
|
return layout
|
|
248
322
|
|
|
249
323
|
|
|
250
|
-
def
|
|
324
|
+
def _pull_property_from_type_recursively(
|
|
325
|
+
stype: builder.SpecTypeDefnObject,
|
|
326
|
+
property_name: str,
|
|
327
|
+
) -> builder.SpecProperty | None:
|
|
328
|
+
assert stype.properties is not None
|
|
329
|
+
prop = stype.properties.get(property_name)
|
|
330
|
+
if prop is not None:
|
|
331
|
+
return prop
|
|
332
|
+
|
|
333
|
+
if stype.base is None:
|
|
334
|
+
return None
|
|
335
|
+
|
|
336
|
+
return _pull_property_from_type_recursively(stype.base, property_name)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
DISCRIMINATOR_COMMON_NAME = "type"
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def _validate_type_ext_info(
|
|
343
|
+
stype: builder.SpecTypeDefnObject,
|
|
344
|
+
) -> tuple[ExtInfoLayout | None, type_info_t.ExtInfo | None]:
|
|
251
345
|
ext_info = _parse_ext_info(stype.ext_info)
|
|
252
346
|
if ext_info is None:
|
|
253
|
-
return None
|
|
347
|
+
return None, None
|
|
348
|
+
|
|
349
|
+
if ext_info.label_fields is not None:
|
|
350
|
+
assert stype.properties is not None
|
|
351
|
+
for name in ext_info.label_fields:
|
|
352
|
+
if name == DISCRIMINATOR_COMMON_NAME:
|
|
353
|
+
continue
|
|
354
|
+
prop = _pull_property_from_type_recursively(stype, name)
|
|
355
|
+
assert prop is not None, f"missing-label-field:{name}"
|
|
356
|
+
|
|
357
|
+
if ext_info.actions is not None:
|
|
358
|
+
assert stype.properties is not None
|
|
359
|
+
for action in ext_info.actions:
|
|
360
|
+
if action.property == DISCRIMINATOR_COMMON_NAME:
|
|
361
|
+
continue
|
|
362
|
+
prop = _pull_property_from_type_recursively(stype, action.property)
|
|
363
|
+
assert prop is not None, f"missing-action-field:{action.property}"
|
|
254
364
|
|
|
255
365
|
if not stype.is_base and isinstance(stype.base, builder.SpecTypeDefnObject):
|
|
256
|
-
base_layout = _validate_type_ext_info(stype.base)
|
|
366
|
+
base_layout, _ = _validate_type_ext_info(stype.base)
|
|
257
367
|
else:
|
|
258
368
|
base_layout = None
|
|
259
369
|
|
|
260
|
-
return _extract_and_validate_layout(stype, ext_info, base_layout)
|
|
370
|
+
return _extract_and_validate_layout(stype, ext_info, base_layout), ext_info
|
|
261
371
|
|
|
262
372
|
|
|
263
373
|
def _build_map_type(
|
|
@@ -270,7 +380,7 @@ def _build_map_type(
|
|
|
270
380
|
and not stype.is_base
|
|
271
381
|
and stype.base is not None
|
|
272
382
|
):
|
|
273
|
-
_validate_type_ext_info(stype)
|
|
383
|
+
_, ext_info = _validate_type_ext_info(stype)
|
|
274
384
|
|
|
275
385
|
properties: dict[str, MapProperty] = {}
|
|
276
386
|
map_type = MapTypeObject(
|
|
@@ -279,7 +389,7 @@ def _build_map_type(
|
|
|
279
389
|
properties=properties,
|
|
280
390
|
desc=stype.desc,
|
|
281
391
|
base_type_path=type_path_of(stype.base),
|
|
282
|
-
ext_info=
|
|
392
|
+
ext_info=serialize_for_api(ext_info), # type: ignore[arg-type]
|
|
283
393
|
)
|
|
284
394
|
|
|
285
395
|
if stype.properties is not None:
|
|
@@ -293,7 +403,7 @@ def _build_map_type(
|
|
|
293
403
|
api_name=ts_name(prop.name, prop.name_case),
|
|
294
404
|
extant=prop.extant,
|
|
295
405
|
type_path=type_path_of(prop.spec_type),
|
|
296
|
-
ext_info=serialize_for_api(parts.ext_info),
|
|
406
|
+
ext_info=serialize_for_api(parts.ext_info), # type: ignore[arg-type]
|
|
297
407
|
desc=parts.desc,
|
|
298
408
|
default=prop.default,
|
|
299
409
|
)
|
|
@@ -311,6 +421,19 @@ def _build_map_type(
|
|
|
311
421
|
discriminator=stype.discriminator,
|
|
312
422
|
)
|
|
313
423
|
|
|
424
|
+
if isinstance(stype, builder.SpecTypeDefnUnion):
|
|
425
|
+
# Emit as a basic alias for now, as the front-end supports only those for now
|
|
426
|
+
# IMPROVE: We should emit a proper union type and support that
|
|
427
|
+
backing = stype.get_backing_type()
|
|
428
|
+
return MapTypeAlias(
|
|
429
|
+
type_name=stype.name,
|
|
430
|
+
label=stype.label,
|
|
431
|
+
desc=stype.desc,
|
|
432
|
+
alias_type_path=type_path_of(backing),
|
|
433
|
+
ext_info=_convert_ext_info(stype.ext_info),
|
|
434
|
+
discriminator=stype.discriminator,
|
|
435
|
+
)
|
|
436
|
+
|
|
314
437
|
if isinstance(stype, builder.SpecTypeDefnStringEnum):
|
|
315
438
|
return MapStringEnum(
|
|
316
439
|
type_name=stype.name,
|
|
@@ -320,7 +443,11 @@ def _build_map_type(
|
|
|
320
443
|
# IMPROVE: We probably want the label here, but this requires a change
|
|
321
444
|
# to the front-end type-info and form code to handle
|
|
322
445
|
values={
|
|
323
|
-
entry.value: (
|
|
446
|
+
entry.value: StringEnumValue(
|
|
447
|
+
value=entry.value,
|
|
448
|
+
label=entry.label or entry.name,
|
|
449
|
+
deprecated=entry.deprecated,
|
|
450
|
+
)
|
|
324
451
|
for entry in stype.values.values()
|
|
325
452
|
},
|
|
326
453
|
)
|
|
@@ -328,7 +455,7 @@ def _build_map_type(
|
|
|
328
455
|
return None
|
|
329
456
|
|
|
330
457
|
|
|
331
|
-
def _parse_ext_info(in_ext: Any) ->
|
|
458
|
+
def _parse_ext_info(in_ext: Any) -> type_info_t.ExtInfo | None:
|
|
332
459
|
if in_ext is None:
|
|
333
460
|
return None
|
|
334
461
|
assert isinstance(in_ext, dict)
|
|
@@ -346,10 +473,12 @@ def _parse_ext_info(in_ext: Any) -> Optional[data_t.ExtInfo]:
|
|
|
346
473
|
df["result_type"] = serialize_for_storage(converted)
|
|
347
474
|
mod_ext["data_format"] = df
|
|
348
475
|
|
|
476
|
+
if "open_api" in mod_ext:
|
|
477
|
+
del mod_ext["open_api"]
|
|
349
478
|
return ext_info_parser.parse_storage(mod_ext)
|
|
350
479
|
|
|
351
480
|
|
|
352
|
-
def _convert_ext_info(in_ext: Any) ->
|
|
481
|
+
def _convert_ext_info(in_ext: Any) -> PureJsonValue | None:
|
|
353
482
|
# we need to convert this to API storage since it'll be used as-is in the UI
|
|
354
483
|
parsed = _parse_ext_info(in_ext)
|
|
355
484
|
return cast(PureJsonValue, serialize_for_api(parsed))
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import re
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from io import StringIO
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import assert_never
|
|
7
|
+
|
|
8
|
+
from main.base.types import (
|
|
9
|
+
base_t,
|
|
10
|
+
ui_entry_actions_t,
|
|
11
|
+
)
|
|
12
|
+
from pkgs.serialization_util import serialize_for_api
|
|
13
|
+
from pkgs.type_spec import emit_typescript_util
|
|
14
|
+
from pkgs.type_spec.builder import (
|
|
15
|
+
BaseTypeName,
|
|
16
|
+
NameCase,
|
|
17
|
+
RawDict,
|
|
18
|
+
SpecBuilder,
|
|
19
|
+
SpecNamespace,
|
|
20
|
+
SpecTypeDefnObject,
|
|
21
|
+
)
|
|
22
|
+
from pkgs.type_spec.config import Config
|
|
23
|
+
from pkgs.type_spec.load_types import load_types
|
|
24
|
+
from pkgs.type_spec.util import rewrite_file
|
|
25
|
+
from pkgs.type_spec.value_spec.convert_type import convert_from_value_spec_type
|
|
26
|
+
|
|
27
|
+
_INIT_ACTION_INDEX_TYPE_DATA = {
|
|
28
|
+
"EntryActionInfo<InputT, OutputT>": {
|
|
29
|
+
"type": BaseTypeName.s_object,
|
|
30
|
+
"properties": {"inputs": {"type": "InputT"}, "outputs": {"type": "OutputT"}},
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
_TYPES_ROOT = "unc_types"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(kw_only=True)
|
|
37
|
+
class EntryActionTypeInfo:
|
|
38
|
+
inputs_type: SpecTypeDefnObject
|
|
39
|
+
outputs_type: SpecTypeDefnObject
|
|
40
|
+
name: str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def ui_entry_variable_to_type_spec_type(
|
|
44
|
+
variable: ui_entry_actions_t.UiEntryActionVariable,
|
|
45
|
+
) -> str:
|
|
46
|
+
match variable:
|
|
47
|
+
case ui_entry_actions_t.UiEntryActionVariableString():
|
|
48
|
+
return BaseTypeName.s_string
|
|
49
|
+
case ui_entry_actions_t.UiEntryActionVariableSingleEntity():
|
|
50
|
+
return "ObjectId"
|
|
51
|
+
case _:
|
|
52
|
+
assert_never(variable)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def construct_inputs_type_data(
|
|
56
|
+
vars: dict[str, ui_entry_actions_t.UiEntryActionVariable],
|
|
57
|
+
) -> RawDict:
|
|
58
|
+
if len(vars) == 0:
|
|
59
|
+
return {"type": BaseTypeName.s_object}
|
|
60
|
+
properties: dict[str, dict[str, str]] = {}
|
|
61
|
+
for input_name, input_defn in (vars).items():
|
|
62
|
+
properties[f"{input_name}"] = {
|
|
63
|
+
"type": ui_entry_variable_to_type_spec_type(input_defn)
|
|
64
|
+
}
|
|
65
|
+
return {"type": BaseTypeName.s_object, "properties": properties}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def construct_outputs_type_data(
|
|
69
|
+
vars: dict[str, ui_entry_actions_t.UiEntryActionOutput],
|
|
70
|
+
) -> RawDict:
|
|
71
|
+
if len(vars) == 0:
|
|
72
|
+
return {"type": BaseTypeName.s_object}
|
|
73
|
+
properties: dict[str, dict[str, str]] = {}
|
|
74
|
+
for output_name, output_defn in (vars).items():
|
|
75
|
+
# All outputs are optional
|
|
76
|
+
properties[f"{output_name}"] = {
|
|
77
|
+
"type": f"Optional<{convert_from_value_spec_type(output_defn.vs_type)}>"
|
|
78
|
+
}
|
|
79
|
+
return {"type": BaseTypeName.s_object, "properties": properties}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def construct_outputs_type(
|
|
83
|
+
*,
|
|
84
|
+
action_scope: ui_entry_actions_t.ActionScope,
|
|
85
|
+
vars: dict[str, ui_entry_actions_t.UiEntryActionOutput],
|
|
86
|
+
builder: SpecBuilder,
|
|
87
|
+
namespace: SpecNamespace,
|
|
88
|
+
) -> SpecTypeDefnObject:
|
|
89
|
+
stype = SpecTypeDefnObject(
|
|
90
|
+
namespace=namespace,
|
|
91
|
+
name=emit_typescript_util.ts_type_name(f"{action_scope}_outputs"),
|
|
92
|
+
)
|
|
93
|
+
namespace.types[stype.name] = stype
|
|
94
|
+
stype.process(
|
|
95
|
+
builder=builder,
|
|
96
|
+
data=construct_outputs_type_data(vars=vars),
|
|
97
|
+
)
|
|
98
|
+
return stype
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def construct_inputs_type(
|
|
102
|
+
*,
|
|
103
|
+
action_scope: ui_entry_actions_t.ActionScope,
|
|
104
|
+
vars: dict[str, ui_entry_actions_t.UiEntryActionVariable],
|
|
105
|
+
builder: SpecBuilder,
|
|
106
|
+
namespace: SpecNamespace,
|
|
107
|
+
) -> SpecTypeDefnObject:
|
|
108
|
+
stype = SpecTypeDefnObject(
|
|
109
|
+
namespace=namespace,
|
|
110
|
+
name=emit_typescript_util.ts_type_name(f"{action_scope}_inputs"),
|
|
111
|
+
)
|
|
112
|
+
stype.process(builder=builder, data=construct_inputs_type_data(vars))
|
|
113
|
+
namespace.types[stype.name] = stype
|
|
114
|
+
return stype
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _get_types_root(destination_root: Path) -> Path:
|
|
118
|
+
return destination_root / "types"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def emit_imports_ts(
|
|
122
|
+
namespaces: set[SpecNamespace],
|
|
123
|
+
out: StringIO,
|
|
124
|
+
) -> None:
|
|
125
|
+
for ns in sorted(
|
|
126
|
+
namespaces,
|
|
127
|
+
key=lambda ns: ns.name,
|
|
128
|
+
):
|
|
129
|
+
import_as = emit_typescript_util.resolve_namespace_ref(ns)
|
|
130
|
+
import_from = f"{_TYPES_ROOT}/{ns.name}"
|
|
131
|
+
out.write(f'import * as {import_as} from "{import_from}"\n')
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def emit_entry_action_definition(
|
|
135
|
+
*,
|
|
136
|
+
ctx: emit_typescript_util.EmitTypescriptContext,
|
|
137
|
+
defn: ui_entry_actions_t.UiEntryActionDefinition,
|
|
138
|
+
builder: SpecBuilder,
|
|
139
|
+
action_scope: ui_entry_actions_t.ActionScope,
|
|
140
|
+
) -> EntryActionTypeInfo:
|
|
141
|
+
inputs_type = construct_inputs_type(
|
|
142
|
+
action_scope=action_scope,
|
|
143
|
+
vars=defn.inputs,
|
|
144
|
+
builder=builder,
|
|
145
|
+
namespace=ctx.namespace,
|
|
146
|
+
)
|
|
147
|
+
outputs_type = construct_outputs_type(
|
|
148
|
+
action_scope=action_scope,
|
|
149
|
+
vars=defn.outputs,
|
|
150
|
+
builder=builder,
|
|
151
|
+
namespace=ctx.namespace,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
return EntryActionTypeInfo(
|
|
155
|
+
inputs_type=inputs_type,
|
|
156
|
+
outputs_type=outputs_type,
|
|
157
|
+
name=action_scope,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _validate_input(input: ui_entry_actions_t.UiEntryActionVariable) -> None:
|
|
162
|
+
if "_" in input.vs_var_name:
|
|
163
|
+
raise ValueError(f"Expected camelCase for variable {input.vs_var_name}")
|
|
164
|
+
if not re.fullmatch(base_t.REF_NAME_STRICT_REGEX, input.vs_var_name):
|
|
165
|
+
raise ValueError(
|
|
166
|
+
f"Variable {input.vs_var_name} has invalid syntax. See REF_NAME_STRICT_REGEX"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def emit_query_index(
|
|
171
|
+
ctx: emit_typescript_util.EmitTypescriptContext,
|
|
172
|
+
defn_infos: list[EntryActionTypeInfo],
|
|
173
|
+
index_path: Path,
|
|
174
|
+
builder: SpecBuilder,
|
|
175
|
+
definitions: dict[
|
|
176
|
+
ui_entry_actions_t.ActionScope, ui_entry_actions_t.UiEntryActionDefinition
|
|
177
|
+
],
|
|
178
|
+
) -> bool:
|
|
179
|
+
query_index_type_data = {
|
|
180
|
+
**_INIT_ACTION_INDEX_TYPE_DATA,
|
|
181
|
+
"EntityActionTypeLookup": {
|
|
182
|
+
"type": BaseTypeName.s_object,
|
|
183
|
+
"properties": {
|
|
184
|
+
defn_info.name: {
|
|
185
|
+
"type": f"EntryActionInfo<{defn_info.inputs_type.name},{defn_info.outputs_type.name}>",
|
|
186
|
+
"name_case": NameCase.preserve,
|
|
187
|
+
}
|
|
188
|
+
for defn_info in defn_infos
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
"InputInfo": {
|
|
192
|
+
"type": BaseTypeName.s_object,
|
|
193
|
+
"properties": {
|
|
194
|
+
"value_spec_var": {"type": "String"},
|
|
195
|
+
"type": {"type": "ui_entry_actions.UiEntryActionDataType"},
|
|
196
|
+
"variable": {"type": "ui_entry_actions.UiEntryActionVariable"},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
"OutputInfo": {
|
|
200
|
+
"type": BaseTypeName.s_object,
|
|
201
|
+
"properties": {
|
|
202
|
+
"name": {"type": "String"},
|
|
203
|
+
"desc": {"type": "String"},
|
|
204
|
+
"type": {"type": "value_spec.BaseType"},
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
"DefinitionInfo": {
|
|
208
|
+
"type": BaseTypeName.s_object,
|
|
209
|
+
"properties": {
|
|
210
|
+
"inputs": {
|
|
211
|
+
"type": "ReadonlyArray<InputInfo>",
|
|
212
|
+
},
|
|
213
|
+
"outputs": {
|
|
214
|
+
"type": "ReadonlyArray<OutputInfo>",
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
}
|
|
219
|
+
ctx.namespace.prescan(query_index_type_data)
|
|
220
|
+
ctx.namespace.process(
|
|
221
|
+
builder=builder,
|
|
222
|
+
data=query_index_type_data,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
defn_lookup_info = {}
|
|
226
|
+
for scope, defn in definitions.items():
|
|
227
|
+
inputs = []
|
|
228
|
+
outputs = []
|
|
229
|
+
for input in defn.inputs.values():
|
|
230
|
+
_validate_input(input)
|
|
231
|
+
inputs.append(
|
|
232
|
+
serialize_for_api({
|
|
233
|
+
"value_spec_var": input.vs_var_name,
|
|
234
|
+
"type": input.type,
|
|
235
|
+
"variable": input,
|
|
236
|
+
})
|
|
237
|
+
)
|
|
238
|
+
for name, output in defn.outputs.items():
|
|
239
|
+
outputs.append(
|
|
240
|
+
serialize_for_api({
|
|
241
|
+
"name": name,
|
|
242
|
+
"desc": output.description,
|
|
243
|
+
"type": output.vs_type,
|
|
244
|
+
})
|
|
245
|
+
)
|
|
246
|
+
defn_lookup_info[scope] = {"inputs": inputs, "outputs": outputs}
|
|
247
|
+
|
|
248
|
+
defn_lookup_out = f"export const DEFINITION_LOOKUP = {json.dumps(defn_lookup_info, sort_keys=True, indent=2)} as const\n\nexport const DEFINITION_LOOKUP_TYPED = DEFINITION_LOOKUP as Record<UiEntryActionsT.ActionScope, DefinitionInfo>\n"
|
|
249
|
+
|
|
250
|
+
for stype in ctx.namespace.types.values():
|
|
251
|
+
emit_typescript_util.emit_type_ts(
|
|
252
|
+
ctx=ctx,
|
|
253
|
+
stype=stype,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
import_buffer = StringIO()
|
|
257
|
+
emit_typescript_util.emit_namespace_imports_from_root_ts(
|
|
258
|
+
namespaces=ctx.namespaces,
|
|
259
|
+
out=import_buffer,
|
|
260
|
+
root=_TYPES_ROOT,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
return rewrite_file(
|
|
264
|
+
content=import_buffer.getvalue() + ctx.out.getvalue() + defn_lookup_out,
|
|
265
|
+
filename=str(index_path),
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def generate_entry_actions_typescript(
|
|
270
|
+
*,
|
|
271
|
+
definitions: dict[
|
|
272
|
+
ui_entry_actions_t.ActionScope, ui_entry_actions_t.UiEntryActionDefinition
|
|
273
|
+
],
|
|
274
|
+
destination_root: Path,
|
|
275
|
+
materials_type_spec_config: Config,
|
|
276
|
+
) -> None:
|
|
277
|
+
builder = load_types(materials_type_spec_config)
|
|
278
|
+
assert builder is not None
|
|
279
|
+
|
|
280
|
+
definition_buffer = StringIO()
|
|
281
|
+
index_namespace = SpecNamespace(name="index")
|
|
282
|
+
ctx = emit_typescript_util.EmitTypescriptContext(
|
|
283
|
+
out=definition_buffer,
|
|
284
|
+
namespace=index_namespace,
|
|
285
|
+
api_endpoints={},
|
|
286
|
+
)
|
|
287
|
+
builder.namespaces[index_namespace.name] = index_namespace
|
|
288
|
+
|
|
289
|
+
defn_infos: list[EntryActionTypeInfo] = []
|
|
290
|
+
|
|
291
|
+
for action_scope, defn in definitions.items():
|
|
292
|
+
defn_infos.append(
|
|
293
|
+
emit_entry_action_definition(
|
|
294
|
+
action_scope=action_scope,
|
|
295
|
+
ctx=ctx,
|
|
296
|
+
defn=defn,
|
|
297
|
+
builder=builder,
|
|
298
|
+
)
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
index_path = _get_types_root(destination_root) / "index.ts"
|
|
302
|
+
emit_query_index(
|
|
303
|
+
ctx=ctx,
|
|
304
|
+
builder=builder,
|
|
305
|
+
defn_infos=defn_infos,
|
|
306
|
+
definitions=definitions,
|
|
307
|
+
index_path=index_path,
|
|
308
|
+
)
|
pkgs/type_spec/util.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import TypeVar, Union
|
|
5
5
|
|
|
6
6
|
import regex as re
|
|
7
7
|
|
|
@@ -29,8 +29,8 @@ LiteralTypeValue = Union[str, bool]
|
|
|
29
29
|
class ParsedTypePart:
|
|
30
30
|
name: str
|
|
31
31
|
# An empty list is distinct from None
|
|
32
|
-
parameters:
|
|
33
|
-
literal_value:
|
|
32
|
+
parameters: list["ParsedTypePath"] | None = None
|
|
33
|
+
literal_value: LiteralTypeValue | None = None
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
ParsedTypePath = list[ParsedTypePart]
|
|
@@ -159,7 +159,7 @@ def is_valid_property_name(name: str) -> bool:
|
|
|
159
159
|
def check_fields(data: dict[str, T], allowed: list[str]) -> None:
|
|
160
160
|
for key in data:
|
|
161
161
|
if key not in allowed:
|
|
162
|
-
raise Exception(f"unexpected-field: {key}")
|
|
162
|
+
raise Exception(f"unexpected-field: {key}. Allowed: {allowed}")
|
|
163
163
|
|
|
164
164
|
|
|
165
165
|
def split_any_name(name: str) -> list[str]:
|