UncountablePythonSDK 0.0.83__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 +3 -5
- 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.83.dist-info → uncountablepythonsdk-0.0.132.dist-info}/WHEEL +1 -1
- UncountablePythonSDK-0.0.83.dist-info/METADATA +0 -60
- UncountablePythonSDK-0.0.83.dist-info/RECORD +0 -292
- docs/quickstart.md +0 -19
- {UncountablePythonSDK-0.0.83.dist-info → uncountablepythonsdk-0.0.132.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from dataclasses import dataclass
|
|
3
3
|
from io import BytesIO
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Union
|
|
5
5
|
|
|
6
6
|
import paramiko
|
|
7
|
+
from azure.core.credentials import (
|
|
8
|
+
AzureNamedKeyCredential,
|
|
9
|
+
AzureSasCredential,
|
|
10
|
+
TokenCredential,
|
|
11
|
+
)
|
|
12
|
+
from azure.storage.blob import ContainerProperties
|
|
7
13
|
|
|
8
14
|
|
|
9
15
|
@dataclass
|
|
@@ -11,9 +17,9 @@ class FileObjectData:
|
|
|
11
17
|
file_data: bytes
|
|
12
18
|
file_IO: BytesIO
|
|
13
19
|
filename: str
|
|
14
|
-
filepath:
|
|
15
|
-
mime_type:
|
|
16
|
-
metadata:
|
|
20
|
+
filepath: str | None = None
|
|
21
|
+
mime_type: str | None = None
|
|
22
|
+
metadata: dict[str, str] | None = None
|
|
17
23
|
|
|
18
24
|
|
|
19
25
|
@dataclass
|
|
@@ -33,7 +39,7 @@ class FileSystemFileReference:
|
|
|
33
39
|
class RemoteObjectReference:
|
|
34
40
|
file_id: str
|
|
35
41
|
mime_type: str
|
|
36
|
-
filename:
|
|
42
|
+
filename: str | None = None
|
|
37
43
|
|
|
38
44
|
@property
|
|
39
45
|
def is_dir(self) -> bool:
|
|
@@ -57,7 +63,7 @@ class FileSystemSFTPConfig:
|
|
|
57
63
|
pem_path: str | None
|
|
58
64
|
pem_key: paramiko.RSAKey | None = None
|
|
59
65
|
password: str | None = None
|
|
60
|
-
valid_extensions:
|
|
66
|
+
valid_extensions: tuple[str] | None = None
|
|
61
67
|
recursive: bool = True
|
|
62
68
|
|
|
63
69
|
|
|
@@ -65,7 +71,21 @@ class FileSystemSFTPConfig:
|
|
|
65
71
|
class FileSystemS3Config:
|
|
66
72
|
endpoint_url: str
|
|
67
73
|
bucket_name: str
|
|
68
|
-
region_name:
|
|
69
|
-
access_key_id:
|
|
70
|
-
secret_access_key:
|
|
71
|
-
session_token:
|
|
74
|
+
region_name: str | None
|
|
75
|
+
access_key_id: str | None
|
|
76
|
+
secret_access_key: str | None
|
|
77
|
+
session_token: str | None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass(kw_only=True)
|
|
81
|
+
class FileSystemBlobConfig:
|
|
82
|
+
account_url: str
|
|
83
|
+
credential: (
|
|
84
|
+
str
|
|
85
|
+
| dict[str, str]
|
|
86
|
+
| AzureNamedKeyCredential
|
|
87
|
+
| AzureSasCredential
|
|
88
|
+
| TokenCredential
|
|
89
|
+
| None
|
|
90
|
+
)
|
|
91
|
+
container: ContainerProperties | str
|
pkgs/serialization/__init__.py
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
from .annotation import unwrap_annotated as unwrap_annotated
|
|
2
2
|
from .missing_sentry import MISSING_SENTRY as MISSING_SENTRY
|
|
3
3
|
from .missing_sentry import MissingSentryType as MissingSentryType
|
|
4
4
|
from .missing_sentry import MissingType as MissingType
|
|
5
5
|
from .missing_sentry import coalesce_missing_sentry as coalesce_missing_sentry
|
|
6
6
|
from .opaque_key import OpaqueKey as OpaqueKey
|
|
7
|
+
from .serial_alias import SerialAliasInspector as SerialAliasInspector
|
|
8
|
+
from .serial_alias import get_serial_alias_data as get_serial_alias_data
|
|
9
|
+
from .serial_alias import serial_alias_annotation as serial_alias_annotation
|
|
10
|
+
from .serial_class import SerialClassDataInspector as SerialClassDataInspector
|
|
7
11
|
from .serial_class import get_serial_class_data as get_serial_class_data
|
|
8
12
|
from .serial_class import get_serial_string_enum_data as get_serial_string_enum_data
|
|
9
13
|
from .serial_class import serial_class as serial_class
|
|
10
14
|
from .serial_class import serial_string_enum as serial_string_enum
|
|
11
|
-
from .
|
|
15
|
+
from .serial_generic import get_serial_data as get_serial_data
|
|
12
16
|
from .serial_union import get_serial_union_data as get_serial_union_data
|
|
17
|
+
from .serial_union import serial_union_annotation as serial_union_annotation
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
T = typing.TypeVar("T")
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclasses.dataclass(kw_only=True, frozen=True, eq=True)
|
|
8
|
+
class SerialBase:
|
|
9
|
+
named_type_path: str | None = None
|
|
10
|
+
# Indicates this type is allowed in dynamic lookups, such as via a named_type_path
|
|
11
|
+
# This isn't meant to be limting, but to catalog all the types where we need it
|
|
12
|
+
is_dynamic_allowed: bool = False
|
|
13
|
+
# Tracks if this data was provided as a decorator to the type.
|
|
14
|
+
# This is used to track "proper types" which are appropriate
|
|
15
|
+
# for serialization and/or dynamic discovery
|
|
16
|
+
from_decorator: bool = False
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_serial_annotation(parsed_type: type[T]) -> SerialBase | None:
|
|
20
|
+
if not hasattr(parsed_type, "__metadata__"):
|
|
21
|
+
return None
|
|
22
|
+
metadata = parsed_type.__metadata__ # type:ignore[attr-defined]
|
|
23
|
+
if not isinstance(metadata, tuple) or len(metadata) != 1:
|
|
24
|
+
return None
|
|
25
|
+
serial = metadata[0]
|
|
26
|
+
if not isinstance(serial, SerialBase):
|
|
27
|
+
return None
|
|
28
|
+
return serial
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SerialInspector(typing.Generic[T]):
|
|
32
|
+
def __init__(self, parsed_type: type[T], serial_base: SerialBase) -> None:
|
|
33
|
+
self._parsed_type = parsed_type
|
|
34
|
+
self._serial_base = serial_base
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def named_type_path(self) -> str | None:
|
|
38
|
+
return self._serial_base.named_type_path
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def from_decorator(self) -> bool:
|
|
42
|
+
return self._serial_base.from_decorator
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def is_field_proper(self) -> bool:
|
|
46
|
+
return (
|
|
47
|
+
self._serial_base.from_decorator
|
|
48
|
+
and self._serial_base.named_type_path is not None
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def is_dynamic_allowed(self) -> bool:
|
|
53
|
+
return self._serial_base.is_dynamic_allowed
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def unwrap_annotated(parsed_type: type[T]) -> type[T]:
|
|
57
|
+
"""
|
|
58
|
+
If the type is an annotated type then return the origin of it.
|
|
59
|
+
Otherwise return the original type.
|
|
60
|
+
"""
|
|
61
|
+
if typing.get_origin(parsed_type) is typing.Annotated:
|
|
62
|
+
# It's unclear if there's anyway to type this correctly
|
|
63
|
+
return parsed_type.__origin__ # type:ignore[attr-defined, no-any-return]
|
|
64
|
+
return parsed_type
|
|
@@ -26,5 +26,5 @@ MISSING_SENTRY = MissingSentryType()
|
|
|
26
26
|
MissingType = Union[MissingSentryType, ClassT]
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def coalesce_missing_sentry(value: MissingType[ClassT]) ->
|
|
29
|
+
def coalesce_missing_sentry(value: MissingType[ClassT]) -> ClassT | None:
|
|
30
30
|
return None if isinstance(value, MissingSentryType) else value
|
pkgs/serialization/opaque_key.py
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
from .annotation import SerialBase, SerialInspector, get_serial_annotation
|
|
5
|
+
|
|
6
|
+
T = typing.TypeVar("T")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclasses.dataclass(kw_only=True, frozen=True, eq=True)
|
|
10
|
+
class _SerialAlias(SerialBase):
|
|
11
|
+
"""
|
|
12
|
+
This class is to be kept private, to provide flexibility in registration/lookup.
|
|
13
|
+
Places that need the data should access it via help classes/methods.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def serial_alias_annotation(
|
|
18
|
+
*,
|
|
19
|
+
named_type_path: str | None = None,
|
|
20
|
+
is_dynamic_allowed: bool = False,
|
|
21
|
+
) -> _SerialAlias:
|
|
22
|
+
return _SerialAlias(
|
|
23
|
+
named_type_path=named_type_path,
|
|
24
|
+
from_decorator=True,
|
|
25
|
+
is_dynamic_allowed=is_dynamic_allowed,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _get_serial_alias(parsed_type: type[T]) -> _SerialAlias | None:
|
|
30
|
+
serial = get_serial_annotation(parsed_type)
|
|
31
|
+
if not isinstance(serial, _SerialAlias):
|
|
32
|
+
return None
|
|
33
|
+
return serial
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class SerialAliasInspector(SerialInspector[T]):
|
|
37
|
+
def __init__(self, parsed_type: type[T], serial_alias: _SerialAlias) -> None:
|
|
38
|
+
super().__init__(parsed_type, serial_alias)
|
|
39
|
+
self._serial_alias = serial_alias
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_serial_alias_data(parsed_type: type[T]) -> SerialAliasInspector[T] | None:
|
|
43
|
+
serial = _get_serial_alias(parsed_type)
|
|
44
|
+
if serial is None:
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
return SerialAliasInspector(parsed_type, serial)
|
|
@@ -3,22 +3,20 @@ from __future__ import annotations
|
|
|
3
3
|
import dataclasses
|
|
4
4
|
from collections.abc import Callable
|
|
5
5
|
from enum import StrEnum
|
|
6
|
-
from typing import Any,
|
|
6
|
+
from typing import Any, TypeVar, cast
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
from .annotation import SerialBase, SerialInspector
|
|
9
9
|
|
|
10
|
+
ClassT = TypeVar("ClassT")
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
@dataclasses.dataclass(kw_only=True, frozen=True, eq=True)
|
|
14
|
+
class _SerialClassData(SerialBase):
|
|
13
15
|
unconverted_keys: set[str] = dataclasses.field(default_factory=set)
|
|
14
16
|
unconverted_values: set[str] = dataclasses.field(default_factory=set)
|
|
15
17
|
to_string_values: set[str] = dataclasses.field(default_factory=set)
|
|
16
18
|
parse_require: set[str] = dataclasses.field(default_factory=set)
|
|
17
|
-
named_type_path:
|
|
18
|
-
# Tracks if this data was provided as a decorator to the type.
|
|
19
|
-
# This is used to track "proper types" which are appropriate
|
|
20
|
-
# for serialization and/or dynamic discovery
|
|
21
|
-
from_decorator: bool = False
|
|
19
|
+
named_type_path: str | None = None
|
|
22
20
|
|
|
23
21
|
|
|
24
22
|
EMPTY_SERIAL_CLASS_DATA = _SerialClassData()
|
|
@@ -26,12 +24,13 @@ EMPTY_SERIAL_CLASS_DATA = _SerialClassData()
|
|
|
26
24
|
|
|
27
25
|
def serial_class(
|
|
28
26
|
*,
|
|
29
|
-
unconverted_keys:
|
|
30
|
-
unconverted_values:
|
|
31
|
-
to_string_values:
|
|
32
|
-
parse_require:
|
|
33
|
-
named_type_path:
|
|
34
|
-
|
|
27
|
+
unconverted_keys: set[str] | None = None,
|
|
28
|
+
unconverted_values: set[str] | None = None,
|
|
29
|
+
to_string_values: set[str] | None = None,
|
|
30
|
+
parse_require: set[str] | None = None,
|
|
31
|
+
named_type_path: str | None = None,
|
|
32
|
+
is_dynamic_allowed: bool = False,
|
|
33
|
+
) -> Callable[[ClassT], ClassT]:
|
|
35
34
|
"""
|
|
36
35
|
An additional decorator to a dataclass that specifies serialization options.
|
|
37
36
|
|
|
@@ -55,7 +54,7 @@ def serial_class(
|
|
|
55
54
|
The type_spec type-path to this type. This applies only to named types.
|
|
56
55
|
"""
|
|
57
56
|
|
|
58
|
-
def decorate(orig_class:
|
|
57
|
+
def decorate(orig_class: ClassT) -> ClassT:
|
|
59
58
|
cast(Any, orig_class).__unc_serial_data = _SerialClassData(
|
|
60
59
|
unconverted_keys=unconverted_keys or set(),
|
|
61
60
|
unconverted_values=unconverted_values or set(),
|
|
@@ -63,17 +62,20 @@ def serial_class(
|
|
|
63
62
|
parse_require=parse_require or set(),
|
|
64
63
|
named_type_path=named_type_path,
|
|
65
64
|
from_decorator=True,
|
|
65
|
+
is_dynamic_allowed=is_dynamic_allowed,
|
|
66
66
|
)
|
|
67
67
|
return orig_class
|
|
68
68
|
|
|
69
69
|
return decorate
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
class SerialClassDataInspector:
|
|
72
|
+
class SerialClassDataInspector(SerialInspector[ClassT]):
|
|
73
73
|
def __init__(
|
|
74
74
|
self,
|
|
75
|
+
parsed_type: type[ClassT],
|
|
75
76
|
current: _SerialClassData,
|
|
76
77
|
) -> None:
|
|
78
|
+
super().__init__(parsed_type, current)
|
|
77
79
|
self.current = current
|
|
78
80
|
|
|
79
81
|
def has_unconverted_key(self, key: str) -> bool:
|
|
@@ -88,20 +90,8 @@ class SerialClassDataInspector:
|
|
|
88
90
|
def has_parse_require(self, key: str) -> bool:
|
|
89
91
|
return key in self.current.parse_require
|
|
90
92
|
|
|
91
|
-
@property
|
|
92
|
-
def from_decorator(self) -> bool:
|
|
93
|
-
return self.current.from_decorator
|
|
94
|
-
|
|
95
|
-
@property
|
|
96
|
-
def named_type_path(self) -> Optional[str]:
|
|
97
|
-
return self.current.named_type_path
|
|
98
|
-
|
|
99
|
-
@property
|
|
100
|
-
def is_field_proper(self) -> bool:
|
|
101
|
-
return self.current.from_decorator and self.current.named_type_path is not None
|
|
102
93
|
|
|
103
|
-
|
|
104
|
-
def _get_merged_serial_class_data(type_class: type[Any]) -> _SerialClassData | None:
|
|
94
|
+
def get_merged_serial_class_data(type_class: type[Any]) -> _SerialClassData | None:
|
|
105
95
|
base_class_data = (
|
|
106
96
|
cast(_SerialClassData, type_class.__unc_serial_data)
|
|
107
97
|
if hasattr(type_class, "__unc_serial_data")
|
|
@@ -110,28 +100,30 @@ def _get_merged_serial_class_data(type_class: type[Any]) -> _SerialClassData | N
|
|
|
110
100
|
if base_class_data is None:
|
|
111
101
|
return None
|
|
112
102
|
|
|
103
|
+
# IMPROVE: We should cache this result on the type
|
|
113
104
|
if type_class.__bases__ is not None:
|
|
114
105
|
for base in type_class.__bases__:
|
|
115
|
-
curr_base_class_data =
|
|
106
|
+
curr_base_class_data = get_merged_serial_class_data(base)
|
|
116
107
|
if curr_base_class_data is not None:
|
|
117
|
-
|
|
118
|
-
base_class_data
|
|
119
|
-
|
|
120
|
-
curr_base_class_data.unconverted_keys
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
curr_base_class_data.
|
|
108
|
+
base_class_data = dataclasses.replace(
|
|
109
|
+
base_class_data,
|
|
110
|
+
unconverted_keys=base_class_data.unconverted_keys
|
|
111
|
+
| curr_base_class_data.unconverted_keys,
|
|
112
|
+
unconverted_values=base_class_data.unconverted_values
|
|
113
|
+
| curr_base_class_data.unconverted_values,
|
|
114
|
+
to_string_values=base_class_data.to_string_values
|
|
115
|
+
| curr_base_class_data.to_string_values,
|
|
116
|
+
parse_require=base_class_data.parse_require
|
|
117
|
+
| curr_base_class_data.parse_require,
|
|
127
118
|
)
|
|
128
|
-
base_class_data.parse_require |= curr_base_class_data.parse_require
|
|
129
119
|
return base_class_data
|
|
130
120
|
|
|
131
121
|
|
|
132
|
-
def get_serial_class_data(
|
|
122
|
+
def get_serial_class_data(
|
|
123
|
+
type_class: type[ClassT],
|
|
124
|
+
) -> SerialClassDataInspector[ClassT]:
|
|
133
125
|
return SerialClassDataInspector(
|
|
134
|
-
|
|
126
|
+
type_class, get_merged_serial_class_data(type_class) or EMPTY_SERIAL_CLASS_DATA
|
|
135
127
|
)
|
|
136
128
|
|
|
137
129
|
|
|
@@ -142,13 +134,13 @@ class _SerialStringEnumData:
|
|
|
142
134
|
|
|
143
135
|
|
|
144
136
|
def serial_string_enum(
|
|
145
|
-
*, labels:
|
|
146
|
-
) -> Callable[[
|
|
137
|
+
*, labels: dict[str, str] | None = None, deprecated: set[str] | None = None
|
|
138
|
+
) -> Callable[[ClassT], ClassT]:
|
|
147
139
|
"""
|
|
148
140
|
A decorator for enums to provide serialization data, including labels.
|
|
149
141
|
"""
|
|
150
142
|
|
|
151
|
-
def decorate(orig_class:
|
|
143
|
+
def decorate(orig_class: ClassT) -> ClassT:
|
|
152
144
|
cast(Any, orig_class).__unc_serial_string_enum_data = _SerialStringEnumData(
|
|
153
145
|
labels=labels or {}, deprecated=deprecated or set()
|
|
154
146
|
)
|
|
@@ -161,7 +153,7 @@ class SerialStringEnumInspector:
|
|
|
161
153
|
def __init__(self, current: _SerialStringEnumData) -> None:
|
|
162
154
|
self.current = current
|
|
163
155
|
|
|
164
|
-
def get_label(self, value: str) ->
|
|
156
|
+
def get_label(self, value: str) -> str | None:
|
|
165
157
|
return self.current.labels.get(value)
|
|
166
158
|
|
|
167
159
|
def get_deprecated(self, value: str) -> bool:
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from .annotation import SerialInspector, get_serial_annotation
|
|
4
|
+
from .serial_class import get_merged_serial_class_data
|
|
5
|
+
|
|
6
|
+
T = typing.TypeVar("T")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_serial_data(parsed_type: type[T]) -> SerialInspector[T] | None:
|
|
10
|
+
serial = get_serial_annotation(parsed_type)
|
|
11
|
+
if serial is None:
|
|
12
|
+
serial = get_merged_serial_class_data(parsed_type)
|
|
13
|
+
|
|
14
|
+
if serial is not None:
|
|
15
|
+
return SerialInspector(parsed_type, serial)
|
|
16
|
+
return None
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import dataclasses
|
|
2
2
|
import typing
|
|
3
3
|
|
|
4
|
+
from .annotation import SerialBase, SerialInspector, get_serial_annotation
|
|
5
|
+
|
|
4
6
|
T = typing.TypeVar("T")
|
|
5
7
|
|
|
6
8
|
|
|
@@ -17,7 +19,7 @@ class IdentityHashWrapper(typing.Generic[T]):
|
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
@dataclasses.dataclass(kw_only=True, frozen=True, eq=True)
|
|
20
|
-
class _SerialUnion:
|
|
22
|
+
class _SerialUnion(SerialBase):
|
|
21
23
|
"""
|
|
22
24
|
This class is to be kept private, to provide flexibility in registration/lookup.
|
|
23
25
|
Places that need the data should access it via help classes/methods.
|
|
@@ -25,16 +27,16 @@ class _SerialUnion:
|
|
|
25
27
|
|
|
26
28
|
# If specified, indicates the Union has a discriminator which should be used to
|
|
27
29
|
# determine which type to parse.
|
|
28
|
-
discriminator:
|
|
29
|
-
discriminator_map:
|
|
30
|
-
named_type_path: typing.Optional[str] = None
|
|
30
|
+
discriminator: str | None = None
|
|
31
|
+
discriminator_map: IdentityHashWrapper[dict[str, type]] | None = None
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
def serial_union_annotation(
|
|
34
35
|
*,
|
|
35
|
-
discriminator:
|
|
36
|
-
discriminator_map:
|
|
37
|
-
named_type_path:
|
|
36
|
+
discriminator: str | None = None,
|
|
37
|
+
discriminator_map: dict[str, type] | None = None,
|
|
38
|
+
named_type_path: str | None = None,
|
|
39
|
+
is_dynamic_allowed: bool = False,
|
|
38
40
|
) -> _SerialUnion:
|
|
39
41
|
return _SerialUnion(
|
|
40
42
|
discriminator=discriminator,
|
|
@@ -42,23 +44,21 @@ def serial_union_annotation(
|
|
|
42
44
|
if discriminator_map is not None
|
|
43
45
|
else None,
|
|
44
46
|
named_type_path=named_type_path,
|
|
47
|
+
from_decorator=True,
|
|
48
|
+
is_dynamic_allowed=is_dynamic_allowed,
|
|
45
49
|
)
|
|
46
50
|
|
|
47
51
|
|
|
48
52
|
def _get_serial_union(parsed_type: type[T]) -> _SerialUnion | None:
|
|
49
|
-
|
|
50
|
-
return None
|
|
51
|
-
metadata = parsed_type.__metadata__ # type:ignore[attr-defined]
|
|
52
|
-
if not isinstance(metadata, tuple) or len(metadata) != 1:
|
|
53
|
-
return None
|
|
54
|
-
serial = metadata[0]
|
|
53
|
+
serial = get_serial_annotation(parsed_type)
|
|
55
54
|
if not isinstance(serial, _SerialUnion):
|
|
56
55
|
return None
|
|
57
56
|
return serial
|
|
58
57
|
|
|
59
58
|
|
|
60
|
-
class SerialClassInspector(
|
|
59
|
+
class SerialClassInspector(SerialInspector[T]):
|
|
61
60
|
def __init__(self, parsed_type: type[T], serial_union: _SerialUnion) -> None:
|
|
61
|
+
super().__init__(parsed_type, serial_union)
|
|
62
62
|
self._parsed_type = parsed_type
|
|
63
63
|
self._serial_union = serial_union
|
|
64
64
|
|
|
@@ -66,11 +66,11 @@ class SerialClassInspector(typing.Generic[T]):
|
|
|
66
66
|
return typing.get_args(self._parsed_type)[0] # type:ignore[no-any-return]
|
|
67
67
|
|
|
68
68
|
@property
|
|
69
|
-
def discriminator(self) ->
|
|
69
|
+
def discriminator(self) -> str | None:
|
|
70
70
|
return self._serial_union.discriminator
|
|
71
71
|
|
|
72
72
|
@property
|
|
73
|
-
def discriminator_map(self) ->
|
|
73
|
+
def discriminator_map(self) -> dict[str, type] | None:
|
|
74
74
|
if self._serial_union.discriminator_map is None:
|
|
75
75
|
return None
|
|
76
76
|
return self._serial_union.discriminator_map.inner
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
from .convert_to_snakecase import convert_dict_to_snake_case
|
|
2
|
+
from .dataclasses import dict_fields as dict_fields
|
|
3
|
+
from .dataclasses import iterate_fields as iterate_fields
|
|
2
4
|
from .serialization_helpers import (
|
|
5
|
+
JsonValue,
|
|
3
6
|
serialize_for_api,
|
|
4
7
|
serialize_for_storage,
|
|
5
8
|
serialize_for_storage_dict,
|
|
@@ -10,4 +13,7 @@ __all__: list[str] = [
|
|
|
10
13
|
"serialize_for_api",
|
|
11
14
|
"serialize_for_storage",
|
|
12
15
|
"serialize_for_storage_dict",
|
|
16
|
+
"iterate_fields",
|
|
17
|
+
"dict_fields",
|
|
18
|
+
"JsonValue",
|
|
13
19
|
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from dataclasses import fields
|
|
2
|
+
from typing import TYPE_CHECKING, Any, Iterator
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from _typeshed import DataclassInstance
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def iterate_fields(d: "DataclassInstance") -> Iterator[tuple[str, Any]]:
|
|
9
|
+
for field in fields(d):
|
|
10
|
+
yield field.name, getattr(d, field.name)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def dict_fields(d: "DataclassInstance") -> dict[str, Any]:
|
|
14
|
+
return dict(iterate_fields(d))
|
|
@@ -25,6 +25,7 @@ from pkgs.serialization import (
|
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
from ._get_type_for_serialization import SerializationType, get_serialization_type
|
|
28
|
+
from .dataclasses import iterate_fields
|
|
28
29
|
|
|
29
30
|
# Inlined types which otherwise would import from types/base.py
|
|
30
31
|
JsonScalar = Union[str, float, bool, Decimal, None, datetime.datetime, datetime.date]
|
|
@@ -77,8 +78,16 @@ def _serialize_dict(d: dict[str, Any]) -> dict[str, JsonValue]:
|
|
|
77
78
|
return {k: serialize_for_storage(v) for k, v in d.items() if v != MISSING_SENTRY}
|
|
78
79
|
|
|
79
80
|
|
|
81
|
+
def _serialize_dataclass(d: Any) -> dict[str, JsonValue]:
|
|
82
|
+
return {
|
|
83
|
+
k: serialize_for_storage(v) for k, v in iterate_fields(d) if v != MISSING_SENTRY
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
80
87
|
def _to_string_value(value: Any) -> str:
|
|
81
|
-
assert isinstance(value, (Decimal, int))
|
|
88
|
+
assert isinstance(value, (Decimal, int)), (
|
|
89
|
+
f"Expecting decimal or int, received: {value} (type={type(value)})"
|
|
90
|
+
)
|
|
82
91
|
return str(value)
|
|
83
92
|
|
|
84
93
|
|
|
@@ -105,7 +114,7 @@ def _get_dataclass_conversion_lookups(dataclass_type: Any) -> DataclassConversio
|
|
|
105
114
|
if scd.has_to_string_value(key):
|
|
106
115
|
value_conversion_functions[key] = _to_string_value
|
|
107
116
|
elif scd.has_unconverted_value(key):
|
|
108
|
-
value_conversion_functions[key] =
|
|
117
|
+
value_conversion_functions[key] = serialize_for_storage
|
|
109
118
|
else:
|
|
110
119
|
value_conversion_functions[key] = serialize_for_api
|
|
111
120
|
|
|
@@ -121,7 +130,7 @@ def _convert_dataclass(d: Any) -> dict[str, JsonValue]:
|
|
|
121
130
|
conversions.key_conversions[k]: (
|
|
122
131
|
conversions.value_conversion_functions[k](v) if v is not None else None
|
|
123
132
|
)
|
|
124
|
-
for k, v in d
|
|
133
|
+
for k, v in iterate_fields(d)
|
|
125
134
|
if v != MISSING_SENTRY
|
|
126
135
|
}
|
|
127
136
|
|
|
@@ -169,14 +178,15 @@ def serialize_for_api(obj: Any) -> JsonValue:
|
|
|
169
178
|
serialization_type == SerializationType.UNKNOWN
|
|
170
179
|
): # performance optimization to not do function lookup
|
|
171
180
|
return obj # type: ignore
|
|
172
|
-
|
|
181
|
+
r = _CONVERSION_SERIALIZATION_FUNCS[serialization_type](obj)
|
|
182
|
+
return r
|
|
173
183
|
|
|
174
184
|
|
|
175
185
|
_SERIALIZATION_FUNCS_DICT: dict[
|
|
176
186
|
SerializationType, Callable[[Any], dict[str, JsonValue]]
|
|
177
187
|
] = {
|
|
178
188
|
SerializationType.DICT: _serialize_dict,
|
|
179
|
-
SerializationType.DATACLASS:
|
|
189
|
+
SerializationType.DATACLASS: _serialize_dataclass,
|
|
180
190
|
}
|
|
181
191
|
|
|
182
192
|
|
|
@@ -6,7 +6,6 @@ import os
|
|
|
6
6
|
import sys
|
|
7
7
|
from collections import defaultdict
|
|
8
8
|
from dataclasses import dataclass
|
|
9
|
-
from typing import TypeVar
|
|
10
9
|
|
|
11
10
|
from main.base.types import actions_registry_t
|
|
12
11
|
from pkgs.type_spec import builder
|
|
@@ -22,9 +21,6 @@ key_short_description = "short_description"
|
|
|
22
21
|
key_description = "description"
|
|
23
22
|
|
|
24
23
|
|
|
25
|
-
TypeT = TypeVar("TypeT")
|
|
26
|
-
|
|
27
|
-
|
|
28
24
|
class InvalidSpecException(Exception):
|
|
29
25
|
pass
|
|
30
26
|
|
|
@@ -64,7 +64,7 @@ def _emit_imports(
|
|
|
64
64
|
sorted([f"type {_action_namespace_name(item)}" for item in list(namespaces)])
|
|
65
65
|
)
|
|
66
66
|
out.write(
|
|
67
|
-
f'import {{ ActionsRegistryT, {namespaces_to_import} }} from "
|
|
67
|
+
f'import {{ ActionsRegistryT, {namespaces_to_import} }} from "unc_types"\n\n'
|
|
68
68
|
)
|
|
69
69
|
out.write(MODIFY_NOTICE)
|
|
70
70
|
return out.getvalue()
|
|
@@ -111,9 +111,7 @@ def _get_argument_type(arguments: str | None) -> str | None:
|
|
|
111
111
|
argument_parts = arguments.split(".")
|
|
112
112
|
namespace = argument_parts[0]
|
|
113
113
|
type_name = argument_parts[1]
|
|
114
|
-
|
|
115
|
-
return f"{_action_namespace_name(namespace)}.{type_name}"
|
|
116
|
-
return None
|
|
114
|
+
return f"{_action_namespace_name(namespace)}.{type_name}"
|
|
117
115
|
|
|
118
116
|
|
|
119
117
|
def _emit_action_definition(
|