ssc_codegen 0.41.0__tar.gz → 0.42.0__tar.gz

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.
Files changed (97) hide show
  1. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/PKG-INFO +1 -1
  2. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/pyproject.toml +1 -1
  3. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/naming.py +17 -0
  4. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/javascript/rest.py +31 -105
  5. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/javascript/visitor.py +32 -58
  6. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/python/rest.py +29 -98
  7. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/python/visitor.py +114 -130
  8. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/traversal/utils.py +17 -23
  9. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/.gitignore +0 -0
  10. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/LICENSE +0 -0
  11. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/README.md +0 -0
  12. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/__init__.py +0 -0
  13. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/_logging.py +0 -0
  14. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/__init__.py +0 -0
  15. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/array.py +0 -0
  16. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/base.py +0 -0
  17. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/cast.py +0 -0
  18. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/control.py +0 -0
  19. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/extension.py +0 -0
  20. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/extract.py +0 -0
  21. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/function.py +0 -0
  22. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/jsondef.py +0 -0
  23. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/module.py +0 -0
  24. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/predicate_containers.py +0 -0
  25. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/predicate_ops.py +0 -0
  26. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/regex.py +0 -0
  27. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/rest.py +0 -0
  28. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/selectors.py +0 -0
  29. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/string.py +0 -0
  30. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/struct.py +0 -0
  31. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/typedef.py +0 -0
  32. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/ast/types.py +0 -0
  33. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/core/__init__.py +0 -0
  34. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/core/contexts.py +0 -0
  35. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/core/expressions.py +0 -0
  36. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/core/extensions.py +0 -0
  37. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/core/format.py +0 -0
  38. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/core/imports.py +0 -0
  39. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/core/linter.py +0 -0
  40. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/core/module_handler.py +0 -0
  41. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/core/predicates.py +0 -0
  42. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/core/reader.py +0 -0
  43. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/core/rest_artifacts.py +0 -0
  44. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/core/struct_parser.py +0 -0
  45. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/core/type_checking.py +0 -0
  46. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/exceptions.py +0 -0
  47. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/explore.py +0 -0
  48. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/generation/__init__.py +0 -0
  49. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/generation/builder.py +0 -0
  50. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/generation/runtime.py +0 -0
  51. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/health.py +0 -0
  52. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/json_to_kdl.py +0 -0
  53. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/main.py +0 -0
  54. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/parsers/__init__.py +0 -0
  55. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/parsers/curl.py +0 -0
  56. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/parsers/http.py +0 -0
  57. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/regex_utils.py +0 -0
  58. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/request_spec.py +0 -0
  59. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/symbols.py +0 -0
  60. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/__init__.py +0 -0
  61. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/golang/__init__.py +0 -0
  62. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/golang/http_libs/__init__.py +0 -0
  63. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/golang/http_libs/base.py +0 -0
  64. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/golang/http_libs/nethttp.py +0 -0
  65. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/golang/literals.py +0 -0
  66. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/golang/regex.py +0 -0
  67. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/golang/rest.py +0 -0
  68. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/golang/runtime.py +0 -0
  69. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/golang/visitor.py +0 -0
  70. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/javascript/__init__.py +0 -0
  71. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/javascript/http_libs/__init__.py +0 -0
  72. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/javascript/http_libs/axios.py +0 -0
  73. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/javascript/http_libs/base.py +0 -0
  74. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/javascript/http_libs/fetch.py +0 -0
  75. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/profile.py +0 -0
  76. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/python/__init__.py +0 -0
  77. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/python/html_libs/__init__.py +0 -0
  78. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/python/html_libs/base.py +0 -0
  79. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/python/html_libs/bs4.py +0 -0
  80. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/python/html_libs/lxml.py +0 -0
  81. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/python/html_libs/parsel.py +0 -0
  82. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/python/html_libs/slax.py +0 -0
  83. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/python/http_libs/__init__.py +0 -0
  84. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/python/http_libs/aiohttp.py +0 -0
  85. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/python/http_libs/base.py +0 -0
  86. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/python/http_libs/httpx.py +0 -0
  87. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/python/http_libs/requests.py +0 -0
  88. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/resolver.py +0 -0
  89. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/rust/__init__.py +0 -0
  90. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/rust/regex.py +0 -0
  91. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/rust/rest.py +0 -0
  92. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/rust/runtime.py +0 -0
  93. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/rust/visitor.py +0 -0
  94. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/targets/spec.py +0 -0
  95. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/traversal/__init__.py +0 -0
  96. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/traversal/context.py +0 -0
  97. {ssc_codegen-0.41.0 → ssc_codegen-0.42.0}/ssc_codegen/traversal/walker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ssc_codegen
3
- Version: 0.41.0
3
+ Version: 0.42.0
4
4
  Summary: Python-dsl code converter to html parser for web scraping
5
5
  Project-URL: Documentation, https://github.com/vypivshiy/selector_schema_codegen#readme
6
6
  Project-URL: Issues, https://github.com/vypivshiy/selector_schema_codegen/issues
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ssc_codegen"
3
- version = "0.41.0"
3
+ version = "0.42.0"
4
4
  description = "Python-dsl code converter to html parser for web scraping "
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -22,6 +22,23 @@ def to_snake_case(s: str) -> str:
22
22
  return s.lower()
23
23
 
24
24
 
25
+ def json_descriptor_var_name(name: str) -> str:
26
+ """Format canonical module-level JSON descriptor constant identifier.
27
+
28
+ Args:
29
+ name: Struct, type, or response schema name to format.
30
+
31
+ Returns:
32
+ Canonical uppercase identifier prefixed with ``JSON_DESCRIPTOR_``.
33
+
34
+ Examples:
35
+ 'Episode' -> 'JSON_DESCRIPTOR_EPISODE'
36
+ 'CatalogReleasesResponse' -> 'JSON_DESCRIPTOR_CATALOG_RELEASES_RESPONSE'
37
+ """
38
+ clean_name = to_snake_case(name).lstrip("_").upper()
39
+ return f"JSON_DESCRIPTOR_{clean_name}"
40
+
41
+
25
42
  def to_pascal_case(s: str) -> str:
26
43
  """
27
44
  'my_field_name' -> 'MyFieldName'
@@ -8,14 +8,12 @@ Called by JsVisitor via thin delegate methods. The HTTP library strategy
8
8
  from __future__ import annotations
9
9
 
10
10
  import json
11
- from typing import Mapping
12
11
 
13
12
  from ssc_codegen.ast import (
14
13
  JsonDef,
15
14
  MatcherListDef,
16
15
  MethodFetch,
17
16
  MethodRest,
18
- Module,
19
17
  PlaceholderSpec,
20
18
  PlaceholderTemplate,
21
19
  ResultAliasDef,
@@ -23,18 +21,21 @@ from ssc_codegen.ast import (
23
21
  StructBase,
24
22
  )
25
23
  from ssc_codegen.ast.struct import RequestHttp
26
- from ssc_codegen.naming import to_camel_case, to_pascal_case, to_snake_case
24
+ from ssc_codegen.naming import (
25
+ json_descriptor_var_name,
26
+ to_camel_case,
27
+ to_pascal_case,
28
+ to_snake_case,
29
+ )
27
30
  from ssc_codegen.request_spec import parse_json_template
28
31
  from ssc_codegen.targets.javascript.http_libs.base import JsHttpLibStrategy
29
32
  from ssc_codegen.traversal.context import WalkContext
30
33
  from ssc_codegen.traversal.utils import (
31
34
  dict_needs_builder,
32
35
  err_subclass_name,
33
- json_def_descriptors,
34
- json_def_mapping,
36
+ find_enclosing_module,
35
37
  )
36
38
 
37
-
38
39
  # ===========================================================================
39
40
  # REST shared runtime source (Ok/Err/sscDispatchErr/etc.)
40
41
  # ===========================================================================
@@ -287,8 +288,7 @@ def js_name(name: str) -> str:
287
288
  def ok_payload_type(node: MethodRest) -> str:
288
289
  if not node.response_schema:
289
290
  return "null"
290
- struct = node.parent
291
- module = struct.parent if struct is not None else None
291
+ module = find_enclosing_module(node)
292
292
  schema_type = f"{to_pascal_case(node.response_schema)}Json"
293
293
  if module is not None:
294
294
  for n in module.body:
@@ -376,21 +376,28 @@ def emit_method_rest(
376
376
  )
377
377
 
378
378
  fn_name = http.fn_name
379
- response_desc = _response_descriptors(node)
379
+ module = find_enclosing_module(node)
380
+ has_response_schema = False
381
+ if node.response_schema and module is not None:
382
+ has_response_schema = any(
383
+ isinstance(n, JsonDef) and n.name == node.response_schema
384
+ for n in module.body
385
+ )
386
+
380
387
  if node.response_path:
381
388
  accessor = "".join(
382
389
  f"[{json.dumps(p)}]" for p in node.response_path.split(".")
383
390
  )
384
- if response_desc is None:
385
- value_fn = f"(_b) => _b{accessor}"
391
+ if has_response_schema:
392
+ desc_var = json_descriptor_var_name(node.response_schema)
393
+ value_fn = f"(_b) => sscJsonProject(_b{accessor}, {desc_var})"
386
394
  else:
387
- value_fn = f"(_b) => sscJsonProject(_b{accessor}, {_render_descriptors(response_desc)})"
395
+ value_fn = f"(_b) => _b{accessor}"
388
396
  elif not node.response_schema:
389
397
  value_fn = "(_b) => null"
390
- elif response_desc is not None:
391
- value_fn = (
392
- f"(_b) => sscJsonProject(_b, {_render_descriptors(response_desc)})"
393
- )
398
+ elif has_response_schema:
399
+ desc_var = json_descriptor_var_name(node.response_schema)
400
+ value_fn = f"(_b) => sscJsonProject(_b, {desc_var})"
394
401
  else:
395
402
  value_fn = "null"
396
403
 
@@ -466,82 +473,6 @@ def emit_method_rest(
466
473
  return lines
467
474
 
468
475
 
469
- def _response_descriptors(
470
- node: MethodRest,
471
- ) -> dict[str, tuple[str, bool, bool, object]] | None:
472
- if not node.response_schema:
473
- return None
474
- module = node.parent.parent if node.parent is not None else None
475
- if not isinstance(module, Module):
476
- return None
477
- definitions = {n.name: n for n in module.body if isinstance(n, JsonDef)}
478
- definition = definitions.get(node.response_schema)
479
- if definition is None:
480
- return None
481
- return json_def_descriptors(definition, definitions)
482
-
483
-
484
- def _render_descriptors(descriptors: Mapping[str, object]) -> str:
485
- def render(value: object) -> str:
486
- if value is None:
487
- return "null"
488
- if isinstance(value, bool):
489
- return "true" if value else "false"
490
- if isinstance(value, str):
491
- return json.dumps(value)
492
- if isinstance(value, tuple):
493
- return "[" + ", ".join(render(x) for x in value) + "]"
494
- if isinstance(value, list):
495
- return "[" + ", ".join(render(item) for item in value) + "]"
496
- if isinstance(value, dict):
497
- return (
498
- "{"
499
- + ", ".join(
500
- f"{json.dumps(key)}: {render(item)}"
501
- for key, item in value.items()
502
- )
503
- + "}"
504
- )
505
- raise TypeError(f"unsupported JSON descriptor value: {value!r}")
506
-
507
- return render(descriptors)
508
-
509
-
510
- def _response_mapping(node: MethodRest) -> dict[str, object] | None:
511
- if not node.response_schema:
512
- return None
513
- module = node.parent.parent if node.parent is not None else None
514
- if not isinstance(module, Module):
515
- return None
516
- definitions = {n.name: n for n in module.body if isinstance(n, JsonDef)}
517
- definition = definitions.get(node.response_schema)
518
- if definition is None or not definition.has_alias_key:
519
- return None
520
- return json_def_mapping(definition, definitions)
521
-
522
-
523
- def _render_mapping(mapping: dict[str, object]) -> str:
524
- def render(value: object) -> str:
525
- if isinstance(value, str):
526
- return json.dumps(value)
527
- if isinstance(value, tuple):
528
- return "[" + json.dumps(value[0]) + ", " + render(value[1]) + "]"
529
- if isinstance(value, list):
530
- return "[" + ", ".join(render(item) for item in value) + "]"
531
- if isinstance(value, dict):
532
- return (
533
- "{"
534
- + ", ".join(
535
- f"{json.dumps(key)}: {render(item)}"
536
- for key, item in value.items()
537
- )
538
- + "}"
539
- )
540
- raise TypeError(f"unsupported JSON mapping value: {value!r}")
541
-
542
- return render(mapping)
543
-
544
-
545
476
  def emit_method_fetch(
546
477
  node: MethodFetch, ctx: WalkContext, http: JsHttpLibStrategy
547
478
  ) -> list[str]:
@@ -679,26 +610,21 @@ def emit_result_alias_def(node: ResultAliasDef) -> list[str]:
679
610
  def emit_matcher_list_def(node: MatcherListDef) -> list[str]:
680
611
  var = f"_{to_snake_case(node.struct_name)}Matchers"
681
612
  lines = [f"const {var} = ["]
682
- module = (
683
- node.parent
684
- if isinstance(node.parent, Module)
685
- else (node.parent.parent if node.parent is not None else None)
686
- )
613
+ module = find_enclosing_module(node)
687
614
  definitions = (
688
615
  {n.name: n for n in module.body if isinstance(n, JsonDef)}
689
- if isinstance(module, Module)
616
+ if module is not None
690
617
  else {}
691
618
  )
692
619
  for e in node.entries:
693
620
  check = render_js_condition_check(e.required_keys, e.conditions)
694
621
  check_arg = check if check else "null"
695
- desc = None
696
- definition = definitions.get(e.error_schema)
697
- if definition is not None:
698
- desc = _render_descriptors(
699
- json_def_descriptors(definition, definitions)
700
- )
701
- value_expr = f"sscJsonProject(_b, {desc})" if desc is not None else "_b"
622
+ desc_var = None
623
+ if e.error_schema and e.error_schema in definitions:
624
+ desc_var = json_descriptor_var_name(e.error_schema)
625
+ value_expr = (
626
+ f"sscJsonProject(_b, {desc_var})" if desc_var is not None else "_b"
627
+ )
702
628
  lines.append(
703
629
  f" {{ status: {e.status}, check: {check_arg}, "
704
630
  f"factory: (_s, _h, _b) => ({{ isOk: false, status: _s, "
@@ -18,8 +18,8 @@ import re as _re
18
18
  from typing import Any
19
19
 
20
20
  from ssc_codegen.ast import (
21
- Attr,
22
21
  Assert,
22
+ Attr,
23
23
  CheckMethod,
24
24
  CodeEndHook,
25
25
  CodeStartHook,
@@ -33,14 +33,14 @@ from ssc_codegen.ast import (
33
33
  Filter,
34
34
  Fmt,
35
35
  FunctionDef,
36
+ Index,
36
37
  Init,
37
38
  InitField,
38
39
  InitFieldCall,
39
- Index,
40
+ Join,
40
41
  JsonDef,
41
42
  JsonDefField,
42
43
  Jsonify,
43
- Join,
44
44
  Key,
45
45
  Len,
46
46
  LogicAnd,
@@ -49,12 +49,12 @@ from ssc_codegen.ast import (
49
49
  Lower,
50
50
  Ltrim,
51
51
  Match,
52
+ MatcherListDef,
52
53
  MethodFetch,
53
54
  MethodRest,
54
55
  Module,
55
56
  Nested,
56
57
  NormalizeSpace,
57
- PreValidate,
58
58
  PredAttrContains,
59
59
  PredAttrEnds,
60
60
  PredAttrEq,
@@ -83,15 +83,15 @@ from ssc_codegen.ast import (
83
83
  PredTextRe,
84
84
  PredTextStarts,
85
85
  PredXpath,
86
+ PreValidate,
86
87
  Raw,
87
88
  Re,
88
89
  ReAll,
89
- ReSub,
90
90
  Repl,
91
91
  ReplMap,
92
+ ReSub,
92
93
  ResultAliasDef,
93
94
  ResultVariantDef,
94
- MatcherListDef,
95
95
  Return,
96
96
  RmPrefix,
97
97
  RmPrefixSuffix,
@@ -105,7 +105,6 @@ from ssc_codegen.ast import (
105
105
  Struct,
106
106
  StructBase,
107
107
  StructRest,
108
- StructType as ST,
109
108
  TableConfig,
110
109
  TableMatchKey,
111
110
  TableRows,
@@ -117,36 +116,44 @@ from ssc_codegen.ast import (
117
116
  TypeDef,
118
117
  TypeDefField,
119
118
  TypeInfo,
120
- Unique,
121
119
  Unescape,
120
+ Unique,
122
121
  Upper,
123
122
  Utilities,
124
123
  Value,
125
- VariableType as VT,
126
124
  XpathRemove,
127
125
  XpathSelect,
128
126
  XpathSelectAll,
129
127
  )
128
+ from ssc_codegen.ast import (
129
+ StructType as ST,
130
+ )
131
+ from ssc_codegen.ast import (
132
+ VariableType as VT,
133
+ )
130
134
  from ssc_codegen.exceptions import BuildTimeError
131
- from ssc_codegen.naming import to_camel_case, to_pascal_case, to_snake_case
135
+ from ssc_codegen.generation.builder import ModuleBuilder
136
+ from ssc_codegen.naming import (
137
+ json_descriptor_var_name,
138
+ to_camel_case,
139
+ to_pascal_case,
140
+ )
141
+ from ssc_codegen.targets.javascript import rest
142
+ from ssc_codegen.targets.javascript.http_libs.axios import AxiosStrategy
143
+ from ssc_codegen.targets.javascript.http_libs.base import JsHttpLibStrategy
144
+ from ssc_codegen.targets.javascript.http_libs.fetch import FetchStrategy
145
+ from ssc_codegen.traversal.context import WalkContext
132
146
  from ssc_codegen.traversal.utils import (
147
+ DescriptorRef,
148
+ find_enclosing_module,
133
149
  find_predicate_container,
134
150
  json_def_descriptors,
135
- json_def_mapping,
136
151
  jsonify_path_to_segments,
137
152
  module_has_rest,
138
153
  resolve_json_def,
139
154
  )
140
-
141
- from ssc_codegen.generation.builder import ModuleBuilder
142
- from ssc_codegen.targets.javascript import rest
143
- from ssc_codegen.targets.javascript.http_libs.axios import AxiosStrategy
144
- from ssc_codegen.targets.javascript.http_libs.base import JsHttpLibStrategy
145
- from ssc_codegen.targets.javascript.http_libs.fetch import FetchStrategy
146
- from ssc_codegen.traversal.context import WalkContext
147
155
  from ssc_codegen.traversal.walker import BaseWalker
148
156
 
149
-
150
157
  _JS_JSON_PROJECT_HELPER = """class SscJsonError extends Error {
151
158
  constructor(message) {
152
159
  super(message);
@@ -286,8 +293,6 @@ function sscRemapJsonKeys(value, mapping) {
286
293
  return result;
287
294
  }"""
288
295
 
289
- _JS_JSON_REMAP_HELPER = _JS_JSON_PROJECT_HELPER
290
-
291
296
 
292
297
  def _js_json_descriptors(
293
298
  node: JsonDef, definitions: dict[str, JsonDef]
@@ -295,6 +300,8 @@ def _js_json_descriptors(
295
300
  descriptors = json_def_descriptors(node, definitions)
296
301
 
297
302
  def render(value: object) -> str:
303
+ if isinstance(value, DescriptorRef):
304
+ return json_descriptor_var_name(value.schema_name)
298
305
  if value is None:
299
306
  return "null"
300
307
  if isinstance(value, bool):
@@ -317,36 +324,7 @@ def _js_json_descriptors(
317
324
  raise TypeError(f"unsupported JSON descriptor value: {value!r}")
318
325
 
319
326
  return [
320
- f"const _{to_snake_case(node.name)}JsonDescriptors = {render(descriptors)};",
321
- "",
322
- ]
323
-
324
-
325
- def _js_json_mapping(
326
- node: JsonDef, definitions: dict[str, JsonDef]
327
- ) -> list[str]:
328
- mapping = json_def_mapping(node, definitions)
329
-
330
- def render(value: object) -> str:
331
- if isinstance(value, str):
332
- return json.dumps(value)
333
- if isinstance(value, tuple):
334
- return "[" + json.dumps(value[0]) + ", " + render(value[1]) + "]"
335
- if isinstance(value, list):
336
- return "[" + ", ".join(render(item) for item in value) + "]"
337
- if isinstance(value, dict):
338
- return (
339
- "{"
340
- + ", ".join(
341
- f"{json.dumps(key)}: {render(item)}"
342
- for key, item in value.items()
343
- )
344
- + "}"
345
- )
346
- raise TypeError(f"unsupported JSON mapping value: {value!r}")
347
-
348
- return [
349
- f"const _{to_snake_case(node.name)}JsonMapping = {render(mapping)};",
327
+ f"const {json_descriptor_var_name(node.name)} = {render(descriptors)};",
350
328
  "",
351
329
  ]
352
330
 
@@ -728,9 +706,7 @@ class JsVisitor(BaseWalker):
728
706
  f" * @typedef {{Record<{key_type}, {val_type}>}} {name}Json",
729
707
  " */",
730
708
  ]
731
- module = node.parent
732
- while module is not None and not isinstance(module, Module):
733
- module = module.parent
709
+ module = find_enclosing_module(node)
734
710
  if isinstance(module, Module):
735
711
  definitions = {
736
712
  n.name: n for n in module.body if isinstance(n, JsonDef)
@@ -744,9 +720,7 @@ class JsVisitor(BaseWalker):
744
720
  lines = ["/**", f" * @typedef {{Object}} {name}Json"]
745
721
  lines.extend(self.walk_children(node, ctx))
746
722
  lines.append(" */")
747
- module = node.parent
748
- while module is not None and not isinstance(module, Module):
749
- module = module.parent
723
+ module = find_enclosing_module(node)
750
724
  if isinstance(module, Module):
751
725
  definitions = {
752
726
  n.name: n for n in module.body if isinstance(n, JsonDef)
@@ -1530,7 +1504,7 @@ class JsVisitor(BaseWalker):
1530
1504
  self._builder.require_std(
1531
1505
  "sscJsonProject", code=_JS_JSON_PROJECT_HELPER
1532
1506
  )
1533
- desc_name = f"_{to_snake_case(node.schema_name)}JsonDescriptors"
1507
+ desc_name = json_descriptor_var_name(node.schema_name)
1534
1508
  return [
1535
1509
  f"{ctx.indent}let {ctx.nxt} = sscJsonProject({raw_expr}, {desc_name});"
1536
1510
  ]
@@ -7,7 +7,7 @@ Called by PythonVisitor via thin delegate methods. The HTTP library strategy
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
- from typing import Mapping, cast
10
+ from typing import cast
11
11
 
12
12
  from ssc_codegen.ast import (
13
13
  JsonDef,
@@ -22,18 +22,21 @@ from ssc_codegen.ast import (
22
22
  StructBase,
23
23
  )
24
24
  from ssc_codegen.ast.struct import RequestHttp
25
- from ssc_codegen.naming import to_pascal_case, to_snake_case
25
+ from ssc_codegen.naming import (
26
+ json_descriptor_var_name,
27
+ to_pascal_case,
28
+ to_snake_case,
29
+ )
26
30
  from ssc_codegen.request_spec import parse_json_template
27
31
  from ssc_codegen.targets.python.http_libs.base import HttpLibStrategy
28
32
  from ssc_codegen.traversal.context import WalkContext
29
33
  from ssc_codegen.traversal.utils import (
30
34
  dict_needs_builder,
35
+ find_enclosing_module,
31
36
  module_has_html_struct,
32
37
  module_has_rest,
33
- json_def_descriptors,
34
38
  )
35
39
 
36
-
37
40
  # ===========================================================================
38
41
  # Placeholder / Template rendering (pure functions)
39
42
  # ===========================================================================
@@ -464,21 +467,27 @@ def emit_method_rest(
464
467
  pre_lines, kw_lines = _build_kw_dict(spec, i2, i3)
465
468
 
466
469
  value_kwarg: list[str] = []
467
- response_desc = _response_descriptors(node)
470
+ module = find_enclosing_module(node)
471
+ has_response_schema = False
472
+ if node.response_schema and isinstance(module, Module):
473
+ has_response_schema = any(
474
+ isinstance(n, JsonDef) and n.name == node.response_schema
475
+ for n in module.body
476
+ )
477
+
468
478
  if node.response_path:
469
479
  accessor = "".join(f"[{p!r}]" for p in node.response_path.split("."))
470
- if response_desc is None:
471
- value_kwarg = [f"{i3}value_fn=lambda _b: _b{accessor},"]
472
- else:
480
+ if has_response_schema:
473
481
  value_kwarg = [
474
- f"{i3}value_fn=lambda _b: ssc_json_project(_b{accessor}, {_render_descriptors(response_desc)}),"
482
+ f"{i3}value_fn=lambda _b: ssc_json_project(_b{accessor}, {json_descriptor_var_name(node.response_schema)}),"
475
483
  ]
484
+ else:
485
+ value_kwarg = [f"{i3}value_fn=lambda _b: _b{accessor},"]
476
486
  elif not node.response_schema:
477
487
  value_kwarg = [f"{i3}value_fn=lambda _: None,"]
478
- elif response_desc is not None:
479
- desc_str = _render_descriptors(response_desc)
488
+ elif has_response_schema:
480
489
  value_kwarg = [
481
- f"{i3}value_fn=lambda _b: ssc_json_project(_b, {desc_str}),"
490
+ f"{i3}value_fn=lambda _b: ssc_json_project(_b, {json_descriptor_var_name(node.response_schema)}),"
482
491
  ]
483
492
 
484
493
  def _body(fn_name: str, await_kw: str) -> list[str]:
@@ -518,82 +527,6 @@ def emit_method_rest(
518
527
  return lines
519
528
 
520
529
 
521
- def _response_descriptors(
522
- node: MethodRest,
523
- ) -> dict[str, tuple[str, bool, bool, object]] | None:
524
- if not node.response_schema:
525
- return None
526
- module = node.parent.parent if node.parent is not None else None
527
- if not isinstance(module, Module):
528
- return None
529
- definitions = {n.name: n for n in module.body if isinstance(n, JsonDef)}
530
- definition = definitions.get(node.response_schema)
531
- if definition is None:
532
- return None
533
- return json_def_descriptors(definition, definitions)
534
-
535
-
536
- def _schema_descriptors_for_entry(
537
- entry: object, module: Module | None
538
- ) -> dict[str, tuple[str, bool, bool, object]] | None:
539
- schema = getattr(entry, "error_schema", "")
540
- if not schema or not isinstance(module, Module):
541
- return None
542
- definitions = {n.name: n for n in module.body if isinstance(n, JsonDef)}
543
- definition = definitions.get(schema)
544
- if definition is None:
545
- return None
546
- return json_def_descriptors(definition, definitions)
547
-
548
-
549
- def _render_descriptors(descriptors: Mapping[str, object]) -> str:
550
- def render(value: object) -> str:
551
- if value is None:
552
- return "None"
553
- if isinstance(value, bool):
554
- return "True" if value else "False"
555
- if isinstance(value, str):
556
- return repr(value)
557
- if isinstance(value, tuple):
558
- return "(" + ", ".join(render(x) for x in value) + ")"
559
- if isinstance(value, list):
560
- return "[" + ", ".join(render(item) for item in value) + "]"
561
- if isinstance(value, dict):
562
- return (
563
- "{"
564
- + ", ".join(
565
- f"{key!r}: {render(item)}" for key, item in value.items()
566
- )
567
- + "}"
568
- )
569
- raise TypeError(f"unsupported JSON descriptor value: {value!r}")
570
-
571
- return render(descriptors)
572
-
573
-
574
- def _render_mapping(mapping: dict[str, object]) -> str:
575
- def render(value: object) -> str:
576
- if isinstance(value, str):
577
- return repr(value)
578
- if isinstance(value, tuple):
579
- return f"({value[0]!r}, {_render_mapping(value[1])})"
580
- if isinstance(value, list):
581
- return (
582
- "[" + ", ".join(_render_mapping(item) for item in value) + "]"
583
- )
584
- if isinstance(value, dict):
585
- return (
586
- "{"
587
- + ", ".join(
588
- f"{key!r}: {render(item)}" for key, item in value.items()
589
- )
590
- + "}"
591
- )
592
- raise TypeError(f"unsupported JSON mapping value: {value!r}")
593
-
594
- return render(mapping)
595
-
596
-
597
530
  def emit_result_variant_def(node: ResultVariantDef) -> list[str]:
598
531
  if node.schema_name:
599
532
  base = f"{to_pascal_case(node.schema_name)}Json"
@@ -626,22 +559,20 @@ def emit_result_alias_def(node: ResultAliasDef) -> list[str]:
626
559
  def emit_matcher_list_def(node: MatcherListDef) -> list[str]:
627
560
  var = f"{to_snake_case(node.struct_name).upper()}_MATCHERS"
628
561
  lines = [f"{var}: List[ErrMatcher] = ["]
562
+ module = find_enclosing_module(node)
563
+ definitions = (
564
+ {n.name: n for n in module.body if isinstance(n, JsonDef)}
565
+ if isinstance(module, Module)
566
+ else {}
567
+ )
629
568
  for e in node.entries:
630
569
  check = render_py_condition_lambda(e.required_keys, e.conditions)
631
570
  check_arg = check if check else "None"
632
- module = (
633
- node.parent
634
- if isinstance(node.parent, Module)
635
- else (node.parent.parent if node.parent is not None else None)
636
- )
637
- desc = _schema_descriptors_for_entry(
638
- e, module if isinstance(module, Module) else None
639
- )
640
571
  factory = e.factory_name
641
- if desc is not None:
572
+ if e.error_schema and e.error_schema in definitions:
642
573
  factory = (
643
574
  f"lambda headers, value: {e.factory_name}(headers=headers, "
644
- f"value=ssc_json_project(value, {_render_descriptors(desc)}))"
575
+ f"value=ssc_json_project(value, {json_descriptor_var_name(e.error_schema)}))"
645
576
  )
646
577
  lines.append(f" ErrMatcher({e.status}, {check_arg}, {factory}),")
647
578
  lines.append("]")