ssc_codegen 0.29.3__tar.gz → 0.29.5__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.
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/PKG-INFO +1 -1
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/pyproject.toml +1 -1
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/struct.py +5 -5
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/converters/js_pure.py +3 -3
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/converters/py_bs4.py +7 -5
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/converters/py_lxml.py +5 -3
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/converters/py_parsel.py +5 -3
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/converters/py_render.py +4 -4
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/converters/py_slax.py +5 -3
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/converters/request_spec.py +2 -2
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/core/linting.py +34 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/core/struct_parser.py +2 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/.gitignore +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/LICENSE +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/README.md +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/__init__.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/_logging.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/__init__.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/array.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/base.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/cast.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/control.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/extract.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/helpers.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/jsondef.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/module.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/predicate_containers.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/predicate_ops.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/regex.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/selectors.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/string.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/transform.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/typedef.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/ast/types.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/converters/base.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/converters/helpers.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/core/__init__.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/core/adapter.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/core/contexts.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/core/expressions.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/core/format.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/core/module_handler.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/core/predicates.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/core/reader.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/core/type_checking.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/document_utils.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/exceptions.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/health.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/kdl/__init__.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/kdl/dict_reader.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/kdl/parser.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/kdl/reader.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/main.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/parsers/__init__.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/parsers/curl.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/parsers/http.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/pseudo_selectors.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/regex_utils.py +0 -0
- {ssc_codegen-0.29.3 → ssc_codegen-0.29.5}/ssc_codegen/selector_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ssc_codegen
|
|
3
|
-
Version: 0.29.
|
|
3
|
+
Version: 0.29.5
|
|
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
|
|
@@ -13,7 +13,7 @@ from .types import VariableType, StructType
|
|
|
13
13
|
# PRIM = str | int | float | bool (default: str)
|
|
14
14
|
# STYLE = repeat | csv | bracket | pipe | space (arrays only; default: repeat)
|
|
15
15
|
# Legacy `{{name}}` remains valid (groups 2-5 = None → str, scalar, required).
|
|
16
|
-
|
|
16
|
+
PLACEHOLDER_RE = _re.compile(
|
|
17
17
|
r"\{\{"
|
|
18
18
|
r"([A-Za-z][A-Za-z0-9_-]*)"
|
|
19
19
|
r"(?::(str|int|float|bool))?"
|
|
@@ -25,7 +25,7 @@ _PLACEHOLDER_RE = _re.compile(
|
|
|
25
25
|
|
|
26
26
|
# Widened pattern — any `{{…}}`-shaped token. Used by the linter to flag
|
|
27
27
|
# malformed placeholders that the strict _PLACEHOLDER_RE would silently skip.
|
|
28
|
-
|
|
28
|
+
PLACEHOLDER_WIDE_RE = _re.compile(r"\{\{([^{}]*)\}\}")
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
@dataclass
|
|
@@ -39,7 +39,7 @@ class PlaceholderSpec:
|
|
|
39
39
|
style: str | None = None # None == default "repeat" when is_array
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
def
|
|
42
|
+
def parse_placeholder(match: "_re.Match[str]") -> PlaceholderSpec:
|
|
43
43
|
return PlaceholderSpec(
|
|
44
44
|
name=match.group(1),
|
|
45
45
|
type_name=match.group(2) or "str",
|
|
@@ -383,8 +383,8 @@ class RequestConfig(Node):
|
|
|
383
383
|
"""Unique placeholders in declaration order (dedup by name)."""
|
|
384
384
|
seen: set[str] = set()
|
|
385
385
|
result: list[PlaceholderSpec] = []
|
|
386
|
-
for m in
|
|
387
|
-
spec =
|
|
386
|
+
for m in PLACEHOLDER_RE.finditer(self.raw_payload):
|
|
387
|
+
spec = parse_placeholder(m)
|
|
388
388
|
if spec.name not in seen:
|
|
389
389
|
seen.add(spec.name)
|
|
390
390
|
result.append(spec)
|
|
@@ -33,7 +33,7 @@ from ssc_codegen.converters.request_spec import (
|
|
|
33
33
|
normalize_placeholder_names,
|
|
34
34
|
_PH,
|
|
35
35
|
)
|
|
36
|
-
from ssc_codegen.ast.struct import
|
|
36
|
+
from ssc_codegen.ast.struct import parse_placeholder
|
|
37
37
|
|
|
38
38
|
JS_CONVERTER = BaseConverter(indent=" " * 2)
|
|
39
39
|
|
|
@@ -1662,7 +1662,7 @@ def _js_array_join(ph: a.PlaceholderSpec) -> str:
|
|
|
1662
1662
|
def _js_render_value(v: str) -> str:
|
|
1663
1663
|
"""Render a RequestSpec string value as a JS expression."""
|
|
1664
1664
|
if m := _PH.fullmatch(v):
|
|
1665
|
-
ph =
|
|
1665
|
+
ph = parse_placeholder(m)
|
|
1666
1666
|
if ph.is_array and ph.style in ("csv", "pipe", "space"):
|
|
1667
1667
|
return _js_array_join(ph)
|
|
1668
1668
|
return ph.name
|
|
@@ -1685,7 +1685,7 @@ def _js_render_obj(d: dict[str, str]) -> str:
|
|
|
1685
1685
|
|
|
1686
1686
|
def _js_dict_entry_placeholder(v: str) -> a.PlaceholderSpec | None:
|
|
1687
1687
|
m = _PH.fullmatch(str(v))
|
|
1688
|
-
return
|
|
1688
|
+
return parse_placeholder(m) if m else None
|
|
1689
1689
|
|
|
1690
1690
|
|
|
1691
1691
|
def _js_dict_needs_builder(d: dict[str, str]) -> bool:
|
|
@@ -55,7 +55,7 @@ _BASE_UTILITY_LINES: list[str] = [
|
|
|
55
55
|
" pass",
|
|
56
56
|
"",
|
|
57
57
|
"def unescape_text(text: str) -> str:",
|
|
58
|
-
" s =
|
|
58
|
+
" s = ssc_html_unescape(text)",
|
|
59
59
|
" s = _RE_HEX_ENTITY.sub(lambda m: chr(int(m.group(1), 16)), s)",
|
|
60
60
|
" s = _RE_UNICODE_ENTITY.sub(lambda m: chr(int(m.group(1), 16)), s)",
|
|
61
61
|
" s = _RE_BYTES_ENTITY.sub(lambda m: chr(int(m.group(1), 16)), s)",
|
|
@@ -111,7 +111,7 @@ def runtime_module_content(node: a.Node) -> str:
|
|
|
111
111
|
"import re",
|
|
112
112
|
"import sys",
|
|
113
113
|
"from typing import Dict",
|
|
114
|
-
"from html import unescape as
|
|
114
|
+
"from html import unescape as ssc_html_unescape",
|
|
115
115
|
]
|
|
116
116
|
has_rest = _module_has_rest(node)
|
|
117
117
|
if has_rest:
|
|
@@ -252,7 +252,7 @@ def pre_imports(node: a.Imports, ctx: ConverterContext):
|
|
|
252
252
|
"import re",
|
|
253
253
|
"import sys",
|
|
254
254
|
"from dataclasses import dataclass",
|
|
255
|
-
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal",
|
|
255
|
+
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal, Mapping",
|
|
256
256
|
]
|
|
257
257
|
base_imports.extend(NOT_REQUIRED_IMPORT)
|
|
258
258
|
else:
|
|
@@ -261,10 +261,12 @@ def pre_imports(node: a.Imports, ctx: ConverterContext):
|
|
|
261
261
|
"import re",
|
|
262
262
|
"import sys",
|
|
263
263
|
"from dataclasses import dataclass",
|
|
264
|
-
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal",
|
|
264
|
+
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal, Mapping",
|
|
265
265
|
]
|
|
266
266
|
if not module_is_rest_only(node):
|
|
267
|
-
base_imports.append(
|
|
267
|
+
base_imports.append(
|
|
268
|
+
"from html import unescape as ssc_html_unescape"
|
|
269
|
+
)
|
|
268
270
|
base_imports.extend(NOT_REQUIRED_IMPORT)
|
|
269
271
|
base_imports.extend(rest_imports(node))
|
|
270
272
|
|
|
@@ -36,7 +36,7 @@ def pre_imports(node: a.Imports, ctx: ConverterContext):
|
|
|
36
36
|
"import re",
|
|
37
37
|
"import sys",
|
|
38
38
|
"from dataclasses import dataclass",
|
|
39
|
-
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal",
|
|
39
|
+
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal, Mapping",
|
|
40
40
|
]
|
|
41
41
|
else:
|
|
42
42
|
base_imports = [
|
|
@@ -44,10 +44,12 @@ def pre_imports(node: a.Imports, ctx: ConverterContext):
|
|
|
44
44
|
"import re",
|
|
45
45
|
"import sys",
|
|
46
46
|
"from dataclasses import dataclass",
|
|
47
|
-
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal",
|
|
47
|
+
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal, Mapping",
|
|
48
48
|
]
|
|
49
49
|
if not py_bs4.module_is_rest_only(node):
|
|
50
|
-
base_imports.append(
|
|
50
|
+
base_imports.append(
|
|
51
|
+
"from html import unescape as ssc_html_unescape"
|
|
52
|
+
)
|
|
51
53
|
base_imports.extend(py_bs4.rest_imports(node))
|
|
52
54
|
|
|
53
55
|
base_imports.extend(py_bs4.NOT_REQUIRED_IMPORT)
|
|
@@ -28,7 +28,7 @@ def pre_imports(node: a.Imports, ctx: ConverterContext):
|
|
|
28
28
|
"import re",
|
|
29
29
|
"import sys",
|
|
30
30
|
"from dataclasses import dataclass",
|
|
31
|
-
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal",
|
|
31
|
+
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal, Mapping",
|
|
32
32
|
]
|
|
33
33
|
base_imports.extend(py_bs4.NOT_REQUIRED_IMPORT)
|
|
34
34
|
else:
|
|
@@ -37,10 +37,12 @@ def pre_imports(node: a.Imports, ctx: ConverterContext):
|
|
|
37
37
|
"import re",
|
|
38
38
|
"import sys",
|
|
39
39
|
"from dataclasses import dataclass",
|
|
40
|
-
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal",
|
|
40
|
+
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal, Mapping",
|
|
41
41
|
]
|
|
42
42
|
if not py_bs4.module_is_rest_only(node):
|
|
43
|
-
base_imports.append(
|
|
43
|
+
base_imports.append(
|
|
44
|
+
"from html import unescape as ssc_html_unescape"
|
|
45
|
+
)
|
|
44
46
|
base_imports.extend(py_bs4.NOT_REQUIRED_IMPORT)
|
|
45
47
|
base_imports.extend(py_bs4.rest_imports(node))
|
|
46
48
|
|
|
@@ -7,7 +7,7 @@ fragments suitable for `requests(...)` / `httpx(...)` call arguments.
|
|
|
7
7
|
import json
|
|
8
8
|
import re
|
|
9
9
|
|
|
10
|
-
from ssc_codegen.ast.struct import PlaceholderSpec,
|
|
10
|
+
from ssc_codegen.ast.struct import PlaceholderSpec, parse_placeholder
|
|
11
11
|
from ssc_codegen.converters.request_spec import _PH, RequestSpec
|
|
12
12
|
|
|
13
13
|
__all__ = [
|
|
@@ -46,7 +46,7 @@ def render_value(v: str) -> str:
|
|
|
46
46
|
``Mozilla/5.0`` -> ``"Mozilla/5.0"`` (string literal)
|
|
47
47
|
"""
|
|
48
48
|
if m := _PH.fullmatch(v):
|
|
49
|
-
ph =
|
|
49
|
+
ph = parse_placeholder(m)
|
|
50
50
|
if ph.is_array and ph.style in ("csv", "pipe", "space"):
|
|
51
51
|
return _render_array_join(ph)
|
|
52
52
|
return ph.name
|
|
@@ -94,7 +94,7 @@ def render_dict(d: dict[str, str], *, indent: str = "") -> str:
|
|
|
94
94
|
def _dict_entry_placeholder(v: str) -> PlaceholderSpec | None:
|
|
95
95
|
"""Return PlaceholderSpec if *v* is a fullmatch placeholder, else None."""
|
|
96
96
|
m = _PH.fullmatch(str(v))
|
|
97
|
-
return
|
|
97
|
+
return parse_placeholder(m) if m else None
|
|
98
98
|
|
|
99
99
|
|
|
100
100
|
def dict_needs_builder(d: dict[str, str]) -> bool:
|
|
@@ -160,7 +160,7 @@ def render_json_body(raw: str) -> str:
|
|
|
160
160
|
if raw[i : i + 2] == "{{":
|
|
161
161
|
m = _PH.match(raw, i)
|
|
162
162
|
if m is not None:
|
|
163
|
-
name =
|
|
163
|
+
name = parse_placeholder(m).name
|
|
164
164
|
key = f"__SSC_PH_{len(sentinels)}__"
|
|
165
165
|
sentinels[key] = name
|
|
166
166
|
out.append(key if in_string else '"' + key + '"')
|
|
@@ -27,7 +27,7 @@ def pre_imports(node: a.Imports, ctx: ConverterContext):
|
|
|
27
27
|
"import re",
|
|
28
28
|
"import sys",
|
|
29
29
|
"from dataclasses import dataclass",
|
|
30
|
-
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal",
|
|
30
|
+
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal, Mapping",
|
|
31
31
|
]
|
|
32
32
|
base_imports.extend(py_bs4.NOT_REQUIRED_IMPORT)
|
|
33
33
|
else:
|
|
@@ -36,10 +36,12 @@ def pre_imports(node: a.Imports, ctx: ConverterContext):
|
|
|
36
36
|
"import re",
|
|
37
37
|
"import sys",
|
|
38
38
|
"from dataclasses import dataclass",
|
|
39
|
-
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal",
|
|
39
|
+
"from typing import TypedDict, Optional, Any, List, Dict, Union, Literal, Mapping",
|
|
40
40
|
]
|
|
41
41
|
if not py_bs4.module_is_rest_only(node):
|
|
42
|
-
base_imports.append(
|
|
42
|
+
base_imports.append(
|
|
43
|
+
"from html import unescape as ssc_html_unescape"
|
|
44
|
+
)
|
|
43
45
|
base_imports.extend(py_bs4.NOT_REQUIRED_IMPORT)
|
|
44
46
|
base_imports.extend(py_bs4.rest_imports(node))
|
|
45
47
|
|
|
@@ -12,7 +12,7 @@ from dataclasses import dataclass, field
|
|
|
12
12
|
from typing import Callable
|
|
13
13
|
from urllib.parse import urlparse, urlunparse
|
|
14
14
|
|
|
15
|
-
from ssc_codegen.ast.struct import PlaceholderSpec,
|
|
15
|
+
from ssc_codegen.ast.struct import PlaceholderSpec, parse_placeholder
|
|
16
16
|
from ssc_codegen.parsers.curl import parse_curl_command
|
|
17
17
|
from ssc_codegen.parsers.http import parse_http_request
|
|
18
18
|
|
|
@@ -48,7 +48,7 @@ class RequestSpec:
|
|
|
48
48
|
result: list[PlaceholderSpec] = []
|
|
49
49
|
for text in _iter_strings(self):
|
|
50
50
|
for m in _PH.finditer(text):
|
|
51
|
-
spec =
|
|
51
|
+
spec = parse_placeholder(m)
|
|
52
52
|
if spec.name not in seen:
|
|
53
53
|
seen.add(spec.name)
|
|
54
54
|
result.append(spec)
|
|
@@ -6,6 +6,7 @@ import difflib as _difflib
|
|
|
6
6
|
import re as _re
|
|
7
7
|
|
|
8
8
|
from ssc_codegen.ast import JsonDefField, Module, VariableType
|
|
9
|
+
from ssc_codegen.ast.struct import PLACEHOLDER_RE, PLACEHOLDER_WIDE_RE
|
|
9
10
|
from ssc_codegen.kdl import KdlNode
|
|
10
11
|
from ssc_codegen.core.contexts import (
|
|
11
12
|
DefineKind,
|
|
@@ -799,6 +800,39 @@ def lint_reserved_field(
|
|
|
799
800
|
)
|
|
800
801
|
|
|
801
802
|
|
|
803
|
+
def lint_request_placeholders(
|
|
804
|
+
node: KdlNode, raw_payload: str, lint: LintContext
|
|
805
|
+
) -> None:
|
|
806
|
+
"""Validate placeholder names in an @request raw payload.
|
|
807
|
+
|
|
808
|
+
Convention: uppercase {{NAME}} is a define substitution (only resolved
|
|
809
|
+
within ``define`` values), lowercase {{name}} is a runtime fetch() param.
|
|
810
|
+
Define substitution does NOT run on @request payloads, so any remaining
|
|
811
|
+
uppercase tokens are bugs that silently become literal strings.
|
|
812
|
+
"""
|
|
813
|
+
for m in PLACEHOLDER_WIDE_RE.finditer(raw_payload):
|
|
814
|
+
token = m.group(0)
|
|
815
|
+
strict = PLACEHOLDER_RE.match(token)
|
|
816
|
+
if strict is None:
|
|
817
|
+
lint.error(
|
|
818
|
+
node,
|
|
819
|
+
message=f"malformed placeholder {token!r} in @request",
|
|
820
|
+
code="E002",
|
|
821
|
+
hint="expected syntax: {{name}} or {{name:type}} (lowercase)",
|
|
822
|
+
)
|
|
823
|
+
continue
|
|
824
|
+
name = strict.group(1)
|
|
825
|
+
if name != name.lower():
|
|
826
|
+
lint.error(
|
|
827
|
+
node,
|
|
828
|
+
message=f"placeholder '{{{{{name}}}}}' in @request must be lowercase; "
|
|
829
|
+
f"uppercase names are define substitutions which don't resolve in @request",
|
|
830
|
+
code="E002",
|
|
831
|
+
hint=f"use lowercase for runtime params (e.g. {{{{{name.lower()}}}}}), "
|
|
832
|
+
f"or compose the URL in a define first",
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
|
|
802
836
|
def lint_regular_field(
|
|
803
837
|
node: KdlNode,
|
|
804
838
|
field_name: str,
|
|
@@ -32,6 +32,7 @@ from ssc_codegen.kdl import KdlArg, KdlNode
|
|
|
32
32
|
|
|
33
33
|
from ssc_codegen.core.contexts import LintContext, ParseContext, WalkCtx
|
|
34
34
|
from ssc_codegen.core.expressions import parse_expressions
|
|
35
|
+
from ssc_codegen.core.linting import lint_request_placeholders
|
|
35
36
|
from ssc_codegen.core.type_checking import check_pipeline_types
|
|
36
37
|
|
|
37
38
|
|
|
@@ -103,6 +104,7 @@ def parse_struct(
|
|
|
103
104
|
)
|
|
104
105
|
req = RequestConfig(parent=parent)
|
|
105
106
|
req.raw_payload = raw_payload
|
|
107
|
+
lint_request_placeholders(node, raw_payload, lint)
|
|
106
108
|
req.response_path = str(
|
|
107
109
|
node.properties.get(
|
|
108
110
|
"response-path",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|