ssc_codegen 0.8.14__tar.gz → 0.8.16__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.
Potentially problematic release.
This version of ssc_codegen might be problematic. Click here for more details.
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/PKG-INFO +2 -2
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/README.md +1 -1
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/pyproject.toml +1 -1
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/__init__.py +1 -1
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/ast_/__init__.py +1 -1
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/ast_/nodes_validate.py +18 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/converters/go_goquery.py +3 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/converters/js_pure.py +40 -16
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/converters/py_bs4.py +28 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/converters/py_parsel.py +31 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/converters/py_selectolax.py +31 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/converters/templates/js_pure.py +9 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/document.py +29 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/tokens.py +2 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/.gitignore +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/LICENSE +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/_compat.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/ast_/base.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/ast_/nodes_array.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/ast_/nodes_cast.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/ast_/nodes_core.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/ast_/nodes_selectors.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/ast_/nodes_string.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/ast_build/__init__.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/ast_build/main.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/ast_build/utils.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/cli/__init__.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/cli/cli_callbacks.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/cli/cli_utils.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/cli/code_callbacks.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/cli/consts.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/cli/main.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/cli/runtime_parse_runners.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/compiler.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/consts.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/converters/__init__.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/converters/base.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/converters/helpers.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/converters/py_base.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/converters/templates/__init__.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/converters/templates/go_goquery.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/document_utlis.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/json_struct.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/json_to_scc.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/logs.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/pseudo_selectors.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/schema.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/selector_utils.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/static_checker/__init__.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/static_checker/base.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/static_checker/callbacks.py +0 -0
- {ssc_codegen-0.8.14 → ssc_codegen-0.8.16}/ssc_codegen/str_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ssc_codegen
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.16
|
|
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
|
|
@@ -52,7 +52,7 @@ Current support converters
|
|
|
52
52
|
|---------------|-------------------------------|---------------|-------------|------------------------------------------|----------------|
|
|
53
53
|
| Python (3.8+) | bs4 | N | Y | TypedDict*, list, dict | ruff |
|
|
54
54
|
| ... | parsel | Y | Y | ... | - |
|
|
55
|
-
| ... | selectolax (
|
|
55
|
+
| ... | selectolax (lexbor) | N | Y | ... | - |
|
|
56
56
|
| js (ES6) | pure (firefox/chrome/nodejs) | Y | Y | Array, Map** | - |
|
|
57
57
|
| go (1.10+) | goquery | N | Y | struct(json anchors include), array, map | gofmt |
|
|
58
58
|
|
|
@@ -19,7 +19,7 @@ Current support converters
|
|
|
19
19
|
|---------------|-------------------------------|---------------|-------------|------------------------------------------|----------------|
|
|
20
20
|
| Python (3.8+) | bs4 | N | Y | TypedDict*, list, dict | ruff |
|
|
21
21
|
| ... | parsel | Y | Y | ... | - |
|
|
22
|
-
| ... | selectolax (
|
|
22
|
+
| ... | selectolax (lexbor) | N | Y | ... | - |
|
|
23
23
|
| js (ES6) | pure (firefox/chrome/nodejs) | Y | Y | Array, Map** | - |
|
|
24
24
|
| go (1.10+) | goquery | N | Y | struct(json anchors include), array, map | gofmt |
|
|
25
25
|
|
|
@@ -30,4 +30,4 @@ from .nodes_string import ExprStringTrim, ExprStringRegex, ExprStringSplit, Expr
|
|
|
30
30
|
ExprStringRmPrefix, ExprStringRmSuffix, ExprStringRmPrefixAndSuffix, ExprListStringRmPrefixAndSuffix, \
|
|
31
31
|
ExprListStringRmPrefix, ExprListStringRmSuffix
|
|
32
32
|
from .nodes_validate import ExprIsCss, ExprIsEqual, ExprStringIsRegex, ExprIsXpath, ExprIsNotEqual, ExprIsContains, \
|
|
33
|
-
ExprListStringAnyRegex, ExprListStringAllRegex
|
|
33
|
+
ExprListStringAnyRegex, ExprListStringAllRegex, ExprHasAttr, ExprListHasAttr
|
|
@@ -97,6 +97,24 @@ class ExprIsXpath(BaseAstNode[KW_IS_SELECT, ARGS_IS_SELECT]):
|
|
|
97
97
|
ret_type: VariableType = VariableType.DOCUMENT
|
|
98
98
|
|
|
99
99
|
|
|
100
|
+
KW_HAS_ATTR = TypedDict("KW_HAS_ATTR", {"key": str, "msg": str})
|
|
101
|
+
ARGS_HAS_ATTR = tuple[str, str]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@dataclass(kw_only=True)
|
|
105
|
+
class ExprHasAttr(BaseAstNode[KW_HAS_ATTR, ARGS_HAS_ATTR]):
|
|
106
|
+
kind: ClassVar[TokenType] = TokenType.HAS_ATTR
|
|
107
|
+
accept_type: VariableType = VariableType.DOCUMENT
|
|
108
|
+
ret_type: VariableType = VariableType.DOCUMENT
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@dataclass(kw_only=True)
|
|
112
|
+
class ExprListHasAttr(BaseAstNode[KW_HAS_ATTR, ARGS_HAS_ATTR]):
|
|
113
|
+
kind: ClassVar[TokenType] = TokenType.HAS_LIST_ATTR
|
|
114
|
+
accept_type: VariableType = VariableType.DOCUMENT
|
|
115
|
+
ret_type: VariableType = VariableType.DOCUMENT
|
|
116
|
+
|
|
117
|
+
|
|
100
118
|
# TODO:
|
|
101
119
|
# ExprIsBiggerThan
|
|
102
120
|
# ExprIsBiggerOrEq
|
|
@@ -83,6 +83,8 @@ from ssc_codegen.ast_ import (
|
|
|
83
83
|
ExprListStringRmPrefixAndSuffix,
|
|
84
84
|
ExprListStringAnyRegex,
|
|
85
85
|
ExprListStringAllRegex,
|
|
86
|
+
ExprListHasAttr,
|
|
87
|
+
ExprHasAttr,
|
|
86
88
|
)
|
|
87
89
|
from ssc_codegen.converters.base import BaseCodeConverter
|
|
88
90
|
from ssc_codegen.converters.helpers import (
|
|
@@ -127,6 +129,17 @@ DOCSTR_SEP = "* "
|
|
|
127
129
|
CONVERTER = BaseCodeConverter(debug_comment_prefix="// ")
|
|
128
130
|
|
|
129
131
|
|
|
132
|
+
# TODO: move to string_utils
|
|
133
|
+
def to_js_regexp(pattern: str, ignore_case: bool = False) -> str:
|
|
134
|
+
"""helper function for convert string pattern to js"""
|
|
135
|
+
pattern = pattern.replace("<\\/", "</")
|
|
136
|
+
pattern = pattern.replace("/", "\\/")
|
|
137
|
+
pattern = f"/{pattern}/g"
|
|
138
|
+
if ignore_case:
|
|
139
|
+
pattern += "i"
|
|
140
|
+
return pattern
|
|
141
|
+
|
|
142
|
+
|
|
130
143
|
def make_js_docstring(value: str) -> str:
|
|
131
144
|
if not value:
|
|
132
145
|
return ""
|
|
@@ -355,10 +368,7 @@ def pre_list_str_replace(node: ExprListStringReplace) -> str:
|
|
|
355
368
|
def pre_str_regex(node: ExprStringRegex) -> str:
|
|
356
369
|
prv, nxt = prev_next_var(node)
|
|
357
370
|
pattern, group, ignore_case = node.unpack_args()
|
|
358
|
-
pattern = pattern
|
|
359
|
-
pattern = f"/{pattern}/g"
|
|
360
|
-
if ignore_case:
|
|
361
|
-
pattern += "i"
|
|
371
|
+
pattern = to_js_regexp(pattern, ignore_case)
|
|
362
372
|
return f"let {nxt} = (new RegExp({pattern})).exec({prv})[{group}];"
|
|
363
373
|
|
|
364
374
|
|
|
@@ -366,10 +376,8 @@ def pre_str_regex(node: ExprStringRegex) -> str:
|
|
|
366
376
|
def pre_str_regex_all(node: ExprStringRegexAll) -> str:
|
|
367
377
|
prv, nxt = prev_next_var(node)
|
|
368
378
|
pattern, ignore_case = node.unpack_args()
|
|
369
|
-
pattern = pattern
|
|
370
|
-
|
|
371
|
-
if ignore_case:
|
|
372
|
-
pattern += "i"
|
|
379
|
+
pattern = to_js_regexp(pattern, ignore_case)
|
|
380
|
+
|
|
373
381
|
return f"let {nxt} = (new RegExp({pattern})).exec({prv});"
|
|
374
382
|
|
|
375
383
|
|
|
@@ -377,8 +385,7 @@ def pre_str_regex_all(node: ExprStringRegexAll) -> str:
|
|
|
377
385
|
def pre_str_regex_sub(node: ExprStringRegexSub) -> str:
|
|
378
386
|
prv, nxt = prev_next_var(node)
|
|
379
387
|
pattern, repl = node.unpack_args()
|
|
380
|
-
pattern = pattern
|
|
381
|
-
pattern = f"/{pattern}/g"
|
|
388
|
+
pattern = to_js_regexp(pattern)
|
|
382
389
|
|
|
383
390
|
return f"let {nxt} = {prv}.replace({pattern}, {repl!r});"
|
|
384
391
|
|
|
@@ -387,8 +394,8 @@ def pre_str_regex_sub(node: ExprStringRegexSub) -> str:
|
|
|
387
394
|
def pre_list_str_regex_sub(node: ExprListStringRegexSub) -> str:
|
|
388
395
|
prv, nxt = prev_next_var(node)
|
|
389
396
|
pattern, repl = node.unpack_args()
|
|
390
|
-
pattern = pattern
|
|
391
|
-
|
|
397
|
+
pattern = to_js_regexp(pattern)
|
|
398
|
+
|
|
392
399
|
return f"let {nxt} = {prv}.map(e => e.replace({pattern}, {repl!r}));"
|
|
393
400
|
|
|
394
401
|
|
|
@@ -454,10 +461,7 @@ def pre_is_contains(node: ExprIsContains) -> str:
|
|
|
454
461
|
def pre_is_regex(node: ExprStringIsRegex) -> str:
|
|
455
462
|
prv, nxt = prev_next_var(node)
|
|
456
463
|
pattern, ignore_case, msg = node.unpack_args()
|
|
457
|
-
pattern = pattern
|
|
458
|
-
pattern = f"/{pattern}/g"
|
|
459
|
-
if ignore_case:
|
|
460
|
-
pattern += "i"
|
|
464
|
+
pattern = to_js_regexp(pattern, ignore_case)
|
|
461
465
|
|
|
462
466
|
expr = f"if ({prv}.match({pattern}) === null) throw new Error({msg!r});"
|
|
463
467
|
if is_last_var_no_ret(node):
|
|
@@ -690,3 +694,23 @@ def pre_list_str_rm_prefix_and_suffix(
|
|
|
690
694
|
f"let {nxt} = {prv}.map((e) => e.startsWith({substr}) ? e.slice({substr}.length) : e);"
|
|
691
695
|
+ f"{nxt} = {nxt}.map((e) => e.endsWith({substr}) ? e.slice(0, -{substr}.length) : e);"
|
|
692
696
|
)
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
@CONVERTER(ExprHasAttr.kind)
|
|
700
|
+
def pre_has_attr(node: ExprHasAttr) -> str:
|
|
701
|
+
prv, nxt = prev_next_var(node)
|
|
702
|
+
key, msg = node.unpack_args()
|
|
703
|
+
expr = f"if (!{prv}?.hasAttribute({key!r}) throw new Error({msg!r});"
|
|
704
|
+
if is_last_var_no_ret(node):
|
|
705
|
+
return expr
|
|
706
|
+
return expr + f"let {nxt} = {prv};"
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
@CONVERTER(ExprListHasAttr.kind)
|
|
710
|
+
def pre_list_has_attr(node: ExprListHasAttr) -> str:
|
|
711
|
+
prv, nxt = prev_next_var(node)
|
|
712
|
+
key, msg = node.unpack_args()
|
|
713
|
+
expr = f"if (!{prv}.every(e => e?.hasAttribute({key!r}))) throw new Error({msg!r});"
|
|
714
|
+
if is_last_var_no_ret(node):
|
|
715
|
+
return expr
|
|
716
|
+
return expr + f"let {nxt} = {prv};"
|
|
@@ -16,6 +16,8 @@ from ssc_codegen.ast_ import (
|
|
|
16
16
|
StructFieldMethod,
|
|
17
17
|
StructPreValidateMethod,
|
|
18
18
|
StructPartDocMethod,
|
|
19
|
+
ExprHasAttr,
|
|
20
|
+
ExprListHasAttr,
|
|
19
21
|
)
|
|
20
22
|
from ssc_codegen.converters.helpers import (
|
|
21
23
|
prev_next_var,
|
|
@@ -173,6 +175,32 @@ def pre_is_css(node: ExprIsCss) -> str:
|
|
|
173
175
|
return expr + "\n" + indent + f"{nxt} = {prv}"
|
|
174
176
|
|
|
175
177
|
|
|
178
|
+
@CONVERTER(ExprHasAttr.kind)
|
|
179
|
+
def pre_has_attr(node: ExprHasAttr) -> str:
|
|
180
|
+
indent = (
|
|
181
|
+
INDENT_DEFAULT_BODY if have_default_expr(node) else INDENT_METHOD_BODY
|
|
182
|
+
)
|
|
183
|
+
prv, nxt = prev_next_var(node)
|
|
184
|
+
key, msg = node.unpack_args()
|
|
185
|
+
expr = indent + f"assert {prv}.get({key!r}, None), {msg!r}"
|
|
186
|
+
if is_last_var_no_ret(node):
|
|
187
|
+
return expr
|
|
188
|
+
return expr + "\n" + indent + f"{nxt} = {prv}"
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@CONVERTER(ExprListHasAttr.kind)
|
|
192
|
+
def pre_list_has_attr(node: ExprListHasAttr) -> str:
|
|
193
|
+
indent = (
|
|
194
|
+
INDENT_DEFAULT_BODY if have_default_expr(node) else INDENT_METHOD_BODY
|
|
195
|
+
)
|
|
196
|
+
prv, nxt = prev_next_var(node)
|
|
197
|
+
key, msg = node.unpack_args()
|
|
198
|
+
expr = indent + f"assert all(i.get({key!r}, None) for i in {prv}), {msg!r}"
|
|
199
|
+
if is_last_var_no_ret(node):
|
|
200
|
+
return expr
|
|
201
|
+
return expr + "\n" + indent + f"{nxt} = {prv}"
|
|
202
|
+
|
|
203
|
+
|
|
176
204
|
# NOT SUPPORT
|
|
177
205
|
@CONVERTER(ExprXpath.kind)
|
|
178
206
|
def pre_xpath(_: ExprXpath) -> str:
|
|
@@ -16,6 +16,8 @@ from ssc_codegen.ast_ import (
|
|
|
16
16
|
StructFieldMethod,
|
|
17
17
|
StructPreValidateMethod,
|
|
18
18
|
StructPartDocMethod,
|
|
19
|
+
ExprListHasAttr,
|
|
20
|
+
ExprHasAttr,
|
|
19
21
|
)
|
|
20
22
|
from ssc_codegen.converters.helpers import (
|
|
21
23
|
prev_next_var,
|
|
@@ -196,3 +198,32 @@ def pre_is_xpath(node: ExprIsXpath) -> str:
|
|
|
196
198
|
if is_last_var_no_ret(node):
|
|
197
199
|
return expr
|
|
198
200
|
return expr + "\n" + indent + f"{nxt} = {prv}"
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@CONVERTER(ExprHasAttr.kind)
|
|
204
|
+
def pre_has_attr(node: ExprHasAttr) -> str:
|
|
205
|
+
indent = (
|
|
206
|
+
INDENT_DEFAULT_BODY if have_default_expr(node) else INDENT_METHOD_BODY
|
|
207
|
+
)
|
|
208
|
+
prv, nxt = prev_next_var(node)
|
|
209
|
+
key, msg = node.unpack_args()
|
|
210
|
+
expr = indent + f"assert {prv}.attrib.get({key!r}, None), {msg!r}"
|
|
211
|
+
if is_last_var_no_ret(node):
|
|
212
|
+
return expr
|
|
213
|
+
return expr + "\n" + indent + f"{nxt} = {prv}"
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@CONVERTER(ExprListHasAttr.kind)
|
|
217
|
+
def pre_list_has_attr(node: ExprListHasAttr) -> str:
|
|
218
|
+
indent = (
|
|
219
|
+
INDENT_DEFAULT_BODY if have_default_expr(node) else INDENT_METHOD_BODY
|
|
220
|
+
)
|
|
221
|
+
prv, nxt = prev_next_var(node)
|
|
222
|
+
key, msg = node.unpack_args()
|
|
223
|
+
expr = (
|
|
224
|
+
indent
|
|
225
|
+
+ f"assert all(i.attrib.get({key!r}, None) for i in {prv}), {msg!r}"
|
|
226
|
+
)
|
|
227
|
+
if is_last_var_no_ret(node):
|
|
228
|
+
return expr
|
|
229
|
+
return expr + "\n" + indent + f"{nxt} = {prv}"
|
|
@@ -16,6 +16,8 @@ from ssc_codegen.ast_ import (
|
|
|
16
16
|
StructFieldMethod,
|
|
17
17
|
StructPreValidateMethod,
|
|
18
18
|
StructPartDocMethod,
|
|
19
|
+
ExprListHasAttr,
|
|
20
|
+
ExprHasAttr,
|
|
19
21
|
)
|
|
20
22
|
from ssc_codegen.converters.helpers import (
|
|
21
23
|
prev_next_var,
|
|
@@ -171,6 +173,35 @@ def pre_is_css(node: ExprIsCss) -> str:
|
|
|
171
173
|
return expr + "\n" + indent + f"{nxt} = {prv}"
|
|
172
174
|
|
|
173
175
|
|
|
176
|
+
@CONVERTER(ExprHasAttr.kind)
|
|
177
|
+
def pre_has_attr(node: ExprHasAttr) -> str:
|
|
178
|
+
indent = (
|
|
179
|
+
INDENT_DEFAULT_BODY if have_default_expr(node) else INDENT_METHOD_BODY
|
|
180
|
+
)
|
|
181
|
+
prv, nxt = prev_next_var(node)
|
|
182
|
+
key, msg = node.unpack_args()
|
|
183
|
+
expr = indent + f"assert {prv}.attributes.get({key!r}, None), {msg!r}"
|
|
184
|
+
if is_last_var_no_ret(node):
|
|
185
|
+
return expr
|
|
186
|
+
return expr + "\n" + indent + f"{nxt} = {prv}"
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@CONVERTER(ExprListHasAttr.kind)
|
|
190
|
+
def pre_list_has_attr(node: ExprListHasAttr) -> str:
|
|
191
|
+
indent = (
|
|
192
|
+
INDENT_DEFAULT_BODY if have_default_expr(node) else INDENT_METHOD_BODY
|
|
193
|
+
)
|
|
194
|
+
prv, nxt = prev_next_var(node)
|
|
195
|
+
key, msg = node.unpack_args()
|
|
196
|
+
expr = (
|
|
197
|
+
indent
|
|
198
|
+
+ f"assert all(i.attributes.get({key!r}, None) for i in {prv}), {msg!r}"
|
|
199
|
+
)
|
|
200
|
+
if is_last_var_no_ret(node):
|
|
201
|
+
return expr
|
|
202
|
+
return expr + "\n" + indent + f"{nxt} = {prv}"
|
|
203
|
+
|
|
204
|
+
|
|
174
205
|
# NOT SUPPORT
|
|
175
206
|
@CONVERTER(ExprXpath.kind)
|
|
176
207
|
def pre_xpath(_: ExprXpath) -> str:
|
|
@@ -100,3 +100,12 @@ let {{ nxt }} = Array.from({ length: {{ snapshot_var }}.snapshotLength }, (_, i)
|
|
|
100
100
|
J2_IS_XPATH = Template("""
|
|
101
101
|
if (document.evaluate("{{ query }}", {{ prv }}, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue === null) throw new Error({{ msg }});
|
|
102
102
|
""")
|
|
103
|
+
# TODO: use in covverter
|
|
104
|
+
JS_HAS_ATTR = Template("""
|
|
105
|
+
if (!{{prv}}?.hasAttribute({{key}}) throw new Error({{ msg }});
|
|
106
|
+
""")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
JS_HAS_ATTR_ALL = Template("""
|
|
110
|
+
if (!{{prv}}.every(e => e?.hasAttribute({{key}}));}) throw new Error({{ msg }});
|
|
111
|
+
""")
|
|
@@ -57,6 +57,8 @@ from ssc_codegen.ast_ import (
|
|
|
57
57
|
ExprListStringRmPrefixAndSuffix,
|
|
58
58
|
ExprListStringAllRegex,
|
|
59
59
|
ExprListStringAnyRegex,
|
|
60
|
+
ExprHasAttr,
|
|
61
|
+
ExprListHasAttr,
|
|
60
62
|
)
|
|
61
63
|
from ssc_codegen.document_utlis import (
|
|
62
64
|
analyze_re_expression,
|
|
@@ -1086,6 +1088,33 @@ class AssertDocument(BaseDocument):
|
|
|
1086
1088
|
)
|
|
1087
1089
|
return self
|
|
1088
1090
|
|
|
1091
|
+
def has_attr(self, key: str, msg: str = "") -> Self:
|
|
1092
|
+
"""assert document has attribute key. If in generated code check failed - throw exception with passed msg
|
|
1093
|
+
|
|
1094
|
+
EXPR DO NOT MODIFY variable
|
|
1095
|
+
|
|
1096
|
+
- accept DOCUMENT, return DOCUMENT
|
|
1097
|
+
- accept LIST_DOCUMENT, return LIST_DOCUMENT
|
|
1098
|
+
"""
|
|
1099
|
+
if self.stack_last_ret == VariableType.DOCUMENT:
|
|
1100
|
+
self._add(ExprHasAttr(kwargs={"key": key, "msg": msg}))
|
|
1101
|
+
elif self.stack_last_ret == VariableType.LIST_DOCUMENT:
|
|
1102
|
+
self._add(ExprListHasAttr(kwargs={"key": key, "msg": msg}))
|
|
1103
|
+
else:
|
|
1104
|
+
LOGGER.warning(
|
|
1105
|
+
"has_attr(%s): Expected type(s) %s got %s",
|
|
1106
|
+
repr(key),
|
|
1107
|
+
(VariableType.DOCUMENT.name, VariableType.LIST_DOCUMENT.name),
|
|
1108
|
+
self.stack_last_ret.name,
|
|
1109
|
+
)
|
|
1110
|
+
self._add(
|
|
1111
|
+
ExprHasAttr(
|
|
1112
|
+
kwargs={"key": key, "msg": msg},
|
|
1113
|
+
ret_type=self.stack_last_ret,
|
|
1114
|
+
)
|
|
1115
|
+
)
|
|
1116
|
+
return self
|
|
1117
|
+
|
|
1089
1118
|
|
|
1090
1119
|
class NestedDocument(BaseDocument):
|
|
1091
1120
|
def sub_parser(self, schema: Type["BaseSchema"]) -> Self:
|
|
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
|