UncountablePythonSDK 0.0.192__py3-none-any.whl → 0.0.194__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- examples/integration-server/pyproject.toml +1 -1
- pkgs/argument_parser/__init__.py +2 -0
- pkgs/type_spec/builder.py +98 -10
- pkgs/type_spec/builder_types.py +0 -6
- pkgs/type_spec/emit_open_api.py +50 -33
- pkgs/type_spec/emit_open_api_util.py +1 -0
- pkgs/type_spec/emit_python.py +159 -194
- pkgs/type_spec/emit_typescript.py +3 -2
- pkgs/type_spec/emit_typescript_util.py +8 -0
- pkgs/type_spec/python/__init__.py +14 -0
- pkgs/type_spec/python/dataclass_fields.py +9 -0
- pkgs/type_spec/python/names.py +33 -0
- pkgs/type_spec/python/serial_class_options.py +45 -0
- pkgs/type_spec/type_info/emit_type_info.py +1 -1
- pkgs/type_spec/value_spec/emit_python.py +2 -2
- uncountable/core/client.py +3 -3
- uncountable/core/types.py +4 -4
- uncountable/integration/queue_runner/command_handlers.py +144 -0
- uncountable/integration/queue_runner/job_dispatcher.py +44 -0
- uncountable/integration/queue_runner/job_scheduler.py +51 -172
- uncountable/integration/queue_runner/queue_runner.py +5 -1
- uncountable/types/__init__.py +4 -0
- uncountable/types/api/files/download_file.py +18 -2
- uncountable/types/api/notebooks/add_notebook_content.py +1 -1
- uncountable/types/api/notebooks/clear_notebook_content.py +83 -0
- uncountable/types/api/recipes/remove_recipe_workflow_step.py +61 -0
- uncountable/types/async_batch_processor.py +73 -0
- uncountable/types/async_batch_t.py +2 -0
- uncountable/types/client_base.py +51 -2
- uncountable/types/entity_t.py +4 -0
- {uncountablepythonsdk-0.0.192.dist-info → uncountablepythonsdk-0.0.194.dist-info}/METADATA +2 -2
- {uncountablepythonsdk-0.0.192.dist-info → uncountablepythonsdk-0.0.194.dist-info}/RECORD +34 -26
- {uncountablepythonsdk-0.0.192.dist-info → uncountablepythonsdk-0.0.194.dist-info}/WHEEL +1 -1
- {uncountablepythonsdk-0.0.192.dist-info → uncountablepythonsdk-0.0.194.dist-info}/top_level.txt +0 -0
pkgs/argument_parser/__init__.py
CHANGED
|
@@ -19,6 +19,7 @@ __all__ = [
|
|
|
19
19
|
"is_string_enum_class",
|
|
20
20
|
"is_union",
|
|
21
21
|
"kebab_to_pascal_case",
|
|
22
|
+
"parse_fallback_legacy_type",
|
|
22
23
|
"resolve_serialized_field_name",
|
|
23
24
|
"snake_to_camel_case",
|
|
24
25
|
]
|
|
@@ -36,6 +37,7 @@ from .case_convert import (
|
|
|
36
37
|
from .field_name_resolution import (
|
|
37
38
|
resolve_serialized_field_name as resolve_serialized_field_name,
|
|
38
39
|
)
|
|
40
|
+
from .parse_fallback import parse_fallback_legacy_type as parse_fallback_legacy_type
|
|
39
41
|
from .parser_base import ParserBase as ParserBase
|
|
40
42
|
from .parser_builder import build_parser as build_parser
|
|
41
43
|
from .parser_error import (
|
pkgs/type_spec/builder.py
CHANGED
|
@@ -17,7 +17,7 @@ import regex
|
|
|
17
17
|
from pkgs.serialization import Usage
|
|
18
18
|
|
|
19
19
|
from . import util
|
|
20
|
-
from .builder_types import CrossOutputPaths
|
|
20
|
+
from .builder_types import CrossOutputPaths
|
|
21
21
|
from .non_discriminated_union_exceptions import NON_DISCRIMINATED_UNION_EXCEPTIONS
|
|
22
22
|
from .util import parse_type_str
|
|
23
23
|
|
|
@@ -83,6 +83,7 @@ class SpecProperty:
|
|
|
83
83
|
has_default: bool = False
|
|
84
84
|
# Requires this value in parsing, even if it has a default
|
|
85
85
|
parse_require: bool = False
|
|
86
|
+
sensitive: bool = False
|
|
86
87
|
desc: str | None = None
|
|
87
88
|
# Holds extra information that will be emitted along with type_info. The builder knows nothing
|
|
88
89
|
# about the contents of this information.
|
|
@@ -103,6 +104,11 @@ class PropertyNameMode(StrEnum):
|
|
|
103
104
|
deprecated_non_snake_case = "deprecated_non_snake_case"
|
|
104
105
|
|
|
105
106
|
|
|
107
|
+
class StringEnumEmitMode(StrEnum):
|
|
108
|
+
STANDARD = "standard"
|
|
109
|
+
SOURCE_ENUM_ALIAS = "source_enum_alias"
|
|
110
|
+
|
|
111
|
+
|
|
106
112
|
class BaseTypeName(StrEnum):
|
|
107
113
|
"""
|
|
108
114
|
Base types that are supported.
|
|
@@ -412,6 +418,7 @@ class SpecTypeDefn(SpecType):
|
|
|
412
418
|
"extant",
|
|
413
419
|
"label",
|
|
414
420
|
"name_case",
|
|
421
|
+
"sensitive",
|
|
415
422
|
"type",
|
|
416
423
|
],
|
|
417
424
|
)
|
|
@@ -494,6 +501,11 @@ class SpecTypeDefn(SpecType):
|
|
|
494
501
|
|
|
495
502
|
ext_info = data.get("ext_info")
|
|
496
503
|
label = data.get("label")
|
|
504
|
+
sensitive = data.get("sensitive", False)
|
|
505
|
+
builder.ensure(
|
|
506
|
+
isinstance(sensitive, bool),
|
|
507
|
+
f"sensitive must be a boolean for property {name}",
|
|
508
|
+
)
|
|
497
509
|
|
|
498
510
|
return SpecProperty(
|
|
499
511
|
name=name,
|
|
@@ -506,6 +518,7 @@ class SpecTypeDefn(SpecType):
|
|
|
506
518
|
has_default=has_default,
|
|
507
519
|
default=default,
|
|
508
520
|
parse_require=parse_require,
|
|
521
|
+
sensitive=sensitive,
|
|
509
522
|
desc=data.get("desc", None),
|
|
510
523
|
ext_info=ext_info,
|
|
511
524
|
explicit_default=explicit_default,
|
|
@@ -527,6 +540,13 @@ class SpecTypeDefn(SpecType):
|
|
|
527
540
|
return self.namespace.usage
|
|
528
541
|
|
|
529
542
|
|
|
543
|
+
@dataclass(kw_only=True, frozen=True)
|
|
544
|
+
class PythonParseFallback:
|
|
545
|
+
legacy_property: str
|
|
546
|
+
convert_namespace: SpecNamespace
|
|
547
|
+
convert: str
|
|
548
|
+
|
|
549
|
+
|
|
530
550
|
class SpecTypeGenericParameter(SpecType):
|
|
531
551
|
def __init__(
|
|
532
552
|
self,
|
|
@@ -692,13 +712,22 @@ class SpecTypeDefnObject(SpecTypeDefn):
|
|
|
692
712
|
isinstance(legacy_property, str) and isinstance(convert, str),
|
|
693
713
|
f"python_parse_fallbacks: {property_name} legacy_property and convert must be strings",
|
|
694
714
|
)
|
|
715
|
+
convert_namespace_path, _, convert_function = convert.rpartition(".")
|
|
716
|
+
convert_namespace = builder.namespaces.get(convert_namespace_path)
|
|
717
|
+
builder.ensure(
|
|
718
|
+
convert_namespace is not None,
|
|
719
|
+
f"python_parse_fallbacks: {property_name} convert must be <namespace>.<function> with a known namespace, got {convert}",
|
|
720
|
+
)
|
|
721
|
+
assert convert_namespace is not None
|
|
695
722
|
builder.ensure_python_prepart_function(
|
|
696
|
-
|
|
697
|
-
function_name=
|
|
723
|
+
namespace=convert_namespace,
|
|
724
|
+
function_name=convert_function,
|
|
698
725
|
option_name="python_parse_fallbacks",
|
|
699
726
|
)
|
|
700
727
|
parse_fallbacks[property_name] = PythonParseFallback(
|
|
701
|
-
legacy_property=legacy_property,
|
|
728
|
+
legacy_property=legacy_property,
|
|
729
|
+
convert_namespace=convert_namespace,
|
|
730
|
+
convert=convert_function,
|
|
702
731
|
)
|
|
703
732
|
return parse_fallbacks
|
|
704
733
|
|
|
@@ -912,6 +941,8 @@ class SpecTypeDefnStringEnum(SpecTypeDefn):
|
|
|
912
941
|
self.desc: str | None = None
|
|
913
942
|
self.sql_type_name: str | None = None
|
|
914
943
|
self.emit_id_source = False
|
|
944
|
+
self.emit_mode = StringEnumEmitMode.STANDARD
|
|
945
|
+
self.alias_source_enum: SpecTypeDefnStringEnum | None = None
|
|
915
946
|
self.source_enums: list[SpecType] = []
|
|
916
947
|
self.property_name_mode = PropertyNameMode.strict
|
|
917
948
|
|
|
@@ -1036,12 +1067,15 @@ class SpecTypeDefnStringEnum(SpecTypeDefn):
|
|
|
1036
1067
|
|
|
1037
1068
|
emit_data = data.get("emit")
|
|
1038
1069
|
if emit_data is not None:
|
|
1039
|
-
util.check_fields(emit_data, ["id_source"])
|
|
1070
|
+
util.check_fields(emit_data, ["id_source", "mode"])
|
|
1040
1071
|
emit_id_source = emit_data.get("id_source", False)
|
|
1041
1072
|
assert isinstance(emit_id_source, bool)
|
|
1042
1073
|
self.emit_id_source = emit_id_source
|
|
1043
1074
|
if emit_id_source:
|
|
1044
1075
|
builder.emit_id_source_enums.add(self)
|
|
1076
|
+
self.emit_mode = StringEnumEmitMode(
|
|
1077
|
+
emit_data.get("mode", StringEnumEmitMode.STANDARD.value)
|
|
1078
|
+
)
|
|
1045
1079
|
|
|
1046
1080
|
if self.emit_id_source:
|
|
1047
1081
|
assert len(self.namespace.path) == 1
|
|
@@ -1062,10 +1096,41 @@ class SpecTypeDefnStringEnum(SpecTypeDefn):
|
|
|
1062
1096
|
for name in self.values:
|
|
1063
1097
|
_validate_enum_name(name)
|
|
1064
1098
|
|
|
1099
|
+
if self.emit_mode is StringEnumEmitMode.SOURCE_ENUM_ALIAS:
|
|
1100
|
+
builder.ensure(
|
|
1101
|
+
len(self.source_enums) == 1,
|
|
1102
|
+
"source enum alias must reference one StringEnum",
|
|
1103
|
+
)
|
|
1104
|
+
source_enum = self.source_enums[0]
|
|
1105
|
+
assert isinstance(source_enum, SpecTypeDefnStringEnum)
|
|
1106
|
+
self.alias_source_enum = source_enum
|
|
1107
|
+
builder.ensure(
|
|
1108
|
+
self.values == source_enum.values,
|
|
1109
|
+
"source enum alias must preserve all source values",
|
|
1110
|
+
)
|
|
1111
|
+
builder.ensure(
|
|
1112
|
+
data_values is None,
|
|
1113
|
+
"source enum alias cannot define local values",
|
|
1114
|
+
)
|
|
1115
|
+
builder.ensure(
|
|
1116
|
+
self.sql_type_name is None,
|
|
1117
|
+
"source enum alias cannot define a SQL type",
|
|
1118
|
+
)
|
|
1119
|
+
|
|
1065
1120
|
def get_referenced_types(self) -> list[SpecType]:
|
|
1066
1121
|
return self.source_enums
|
|
1067
1122
|
|
|
1068
1123
|
|
|
1124
|
+
SpecTypeDefnVariant = (
|
|
1125
|
+
SpecTypeDefnAlias
|
|
1126
|
+
| SpecTypeDefnExternal
|
|
1127
|
+
| SpecTypeDefnNewType
|
|
1128
|
+
| SpecTypeDefnObject
|
|
1129
|
+
| SpecTypeDefnStringEnum
|
|
1130
|
+
| SpecTypeDefnUnion
|
|
1131
|
+
)
|
|
1132
|
+
|
|
1133
|
+
|
|
1069
1134
|
TOKEN_ENDPOINT = "$endpoint"
|
|
1070
1135
|
TOKEN_EMIT_IO_TS = "$emit_io_ts"
|
|
1071
1136
|
TOKEN_EMIT_TYPE_INFO = "$emit_type_info"
|
|
@@ -1171,6 +1236,7 @@ def parse_endpoint_specific_path(
|
|
|
1171
1236
|
|
|
1172
1237
|
class SpecEndpoint:
|
|
1173
1238
|
method: RouteMethod
|
|
1239
|
+
alternate_methods: list[RouteMethod]
|
|
1174
1240
|
data_loader: bool
|
|
1175
1241
|
is_sdk: EndpointEmitType
|
|
1176
1242
|
stability_level: StabilityLevel | None
|
|
@@ -1199,6 +1265,7 @@ class SpecEndpoint:
|
|
|
1199
1265
|
data,
|
|
1200
1266
|
[
|
|
1201
1267
|
"method",
|
|
1268
|
+
"alternate_methods",
|
|
1202
1269
|
"path",
|
|
1203
1270
|
"data_loader",
|
|
1204
1271
|
"deprecated",
|
|
@@ -1219,6 +1286,26 @@ class SpecEndpoint:
|
|
|
1219
1286
|
)
|
|
1220
1287
|
self.method = RouteMethod(data["method"])
|
|
1221
1288
|
|
|
1289
|
+
alternate_methods_raw = data.get("alternate_methods", [])
|
|
1290
|
+
assert isinstance(alternate_methods_raw, list)
|
|
1291
|
+
self.alternate_methods = [
|
|
1292
|
+
RouteMethod(alternate_method) for alternate_method in alternate_methods_raw
|
|
1293
|
+
]
|
|
1294
|
+
assert self.method not in self.alternate_methods, (
|
|
1295
|
+
"alternate-methods-must-not-repeat-method"
|
|
1296
|
+
)
|
|
1297
|
+
assert len(set(self.alternate_methods)) == len(self.alternate_methods), (
|
|
1298
|
+
"alternate-methods-must-be-unique"
|
|
1299
|
+
)
|
|
1300
|
+
if self.alternate_methods:
|
|
1301
|
+
assert self.method == RouteMethod.get, (
|
|
1302
|
+
"alternate-methods-require-get-primary-method"
|
|
1303
|
+
)
|
|
1304
|
+
assert all(
|
|
1305
|
+
alternate_method == RouteMethod.post
|
|
1306
|
+
for alternate_method in self.alternate_methods
|
|
1307
|
+
), "alternate-methods-must-be-post"
|
|
1308
|
+
|
|
1222
1309
|
data_loader = data.get("data_loader", False)
|
|
1223
1310
|
assert isinstance(data_loader, bool)
|
|
1224
1311
|
self.data_loader = data_loader
|
|
@@ -1446,7 +1533,7 @@ class SpecNamespace:
|
|
|
1446
1533
|
self,
|
|
1447
1534
|
name: str,
|
|
1448
1535
|
):
|
|
1449
|
-
self.types: dict[str,
|
|
1536
|
+
self.types: dict[str, SpecTypeDefnVariant] = {}
|
|
1450
1537
|
self.constants: dict[str, SpecConstant] = {}
|
|
1451
1538
|
self.endpoint: SpecEndpoint | None = None
|
|
1452
1539
|
self.emit_io_ts = False
|
|
@@ -1544,7 +1631,7 @@ class SpecNamespace:
|
|
|
1544
1631
|
assert name not in self.types, f"{name} is duplicate"
|
|
1545
1632
|
defn_type = defn.get("type")
|
|
1546
1633
|
assert isinstance(defn_type, str), f"{name} requires a string type"
|
|
1547
|
-
spec_type:
|
|
1634
|
+
spec_type: SpecTypeDefnVariant
|
|
1548
1635
|
named_parameters = (
|
|
1549
1636
|
[
|
|
1550
1637
|
parameter.name
|
|
@@ -1775,13 +1862,14 @@ class SpecBuilder:
|
|
|
1775
1862
|
raise BuilderError()
|
|
1776
1863
|
|
|
1777
1864
|
def ensure_python_prepart_function(
|
|
1778
|
-
self, *,
|
|
1865
|
+
self, *, namespace: SpecNamespace, function_name: str, option_name: str
|
|
1779
1866
|
) -> None:
|
|
1780
|
-
|
|
1867
|
+
namespace_key = ".".join(namespace.path)
|
|
1868
|
+
python_prepart = self.preparts["python"].get(namespace_key, "")
|
|
1781
1869
|
def_prefix = f"def {function_name}"
|
|
1782
1870
|
self.ensure(
|
|
1783
1871
|
f"{def_prefix}(" in python_prepart or f"{def_prefix}[" in python_prepart,
|
|
1784
|
-
f"{option_name}: the {
|
|
1872
|
+
f"{option_name}: the {namespace.name} python prepart file must define {function_name}",
|
|
1785
1873
|
)
|
|
1786
1874
|
|
|
1787
1875
|
def prescan(self, namespace_path: str, data: RawDict) -> None:
|
pkgs/type_spec/builder_types.py
CHANGED
pkgs/type_spec/emit_open_api.py
CHANGED
|
@@ -4,6 +4,7 @@ Generates an OpenAPI specification based on the type_spec definitions
|
|
|
4
4
|
WORK-IN-PROGRESS, DON'T USE!
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import copy
|
|
7
8
|
import dataclasses
|
|
8
9
|
import json
|
|
9
10
|
import re
|
|
@@ -248,12 +249,12 @@ def _emit_endpoint_parameter_examples(
|
|
|
248
249
|
|
|
249
250
|
|
|
250
251
|
def _emit_endpoint_parameters(
|
|
251
|
-
|
|
252
|
+
method: str,
|
|
252
253
|
argument_type: OpenAPIType | None,
|
|
253
254
|
examples: list[EmitOpenAPIEndpointExample],
|
|
254
255
|
) -> DictApiSchema:
|
|
255
256
|
if (
|
|
256
|
-
|
|
257
|
+
method.lower() not in _QUERY_PARM_METHODS
|
|
257
258
|
or argument_type is None
|
|
258
259
|
or _is_empty_object_type(argument_type)
|
|
259
260
|
):
|
|
@@ -307,12 +308,12 @@ def _emit_stability_level(
|
|
|
307
308
|
|
|
308
309
|
|
|
309
310
|
def _emit_endpoint_request_body(
|
|
310
|
-
|
|
311
|
+
method: str,
|
|
311
312
|
arguments_type: OpenAPIType | None,
|
|
312
313
|
examples: list[EmitOpenAPIEndpointExample],
|
|
313
314
|
) -> DictApiSchema:
|
|
314
315
|
if (
|
|
315
|
-
|
|
316
|
+
method.lower() not in _REQUEST_BODY_METHODS
|
|
316
317
|
or arguments_type is None
|
|
317
318
|
or _is_empty_object_type(arguments_type)
|
|
318
319
|
):
|
|
@@ -406,6 +407,40 @@ def _emit_endpoint_description(
|
|
|
406
407
|
return {"description": full_description}
|
|
407
408
|
|
|
408
409
|
|
|
410
|
+
def _emit_endpoint_operation(
|
|
411
|
+
*,
|
|
412
|
+
endpoint: EmitOpenAPIEndpoint,
|
|
413
|
+
method: str,
|
|
414
|
+
argument_type: OpenAPIType | None,
|
|
415
|
+
) -> DictApiSchema:
|
|
416
|
+
return (
|
|
417
|
+
{
|
|
418
|
+
"tags": endpoint.tags,
|
|
419
|
+
"summary": endpoint.summary,
|
|
420
|
+
}
|
|
421
|
+
| _emit_endpoint_deprecated(endpoint.deprecated)
|
|
422
|
+
| _emit_endpoint_description(
|
|
423
|
+
endpoint.description, endpoint.guides, endpoint.stability_level
|
|
424
|
+
)
|
|
425
|
+
| _emit_stability_level(endpoint.stability_level)
|
|
426
|
+
| _emit_endpoint_parameters(method, argument_type, endpoint.examples)
|
|
427
|
+
| _emit_endpoint_request_body(method, argument_type, endpoint.examples)
|
|
428
|
+
| {
|
|
429
|
+
"responses": {
|
|
430
|
+
"200": {
|
|
431
|
+
"description": "OK",
|
|
432
|
+
"content": {
|
|
433
|
+
"application/json": {
|
|
434
|
+
"schema": {"$ref": "#/components/schema/Data"}
|
|
435
|
+
}
|
|
436
|
+
| _emit_endpoint_response_examples(endpoint.examples)
|
|
437
|
+
},
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
}
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
|
|
409
444
|
def _emit_namespace(
|
|
410
445
|
gctx: EmitOpenAPIGlobalContext,
|
|
411
446
|
ctx: EmitOpenAPIContext,
|
|
@@ -432,36 +467,14 @@ def _emit_namespace(
|
|
|
432
467
|
if ctx.endpoint is not None:
|
|
433
468
|
endpoint = ctx.endpoint
|
|
434
469
|
argument_type = ctx.types.get(ARGUMENTS_TYPE_NAME)
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
| _emit_endpoint_deprecated(endpoint.deprecated)
|
|
442
|
-
| _emit_endpoint_description(
|
|
443
|
-
endpoint.description, ctx.endpoint.guides, endpoint.stability_level
|
|
444
|
-
)
|
|
445
|
-
| _emit_stability_level(endpoint.stability_level)
|
|
446
|
-
| _emit_endpoint_parameters(endpoint, argument_type, ctx.endpoint.examples)
|
|
447
|
-
| _emit_endpoint_request_body(
|
|
448
|
-
endpoint, argument_type, ctx.endpoint.examples
|
|
470
|
+
oa_components["endpoint"] = {
|
|
471
|
+
method: copy.deepcopy(
|
|
472
|
+
_emit_endpoint_operation(
|
|
473
|
+
endpoint=endpoint, method=method, argument_type=argument_type
|
|
474
|
+
)
|
|
449
475
|
)
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
"200": {
|
|
453
|
-
"description": "OK",
|
|
454
|
-
"content": {
|
|
455
|
-
"application/json": {
|
|
456
|
-
"schema": {"$ref": "#/components/schema/Data"}
|
|
457
|
-
}
|
|
458
|
-
| _emit_endpoint_response_examples(ctx.endpoint.examples)
|
|
459
|
-
},
|
|
460
|
-
}
|
|
461
|
-
},
|
|
462
|
-
}
|
|
463
|
-
)
|
|
464
|
-
oa_components["endpoint"] = oa_endpoint
|
|
476
|
+
for method in [endpoint.method, *endpoint.alternate_methods]
|
|
477
|
+
}
|
|
465
478
|
|
|
466
479
|
types = ctx.types
|
|
467
480
|
if ctx.endpoint is not None:
|
|
@@ -682,6 +695,10 @@ def _emit_endpoint(
|
|
|
682
695
|
|
|
683
696
|
ctx.endpoint = EmitOpenAPIEndpoint(
|
|
684
697
|
method=namespace.endpoint.method.lower(),
|
|
698
|
+
alternate_methods=[
|
|
699
|
+
alternate_method.lower()
|
|
700
|
+
for alternate_method in namespace.endpoint.alternate_methods
|
|
701
|
+
],
|
|
685
702
|
tags=[tag_name],
|
|
686
703
|
summary=f"{'/'.join(namespace.path[path_cutoff:])}",
|
|
687
704
|
description=description,
|