UncountablePythonSDK 0.0.7__py3-none-any.whl → 0.0.92__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of UncountablePythonSDK might be problematic. Click here for more details.
- UncountablePythonSDK-0.0.92.dist-info/METADATA +61 -0
- UncountablePythonSDK-0.0.92.dist-info/RECORD +301 -0
- {UncountablePythonSDK-0.0.7.dist-info → UncountablePythonSDK-0.0.92.dist-info}/WHEEL +1 -1
- {UncountablePythonSDK-0.0.7.dist-info → UncountablePythonSDK-0.0.92.dist-info}/top_level.txt +1 -1
- docs/.gitignore +1 -0
- docs/conf.py +57 -0
- docs/index.md +13 -0
- docs/justfile +12 -0
- docs/quickstart.md +19 -0
- docs/requirements.txt +7 -0
- docs/static/favicons/android-chrome-192x192.png +0 -0
- docs/static/favicons/android-chrome-512x512.png +0 -0
- docs/static/favicons/apple-touch-icon.png +0 -0
- docs/static/favicons/browserconfig.xml +9 -0
- docs/static/favicons/favicon-16x16.png +0 -0
- docs/static/favicons/favicon-32x32.png +0 -0
- docs/static/favicons/manifest.json +18 -0
- docs/static/favicons/mstile-150x150.png +0 -0
- docs/static/favicons/safari-pinned-tab.svg +32 -0
- docs/static/logo_blue.png +0 -0
- examples/async_batch.py +35 -0
- examples/create_entity.py +22 -17
- examples/download_files.py +26 -0
- examples/edit_recipe_inputs.py +50 -0
- examples/integration-server/jobs/materials_auto/example_cron.py +18 -0
- examples/integration-server/jobs/materials_auto/example_wh.py +15 -0
- examples/integration-server/jobs/materials_auto/profile.yaml +43 -0
- examples/integration-server/pyproject.toml +224 -0
- examples/invoke_uploader.py +26 -0
- examples/set_recipe_metadata_file.py +40 -0
- examples/set_recipe_output_file_sdk.py +26 -0
- examples/upload_files.py +18 -0
- pkgs/argument_parser/__init__.py +5 -0
- pkgs/argument_parser/_is_enum.py +1 -6
- pkgs/argument_parser/argument_parser.py +232 -76
- pkgs/argument_parser/case_convert.py +4 -3
- pkgs/filesystem_utils/__init__.py +20 -0
- pkgs/filesystem_utils/_blob_session.py +137 -0
- pkgs/filesystem_utils/_gdrive_session.py +309 -0
- pkgs/filesystem_utils/_local_session.py +69 -0
- pkgs/filesystem_utils/_s3_session.py +117 -0
- pkgs/filesystem_utils/_sftp_session.py +147 -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/opaque_key.py +1 -1
- pkgs/serialization/serial_alias.py +47 -0
- pkgs/serialization/serial_class.py +65 -50
- 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/_get_type_for_serialization.py +1 -3
- pkgs/serialization_util/convert_to_snakecase.py +27 -0
- pkgs/serialization_util/dataclasses.py +14 -0
- pkgs/serialization_util/serialization_helpers.py +118 -73
- pkgs/strenum_compat/strenum_compat.py +1 -9
- pkgs/type_spec/actions_registry/__init__.py +0 -0
- pkgs/type_spec/actions_registry/__main__.py +126 -0
- pkgs/type_spec/actions_registry/emit_typescript.py +182 -0
- pkgs/type_spec/builder.py +475 -89
- pkgs/type_spec/config.py +24 -19
- pkgs/type_spec/emit_io_ts.py +5 -2
- pkgs/type_spec/emit_open_api.py +266 -32
- pkgs/type_spec/emit_open_api_util.py +32 -13
- pkgs/type_spec/emit_python.py +601 -150
- pkgs/type_spec/emit_typescript.py +74 -273
- pkgs/type_spec/emit_typescript_util.py +239 -5
- pkgs/type_spec/load_types.py +55 -10
- pkgs/type_spec/open_api_util.py +30 -41
- pkgs/type_spec/parts/base.py.prepart +4 -3
- pkgs/type_spec/type_info/emit_type_info.py +178 -16
- pkgs/type_spec/util.py +11 -11
- pkgs/type_spec/value_spec/__main__.py +3 -3
- pkgs/type_spec/value_spec/convert_type.py +8 -1
- pkgs/type_spec/value_spec/emit_python.py +13 -4
- uncountable/__init__.py +1 -2
- uncountable/core/__init__.py +12 -2
- uncountable/core/async_batch.py +37 -0
- uncountable/core/client.py +293 -43
- uncountable/core/environment.py +41 -0
- uncountable/core/file_upload.py +135 -0
- uncountable/core/types.py +17 -0
- uncountable/integration/__init__.py +0 -0
- uncountable/integration/cli.py +49 -0
- uncountable/integration/construct_client.py +51 -0
- uncountable/integration/cron.py +29 -0
- uncountable/integration/db/__init__.py +0 -0
- uncountable/integration/db/connect.py +18 -0
- uncountable/integration/db/session.py +25 -0
- uncountable/integration/entrypoint.py +13 -0
- uncountable/integration/executors/__init__.py +0 -0
- uncountable/integration/executors/executors.py +148 -0
- uncountable/integration/executors/generic_upload_executor.py +284 -0
- uncountable/integration/executors/script_executor.py +25 -0
- uncountable/integration/job.py +87 -0
- uncountable/integration/queue_runner/__init__.py +0 -0
- uncountable/integration/queue_runner/command_server/__init__.py +24 -0
- uncountable/integration/queue_runner/command_server/command_client.py +68 -0
- uncountable/integration/queue_runner/command_server/command_server.py +64 -0
- uncountable/integration/queue_runner/command_server/protocol/__init__.py +0 -0
- uncountable/integration/queue_runner/command_server/protocol/command_server.proto +22 -0
- uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.py +40 -0
- uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.pyi +38 -0
- uncountable/integration/queue_runner/command_server/protocol/command_server_pb2_grpc.py +129 -0
- uncountable/integration/queue_runner/command_server/types.py +52 -0
- uncountable/integration/queue_runner/datastore/__init__.py +3 -0
- uncountable/integration/queue_runner/datastore/datastore_sqlite.py +93 -0
- uncountable/integration/queue_runner/datastore/interface.py +19 -0
- uncountable/integration/queue_runner/datastore/model.py +17 -0
- uncountable/integration/queue_runner/job_scheduler.py +163 -0
- uncountable/integration/queue_runner/queue_runner.py +26 -0
- uncountable/integration/queue_runner/types.py +7 -0
- uncountable/integration/queue_runner/worker.py +119 -0
- uncountable/integration/scan_profiles.py +67 -0
- uncountable/integration/scheduler.py +150 -0
- uncountable/integration/secret_retrieval/__init__.py +3 -0
- uncountable/integration/secret_retrieval/retrieve_secret.py +93 -0
- uncountable/integration/server.py +117 -0
- uncountable/integration/telemetry.py +209 -0
- uncountable/integration/webhook_server/entrypoint.py +170 -0
- uncountable/types/__init__.py +151 -5
- uncountable/types/api/batch/execute_batch.py +15 -7
- uncountable/types/api/batch/execute_batch_load_async.py +42 -0
- uncountable/types/api/chemical/__init__.py +1 -0
- uncountable/types/api/chemical/convert_chemical_formats.py +63 -0
- uncountable/types/api/entity/create_entities.py +23 -10
- uncountable/types/api/entity/create_entity.py +21 -12
- uncountable/types/api/entity/get_entities_data.py +19 -29
- uncountable/types/api/entity/grant_entity_permissions.py +48 -0
- uncountable/types/api/entity/list_entities.py +28 -20
- uncountable/types/api/entity/lock_entity.py +45 -0
- uncountable/types/api/entity/resolve_entity_ids.py +19 -7
- uncountable/types/api/entity/set_entity_field_values.py +44 -0
- uncountable/types/api/entity/set_values.py +13 -28
- uncountable/types/api/entity/transition_entity_phase.py +80 -0
- 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 +44 -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/__init__.py +1 -0
- uncountable/types/api/id_source/list_id_source.py +56 -0
- uncountable/types/api/id_source/match_id_source.py +54 -0
- uncountable/types/api/input_groups/get_input_group_names.py +18 -7
- uncountable/types/api/inputs/create_inputs.py +25 -24
- uncountable/types/api/inputs/get_input_data.py +37 -31
- uncountable/types/api/inputs/get_input_names.py +20 -9
- uncountable/types/api/inputs/get_inputs_data.py +33 -27
- uncountable/types/api/inputs/set_input_attribute_values.py +18 -13
- 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/material_families/__init__.py +1 -0
- uncountable/types/api/material_families/update_entity_material_families.py +48 -0
- uncountable/types/api/outputs/get_output_data.py +38 -29
- uncountable/types/api/outputs/get_output_names.py +20 -9
- uncountable/types/api/outputs/resolve_output_conditions.py +23 -10
- uncountable/types/api/permissions/__init__.py +1 -0
- uncountable/types/api/permissions/set_core_permissions.py +105 -0
- uncountable/types/api/project/get_projects.py +23 -19
- uncountable/types/api/project/get_projects_data.py +26 -43
- uncountable/types/api/recipe_links/__init__.py +1 -0
- uncountable/types/api/recipe_links/create_recipe_link.py +46 -0
- uncountable/types/api/recipe_links/remove_recipe_link.py +45 -0
- uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py +21 -10
- uncountable/types/api/recipes/add_recipe_to_project.py +42 -0
- uncountable/types/api/recipes/archive_recipes.py +42 -0
- uncountable/types/api/recipes/associate_recipe_as_input.py +44 -0
- uncountable/types/api/recipes/associate_recipe_as_lot.py +43 -0
- uncountable/types/api/recipes/clear_recipe_outputs.py +42 -0
- uncountable/types/api/recipes/create_recipe.py +51 -0
- uncountable/types/api/recipes/create_recipes.py +25 -24
- uncountable/types/api/recipes/disassociate_recipe_as_input.py +42 -0
- uncountable/types/api/recipes/edit_recipe_inputs.py +283 -0
- uncountable/types/api/recipes/get_column_calculation_values.py +58 -0
- uncountable/types/api/recipes/get_curve.py +13 -27
- uncountable/types/api/recipes/get_recipe_calculations.py +21 -21
- uncountable/types/api/recipes/get_recipe_links.py +14 -6
- uncountable/types/api/recipes/get_recipe_names.py +18 -7
- uncountable/types/api/recipes/get_recipe_output_metadata.py +18 -19
- uncountable/types/api/recipes/get_recipes_data.py +83 -144
- uncountable/types/api/recipes/lock_recipes.py +63 -0
- uncountable/types/api/recipes/remove_recipe_from_project.py +42 -0
- uncountable/types/api/recipes/set_recipe_inputs.py +21 -11
- uncountable/types/api/recipes/set_recipe_metadata.py +43 -0
- uncountable/types/api/recipes/set_recipe_output_annotations.py +115 -0
- uncountable/types/api/recipes/set_recipe_output_file.py +56 -0
- uncountable/types/api/recipes/set_recipe_outputs.py +28 -15
- uncountable/types/api/recipes/set_recipe_tags.py +109 -0
- uncountable/types/api/recipes/unarchive_recipes.py +41 -0
- uncountable/types/api/recipes/unlock_recipes.py +50 -0
- uncountable/types/api/triggers/__init__.py +1 -0
- uncountable/types/api/triggers/run_trigger.py +43 -0
- uncountable/types/api/uploader/__init__.py +1 -0
- uncountable/types/api/uploader/invoke_uploader.py +47 -0
- uncountable/types/async_batch.py +13 -0
- uncountable/types/async_batch_processor.py +384 -0
- uncountable/types/async_batch_t.py +97 -0
- uncountable/types/async_jobs.py +9 -0
- uncountable/types/async_jobs_t.py +53 -0
- uncountable/types/auth_retrieval.py +12 -0
- uncountable/types/auth_retrieval_t.py +75 -0
- uncountable/types/base.py +5 -78
- uncountable/types/base_t.py +85 -0
- uncountable/types/calculations.py +8 -0
- uncountable/types/calculations_t.py +27 -0
- uncountable/types/chemical_structure.py +8 -0
- uncountable/types/chemical_structure_t.py +28 -0
- uncountable/types/client_base.py +1115 -76
- uncountable/types/client_config.py +8 -0
- uncountable/types/client_config_t.py +26 -0
- uncountable/types/curves.py +10 -0
- uncountable/types/curves_t.py +51 -0
- uncountable/types/entity.py +8 -266
- uncountable/types/entity_t.py +393 -0
- uncountable/types/experiment_groups.py +8 -0
- uncountable/types/experiment_groups_t.py +27 -0
- uncountable/types/field_values.py +17 -23
- uncountable/types/field_values_t.py +204 -0
- uncountable/types/fields.py +8 -0
- uncountable/types/fields_t.py +28 -0
- uncountable/types/generic_upload.py +15 -0
- uncountable/types/generic_upload_t.py +119 -0
- uncountable/types/id_source.py +12 -0
- uncountable/types/id_source_t.py +68 -0
- uncountable/types/identifier.py +11 -0
- uncountable/types/identifier_t.py +63 -0
- uncountable/types/input_attributes.py +8 -0
- uncountable/types/input_attributes_t.py +30 -0
- uncountable/types/inputs.py +11 -0
- uncountable/types/inputs_t.py +83 -0
- uncountable/types/integration_server.py +9 -0
- uncountable/types/integration_server_t.py +42 -0
- uncountable/types/job_definition.py +27 -0
- uncountable/types/job_definition_t.py +260 -0
- uncountable/types/outputs.py +8 -0
- uncountable/types/outputs_t.py +30 -0
- uncountable/types/overrides.py +10 -0
- uncountable/types/overrides_t.py +49 -0
- uncountable/types/permissions.py +8 -0
- uncountable/types/permissions_t.py +46 -0
- uncountable/types/phases.py +8 -0
- uncountable/types/phases_t.py +27 -0
- uncountable/types/post_base.py +8 -0
- uncountable/types/post_base_t.py +30 -0
- uncountable/types/queued_job.py +16 -0
- uncountable/types/queued_job_t.py +123 -0
- uncountable/types/recipe_identifiers.py +12 -0
- uncountable/types/recipe_identifiers_t.py +76 -0
- uncountable/types/recipe_inputs.py +9 -0
- uncountable/types/recipe_inputs_t.py +30 -0
- uncountable/types/recipe_links.py +4 -44
- uncountable/types/recipe_links_t.py +54 -0
- uncountable/types/recipe_metadata.py +10 -0
- uncountable/types/recipe_metadata_t.py +58 -0
- uncountable/types/recipe_output_metadata.py +8 -0
- uncountable/types/recipe_output_metadata_t.py +28 -0
- uncountable/types/recipe_tags.py +8 -0
- uncountable/types/recipe_tags_t.py +27 -0
- uncountable/types/recipe_workflow_steps.py +14 -0
- uncountable/types/recipe_workflow_steps_t.py +95 -0
- uncountable/types/recipes.py +8 -0
- uncountable/types/recipes_t.py +25 -0
- uncountable/types/response.py +8 -0
- uncountable/types/response_t.py +26 -0
- uncountable/types/secret_retrieval.py +12 -0
- uncountable/types/secret_retrieval_t.py +75 -0
- uncountable/types/units.py +8 -0
- uncountable/types/units_t.py +27 -0
- uncountable/types/users.py +8 -0
- uncountable/types/users_t.py +28 -0
- uncountable/types/webhook_job.py +9 -0
- uncountable/types/webhook_job_t.py +37 -0
- uncountable/types/workflows.py +9 -0
- uncountable/types/workflows_t.py +39 -0
- UncountablePythonSDK-0.0.7.dist-info/METADATA +0 -27
- UncountablePythonSDK-0.0.7.dist-info/RECORD +0 -119
- examples/recipe-import/importer.py +0 -39
- type_spec/external/api/batch/execute_batch.yaml +0 -56
- type_spec/external/api/entity/create_entities.yaml +0 -33
- type_spec/external/api/entity/create_entity.yaml +0 -39
- type_spec/external/api/entity/get_entities_data.yaml +0 -55
- type_spec/external/api/entity/list_entities.yaml +0 -62
- type_spec/external/api/entity/resolve_entity_ids.yaml +0 -29
- type_spec/external/api/entity/set_values.yaml +0 -45
- type_spec/external/api/input_groups/get_input_group_names.yaml +0 -29
- type_spec/external/api/inputs/create_inputs.yaml +0 -61
- type_spec/external/api/inputs/get_input_data.yaml +0 -108
- type_spec/external/api/inputs/get_input_names.yaml +0 -38
- type_spec/external/api/inputs/get_inputs_data.yaml +0 -95
- type_spec/external/api/inputs/set_input_attribute_values.yaml +0 -37
- type_spec/external/api/outputs/get_output_data.yaml +0 -103
- 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/project/get_projects.yaml +0 -52
- type_spec/external/api/project/get_projects_data.yaml +0 -86
- type_spec/external/api/recipe_metadata/get_recipe_metadata_data.yaml +0 -41
- type_spec/external/api/recipes/create_recipes.yaml +0 -60
- type_spec/external/api/recipes/get_curve.yaml +0 -50
- type_spec/external/api/recipes/get_recipe_calculations.yaml +0 -49
- 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 -49
- type_spec/external/api/recipes/get_recipes_data.yaml +0 -372
- type_spec/external/api/recipes/set_recipe_inputs.yaml +0 -36
- type_spec/external/api/recipes/set_recipe_outputs.yaml +0 -56
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import dataclasses
|
|
3
|
+
import decimal
|
|
3
4
|
import io
|
|
4
5
|
import json
|
|
5
|
-
from typing import Any,
|
|
6
|
+
from typing import Any, cast
|
|
6
7
|
|
|
7
|
-
from main.base.types import data_t
|
|
8
|
-
from main.base.types.
|
|
8
|
+
from main.base.types import data_t, type_info_t
|
|
9
|
+
from main.base.types.base_t import PureJsonValue
|
|
9
10
|
from pkgs.argument_parser import CachedParser
|
|
10
|
-
from pkgs.serialization_util import
|
|
11
|
+
from pkgs.serialization_util import (
|
|
12
|
+
serialize_for_api,
|
|
13
|
+
serialize_for_storage,
|
|
14
|
+
)
|
|
11
15
|
|
|
12
16
|
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
|
|
@@ -23,7 +27,7 @@ def type_path_of(stype: builder.SpecType) -> object: # NamePath
|
|
|
23
27
|
extended scopes, generics, and enum literal values.
|
|
24
28
|
- Scoped Type: [ (namespace-string)..., type-string ]
|
|
25
29
|
- Instance Type: [ "$instance", Scoped-Type-Base, [TypePath-Parameters...] ]
|
|
26
|
-
- Literal Type: [ "$literal", [ "$value", value ]... ]
|
|
30
|
+
- Literal Type: [ "$literal", [ "$value", value, value-type-string ]... ]
|
|
27
31
|
|
|
28
32
|
@return (string-specific, multiple-types)
|
|
29
33
|
"""
|
|
@@ -34,11 +38,15 @@ def type_path_of(stype: builder.SpecType) -> object: # NamePath
|
|
|
34
38
|
|
|
35
39
|
if isinstance(stype, builder.SpecTypeInstance):
|
|
36
40
|
if stype.defn_type.name == builder.BaseTypeName.s_literal:
|
|
37
|
-
parts: list[
|
|
41
|
+
parts: list[object] = ["$literal"]
|
|
38
42
|
for parameter in stype.parameters:
|
|
39
43
|
assert isinstance(parameter, builder.SpecTypeLiteralWrapper)
|
|
40
44
|
# This allows expansion to enum literal values later
|
|
41
|
-
parts.append([
|
|
45
|
+
parts.append([
|
|
46
|
+
"$value",
|
|
47
|
+
parameter.value,
|
|
48
|
+
type_path_of(parameter.value_type),
|
|
49
|
+
])
|
|
42
50
|
return parts
|
|
43
51
|
|
|
44
52
|
return [
|
|
@@ -65,12 +73,21 @@ def _dict_null_strip(data: dict[str, object]) -> dict[str, object]:
|
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
|
|
76
|
+
class JsonEncoder(json.JSONEncoder):
|
|
77
|
+
"""We have some defaults of special types that we need to emit"""
|
|
78
|
+
|
|
79
|
+
def default(self, obj: object) -> object:
|
|
80
|
+
if isinstance(obj, decimal.Decimal):
|
|
81
|
+
return str(obj)
|
|
82
|
+
return json.JSONEncoder.default(self, obj)
|
|
83
|
+
|
|
84
|
+
|
|
68
85
|
def emit_type_info(build: builder.SpecBuilder, output: str) -> None:
|
|
69
86
|
type_map = _build_map_all(build)
|
|
70
87
|
|
|
71
88
|
# sort for stability, indent for smaller diffs
|
|
72
89
|
stripped = _dict_null_strip(dataclasses.asdict(type_map))
|
|
73
|
-
serial = json.dumps(stripped, sort_keys=True, indent=2)
|
|
90
|
+
serial = json.dumps(stripped, sort_keys=True, indent=2, cls=JsonEncoder)
|
|
74
91
|
type_map_out = io.StringIO()
|
|
75
92
|
type_map_out.write(MODIFY_NOTICE)
|
|
76
93
|
type_map_out.write(f"export const TYPE_MAP = {serial}")
|
|
@@ -117,7 +134,7 @@ class MapStringEnum(MapTypeBase):
|
|
|
117
134
|
values: dict[str, str]
|
|
118
135
|
|
|
119
136
|
|
|
120
|
-
MapType
|
|
137
|
+
type MapType = MapTypeObject | MapTypeAlias | MapStringEnum
|
|
121
138
|
|
|
122
139
|
|
|
123
140
|
@dataclasses.dataclass
|
|
@@ -148,6 +165,129 @@ def _build_map_all(build: builder.SpecBuilder) -> MapAll:
|
|
|
148
165
|
return map_all
|
|
149
166
|
|
|
150
167
|
|
|
168
|
+
@dataclasses.dataclass(kw_only=True)
|
|
169
|
+
class InheritablePropertyParts:
|
|
170
|
+
"""This uses only the "soft" information for now, things that aren't relevant
|
|
171
|
+
to the language emitted types. There are some fields that should be inherited
|
|
172
|
+
at that level, but that needs to be done in builder. When that is done, the
|
|
173
|
+
"label" and "desc" could probably be removed from this list."""
|
|
174
|
+
|
|
175
|
+
label: str | None = None
|
|
176
|
+
desc: str | None = None
|
|
177
|
+
ext_info: type_info_t.ExtInfo | None = None
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _extract_inheritable_property_parts(
|
|
181
|
+
stype: builder.SpecTypeDefnObject,
|
|
182
|
+
prop: builder.SpecProperty,
|
|
183
|
+
) -> InheritablePropertyParts:
|
|
184
|
+
if not stype.is_base and isinstance(stype.base, builder.SpecTypeDefn):
|
|
185
|
+
base_prop = (stype.base.properties or {}).get(prop.name)
|
|
186
|
+
if base_prop is None:
|
|
187
|
+
base_parts = InheritablePropertyParts()
|
|
188
|
+
else:
|
|
189
|
+
base_parts = _extract_inheritable_property_parts(stype.base, base_prop)
|
|
190
|
+
# Layout should not be inherited, as it'd end up hiding properties in the derived type
|
|
191
|
+
if base_parts.ext_info is not None:
|
|
192
|
+
base_parts.ext_info.layout = None
|
|
193
|
+
else:
|
|
194
|
+
base_parts = InheritablePropertyParts()
|
|
195
|
+
|
|
196
|
+
label = prop.label or base_parts.label
|
|
197
|
+
desc = prop.desc or base_parts.desc
|
|
198
|
+
local_ext_info = _parse_ext_info(prop.ext_info)
|
|
199
|
+
if local_ext_info is None:
|
|
200
|
+
ext_info = base_parts.ext_info
|
|
201
|
+
elif base_parts.ext_info is None:
|
|
202
|
+
ext_info = local_ext_info
|
|
203
|
+
else:
|
|
204
|
+
ext_info = dataclasses.replace(
|
|
205
|
+
local_ext_info,
|
|
206
|
+
**{
|
|
207
|
+
field.name: getattr(base_parts.ext_info, field.name)
|
|
208
|
+
for field in dataclasses.fields(type_info_t.ExtInfo)
|
|
209
|
+
if getattr(base_parts.ext_info, field.name) is not None
|
|
210
|
+
},
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
return InheritablePropertyParts(label=label, desc=desc, ext_info=ext_info)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
ExtInfoLayout = dict[str, set[str]]
|
|
217
|
+
ALL_FIELDS_GROUP = "*all_fields"
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _extract_and_validate_layout(
|
|
221
|
+
stype: builder.SpecTypeDefnObject,
|
|
222
|
+
ext_info: type_info_t.ExtInfo,
|
|
223
|
+
base_layout: ExtInfoLayout | None,
|
|
224
|
+
) -> ExtInfoLayout:
|
|
225
|
+
"""
|
|
226
|
+
Produce a map of groups to fields, for validation.
|
|
227
|
+
"""
|
|
228
|
+
if ext_info.layout is None:
|
|
229
|
+
return {}
|
|
230
|
+
assert stype.properties is not None
|
|
231
|
+
|
|
232
|
+
all_fields_group: set[str] = set()
|
|
233
|
+
layout: ExtInfoLayout = {ALL_FIELDS_GROUP: all_fields_group}
|
|
234
|
+
|
|
235
|
+
for group in ext_info.layout.groups:
|
|
236
|
+
fields = set(group.fields or [])
|
|
237
|
+
for field in fields:
|
|
238
|
+
assert field in stype.properties, f"layout-refers-to-missing-field:{field}"
|
|
239
|
+
|
|
240
|
+
local_ref_name = None
|
|
241
|
+
if group.ref_name is not None:
|
|
242
|
+
assert base_layout is None or base_layout.get(group.ref_name) is None, (
|
|
243
|
+
f"group-name-duplicate-in-base:{group.ref_name}"
|
|
244
|
+
)
|
|
245
|
+
local_ref_name = group.ref_name
|
|
246
|
+
|
|
247
|
+
if group.extends:
|
|
248
|
+
assert base_layout is not None, "missing-base-layout"
|
|
249
|
+
base_group = base_layout.get(group.extends)
|
|
250
|
+
assert base_group is not None, f"missing-base-group:{group.extends}"
|
|
251
|
+
fields.update(base_group)
|
|
252
|
+
local_ref_name = group.extends
|
|
253
|
+
|
|
254
|
+
assert local_ref_name not in layout, f"duplicate-group:{local_ref_name}"
|
|
255
|
+
if local_ref_name is not None:
|
|
256
|
+
layout[local_ref_name] = fields
|
|
257
|
+
all_fields_group.update(fields)
|
|
258
|
+
|
|
259
|
+
for group_ref_name in base_layout or {}:
|
|
260
|
+
assert group_ref_name in layout, f"missing-base-group:{group_ref_name}"
|
|
261
|
+
|
|
262
|
+
for prop_ref_name in stype.properties:
|
|
263
|
+
assert prop_ref_name in all_fields_group, (
|
|
264
|
+
f"layout-missing-field:{prop_ref_name}"
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
return layout
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _validate_type_ext_info(
|
|
271
|
+
stype: builder.SpecTypeDefnObject,
|
|
272
|
+
) -> tuple[ExtInfoLayout | None, type_info_t.ExtInfo | None]:
|
|
273
|
+
ext_info = _parse_ext_info(stype.ext_info)
|
|
274
|
+
if ext_info is None:
|
|
275
|
+
return None, None
|
|
276
|
+
|
|
277
|
+
if ext_info.label_fields is not None:
|
|
278
|
+
assert stype.properties is not None
|
|
279
|
+
for name in ext_info.label_fields:
|
|
280
|
+
prop = stype.properties.get(name)
|
|
281
|
+
assert prop is not None, f"missing-label-field:{name}"
|
|
282
|
+
|
|
283
|
+
if not stype.is_base and isinstance(stype.base, builder.SpecTypeDefnObject):
|
|
284
|
+
base_layout, _ = _validate_type_ext_info(stype.base)
|
|
285
|
+
else:
|
|
286
|
+
base_layout = None
|
|
287
|
+
|
|
288
|
+
return _extract_and_validate_layout(stype, ext_info, base_layout), ext_info
|
|
289
|
+
|
|
290
|
+
|
|
151
291
|
def _build_map_type(
|
|
152
292
|
build: builder.SpecBuilder, stype: builder.SpecTypeDefn
|
|
153
293
|
) -> MapType | None:
|
|
@@ -158,6 +298,8 @@ def _build_map_type(
|
|
|
158
298
|
and not stype.is_base
|
|
159
299
|
and stype.base is not None
|
|
160
300
|
):
|
|
301
|
+
_, ext_info = _validate_type_ext_info(stype)
|
|
302
|
+
|
|
161
303
|
properties: dict[str, MapProperty] = {}
|
|
162
304
|
map_type = MapTypeObject(
|
|
163
305
|
type_name=stype.name,
|
|
@@ -165,19 +307,22 @@ def _build_map_type(
|
|
|
165
307
|
properties=properties,
|
|
166
308
|
desc=stype.desc,
|
|
167
309
|
base_type_path=type_path_of(stype.base),
|
|
168
|
-
ext_info=
|
|
310
|
+
ext_info=serialize_for_api(ext_info), # type: ignore[arg-type]
|
|
169
311
|
)
|
|
170
312
|
|
|
171
313
|
if stype.properties is not None:
|
|
172
314
|
for prop in stype.properties.values():
|
|
315
|
+
parts = _extract_inheritable_property_parts(stype, prop)
|
|
316
|
+
# Propertis can't have layouts
|
|
317
|
+
assert parts.ext_info is None or parts.ext_info.layout is None
|
|
173
318
|
map_property = MapProperty(
|
|
174
319
|
type_name=prop.name,
|
|
175
|
-
label=
|
|
320
|
+
label=parts.label,
|
|
176
321
|
api_name=ts_name(prop.name, prop.name_case),
|
|
177
322
|
extant=prop.extant,
|
|
178
323
|
type_path=type_path_of(prop.spec_type),
|
|
179
|
-
ext_info=
|
|
180
|
-
desc=
|
|
324
|
+
ext_info=serialize_for_api(parts.ext_info), # type: ignore[arg-type]
|
|
325
|
+
desc=parts.desc,
|
|
181
326
|
default=prop.default,
|
|
182
327
|
)
|
|
183
328
|
map_type.properties[prop.name] = map_property
|
|
@@ -194,6 +339,19 @@ def _build_map_type(
|
|
|
194
339
|
discriminator=stype.discriminator,
|
|
195
340
|
)
|
|
196
341
|
|
|
342
|
+
if isinstance(stype, builder.SpecTypeDefnUnion):
|
|
343
|
+
# Emit as a basic alias for now, as the front-end supports only those for now
|
|
344
|
+
# IMPROVE: We should emit a proper union type and support that
|
|
345
|
+
backing = stype.get_backing_type()
|
|
346
|
+
return MapTypeAlias(
|
|
347
|
+
type_name=stype.name,
|
|
348
|
+
label=stype.label,
|
|
349
|
+
desc=stype.desc,
|
|
350
|
+
alias_type_path=type_path_of(backing),
|
|
351
|
+
ext_info=_convert_ext_info(stype.ext_info),
|
|
352
|
+
discriminator=stype.discriminator,
|
|
353
|
+
)
|
|
354
|
+
|
|
197
355
|
if isinstance(stype, builder.SpecTypeDefnStringEnum):
|
|
198
356
|
return MapStringEnum(
|
|
199
357
|
type_name=stype.name,
|
|
@@ -211,7 +369,7 @@ def _build_map_type(
|
|
|
211
369
|
return None
|
|
212
370
|
|
|
213
371
|
|
|
214
|
-
def
|
|
372
|
+
def _parse_ext_info(in_ext: Any) -> type_info_t.ExtInfo | None:
|
|
215
373
|
if in_ext is None:
|
|
216
374
|
return None
|
|
217
375
|
assert isinstance(in_ext, dict)
|
|
@@ -229,6 +387,10 @@ def _convert_ext_info(in_ext: Any) -> Optional[PureJsonValue]:
|
|
|
229
387
|
df["result_type"] = serialize_for_storage(converted)
|
|
230
388
|
mod_ext["data_format"] = df
|
|
231
389
|
|
|
232
|
-
|
|
390
|
+
return ext_info_parser.parse_storage(mod_ext)
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def _convert_ext_info(in_ext: Any) -> PureJsonValue | None:
|
|
233
394
|
# we need to convert this to API storage since it'll be used as-is in the UI
|
|
395
|
+
parsed = _parse_ext_info(in_ext)
|
|
234
396
|
return cast(PureJsonValue, serialize_for_api(parsed))
|
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,11 +29,11 @@ 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
|
-
ParsedTypePath =
|
|
36
|
+
ParsedTypePath = list[ParsedTypePart]
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
@dataclass
|
|
@@ -48,7 +48,7 @@ def consume_parameter(
|
|
|
48
48
|
) -> ConsumedParameter:
|
|
49
49
|
if bits[at] != "'":
|
|
50
50
|
return ConsumedParameter(at=at, part=ParsedTypePart(name=bits[at]))
|
|
51
|
-
quote_stack:
|
|
51
|
+
quote_stack: list[str] = []
|
|
52
52
|
at += 1
|
|
53
53
|
while at < len(bits):
|
|
54
54
|
if bits[at] == "'":
|
|
@@ -68,7 +68,7 @@ def parse_type_str(type_str: str) -> ParsedTypePath:
|
|
|
68
68
|
"""
|
|
69
69
|
IMPROVE: will not detect all errors yet, focuses on correct cases
|
|
70
70
|
"""
|
|
71
|
-
raw_bits:
|
|
71
|
+
raw_bits: list[str] = re.split(r"([.<>,'])", type_str)
|
|
72
72
|
bits = [
|
|
73
73
|
stripped_bit
|
|
74
74
|
for stripped_bit in (padded_bit.strip() for padded_bit in raw_bits)
|
|
@@ -81,7 +81,7 @@ def parse_type_str(type_str: str) -> ParsedTypePath:
|
|
|
81
81
|
cur_path = result
|
|
82
82
|
cur_path.append(cur_part)
|
|
83
83
|
|
|
84
|
-
path_stack:
|
|
84
|
+
path_stack: list[ParsedTypePath] = []
|
|
85
85
|
|
|
86
86
|
at = 1
|
|
87
87
|
while at < len(bits):
|
|
@@ -156,19 +156,19 @@ def is_valid_property_name(name: str) -> bool:
|
|
|
156
156
|
return re_pattern_property_name.match(name) is not None
|
|
157
157
|
|
|
158
158
|
|
|
159
|
-
def check_fields(data:
|
|
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
|
-
def split_any_name(name: str) ->
|
|
165
|
+
def split_any_name(name: str) -> list[str]:
|
|
166
166
|
"""
|
|
167
167
|
Splits a name on case and underscores.
|
|
168
168
|
myName => [my, name]
|
|
169
169
|
my_name => [my, name]
|
|
170
170
|
"""
|
|
171
|
-
bits:
|
|
171
|
+
bits: list[str] = re_pattern_split_name.split(name)
|
|
172
172
|
return [s.lower() for s in filter(lambda x: x is not None and x != "_", bits)]
|
|
173
173
|
|
|
174
174
|
|
|
@@ -17,12 +17,12 @@ If null is allowed as a legitimate value, such as in conditionals like `if`, the
|
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
19
|
import sys
|
|
20
|
-
from typing import
|
|
20
|
+
from typing import TypeVar, cast
|
|
21
21
|
|
|
22
22
|
import regex as re
|
|
23
|
-
import yaml
|
|
24
23
|
|
|
25
24
|
from main.base.types import base_t, value_spec_t
|
|
25
|
+
from pkgs.serialization import yaml
|
|
26
26
|
|
|
27
27
|
from ..util import parse_type_str, rewrite_file
|
|
28
28
|
from .convert_type import convert_to_value_spec_type
|
|
@@ -177,7 +177,7 @@ def main() -> None:
|
|
|
177
177
|
raise Exception(f"missing-{key}:{get_where()}")
|
|
178
178
|
return cast(base_t.PureJsonValue, x)
|
|
179
179
|
|
|
180
|
-
def get_as(node: base_t.PureJsonValue, key: str, type_:
|
|
180
|
+
def get_as(node: base_t.PureJsonValue, key: str, type_: type[TypeT]) -> TypeT:
|
|
181
181
|
raw = get(node, key)
|
|
182
182
|
assert isinstance(raw, type_)
|
|
183
183
|
|
|
@@ -25,10 +25,17 @@ TYPE_MAP = {
|
|
|
25
25
|
"List": MappedType(base_type=value_spec_t.BaseType.LIST, param_count=1),
|
|
26
26
|
"Optional": MappedType(base_type=value_spec_t.BaseType.OPTIONAL, param_count=1),
|
|
27
27
|
"String": MappedType(base_type=value_spec_t.BaseType.STRING),
|
|
28
|
-
"Union": MappedType(
|
|
28
|
+
"Union": MappedType(
|
|
29
|
+
base_type=value_spec_t.BaseType.UNION, variable_param_count=True
|
|
30
|
+
),
|
|
29
31
|
# not part of type_spec's types now
|
|
30
32
|
"Symbol": MappedType(base_type=value_spec_t.BaseType.SYMBOL),
|
|
31
33
|
"Any": MappedType(base_type=value_spec_t.BaseType.ANY),
|
|
34
|
+
"None": MappedType(base_type=value_spec_t.BaseType.NONE),
|
|
35
|
+
"Tuple": MappedType(
|
|
36
|
+
base_type=value_spec_t.BaseType.TUPLE, variable_param_count=True
|
|
37
|
+
),
|
|
38
|
+
"Never": MappedType(base_type=value_spec_t.BaseType.NEVER),
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
|
|
@@ -22,6 +22,7 @@ def emit_functions(functions: list[value_spec_t.Function]) -> str:
|
|
|
22
22
|
out.write(
|
|
23
23
|
f"""{MODIFY_NOTICE}
|
|
24
24
|
{LINT_HEADER}
|
|
25
|
+
import datetime
|
|
25
26
|
from typing import cast, Union
|
|
26
27
|
|
|
27
28
|
from decimal import Decimal
|
|
@@ -159,7 +160,9 @@ def _emit_function(function: value_spec_t.Function, indent: str) -> str:
|
|
|
159
160
|
sub_indent = indent + INDENT
|
|
160
161
|
out.write(f"{_function_symbol_name(function)} = value_spec_t.Function(\n")
|
|
161
162
|
out.write(f"{sub_indent}name={encode_common_string(function.name)},\n")
|
|
162
|
-
out.write(
|
|
163
|
+
out.write(
|
|
164
|
+
f"{sub_indent}description={encode_common_string(function.description)},\n"
|
|
165
|
+
)
|
|
163
166
|
out.write(f"{sub_indent}brief={encode_common_string(function.brief)},\n")
|
|
164
167
|
out.write(
|
|
165
168
|
f"{sub_indent}return_value={_emit_function_return(function.return_value, sub_indent)},\n"
|
|
@@ -183,16 +186,22 @@ def _emit_argument(argument: value_spec_t.FunctionArgument, indent: str) -> str:
|
|
|
183
186
|
out.write("value_spec_t.FunctionArgument(\n")
|
|
184
187
|
out.write(f"{sub_indent}ref_name={encode_common_string(argument.ref_name)},\n")
|
|
185
188
|
out.write(f"{sub_indent}name={encode_common_string(argument.name)},\n")
|
|
186
|
-
out.write(
|
|
189
|
+
out.write(
|
|
190
|
+
f"{sub_indent}description={encode_common_string(argument.description)},\n"
|
|
191
|
+
)
|
|
187
192
|
out.write(f"{sub_indent}pass_null={str(argument.pass_null)},\n")
|
|
188
|
-
out.write(
|
|
193
|
+
out.write(
|
|
194
|
+
f"{sub_indent}extant=value_spec_t.ArgumentExtant.{argument.extant.name},\n"
|
|
195
|
+
)
|
|
189
196
|
out.write(f"{sub_indent}type={_emit_type(argument.type, sub_indent)},\n")
|
|
190
197
|
out.write(f"{indent})")
|
|
191
198
|
|
|
192
199
|
return out.getvalue()
|
|
193
200
|
|
|
194
201
|
|
|
195
|
-
def _emit_function_return(
|
|
202
|
+
def _emit_function_return(
|
|
203
|
+
return_value: value_spec_t.FunctionReturn, indent: str
|
|
204
|
+
) -> str:
|
|
196
205
|
out = io.StringIO()
|
|
197
206
|
|
|
198
207
|
sub_indent = indent + INDENT
|
uncountable/__init__.py
CHANGED
uncountable/core/__init__.py
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
from .
|
|
1
|
+
from .async_batch import AsyncBatchProcessor
|
|
2
|
+
from .client import Client
|
|
3
|
+
from .file_upload import MediaFileUpload, UploadedFile
|
|
4
|
+
from .types import AuthDetailsApiKey, AuthDetailsOAuth
|
|
2
5
|
|
|
3
|
-
__all__: list[str] = [
|
|
6
|
+
__all__: list[str] = [
|
|
7
|
+
"AuthDetailsApiKey",
|
|
8
|
+
"AuthDetailsOAuth",
|
|
9
|
+
"AsyncBatchProcessor",
|
|
10
|
+
"Client",
|
|
11
|
+
"MediaFileUpload",
|
|
12
|
+
"UploadedFile",
|
|
13
|
+
]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from uncountable.core.client import Client
|
|
2
|
+
from uncountable.types import async_batch_t, base_t
|
|
3
|
+
from uncountable.types.async_batch import AsyncBatchRequest
|
|
4
|
+
from uncountable.types.async_batch_processor import AsyncBatchProcessorBase
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AsyncBatchSubmissionError(Exception):
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AsyncBatchProcessor(AsyncBatchProcessorBase):
|
|
12
|
+
_client: Client
|
|
13
|
+
_queue: list[AsyncBatchRequest]
|
|
14
|
+
_submitted_job_ids: list[base_t.ObjectId]
|
|
15
|
+
|
|
16
|
+
def __init__(self, *, client: Client) -> None:
|
|
17
|
+
super().__init__()
|
|
18
|
+
self._client = client
|
|
19
|
+
self._queue = []
|
|
20
|
+
self._submitted_job_ids = []
|
|
21
|
+
|
|
22
|
+
def _enqueue(self, req: async_batch_t.AsyncBatchRequest) -> None:
|
|
23
|
+
self._queue.append(req)
|
|
24
|
+
|
|
25
|
+
def current_queue_size(self) -> int:
|
|
26
|
+
return len(self._queue)
|
|
27
|
+
|
|
28
|
+
def send(self) -> base_t.ObjectId:
|
|
29
|
+
if len(self._queue) == 0:
|
|
30
|
+
raise AsyncBatchSubmissionError("queue is empty")
|
|
31
|
+
job_id = self._client.execute_batch_load_async(requests=self._queue).job_id
|
|
32
|
+
self._submitted_job_ids.append(job_id)
|
|
33
|
+
self._queue = []
|
|
34
|
+
return job_id
|
|
35
|
+
|
|
36
|
+
def get_submitted_job_ids(self) -> list[base_t.ObjectId]:
|
|
37
|
+
return self._submitted_job_ids
|