UncountablePythonSDK 0.0.171__py3-none-any.whl → 0.0.172__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.
- examples/integration-server/jobs/materials_auto/example_instrument.py +1 -1
- examples/integration-server/pyproject.toml +3 -4
- pkgs/argument_parser/__init__.py +47 -17
- pkgs/argument_parser/cached_parser.py +56 -0
- pkgs/argument_parser/parser_base.py +42 -0
- pkgs/argument_parser/parser_builder.py +52 -0
- pkgs/argument_parser/parser_cache.py +30 -0
- pkgs/argument_parser/parser_function_type.py +6 -0
- pkgs/argument_parser/{argument_parser.py → parser_inner.py} +65 -199
- pkgs/argument_parser/parser_options.py +49 -0
- pkgs/argument_parser/type_predicates.py +45 -0
- pkgs/filesystem_utils/__init__.py +29 -19
- pkgs/filesystem_utils/_sftp_connection.py +168 -0
- pkgs/filesystem_utils/_sftp_session.py +9 -10
- pkgs/serialization/__init__.py +23 -15
- pkgs/serialization/annotation.py +40 -9
- pkgs/serialization/serial_class.py +6 -2
- pkgs/serialization/serial_generic.py +2 -2
- pkgs/serialization/serial_union.py +18 -5
- pkgs/serialization/yaml.py +1 -6
- pkgs/serialization_util/__init__.py +1 -2
- pkgs/serialization_util/serialization_helpers.py +2 -2
- pkgs/type_spec/actions_registry/__main__.py +10 -1
- pkgs/type_spec/actions_registry/emit_python.py +92 -0
- pkgs/type_spec/emit_python.py +6 -0
- pkgs/type_spec/load_types.py +35 -25
- pkgs/type_spec/non_discriminated_union_exceptions.py +2 -1
- pkgs/type_spec/open_api_util.py +5 -5
- pkgs/type_spec/type_info/emit_type_info.py +2 -2
- pkgs/type_spec/value_spec/__main__.py +53 -21
- pkgs/type_spec/value_spec/emit_python.py +6 -1
- uncountable/core/client.py +34 -1
- uncountable/integration/cli.py +1 -2
- uncountable/integration/http_server/__init__.py +5 -3
- uncountable/integration/queue_runner/job_scheduler.py +4 -1
- uncountable/integration/telemetry.py +29 -11
- uncountable/types/__init__.py +2 -0
- uncountable/types/api/batch/execute_batch.py +2 -0
- uncountable/types/api/batch/execute_batch_load_async.py +2 -0
- uncountable/types/api/chemical/convert_chemical_formats.py +2 -0
- uncountable/types/api/condition_parameters/upsert_condition_match.py +2 -0
- uncountable/types/api/condition_parameters/upsert_condition_matches.py +2 -0
- uncountable/types/api/entity/create_entities.py +2 -0
- uncountable/types/api/entity/create_entity.py +2 -0
- uncountable/types/api/entity/create_or_update_entities.py +2 -0
- uncountable/types/api/entity/create_or_update_entity.py +2 -0
- uncountable/types/api/entity/export_entities.py +2 -0
- uncountable/types/api/entity/get_entities_data.py +2 -0
- uncountable/types/api/entity/grant_entity_permissions.py +2 -0
- uncountable/types/api/entity/list_aggregate.py +2 -0
- uncountable/types/api/entity/list_entities.py +2 -0
- uncountable/types/api/entity/lock_entity.py +2 -0
- uncountable/types/api/entity/lookup_entity.py +2 -0
- uncountable/types/api/entity/resolve_entity_ids.py +2 -0
- uncountable/types/api/entity/set_barcode.py +2 -0
- uncountable/types/api/entity/set_entities_field_values.py +2 -0
- uncountable/types/api/entity/set_entity_field_values.py +2 -0
- uncountable/types/api/entity/set_values.py +2 -0
- uncountable/types/api/entity/transition_entity_phase.py +2 -0
- uncountable/types/api/entity/unlock_entity.py +2 -0
- uncountable/types/api/equipment/associate_equipment_input.py +2 -0
- uncountable/types/api/field_options/upsert_field_options.py +2 -0
- uncountable/types/api/file_folders/add_file_to_folder.py +2 -0
- uncountable/types/api/file_folders/modify_file_system.py +2 -0
- uncountable/types/api/files/download_file.py +2 -0
- uncountable/types/api/id_source/list_id_source.py +2 -0
- uncountable/types/api/id_source/match_id_source.py +2 -0
- uncountable/types/api/input_groups/get_input_group_names.py +2 -0
- uncountable/types/api/inputs/create_inputs.py +2 -0
- uncountable/types/api/inputs/get_input_data.py +2 -0
- uncountable/types/api/inputs/get_input_names.py +2 -0
- uncountable/types/api/inputs/get_inputs_data.py +2 -0
- uncountable/types/api/inputs/set_input_attribute_values.py +2 -0
- uncountable/types/api/inputs/set_input_category.py +2 -0
- uncountable/types/api/inputs/set_input_subcategories.py +2 -0
- uncountable/types/api/inputs/set_intermediate_type.py +2 -0
- uncountable/types/api/integrations/publish_realtime_data.py +2 -0
- uncountable/types/api/integrations/push_notification.py +2 -0
- uncountable/types/api/integrations/register_sockets_token.py +2 -0
- uncountable/types/api/listing/export_listing.py +2 -0
- uncountable/types/api/listing/fetch_listing.py +2 -0
- uncountable/types/api/material_families/update_entity_material_families.py +2 -0
- uncountable/types/api/notebooks/add_notebook_content.py +4 -0
- uncountable/types/api/notebooks/get_notebook_content.py +2 -0
- uncountable/types/api/output_parameters/swap_output_condition_parameters.py +2 -0
- uncountable/types/api/outputs/get_output_data.py +2 -0
- uncountable/types/api/outputs/get_output_names.py +2 -0
- uncountable/types/api/outputs/get_output_organization.py +2 -0
- uncountable/types/api/outputs/resolve_output_conditions.py +2 -0
- uncountable/types/api/outputs/update_output_condition_parameter.py +2 -0
- uncountable/types/api/permissions/set_core_permissions.py +2 -0
- uncountable/types/api/permissions/set_entity_permission.py +2 -0
- uncountable/types/api/project/get_projects.py +2 -0
- uncountable/types/api/project/get_projects_data.py +2 -0
- uncountable/types/api/recipe_links/create_recipe_link.py +2 -0
- uncountable/types/api/recipe_links/create_recipe_links.py +57 -0
- uncountable/types/api/recipe_links/remove_recipe_link.py +2 -0
- uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py +2 -0
- uncountable/types/api/recipes/add_recipe_to_project.py +2 -0
- uncountable/types/api/recipes/add_time_series_data.py +2 -0
- uncountable/types/api/recipes/archive_recipes.py +2 -0
- uncountable/types/api/recipes/associate_recipe_as_input.py +2 -0
- uncountable/types/api/recipes/associate_recipe_as_lot.py +2 -0
- uncountable/types/api/recipes/clear_recipe_outputs.py +2 -0
- uncountable/types/api/recipes/create_mix_order.py +2 -0
- uncountable/types/api/recipes/create_recipe.py +2 -0
- uncountable/types/api/recipes/create_recipes.py +2 -0
- uncountable/types/api/recipes/disassociate_recipe_as_input.py +2 -0
- uncountable/types/api/recipes/edit_recipe_inputs.py +61 -2
- uncountable/types/api/recipes/get_column_calculation_values.py +2 -0
- uncountable/types/api/recipes/get_curve.py +2 -0
- uncountable/types/api/recipes/get_recipe_calculations.py +2 -0
- uncountable/types/api/recipes/get_recipe_links.py +2 -0
- uncountable/types/api/recipes/get_recipe_names.py +2 -0
- uncountable/types/api/recipes/get_recipe_output_metadata.py +2 -0
- uncountable/types/api/recipes/get_recipes_data.py +16 -0
- uncountable/types/api/recipes/lock_recipes.py +2 -0
- uncountable/types/api/recipes/remove_recipe_from_project.py +2 -0
- uncountable/types/api/recipes/set_recipe_inputs.py +2 -0
- uncountable/types/api/recipes/set_recipe_metadata.py +2 -0
- uncountable/types/api/recipes/set_recipe_output_annotations.py +2 -0
- uncountable/types/api/recipes/set_recipe_output_file.py +2 -0
- uncountable/types/api/recipes/set_recipe_outputs.py +2 -0
- uncountable/types/api/recipes/set_recipe_tags.py +2 -0
- uncountable/types/api/recipes/set_recipe_total.py +2 -0
- uncountable/types/api/recipes/unarchive_recipes.py +2 -0
- uncountable/types/api/recipes/unlock_recipes.py +2 -0
- uncountable/types/api/recipes/upsert_recipe_workflow_step.py +2 -0
- uncountable/types/api/recipes/upsert_step_relationships.py +2 -0
- uncountable/types/api/runsheet/complete_async_upload.py +2 -0
- uncountable/types/api/runsheet/export_default_runsheet.py +2 -0
- uncountable/types/api/triggers/run_trigger.py +2 -0
- uncountable/types/api/uploader/complete_async_parse.py +2 -0
- uncountable/types/api/uploader/invoke_uploader.py +2 -0
- uncountable/types/api/user/get_current_user_info.py +2 -0
- uncountable/types/async_batch_processor.py +1 -1
- uncountable/types/client_base.py +29 -2
- uncountable/types/entity_t.py +18 -0
- uncountable/types/recipe_workflow_step_types_t.py +2 -2
- uncountable/types/request_headers.py +1 -0
- uncountable/types/request_headers_t.py +6 -0
- {uncountablepythonsdk-0.0.171.dist-info → uncountablepythonsdk-0.0.172.dist-info}/METADATA +6 -8
- {uncountablepythonsdk-0.0.171.dist-info → uncountablepythonsdk-0.0.172.dist-info}/RECORD +145 -137
- pkgs/argument_parser/_is_enum.py +0 -11
- pkgs/argument_parser/_is_namedtuple.py +0 -14
- {uncountablepythonsdk-0.0.171.dist-info → uncountablepythonsdk-0.0.172.dist-info}/WHEEL +0 -0
- {uncountablepythonsdk-0.0.171.dist-info → uncountablepythonsdk-0.0.172.dist-info}/top_level.txt +0 -0
|
@@ -14,7 +14,7 @@ from uncountable.types.integration_session_t import IntegrationSessionInstrument
|
|
|
14
14
|
from websockets.sync.client import connect
|
|
15
15
|
from websockets.typing import Data
|
|
16
16
|
|
|
17
|
-
from pkgs.argument_parser
|
|
17
|
+
from pkgs.argument_parser import CachedParser
|
|
18
18
|
from pkgs.serialization_util import serialize_for_api
|
|
19
19
|
|
|
20
20
|
|
|
@@ -5,19 +5,18 @@ name = "integration-server-testing"
|
|
|
5
5
|
# end_project_name
|
|
6
6
|
dynamic = ["version"]
|
|
7
7
|
dependencies = [
|
|
8
|
-
"mypy
|
|
8
|
+
"mypy >= 2.1.0, < 3",
|
|
9
9
|
"ruff == 0.*",
|
|
10
10
|
"openpyxl == 3.*",
|
|
11
11
|
"more_itertools ==11.*",
|
|
12
|
-
"types-paramiko ==4.0.0.
|
|
12
|
+
"types-paramiko ==4.0.0.20260518",
|
|
13
13
|
"types-openpyxl == 3.*",
|
|
14
|
-
"types-pysftp == 0.*",
|
|
15
14
|
"types-pytz ==2026.*",
|
|
16
15
|
"types-requests == 2.*",
|
|
17
16
|
"types-simplejson == 3.*",
|
|
18
17
|
"pandas-stubs",
|
|
19
18
|
"xlrd == 2.*",
|
|
20
|
-
"msgspec ==0.
|
|
19
|
+
"msgspec ==0.21.*",
|
|
21
20
|
"websockets==16.0",
|
|
22
21
|
]
|
|
23
22
|
|
pkgs/argument_parser/__init__.py
CHANGED
|
@@ -1,17 +1,47 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
# CLOSED MODULE
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"CachedParser",
|
|
5
|
+
"ParserBase",
|
|
6
|
+
"ParserEnumError",
|
|
7
|
+
"ParserError",
|
|
8
|
+
"ParserExtraFieldsError",
|
|
9
|
+
"ParserFunction",
|
|
10
|
+
"ParserOptions",
|
|
11
|
+
"ParserTypeError",
|
|
12
|
+
"SourceEncoding",
|
|
13
|
+
"build_parser",
|
|
14
|
+
"camel_to_snake_case",
|
|
15
|
+
"is_missing",
|
|
16
|
+
"is_optional",
|
|
17
|
+
"is_string_enum_class",
|
|
18
|
+
"is_union",
|
|
19
|
+
"kebab_to_pascal_case",
|
|
20
|
+
"snake_to_camel_case",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
from .cached_parser import CachedParser as CachedParser
|
|
24
|
+
from .case_convert import (
|
|
25
|
+
camel_to_snake_case as camel_to_snake_case,
|
|
26
|
+
kebab_to_pascal_case as kebab_to_pascal_case,
|
|
27
|
+
snake_to_camel_case as snake_to_camel_case,
|
|
28
|
+
)
|
|
29
|
+
from .parser_base import ParserBase as ParserBase
|
|
30
|
+
from .parser_builder import build_parser as build_parser
|
|
31
|
+
from .parser_error import (
|
|
32
|
+
ParserEnumError as ParserEnumError,
|
|
33
|
+
ParserError as ParserError,
|
|
34
|
+
ParserExtraFieldsError as ParserExtraFieldsError,
|
|
35
|
+
ParserTypeError as ParserTypeError,
|
|
36
|
+
)
|
|
37
|
+
from .parser_function_type import ParserFunction as ParserFunction
|
|
38
|
+
from .parser_options import (
|
|
39
|
+
ParserOptions as ParserOptions,
|
|
40
|
+
SourceEncoding as SourceEncoding,
|
|
41
|
+
)
|
|
42
|
+
from .type_predicates import (
|
|
43
|
+
is_missing as is_missing,
|
|
44
|
+
is_optional as is_optional,
|
|
45
|
+
is_string_enum_class as is_string_enum_class,
|
|
46
|
+
is_union as is_union,
|
|
47
|
+
)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from pkgs.serialization import SerialType
|
|
6
|
+
|
|
7
|
+
from .parser_base import ParserBase
|
|
8
|
+
from .parser_builder import build_parser
|
|
9
|
+
from .parser_function_type import ParserFunction
|
|
10
|
+
from .parser_options import ParserOptions
|
|
11
|
+
|
|
12
|
+
T = typing.TypeVar("T")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CachedParser(ParserBase[T], typing.Generic[T]):
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
args: SerialType[T],
|
|
19
|
+
strict_property_parsing: bool = False,
|
|
20
|
+
):
|
|
21
|
+
self.arguments = args
|
|
22
|
+
self.parser_api: ParserFunction[T] | None = None
|
|
23
|
+
self.parser_storage: ParserFunction[T] | None = None
|
|
24
|
+
self.strict_property_parsing = strict_property_parsing
|
|
25
|
+
|
|
26
|
+
def parse_api(self, args: typing.Any) -> T:
|
|
27
|
+
"""
|
|
28
|
+
Parses data coming from an API/Endpoint
|
|
29
|
+
|
|
30
|
+
NOTE: Some places use this to parse storage data due to backwards
|
|
31
|
+
compatibility. If your data is coming from the DB or a file, it is
|
|
32
|
+
preferred to use parse_storage.
|
|
33
|
+
"""
|
|
34
|
+
if self.parser_api is None:
|
|
35
|
+
self.parser_api = build_parser(
|
|
36
|
+
self.arguments,
|
|
37
|
+
ParserOptions.Api(
|
|
38
|
+
strict_property_parsing=self.strict_property_parsing,
|
|
39
|
+
),
|
|
40
|
+
)
|
|
41
|
+
assert self.parser_api is not None
|
|
42
|
+
return self.parser_api(args)
|
|
43
|
+
|
|
44
|
+
def parse_storage(self, args: typing.Any) -> T:
|
|
45
|
+
"""
|
|
46
|
+
Parses data coming from the database or file.
|
|
47
|
+
"""
|
|
48
|
+
if self.parser_storage is None:
|
|
49
|
+
self.parser_storage = build_parser(
|
|
50
|
+
self.arguments,
|
|
51
|
+
ParserOptions.Storage(
|
|
52
|
+
strict_property_parsing=self.strict_property_parsing,
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
assert self.parser_storage is not None
|
|
56
|
+
return self.parser_storage(args)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from importlib import resources
|
|
6
|
+
|
|
7
|
+
import msgspec.yaml
|
|
8
|
+
|
|
9
|
+
from .parser_options import SourceEncoding
|
|
10
|
+
|
|
11
|
+
T = typing.TypeVar("T")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ParserBase(ABC, typing.Generic[T]):
|
|
15
|
+
def parse_from_encoding(
|
|
16
|
+
self,
|
|
17
|
+
args: typing.Any,
|
|
18
|
+
*,
|
|
19
|
+
source_encoding: SourceEncoding,
|
|
20
|
+
) -> T:
|
|
21
|
+
match source_encoding:
|
|
22
|
+
case SourceEncoding.API:
|
|
23
|
+
return self.parse_api(args)
|
|
24
|
+
case SourceEncoding.STORAGE:
|
|
25
|
+
return self.parse_storage(args)
|
|
26
|
+
case _:
|
|
27
|
+
typing.assert_never(source_encoding)
|
|
28
|
+
|
|
29
|
+
# IMPROVE: Args would be better typed as "object"
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def parse_storage(self, args: typing.Any) -> T: ...
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def parse_api(self, args: typing.Any) -> T: ...
|
|
35
|
+
|
|
36
|
+
def parse_yaml_file(self, path: str) -> T:
|
|
37
|
+
with open(path, encoding="utf-8") as data_in:
|
|
38
|
+
return self.parse_storage(msgspec.yaml.decode(data_in.read()))
|
|
39
|
+
|
|
40
|
+
def parse_yaml_resource(self, package: resources.Package, resource: str) -> T:
|
|
41
|
+
with resources.open_text(package, resource) as fp:
|
|
42
|
+
return self.parse_storage(msgspec.yaml.decode(fp.read()))
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
|
|
6
|
+
from pkgs.serialization import SerialType
|
|
7
|
+
|
|
8
|
+
from .parser_cache import ParserCache, ParserCacheManager
|
|
9
|
+
from .parser_function_type import ParserFunction
|
|
10
|
+
from .parser_inner import build_parser_inner
|
|
11
|
+
from .parser_options import ParserContext, ParserOptions
|
|
12
|
+
|
|
13
|
+
T = typing.TypeVar("T")
|
|
14
|
+
|
|
15
|
+
_CACHE_MAP: dict[ParserOptions, ParserCache] = defaultdict(ParserCache)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def build_parser(
|
|
19
|
+
parsed_type: SerialType[T],
|
|
20
|
+
options: ParserOptions,
|
|
21
|
+
) -> ParserFunction[T]:
|
|
22
|
+
"""
|
|
23
|
+
Consider using CachedParser to provide a cleaner API for storage and API
|
|
24
|
+
data parsing.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
# Keep a cache per ParserOptions type, as they produce distinct parsers
|
|
28
|
+
global_cache = _CACHE_MAP[options]
|
|
29
|
+
|
|
30
|
+
context = ParserContext(options=options, cache=ParserCacheManager(global_cache))
|
|
31
|
+
|
|
32
|
+
# PEP 695 ``type X[T] = ...`` aliases (and their subscripted forms) are not
|
|
33
|
+
# ``type`` instances, so they can't share the top-level cache keyed on
|
|
34
|
+
# ``type[Any]``. Skip the top-level cache for them; ``build_parser_inner``
|
|
35
|
+
# caches parameterized dataclasses internally by ``(type, type_var_map)``.
|
|
36
|
+
is_alias_type = isinstance(parsed_type, typing.TypeAliasType) or isinstance(
|
|
37
|
+
typing.get_origin(parsed_type), typing.TypeAliasType
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if not is_alias_type:
|
|
41
|
+
cur_parser = global_cache.get(parsed_type)
|
|
42
|
+
if cur_parser is not None:
|
|
43
|
+
return cur_parser
|
|
44
|
+
|
|
45
|
+
built_parser = build_parser_inner(parsed_type, context, {})
|
|
46
|
+
|
|
47
|
+
if not is_alias_type:
|
|
48
|
+
context.cache.put(parsed_type, built_parser)
|
|
49
|
+
|
|
50
|
+
global_cache.update(context.cache.local_cache())
|
|
51
|
+
|
|
52
|
+
return built_parser
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import types
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
from .parser_function_type import ParserFunction
|
|
7
|
+
|
|
8
|
+
T = typing.TypeVar("T")
|
|
9
|
+
ParserCacheKey = type[typing.Any] | typing.TypeAliasType | types.UnionType
|
|
10
|
+
ParserCache = dict[
|
|
11
|
+
ParserCacheKey,
|
|
12
|
+
ParserFunction[typing.Any],
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ParserCacheManager:
|
|
17
|
+
def __init__(self, global_cache: ParserCache) -> None:
|
|
18
|
+
self._global_cache = global_cache
|
|
19
|
+
self._local_cache: ParserCache = dict()
|
|
20
|
+
|
|
21
|
+
def get(self, key: ParserCacheKey) -> ParserFunction[typing.Any] | None:
|
|
22
|
+
if key in self._local_cache:
|
|
23
|
+
return self._local_cache[key]
|
|
24
|
+
return self._global_cache.get(key)
|
|
25
|
+
|
|
26
|
+
def put(self, key: ParserCacheKey, value: ParserFunction[typing.Any]) -> None:
|
|
27
|
+
self._local_cache[key] = value
|
|
28
|
+
|
|
29
|
+
def local_cache(self) -> ParserCache:
|
|
30
|
+
return self._local_cache
|