UncountablePythonSDK 0.0.20__py3-none-any.whl → 0.0.22__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.22.dist-info}/METADATA +3 -1
- {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.22.dist-info}/RECORD +55 -34
- examples/async_batch.py +36 -0
- 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 +125 -8
- pkgs/type_spec/config.py +1 -0
- pkgs/type_spec/emit_open_api.py +197 -16
- pkgs/type_spec/emit_open_api_util.py +18 -0
- pkgs/type_spec/emit_python.py +241 -55
- pkgs/type_spec/load_types.py +48 -5
- pkgs/type_spec/open_api_util.py +13 -33
- pkgs/type_spec/type_info/emit_type_info.py +129 -8
- type_spec/external/api/entity/create_entities.yaml +13 -1
- type_spec/external/api/entity/create_entity.yaml +13 -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 +86 -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 +3 -1
- uncountable/core/async_batch.py +22 -0
- uncountable/core/client.py +84 -10
- 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 +106 -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 +10 -0
- uncountable/types/async_batch_processor.py +154 -0
- uncountable/types/client_base.py +113 -48
- uncountable/types/identifier.py +3 -3
- 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.22.dist-info}/WHEEL +0 -0
- {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.22.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="QueuedAsyncBatchRequest"
|
|
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()
|
pkgs/type_spec/load_types.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from collections.abc import Callable
|
|
3
|
+
from io import StringIO
|
|
3
4
|
from typing import Optional
|
|
4
5
|
|
|
5
6
|
import yaml
|
|
@@ -13,11 +14,28 @@ ext_map = {
|
|
|
13
14
|
".py": "python",
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
_DOC_FILE_REFEX = ".*/docs/(examples|guides)/.*yaml"
|
|
18
|
+
_EXAMPLE_FILE_REGEX = ".*/docs/examples/.*yaml"
|
|
19
|
+
_GUIDE_FILE_REGEX = ".*/docs/guides/.*md"
|
|
20
|
+
|
|
16
21
|
|
|
17
22
|
def find_and_handle_files(
|
|
18
|
-
|
|
23
|
+
*,
|
|
24
|
+
root_folder: str,
|
|
25
|
+
handler: Callable[[str, str], None],
|
|
26
|
+
name_regex: str | None = None,
|
|
27
|
+
not_name_regex: str | None = None,
|
|
28
|
+
whole_name_regex: str | None = None,
|
|
29
|
+
not_whole_name_regex: str | None = None,
|
|
19
30
|
) -> None:
|
|
20
|
-
for file_name in fs.find(
|
|
31
|
+
for file_name in fs.find(
|
|
32
|
+
root_folder,
|
|
33
|
+
name_regex=name_regex,
|
|
34
|
+
not_name_regex=not_name_regex,
|
|
35
|
+
whole_name_regex=whole_name_regex,
|
|
36
|
+
not_whole_name_regex=not_whole_name_regex,
|
|
37
|
+
relative=True,
|
|
38
|
+
):
|
|
21
39
|
with open(os.path.join(root_folder, file_name), encoding="utf-8") as file:
|
|
22
40
|
handler(file_name, file.read())
|
|
23
41
|
|
|
@@ -32,9 +50,16 @@ def load_types(config: Config) -> Optional[SpecBuilder]:
|
|
|
32
50
|
name, ext = os.path.splitext(by_name)
|
|
33
51
|
handler(ext_map[ext], name, file_content)
|
|
34
52
|
|
|
53
|
+
def handle_builder_example_add(file_name: str, file_content: str) -> None:
|
|
54
|
+
yaml_content = yaml.safe_load(StringIO(file_content))
|
|
55
|
+
builder.add_example_file(yaml_content)
|
|
56
|
+
|
|
57
|
+
def handle_builder_guide_add(file_name: str, file_content: str) -> None:
|
|
58
|
+
builder.add_guide_file(file_content)
|
|
59
|
+
|
|
35
60
|
for folder in config.type_spec_types:
|
|
36
61
|
find_and_handle_files(
|
|
37
|
-
folder,
|
|
62
|
+
root_folder=folder,
|
|
38
63
|
name_regex=".*\\.(ts|py)\\.part",
|
|
39
64
|
handler=lambda file_name, file_content: handle_builder_add(
|
|
40
65
|
file_name, file_content, builder.add_part_file
|
|
@@ -43,7 +68,7 @@ def load_types(config: Config) -> Optional[SpecBuilder]:
|
|
|
43
68
|
|
|
44
69
|
for folder in config.type_spec_types:
|
|
45
70
|
find_and_handle_files(
|
|
46
|
-
folder,
|
|
71
|
+
root_folder=folder,
|
|
47
72
|
name_regex=".*\\.(ts|py)\\.prepart",
|
|
48
73
|
handler=lambda file_name, file_content: handle_builder_add(
|
|
49
74
|
file_name, file_content, builder.add_prepart_file
|
|
@@ -64,9 +89,27 @@ def load_types(config: Config) -> Optional[SpecBuilder]:
|
|
|
64
89
|
|
|
65
90
|
for folder in config.type_spec_types:
|
|
66
91
|
find_and_handle_files(
|
|
67
|
-
folder,
|
|
92
|
+
root_folder=folder,
|
|
93
|
+
name_regex=".*\\.yaml",
|
|
94
|
+
not_whole_name_regex=_DOC_FILE_REFEX,
|
|
95
|
+
handler=builder_prescan_file,
|
|
68
96
|
)
|
|
69
97
|
|
|
98
|
+
if config.open_api is not None:
|
|
99
|
+
for folder in config.type_spec_types:
|
|
100
|
+
find_and_handle_files(
|
|
101
|
+
root_folder=folder,
|
|
102
|
+
whole_name_regex=_EXAMPLE_FILE_REGEX,
|
|
103
|
+
handler=handle_builder_example_add,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
for folder in config.type_spec_types:
|
|
107
|
+
find_and_handle_files(
|
|
108
|
+
root_folder=folder,
|
|
109
|
+
whole_name_regex=_GUIDE_FILE_REGEX,
|
|
110
|
+
handler=handle_builder_guide_add,
|
|
111
|
+
)
|
|
112
|
+
|
|
70
113
|
if not builder.process():
|
|
71
114
|
return None
|
|
72
115
|
|
pkgs/type_spec/open_api_util.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from enum import StrEnum
|
|
3
|
-
from io import UnsupportedOperation
|
|
4
3
|
from typing import Optional
|
|
5
4
|
|
|
6
5
|
|
|
@@ -16,9 +15,6 @@ class OpenAPIType(ABC):
|
|
|
16
15
|
def asdict(self) -> dict[str, object]:
|
|
17
16
|
pass
|
|
18
17
|
|
|
19
|
-
def asarguments(self) -> dict[str, dict[str, object]]:
|
|
20
|
-
raise UnsupportedOperation
|
|
21
|
-
|
|
22
18
|
def add_addl_info(self, emitted: dict[str, object]) -> dict[str, object]:
|
|
23
19
|
if self.description is not None:
|
|
24
20
|
emitted["description"] = self.description
|
|
@@ -148,9 +144,6 @@ class OpenAPIFreeFormObjectType(OpenAPIType):
|
|
|
148
144
|
def asdict(self) -> dict[str, object]:
|
|
149
145
|
return self.add_addl_info({"type": "object"})
|
|
150
146
|
|
|
151
|
-
def asarguments(self) -> dict[str, dict[str, object]]:
|
|
152
|
-
return {}
|
|
153
|
-
|
|
154
147
|
|
|
155
148
|
class OpenAPIObjectType(OpenAPIType):
|
|
156
149
|
"""
|
|
@@ -174,31 +167,30 @@ class OpenAPIObjectType(OpenAPIType):
|
|
|
174
167
|
self.property_desc = property_desc
|
|
175
168
|
super().__init__(description=description, nullable=nullable)
|
|
176
169
|
|
|
170
|
+
def _emit_property_desc(self, property_name: str) -> dict[str, str]:
|
|
171
|
+
desc = self.property_desc.get(property_name)
|
|
172
|
+
if desc is None or desc.strip() == "":
|
|
173
|
+
return {}
|
|
174
|
+
|
|
175
|
+
return {"description": desc}
|
|
176
|
+
|
|
177
177
|
def asdict(self) -> dict[str, object]:
|
|
178
178
|
return self.add_addl_info({
|
|
179
179
|
"type": "object",
|
|
180
|
+
"required": [
|
|
181
|
+
property_name
|
|
182
|
+
for property_name, property_type in self.properties.items()
|
|
183
|
+
if not property_type.nullable
|
|
184
|
+
],
|
|
180
185
|
"properties": {
|
|
181
186
|
property_name: {
|
|
182
187
|
**property_type.asdict(),
|
|
183
|
-
"description": self.property_desc.get(property_name),
|
|
184
188
|
}
|
|
189
|
+
| self._emit_property_desc(property_name)
|
|
185
190
|
for property_name, property_type in self.properties.items()
|
|
186
191
|
},
|
|
187
192
|
})
|
|
188
193
|
|
|
189
|
-
def asarguments(self) -> dict[str, dict[str, object]]:
|
|
190
|
-
argument_types: dict[str, dict[str, object]] = {}
|
|
191
|
-
for property_name, property_type in self.properties.items():
|
|
192
|
-
desc = self.property_desc.get(property_name)
|
|
193
|
-
argument_types[property_name] = {
|
|
194
|
-
"name": property_name,
|
|
195
|
-
"in": "query",
|
|
196
|
-
"schema": property_type.asdict(),
|
|
197
|
-
"required": not property_type.nullable,
|
|
198
|
-
"description": desc or "",
|
|
199
|
-
}
|
|
200
|
-
return argument_types
|
|
201
|
-
|
|
202
194
|
|
|
203
195
|
class OpenAPIUnionType(OpenAPIType):
|
|
204
196
|
"""
|
|
@@ -220,12 +212,6 @@ class OpenAPIUnionType(OpenAPIType):
|
|
|
220
212
|
# TODO: use parents description and nullable
|
|
221
213
|
return {"oneOf": [base_type.asdict() for base_type in self.base_types]}
|
|
222
214
|
|
|
223
|
-
def asarguments(self) -> dict[str, dict[str, object]]:
|
|
224
|
-
# TODO handle inheritence (allOf and refs); need to inline here...
|
|
225
|
-
# for now skip this endpoint
|
|
226
|
-
|
|
227
|
-
return {}
|
|
228
|
-
|
|
229
215
|
|
|
230
216
|
class OpenAPIIntersectionType(OpenAPIType):
|
|
231
217
|
"""
|
|
@@ -246,9 +232,3 @@ class OpenAPIIntersectionType(OpenAPIType):
|
|
|
246
232
|
def asdict(self) -> dict[str, object]:
|
|
247
233
|
# TODO: use parents description and nullable
|
|
248
234
|
return {"allOf": [base_type.asdict() for base_type in self.base_types]}
|
|
249
|
-
|
|
250
|
-
def asarguments(self) -> dict[str, dict[str, object]]:
|
|
251
|
-
# TODO handle inheritence (allOf and refs); need to inline here...
|
|
252
|
-
# for now skip this endpoint
|
|
253
|
-
|
|
254
|
-
return {}
|