UncountablePythonSDK 0.0.115__py3-none-any.whl → 0.0.117__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.

Files changed (28) hide show
  1. examples/integration-server/jobs/materials_auto/example_http.py +6 -6
  2. examples/integration-server/jobs/materials_auto/example_instrument.py +38 -0
  3. examples/integration-server/jobs/materials_auto/profile.yaml +9 -0
  4. pkgs/type_spec/builder.py +18 -5
  5. pkgs/type_spec/config.py +26 -5
  6. pkgs/type_spec/cross_output_links.py +9 -7
  7. pkgs/type_spec/emit_open_api.py +18 -2
  8. pkgs/type_spec/emit_open_api_util.py +1 -0
  9. pkgs/type_spec/emit_python.py +4 -1
  10. pkgs/type_spec/emit_typescript.py +46 -30
  11. pkgs/type_spec/emit_typescript_util.py +16 -0
  12. pkgs/type_spec/load_types.py +1 -1
  13. pkgs/type_spec/open_api_util.py +16 -1
  14. pkgs/type_spec/parts/base.ts.prepart +1 -0
  15. pkgs/type_spec/ui_entry_actions/generate_ui_entry_actions.py +1 -0
  16. uncountable/types/__init__.py +4 -0
  17. uncountable/types/api/entity/create_or_update_entity.py +1 -0
  18. uncountable/types/api/integrations/__init__.py +1 -0
  19. uncountable/types/api/integrations/publish_realtime_data.py +41 -0
  20. uncountable/types/async_batch_processor.py +3 -0
  21. uncountable/types/client_base.py +3 -0
  22. uncountable/types/entity_t.py +6 -0
  23. uncountable/types/integrations.py +10 -0
  24. uncountable/types/integrations_t.py +62 -0
  25. {uncountablepythonsdk-0.0.115.dist-info → uncountablepythonsdk-0.0.117.dist-info}/METADATA +1 -1
  26. {uncountablepythonsdk-0.0.115.dist-info → uncountablepythonsdk-0.0.117.dist-info}/RECORD +28 -23
  27. {uncountablepythonsdk-0.0.115.dist-info → uncountablepythonsdk-0.0.117.dist-info}/WHEEL +0 -0
  28. {uncountablepythonsdk-0.0.115.dist-info → uncountablepythonsdk-0.0.117.dist-info}/top_level.txt +0 -0
@@ -19,17 +19,17 @@ class HttpExample(CustomHttpJob):
19
19
  @staticmethod
20
20
  def validate_request(
21
21
  *,
22
- request: GenericHttpRequest,
23
- job_definition: job_definition_t.HttpJobDefinitionBase,
24
- profile_meta: job_definition_t.ProfileMetadata,
22
+ request: GenericHttpRequest, # noqa: ARG004
23
+ job_definition: job_definition_t.HttpJobDefinitionBase, # noqa: ARG004
24
+ profile_meta: job_definition_t.ProfileMetadata, # noqa: ARG004
25
25
  ) -> None:
26
26
  return None
27
27
 
28
28
  @staticmethod
29
29
  def handle_request(
30
30
  *,
31
- request: GenericHttpRequest,
32
- job_definition: job_definition_t.HttpJobDefinitionBase,
33
- profile_meta: job_definition_t.ProfileMetadata,
31
+ request: GenericHttpRequest, # noqa: ARG004
32
+ job_definition: job_definition_t.HttpJobDefinitionBase, # noqa: ARG004
33
+ profile_meta: job_definition_t.ProfileMetadata, # noqa: ARG004
34
34
  ) -> GenericHttpResponse:
35
35
  return GenericHttpResponse(response="OK", status_code=200)
@@ -0,0 +1,38 @@
1
+ from dataclasses import dataclass
2
+
3
+ from uncountable.integration.job import JobArguments, WebhookJob, register_job
4
+ from uncountable.types import base_t, entity_t, job_definition_t
5
+
6
+
7
+ @dataclass(kw_only=True)
8
+ class InstrumentPayload:
9
+ equipment_id: base_t.ObjectId
10
+
11
+
12
+ @register_job
13
+ class InstrumentExample(WebhookJob[InstrumentPayload]):
14
+ def run(
15
+ self, args: JobArguments, payload: InstrumentPayload
16
+ ) -> job_definition_t.JobResult:
17
+ equipment_data = args.client.get_entities_data(
18
+ entity_type=entity_t.EntityType.EQUIPMENT,
19
+ entity_ids=[payload.equipment_id],
20
+ ).entity_details[0]
21
+
22
+ # Load the instrument's connection details from the entity
23
+ instrument_id = None
24
+ for field in equipment_data.field_values:
25
+ if field.field_ref_name == "ins_instrument_id":
26
+ instrument_id = field.value
27
+
28
+ if instrument_id is None:
29
+ args.logger.log_error("Could not find instrument ID")
30
+ return job_definition_t.JobResult(success=False)
31
+
32
+ args.logger.log_info(f"Instrument ID: {instrument_id}")
33
+
34
+ return job_definition_t.JobResult(success=True)
35
+
36
+ @property
37
+ def webhook_payload_type(self) -> type:
38
+ return InstrumentPayload
@@ -75,3 +75,12 @@ jobs:
75
75
  executor:
76
76
  type: script
77
77
  import_path: example_runsheet_wh
78
+ - id: example_instrument
79
+ type: webhook
80
+ name: Webhook Instrument Connection
81
+ signature_key_secret:
82
+ type: env
83
+ env_key: WH_INSTRUMENT_SIGNATURE_KEY
84
+ executor:
85
+ type: script
86
+ import_path: example_instrument
pkgs/type_spec/builder.py CHANGED
@@ -15,12 +15,23 @@ from typing import Any, Self
15
15
  from . import util
16
16
  from .cross_output_links import CrossOutputPaths
17
17
  from .non_discriminated_union_exceptions import NON_DISCRIMINATED_UNION_EXCEPTIONS
18
- from .util import parse_type_str, unused
18
+ from .util import parse_type_str
19
19
 
20
20
  RawDict = dict[Any, Any]
21
21
  EndpointKey = str
22
22
 
23
23
 
24
+ class PathMapping(StrEnum):
25
+ NO_MAPPING = "no_mapping"
26
+ DEFAULT_MAPPING = "default_mapping"
27
+
28
+
29
+ @dataclass(kw_only=True)
30
+ class APIEndpointInfo:
31
+ root_path: str
32
+ path_mapping: PathMapping
33
+
34
+
24
35
  class StabilityLevel(StrEnum):
25
36
  """These are currently used for open api,
26
37
  see: https://github.com/Tufin/oasdiff/blob/main/docs/STABILITY.md
@@ -835,7 +846,7 @@ class _EndpointPathDetails:
835
846
 
836
847
 
837
848
  def _resolve_endpoint_path(
838
- path: str, api_endpoints: dict[EndpointKey, str]
849
+ path: str, api_endpoints: dict[EndpointKey, APIEndpointInfo]
839
850
  ) -> _EndpointPathDetails:
840
851
  root_path_source = path.split("/")[0]
841
852
  root_match = RE_ENDPOINT_ROOT.fullmatch(root_path_source)
@@ -843,7 +854,7 @@ def _resolve_endpoint_path(
843
854
  raise Exception(f"invalid-api-path-root:{root_path_source}")
844
855
 
845
856
  root_var = root_match.group(1)
846
- root_path = api_endpoints[root_var]
857
+ root_path = api_endpoints[root_var].root_path
847
858
 
848
859
  _, *rest_path = path.split("/", 1)
849
860
  resolved_path = "/".join([root_path] + rest_path)
@@ -911,6 +922,7 @@ class SpecEndpoint:
911
922
  stability_level: StabilityLevel | None
912
923
  # Don't emit TypeScript endpoint code
913
924
  suppress_ts: bool
925
+ deprecated: bool = False
914
926
  async_batch_path: str | None = None
915
927
  result_type: ResultType = ResultType.json
916
928
  has_attachment: bool = False
@@ -928,13 +940,13 @@ class SpecEndpoint:
928
940
  pass
929
941
 
930
942
  def process(self, builder: SpecBuilder, data: RawDict) -> None:
931
- unused(builder)
932
943
  util.check_fields(
933
944
  data,
934
945
  [
935
946
  "method",
936
947
  "path",
937
948
  "data_loader",
949
+ "deprecated",
938
950
  "is_sdk",
939
951
  "stability_level",
940
952
  "async_batch_path",
@@ -954,6 +966,7 @@ class SpecEndpoint:
954
966
  data_loader = data.get("data_loader", False)
955
967
  assert isinstance(data_loader, bool)
956
968
  self.data_loader = data_loader
969
+ self.deprecated = data.get("deprecated", False)
957
970
 
958
971
  is_sdk = data.get("is_sdk", EndpointEmitType.EMIT_NOTHING)
959
972
 
@@ -1346,7 +1359,7 @@ class SpecBuilder:
1346
1359
  def __init__(
1347
1360
  self,
1348
1361
  *,
1349
- api_endpoints: dict[EndpointKey, str],
1362
+ api_endpoints: dict[EndpointKey, APIEndpointInfo],
1350
1363
  top_namespace: str,
1351
1364
  cross_output_paths: CrossOutputPaths | None,
1352
1365
  ) -> None:
pkgs/type_spec/config.py CHANGED
@@ -4,6 +4,7 @@ from dataclasses import dataclass
4
4
  from typing import Self, TypeVar
5
5
 
6
6
  from pkgs.serialization import yaml
7
+ from pkgs.type_spec.builder import APIEndpointInfo, EndpointKey
7
8
 
8
9
  ConfigValueType = str | None | Mapping[str, str | None] | list[str]
9
10
 
@@ -19,6 +20,22 @@ def _parse_string_lookup(
19
20
  }
20
21
 
21
22
 
23
+ VT = TypeVar("VT")
24
+
25
+
26
+ def _parse_data_lookup(
27
+ key: str,
28
+ raw_value: ConfigValueType,
29
+ conv_func: type[VT],
30
+ ) -> dict[str, VT]:
31
+ assert isinstance(raw_value, dict), f"{key} must be key/values"
32
+ return {
33
+ k: conv_func(**v)
34
+ for k, v in raw_value.items()
35
+ if v is not None and isinstance(v, dict)
36
+ }
37
+
38
+
22
39
  @dataclass(kw_only=True)
23
40
  class BaseLanguageConfig:
24
41
  types_output: (
@@ -31,7 +48,9 @@ class BaseLanguageConfig:
31
48
 
32
49
  @dataclass(kw_only=True)
33
50
  class TypeScriptConfig(BaseLanguageConfig):
34
- routes_output: str # folder for generate route files will be located.
51
+ endpoint_to_routes_output: dict[
52
+ EndpointKey, str
53
+ ] # folder for generate route files will be located.
35
54
  type_info_output: str # folder for generated type info files
36
55
  id_source_output: str | None = None # folder for emitted id source maps.
37
56
  endpoint_to_frontend_app_type: dict[
@@ -39,7 +58,7 @@ class TypeScriptConfig(BaseLanguageConfig):
39
58
  ] # map from api_endpoint to frontend app type
40
59
 
41
60
  def __post_init__(self: Self) -> None:
42
- self.routes_output = self.routes_output
61
+ self.endpoint_to_routes_output = self.endpoint_to_routes_output
43
62
  self.type_info_output = os.path.abspath(self.type_info_output)
44
63
  self.id_source_output = (
45
64
  os.path.abspath(self.id_source_output)
@@ -100,7 +119,7 @@ class OpenAPIConfig(BaseLanguageConfig):
100
119
  class Config:
101
120
  top_namespace: str
102
121
  type_spec_types: list[str] # folders containing the yaml type spec definitions
103
- api_endpoint: dict[str, str]
122
+ api_endpoint: dict[str, APIEndpointInfo]
104
123
  # languages
105
124
  typescript: TypeScriptConfig | None
106
125
  python: PythonConfig
@@ -125,8 +144,10 @@ def parse_yaml_config(config_file: str) -> Config:
125
144
  )
126
145
  type_spec_types = [os.path.abspath(folder) for folder in raw_type_spec_types]
127
146
 
128
- api_endpoint = _parse_string_lookup(
129
- "api_endpoint", raw_config.get("api_endpoint", {}), lambda x: x
147
+ api_endpoint = _parse_data_lookup(
148
+ "api_endpoint",
149
+ raw_config.get("api_endpoint", {}),
150
+ APIEndpointInfo,
130
151
  )
131
152
 
132
153
  raw_typescript = raw_config.get("typescript")
@@ -21,7 +21,7 @@ def get_python_stub_file_path(
21
21
  class CrossOutputPaths:
22
22
  python_types_output: str
23
23
  typescript_types_output: str
24
- typescript_routes_output: str
24
+ typescript_routes_output_by_endpoint: dict[str, str]
25
25
  typespec_files_input: list[str]
26
26
 
27
27
 
@@ -35,10 +35,9 @@ def get_python_api_file_path(
35
35
  def get_typescript_api_file_path(
36
36
  cross_output_paths: CrossOutputPaths,
37
37
  namespace: builder.SpecNamespace,
38
+ endpoint_key: builder.EndpointKey,
38
39
  ) -> str:
39
- return (
40
- f"{cross_output_paths.typescript_routes_output}/{'/'.join(namespace.path)}.tsx"
41
- )
40
+ return f"{cross_output_paths.typescript_routes_output_by_endpoint[endpoint_key]}/{'/'.join(namespace.path)}.tsx"
42
41
 
43
42
 
44
43
  def get_yaml_api_file_path(
@@ -68,13 +67,16 @@ def get_path_links(
68
67
  namespace: builder.SpecNamespace,
69
68
  *,
70
69
  current_path_type: str,
70
+ endpoint: builder.SpecEndpoint,
71
71
  ) -> str:
72
72
  if cross_output_paths is None:
73
73
  return ""
74
74
 
75
75
  api_paths = {
76
76
  "Python": get_python_api_file_path(cross_output_paths, namespace),
77
- "TypeScript": get_typescript_api_file_path(cross_output_paths, namespace),
77
+ "TypeScript": get_typescript_api_file_path(
78
+ cross_output_paths, namespace, endpoint.default_endpoint_key
79
+ ),
78
80
  "YAML": get_yaml_api_file_path(cross_output_paths, namespace),
79
81
  }
80
82
 
@@ -95,11 +97,11 @@ def get_path_links(
95
97
 
96
98
  if namespace.endpoint is not None:
97
99
  for (
98
- endpoint,
100
+ endpoint_key,
99
101
  path_specific_endpoint,
100
102
  ) in namespace.endpoint.path_per_api_endpoint.items():
101
103
  path_from_root = get_python_stub_file_path(path_specific_endpoint.function)
102
104
  if path_from_root is None:
103
105
  continue
104
- paths_string += f"{comment_prefix} Implementation for {endpoint}: file://./{return_to_root_path}{path_from_root}\n"
106
+ paths_string += f"{comment_prefix} Implementation for {endpoint_key}: file://./{return_to_root_path}{path_from_root}\n"
105
107
  return paths_string
@@ -261,6 +261,10 @@ def _emit_endpoint_parameters(
261
261
  } | _emit_endpoint_parameter_examples(examples)
262
262
 
263
263
 
264
+ def _emit_endpoint_deprecated(deprecated: bool) -> DictApiSchema:
265
+ return {"deprecated": True} if deprecated else {}
266
+
267
+
264
268
  def _emit_stability_level(
265
269
  stability_level: EmitOpenAPIStabilityLevel | None,
266
270
  ) -> DictApiSchema:
@@ -376,6 +380,7 @@ def _emit_namespace(
376
380
  "tags": endpoint.tags,
377
381
  "summary": endpoint.summary,
378
382
  }
383
+ | _emit_endpoint_deprecated(endpoint.deprecated)
379
384
  | _emit_endpoint_description(endpoint.description, ctx.endpoint.guides)
380
385
  | _emit_stability_level(endpoint.stability_level)
381
386
  | _emit_endpoint_parameters(endpoint, argument_type, ctx.endpoint.examples)
@@ -474,8 +479,18 @@ def _emit_type(
474
479
  return
475
480
 
476
481
  if isinstance(stype, builder.SpecTypeDefnUnion):
477
- ctx.types[stype.name] = open_api_type(
478
- ctx, stype.get_backing_type(), config=config
482
+ converted_discriminator_map: dict[str, OpenAPIRefType] = dict()
483
+ if stype.discriminator_map is not None:
484
+ for discriminator_value, base_type in stype.discriminator_map.items():
485
+ converted_base_type = open_api_type(ctx, base_type, config=config)
486
+ assert isinstance(converted_base_type, OpenAPIRefType)
487
+ converted_discriminator_map[discriminator_value] = converted_base_type
488
+ ctx.types[stype.name] = OpenAPIUnionType(
489
+ [open_api_type(ctx, p, config=config) for p in stype.types],
490
+ discriminator=stype.discriminator,
491
+ discriminator_map=converted_discriminator_map
492
+ if stype.discriminator_map is not None
493
+ else None,
479
494
  )
480
495
  return
481
496
 
@@ -617,6 +632,7 @@ def _emit_endpoint(
617
632
  tags=[tag_name],
618
633
  summary=f"{'/'.join(namespace.path[path_cutoff:])}",
619
634
  description=description,
635
+ deprecated=namespace.endpoint.deprecated,
620
636
  stability_level=namespace.endpoint.stability_level,
621
637
  examples=[
622
638
  EmitOpenAPIEndpointExample(
@@ -82,6 +82,7 @@ class EmitOpenAPIEndpoint:
82
82
  tags: list[str]
83
83
  summary: str
84
84
  description: str
85
+ deprecated: bool
85
86
  stability_level: EmitOpenAPIStabilityLevel | None
86
87
  examples: list[EmitOpenAPIEndpointExample]
87
88
  guides: list[EmitOpenAPIGuide]
@@ -357,7 +357,10 @@ def _emit_namespace(ctx: Context, namespace: builder.SpecNamespace) -> None:
357
357
  endpoint = namespace.endpoint
358
358
  if endpoint is not None:
359
359
  path_links = get_path_links(
360
- ctx.builder.cross_output_paths, namespace, current_path_type="Python"
360
+ ctx.builder.cross_output_paths,
361
+ namespace,
362
+ current_path_type="Python",
363
+ endpoint=endpoint,
361
364
  )
362
365
  if path_links != "":
363
366
  ctx.out.write("\n")
@@ -1,17 +1,18 @@
1
1
  import io
2
2
  import os
3
+ from typing import assert_never
3
4
 
4
5
  from . import builder, util
5
- from .builder import EndpointKey, EndpointSpecificPath
6
+ from .builder import EndpointKey, EndpointSpecificPath, PathMapping
6
7
  from .config import TypeScriptConfig
7
8
  from .cross_output_links import get_path_links
8
9
  from .emit_io_ts import emit_type_io_ts
9
10
  from .emit_typescript_util import (
10
11
  MODIFY_NOTICE,
11
12
  EmitTypescriptContext,
13
+ emit_constant_ts,
12
14
  emit_namespace_imports_ts,
13
15
  emit_type_ts,
14
- emit_value_ts,
15
16
  resolve_namespace_name,
16
17
  resolve_namespace_ref,
17
18
  ts_type_name,
@@ -37,6 +38,7 @@ def _emit_types(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
37
38
  out=io.StringIO(),
38
39
  namespace=namespace,
39
40
  cross_output_paths=builder.cross_output_paths,
41
+ api_endpoints=builder.api_endpoints,
40
42
  )
41
43
 
42
44
  _emit_namespace(ctx, config, namespace)
@@ -78,6 +80,7 @@ def _emit_types(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
78
80
  full.write("\n")
79
81
  full.write(MODIFY_NOTICE)
80
82
  full.write(f"// === START section from {namespace.name}.ts.part ===\n")
83
+ full.write("\n")
81
84
  full.write(part)
82
85
  full.write(f"// === END section from {namespace.name}.ts.part ===\n")
83
86
 
@@ -112,7 +115,7 @@ def _emit_namespace(
112
115
  emit_type_ts(ctx, stype)
113
116
 
114
117
  for sconst in namespace.constants.values():
115
- _emit_constant(ctx, sconst)
118
+ emit_constant_ts(ctx, sconst)
116
119
 
117
120
  if namespace.endpoint is not None:
118
121
  _emit_endpoint(ctx, config, namespace, namespace.endpoint)
@@ -145,7 +148,10 @@ def _emit_endpoint(
145
148
  assert endpoint.result_type == builder.ResultType.json
146
149
 
147
150
  paths_string = get_path_links(
148
- ctx.cross_output_paths, namespace, current_path_type="TypeScript"
151
+ ctx.cross_output_paths,
152
+ namespace,
153
+ current_path_type="TypeScript",
154
+ endpoint=endpoint,
149
155
  )
150
156
 
151
157
  data_loader_head = ""
@@ -155,7 +161,7 @@ def _emit_endpoint(
155
161
  assert has_data
156
162
 
157
163
  data_loader_head = (
158
- 'import { buildApiDataLoader, argsKey } from "unc_base/data_manager"\n'
164
+ 'import { argsKey, buildApiDataLoader } from "unc_base/data_manager"\n'
159
165
  )
160
166
  data_loader_body = (
161
167
  "\nexport const data = buildApiDataLoader(argsKey(), apiCall)\n"
@@ -175,38 +181,56 @@ def _emit_endpoint(
175
181
  unc_base_api_imports = (
176
182
  f"appSpecificApiPath, {wrap_name}" if has_multiple_endpoints else wrap_name
177
183
  )
184
+ path_mapping = ctx.api_endpoints[endpoint.default_endpoint_key].path_mapping
185
+
186
+ match path_mapping:
187
+ case PathMapping.NO_MAPPING:
188
+ path_mapping_part = (
189
+ "\n { pathMapping: ApplicationT.APIPathMapping.noMapping },"
190
+ )
191
+ case PathMapping.DEFAULT_MAPPING:
192
+ path_mapping_part = ""
193
+ case _:
194
+ assert_never(path_mapping)
195
+
178
196
  unc_types_imports = (
179
- 'import { ApplicationT } from "unc_types"\n' if has_multiple_endpoints else ""
197
+ 'import { ApplicationT } from "unc_types"\n'
198
+ if has_multiple_endpoints or path_mapping_part != ""
199
+ else ""
180
200
  )
181
201
 
182
202
  type_path = f"unc_types/{'/'.join(namespace.path)}"
183
203
 
184
204
  if is_binary:
185
- tsx_response_part = f"""import {{ {unc_base_api_imports} }} from "unc_base/api"
186
- import type {{ Arguments }} from "{type_path}"
187
- {unc_types_imports}
205
+ tsx_response_head = f"""import {{ {unc_base_api_imports} }} from "unc_base/api"
206
+ """
207
+ tsx_response_part = f"""import type {{ Arguments }} from "{type_path}"
208
+
188
209
  export type {{ Arguments }}
189
210
  """
190
211
  elif has_data and endpoint.has_attachment:
191
- tsx_response_part = f"""import {{ {unc_base_api_imports}, type AttachmentResponse }} from "unc_base/api"
192
- import type {{ Arguments, Data }} from "{type_path}"
193
- {unc_types_imports}
212
+ tsx_response_head = f"""import {{ type AttachmentResponse, {unc_base_api_imports} }} from "unc_base/api"
213
+ """
214
+ tsx_response_part = f"""import type {{ Arguments, Data }} from "{type_path}"
215
+
194
216
  export type {{ Arguments, Data }}
195
217
  export type Response = AttachmentResponse<Data>
196
218
  """
197
219
  elif has_data:
198
- tsx_response_part = f"""import {{ {unc_base_api_imports}, type JsonResponse }} from "unc_base/api"
199
- import type {{ Arguments, Data }} from "{type_path}"
200
- {unc_types_imports}
220
+ tsx_response_head = f"""import {{ {unc_base_api_imports}, type JsonResponse }} from "unc_base/api"
221
+ """
222
+ tsx_response_part = f"""import type {{ Arguments, Data }} from "{type_path}"
223
+
201
224
  export type {{ Arguments, Data }}
202
225
  export type Response = JsonResponse<Data>
203
226
  """
204
227
 
205
228
  else:
206
229
  assert has_deprecated_result
207
- tsx_response_part = f"""import {{ {unc_base_api_imports} }} from "unc_base/api"
208
- import type {{ Arguments, DeprecatedResult }} from "{type_path}"
209
- {unc_types_imports}
230
+ tsx_response_head = f"""import {{ {unc_base_api_imports} }} from "unc_base/api"
231
+ """
232
+ tsx_response_part = f"""import type {{ Arguments, DeprecatedResult }} from "{type_path}"
233
+
210
234
  export type {{ Arguments }}
211
235
  export type Response = DeprecatedResult
212
236
  """
@@ -247,18 +271,18 @@ export type Response = DeprecatedResult
247
271
 
248
272
  # tsx_api = f"""{MODIFY_NOTICE}
249
273
  tsx_api = f"""{MODIFY_NOTICE}{paths_string}
250
- {data_loader_head}{tsx_response_part}
274
+ {tsx_response_head}{data_loader_head}{unc_types_imports}{tsx_response_part}
251
275
  export const apiCall = {wrap_call}(
252
- {endpoint_path_part}
276
+ {endpoint_path_part}{path_mapping_part}
253
277
  )
254
278
  {data_loader_body}"""
255
279
 
256
- output = f"{config.routes_output}/{'/'.join(namespace.path)}.tsx"
280
+ output = f"{config.endpoint_to_routes_output[endpoint.default_endpoint_key]}/{'/'.join(namespace.path)}.tsx"
257
281
  util.rewrite_file(output, tsx_api)
258
282
 
259
283
  # Hacky index support, until enough is migrated to regen entirely
260
284
  # Emits the import into the UI API index file
261
- index_path = f"{config.routes_output}/{'/'.join(namespace.path[0:-1])}/index.tsx"
285
+ index_path = f"{config.endpoint_to_routes_output[endpoint.default_endpoint_key]}/{'/'.join(namespace.path[0:-1])}/index.tsx"
262
286
  api_name = f"Api{ts_type_name(namespace.path[0 - 1])}"
263
287
  if os.path.exists(index_path):
264
288
  with open(index_path) as index:
@@ -274,14 +298,6 @@ export const apiCall = {wrap_call}(
274
298
  index.write(f"export {{ {api_name} }}\n")
275
299
 
276
300
 
277
- def _emit_constant(ctx: EmitTypescriptContext, sconst: builder.SpecConstant) -> None:
278
- ctx.out.write("\n\n")
279
- ctx.out.write(MODIFY_NOTICE)
280
- value = emit_value_ts(ctx, sconst.value_type, sconst.value)
281
- const_name = sconst.name.upper()
282
- ctx.out.write(f"export const {const_name} = {value}\n")
283
-
284
-
285
301
  def _emit_id_source(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
286
302
  id_source_output = config.id_source_output
287
303
  if id_source_output is None:
@@ -32,6 +32,7 @@ class EmitTypescriptContext:
32
32
  namespace: builder.SpecNamespace
33
33
  namespaces: set[builder.SpecNamespace] = field(default_factory=set)
34
34
  cross_output_paths: CrossOutputPaths | None = None
35
+ api_endpoints: dict[builder.EndpointKey, builder.APIEndpointInfo]
35
36
 
36
37
 
37
38
  def ts_type_name(name: str) -> str:
@@ -80,6 +81,12 @@ def emit_value_ts(
80
81
  if stype.defn_type.is_base_type(builder.BaseTypeName.s_dict):
81
82
  key_type = stype.parameters[0]
82
83
  value_type = stype.parameters[1]
84
+
85
+ if not key_type.is_base_type(
86
+ builder.BaseTypeName.s_string
87
+ ) and not isinstance(key_type, builder.SpecTypeDefnStringEnum):
88
+ raise Exception("invalid dict keys -- dict keys must be string or enum")
89
+
83
90
  return (
84
91
  "{\n\t"
85
92
  + ",\n\t".join(
@@ -106,6 +113,14 @@ def emit_value_ts(
106
113
  raise Exception("invalid constant type", value, stype, type(stype))
107
114
 
108
115
 
116
+ def emit_constant_ts(ctx: EmitTypescriptContext, sconst: builder.SpecConstant) -> None:
117
+ ctx.out.write("\n\n")
118
+ ctx.out.write(MODIFY_NOTICE)
119
+ value = emit_value_ts(ctx, sconst.value_type, sconst.value)
120
+ const_name = sconst.name.upper()
121
+ ctx.out.write(f"export const {const_name} = {value}\n")
122
+
123
+
109
124
  def emit_type_ts(ctx: EmitTypescriptContext, stype: builder.SpecType) -> None:
110
125
  if not isinstance(stype, builder.SpecTypeDefn):
111
126
  return
@@ -118,6 +133,7 @@ def emit_type_ts(ctx: EmitTypescriptContext, stype: builder.SpecType) -> None:
118
133
 
119
134
  if isinstance(stype, builder.SpecTypeDefnExternal):
120
135
  assert not stype.is_exported, "expecting private names"
136
+ ctx.out.write("\n")
121
137
  ctx.out.write(stype.external_map["ts"])
122
138
  ctx.out.write("\n")
123
139
  return
@@ -46,7 +46,7 @@ def load_types(config: Config) -> SpecBuilder | None:
46
46
  cross_output_paths = CrossOutputPaths(
47
47
  python_types_output=config.python.types_output,
48
48
  typescript_types_output=config.typescript.types_output,
49
- typescript_routes_output=config.typescript.routes_output,
49
+ typescript_routes_output_by_endpoint=config.typescript.endpoint_to_routes_output,
50
50
  typespec_files_input=config.type_spec_types,
51
51
  # IMPROVE not sure how to know which one is the correct one in emit_typescript
52
52
  )
@@ -223,13 +223,28 @@ class OpenAPIUnionType(OpenAPIType):
223
223
  base_types: list[OpenAPIType],
224
224
  description: str | None = None,
225
225
  nullable: bool = False,
226
+ discriminator: str | None = None,
227
+ discriminator_map: dict[str, OpenAPIRefType] | None = None,
226
228
  ) -> None:
227
229
  self.base_types = base_types
230
+ self._discriminator = discriminator
231
+ self._discriminator_map = discriminator_map
228
232
  super().__init__(description=description, nullable=nullable)
229
233
 
230
234
  def asdict(self) -> dict[str, object]:
231
235
  # TODO: use parents description and nullable
232
- return {"oneOf": [base_type.asdict() for base_type in self.base_types]}
236
+ return {
237
+ "oneOf": [base_type.asdict() for base_type in self.base_types],
238
+ "discriminator": {
239
+ "propertyName": self._discriminator,
240
+ "mapping": {
241
+ discriminator_value: base_type.source
242
+ for discriminator_value, base_type in self._discriminator_map.items()
243
+ },
244
+ }
245
+ if self._discriminator is not None and self._discriminator_map is not None
246
+ else None,
247
+ }
233
248
 
234
249
 
235
250
  class OpenAPIIntersectionType(OpenAPIType):
@@ -3,6 +3,7 @@
3
3
  // doesn't allow referring explicitly to global names (thus cannot override here)
4
4
  // IMPROVE: invert relationship for global.d.ts looks here instead
5
5
  import * as IO from 'io-ts';
6
+
6
7
  type localJsonScalar = JsonScalar
7
8
  type localJsonValue = JsonValue
8
9
  type localObjectId = ObjectId
@@ -282,6 +282,7 @@ def generate_entry_actions_typescript(
282
282
  ctx = emit_typescript_util.EmitTypescriptContext(
283
283
  out=definition_buffer,
284
284
  namespace=index_namespace,
285
+ api_endpoints={},
285
286
  )
286
287
  builder.namespaces[index_namespace.name] = index_namespace
287
288
 
@@ -63,6 +63,7 @@ from . import identifier_t as identifier_t
63
63
  from . import input_attributes_t as input_attributes_t
64
64
  from . import inputs_t as inputs_t
65
65
  from . import integration_server_t as integration_server_t
66
+ from . import integrations_t as integrations_t
66
67
  from .api.uploader import invoke_uploader as invoke_uploader_t
67
68
  from . import job_definition_t as job_definition_t
68
69
  from .api.entity import list_entities as list_entities_t
@@ -76,6 +77,7 @@ from . import overrides_t as overrides_t
76
77
  from . import permissions_t as permissions_t
77
78
  from . import phases_t as phases_t
78
79
  from . import post_base_t as post_base_t
80
+ from .api.integrations import publish_realtime_data as publish_realtime_data_t
79
81
  from . import queued_job_t as queued_job_t
80
82
  from . import recipe_identifiers_t as recipe_identifiers_t
81
83
  from . import recipe_inputs_t as recipe_inputs_t
@@ -180,6 +182,7 @@ __all__: list[str] = [
180
182
  "input_attributes_t",
181
183
  "inputs_t",
182
184
  "integration_server_t",
185
+ "integrations_t",
183
186
  "invoke_uploader_t",
184
187
  "job_definition_t",
185
188
  "list_entities_t",
@@ -193,6 +196,7 @@ __all__: list[str] = [
193
196
  "permissions_t",
194
197
  "phases_t",
195
198
  "post_base_t",
199
+ "publish_realtime_data_t",
196
200
  "queued_job_t",
197
201
  "recipe_identifiers_t",
198
202
  "recipe_inputs_t",
@@ -35,6 +35,7 @@ class Arguments:
35
35
  definition_key: identifier_t.IdentifierKey
36
36
  field_values: list[field_values_t.FieldArgumentValue]
37
37
  entity_key: identifier_t.IdentifierKey | None = None
38
+ on_create_init_field_values: list[field_values_t.FieldArgumentValue] | None = None
38
39
 
39
40
 
40
41
  # DO NOT MODIFY -- This file is generated by type_spec
@@ -0,0 +1 @@
1
+ # DO NOT MODIFY -- This file is generated by type_spec
@@ -0,0 +1,41 @@
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
+ import dataclasses
10
+ from pkgs.serialization import serial_class
11
+ from ... import base_t
12
+ from ... import integrations_t
13
+
14
+ __all__: list[str] = [
15
+ "Arguments",
16
+ "Data",
17
+ "ENDPOINT_METHOD",
18
+ "ENDPOINT_PATH",
19
+ ]
20
+
21
+ ENDPOINT_METHOD = "POST"
22
+ ENDPOINT_PATH = "api/external/integrations/publish_realtime_data"
23
+
24
+
25
+ # DO NOT MODIFY -- This file is generated by type_spec
26
+ @serial_class(
27
+ named_type_path="sdk.api.integrations.publish_realtime_data.Arguments",
28
+ )
29
+ @dataclasses.dataclass(slots=base_t.ENABLE_SLOTS, kw_only=True) # type: ignore[literal-required]
30
+ class Arguments:
31
+ data_package: integrations_t.DataPackage
32
+
33
+
34
+ # DO NOT MODIFY -- This file is generated by type_spec
35
+ @serial_class(
36
+ named_type_path="sdk.api.integrations.publish_realtime_data.Data",
37
+ )
38
+ @dataclasses.dataclass(slots=base_t.ENABLE_SLOTS, kw_only=True) # type: ignore[literal-required]
39
+ class Data:
40
+ pass
41
+ # DO NOT MODIFY -- This file is generated by type_spec
@@ -258,10 +258,12 @@ class AsyncBatchProcessorBase(ABC):
258
258
  definition_key: identifier_t.IdentifierKey,
259
259
  field_values: list[field_values_t.FieldArgumentValue],
260
260
  entity_key: identifier_t.IdentifierKey | None = None,
261
+ on_create_init_field_values: list[field_values_t.FieldArgumentValue] | None = None,
261
262
  depends_on: list[str] | None = None,
262
263
  ) -> async_batch_t.QueuedAsyncBatchRequest:
263
264
  """Creates or updates field values for an entity
264
265
 
266
+ :param on_create_init_field_values: Field values set only when the entity is created (will be ignored if entity already exists)
265
267
  :param depends_on: A list of batch reference keys to process before processing this request
266
268
  """
267
269
  args = create_or_update_entity_t.Arguments(
@@ -269,6 +271,7 @@ class AsyncBatchProcessorBase(ABC):
269
271
  entity_type=entity_type,
270
272
  definition_key=definition_key,
271
273
  field_values=field_values,
274
+ on_create_init_field_values=on_create_init_field_values,
272
275
  )
273
276
  json_data = serialize_for_api(args)
274
277
 
@@ -412,15 +412,18 @@ class ClientMethods(ABC):
412
412
  definition_key: identifier_t.IdentifierKey,
413
413
  field_values: list[field_values_t.FieldArgumentValue],
414
414
  entity_key: identifier_t.IdentifierKey | None = None,
415
+ on_create_init_field_values: list[field_values_t.FieldArgumentValue] | None = None,
415
416
  ) -> create_or_update_entity_t.Data:
416
417
  """Creates or updates field values for an entity
417
418
 
419
+ :param on_create_init_field_values: Field values set only when the entity is created (will be ignored if entity already exists)
418
420
  """
419
421
  args = create_or_update_entity_t.Arguments(
420
422
  entity_key=entity_key,
421
423
  entity_type=entity_type,
422
424
  definition_key=definition_key,
423
425
  field_values=field_values,
426
+ on_create_init_field_values=on_create_init_field_values,
424
427
  )
425
428
  api_request = APIRequest(
426
429
  method=create_or_update_entity_t.ENDPOINT_METHOD,
@@ -42,6 +42,7 @@ __all__: list[str] = [
42
42
  "condition_match": "Condition Match",
43
43
  "condition_parameter": "Condition Parameter",
44
44
  "condition_parameter_mat_family": "Condition Parameter Material Family",
45
+ "condition_parameter_matches": "Condition Parameter Matches",
45
46
  "condition_parameter_rule": "Condition Parameter Rule",
46
47
  "condition_parameter_value": "Condition Parameter Value",
47
48
  "constraint": "Constraint",
@@ -110,6 +111,7 @@ __all__: list[str] = [
110
111
  "output_calculation_entity_mapping": "Output Calculation Entity Mapping",
111
112
  "output_category_all": "Output Category",
112
113
  "output_condition": "Output Condition",
114
+ "output_condition_condition_parameter": "Output Condition Condition Parameter",
113
115
  "output_default_condition": "Output Default Condition",
114
116
  "output_condition_parameter": "Output Condition Parameter",
115
117
  "output_condition_parameter_analytical_method_category": "Output Condition Parameter Analytical Method Category",
@@ -138,6 +140,7 @@ __all__: list[str] = [
138
140
  "recipe_ingredient_actual": "Recipe Ingredient Actual",
139
141
  "recipe_ingredients_compounded": "Recipe Ingredients Compounded",
140
142
  "recipe_ingredients_compounded_calculation": "Recipe Ingredients Compounded Calculation",
143
+ "recipe_link": "Experiment Link",
141
144
  "recipe_output": "Recipe Output",
142
145
  "recipe_output_annotation": "Recipe Output Annotation",
143
146
  "recipe_output_metadata": "Recipe Output Metadata",
@@ -224,6 +227,7 @@ class EntityType(StrEnum):
224
227
  CONDITION_MATCH = "condition_match"
225
228
  CONDITION_PARAMETER = "condition_parameter"
226
229
  CONDITION_PARAMETER_MAT_FAMILY = "condition_parameter_mat_family"
230
+ CONDITION_PARAMETER_MATCHES = "condition_parameter_matches"
227
231
  CONDITION_PARAMETER_RULE = "condition_parameter_rule"
228
232
  CONDITION_PARAMETER_VALUE = "condition_parameter_value"
229
233
  CONSTRAINT = "constraint"
@@ -292,6 +296,7 @@ class EntityType(StrEnum):
292
296
  OUTPUT_CALCULATION_ENTITY_MAPPING = "output_calculation_entity_mapping"
293
297
  OUTPUT_CATEGORY_ALL = "output_category_all"
294
298
  OUTPUT_CONDITION = "output_condition"
299
+ OUTPUT_CONDITION_CONDITION_PARAMETER = "output_condition_condition_parameter"
295
300
  OUTPUT_DEFAULT_CONDITION = "output_default_condition"
296
301
  OUTPUT_CONDITION_PARAMETER = "output_condition_parameter"
297
302
  OUTPUT_CONDITION_PARAMETER_ANALYTICAL_METHOD_CATEGORY = "output_condition_parameter_analytical_method_category"
@@ -320,6 +325,7 @@ class EntityType(StrEnum):
320
325
  RECIPE_INGREDIENT_ACTUAL = "recipe_ingredient_actual"
321
326
  RECIPE_INGREDIENTS_COMPOUNDED = "recipe_ingredients_compounded"
322
327
  RECIPE_INGREDIENTS_COMPOUNDED_CALCULATION = "recipe_ingredients_compounded_calculation"
328
+ RECIPE_LINK = "recipe_link"
323
329
  RECIPE_OUTPUT = "recipe_output"
324
330
  RECIPE_OUTPUT_ANNOTATION = "recipe_output_annotation"
325
331
  RECIPE_OUTPUT_METADATA = "recipe_output_metadata"
@@ -0,0 +1,10 @@
1
+ # ruff: noqa: E402 Q003
2
+ # fmt: off
3
+ # isort: skip_file
4
+ # DO NOT MODIFY -- This file is generated by type_spec
5
+ # Kept only for SDK backwards compatibility
6
+ from .integrations_t import DataPackageType as DataPackageType
7
+ from .integrations_t import DataPackageBase as DataPackageBase
8
+ from .integrations_t import DataPackageNumericReading as DataPackageNumericReading
9
+ from .integrations_t import DataPackage as DataPackage
10
+ # DO NOT MODIFY -- This file is generated by type_spec
@@ -0,0 +1,62 @@
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 base_t
14
+ from . import entity_t
15
+
16
+ __all__: list[str] = [
17
+ "DataPackage",
18
+ "DataPackageBase",
19
+ "DataPackageNumericReading",
20
+ "DataPackageType",
21
+ ]
22
+
23
+
24
+ # DO NOT MODIFY -- This file is generated by type_spec
25
+ class DataPackageType(StrEnum):
26
+ NUMERIC_READING = "numeric_reading"
27
+
28
+
29
+ # DO NOT MODIFY -- This file is generated by type_spec
30
+ @serial_class(
31
+ named_type_path="sdk.integrations.DataPackageBase",
32
+ )
33
+ @dataclasses.dataclass(slots=base_t.ENABLE_SLOTS, kw_only=True) # type: ignore[literal-required]
34
+ class DataPackageBase:
35
+ type: DataPackageType
36
+
37
+
38
+ # DO NOT MODIFY -- This file is generated by type_spec
39
+ @serial_class(
40
+ named_type_path="sdk.integrations.DataPackageNumericReading",
41
+ to_string_values={"value"},
42
+ parse_require={"type"},
43
+ )
44
+ @dataclasses.dataclass(slots=base_t.ENABLE_SLOTS, kw_only=True) # type: ignore[literal-required]
45
+ class DataPackageNumericReading(DataPackageBase):
46
+ type: typing.Literal[DataPackageType.NUMERIC_READING] = DataPackageType.NUMERIC_READING
47
+ value: Decimal
48
+ target_entity: entity_t.EntityIdentifier
49
+
50
+
51
+ # DO NOT MODIFY -- This file is generated by type_spec
52
+ DataPackage = typing.Annotated[
53
+ typing.Union[DataPackageNumericReading],
54
+ serial_union_annotation(
55
+ named_type_path="sdk.integrations.DataPackage",
56
+ discriminator="type",
57
+ discriminator_map={
58
+ "numeric_reading": DataPackageNumericReading,
59
+ },
60
+ ),
61
+ ]
62
+ # DO NOT MODIFY -- This file is generated by type_spec
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: UncountablePythonSDK
3
- Version: 0.0.115
3
+ Version: 0.0.117
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
@@ -26,10 +26,11 @@ examples/upload_files.py,sha256=qMaSvMSdTMPOOP55y1AwEurc0SOdZAMvEydlqJPsGpg,432
26
26
  examples/integration-server/pyproject.toml,sha256=i4Px7I__asDvP4WlAd2PncfRRQ-U4t5xp0tqT9YYs3s,9149
27
27
  examples/integration-server/jobs/materials_auto/concurrent_cron.py,sha256=xsK3H9ZEaniedC2nJUB0rqOcFI8y-ojfl_nLSJb9AMM,312
28
28
  examples/integration-server/jobs/materials_auto/example_cron.py,sha256=7VVQ-UJsq3DbGpN3XPnorRVZYo-vCwbfSU3VVDluIzA,699
29
- examples/integration-server/jobs/materials_auto/example_http.py,sha256=h97_IBC5EQiAUA4A5xyPpgFIqPTZWKIhMpmVAgVZEBE,941
29
+ examples/integration-server/jobs/materials_auto/example_http.py,sha256=eVq-Fss_AhmztxOMqqO-GYGF3KvPt1O5HbNwwC2arh8,1037
30
+ examples/integration-server/jobs/materials_auto/example_instrument.py,sha256=6qq6a8S5soKC-ypVswZEhkLzB_mxd4dxCSIoXKrbGDs,1261
30
31
  examples/integration-server/jobs/materials_auto/example_runsheet_wh.py,sha256=_wILTnbzzLf9zrcQb_KQKytxxcya1ej6MqQnoUSS4fA,1180
31
32
  examples/integration-server/jobs/materials_auto/example_wh.py,sha256=PN-skP27yJwDZboWk5g5EZEc3AKfVayQLfnopjsDKJc,659
32
- examples/integration-server/jobs/materials_auto/profile.yaml,sha256=MNKRn09iaiNF8dxOZ7Y6558UW6aCZ-9l9hQAwzYN8zs,1685
33
+ examples/integration-server/jobs/materials_auto/profile.yaml,sha256=btUdn8hStM6Zp4zr0kBI2lL461MqDdzfSuF4LvuSQ8k,1926
33
34
  pkgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
35
  pkgs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
36
  pkgs/argument_parser/__init__.py,sha256=VWUOOtJ-ueRF2lkIJzgQe4xhBKR9IPkgf9vY28nF35s,870
@@ -63,29 +64,29 @@ pkgs/strenum_compat/__init__.py,sha256=wXRFeNvBm8RU6dy1PFJ5sRLgUIEeH_DVR95Sv5qpG
63
64
  pkgs/strenum_compat/strenum_compat.py,sha256=uOUAgpYTjHs1MX8dG81jRlyTkt3KNbkV_25zp7xTX2s,36
64
65
  pkgs/type_spec/__init__.py,sha256=h5DmJTca4QVV10sZR1x0-MlkZfuGYDfapR3zHvXfzto,19
65
66
  pkgs/type_spec/__main__.py,sha256=5bJaX9Y_-FavP0qwzhk-z-V97UY7uaezJTa1zhO_HHQ,1048
66
- pkgs/type_spec/builder.py,sha256=XqkKO-GxV2DLS_iDN9tnQJfNcefz5yMAwyjnMog87jE,54049
67
- pkgs/type_spec/config.py,sha256=K6WebgeI3Saew0IEBcm1s2fauw_CyvH183emVrNoUXg,5327
68
- pkgs/type_spec/cross_output_links.py,sha256=bVNn0a4LMVTRLg_zjtiHnoTwdINHfftjWoH6tGdxhlk,3124
67
+ pkgs/type_spec/builder.py,sha256=17_hkJI5jDb_IGOHZCiZDJ8_er-amVH0-WRpqJb7hEE,54365
68
+ pkgs/type_spec/config.py,sha256=m0Rky7Rg2jMglDPQChF30p5h5P86Ap1GObwzLzmypNE,5829
69
+ pkgs/type_spec/cross_output_links.py,sha256=ttFNfuQmR3sNnPSeUER5IPgLiYc-FB5gjlf7RyFYMpc,3293
69
70
  pkgs/type_spec/emit_io_ts.py,sha256=CUvBs0boB_X-Kndh66yYcqFfq3oC_LGs8YffLkJ0ZXA,5707
70
- pkgs/type_spec/emit_open_api.py,sha256=B9cGFR7TU90_yky-HdNq1sDIJOuP3eU4o3j7nNgIpkU,26047
71
- pkgs/type_spec/emit_open_api_util.py,sha256=xnc4ymNzEyAS1Q2cV6Ma9Y6mQ1MbPPi2WaHKTSFETr8,2346
72
- pkgs/type_spec/emit_python.py,sha256=JCT_o6sPJRaAL1J-TlMT-RN8d4bzLcYJF2iwNTcGlBA,52651
73
- pkgs/type_spec/emit_typescript.py,sha256=0HRzxlbIP91rzbVkAntF4TKZppoKcWsqnDLAIRc1bng,10927
74
- pkgs/type_spec/emit_typescript_util.py,sha256=pYhzRb-U-B5peWdfJDQ0i9kI80Ojf2wbfkvJutk9rTw,10975
75
- pkgs/type_spec/load_types.py,sha256=GndEKQtICCQi4oXsL6cZ9khm8lBB830e6hx0wML4dHs,4278
71
+ pkgs/type_spec/emit_open_api.py,sha256=-2mNpucrEbWNXGZ2xAlqfouPPDP_yOkZVC_ycWpC8UE,26922
72
+ pkgs/type_spec/emit_open_api_util.py,sha256=bTmRvrGP82-eB75hwf9ySI7pDEC87FNQTF18VKEWSXY,2367
73
+ pkgs/type_spec/emit_python.py,sha256=aURsc-wWdamVDCrIWxA7s8_MLAMjLdXZor6ykkibzXY,52707
74
+ pkgs/type_spec/emit_typescript.py,sha256=FINir79bz4tJYgJuUylNJFvqChzaFlHNCfZ5D7A6B1I,11447
75
+ pkgs/type_spec/emit_typescript_util.py,sha256=ChP4oF2ZJoL0qErGCL0nscj0w_yH6TBgE92MtQS8nPI,11638
76
+ pkgs/type_spec/load_types.py,sha256=JL7tX2H_cy3p5HjGuvNFJlY4pDSbDVnYFsh9mK16-ic,4302
76
77
  pkgs/type_spec/non_discriminated_union_exceptions.py,sha256=JB4WNDJWc3e9WMOabX4aTd0-6K7n1hctIW2lGf1bYts,612
77
- pkgs/type_spec/open_api_util.py,sha256=DYnlygaMIqDQtSuYpUpd5lpA9JG4JHd_-iGe-BY2lhw,7333
78
+ pkgs/type_spec/open_api_util.py,sha256=r4rryE95valu4kmHaMAbXQ5PMwbUnnwYv4Gh6_CwiSU,7966
78
79
  pkgs/type_spec/test.py,sha256=4ueujBq-pEgnX3Z69HyPmD-bullFXmpixcpVzfOkhP4,489
79
80
  pkgs/type_spec/util.py,sha256=S_SGTJU192x-wIbngVUTvXhQENMbBfxluigLmnItGI8,4848
80
81
  pkgs/type_spec/actions_registry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
82
  pkgs/type_spec/actions_registry/__main__.py,sha256=SRw6kIhHTW7W2wGijYq66JARzoc4KpPmbLqwvnETyTE,4277
82
83
  pkgs/type_spec/actions_registry/emit_typescript.py,sha256=W1lI36ITdJ7MBf37wlTB7H3X9Ljt217vIGMv4e3fxfY,5986
83
84
  pkgs/type_spec/parts/base.py.prepart,sha256=Xy8my5ol_Iu0hpQpvgsmqGLkGcMsLSg-cgjm4Yp-QI4,2369
84
- pkgs/type_spec/parts/base.ts.prepart,sha256=2FJJvpg2olCcavxj0nbYWdwKl6KeScour2JjSvN42l8,1001
85
+ pkgs/type_spec/parts/base.ts.prepart,sha256=42-1_N_K04t4c6pE62V4wBw3bR5bgPxhmXUk__A7gAs,1002
85
86
  pkgs/type_spec/type_info/__main__.py,sha256=TLNvCHGcmaj_8Sj5bAQNpuNaaw2dpDzoFDWZds0V4Qo,1002
86
87
  pkgs/type_spec/type_info/emit_type_info.py,sha256=xRjZiwDDii4Bq8yVfcgE8YFechoKAcGmYXBk3Dq-K-s,15387
87
88
  pkgs/type_spec/ui_entry_actions/__init__.py,sha256=WiHE_BexOEZWbkkbD7EnFau1aMLNmfgQywG9PTQNCkw,135
88
- pkgs/type_spec/ui_entry_actions/generate_ui_entry_actions.py,sha256=IPBdVfJReLLGVaZOkb0QYGGrm73JqMXuAGSbeoBVbDg,9477
89
+ pkgs/type_spec/ui_entry_actions/generate_ui_entry_actions.py,sha256=65qUEp9zVcAsHEe3QjOTlPfLf45kH980fOXZXKNmOC8,9503
89
90
  pkgs/type_spec/value_spec/__init__.py,sha256=Z-grlcZtxAfEXhPHsK0nD7PFLGsv4eqvunaPN7_TA84,83
90
91
  pkgs/type_spec/value_spec/__main__.py,sha256=oM5lcV6Hv_03okjtfWn2fzSHsarFVa9ArU_g02XnQJw,8879
91
92
  pkgs/type_spec/value_spec/convert_type.py,sha256=OvP7dwUMHXNHVXWYT4jkaYJ96S3a2SnFuC_iMdYVB7s,2927
@@ -139,9 +140,9 @@ uncountable/integration/queue_runner/datastore/model.py,sha256=8-RI5A2yPZVGBLWIN
139
140
  uncountable/integration/secret_retrieval/__init__.py,sha256=3QXVj35w8rRMxVvmmsViFYDi3lcb3g70incfalOEm6o,87
140
141
  uncountable/integration/secret_retrieval/retrieve_secret.py,sha256=LBEf18KHtXZxg-ZZ80stJ1vW39AWf0CQllP6pNu3Eq8,2994
141
142
  uncountable/integration/webhook_server/entrypoint.py,sha256=NQawXl_JCRojdVniS5RF7dobQQKW_Wy03bwy-uXknuA,3441
142
- uncountable/types/__init__.py,sha256=eqeDMCXTr9Mwo10RSGMa4znfu_7TVp_0gYJxPKmLCfQ,9990
143
+ uncountable/types/__init__.py,sha256=EZC_LgnO80PfE3SW4Sg95Ew3iUORmT_7oz-6u4zTulY,10169
143
144
  uncountable/types/async_batch.py,sha256=yCCWrrLQfxXVqZp-KskxLBNkNmuELdz4PJjx8ULppgs,662
144
- uncountable/types/async_batch_processor.py,sha256=Y8rp-GtuhwUBg18yb5T7bQpb48vsQpACGrvaIiFYLrU,21765
145
+ uncountable/types/async_batch_processor.py,sha256=h_8Snzt3lbEFlZAZFByt4Hg4dv2YlxMijHjTHjZ0aXY,22062
145
146
  uncountable/types/async_batch_t.py,sha256=JuswurXlYW38MfAXJ0UWb7hE2rmzFaHBAsNhRYAyMD4,3779
146
147
  uncountable/types/async_jobs.py,sha256=JI0ScfawaqMRbJ2jbgW3YQLhijPnBeYdMnZJjygSxHg,322
147
148
  uncountable/types/async_jobs_t.py,sha256=u4xd3i512PZ-9592Q2ZgWh_faMiI4UMm0F_gOmZnerI,1389
@@ -153,7 +154,7 @@ uncountable/types/calculations.py,sha256=fApOFpgBemt_t7IVneVR0VdI3X5EOxiG6Xhzr6R
153
154
  uncountable/types/calculations_t.py,sha256=pl-lhjyDQuj11Sf9g1-0BsSkN7Ez8UxDp8-KMQ_3enM,709
154
155
  uncountable/types/chemical_structure.py,sha256=ujyragaD26-QG5jgKnWhO7TN3N1V9b_04T2WhqNYxxo,281
155
156
  uncountable/types/chemical_structure_t.py,sha256=VFFyits_vx4t5L2euu_qFiSpsGJjURkDPr3ISnr3nPc,855
156
- uncountable/types/client_base.py,sha256=m4AI0E4ET-FFmewHicW1t2oayraIhS_VYMuyTCnOk1g,76085
157
+ uncountable/types/client_base.py,sha256=Qd8kxA0C76FKTAuoJOVjEw48mInfV_IXH2CBBTyYwAs,76382
157
158
  uncountable/types/client_config.py,sha256=qLpHt4O_B098CyN6qQajoxZ2zjZ1DILXLUEGyyGP0TQ,280
158
159
  uncountable/types/client_config_t.py,sha256=yTFIYAitMrcc4oV9J-HADODS_Hwi45z-piz7rr7QT04,781
159
160
  uncountable/types/curves.py,sha256=QyEyC20jsG-LGKVx6miiF-w70vKMwNkILFBDIJ5Ok9g,345
@@ -161,7 +162,7 @@ uncountable/types/curves_t.py,sha256=DxYepdC3QKKR7mepOOBoyarNcFZQdUa5ZYH-hwCY3BI
161
162
  uncountable/types/data.py,sha256=u2isf4XEug3Eu-xSIoqGaCQmW2dFaKBHCkP_WKYwwBc,500
162
163
  uncountable/types/data_t.py,sha256=vFoypK_WMGfN28r1sSlDYHZNUdBQC0XCN7-_Mlo4FJk,2832
163
164
  uncountable/types/entity.py,sha256=Zclk1LYcRaYrMDhqyCjMSLEg0fE6_q8LHvV22Qvscgs,566
164
- uncountable/types/entity_t.py,sha256=I_dJ08Wd7NsVFkZx3p9-VAARx8nCkLJCtY--hv0zR8c,20288
165
+ uncountable/types/entity_t.py,sha256=Q-Ji3IMpQxXoY680ZOYz5Zkcy_wrz3lgQOnKqoU9noA,20666
165
166
  uncountable/types/experiment_groups.py,sha256=qUpFOx1AKgzaT_4khCOv5Xs6jwiQGbvHH-GUh3v1nv4,288
166
167
  uncountable/types/experiment_groups_t.py,sha256=29Ct-WPejpYMuGfnFfOoosU9iSfjzxpabpBX6oTPFUA,761
167
168
  uncountable/types/exports.py,sha256=VMmxUO2PpV1Y63hZ2AnVor4H-B6aswJ7YpSru_u89lU,334
@@ -182,6 +183,8 @@ uncountable/types/inputs.py,sha256=3ghg39_oiLF5HqWF_wNwYv4HMR1lrKLfeRLn5ptIGw4,4
182
183
  uncountable/types/inputs_t.py,sha256=eSVA7LNgLI3ja83GJm4sA9KhPICVV4zj2Dd4OhbuY9g,2158
183
184
  uncountable/types/integration_server.py,sha256=VonA8h8TGnVBiss5W8-K82lA01JQa7TLk0ubFo8iiBQ,364
184
185
  uncountable/types/integration_server_t.py,sha256=pgtoyuW6QvGRawidJZFB-WnOdwCE4OIoJAvGfussZKU,1304
186
+ uncountable/types/integrations.py,sha256=0fOhtbLIOl9w1GP9J3PTagRU8mjOKV48JNLLH3SJQP0,472
187
+ uncountable/types/integrations_t.py,sha256=ihyhuMDKtJarQ19OppS0fYpJUYd8o5-w6YCDE440O-w,1871
185
188
  uncountable/types/job_definition.py,sha256=hYp5jPYLLYm3NKEqzQrQfXL0Ms5KgEQGTON13YWSPYk,1804
186
189
  uncountable/types/job_definition_t.py,sha256=E4IQvcYF3VDHbwRlvopy8y-HNAyEMZpwy7jkmp74fgQ,9563
187
190
  uncountable/types/outputs.py,sha256=I6zP2WHXg_jXgMqmuEJuJOlsjKjQGHjfs1JOwW9YxBM,260
@@ -235,7 +238,7 @@ uncountable/types/api/condition_parameters/upsert_condition_match.py,sha256=7I9l
235
238
  uncountable/types/api/entity/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
236
239
  uncountable/types/api/entity/create_entities.py,sha256=cCDEra2SHvGWvz7nIxxMDSQN6OWrHMTT0JSomWUesto,1794
237
240
  uncountable/types/api/entity/create_entity.py,sha256=urT6C7iGAa7_rCv9Wcz6GM_lKg1tP55E__rjNkj-Rjc,1879
238
- uncountable/types/api/entity/create_or_update_entity.py,sha256=OK05B9nqlxsCU41iVCI_Nn66qVeQfqgdf2oZn7kUPFM,1445
241
+ uncountable/types/api/entity/create_or_update_entity.py,sha256=cxjJIcZTKOg8Y5kGzctYKayfnr8BNZDOM5YfI4dBSf0,1532
239
242
  uncountable/types/api/entity/export_entities.py,sha256=zz_4P6bQAt7gU2o2to9zUh0HHLQKaxLkbFGfbgY3KVk,1395
240
243
  uncountable/types/api/entity/get_entities_data.py,sha256=hu0UfkU4PTyv3_CBZ7YmR8L8BKMq8hx6zH43XtUm16E,1616
241
244
  uncountable/types/api/entity/grant_entity_permissions.py,sha256=4CvVIMvpdok8K1Bh6wMlwuUmoeP_-nL9y2GCEM6uAhY,1536
@@ -267,6 +270,8 @@ uncountable/types/api/inputs/set_input_attribute_values.py,sha256=Tgd3KofNXYBj2u
267
270
  uncountable/types/api/inputs/set_input_category.py,sha256=6e1L0RQU6nt51PTZ99Ca4ZfIxIcpa0pXBDCwRFxTKPs,1283
268
271
  uncountable/types/api/inputs/set_input_subcategories.py,sha256=w5U6eXes5KquPW1UcYPRHimrfY_cN-K93IrpOw1Gl7s,1320
269
272
  uncountable/types/api/inputs/set_intermediate_type.py,sha256=S1RLI2RtrRze0NdMUfK2nwR4Twn_DnLnWNsg0-ivi_A,1431
273
+ uncountable/types/api/integrations/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
274
+ uncountable/types/api/integrations/publish_realtime_data.py,sha256=-5r2U78AwKUCpModcUIchVIZ9b7L-Ln6O6T-9d57M2A,1181
270
275
  uncountable/types/api/material_families/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
271
276
  uncountable/types/api/material_families/update_entity_material_families.py,sha256=qWJgAKH0MayadXvxckePCdo9yd34QXOmGZ7cKz5VLNo,1761
272
277
  uncountable/types/api/outputs/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
@@ -318,7 +323,7 @@ uncountable/types/api/triggers/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr
318
323
  uncountable/types/api/triggers/run_trigger.py,sha256=dgDX_sRWSJ36UuzMZhG25oHV1HIOUKYY2G3fjKugZrw,1204
319
324
  uncountable/types/api/uploader/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
320
325
  uncountable/types/api/uploader/invoke_uploader.py,sha256=Bj7Dq4A90k00suacwk3bLA_dCb2aovS1kAbVam2AQnM,1395
321
- uncountablepythonsdk-0.0.115.dist-info/METADATA,sha256=-CVaJt1Lh0NBrGUWB8G7L3jEsniPCdVd_1un50_IUQo,2143
322
- uncountablepythonsdk-0.0.115.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
323
- uncountablepythonsdk-0.0.115.dist-info/top_level.txt,sha256=1UVGjAU-6hJY9qw2iJ7nCBeEwZ793AEN5ZfKX9A1uj4,31
324
- uncountablepythonsdk-0.0.115.dist-info/RECORD,,
326
+ uncountablepythonsdk-0.0.117.dist-info/METADATA,sha256=tj3OqRKTNxJzMEkVnW6fwy_8S1OUT-00sE7vV4W9lag,2143
327
+ uncountablepythonsdk-0.0.117.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
328
+ uncountablepythonsdk-0.0.117.dist-info/top_level.txt,sha256=1UVGjAU-6hJY9qw2iJ7nCBeEwZ793AEN5ZfKX9A1uj4,31
329
+ uncountablepythonsdk-0.0.117.dist-info/RECORD,,