ssc_codegen 0.29.6__tar.gz → 0.30.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 (60) hide show
  1. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/PKG-INFO +3 -3
  2. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/pyproject.toml +3 -5
  3. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/__init__.py +3 -3
  4. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/contexts.py +197 -235
  5. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/expressions.py +4 -13
  6. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/format.py +361 -361
  7. ssc_codegen-0.30.0/ssc_codegen/core/linter.py +1491 -0
  8. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/module_handler.py +9 -49
  9. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/predicates.py +2 -2
  10. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/reader.py +116 -164
  11. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/struct_parser.py +17 -41
  12. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/type_checking.py +401 -401
  13. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/kdl/__init__.py +8 -5
  14. ssc_codegen-0.29.6/ssc_codegen/core/adapter.py +0 -25
  15. ssc_codegen-0.29.6/ssc_codegen/core/linting.py +0 -1179
  16. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/.gitignore +0 -0
  17. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/LICENSE +0 -0
  18. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/README.md +0 -0
  19. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/__init__.py +0 -0
  20. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/_logging.py +0 -0
  21. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/__init__.py +0 -0
  22. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/array.py +0 -0
  23. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/base.py +0 -0
  24. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/cast.py +0 -0
  25. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/control.py +0 -0
  26. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/extract.py +0 -0
  27. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/helpers.py +0 -0
  28. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/jsondef.py +0 -0
  29. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/module.py +0 -0
  30. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/predicate_containers.py +0 -0
  31. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/predicate_ops.py +0 -0
  32. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/regex.py +0 -0
  33. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/selectors.py +0 -0
  34. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/string.py +0 -0
  35. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/struct.py +0 -0
  36. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/transform.py +0 -0
  37. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/typedef.py +0 -0
  38. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/types.py +0 -0
  39. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/base.py +0 -0
  40. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/helpers.py +0 -0
  41. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/js_pure.py +0 -0
  42. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/py_bs4.py +0 -0
  43. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/py_lxml.py +0 -0
  44. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/py_parsel.py +0 -0
  45. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/py_render.py +0 -0
  46. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/py_slax.py +0 -0
  47. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/request_spec.py +0 -0
  48. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/document_utils.py +0 -0
  49. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/exceptions.py +0 -0
  50. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/health.py +0 -0
  51. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/kdl/dict_reader.py +0 -0
  52. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/kdl/parser.py +0 -0
  53. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/kdl/reader.py +0 -0
  54. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/main.py +0 -0
  55. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/parsers/__init__.py +0 -0
  56. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/parsers/curl.py +0 -0
  57. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/parsers/http.py +0 -0
  58. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/pseudo_selectors.py +0 -0
  59. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/regex_utils.py +0 -0
  60. {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/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.6
3
+ Version: 0.30.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
@@ -20,13 +20,13 @@ Classifier: Topic :: Text Processing :: Markup :: HTML
20
20
  Classifier: Topic :: Utilities
21
21
  Requires-Python: >=3.10
22
22
  Requires-Dist: bs4>=0.0.2
23
- Requires-Dist: click<8.2.0
24
23
  Requires-Dist: colorama>=0.4.6; sys_platform == 'win32'
25
24
  Requires-Dist: cssselect>=1.2.0
25
+ Requires-Dist: kdlquery>=1.1.2
26
26
  Requires-Dist: lxml>=5.3.0
27
27
  Requires-Dist: pyyaml>=6.0
28
28
  Requires-Dist: soupsieve>=2.6
29
- Requires-Dist: typer>=0.15.1
29
+ Requires-Dist: typer>0.26
30
30
  Requires-Dist: typing-extensions; python_version < '3.11'
31
31
  Description-Content-Type: text/markdown
32
32
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ssc_codegen"
3
- version = "0.29.6"
3
+ version = "0.30.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"
@@ -10,12 +10,10 @@ dependencies = [
10
10
  "cssselect>=1.2.0",
11
11
  "lxml>=5.3.0",
12
12
  "soupsieve>=2.6",
13
- "typer>=0.15.1",
13
+ "typer>0.26",
14
14
  "typing_extensions;python_version<'3.11'",
15
- # https://github.com/fastapi/typer/discussions/1215
16
- # https://github.com/fastapi/typer/pull/1145
17
- "click<8.2.0",
18
15
  "pyyaml>=6.0",
16
+ "kdlquery>=1.1.2",
19
17
  ]
20
18
 
21
19
  classifiers = [
@@ -1,10 +1,10 @@
1
1
  """ssc_codegen.core — unified KDL -> Module AST reader with integrated linting."""
2
- from ssc_codegen.core.reader import parse_module, SscReader, ReadDiagnostic
2
+ from ssc_codegen.core.reader import parse_module
3
3
  from ssc_codegen.core.format import format_diagnostics, format_diagnostic
4
+ from kdlquery import ReadDiagnostic
4
5
  __all__ = [
5
6
  "parse_module",
6
- "SscReader",
7
7
  "ReadDiagnostic",
8
8
  "format_diagnostics",
9
9
  "format_diagnostic"
10
- ]
10
+ ]
@@ -1,235 +1,197 @@
1
- """Parse/lint contexts, error codes, and supporting data classes."""
2
-
3
- from __future__ import annotations
4
-
5
- from dataclasses import dataclass, field
6
- from enum import Enum, auto
7
- from pathlib import Path
8
-
9
- from ssc_codegen.ast import VariableType
10
- from ssc_codegen.kdl import KdlNode, ReadDiagnostic, Severity
11
- from ssc_codegen.kdl.parser import Span
12
-
13
-
14
- # ── Walk context enum ──────────────────────────────────────────────────────────
15
-
16
-
17
- class WalkCtx(Enum):
18
- MODULE = auto()
19
- STRUCT_BODY = auto()
20
- INIT_BLOCK = auto()
21
- PIPELINE = auto()
22
- JSON_TYPEDEF = auto()
23
- SPECIAL_FIELD = auto()
24
-
25
-
26
- # ── Error codes ────────────────────────────────────────────────────────────────
27
-
28
-
29
- class ErrorCode(Enum):
30
- INVALID_SYNTAX = "E000"
31
- MISSING_ARGUMENT = "E001"
32
- INVALID_ARGUMENT = "E002"
33
- EMPTY_BLOCK = "E003"
34
- UNEXPECTED_CHILDREN = "E004"
35
- TYPE_MISMATCH = "E100"
36
- INCOMPATIBLE_OPERATION = "E101"
37
- UNKNOWN_OPERATION = "E200"
38
- UNKNOWN_FIELD = "E201"
39
- MISSING_REQUIRED_FIELD = "E202"
40
- INVALID_FIELD_FOR_TYPE = "E203"
41
- UNDEFINED_REFERENCE = "E300"
42
- INIT_FIELD_NOT_FOUND = "E301"
43
- DEFINE_NOT_FOUND = "E302"
44
- INVALID_STRUCT_TYPE = "E400"
45
- MISSING_SPECIAL_FIELD = "E401"
46
- DEPRECATED_SYNTAX = "W001"
47
- UNUSED_FIELD = "W002"
48
-
49
-
50
- class DefineKind(Enum):
51
- SCALAR = auto()
52
- BLOCK = auto()
53
-
54
-
55
- @dataclass
56
- class RawArg:
57
- value: str
58
- is_identifier: bool
59
- span: Span
60
-
61
-
62
- @dataclass
63
- class DefineInfo:
64
- name: str
65
- kind: DefineKind
66
- value: str | None
67
- node: KdlNode
68
-
69
-
70
- @dataclass
71
- class TransformInfo:
72
- name: str
73
- accept: str
74
- ret: str
75
- langs: list[str]
76
- node: KdlNode
77
-
78
-
79
- # ── Parse context ───────────────────────────────────────────────────────────────
80
-
81
-
82
- @dataclass
83
- class ParseContext:
84
- property_defines: dict[str, str | int | float | bool] = field(
85
- default_factory=dict
86
- )
87
- children_defines: dict[str, list[KdlNode]] = field(default_factory=dict)
88
- transforms: dict[str, TransformDef] = field(default_factory=dict)
89
- structs: dict[str, Struct] = field(default_factory=dict)
90
- json_defs: dict[str, JsonDef] = field(default_factory=dict)
91
- source_path: Path | None = None
92
-
93
- def all_names(self) -> set[str]:
94
- return (
95
- set(self.property_defines)
96
- | set(self.children_defines)
97
- | set(self.transforms)
98
- | set(self.structs)
99
- | set(self.json_defs)
100
- )
101
-
102
-
103
- # ── Lint context ───────────────────────────────────────────────────────────────
104
-
105
-
106
- @dataclass
107
- class LintContext:
108
- """Lint state: defines, transforms, init_fields, walk context, path, diagnostics."""
109
-
110
- defines: dict[str, DefineInfo] = field(default_factory=dict)
111
- transforms: dict[str, TransformInfo] = field(default_factory=dict)
112
- init_fields: set[str] = field(default_factory=set)
113
- walk_context: WalkCtx = WalkCtx.MODULE
114
- _path_segments: list[str] = field(default_factory=list)
115
- diagnostics: list[ReadDiagnostic] = field(default_factory=list)
116
- inferred_define_types: dict[str, tuple[VariableType, VariableType]] = field(
117
- default_factory=dict
118
- )
119
- dsl_names: set[str] = field(default_factory=set)
120
- json_kdl_nodes: dict[str, "KdlNode"] = field(default_factory=dict)
121
- rest_response_refs: list[tuple["KdlNode", str]] = field(
122
- default_factory=list
123
- )
124
- rest_error_refs: list[tuple["KdlNode", str]] = field(default_factory=list)
125
- _predicate_depth: int = field(default=0)
126
- _predicate_context: str = ""
127
-
128
- def error(
129
- self,
130
- node: KdlNode,
131
- *,
132
- message: str,
133
- code: str = "",
134
- hint: str = "",
135
- label: str | None = None,
136
- notes: list[str] | None = None,
137
- ) -> None:
138
- self.diagnostics.append(
139
- ReadDiagnostic(
140
- message=message,
141
- severity=Severity.ERROR,
142
- span=node.span,
143
- path=self.path,
144
- hint=hint,
145
- code=code,
146
- label=label,
147
- notes=tuple(notes) if notes else (),
148
- )
149
- )
150
-
151
- def warning(
152
- self,
153
- node: KdlNode,
154
- *,
155
- message: str,
156
- code: str = "",
157
- hint: str = "",
158
- label: str | None = None,
159
- notes: list[str] | None = None,
160
- ) -> None:
161
- self.diagnostics.append(
162
- ReadDiagnostic(
163
- message=message,
164
- severity=Severity.WARNING,
165
- span=node.span,
166
- path=self.path,
167
- hint=hint,
168
- code=code,
169
- label=label,
170
- notes=tuple(notes) if notes else (),
171
- )
172
- )
173
-
174
- @property
175
- def path(self) -> str:
176
- return "/".join(self._path_segments)
177
-
178
- def push(self, segment: str) -> None:
179
- self._path_segments.append(segment)
180
-
181
- def pop(self) -> None:
182
- self._path_segments.pop()
183
-
184
- def node_name(self, node: KdlNode) -> str:
185
- return node.name
186
-
187
- def get_args(self, node: KdlNode) -> list[str]:
188
- return [str(a.value) for a in node.args]
189
-
190
- def get_raw_args(self, node: KdlNode) -> list[RawArg]:
191
- return [
192
- RawArg(
193
- value=str(a.value), is_identifier=a.is_identifier, span=a.span
194
- )
195
- for a in node.args
196
- ]
197
-
198
- def get_arg(self, node: KdlNode, index: int) -> str | None:
199
- args = self.get_args(node)
200
- return args[index] if index < len(args) else None
201
-
202
- def get_prop(self, node: KdlNode, key: str) -> str | None:
203
- entry = node.properties.get(key)
204
- return str(entry.value) if entry else None
205
-
206
- def get_children_nodes(self, node: KdlNode) -> list[KdlNode]:
207
- return list(node.children)
208
-
209
- def has_empty_block(self, node: KdlNode) -> bool:
210
- return len(node.children) == 0
211
-
212
- def is_define_ref(self, arg: str) -> bool:
213
- return arg in self.defines
214
-
215
- def resolve_scalar_arg(self, arg: str) -> str | None:
216
- info = self.defines.get(arg)
217
- if info is not None and info.kind == DefineKind.SCALAR:
218
- return info.value
219
- return None
220
-
221
- @property
222
- def in_predicate(self) -> bool:
223
- return self._predicate_depth > 0
224
-
225
- @property
226
- def in_assert(self) -> bool:
227
- return self._predicate_depth > 0 and self._predicate_context == "assert"
228
-
229
-
230
- # lazy imports for forward refs
231
- from ssc_codegen.ast import ( # noqa: E402
232
- JsonDef,
233
- Struct,
234
- TransformDef,
235
- )
1
+ """Parse/lint contexts, error codes, and supporting data classes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from enum import Enum, auto
7
+ from pathlib import Path
8
+
9
+ from ssc_codegen.ast import VariableType
10
+ from kdlquery import KdlNode, ReadDiagnostic, Severity
11
+ from kdlquery.types import Span
12
+
13
+
14
+ # ── Walk context enum ──────────────────────────────────────────────────────────
15
+
16
+
17
+ class WalkCtx(Enum):
18
+ MODULE = auto()
19
+ STRUCT_BODY = auto()
20
+ INIT_BLOCK = auto()
21
+ PIPELINE = auto()
22
+ JSON_TYPEDEF = auto()
23
+ SPECIAL_FIELD = auto()
24
+
25
+
26
+ # ── Error codes ────────────────────────────────────────────────────────────────
27
+
28
+
29
+ class ErrorCode(Enum):
30
+ INVALID_SYNTAX = "E000"
31
+ MISSING_ARGUMENT = "E001"
32
+ INVALID_ARGUMENT = "E002"
33
+ EMPTY_BLOCK = "E003"
34
+ UNEXPECTED_CHILDREN = "E004"
35
+ TYPE_MISMATCH = "E100"
36
+ INCOMPATIBLE_OPERATION = "E101"
37
+ UNKNOWN_OPERATION = "E200"
38
+ UNKNOWN_FIELD = "E201"
39
+ MISSING_REQUIRED_FIELD = "E202"
40
+ INVALID_FIELD_FOR_TYPE = "E203"
41
+ UNDEFINED_REFERENCE = "E300"
42
+ INIT_FIELD_NOT_FOUND = "E301"
43
+ DEFINE_NOT_FOUND = "E302"
44
+ INVALID_STRUCT_TYPE = "E400"
45
+ MISSING_SPECIAL_FIELD = "E401"
46
+ DEPRECATED_SYNTAX = "W001"
47
+ UNUSED_FIELD = "W002"
48
+
49
+
50
+ class DefineKind(Enum):
51
+ SCALAR = auto()
52
+ BLOCK = auto()
53
+
54
+
55
+ @dataclass
56
+ class RawArg:
57
+ value: str
58
+ span: Span
59
+
60
+
61
+ @dataclass
62
+ class DefineInfo:
63
+ name: str
64
+ kind: DefineKind
65
+ value: str | None
66
+ node: KdlNode
67
+
68
+
69
+ @dataclass
70
+ class TransformInfo:
71
+ name: str
72
+ accept: str
73
+ ret: str
74
+ langs: list[str]
75
+ node: KdlNode
76
+
77
+
78
+ # ── Parse context ───────────────────────────────────────────────────────────────
79
+
80
+
81
+ @dataclass
82
+ class ParseContext:
83
+ property_defines: dict[str, str | int | float | bool] = field(
84
+ default_factory=dict
85
+ )
86
+ children_defines: dict[str, list[KdlNode]] = field(default_factory=dict)
87
+ transforms: dict[str, TransformDef] = field(default_factory=dict)
88
+ structs: dict[str, Struct] = field(default_factory=dict)
89
+ json_defs: dict[str, JsonDef] = field(default_factory=dict)
90
+ source_path: Path | None = None
91
+
92
+ def all_names(self) -> set[str]:
93
+ return (
94
+ set(self.property_defines)
95
+ | set(self.children_defines)
96
+ | set(self.transforms)
97
+ | set(self.structs)
98
+ | set(self.json_defs)
99
+ )
100
+
101
+
102
+ # ── Lint context ───────────────────────────────────────────────────────────────
103
+
104
+
105
+ @dataclass
106
+ class LintContext:
107
+ """Lint state for reader-side validation (type inference, per-op checks)."""
108
+
109
+ defines: dict[str, DefineInfo] = field(default_factory=dict)
110
+ transforms: dict[str, TransformInfo] = field(default_factory=dict)
111
+ init_fields: set[str] = field(default_factory=set)
112
+ walk_context: WalkCtx = WalkCtx.MODULE
113
+ _path_segments: list[str] = field(default_factory=list)
114
+ diagnostics: list[ReadDiagnostic] = field(default_factory=list)
115
+ inferred_define_types: dict[str, tuple[VariableType, VariableType]] = field(
116
+ default_factory=dict
117
+ )
118
+ _predicate_depth: int = field(default=0)
119
+ _predicate_context: str = ""
120
+
121
+ def error(
122
+ self,
123
+ node: KdlNode,
124
+ *,
125
+ message: str,
126
+ code: str = "",
127
+ hint: str = "",
128
+ label: str | None = None,
129
+ notes: list[str] | None = None,
130
+ ) -> None:
131
+ self.diagnostics.append(
132
+ ReadDiagnostic(
133
+ message=message,
134
+ severity=Severity.ERROR,
135
+ span=node.span,
136
+ path=self.path,
137
+ hint=hint,
138
+ code=code,
139
+ label=label,
140
+ notes=tuple(notes) if notes else (),
141
+ )
142
+ )
143
+
144
+ def warning(
145
+ self,
146
+ node: KdlNode,
147
+ *,
148
+ message: str,
149
+ code: str = "",
150
+ hint: str = "",
151
+ label: str | None = None,
152
+ notes: list[str] | None = None,
153
+ ) -> None:
154
+ self.diagnostics.append(
155
+ ReadDiagnostic(
156
+ message=message,
157
+ severity=Severity.WARNING,
158
+ span=node.span,
159
+ path=self.path,
160
+ hint=hint,
161
+ code=code,
162
+ label=label,
163
+ notes=tuple(notes) if notes else (),
164
+ )
165
+ )
166
+
167
+ @property
168
+ def path(self) -> str:
169
+ return "/".join(self._path_segments)
170
+
171
+ def push(self, segment: str) -> None:
172
+ self._path_segments.append(segment)
173
+
174
+ def pop(self) -> None:
175
+ self._path_segments.pop()
176
+
177
+ def resolve_scalar_arg(self, arg: str) -> str | None:
178
+ info = self.defines.get(arg)
179
+ if info is not None and info.kind == DefineKind.SCALAR:
180
+ return info.value
181
+ return None
182
+
183
+ @property
184
+ def in_predicate(self) -> bool:
185
+ return self._predicate_depth > 0
186
+
187
+ @property
188
+ def in_assert(self) -> bool:
189
+ return self._predicate_depth > 0 and self._predicate_context == "assert"
190
+
191
+
192
+ # lazy imports for forward refs
193
+ from ssc_codegen.ast import ( # noqa: E402
194
+ JsonDef,
195
+ Struct,
196
+ TransformDef,
197
+ )
@@ -76,12 +76,12 @@ from ssc_codegen.ast import (
76
76
  XpathSelectAll,
77
77
  )
78
78
  from ssc_codegen.exceptions import BuildTimeError
79
- from ssc_codegen.kdl import KdlArg, KdlNode
79
+ from kdlquery import KdlNode
80
80
  from ssc_codegen.regex_utils import normalize_regex_pattern
81
81
  from typing import cast
82
82
 
83
83
  from ssc_codegen.core.contexts import LintContext, ParseContext, WalkCtx
84
- from ssc_codegen.core.linting import (
84
+ from ssc_codegen.core.linter import (
85
85
  lint_pipeline_op,
86
86
  lint_validate_css,
87
87
  lint_validate_xpath,
@@ -866,12 +866,7 @@ def _expr_len(
866
866
  def _expr_unique(
867
867
  node: KdlNode, parent: FieldLikeNode, ctx: ParseContext, lint: LintContext
868
868
  ):
869
- keep_order = bool(
870
- node.properties.get(
871
- "keep-order",
872
- KdlArg(value=False, span=node.span, is_identifier=False),
873
- ).value
874
- )
869
+ keep_order = bool(node.get_prop("keep-order") or False)
875
870
  return Unique(parent=parent, keep_order=keep_order)
876
871
 
877
872
 
@@ -917,11 +912,7 @@ def _expr_jsonify(
917
912
  node: KdlNode, parent: FieldLikeNode, ctx: ParseContext, lint: LintContext
918
913
  ):
919
914
  schema_name = str(node.args[0].value)
920
- path = str(
921
- node.properties.get(
922
- "path", KdlArg(value="", span=node.span, is_identifier=False)
923
- ).value
924
- )
915
+ path = node.get_prop("path") or ""
925
916
  json_def = ctx.json_defs.get(schema_name)
926
917
  if json_def is None:
927
918
  lint.error(