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.

Files changed (55) hide show
  1. {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.22.dist-info}/METADATA +3 -1
  2. {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.22.dist-info}/RECORD +55 -34
  3. examples/async_batch.py +36 -0
  4. examples/upload_files.py +19 -0
  5. pkgs/type_spec/actions_registry/__main__.py +35 -23
  6. pkgs/type_spec/actions_registry/emit_typescript.py +71 -9
  7. pkgs/type_spec/builder.py +125 -8
  8. pkgs/type_spec/config.py +1 -0
  9. pkgs/type_spec/emit_open_api.py +197 -16
  10. pkgs/type_spec/emit_open_api_util.py +18 -0
  11. pkgs/type_spec/emit_python.py +241 -55
  12. pkgs/type_spec/load_types.py +48 -5
  13. pkgs/type_spec/open_api_util.py +13 -33
  14. pkgs/type_spec/type_info/emit_type_info.py +129 -8
  15. type_spec/external/api/entity/create_entities.yaml +13 -1
  16. type_spec/external/api/entity/create_entity.yaml +13 -1
  17. type_spec/external/api/entity/transition_entity_phase.yaml +44 -0
  18. type_spec/external/api/permissions/set_core_permissions.yaml +69 -0
  19. type_spec/external/api/recipes/associate_recipe_as_input.yaml +4 -4
  20. type_spec/external/api/recipes/create_recipe.yaml +2 -1
  21. type_spec/external/api/recipes/disassociate_recipe_as_input.yaml +16 -0
  22. type_spec/external/api/recipes/edit_recipe_inputs.yaml +86 -0
  23. type_spec/external/api/recipes/get_curve.yaml +4 -1
  24. type_spec/external/api/recipes/get_recipes_data.yaml +6 -0
  25. type_spec/external/api/recipes/set_recipe_metadata.yaml +1 -0
  26. type_spec/external/api/recipes/set_recipe_tags.yaml +62 -0
  27. uncountable/core/__init__.py +3 -1
  28. uncountable/core/async_batch.py +22 -0
  29. uncountable/core/client.py +84 -10
  30. uncountable/core/file_upload.py +95 -0
  31. uncountable/core/types.py +22 -0
  32. uncountable/types/__init__.py +18 -0
  33. uncountable/types/api/entity/create_entities.py +1 -1
  34. uncountable/types/api/entity/create_entity.py +1 -1
  35. uncountable/types/api/entity/transition_entity_phase.py +66 -0
  36. uncountable/types/api/permissions/__init__.py +1 -0
  37. uncountable/types/api/permissions/set_core_permissions.py +89 -0
  38. uncountable/types/api/recipes/associate_recipe_as_input.py +4 -3
  39. uncountable/types/api/recipes/create_recipe.py +1 -1
  40. uncountable/types/api/recipes/disassociate_recipe_as_input.py +35 -0
  41. uncountable/types/api/recipes/edit_recipe_inputs.py +106 -0
  42. uncountable/types/api/recipes/get_curve.py +2 -1
  43. uncountable/types/api/recipes/get_recipes_data.py +2 -0
  44. uncountable/types/api/recipes/set_recipe_tags.py +91 -0
  45. uncountable/types/async_batch.py +10 -0
  46. uncountable/types/async_batch_processor.py +154 -0
  47. uncountable/types/client_base.py +113 -48
  48. uncountable/types/identifier.py +3 -3
  49. uncountable/types/permissions.py +46 -0
  50. uncountable/types/post_base.py +30 -0
  51. uncountable/types/recipe_inputs.py +30 -0
  52. uncountable/types/recipe_metadata.py +2 -0
  53. uncountable/types/recipe_workflow_steps.py +77 -0
  54. {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.22.dist-info}/WHEEL +0 -0
  55. {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.22.dist-info}/top_level.txt +0 -0
@@ -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
- arguments_type: builder.SpecTypeDefnObject,
368
+ properties: list[builder.SpecProperty],
357
369
  ) -> None:
358
- has_argument_desc = arguments_type.properties is not None and any(
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 arguments_type.properties is not None and has_argument_desc:
374
- for prop in arguments_type.properties.values():
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 _emit_endpoint_invocation_function(
382
- ctx: Context, namespace: builder.SpecNamespace
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
- endpoint = namespace.endpoint
385
- if endpoint is None:
386
- return
387
- if not endpoint.is_sdk:
388
- return
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 has_arguments:
411
+ if len(all_arguments) > 0:
418
412
  ctx.out.write(f"{INDENT}{INDENT}*,\n")
419
- _emit_type_properties(
413
+ _emit_properties(
420
414
  ctx=ctx,
421
- stype=arguments_type,
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
- _emit_endpoint_invocation_docstring(
430
- ctx=ctx,
431
- endpoint=endpoint,
432
- arguments_type=arguments_type,
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
- ctx.out.write(f"{INDENT}{INDENT}args = {refer_to(ctx=ctx, stype=arguments_type)}(")
436
- if has_arguments:
437
- assert arguments_type.properties is not None
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 arguments_type.properties.values():
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 stype.properties is not None and len(stype.properties) > 0:
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 stype.properties.values():
690
+ for prop in properties:
563
691
  if prop.extant == builder.PropertyExtant.required:
564
692
  write_field(prop)
565
- for prop in stype.properties.values():
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("client_class"))
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=builder.SpecNamespace("client_base"),
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()
@@ -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
- root_folder: str, name_regex: str, handler: Callable[[str, str], None]
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(root_folder, name_regex=name_regex, relative=True):
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, name_regex=".*\\.yaml", handler=builder_prescan_file
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
 
@@ -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 {}