UncountablePythonSDK 0.0.100__py3-none-any.whl → 0.0.103__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/justfile CHANGED
@@ -1,5 +1,6 @@
1
1
  docs-setup-python:
2
- pip install -r requirements.txt
2
+ curl -LsSf https://astral.sh/uv/0.6.1/install.sh | sh
3
+ uv pip install -r requirements.txt
3
4
 
4
5
  docs-clean:
5
6
  rm -rf _build/
@@ -9,4 +10,3 @@ docs-build: docs-clean
9
10
 
10
11
  docs-serve: docs-build
11
12
  npx serve _build/dirhtml
12
-
@@ -12,7 +12,7 @@ dependencies = [
12
12
  "types-paramiko ==3.5.0.20240918",
13
13
  "types-openpyxl == 3.*",
14
14
  "types-pysftp == 0.*",
15
- "types-pytz == 2024.*",
15
+ "types-pytz ==2025.*",
16
16
  "types-requests == 2.*",
17
17
  "types-simplejson == 3.*",
18
18
  "pandas-stubs",
@@ -173,9 +173,9 @@ lint.ignore = [
173
173
  "SLOT000", # subclass of string should define slot. should add
174
174
  "T201", # print. skip
175
175
  "T203", # pprint. skip
176
- "TCH001", # move application import into type checking. skip
177
- "TCH002", # move third party import into type checking. consider
178
- "TCH003", # move library into type checking lbock. consider
176
+ "TC001", # move application import into type checking. skip
177
+ "TC002", # move third party import into type checking. consider
178
+ "TC003", # move library into type checking lbock. consider
179
179
  "TD", # todo comments. skip
180
180
  "TID252", # eliminate relative imports from parents. skip
181
181
  "TRY002", # create your own exception. skip
@@ -1,5 +1,6 @@
1
1
  from ._is_enum import is_string_enum_class as is_string_enum_class
2
2
  from .argument_parser import CachedParser as CachedParser
3
+ from .argument_parser import ParserBase as ParserBase
3
4
  from .argument_parser import ParserError as ParserError
4
5
  from .argument_parser import ParserExtraFieldsError as ParserExtraFieldsError
5
6
  from .argument_parser import ParserFunction as ParserFunction
@@ -4,6 +4,7 @@ import dataclasses
4
4
  import math
5
5
  import types
6
6
  import typing
7
+ from abc import ABC, abstractmethod
7
8
  from collections import defaultdict
8
9
  from datetime import date, datetime
9
10
  from decimal import Decimal
@@ -365,7 +366,7 @@ def _build_parser_inner[T](
365
366
  context,
366
367
  )
367
368
 
368
- raise ValueError(f"Unhandled type {parsed_type}")
369
+ raise ValueError(f"Unhandled type {parsed_type}/{origin}")
369
370
 
370
371
 
371
372
  def _build_parser_dataclass[T](
@@ -501,17 +502,7 @@ def build_parser[T](
501
502
  return built_parser
502
503
 
503
504
 
504
- class CachedParser[T]:
505
- def __init__(
506
- self,
507
- args: type[T],
508
- strict_property_parsing: bool = False,
509
- ):
510
- self.arguments = args
511
- self.parser_api: ParserFunction[T] | None = None
512
- self.parser_storage: ParserFunction[T] | None = None
513
- self.strict_property_parsing = strict_property_parsing
514
-
505
+ class ParserBase[T](ABC):
515
506
  def parse_from_encoding(
516
507
  self,
517
508
  args: typing.Any,
@@ -526,6 +517,33 @@ class CachedParser[T]:
526
517
  case _:
527
518
  typing.assert_never(source_encoding)
528
519
 
520
+ # IMPROVE: Args would be better typed as "object"
521
+ @abstractmethod
522
+ def parse_storage(self, args: typing.Any) -> T: ...
523
+
524
+ @abstractmethod
525
+ def parse_api(self, args: typing.Any) -> T: ...
526
+
527
+ def parse_yaml_file(self, path: str) -> T:
528
+ with open(path, encoding="utf-8") as data_in:
529
+ return self.parse_storage(yaml.safe_load(data_in))
530
+
531
+ def parse_yaml_resource(self, package: resources.Package, resource: str) -> T:
532
+ with resources.open_text(package, resource) as fp:
533
+ return self.parse_storage(yaml.safe_load(fp))
534
+
535
+
536
+ class CachedParser[T](ParserBase[T]):
537
+ def __init__(
538
+ self,
539
+ args: type[T],
540
+ strict_property_parsing: bool = False,
541
+ ):
542
+ self.arguments = args
543
+ self.parser_api: ParserFunction[T] | None = None
544
+ self.parser_storage: ParserFunction[T] | None = None
545
+ self.strict_property_parsing = strict_property_parsing
546
+
529
547
  def parse_api(self, args: typing.Any) -> T:
530
548
  """
531
549
  Parses data coming from an API/Endpoint
@@ -557,11 +575,3 @@ class CachedParser[T]:
557
575
  )
558
576
  assert self.parser_storage is not None
559
577
  return self.parser_storage(args)
560
-
561
- def parse_yaml_file(self, path: str) -> T:
562
- with open(path, encoding="utf-8") as data_in:
563
- return self.parse_storage(yaml.safe_load(data_in))
564
-
565
- def parse_yaml_resource(self, package: resources.Package, resource: str) -> T:
566
- with resources.open_text(package, resource) as fp:
567
- return self.parse_storage(yaml.safe_load(fp))
@@ -7,7 +7,7 @@ from typing import Any, TypeVar, cast
7
7
 
8
8
  from .annotation import SerialBase, SerialInspector
9
9
 
10
- _ClassT = TypeVar("_ClassT")
10
+ ClassT = TypeVar("ClassT")
11
11
 
12
12
 
13
13
  @dataclasses.dataclass(kw_only=True, frozen=True, eq=True)
@@ -30,7 +30,7 @@ def serial_class(
30
30
  parse_require: set[str] | None = None,
31
31
  named_type_path: str | None = None,
32
32
  is_dynamic_allowed: bool = False,
33
- ) -> Callable[[_ClassT], _ClassT]:
33
+ ) -> Callable[[ClassT], ClassT]:
34
34
  """
35
35
  An additional decorator to a dataclass that specifies serialization options.
36
36
 
@@ -54,7 +54,7 @@ def serial_class(
54
54
  The type_spec type-path to this type. This applies only to named types.
55
55
  """
56
56
 
57
- def decorate(orig_class: _ClassT) -> _ClassT:
57
+ def decorate(orig_class: ClassT) -> ClassT:
58
58
  cast(Any, orig_class).__unc_serial_data = _SerialClassData(
59
59
  unconverted_keys=unconverted_keys or set(),
60
60
  unconverted_values=unconverted_values or set(),
@@ -69,10 +69,10 @@ def serial_class(
69
69
  return decorate
70
70
 
71
71
 
72
- class SerialClassDataInspector(SerialInspector[_ClassT]):
72
+ class SerialClassDataInspector(SerialInspector[ClassT]):
73
73
  def __init__(
74
74
  self,
75
- parsed_type: type[_ClassT],
75
+ parsed_type: type[ClassT],
76
76
  current: _SerialClassData,
77
77
  ) -> None:
78
78
  super().__init__(parsed_type, current)
@@ -119,9 +119,9 @@ def get_merged_serial_class_data(type_class: type[Any]) -> _SerialClassData | No
119
119
  return base_class_data
120
120
 
121
121
 
122
- def get_serial_class_data[_ClassT](
123
- type_class: type[_ClassT],
124
- ) -> SerialClassDataInspector[_ClassT]:
122
+ def get_serial_class_data[ClassT](
123
+ type_class: type[ClassT],
124
+ ) -> SerialClassDataInspector[ClassT]:
125
125
  return SerialClassDataInspector(
126
126
  type_class, get_merged_serial_class_data(type_class) or EMPTY_SERIAL_CLASS_DATA
127
127
  )
@@ -135,12 +135,12 @@ class _SerialStringEnumData:
135
135
 
136
136
  def serial_string_enum(
137
137
  *, labels: dict[str, str] | None = None, deprecated: set[str] | None = None
138
- ) -> Callable[[_ClassT], _ClassT]:
138
+ ) -> Callable[[ClassT], ClassT]:
139
139
  """
140
140
  A decorator for enums to provide serialization data, including labels.
141
141
  """
142
142
 
143
- def decorate(orig_class: _ClassT) -> _ClassT:
143
+ def decorate(orig_class: ClassT) -> ClassT:
144
144
  cast(Any, orig_class).__unc_serial_string_enum_data = _SerialStringEnumData(
145
145
  labels=labels or {}, deprecated=deprecated or set()
146
146
  )
pkgs/type_spec/builder.py CHANGED
@@ -26,7 +26,6 @@ class StabilityLevel(StrEnum):
26
26
  """
27
27
 
28
28
  draft = "draft"
29
- alpha = "alpha"
30
29
  beta = "beta"
31
30
  stable = "stable"
32
31
 
@@ -891,7 +890,6 @@ class SpecEndpoint:
891
890
  method: RouteMethod
892
891
  data_loader: bool
893
892
  is_sdk: EndpointEmitType
894
- is_beta: bool
895
893
  stability_level: StabilityLevel | None
896
894
  # Don't emit TypeScript endpoint code
897
895
  suppress_ts: bool
@@ -920,7 +918,6 @@ class SpecEndpoint:
920
918
  "path",
921
919
  "data_loader",
922
920
  "is_sdk",
923
- "is_beta",
924
921
  "stability_level",
925
922
  "async_batch_path",
926
923
  "function",
@@ -966,10 +963,6 @@ class SpecEndpoint:
966
963
  assert account_type is None or isinstance(account_type, str)
967
964
  self.account_type = account_type
968
965
 
969
- is_beta = data.get("is_beta", False)
970
- assert isinstance(is_beta, bool)
971
- self.is_beta = is_beta
972
-
973
966
  stability_level_raw = data.get("stability_level")
974
967
  assert stability_level_raw is None or isinstance(stability_level_raw, str)
975
968
  self.stability_level = (
pkgs/type_spec/config.py CHANGED
@@ -67,6 +67,7 @@ class PythonConfig(BaseLanguageConfig):
67
67
  emit_client_class: bool = False # emit the base class for the api client
68
68
  all_named_type_exports: bool = False # emit __all__ for all named type exports
69
69
  sdk_endpoints_only: bool = False # only emit is_sdk endpoints
70
+ type_info_output: str | None = None # folder for generated type info files
70
71
 
71
72
  def __post_init__(self: Self) -> None:
72
73
  self.routes_output = _parse_string_lookup(
@@ -78,6 +79,9 @@ class PythonConfig(BaseLanguageConfig):
78
79
  else None
79
80
  )
80
81
 
82
+ if self.type_info_output is not None:
83
+ self.type_info_output = os.path.abspath(self.type_info_output)
84
+
81
85
 
82
86
  @dataclass(kw_only=True)
83
87
  class OpenAPIConfig(BaseLanguageConfig):
@@ -103,7 +107,7 @@ class Config:
103
107
  open_api: OpenAPIConfig | None
104
108
 
105
109
 
106
- def _parse_language[_T](config_class: type[_T], raw_value: ConfigValueType) -> _T:
110
+ def _parse_language[T](config_class: type[T], raw_value: ConfigValueType) -> T:
107
111
  assert isinstance(raw_value, dict), "expecting language config to have key/values."
108
112
  return config_class(**raw_value)
109
113
 
@@ -7,7 +7,7 @@ WORK-IN-PROGRESS, DON'T USE!
7
7
  import dataclasses
8
8
  import json
9
9
  import re
10
- from typing import Collection, cast
10
+ from typing import Collection, assert_never, cast
11
11
 
12
12
  from pkgs.serialization import yaml
13
13
  from pkgs.serialization_util import serialize_for_api
@@ -126,7 +126,11 @@ def emit_open_api(builder: builder.SpecBuilder, *, config: OpenAPIConfig) -> Non
126
126
  for namespace in sorted(builder.namespaces.values(), key=lambda ns: ns.name):
127
127
  ctx = EmitOpenAPIContext(namespace=namespace)
128
128
 
129
- if ctx.namespace.endpoint is not None and ctx.namespace.endpoint.is_beta:
129
+ if (
130
+ ctx.namespace.endpoint is not None
131
+ and ctx.namespace.endpoint.stability_level
132
+ == EmitOpenAPIStabilityLevel.draft
133
+ ):
130
134
  continue
131
135
 
132
136
  if ctx.namespace.name == "base":
@@ -252,18 +256,26 @@ def _emit_endpoint_parameters(
252
256
  } | _emit_endpoint_parameter_examples(examples)
253
257
 
254
258
 
255
- def _emit_is_beta(is_beta: bool) -> DictApiSchema:
256
- if is_beta:
257
- return {"x-beta": True}
258
- return {}
259
-
260
-
261
259
  def _emit_stability_level(
262
260
  stability_level: EmitOpenAPIStabilityLevel | None,
263
261
  ) -> DictApiSchema:
264
- if stability_level is not None:
265
- return {"x-stability-level": str(stability_level)}
266
- return {}
262
+ stability_info: dict[str, ApiSchema] = {}
263
+ resolved_stability_level = (
264
+ stability_level
265
+ if stability_level is not None
266
+ else EmitOpenAPIStabilityLevel.stable
267
+ )
268
+ stability_info["x-stability-level"] = str(resolved_stability_level)
269
+ match resolved_stability_level:
270
+ case EmitOpenAPIStabilityLevel.draft:
271
+ stability_info["x-beta"] = True
272
+ case EmitOpenAPIStabilityLevel.beta:
273
+ stability_info["x-badges"] = [{"name": "Beta", "color": "DarkOrange"}]
274
+ case EmitOpenAPIStabilityLevel.stable:
275
+ pass
276
+ case _:
277
+ assert_never(stability_level)
278
+ return stability_info
267
279
 
268
280
 
269
281
  def _emit_endpoint_request_body(
@@ -360,7 +372,6 @@ def _emit_namespace(
360
372
  "summary": endpoint.summary,
361
373
  }
362
374
  | _emit_endpoint_description(endpoint.description, ctx.endpoint.guides)
363
- | _emit_is_beta(endpoint.is_beta)
364
375
  | _emit_stability_level(endpoint.stability_level)
365
376
  | _emit_endpoint_parameters(endpoint, argument_type, ctx.endpoint.examples)
366
377
  | _emit_endpoint_request_body(
@@ -579,7 +590,6 @@ def _emit_endpoint(
579
590
  tags=[tag_name],
580
591
  summary=f"{'/'.join(namespace.path[path_cutoff:])}",
581
592
  description=description,
582
- is_beta=namespace.endpoint.is_beta,
583
593
  stability_level=namespace.endpoint.stability_level,
584
594
  examples=[
585
595
  EmitOpenAPIEndpointExample(
@@ -82,7 +82,6 @@ class EmitOpenAPIEndpoint:
82
82
  tags: list[str]
83
83
  summary: str
84
84
  description: str
85
- is_beta: bool
86
85
  stability_level: EmitOpenAPIStabilityLevel | None
87
86
  examples: list[EmitOpenAPIEndpointExample]
88
87
  guides: list[EmitOpenAPIGuide]
@@ -8,6 +8,7 @@ from . import builder, util
8
8
  from .builder import EndpointEmitType, EndpointSpecificPath
9
9
  from .config import PythonConfig
10
10
  from .cross_output_links import get_path_links
11
+ from .emit_open_api_util import EmitOpenAPIStabilityLevel
11
12
 
12
13
  INDENT = " "
13
14
  LINE_BREAK = "\n"
@@ -601,7 +602,10 @@ def _emit_endpoint_invocation_function(
601
602
  endpoint = namespace.endpoint
602
603
  if endpoint is None:
603
604
  return
604
- if endpoint.is_sdk != EndpointEmitType.EMIT_ENDPOINT or endpoint.is_beta:
605
+ if (
606
+ endpoint.is_sdk != EndpointEmitType.EMIT_ENDPOINT
607
+ or endpoint.stability_level == EmitOpenAPIStabilityLevel.draft
608
+ ):
605
609
  return
606
610
 
607
611
  ctx.out.write("\n")
@@ -9,7 +9,7 @@ import sys
9
9
 
10
10
  from ..config import parse_yaml_config
11
11
  from ..load_types import load_types
12
- from .emit_type_info import emit_type_info
12
+ from .emit_type_info import emit_type_info, emit_type_info_python
13
13
 
14
14
 
15
15
  def main() -> bool:
@@ -26,6 +26,8 @@ def main() -> bool:
26
26
 
27
27
  assert config.typescript is not None
28
28
  emit_type_info(builder, config.typescript.type_info_output)
29
+ if config.python.type_info_output is not None:
30
+ emit_type_info_python(builder, config.python.type_info_output)
29
31
 
30
32
  return True
31
33
 
@@ -3,15 +3,15 @@ import dataclasses
3
3
  import decimal
4
4
  import io
5
5
  import json
6
+ from enum import Enum
6
7
  from typing import Any, cast
7
8
 
9
+ import yaml
10
+
8
11
  from main.base.types import data_t, type_info_t
9
12
  from main.base.types.base_t import PureJsonValue
10
13
  from pkgs.argument_parser import CachedParser
11
- from pkgs.serialization_util import (
12
- serialize_for_api,
13
- serialize_for_storage,
14
- )
14
+ from pkgs.serialization_util import serialize_for_api, serialize_for_storage
15
15
 
16
16
  from .. import builder, util
17
17
  from ..emit_typescript_util import MODIFY_NOTICE, ts_name
@@ -95,6 +95,35 @@ def emit_type_info(build: builder.SpecBuilder, output: str) -> None:
95
95
  util.rewrite_file(f"{output}/type_map.ts", type_map_out.getvalue())
96
96
 
97
97
 
98
+ def _convert_value_for_yaml_dump(value: Any) -> Any:
99
+ if isinstance(value, Enum):
100
+ return value.value
101
+ elif isinstance(value, list):
102
+ return [_convert_value_for_yaml_dump(item) for item in value]
103
+ elif isinstance(value, dict):
104
+ return {k: _convert_value_for_yaml_dump(v) for k, v in value.items()}
105
+ elif isinstance(value, decimal.Decimal):
106
+ return str(value)
107
+ else:
108
+ return value
109
+
110
+
111
+ def asdict_for_yaml_dump(dataclass_instance: Any) -> Any:
112
+ return {
113
+ k: _convert_value_for_yaml_dump(v)
114
+ for k, v in dataclasses.asdict(dataclass_instance).items()
115
+ }
116
+
117
+
118
+ def emit_type_info_python(build: builder.SpecBuilder, output: str) -> None:
119
+ type_map = _build_map_all(build)
120
+
121
+ stripped = _dict_null_strip(asdict_for_yaml_dump(type_map))
122
+
123
+ yaml_content = yaml.dump(stripped, default_flow_style=False, sort_keys=True)
124
+ util.rewrite_file(f"{output}/type_map.yaml", yaml_content)
125
+
126
+
98
127
  @dataclasses.dataclass
99
128
  class MapProperty:
100
129
  api_name: str
@@ -106,7 +135,7 @@ class MapProperty:
106
135
  desc: str | None
107
136
  # We don't have typing on defaults yet, relying on emitters to check it. Limit
108
137
  # use of this field, as it'll necessarily change when adding type info
109
- default: object
138
+ default: PureJsonValue
110
139
 
111
140
 
112
141
  @dataclasses.dataclass
@@ -43,13 +43,13 @@ class HTTPRequestBase:
43
43
 
44
44
  @dataclass(kw_only=True)
45
45
  class HTTPGetRequest(HTTPRequestBase):
46
- method = EndpointMethod.GET
46
+ method: EndpointMethod = EndpointMethod.GET
47
47
  query_params: dict[str, str]
48
48
 
49
49
 
50
50
  @dataclass(kw_only=True)
51
51
  class HTTPPostRequest(HTTPRequestBase):
52
- method = EndpointMethod.POST
52
+ method: EndpointMethod = EndpointMethod.POST
53
53
  body: str | dict[str, str]
54
54
 
55
55
 
@@ -131,7 +131,7 @@ class Logger:
131
131
  message: str | None = None,
132
132
  attributes: Attributes | None = None,
133
133
  ) -> None:
134
- traceback_str = "".join(traceback.format_tb(exception.__traceback__))
134
+ traceback_str = "".join(traceback.format_exception(exception))
135
135
  patched_attributes = self._patch_attributes(attributes)
136
136
  self.current_span.record_exception(
137
137
  exception=exception, attributes=patched_attributes
@@ -3,6 +3,7 @@
3
3
  # fmt: off
4
4
  # isort: skip_file
5
5
  from .api.recipes import add_recipe_to_project as add_recipe_to_project_t
6
+ from .api.recipes import add_time_series_data as add_time_series_data_t
6
7
  from .api.recipes import archive_recipes as archive_recipes_t
7
8
  from .api.equipment import associate_equipment_input as associate_equipment_input_t
8
9
  from .api.recipes import associate_recipe_as_input as associate_recipe_as_input_t
@@ -63,6 +64,7 @@ from .api.entity import list_entities as list_entities_t
63
64
  from .api.id_source import list_id_source as list_id_source_t
64
65
  from .api.entity import lock_entity as lock_entity_t
65
66
  from .api.recipes import lock_recipes as lock_recipes_t
67
+ from .api.entity import lookup_entity as lookup_entity_t
66
68
  from .api.id_source import match_id_source as match_id_source_t
67
69
  from . import outputs_t as outputs_t
68
70
  from . import overrides_t as overrides_t
@@ -112,6 +114,7 @@ from . import workflows_t as workflows_t
112
114
 
113
115
  __all__: list[str] = [
114
116
  "add_recipe_to_project_t",
117
+ "add_time_series_data_t",
115
118
  "archive_recipes_t",
116
119
  "associate_equipment_input_t",
117
120
  "associate_recipe_as_input_t",
@@ -172,6 +175,7 @@ __all__: list[str] = [
172
175
  "list_id_source_t",
173
176
  "lock_entity_t",
174
177
  "lock_recipes_t",
178
+ "lookup_entity_t",
175
179
  "match_id_source_t",
176
180
  "outputs_t",
177
181
  "overrides_t",
@@ -0,0 +1,100 @@
1
+ # DO NOT MODIFY -- This file is generated by type_spec
2
+ # ruff: noqa: E402 Q003
3
+ # fmt: off
4
+ # isort: skip_file
5
+ from __future__ import annotations
6
+ import typing # noqa: F401
7
+ import datetime # noqa: F401
8
+ from decimal import Decimal # noqa: F401
9
+ from enum import StrEnum
10
+ import dataclasses
11
+ from pkgs.serialization import serial_class
12
+ from pkgs.serialization import serial_union_annotation
13
+ from ... import async_batch_t
14
+ from ... import base_t
15
+ from ... import entity_t
16
+ from ... import identifier_t
17
+
18
+ __all__: list[str] = [
19
+ "Arguments",
20
+ "Data",
21
+ "ENDPOINT_METHOD",
22
+ "ENDPOINT_PATH",
23
+ "LookupEntityFieldValue",
24
+ "LookupEntityQuery",
25
+ "LookupEntityQueryBase",
26
+ "LookupEntityQueryType",
27
+ "LookupFieldArgumentValue",
28
+ ]
29
+
30
+ ENDPOINT_METHOD = "GET"
31
+ ENDPOINT_PATH = "api/external/entity/lookup_entity"
32
+
33
+
34
+ # DO NOT MODIFY -- This file is generated by type_spec
35
+ class LookupEntityQueryType(StrEnum):
36
+ FIELD_VALUE = "field_value"
37
+
38
+
39
+ # DO NOT MODIFY -- This file is generated by type_spec
40
+ @serial_class(
41
+ named_type_path="sdk.api.entity.lookup_entity.LookupEntityQueryBase",
42
+ )
43
+ @dataclasses.dataclass(kw_only=True)
44
+ class LookupEntityQueryBase:
45
+ type: LookupEntityQueryType
46
+
47
+
48
+ # DO NOT MODIFY -- This file is generated by type_spec
49
+ @serial_class(
50
+ named_type_path="sdk.api.entity.lookup_entity.LookupFieldArgumentValue",
51
+ unconverted_values={"field_value"},
52
+ )
53
+ @dataclasses.dataclass(kw_only=True)
54
+ class LookupFieldArgumentValue:
55
+ field_key: identifier_t.IdentifierKey
56
+ field_value: base_t.JsonValue
57
+
58
+
59
+ # DO NOT MODIFY -- This file is generated by type_spec
60
+ @serial_class(
61
+ named_type_path="sdk.api.entity.lookup_entity.LookupEntityFieldValue",
62
+ parse_require={"type"},
63
+ )
64
+ @dataclasses.dataclass(kw_only=True)
65
+ class LookupEntityFieldValue:
66
+ type: typing.Literal[LookupEntityQueryType.FIELD_VALUE] = LookupEntityQueryType.FIELD_VALUE
67
+ value: LookupFieldArgumentValue
68
+
69
+
70
+ # DO NOT MODIFY -- This file is generated by type_spec
71
+ LookupEntityQuery = typing.Annotated[
72
+ typing.Union[LookupEntityFieldValue],
73
+ serial_union_annotation(
74
+ named_type_path="sdk.api.entity.lookup_entity.LookupEntityQuery",
75
+ discriminator="type",
76
+ discriminator_map={
77
+ "field_value": LookupEntityFieldValue,
78
+ },
79
+ ),
80
+ ]
81
+
82
+
83
+ # DO NOT MODIFY -- This file is generated by type_spec
84
+ @serial_class(
85
+ named_type_path="sdk.api.entity.lookup_entity.Arguments",
86
+ )
87
+ @dataclasses.dataclass(kw_only=True)
88
+ class Arguments:
89
+ entity_type: entity_t.EntityType
90
+ query: LookupEntityQuery
91
+
92
+
93
+ # DO NOT MODIFY -- This file is generated by type_spec
94
+ @serial_class(
95
+ named_type_path="sdk.api.entity.lookup_entity.Data",
96
+ )
97
+ @dataclasses.dataclass(kw_only=True)
98
+ class Data(async_batch_t.AsyncBatchActionReturn):
99
+ pass
100
+ # DO NOT MODIFY -- This file is generated by type_spec
@@ -0,0 +1,63 @@
1
+ # DO NOT MODIFY -- This file is generated by type_spec
2
+ # ruff: noqa: E402 Q003
3
+ # fmt: off
4
+ # isort: skip_file
5
+ from __future__ import annotations
6
+ import typing # noqa: F401
7
+ import datetime # noqa: F401
8
+ from decimal import Decimal # noqa: F401
9
+ from enum import StrEnum
10
+ import dataclasses
11
+ from pkgs.serialization import serial_class
12
+ from ... import async_batch_t
13
+ from ... import identifier_t
14
+
15
+ __all__: list[str] = [
16
+ "Arguments",
17
+ "Data",
18
+ "ENDPOINT_METHOD",
19
+ "ENDPOINT_PATH",
20
+ "OnTimepointConflict",
21
+ "TimeSeriesDatum",
22
+ ]
23
+
24
+ ENDPOINT_METHOD = "POST"
25
+ ENDPOINT_PATH = "api/external/recipes/external_add_time_series_data"
26
+
27
+
28
+ # DO NOT MODIFY -- This file is generated by type_spec
29
+ class OnTimepointConflict(StrEnum):
30
+ OVERWRITE = "overwrite"
31
+ ERROR = "error"
32
+
33
+
34
+ # DO NOT MODIFY -- This file is generated by type_spec
35
+ @serial_class(
36
+ named_type_path="sdk.api.recipes.add_time_series_data.TimeSeriesDatum",
37
+ to_string_values={"value"},
38
+ )
39
+ @dataclasses.dataclass(kw_only=True)
40
+ class TimeSeriesDatum:
41
+ timestamp: datetime.datetime
42
+ value: Decimal | None = None
43
+
44
+
45
+ # DO NOT MODIFY -- This file is generated by type_spec
46
+ @serial_class(
47
+ named_type_path="sdk.api.recipes.add_time_series_data.Arguments",
48
+ )
49
+ @dataclasses.dataclass(kw_only=True)
50
+ class Arguments:
51
+ time_series_segment_key: identifier_t.IdentifierKey
52
+ time_series_data: list[TimeSeriesDatum]
53
+ on_conflict: OnTimepointConflict = OnTimepointConflict.ERROR
54
+
55
+
56
+ # DO NOT MODIFY -- This file is generated by type_spec
57
+ @serial_class(
58
+ named_type_path="sdk.api.recipes.add_time_series_data.Data",
59
+ )
60
+ @dataclasses.dataclass(kw_only=True)
61
+ class Data(async_batch_t.AsyncBatchActionReturn):
62
+ pass
63
+ # DO NOT MODIFY -- This file is generated by type_spec
@@ -6,6 +6,7 @@ from __future__ import annotations
6
6
  import typing # noqa: F401
7
7
  import datetime # noqa: F401
8
8
  from decimal import Decimal # noqa: F401
9
+ from enum import StrEnum
9
10
  import dataclasses
10
11
  from pkgs.serialization import serial_class
11
12
  from ... import base_t
@@ -19,6 +20,7 @@ __all__: list[str] = [
19
20
  "Data",
20
21
  "ENDPOINT_METHOD",
21
22
  "ENDPOINT_PATH",
23
+ "NullBehavior",
22
24
  "RecipeOutputValue",
23
25
  ]
24
26
 
@@ -36,6 +38,12 @@ class CurveValues:
36
38
  y_values: list[Decimal | None]
37
39
 
38
40
 
41
+ # DO NOT MODIFY -- This file is generated by type_spec
42
+ class NullBehavior(StrEnum):
43
+ DO_NOTHING = "do_nothing"
44
+ ENSURE_EXISTS = "ensure_exists"
45
+
46
+
39
47
  # DO NOT MODIFY -- This file is generated by type_spec
40
48
  @serial_class(
41
49
  named_type_path="sdk.api.recipes.set_recipe_outputs.RecipeOutputValue",
@@ -46,6 +54,7 @@ class RecipeOutputValue:
46
54
  recipe_id: base_t.ObjectId
47
55
  output_id: base_t.ObjectId
48
56
  experiment_num: int
57
+ null_behavior: NullBehavior | None = NullBehavior.DO_NOTHING
49
58
  condition_id: base_t.ObjectId | None = None
50
59
  value_numeric: Decimal | None = None
51
60
  value_str: str | None = None
@@ -9,6 +9,7 @@ import datetime # noqa: F401
9
9
  from decimal import Decimal # noqa: F401
10
10
  import uncountable.types.api.equipment.associate_equipment_input as associate_equipment_input_t
11
11
  import uncountable.types.api.recipes.associate_recipe_as_input as associate_recipe_as_input_t
12
+ import uncountable.types.api.recipes.associate_recipe_as_lot as associate_recipe_as_lot_t
12
13
  from uncountable.types import async_batch_t
13
14
  from uncountable.types import base_t
14
15
  import uncountable.types.api.recipes.clear_recipe_outputs as clear_recipe_outputs_t
@@ -21,6 +22,7 @@ from uncountable.types import generic_upload_t
21
22
  import uncountable.types.api.entity.grant_entity_permissions as grant_entity_permissions_t
22
23
  from uncountable.types import identifier_t
23
24
  import uncountable.types.api.uploader.invoke_uploader as invoke_uploader_t
25
+ import uncountable.types.api.entity.lookup_entity as lookup_entity_t
24
26
  from uncountable.types import recipe_identifiers_t
25
27
  from uncountable.types import recipe_metadata_t
26
28
  from uncountable.types import recipe_workflow_steps_t
@@ -113,6 +115,41 @@ class AsyncBatchProcessorBase(ABC):
113
115
  batch_reference=req.batch_reference,
114
116
  )
115
117
 
118
+ def associate_recipe_as_lot(
119
+ self,
120
+ *,
121
+ recipe_key: identifier_t.IdentifierKey,
122
+ ingredient_key: identifier_t.IdentifierKey,
123
+ depends_on: list[str] | None = None,
124
+ ) -> async_batch_t.QueuedAsyncBatchRequest:
125
+ """Create a new lot association for the provided recipe with the provided ingredient
126
+
127
+ :param recipe_key: Identifier for the recipe
128
+ :param ingredient_key: Identifier for the ingredient
129
+ :param depends_on: A list of batch reference keys to process before processing this request
130
+ """
131
+ args = associate_recipe_as_lot_t.Arguments(
132
+ recipe_key=recipe_key,
133
+ ingredient_key=ingredient_key,
134
+ )
135
+ json_data = serialize_for_api(args)
136
+
137
+ batch_reference = str(uuid.uuid4())
138
+
139
+ req = async_batch_t.AsyncBatchRequest(
140
+ path=async_batch_t.AsyncBatchRequestPath.ASSOCIATE_RECIPE_AS_LOT,
141
+ data=json_data,
142
+ depends_on=depends_on,
143
+ batch_reference=batch_reference,
144
+ )
145
+
146
+ self._enqueue(req)
147
+
148
+ return async_batch_t.QueuedAsyncBatchRequest(
149
+ path=req.path,
150
+ batch_reference=req.batch_reference,
151
+ )
152
+
116
153
  def clear_recipe_outputs(
117
154
  self,
118
155
  *,
@@ -350,6 +387,39 @@ class AsyncBatchProcessorBase(ABC):
350
387
  batch_reference=req.batch_reference,
351
388
  )
352
389
 
390
+ def lookup_entity(
391
+ self,
392
+ *,
393
+ entity_type: entity_t.EntityType,
394
+ query: lookup_entity_t.LookupEntityQuery,
395
+ depends_on: list[str] | None = None,
396
+ ) -> async_batch_t.QueuedAsyncBatchRequest:
397
+ """Look up an entity based on an identifier or field values
398
+
399
+ :param depends_on: A list of batch reference keys to process before processing this request
400
+ """
401
+ args = lookup_entity_t.Arguments(
402
+ entity_type=entity_type,
403
+ query=query,
404
+ )
405
+ json_data = serialize_for_api(args)
406
+
407
+ batch_reference = str(uuid.uuid4())
408
+
409
+ req = async_batch_t.AsyncBatchRequest(
410
+ path=async_batch_t.AsyncBatchRequestPath.LOOKUP_ENTITY,
411
+ data=json_data,
412
+ depends_on=depends_on,
413
+ batch_reference=batch_reference,
414
+ )
415
+
416
+ self._enqueue(req)
417
+
418
+ return async_batch_t.QueuedAsyncBatchRequest(
419
+ path=req.path,
420
+ batch_reference=req.batch_reference,
421
+ )
422
+
353
423
  def set_entity_field_values(
354
424
  self,
355
425
  *,
@@ -37,7 +37,10 @@ class AsyncBatchRequestPath(StrEnum):
37
37
  CLEAR_RECIPE_OUTPUTS = "recipes/clear_recipe_outputs"
38
38
  GRANT_ENTITY_PERMISSIONS = "entity/grant_entity_permissions"
39
39
  SET_ENTITY_FIELD_VALUES = "entity/set_entity_field_values"
40
+ ASSOCIATE_RECIPE_AS_LOT = "recipes/associate_recipe_as_lot"
40
41
  CREATE_OR_UPDATE_ENTITY = "entity/create_or_update_entity"
42
+ ADD_TIME_SERIES_DATA = "recipes/add_time_series_data"
43
+ LOOKUP_ENTITY = "entity/lookup_entity"
41
44
 
42
45
 
43
46
  # DO NOT MODIFY -- This file is generated by type_spec
@@ -9,6 +9,7 @@ import datetime # noqa: F401
9
9
  from decimal import Decimal # noqa: F401
10
10
  from pkgs.serialization import OpaqueKey
11
11
  import uncountable.types.api.recipes.add_recipe_to_project as add_recipe_to_project_t
12
+ import uncountable.types.api.recipes.add_time_series_data as add_time_series_data_t
12
13
  import uncountable.types.api.recipes.archive_recipes as archive_recipes_t
13
14
  import uncountable.types.api.equipment.associate_equipment_input as associate_equipment_input_t
14
15
  import uncountable.types.api.recipes.associate_recipe_as_input as associate_recipe_as_input_t
@@ -128,6 +129,31 @@ class ClientMethods(ABC):
128
129
  )
129
130
  return self.do_request(api_request=api_request, return_type=add_recipe_to_project_t.Data)
130
131
 
132
+ def external_add_time_series_data(
133
+ self,
134
+ *,
135
+ time_series_segment_key: identifier_t.IdentifierKey,
136
+ time_series_data: list[add_time_series_data_t.TimeSeriesDatum],
137
+ on_conflict: add_time_series_data_t.OnTimepointConflict = add_time_series_data_t.OnTimepointConflict.ERROR,
138
+ ) -> add_time_series_data_t.Data:
139
+ """Add timepoint data to a time series segment.
140
+
141
+ :param time_series_segment_key: The time series segment to add data to
142
+ :param time_series_data: The data to add to the time series segment
143
+ :param on_conflict: What to do if a TimeSeriesDatum already exists at the same timestamp
144
+ """
145
+ args = add_time_series_data_t.Arguments(
146
+ time_series_segment_key=time_series_segment_key,
147
+ time_series_data=time_series_data,
148
+ on_conflict=on_conflict,
149
+ )
150
+ api_request = APIRequest(
151
+ method=add_time_series_data_t.ENDPOINT_METHOD,
152
+ endpoint=add_time_series_data_t.ENDPOINT_PATH,
153
+ args=args,
154
+ )
155
+ return self.do_request(api_request=api_request, return_type=add_time_series_data_t.Data)
156
+
131
157
  def archive_recipes(
132
158
  self,
133
159
  *,
@@ -49,6 +49,7 @@ __all__: list[str] = [
49
49
  "doe_run": "Suggest Experiments",
50
50
  "email_record_external": "External Emails",
51
51
  "email_relay_server": "Email Relay Server",
52
+ "enter_measurement_view_preference": "Enter Measurement View Preference",
52
53
  "enter_recipe_view_preference": "Enter Experiment View Preference",
53
54
  "entities_viewed": "Entities Viewed",
54
55
  "entity_annotation": "Annotation",
@@ -150,6 +151,7 @@ __all__: list[str] = [
150
151
  "task": "Task",
151
152
  "task_board": "Task Board",
152
153
  "template": "Template",
154
+ "time_series_segment": "Time Series Segment",
153
155
  "timesheet_entry": "Timesheet Entry",
154
156
  "training_run": "Analyze Experiment",
155
157
  "training_run_template": "Scheduled Training Run",
@@ -208,6 +210,7 @@ class EntityType(StrEnum):
208
210
  DOE_RUN = "doe_run"
209
211
  EMAIL_RECORD_EXTERNAL = "email_record_external"
210
212
  EMAIL_RELAY_SERVER = "email_relay_server"
213
+ ENTER_MEASUREMENT_VIEW_PREFERENCE = "enter_measurement_view_preference"
211
214
  ENTER_RECIPE_VIEW_PREFERENCE = "enter_recipe_view_preference"
212
215
  ENTITIES_VIEWED = "entities_viewed"
213
216
  ENTITY_ANNOTATION = "entity_annotation"
@@ -309,6 +312,7 @@ class EntityType(StrEnum):
309
312
  TASK = "task"
310
313
  TASK_BOARD = "task_board"
311
314
  TEMPLATE = "template"
315
+ TIME_SERIES_SEGMENT = "time_series_segment"
312
316
  TIMESHEET_ENTRY = "timesheet_entry"
313
317
  TRAINING_RUN = "training_run"
314
318
  TRAINING_RUN_TEMPLATE = "training_run_template"
@@ -346,7 +350,7 @@ class EntityType(StrEnum):
346
350
 
347
351
  # DO NOT MODIFY -- This file is generated by type_spec
348
352
  LimitedEntityType = typing.Annotated[
349
- typing.Literal[EntityType.LAB_REQUEST] | typing.Literal[EntityType.APPROVAL] | typing.Literal[EntityType.CUSTOM_ENTITY] | typing.Literal[EntityType.INGREDIENT_ATTRIBUTE] | typing.Literal[EntityType.INVENTORY_AMOUNT] | typing.Literal[EntityType.TASK] | typing.Literal[EntityType.PROJECT] | typing.Literal[EntityType.EQUIPMENT] | typing.Literal[EntityType.INV_LOCAL_LOCATIONS] | typing.Literal[EntityType.FIELD_OPTION_SET] | typing.Literal[EntityType.WEBHOOK] | typing.Literal[EntityType.SPECS] | typing.Literal[EntityType.GOAL] | typing.Literal[EntityType.INGREDIENT_TAG_MAP] | typing.Literal[EntityType.INGREDIENT_TAG] | typing.Literal[EntityType.CONDITION_PARAMETER] | typing.Literal[EntityType.OUTPUT] | typing.Literal[EntityType.OUTPUT_CONDITION_PARAMETER] | typing.Literal[EntityType.ASYNC_JOB] | typing.Literal[EntityType.CONSTRAINT] | typing.Literal[EntityType.INGREDIENT_CATEGORY_ALL],
353
+ typing.Literal[EntityType.LAB_REQUEST] | typing.Literal[EntityType.APPROVAL] | typing.Literal[EntityType.CUSTOM_ENTITY] | typing.Literal[EntityType.INGREDIENT_ATTRIBUTE] | typing.Literal[EntityType.INVENTORY_AMOUNT] | typing.Literal[EntityType.TASK] | typing.Literal[EntityType.PROJECT] | typing.Literal[EntityType.EQUIPMENT] | typing.Literal[EntityType.INV_LOCAL_LOCATIONS] | typing.Literal[EntityType.FIELD_OPTION_SET] | typing.Literal[EntityType.WEBHOOK] | typing.Literal[EntityType.SPECS] | typing.Literal[EntityType.GOAL] | typing.Literal[EntityType.INGREDIENT_TAG_MAP] | typing.Literal[EntityType.INGREDIENT_TAG] | typing.Literal[EntityType.CONDITION_PARAMETER] | typing.Literal[EntityType.OUTPUT] | typing.Literal[EntityType.OUTPUT_CONDITION_PARAMETER] | typing.Literal[EntityType.ASYNC_JOB] | typing.Literal[EntityType.CONSTRAINT] | typing.Literal[EntityType.INGREDIENT_CATEGORY_ALL] | typing.Literal[EntityType.TIME_SERIES_SEGMENT],
350
354
  serial_alias_annotation(
351
355
  named_type_path="sdk.entity.LimitedEntityType",
352
356
  ),
@@ -10,6 +10,9 @@ from .field_values_t import ArgumentValueId as ArgumentValueId
10
10
  from .field_values_t import FieldValueType as FieldValueType
11
11
  from .field_values_t import FieldValueBase as FieldValueBase
12
12
  from .field_values_t import FieldValueFiles as FieldValueFiles
13
+ from .field_values_t import ValueResolution as ValueResolution
14
+ from .field_values_t import FieldValueFieldOption as FieldValueFieldOption
15
+ from .field_values_t import FieldValueFieldOptions as FieldValueFieldOptions
13
16
  from .field_values_t import FieldValueId as FieldValueId
14
17
  from .field_values_t import FieldValueIds as FieldValueIds
15
18
  from .field_values_t import FieldValueText as FieldValueText
@@ -24,12 +24,15 @@ __all__: list[str] = [
24
24
  "FieldValueBase",
25
25
  "FieldValueBatchReference",
26
26
  "FieldValueBoolean",
27
+ "FieldValueFieldOption",
28
+ "FieldValueFieldOptions",
27
29
  "FieldValueFiles",
28
30
  "FieldValueId",
29
31
  "FieldValueIds",
30
32
  "FieldValueNumeric",
31
33
  "FieldValueText",
32
34
  "FieldValueType",
35
+ "ValueResolution",
33
36
  ]
34
37
 
35
38
 
@@ -91,6 +94,8 @@ class FieldValueType(StrEnum):
91
94
  BOOLEAN = "boolean"
92
95
  NUMERIC = "numeric"
93
96
  BATCH_REFERENCE = "batch_reference"
97
+ FIELD_OPTION = "field_option"
98
+ FIELD_OPTIONS = "field_options"
94
99
 
95
100
 
96
101
  # DO NOT MODIFY -- This file is generated by type_spec
@@ -113,6 +118,36 @@ class FieldValueFiles(FieldValueBase):
113
118
  file_ids: list[base_t.ObjectId]
114
119
 
115
120
 
121
+ # DO NOT MODIFY -- This file is generated by type_spec
122
+ class ValueResolution(StrEnum):
123
+ DIRECT = "direct"
124
+ LABEL = "label"
125
+
126
+
127
+ # DO NOT MODIFY -- This file is generated by type_spec
128
+ @serial_class(
129
+ named_type_path="sdk.field_values.FieldValueFieldOption",
130
+ parse_require={"type"},
131
+ )
132
+ @dataclasses.dataclass(kw_only=True)
133
+ class FieldValueFieldOption(FieldValueBase):
134
+ type: typing.Literal[FieldValueType.FIELD_OPTION] = FieldValueType.FIELD_OPTION
135
+ value: str
136
+ value_resolution: ValueResolution = ValueResolution.DIRECT
137
+
138
+
139
+ # DO NOT MODIFY -- This file is generated by type_spec
140
+ @serial_class(
141
+ named_type_path="sdk.field_values.FieldValueFieldOptions",
142
+ parse_require={"type"},
143
+ )
144
+ @dataclasses.dataclass(kw_only=True)
145
+ class FieldValueFieldOptions(FieldValueBase):
146
+ type: typing.Literal[FieldValueType.FIELD_OPTIONS] = FieldValueType.FIELD_OPTIONS
147
+ value: list[str]
148
+ value_resolution: ValueResolution = ValueResolution.DIRECT
149
+
150
+
116
151
  # DO NOT MODIFY -- This file is generated by type_spec
117
152
  @serial_class(
118
153
  named_type_path="sdk.field_values.FieldValueId",
@@ -185,7 +220,7 @@ class FieldValueBatchReference(FieldValueBase):
185
220
 
186
221
  # DO NOT MODIFY -- This file is generated by type_spec
187
222
  FieldValue = typing.Annotated[
188
- FieldValueFiles | FieldValueId | FieldValueIds | FieldValueText | FieldValueBoolean | FieldValueNumeric | FieldValueBatchReference,
223
+ FieldValueFiles | FieldValueId | FieldValueIds | FieldValueText | FieldValueBoolean | FieldValueNumeric | FieldValueBatchReference | FieldValueFieldOption | FieldValueFieldOptions,
189
224
  serial_union_annotation(
190
225
  named_type_path="sdk.field_values.FieldValue",
191
226
  ),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: UncountablePythonSDK
3
- Version: 0.0.100
3
+ Version: 0.0.103
4
4
  Summary: Uncountable SDK
5
5
  Project-URL: Homepage, https://github.com/uncountableinc/uncountable-python-sdk
6
6
  Project-URL: Repository, https://github.com/uncountableinc/uncountable-python-sdk.git
@@ -38,7 +38,7 @@ Requires-Dist: simplejson==3.*
38
38
  Requires-Dist: grpcio==1.67.1
39
39
  Requires-Dist: protobuf>=4.21.1
40
40
  Requires-Dist: azure-storage-blob==12.*
41
- Requires-Dist: boto3-stubs[essential,ses]==1.36.4
41
+ Requires-Dist: boto3-stubs[essential,ses]==1.*
42
42
  Provides-Extra: test
43
43
  Requires-Dist: mypy==1.*; extra == "test"
44
44
  Requires-Dist: ruff==0.*; extra == "test"
@@ -1,7 +1,7 @@
1
1
  docs/.gitignore,sha256=_ebkZUcwfvfnGEJ95rfj1lxoBNd6EE9ZvtOc7FsbfFE,7
2
2
  docs/conf.py,sha256=YF5J-9g_Wg8wXmyHsGaE8xYlDEzqocNl3UWUmP0CwBg,1702
3
3
  docs/index.md,sha256=eEdirX_Ds6ICTRtIS5iT4irCquHcQyKN7E4M5QP9T8A,257
4
- docs/justfile,sha256=cvNcpb-ByPOF2aCrFlg3DDZBoYMx5W8xGdr13m9HcnI,215
4
+ docs/justfile,sha256=Ej6_tqyrBIQaEXOwjIrwIfhTOOC-9xZr0i7wPWhDJkQ,273
5
5
  docs/quickstart.md,sha256=3GuJ0MB1O5kjlsrgAmdSkDq0rYqATrYy-tzEHDy8H-c,422
6
6
  docs/requirements.txt,sha256=jyGjzxSfYlDaELlnD_G2PE7wMFV0b3aB_kUaJrC1HRo,153
7
7
  docs/static/logo_blue.png,sha256=SyYpMTVhhBbhF5Wl8lWaVwz-_p1MIR6dW6bVhufQRME,46708
@@ -22,16 +22,16 @@ examples/invoke_uploader.py,sha256=rEvmVY5TjigN_-4PTQdkjY-bC5DrYMcJgquyZ4Tt5FM,7
22
22
  examples/set_recipe_metadata_file.py,sha256=cRVXGz4UN4aqnNrNSzyBmikYHpe63lMIuzOpMwD9EDU,1036
23
23
  examples/set_recipe_output_file_sdk.py,sha256=Lz1amqppnWTX83z-C090wCJ4hcKmCD3kb-4v0uBRi0Y,782
24
24
  examples/upload_files.py,sha256=qMaSvMSdTMPOOP55y1AwEurc0SOdZAMvEydlqJPsGpg,432
25
- examples/integration-server/pyproject.toml,sha256=ziyEbSiTYlOk784euVJjuZ3UqIEENkPXuug5VTrkjms,9131
25
+ examples/integration-server/pyproject.toml,sha256=VjUN8AZWIzliyLVX2hLfoMAOzxGini62g1287kJflr4,9127
26
26
  examples/integration-server/jobs/materials_auto/example_cron.py,sha256=7VVQ-UJsq3DbGpN3XPnorRVZYo-vCwbfSU3VVDluIzA,699
27
27
  examples/integration-server/jobs/materials_auto/example_wh.py,sha256=Hx5nonavOh2L3JykDpI5bPqPu8L1wwhwekTUfTRgq9g,479
28
28
  examples/integration-server/jobs/materials_auto/profile.yaml,sha256=XlOXSRplMJ13T6900pv1wDKxeE9V1hZZTMuvup1MiBM,896
29
29
  pkgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  pkgs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- pkgs/argument_parser/__init__.py,sha256=nQGVudX2XjaFRNXU7V3e-Ap-zmUpRVrwHqlzJ1FDgvk,816
31
+ pkgs/argument_parser/__init__.py,sha256=VWUOOtJ-ueRF2lkIJzgQe4xhBKR9IPkgf9vY28nF35s,870
32
32
  pkgs/argument_parser/_is_enum.py,sha256=Gw6jJa8nBwYGqXwwCZbSnWL8Rvr5alkg5lSVAqXtOZM,257
33
33
  pkgs/argument_parser/_is_namedtuple.py,sha256=Rjc1bKanIPPogl3qG5JPBxglG1TqWYOo1nxxhBASQWY,265
34
- pkgs/argument_parser/argument_parser.py,sha256=_W0-HLC1F8jXgeFJnhUREorqcXzuhZi__eX_7FFkkVs,19363
34
+ pkgs/argument_parser/argument_parser.py,sha256=_0vZH2yr_yzakQ4KpucAS8xSGbBhxF-k7vWMLSYfgaA,19654
35
35
  pkgs/argument_parser/case_convert.py,sha256=NuJLJUJRbyVb6_Slen4uqaStEHbcOS1d-hBBfDrrw-c,605
36
36
  pkgs/filesystem_utils/__init__.py,sha256=2a0d2rEPlEEYwhm3Wckny4VCp4ZS7JtYSXmwdwNCRjo,1332
37
37
  pkgs/filesystem_utils/_blob_session.py,sha256=CtoB7PIocuZo8vvFIS_Rc-YR6KwzFB0rHUVPKFEbRAI,4862
@@ -46,7 +46,7 @@ pkgs/serialization/annotation.py,sha256=wn9yb4a4DrsyPAYqY9mVswJarP4lsOkgfnvchrg5
46
46
  pkgs/serialization/missing_sentry.py,sha256=89SomyM0sBWLr_N0SPOyxU3hWcm1ljL-veTazZ1-GU8,811
47
47
  pkgs/serialization/opaque_key.py,sha256=8ak7aMCGWkKDjnG374yqy8gtnCCUzG2DSJEBfoPgi0c,194
48
48
  pkgs/serialization/serial_alias.py,sha256=zP0wAy7NGvpyOe-TGUwHy3wPF0XZTIwFLh_caDX67BU,1343
49
- pkgs/serialization/serial_class.py,sha256=hL5SVL4MqSzeTXSLL15fvDQtk32z0NywsKBWUL9b-k0,6100
49
+ pkgs/serialization/serial_class.py,sha256=xMcvPJ5fdaaI9dY5SAz3vMKY0V6KvnDKp5-YsgYvLLk,6085
50
50
  pkgs/serialization/serial_generic.py,sha256=vAC0-o9fkco2lM_YvBavY0JDYncDxbW3jFboYeHRImw,461
51
51
  pkgs/serialization/serial_union.py,sha256=IdWsSsM8j-qGfl5Wv6haX37ZS1SKob8XT8cilBBOVIo,2670
52
52
  pkgs/serialization/yaml.py,sha256=yoJtu7_ixnJV6uTxA_U1PpK5F_ixT08AKVh5ocyYwXM,1466
@@ -59,13 +59,13 @@ pkgs/strenum_compat/__init__.py,sha256=wXRFeNvBm8RU6dy1PFJ5sRLgUIEeH_DVR95Sv5qpG
59
59
  pkgs/strenum_compat/strenum_compat.py,sha256=uOUAgpYTjHs1MX8dG81jRlyTkt3KNbkV_25zp7xTX2s,36
60
60
  pkgs/type_spec/__init__.py,sha256=h5DmJTca4QVV10sZR1x0-MlkZfuGYDfapR3zHvXfzto,19
61
61
  pkgs/type_spec/__main__.py,sha256=5bJaX9Y_-FavP0qwzhk-z-V97UY7uaezJTa1zhO_HHQ,1048
62
- pkgs/type_spec/builder.py,sha256=GY8t34G-0YGjUsVgrCmprGpkYGZZkdbENu4ZGOsWAtc,52911
63
- pkgs/type_spec/config.py,sha256=K-DFjRVIW5RCJOuB0QhkWZMOrsIZavFURqmhgAoULgw,5103
62
+ pkgs/type_spec/builder.py,sha256=8oiNhxN_pidHkB62zt1rLsLnAPsNhC2QyFf8t1ePFns,52728
63
+ pkgs/type_spec/config.py,sha256=HThKnWQ4zkWOyUIb12hTodsrvEnhSzOvhHKCOeTM0iY,5302
64
64
  pkgs/type_spec/cross_output_links.py,sha256=nmyWUaFRk7MLTdJiosfKkhOb4VLE71A3DDKKwTZAncY,3123
65
65
  pkgs/type_spec/emit_io_ts.py,sha256=CUvBs0boB_X-Kndh66yYcqFfq3oC_LGs8YffLkJ0ZXA,5707
66
- pkgs/type_spec/emit_open_api.py,sha256=9AgtHB3ZIObs9cV-RqNZt_q1icmIGwPeovQ63gTl-l8,24541
67
- pkgs/type_spec/emit_open_api_util.py,sha256=34zslFjTiOI5q8YreBOLD-afmaW4lrlK7pBpNAd3CxA,2379
68
- pkgs/type_spec/emit_python.py,sha256=AGpJ63tGXj9AkTkqP9Sbx3eFIO27T5wQ7yLRmLoViRo,52277
66
+ pkgs/type_spec/emit_open_api.py,sha256=kHP4jKRica4Us28_9G2iHq9K7BrUdsl8daIIHZaPdws,25000
67
+ pkgs/type_spec/emit_open_api_util.py,sha256=UwaUcgqTwl_D8s2y6ic3fC23VllVcP0w_2iUg9ThYCA,2361
68
+ pkgs/type_spec/emit_python.py,sha256=aHRrEGx_WAnf5w5zN7lWrIToslh8l-yJtGn50r_6fr4,52402
69
69
  pkgs/type_spec/emit_typescript.py,sha256=0HRzxlbIP91rzbVkAntF4TKZppoKcWsqnDLAIRc1bng,10927
70
70
  pkgs/type_spec/emit_typescript_util.py,sha256=8ophCR8MX0IvYtLYu3omfPQk2H6BeYGd2psRir9ImmQ,10550
71
71
  pkgs/type_spec/load_types.py,sha256=GndEKQtICCQi4oXsL6cZ9khm8lBB830e6hx0wML4dHs,4278
@@ -77,8 +77,8 @@ pkgs/type_spec/actions_registry/__main__.py,sha256=SRw6kIhHTW7W2wGijYq66JARzoc4K
77
77
  pkgs/type_spec/actions_registry/emit_typescript.py,sha256=W1lI36ITdJ7MBf37wlTB7H3X9Ljt217vIGMv4e3fxfY,5986
78
78
  pkgs/type_spec/parts/base.py.prepart,sha256=RDNNo4nbLiC9ASIODq2xIt0HkwDNiJVcmjDD33Bubaw,2303
79
79
  pkgs/type_spec/parts/base.ts.prepart,sha256=2FJJvpg2olCcavxj0nbYWdwKl6KeScour2JjSvN42l8,1001
80
- pkgs/type_spec/type_info/__main__.py,sha256=pmVjVqXyVh8vKTNCTFgz80Sg74C5BKToP3E6GS-X_So,857
81
- pkgs/type_spec/type_info/emit_type_info.py,sha256=mQSxG-UTFl0i8ylvXEvpk1d9NIMYsx39Le7-i5JVuN0,13520
80
+ pkgs/type_spec/type_info/__main__.py,sha256=TLNvCHGcmaj_8Sj5bAQNpuNaaw2dpDzoFDWZds0V4Qo,1002
81
+ pkgs/type_spec/type_info/emit_type_info.py,sha256=Rro9QVq6ZxEDm_gWlwtqqLeazelGIZhSf4pHtMUIJyo,14495
82
82
  pkgs/type_spec/value_spec/__init__.py,sha256=Z-grlcZtxAfEXhPHsK0nD7PFLGsv4eqvunaPN7_TA84,83
83
83
  pkgs/type_spec/value_spec/__main__.py,sha256=hCTdANTgCNTdZVcXeUd8axENrKNb_oVNrmrygjQht48,8663
84
84
  pkgs/type_spec/value_spec/convert_type.py,sha256=X5N7DOTo5XOIHbcYHh9RZFthzb2gcnYg2tRuVMBhbxY,2517
@@ -88,7 +88,7 @@ uncountable/__init__.py,sha256=8l8XWNCKsu7TG94c-xa2KHpDegvxDC2FyQISdWC763Y,89
88
88
  uncountable/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
89
  uncountable/core/__init__.py,sha256=RFv0kO6rKFf1PtBPu83hCGmxqkJamRtsgQ9_-ztw7tA,341
90
90
  uncountable/core/async_batch.py,sha256=9pYGFzVCQXt8059qFHgutweGIFPquJ5Xfq6NT5P-1K0,1206
91
- uncountable/core/client.py,sha256=ZlGKzpAnPaU0ABOb_bhIDUKZ9x8rlezqVaK0mfDvdw8,13253
91
+ uncountable/core/client.py,sha256=9yvwFKA3rGXlvXqwZPB4B6WwVrVYs2_RwH8Z7MYJtsI,13285
92
92
  uncountable/core/environment.py,sha256=6cc-nUvUIbJMI0OSEg3dWI-iNgSYhCdUKKn607Z3QpM,1040
93
93
  uncountable/core/file_upload.py,sha256=bgvXk9vfF5qlhy2NAUcEEG7Q7i-c1wr2HrpaWD7HldU,4516
94
94
  uncountable/core/types.py,sha256=s2CjqYJpsmbC7xMwxxT7kJ_V9bwokrjjWVVjpMcQpKI,333
@@ -101,7 +101,7 @@ uncountable/integration/job.py,sha256=zamfyEhtZh0cWyd8DvVq64RlaA1aX_wtOALv6Kf7ty
101
101
  uncountable/integration/scan_profiles.py,sha256=760zbv7O7wXxHUHqUkFBpd1Afe8hqxMPU3ugwZGdhEo,2925
102
102
  uncountable/integration/scheduler.py,sha256=99Vq9RBdK_clu4-dS7JtU4aqcxZicG8Njt992if2jK8,4811
103
103
  uncountable/integration/server.py,sha256=m_DYRosGbHuPhygM32Xo-jRBl_oaUhOYsBBD1qwwKh4,4697
104
- uncountable/integration/telemetry.py,sha256=QFvvJdzc15CvdaRLFaJeru0OouAFbYdhHYYovbT8bXg,7425
104
+ uncountable/integration/telemetry.py,sha256=jeeycEnGcW0Fk-f7JI5d-k8Ih4FLoNBCxvSJlJ98wwU,7418
105
105
  uncountable/integration/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
106
  uncountable/integration/db/connect.py,sha256=mE3bdV0huclH2iT_dXCQdRL4LkjIuf_myAR64RTWXEs,498
107
107
  uncountable/integration/db/session.py,sha256=96cGQXpe6IugBTdSsjdP0S5yhJ6toSmbVB6qhc3FJzE,693
@@ -130,10 +130,10 @@ uncountable/integration/queue_runner/datastore/model.py,sha256=8-RI5A2yPZVGBLWIN
130
130
  uncountable/integration/secret_retrieval/__init__.py,sha256=3QXVj35w8rRMxVvmmsViFYDi3lcb3g70incfalOEm6o,87
131
131
  uncountable/integration/secret_retrieval/retrieve_secret.py,sha256=9iz9N8Z-B68QwFCXsx8hTYbgDbk06ejkJ3RQ9mCLMyM,3000
132
132
  uncountable/integration/webhook_server/entrypoint.py,sha256=yQWQq_k3kbJkSsEEt6k22YwhXekezJZfV0rnn-hP-Yo,5516
133
- uncountable/types/__init__.py,sha256=LIhmCf1ohKuXV4FDQys7NkNFSTTQMpS3OqoEoXWwnoI,9306
133
+ uncountable/types/__init__.py,sha256=a2kCvZBwEAFM3OaadEAZ2bugCLt5XF3-jtMv1fnQLQw,9488
134
134
  uncountable/types/async_batch.py,sha256=yCCWrrLQfxXVqZp-KskxLBNkNmuELdz4PJjx8ULppgs,662
135
- uncountable/types/async_batch_processor.py,sha256=OIVZdDTkxuIJOzjU_IT1Sol1qtpvPWxuajcxp4tK0YY,15518
136
- uncountable/types/async_batch_t.py,sha256=aFH_2L2uf4FuW54wh5ZRKLXBJZs92CTNIqh-Tw3Kfj8,3064
135
+ uncountable/types/async_batch_processor.py,sha256=zko4MIgYSfV0bh46jyUBYVGbUQUxTESajGH7hAFUlCg,17912
136
+ uncountable/types/async_batch_t.py,sha256=imFU0INA-LAGYXDhwlWWTVxfGizmDOYZwUs7BgqFzpA,3229
137
137
  uncountable/types/async_jobs.py,sha256=JI0ScfawaqMRbJ2jbgW3YQLhijPnBeYdMnZJjygSxHg,322
138
138
  uncountable/types/async_jobs_t.py,sha256=u4xd3i512PZ-9592Q2ZgWh_faMiI4UMm0F_gOmZnerI,1389
139
139
  uncountable/types/auth_retrieval.py,sha256=770zjN1K9EF5zs1Xml7x6ke6Hkze7rcMT5FdDVCIl9M,549
@@ -144,17 +144,17 @@ uncountable/types/calculations.py,sha256=fApOFpgBemt_t7IVneVR0VdI3X5EOxiG6Xhzr6R
144
144
  uncountable/types/calculations_t.py,sha256=LE_HWYPf7yxNTpH5sVPaC0e98OxJ5hUOhHezeJNKDtk,648
145
145
  uncountable/types/chemical_structure.py,sha256=ujyragaD26-QG5jgKnWhO7TN3N1V9b_04T2WhqNYxxo,281
146
146
  uncountable/types/chemical_structure_t.py,sha256=iYmGER_vXqoksv2Nr189qU1Zm70s-U49Eiv0DkWaB1I,773
147
- uncountable/types/client_base.py,sha256=DqEnzUQdJP2UDEbYoM_Yg8MN1pqxZSlv3min7Sbd2lk,70129
147
+ uncountable/types/client_base.py,sha256=EMf8jyO2gJ3M_2ypkddkF_4O-bjLJ7n4qSWQW0EHYZ8,71372
148
148
  uncountable/types/client_config.py,sha256=qLpHt4O_B098CyN6qQajoxZ2zjZ1DILXLUEGyyGP0TQ,280
149
149
  uncountable/types/client_config_t.py,sha256=k_UkjemqiVuJBiFUjJYk3h673Nahr0nGZvfEZFzsC0k,699
150
150
  uncountable/types/curves.py,sha256=QyEyC20jsG-LGKVx6miiF-w70vKMwNkILFBDIJ5Ok9g,345
151
151
  uncountable/types/curves_t.py,sha256=2_9qdrSl1XAvIG57lo45KWNpa0wXgZ97OkSRCPRrudc,1347
152
152
  uncountable/types/entity.py,sha256=Zclk1LYcRaYrMDhqyCjMSLEg0fE6_q8LHvV22Qvscgs,566
153
- uncountable/types/entity_t.py,sha256=k3kJhPl9bEGtcIlu1BOzdpMAszaFDVHUTZRrDWvW2Fo,17565
153
+ uncountable/types/entity_t.py,sha256=MujknU1WOE77q1qS67ub3QSBYhed2j8lbZpSkkotsRs,17874
154
154
  uncountable/types/experiment_groups.py,sha256=qUpFOx1AKgzaT_4khCOv5Xs6jwiQGbvHH-GUh3v1nv4,288
155
155
  uncountable/types/experiment_groups_t.py,sha256=_fAYZwqYLR3cFdv2vwLOYs5TvH5CEWDEbh3kFpg26zY,700
156
- uncountable/types/field_values.py,sha256=hERqHIDmWPQpdW7BLitdA-DNiutmll-ggecwEO7FoVU,1181
157
- uncountable/types/field_values_t.py,sha256=2BFTqfmlf1tvTFfY5I8xMIBd0IX-EupnzOtwFYFklQs,5751
156
+ uncountable/types/field_values.py,sha256=_5PSyCRPA0IlRThRa8Ny3p0jrumnVvbz_3jyQvnmCp4,1396
157
+ uncountable/types/field_values_t.py,sha256=IE6Q8OFwy1KTZM7-xzohSnPfzJfjR7z2qwR-Atcafho,6909
158
158
  uncountable/types/fields.py,sha256=M0_ZZr0QdNLXkdHAGo5mfU90kEtHedCSKrcod-FG30Y,245
159
159
  uncountable/types/fields_t.py,sha256=Ze-X83HyM7q4oMk5LLRfPqvRojyAx6dDqIUPX70gNYc,644
160
160
  uncountable/types/generic_upload.py,sha256=bNep2nT0fbKAlJaGvHWPmuvfX5KtS8kgTqTh8FQk1NA,858
@@ -225,6 +225,7 @@ uncountable/types/api/entity/get_entities_data.py,sha256=OUeBo0YkZmVTHT2EHAQt19r
225
225
  uncountable/types/api/entity/grant_entity_permissions.py,sha256=Zx3FDHSjiBN_wRtmxbWSWdmT-CkzEoi2uoVLCIdS2Q8,1391
226
226
  uncountable/types/api/entity/list_entities.py,sha256=1I6T6fjfolXx1lg8iRiCO5J0ieU0bdudPp31ZmMkXIQ,1895
227
227
  uncountable/types/api/entity/lock_entity.py,sha256=cWRTNlXj6Ei1M0mHwfbhid7hQs4whCheUCOmf8PIGqw,1166
228
+ uncountable/types/api/entity/lookup_entity.py,sha256=_Znw-Kiw6y9YCpZXE2APhgxJ4aEK_5rVwl6003c8N0g,2803
228
229
  uncountable/types/api/entity/resolve_entity_ids.py,sha256=44B79L4erUB0G90WBGor0ZPs3XhdkuX7ZvgVF9uWB2M,1340
229
230
  uncountable/types/api/entity/set_entity_field_values.py,sha256=ZYtRrsx0MWc0txC6yH2HtCONC5A0IolpqvwHm_YgirU,1179
230
231
  uncountable/types/api/entity/set_values.py,sha256=2lANfjqPOcyxqDeISmzW0KghVbe8_i4QNkcLXzdAkag,1104
@@ -268,6 +269,7 @@ uncountable/types/api/recipe_metadata/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrH
268
269
  uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py,sha256=fW-6_vBIGa5Wx8JvEdpKb3orXD60DX_wOeHnmMPduc8,1550
269
270
  uncountable/types/api/recipes/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
270
271
  uncountable/types/api/recipes/add_recipe_to_project.py,sha256=vJL6DljCu4okNHvXJ5_uaBj0-QDEm9iTvVGMLYyDd2U,1061
272
+ uncountable/types/api/recipes/add_time_series_data.py,sha256=1pghWSwdbaEo--M__2VqYyGRMeXXh_G2--OzuhNWLpE,1737
271
273
  uncountable/types/api/recipes/archive_recipes.py,sha256=07Rjd9kwh5pMpiMXNr9OfjOB4vYXeuLjQtBxrXkXC7o,1024
272
274
  uncountable/types/api/recipes/associate_recipe_as_input.py,sha256=TtsAJRT9vRjnsIh0IvfFimA2hstZvgyn6rpaDIsDfK4,1194
273
275
  uncountable/types/api/recipes/associate_recipe_as_lot.py,sha256=_4SgFfH-wFxW9Vevv9k3G3T10Uz8CMLjuiDEv1oT2So,1138
@@ -289,7 +291,7 @@ uncountable/types/api/recipes/set_recipe_inputs.py,sha256=RvWj2SOtcuy_msp9pUjz20
289
291
  uncountable/types/api/recipes/set_recipe_metadata.py,sha256=R4GVqpMJdJuoSXsJx1e4vhmWqKKTPMyPPEArwV45crI,1104
290
292
  uncountable/types/api/recipes/set_recipe_output_annotations.py,sha256=yczFBmuB0grzNWsnXspCPUsDA608M9wGkJ2dugyxG4U,3526
291
293
  uncountable/types/api/recipes/set_recipe_output_file.py,sha256=W_qvEkZxzDczwU9EZ7zKyBmLGnO6GFqMsJ3zwXCnpA0,1502
292
- uncountable/types/api/recipes/set_recipe_outputs.py,sha256=hNeV3hwPw3fT-pnhe17fPPgLH7pfpva0kjpt2NTt55E,2104
294
+ uncountable/types/api/recipes/set_recipe_outputs.py,sha256=FyReW7TLJ9eyO5lmL9U8o6DLxxxMp78Z__rLIAyd9IM,2366
293
295
  uncountable/types/api/recipes/set_recipe_tags.py,sha256=HS5GG-nTy0vTNTANG-ROawku0VhzOs0hIzGKfRFefYY,3098
294
296
  uncountable/types/api/recipes/unarchive_recipes.py,sha256=G0jYuarNZLmTCQ6m5_ZAUwBl4M8_cqKRlP-jMJp-Rcw,1000
295
297
  uncountable/types/api/recipes/unlock_recipes.py,sha256=QFvz13eJipWQlIRrqOybnIPeJxfR4yZM8yIr841jd28,1258
@@ -297,7 +299,7 @@ uncountable/types/api/triggers/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr
297
299
  uncountable/types/api/triggers/run_trigger.py,sha256=diX1ix_5hkti1F1uYoZhP5iyc6GHAU5coKgqq5syLhI,1059
298
300
  uncountable/types/api/uploader/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
299
301
  uncountable/types/api/uploader/invoke_uploader.py,sha256=-loZzBihKqx63eP-f5RuV1mu6tgkRTZmIc545kklZLk,1273
300
- UncountablePythonSDK-0.0.100.dist-info/METADATA,sha256=Fqij3M1FpcoJ4yPnNILcTkFQz2_2mdGIybT6PWTX93o,2115
301
- UncountablePythonSDK-0.0.100.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
302
- UncountablePythonSDK-0.0.100.dist-info/top_level.txt,sha256=1UVGjAU-6hJY9qw2iJ7nCBeEwZ793AEN5ZfKX9A1uj4,31
303
- UncountablePythonSDK-0.0.100.dist-info/RECORD,,
302
+ uncountablepythonsdk-0.0.103.dist-info/METADATA,sha256=lPJd1v2DjOMjZNnl8_kF5bho1QcSYA6x5qU-axsTzX8,2112
303
+ uncountablepythonsdk-0.0.103.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
304
+ uncountablepythonsdk-0.0.103.dist-info/top_level.txt,sha256=1UVGjAU-6hJY9qw2iJ7nCBeEwZ793AEN5ZfKX9A1uj4,31
305
+ uncountablepythonsdk-0.0.103.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (75.8.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5