UncountablePythonSDK 0.0.20__py3-none-any.whl → 0.0.21__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of UncountablePythonSDK might be problematic. Click here for more details.
- {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.21.dist-info}/METADATA +3 -1
- {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.21.dist-info}/RECORD +50 -31
- examples/upload_files.py +19 -0
- pkgs/type_spec/actions_registry/__main__.py +35 -23
- pkgs/type_spec/actions_registry/emit_typescript.py +71 -9
- pkgs/type_spec/builder.py +13 -0
- pkgs/type_spec/config.py +1 -0
- pkgs/type_spec/emit_open_api.py +11 -0
- pkgs/type_spec/emit_open_api_util.py +1 -0
- pkgs/type_spec/emit_python.py +241 -55
- pkgs/type_spec/type_info/emit_type_info.py +129 -8
- type_spec/external/api/entity/create_entities.yaml +12 -1
- type_spec/external/api/entity/create_entity.yaml +12 -1
- type_spec/external/api/entity/transition_entity_phase.yaml +44 -0
- type_spec/external/api/permissions/set_core_permissions.yaml +69 -0
- type_spec/external/api/recipes/associate_recipe_as_input.yaml +4 -4
- type_spec/external/api/recipes/create_recipe.yaml +2 -1
- type_spec/external/api/recipes/disassociate_recipe_as_input.yaml +16 -0
- type_spec/external/api/recipes/edit_recipe_inputs.yaml +88 -0
- type_spec/external/api/recipes/get_curve.yaml +4 -1
- type_spec/external/api/recipes/get_recipes_data.yaml +6 -0
- type_spec/external/api/recipes/set_recipe_metadata.yaml +1 -0
- type_spec/external/api/recipes/set_recipe_tags.yaml +62 -0
- uncountable/core/__init__.py +2 -1
- uncountable/core/client.py +11 -9
- uncountable/core/file_upload.py +95 -0
- uncountable/core/types.py +22 -0
- uncountable/types/__init__.py +18 -0
- uncountable/types/api/entity/create_entities.py +1 -1
- uncountable/types/api/entity/create_entity.py +1 -1
- uncountable/types/api/entity/transition_entity_phase.py +66 -0
- uncountable/types/api/permissions/__init__.py +1 -0
- uncountable/types/api/permissions/set_core_permissions.py +89 -0
- uncountable/types/api/recipes/associate_recipe_as_input.py +4 -3
- uncountable/types/api/recipes/create_recipe.py +1 -1
- uncountable/types/api/recipes/disassociate_recipe_as_input.py +35 -0
- uncountable/types/api/recipes/edit_recipe_inputs.py +107 -0
- uncountable/types/api/recipes/get_curve.py +2 -1
- uncountable/types/api/recipes/get_recipes_data.py +2 -0
- uncountable/types/api/recipes/set_recipe_tags.py +91 -0
- uncountable/types/async_batch.py +9 -0
- uncountable/types/async_batch_processor.py +154 -0
- uncountable/types/client_base.py +113 -48
- uncountable/types/permissions.py +46 -0
- uncountable/types/post_base.py +30 -0
- uncountable/types/recipe_inputs.py +30 -0
- uncountable/types/recipe_metadata.py +2 -0
- uncountable/types/recipe_workflow_steps.py +77 -0
- {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.21.dist-info}/WHEEL +0 -0
- {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.21.dist-info}/top_level.txt +0 -0
pkgs/type_spec/emit_python.py
CHANGED
|
@@ -21,6 +21,17 @@ __all__: list[str] = [
|
|
|
21
21
|
"""
|
|
22
22
|
END_ALL_EXPORTS = "]\n"
|
|
23
23
|
|
|
24
|
+
ASYNC_BATCH_TYPE_NAMESPACE = builder.SpecNamespace(name="uncountable.types.async_batch")
|
|
25
|
+
ASYNC_BATCH_REQUEST_PATH_STYPE = builder.SpecTypeDefnStringEnum(
|
|
26
|
+
namespace=ASYNC_BATCH_TYPE_NAMESPACE, name="AsyncBatchRequestPath"
|
|
27
|
+
)
|
|
28
|
+
ASYNC_BATCH_REQUEST_STYPE = builder.SpecTypeDefnObject(
|
|
29
|
+
namespace=ASYNC_BATCH_TYPE_NAMESPACE, name="AsyncBatchRequest"
|
|
30
|
+
)
|
|
31
|
+
QUEUED_BATCH_REQUEST_STYPE = builder.SpecTypeDefnObject(
|
|
32
|
+
namespace=ASYNC_BATCH_TYPE_NAMESPACE, name="QueuedBatchRequest"
|
|
33
|
+
)
|
|
34
|
+
|
|
24
35
|
|
|
25
36
|
@dataclass(kw_only=True)
|
|
26
37
|
class TrackingContext:
|
|
@@ -194,6 +205,7 @@ def emit_python(builder: builder.SpecBuilder, *, config: PythonConfig) -> None:
|
|
|
194
205
|
_emit_api_stubs(builder=builder, config=config)
|
|
195
206
|
_emit_api_argument_lookup(builder=builder, config=config)
|
|
196
207
|
_emit_client_class(spec_builder=builder, config=config)
|
|
208
|
+
_emit_async_batch_processor(spec_builder=builder, config=config)
|
|
197
209
|
|
|
198
210
|
|
|
199
211
|
def _emit_types_imports(*, out: io.StringIO, ctx: Context) -> None:
|
|
@@ -353,11 +365,9 @@ def _validate_supports_handler_generation(
|
|
|
353
365
|
def _emit_endpoint_invocation_docstring(
|
|
354
366
|
ctx: Context,
|
|
355
367
|
endpoint: builder.SpecEndpoint,
|
|
356
|
-
|
|
368
|
+
properties: list[builder.SpecProperty],
|
|
357
369
|
) -> None:
|
|
358
|
-
has_argument_desc =
|
|
359
|
-
prop.desc is not None for prop in arguments_type.properties.values()
|
|
360
|
-
)
|
|
370
|
+
has_argument_desc = any(prop.desc is not None for prop in properties)
|
|
361
371
|
has_endpoint_desc = endpoint.desc
|
|
362
372
|
if not has_argument_desc and not has_endpoint_desc:
|
|
363
373
|
return
|
|
@@ -370,55 +380,39 @@ def _emit_endpoint_invocation_docstring(
|
|
|
370
380
|
ctx.out.write(f"{endpoint.desc}\n")
|
|
371
381
|
ctx.out.write("\n")
|
|
372
382
|
|
|
373
|
-
if
|
|
374
|
-
for prop in
|
|
383
|
+
if has_argument_desc:
|
|
384
|
+
for prop in properties:
|
|
375
385
|
if prop.desc:
|
|
376
386
|
ctx.out.write(f"{FULL_INDENT}:param {prop.name}: {prop.desc}\n")
|
|
377
387
|
|
|
378
388
|
ctx.out.write(f'{FULL_INDENT}"""\n')
|
|
379
389
|
|
|
380
390
|
|
|
381
|
-
def
|
|
382
|
-
ctx: Context,
|
|
391
|
+
def _emit_endpoint_invocation_function_signature(
|
|
392
|
+
ctx: Context,
|
|
393
|
+
endpoint: builder.SpecEndpoint,
|
|
394
|
+
arguments_type: builder.SpecTypeDefnObject,
|
|
395
|
+
data_type: builder.SpecTypeDefnObject,
|
|
396
|
+
extra_params: list[builder.SpecProperty] | None = None,
|
|
383
397
|
) -> None:
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
arguments_type = namespace.types["Arguments"]
|
|
391
|
-
data_type = namespace.types["Data"]
|
|
398
|
+
all_arguments = (
|
|
399
|
+
list(arguments_type.properties.values())
|
|
400
|
+
if arguments_type.properties is not None
|
|
401
|
+
else []
|
|
402
|
+
) + (extra_params if extra_params is not None else [])
|
|
392
403
|
|
|
393
|
-
arguments_type = _validate_supports_handler_generation(arguments_type, "arguments")
|
|
394
|
-
data_type = _validate_supports_handler_generation(
|
|
395
|
-
data_type, "response", supports_inheritance=True
|
|
396
|
-
)
|
|
397
|
-
|
|
398
|
-
endpoint_method_stype = builder.SpecTypeDefnObject(
|
|
399
|
-
namespace=arguments_type.namespace, name=ENDPOINT_METHOD
|
|
400
|
-
)
|
|
401
|
-
endpoint_path_stype = builder.SpecTypeDefnObject(
|
|
402
|
-
namespace=arguments_type.namespace, name=ENDPOINT_PATH
|
|
403
|
-
)
|
|
404
|
-
|
|
405
|
-
has_arguments = (
|
|
406
|
-
arguments_type.properties is not None
|
|
407
|
-
and len(arguments_type.properties.values()) > 0
|
|
408
|
-
)
|
|
409
404
|
assert endpoint.function is not None
|
|
410
405
|
function_name = endpoint.function.split(".")[-1]
|
|
411
|
-
ctx.out.write("\n")
|
|
412
406
|
ctx.out.write(
|
|
413
407
|
f"""
|
|
414
408
|
def {function_name}(
|
|
415
409
|
self,\n"""
|
|
416
410
|
)
|
|
417
|
-
if
|
|
411
|
+
if len(all_arguments) > 0:
|
|
418
412
|
ctx.out.write(f"{INDENT}{INDENT}*,\n")
|
|
419
|
-
|
|
413
|
+
_emit_properties(
|
|
420
414
|
ctx=ctx,
|
|
421
|
-
|
|
415
|
+
properties=all_arguments,
|
|
422
416
|
num_indent=2,
|
|
423
417
|
separator=",\n",
|
|
424
418
|
class_out=ctx.out,
|
|
@@ -426,23 +420,143 @@ def _emit_endpoint_invocation_function(
|
|
|
426
420
|
ctx.out.write(f"{INDENT}) -> {refer_to(ctx=ctx, stype=data_type)}:")
|
|
427
421
|
ctx.out.write("\n")
|
|
428
422
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
423
|
+
if len(all_arguments) > 0:
|
|
424
|
+
_emit_endpoint_invocation_docstring(
|
|
425
|
+
ctx=ctx, endpoint=endpoint, properties=all_arguments
|
|
426
|
+
)
|
|
427
|
+
|
|
434
428
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
429
|
+
def _emit_instantiate_type_from_locals(
|
|
430
|
+
ctx: Context, variable_name: str, variable_type: builder.SpecTypeDefnObject
|
|
431
|
+
) -> None:
|
|
432
|
+
ctx.out.write(
|
|
433
|
+
f"{INDENT}{INDENT}{variable_name} = {refer_to(ctx=ctx, stype=variable_type)}("
|
|
434
|
+
)
|
|
435
|
+
if variable_type.properties is not None and len(variable_type.properties) > 0:
|
|
438
436
|
ctx.out.write("\n")
|
|
439
|
-
for prop in
|
|
437
|
+
for prop in variable_type.properties.values():
|
|
440
438
|
ctx.out.write(f"{INDENT}{INDENT}{INDENT}{prop.name}={prop.name},")
|
|
441
439
|
ctx.out.write("\n")
|
|
442
440
|
ctx.out.write(f"{INDENT}{INDENT})")
|
|
443
441
|
else:
|
|
444
442
|
ctx.out.write(")")
|
|
445
443
|
|
|
444
|
+
|
|
445
|
+
def _emit_async_batch_invocation_function(
|
|
446
|
+
ctx: Context, namespace: builder.SpecNamespace
|
|
447
|
+
) -> None:
|
|
448
|
+
endpoint = namespace.endpoint
|
|
449
|
+
if endpoint is None:
|
|
450
|
+
return
|
|
451
|
+
if endpoint.async_batch_path is None or not endpoint.is_sdk:
|
|
452
|
+
return
|
|
453
|
+
|
|
454
|
+
ctx.out.write("\n")
|
|
455
|
+
arguments_type = namespace.types["Arguments"]
|
|
456
|
+
arguments_type = _validate_supports_handler_generation(arguments_type, "arguments")
|
|
457
|
+
data_type = QUEUED_BATCH_REQUEST_STYPE
|
|
458
|
+
|
|
459
|
+
list_str_params: list[builder.SpecType] = []
|
|
460
|
+
list_str_params.append(
|
|
461
|
+
builder.SpecTypeGenericParameter(
|
|
462
|
+
name="str",
|
|
463
|
+
spec_type_definition=builder.SpecTypeDefnObject(
|
|
464
|
+
namespace=namespace, name=builder.BaseTypeName.s_string, is_base=True
|
|
465
|
+
),
|
|
466
|
+
)
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
_emit_endpoint_invocation_function_signature(
|
|
470
|
+
ctx=ctx,
|
|
471
|
+
endpoint=endpoint,
|
|
472
|
+
arguments_type=arguments_type,
|
|
473
|
+
data_type=data_type,
|
|
474
|
+
extra_params=[
|
|
475
|
+
builder.SpecProperty(
|
|
476
|
+
name="depends_on",
|
|
477
|
+
extant=builder.PropertyExtant.optional,
|
|
478
|
+
convert_value=builder.PropertyConvertValue.auto,
|
|
479
|
+
name_case=builder.NameCase.convert,
|
|
480
|
+
label="depends_on",
|
|
481
|
+
desc="A list of batch reference keys to process before processing this request",
|
|
482
|
+
spec_type=builder.SpecTypeInstance(
|
|
483
|
+
defn_type=builder.SpecTypeDefnObject(
|
|
484
|
+
name=builder.BaseTypeName.s_list,
|
|
485
|
+
is_base=True,
|
|
486
|
+
namespace=namespace,
|
|
487
|
+
),
|
|
488
|
+
parameters=list_str_params,
|
|
489
|
+
),
|
|
490
|
+
)
|
|
491
|
+
],
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
# Emit function body
|
|
495
|
+
_emit_instantiate_type_from_locals(
|
|
496
|
+
ctx=ctx, variable_name="args", variable_type=arguments_type
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
path = _emit_value(
|
|
500
|
+
ctx=ctx,
|
|
501
|
+
stype=ASYNC_BATCH_REQUEST_PATH_STYPE,
|
|
502
|
+
value=endpoint.async_batch_path,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
ctx.out.write(
|
|
506
|
+
f"""
|
|
507
|
+
json_data = serialize_for_api(args)
|
|
508
|
+
|
|
509
|
+
batch_reference = str(uuid.uuid4())
|
|
510
|
+
|
|
511
|
+
req = {refer_to(ctx=ctx, stype=ASYNC_BATCH_REQUEST_STYPE)}(
|
|
512
|
+
path={path},
|
|
513
|
+
data=json_data,
|
|
514
|
+
depends_on=depends_on,
|
|
515
|
+
batch_reference=batch_reference,
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
self._enqueue(req)
|
|
519
|
+
|
|
520
|
+
return {refer_to(ctx=ctx, stype=data_type)}(
|
|
521
|
+
path=req.path,
|
|
522
|
+
batch_reference=req.batch_reference,
|
|
523
|
+
)"""
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
def _emit_endpoint_invocation_function(
|
|
528
|
+
ctx: Context, namespace: builder.SpecNamespace
|
|
529
|
+
) -> None:
|
|
530
|
+
endpoint = namespace.endpoint
|
|
531
|
+
if endpoint is None:
|
|
532
|
+
return
|
|
533
|
+
if not endpoint.is_sdk or endpoint.is_beta:
|
|
534
|
+
return
|
|
535
|
+
|
|
536
|
+
ctx.out.write("\n")
|
|
537
|
+
arguments_type = namespace.types["Arguments"]
|
|
538
|
+
data_type = namespace.types["Data"]
|
|
539
|
+
arguments_type = _validate_supports_handler_generation(arguments_type, "arguments")
|
|
540
|
+
data_type = _validate_supports_handler_generation(
|
|
541
|
+
data_type, "response", supports_inheritance=True
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
_emit_endpoint_invocation_function_signature(
|
|
545
|
+
ctx=ctx, endpoint=endpoint, arguments_type=arguments_type, data_type=data_type
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
endpoint_method_stype = builder.SpecTypeDefnObject(
|
|
549
|
+
namespace=arguments_type.namespace, name=ENDPOINT_METHOD
|
|
550
|
+
)
|
|
551
|
+
endpoint_path_stype = builder.SpecTypeDefnObject(
|
|
552
|
+
namespace=arguments_type.namespace, name=ENDPOINT_PATH
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
# Emit function body
|
|
556
|
+
_emit_instantiate_type_from_locals(
|
|
557
|
+
ctx=ctx, variable_name="args", variable_type=arguments_type
|
|
558
|
+
)
|
|
559
|
+
|
|
446
560
|
ctx.out.write(
|
|
447
561
|
f"""
|
|
448
562
|
api_request = APIRequest(
|
|
@@ -452,7 +566,6 @@ def _emit_endpoint_invocation_function(
|
|
|
452
566
|
)
|
|
453
567
|
return self.do_request(api_request=api_request, return_type={refer_to(ctx=ctx, stype=data_type)})"""
|
|
454
568
|
)
|
|
455
|
-
ctx.out.write("\n")
|
|
456
569
|
|
|
457
570
|
|
|
458
571
|
def _emit_string_enum(ctx: Context, stype: builder.SpecTypeDefnStringEnum) -> None:
|
|
@@ -516,17 +629,32 @@ def _emit_type_properties(
|
|
|
516
629
|
stype: builder.SpecTypeDefnObject,
|
|
517
630
|
num_indent: int = 1,
|
|
518
631
|
separator: str = "\n",
|
|
632
|
+
) -> EmittedPropertiesMetadata:
|
|
633
|
+
return _emit_properties(
|
|
634
|
+
ctx=ctx,
|
|
635
|
+
class_out=class_out,
|
|
636
|
+
properties=list((stype.properties or {}).values()),
|
|
637
|
+
num_indent=num_indent,
|
|
638
|
+
separator=separator,
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
def _emit_properties(
|
|
643
|
+
*,
|
|
644
|
+
ctx: Context,
|
|
645
|
+
class_out: io.StringIO,
|
|
646
|
+
properties: list[builder.SpecProperty],
|
|
647
|
+
num_indent: int = 1,
|
|
648
|
+
separator: str = "\n",
|
|
519
649
|
) -> EmittedPropertiesMetadata:
|
|
520
650
|
unconverted_keys: set[str] = set()
|
|
521
651
|
unconverted_values: set[str] = set()
|
|
522
652
|
to_string_values: set[str] = set()
|
|
523
653
|
parse_require: set[str] = set()
|
|
524
654
|
|
|
525
|
-
if
|
|
655
|
+
if len(properties) > 0:
|
|
526
656
|
|
|
527
657
|
def write_field(prop: builder.SpecProperty) -> None:
|
|
528
|
-
# Checked in outer function, MyPy doens't track the check inside here
|
|
529
|
-
assert isinstance(stype, builder.SpecTypeDefn)
|
|
530
658
|
if prop.name_case == builder.NameCase.preserve:
|
|
531
659
|
unconverted_keys.add(prop.name)
|
|
532
660
|
py_name = python_field_name(prop.name, prop.name_case)
|
|
@@ -559,10 +687,10 @@ def _emit_type_properties(
|
|
|
559
687
|
class_out.write(f" = {default}")
|
|
560
688
|
class_out.write(separator)
|
|
561
689
|
|
|
562
|
-
for prop in
|
|
690
|
+
for prop in properties:
|
|
563
691
|
if prop.extant == builder.PropertyExtant.required:
|
|
564
692
|
write_field(prop)
|
|
565
|
-
for prop in
|
|
693
|
+
for prop in properties:
|
|
566
694
|
if prop.extant != builder.PropertyExtant.required:
|
|
567
695
|
write_field(prop)
|
|
568
696
|
else:
|
|
@@ -1014,6 +1142,64 @@ CLIENT_CLASS_IMPORTS = [
|
|
|
1014
1142
|
"from abc import ABC, abstractmethod",
|
|
1015
1143
|
"from dataclasses import dataclass",
|
|
1016
1144
|
]
|
|
1145
|
+
ASYNC_BATCH_PROCESSOR_FILENAME = "async_batch_processor"
|
|
1146
|
+
ASYNC_BATCH_PROCESSOR_IMPORTS = [
|
|
1147
|
+
"import uuid",
|
|
1148
|
+
"from abc import ABC, abstractmethod",
|
|
1149
|
+
"from dataclasses import dataclass",
|
|
1150
|
+
"from pkgs.serialization_util.serialization_helpers import serialize_for_api",
|
|
1151
|
+
]
|
|
1152
|
+
|
|
1153
|
+
|
|
1154
|
+
def _emit_async_batch_processor(
|
|
1155
|
+
*, spec_builder: builder.SpecBuilder, config: PythonConfig
|
|
1156
|
+
) -> None:
|
|
1157
|
+
if not config.emit_async_batch_processor:
|
|
1158
|
+
return
|
|
1159
|
+
|
|
1160
|
+
async_batch_processor_out = io.StringIO()
|
|
1161
|
+
ctx = Context(
|
|
1162
|
+
out=io.StringIO(), namespace=builder.SpecNamespace("async_batch_processor")
|
|
1163
|
+
)
|
|
1164
|
+
|
|
1165
|
+
for namespace in sorted(
|
|
1166
|
+
spec_builder.namespaces.values(),
|
|
1167
|
+
key=lambda ns: _resolve_namespace_name(ns),
|
|
1168
|
+
):
|
|
1169
|
+
_emit_async_batch_invocation_function(ctx=ctx, namespace=namespace)
|
|
1170
|
+
|
|
1171
|
+
async_batch_processor_out.write(MODIFY_NOTICE)
|
|
1172
|
+
async_batch_processor_out.write(LINT_HEADER)
|
|
1173
|
+
async_batch_processor_out.write("# ruff: noqa: PLR0904\n")
|
|
1174
|
+
|
|
1175
|
+
_emit_types_imports(out=async_batch_processor_out, ctx=ctx)
|
|
1176
|
+
_emit_namespace_imports(
|
|
1177
|
+
out=async_batch_processor_out,
|
|
1178
|
+
namespaces=ctx.namespaces,
|
|
1179
|
+
from_namespace=None,
|
|
1180
|
+
config=config,
|
|
1181
|
+
)
|
|
1182
|
+
|
|
1183
|
+
async_batch_processor_out.write(
|
|
1184
|
+
f"""{LINE_BREAK.join(ASYNC_BATCH_PROCESSOR_IMPORTS)}
|
|
1185
|
+
|
|
1186
|
+
|
|
1187
|
+
class AsyncBatchProcessorBase(ABC):
|
|
1188
|
+
@abstractmethod
|
|
1189
|
+
def _enqueue(self, req: {refer_to(ctx=ctx, stype=ASYNC_BATCH_REQUEST_STYPE)}) -> None:
|
|
1190
|
+
...
|
|
1191
|
+
|
|
1192
|
+
@abstractmethod
|
|
1193
|
+
def send(self) -> base_t.ObjectId:
|
|
1194
|
+
..."""
|
|
1195
|
+
)
|
|
1196
|
+
async_batch_processor_out.write(ctx.out.getvalue())
|
|
1197
|
+
async_batch_processor_out.write("\n")
|
|
1198
|
+
|
|
1199
|
+
util.rewrite_file(
|
|
1200
|
+
f"{config.types_output}/{ASYNC_BATCH_PROCESSOR_FILENAME}.py",
|
|
1201
|
+
async_batch_processor_out.getvalue(),
|
|
1202
|
+
)
|
|
1017
1203
|
|
|
1018
1204
|
|
|
1019
1205
|
def _emit_client_class(
|
|
@@ -1023,7 +1209,7 @@ def _emit_client_class(
|
|
|
1023
1209
|
return
|
|
1024
1210
|
|
|
1025
1211
|
client_base_out = io.StringIO()
|
|
1026
|
-
ctx = Context(out=io.StringIO(), namespace=builder.SpecNamespace("
|
|
1212
|
+
ctx = Context(out=io.StringIO(), namespace=builder.SpecNamespace("client_base"))
|
|
1027
1213
|
for namespace in sorted(
|
|
1028
1214
|
spec_builder.namespaces.values(),
|
|
1029
1215
|
key=lambda ns: _resolve_namespace_name(ns),
|
|
@@ -1038,7 +1224,7 @@ def _emit_client_class(
|
|
|
1038
1224
|
_emit_namespace_imports(
|
|
1039
1225
|
out=client_base_out,
|
|
1040
1226
|
namespaces=ctx.namespaces,
|
|
1041
|
-
from_namespace=
|
|
1227
|
+
from_namespace=None,
|
|
1042
1228
|
config=config,
|
|
1043
1229
|
)
|
|
1044
1230
|
|
|
@@ -1059,10 +1245,10 @@ class ClientMethods(ABC):
|
|
|
1059
1245
|
|
|
1060
1246
|
@abstractmethod
|
|
1061
1247
|
def do_request(self, *, api_request: APIRequest, return_type: type[DT]) -> DT:
|
|
1062
|
-
...
|
|
1063
|
-
"""
|
|
1248
|
+
..."""
|
|
1064
1249
|
)
|
|
1065
1250
|
client_base_out.write(ctx.out.getvalue())
|
|
1251
|
+
client_base_out.write("\n")
|
|
1066
1252
|
|
|
1067
1253
|
util.rewrite_file(
|
|
1068
1254
|
f"{config.types_output}/{CLIENT_CLASS_FILENAME}.py", client_base_out.getvalue()
|
|
@@ -23,7 +23,7 @@ def type_path_of(stype: builder.SpecType) -> object: # NamePath
|
|
|
23
23
|
extended scopes, generics, and enum literal values.
|
|
24
24
|
- Scoped Type: [ (namespace-string)..., type-string ]
|
|
25
25
|
- Instance Type: [ "$instance", Scoped-Type-Base, [TypePath-Parameters...] ]
|
|
26
|
-
- Literal Type: [ "$literal", [ "$value", value ]... ]
|
|
26
|
+
- Literal Type: [ "$literal", [ "$value", value, value-type-string ]... ]
|
|
27
27
|
|
|
28
28
|
@return (string-specific, multiple-types)
|
|
29
29
|
"""
|
|
@@ -34,11 +34,15 @@ def type_path_of(stype: builder.SpecType) -> object: # NamePath
|
|
|
34
34
|
|
|
35
35
|
if isinstance(stype, builder.SpecTypeInstance):
|
|
36
36
|
if stype.defn_type.name == builder.BaseTypeName.s_literal:
|
|
37
|
-
parts: list[
|
|
37
|
+
parts: list[object] = ["$literal"]
|
|
38
38
|
for parameter in stype.parameters:
|
|
39
39
|
assert isinstance(parameter, builder.SpecTypeLiteralWrapper)
|
|
40
40
|
# This allows expansion to enum literal values later
|
|
41
|
-
parts.append([
|
|
41
|
+
parts.append([
|
|
42
|
+
"$value",
|
|
43
|
+
parameter.value,
|
|
44
|
+
type_path_of(parameter.value_type),
|
|
45
|
+
])
|
|
42
46
|
return parts
|
|
43
47
|
|
|
44
48
|
return [
|
|
@@ -148,6 +152,114 @@ def _build_map_all(build: builder.SpecBuilder) -> MapAll:
|
|
|
148
152
|
return map_all
|
|
149
153
|
|
|
150
154
|
|
|
155
|
+
@dataclasses.dataclass(kw_only=True)
|
|
156
|
+
class InheritablePropertyParts:
|
|
157
|
+
"""This uses only the "soft" information for now, things that aren't relevant
|
|
158
|
+
to the language emitted types. There are some fields that should be inherited
|
|
159
|
+
at that level, but that needs to be done in builder. When that is done, the
|
|
160
|
+
"label" and "desc" could probably be removed from this list."""
|
|
161
|
+
|
|
162
|
+
label: Optional[str] = None
|
|
163
|
+
desc: Optional[str] = None
|
|
164
|
+
ext_info: Optional[data_t.ExtInfo] = None
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _extract_inheritable_property_parts(
|
|
168
|
+
stype: builder.SpecTypeDefnObject,
|
|
169
|
+
prop: builder.SpecProperty,
|
|
170
|
+
) -> InheritablePropertyParts:
|
|
171
|
+
if not stype.is_base and isinstance(stype.base, builder.SpecTypeDefn):
|
|
172
|
+
base_prop = (stype.base.properties or {}).get(prop.name)
|
|
173
|
+
if base_prop is None:
|
|
174
|
+
base_parts = InheritablePropertyParts()
|
|
175
|
+
else:
|
|
176
|
+
base_parts = _extract_inheritable_property_parts(stype.base, base_prop)
|
|
177
|
+
# Layout should not be inherited, as it'd end up hiding properties in the derived type
|
|
178
|
+
if base_parts.ext_info is not None:
|
|
179
|
+
base_parts.ext_info.layout = None
|
|
180
|
+
else:
|
|
181
|
+
base_parts = InheritablePropertyParts()
|
|
182
|
+
|
|
183
|
+
label = prop.label or base_parts.label
|
|
184
|
+
desc = prop.desc or base_parts.desc
|
|
185
|
+
local_ext_info = _parse_ext_info(prop.ext_info)
|
|
186
|
+
if local_ext_info is None:
|
|
187
|
+
ext_info = base_parts.ext_info
|
|
188
|
+
elif base_parts.ext_info is None:
|
|
189
|
+
ext_info = local_ext_info
|
|
190
|
+
else:
|
|
191
|
+
ext_info = data_t.ExtInfo(
|
|
192
|
+
**(local_ext_info.__dict__ | base_parts.ext_info.__dict__)
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
return InheritablePropertyParts(label=label, desc=desc, ext_info=ext_info)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
ExtInfoLayout = dict[str, set[str]]
|
|
199
|
+
ALL_FIELDS_GROUP = "*all_fields"
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _extract_and_validate_layout(
|
|
203
|
+
stype: builder.SpecTypeDefnObject,
|
|
204
|
+
ext_info: data_t.ExtInfo,
|
|
205
|
+
base_layout: ExtInfoLayout | None,
|
|
206
|
+
) -> ExtInfoLayout:
|
|
207
|
+
"""
|
|
208
|
+
Produce a map of groups to fields, for validation.
|
|
209
|
+
"""
|
|
210
|
+
if ext_info.layout is None:
|
|
211
|
+
return {}
|
|
212
|
+
assert stype.properties is not None
|
|
213
|
+
|
|
214
|
+
all_fields_group: set[str] = set()
|
|
215
|
+
layout: ExtInfoLayout = {ALL_FIELDS_GROUP: all_fields_group}
|
|
216
|
+
|
|
217
|
+
for group in ext_info.layout.groups:
|
|
218
|
+
fields = set(group.fields or [])
|
|
219
|
+
for field in fields:
|
|
220
|
+
assert field in stype.properties, f"layout-refers-to-missing-field:{field}"
|
|
221
|
+
|
|
222
|
+
local_ref_name = None
|
|
223
|
+
if group.ref_name is not None:
|
|
224
|
+
assert (
|
|
225
|
+
base_layout is None or base_layout.get(group.ref_name) is None
|
|
226
|
+
), f"group-name-duplicate-in-base:{group.ref_name}"
|
|
227
|
+
local_ref_name = group.ref_name
|
|
228
|
+
|
|
229
|
+
if group.extends:
|
|
230
|
+
assert base_layout is not None, "missing-base-layout"
|
|
231
|
+
base_group = base_layout.get(group.extends)
|
|
232
|
+
assert base_group is not None, f"missing-base-group:{group.extends}"
|
|
233
|
+
fields.update(base_group)
|
|
234
|
+
local_ref_name = group.extends
|
|
235
|
+
|
|
236
|
+
assert local_ref_name not in layout, f"duplicate-group:{local_ref_name}"
|
|
237
|
+
if local_ref_name is not None:
|
|
238
|
+
layout[local_ref_name] = fields
|
|
239
|
+
all_fields_group.update(fields)
|
|
240
|
+
|
|
241
|
+
for group_ref_name in base_layout or {}:
|
|
242
|
+
assert group_ref_name in layout, f"missing-base-group:{group_ref_name}"
|
|
243
|
+
|
|
244
|
+
for prop_ref_name in stype.properties:
|
|
245
|
+
assert prop_ref_name in all_fields_group, f"layout-missing-field:{prop_ref_name}"
|
|
246
|
+
|
|
247
|
+
return layout
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _validate_type_ext_info(stype: builder.SpecTypeDefnObject) -> ExtInfoLayout | None:
|
|
251
|
+
ext_info = _parse_ext_info(stype.ext_info)
|
|
252
|
+
if ext_info is None:
|
|
253
|
+
return None
|
|
254
|
+
|
|
255
|
+
if not stype.is_base and isinstance(stype.base, builder.SpecTypeDefnObject):
|
|
256
|
+
base_layout = _validate_type_ext_info(stype.base)
|
|
257
|
+
else:
|
|
258
|
+
base_layout = None
|
|
259
|
+
|
|
260
|
+
return _extract_and_validate_layout(stype, ext_info, base_layout)
|
|
261
|
+
|
|
262
|
+
|
|
151
263
|
def _build_map_type(
|
|
152
264
|
build: builder.SpecBuilder, stype: builder.SpecTypeDefn
|
|
153
265
|
) -> MapType | None:
|
|
@@ -158,6 +270,8 @@ def _build_map_type(
|
|
|
158
270
|
and not stype.is_base
|
|
159
271
|
and stype.base is not None
|
|
160
272
|
):
|
|
273
|
+
_validate_type_ext_info(stype)
|
|
274
|
+
|
|
161
275
|
properties: dict[str, MapProperty] = {}
|
|
162
276
|
map_type = MapTypeObject(
|
|
163
277
|
type_name=stype.name,
|
|
@@ -170,14 +284,17 @@ def _build_map_type(
|
|
|
170
284
|
|
|
171
285
|
if stype.properties is not None:
|
|
172
286
|
for prop in stype.properties.values():
|
|
287
|
+
parts = _extract_inheritable_property_parts(stype, prop)
|
|
288
|
+
# Propertis can't have layouts
|
|
289
|
+
assert parts.ext_info is None or parts.ext_info.layout is None
|
|
173
290
|
map_property = MapProperty(
|
|
174
291
|
type_name=prop.name,
|
|
175
|
-
label=
|
|
292
|
+
label=parts.label,
|
|
176
293
|
api_name=ts_name(prop.name, prop.name_case),
|
|
177
294
|
extant=prop.extant,
|
|
178
295
|
type_path=type_path_of(prop.spec_type),
|
|
179
|
-
ext_info=
|
|
180
|
-
desc=
|
|
296
|
+
ext_info=serialize_for_api(parts.ext_info),
|
|
297
|
+
desc=parts.desc,
|
|
181
298
|
default=prop.default,
|
|
182
299
|
)
|
|
183
300
|
map_type.properties[prop.name] = map_property
|
|
@@ -211,7 +328,7 @@ def _build_map_type(
|
|
|
211
328
|
return None
|
|
212
329
|
|
|
213
330
|
|
|
214
|
-
def
|
|
331
|
+
def _parse_ext_info(in_ext: Any) -> Optional[data_t.ExtInfo]:
|
|
215
332
|
if in_ext is None:
|
|
216
333
|
return None
|
|
217
334
|
assert isinstance(in_ext, dict)
|
|
@@ -229,6 +346,10 @@ def _convert_ext_info(in_ext: Any) -> Optional[PureJsonValue]:
|
|
|
229
346
|
df["result_type"] = serialize_for_storage(converted)
|
|
230
347
|
mod_ext["data_format"] = df
|
|
231
348
|
|
|
232
|
-
|
|
349
|
+
return ext_info_parser.parse_storage(mod_ext)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _convert_ext_info(in_ext: Any) -> Optional[PureJsonValue]:
|
|
233
353
|
# we need to convert this to API storage since it'll be used as-is in the UI
|
|
354
|
+
parsed = _parse_ext_info(in_ext)
|
|
234
355
|
return cast(PureJsonValue, serialize_for_api(parsed))
|
|
@@ -19,7 +19,18 @@ Arguments:
|
|
|
19
19
|
type: ObjectId
|
|
20
20
|
desc: "Definition id for the entities to create"
|
|
21
21
|
entity_type:
|
|
22
|
-
type:
|
|
22
|
+
type: |
|
|
23
|
+
Union<
|
|
24
|
+
Literal<entity.EntityType.lab_request>,
|
|
25
|
+
Literal<entity.EntityType.approval>,
|
|
26
|
+
Literal<entity.EntityType.custom_entity>,
|
|
27
|
+
Literal<entity.EntityType.task>,
|
|
28
|
+
Literal<entity.EntityType.project>,
|
|
29
|
+
Literal<entity.EntityType.equipment>,
|
|
30
|
+
Literal<entity.EntityType.inv_local_locations>,
|
|
31
|
+
Literal<entity.EntityType.field_option_set>,
|
|
32
|
+
Literal<entity.EntityType.webhook>
|
|
33
|
+
>
|
|
23
34
|
desc: "The type of the entities to create"
|
|
24
35
|
entities_to_create:
|
|
25
36
|
type: List<EntityToCreate>
|
|
@@ -26,7 +26,18 @@ Arguments:
|
|
|
26
26
|
type: ObjectId
|
|
27
27
|
desc: "Definition id of the entity to create"
|
|
28
28
|
entity_type:
|
|
29
|
-
type:
|
|
29
|
+
type: |
|
|
30
|
+
Union<
|
|
31
|
+
Literal<entity.EntityType.lab_request>,
|
|
32
|
+
Literal<entity.EntityType.approval>,
|
|
33
|
+
Literal<entity.EntityType.custom_entity>,
|
|
34
|
+
Literal<entity.EntityType.task>,
|
|
35
|
+
Literal<entity.EntityType.project>,
|
|
36
|
+
Literal<entity.EntityType.equipment>,
|
|
37
|
+
Literal<entity.EntityType.inv_local_locations>,
|
|
38
|
+
Literal<entity.EntityType.field_option_set>,
|
|
39
|
+
Literal<entity.EntityType.webhook>
|
|
40
|
+
>
|
|
30
41
|
desc: "The type of the entities requested"
|
|
31
42
|
field_values?:
|
|
32
43
|
type: Optional<List<field_values.FieldRefNameValue>>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
method: post
|
|
4
|
+
path: ${external}/entity/transition_entity_phase
|
|
5
|
+
function: main.site.app.external.entity.transition_entity_phase.transition_entity_phase
|
|
6
|
+
desc: "Transitions an entity from one phase to another"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
TransitionIdentifierPhases:
|
|
10
|
+
type: Object
|
|
11
|
+
properties:
|
|
12
|
+
type:
|
|
13
|
+
type: Literal<'phases'>
|
|
14
|
+
phase_from_key:
|
|
15
|
+
type: identifier.IdentifierKey
|
|
16
|
+
phase_to_key:
|
|
17
|
+
type: identifier.IdentifierKey
|
|
18
|
+
|
|
19
|
+
TransitionIdentifierTransition:
|
|
20
|
+
type: Object
|
|
21
|
+
properties:
|
|
22
|
+
type:
|
|
23
|
+
type: Literal<'transition'>
|
|
24
|
+
transition_key:
|
|
25
|
+
type: identifier.IdentifierKey
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
TransitionIdentifier:
|
|
29
|
+
type: Alias
|
|
30
|
+
alias: |
|
|
31
|
+
Union<TransitionIdentifierPhases, TransitionIdentifierTransition>
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
Arguments:
|
|
35
|
+
type: Object
|
|
36
|
+
properties:
|
|
37
|
+
entity:
|
|
38
|
+
type: entity.Entity
|
|
39
|
+
transition:
|
|
40
|
+
type: TransitionIdentifier
|
|
41
|
+
|
|
42
|
+
Data:
|
|
43
|
+
type: response.Response
|
|
44
|
+
properties:
|