ssc_codegen 0.13.1__tar.gz → 0.13.3__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.13.1 → ssc_codegen-0.13.3}/PKG-INFO +1 -1
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/pyproject.toml +1 -1
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/__init__.py +1 -1
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/ast_build/main.py +4 -1
- ssc_codegen-0.13.3/ssc_codegen/ast_grep_rules/py_drop_prefix_suffix_backport.yml +47 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/cli/ast_grep.py +3 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/cli/main.py +18 -1
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/py_lxml.py +86 -48
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/templates/lua_re_compat.py +1 -1
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/logs.py +2 -2
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/.gitignore +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/LICENSE +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/README.md +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/_compat.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/ast_/__init__.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/ast_/base.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/ast_/nodes_array.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/ast_/nodes_cast.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/ast_/nodes_core.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/ast_/nodes_filter.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/ast_/nodes_selectors.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/ast_/nodes_string.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/ast_/nodes_validate.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/ast_build/__init__.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/ast_build/builder.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/ast_build/utils.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/ast_grep_rules/js_rules.yml +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/ast_grep_rules/py_rules.yml +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/cli/__init__.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/cli/cli_callbacks.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/cli/cli_utils.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/cli/code_callbacks.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/cli/consts.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/cli/runtime_parse_runners.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/compiler.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/__init__.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/base.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/go_goquery.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/helpers.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/js_pure.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/lua_htmlparser.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/py_base.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/py_bs4.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/py_parsel.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/py_selectolax.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/templates/__init__.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/templates/go_goquery.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/templates/js_pure.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/templates/lua_base.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/templates/lua_css_compat.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/templates/py_base.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/document.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/document_utlis.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/json_struct.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/json_to_scc.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/pseudo_selectors.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/schema.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/selector_utils.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/static_checker/__init__.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/static_checker/base.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/static_checker/callbacks.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/str_utils.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/tokens.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ssc_codegen
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.3
|
|
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 ssc_codegen.schema import BaseSchema
|
|
|
13
13
|
from ssc_codegen.static_checker import run_analyze_schema
|
|
14
14
|
import logging
|
|
15
15
|
|
|
16
|
-
LOGGER = logging.getLogger("
|
|
16
|
+
LOGGER = logging.getLogger("ssc_gen")
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def build_ast_module_parser(
|
|
@@ -37,6 +37,9 @@ def build_ast_module_parser(
|
|
|
37
37
|
py_module = exec_module_code(path)
|
|
38
38
|
# check code configs before build AST
|
|
39
39
|
schemas = extract_schemas_from_module(py_module)
|
|
40
|
+
if len(schemas) == 0:
|
|
41
|
+
LOGGER.warning(f"{path} does not contains defined schemas")
|
|
42
|
+
|
|
40
43
|
count_errors = 0
|
|
41
44
|
for schema in schemas:
|
|
42
45
|
errors = run_analyze_schema(schema)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
id: py_drop_prefix_backport
|
|
2
|
+
language: python
|
|
3
|
+
rule:
|
|
4
|
+
pattern:
|
|
5
|
+
context: |-
|
|
6
|
+
def ssc_rm_prefix($$$) -> str:$$$
|
|
7
|
+
fix: ''
|
|
8
|
+
---
|
|
9
|
+
id: py_fix_new_prefix_syntax
|
|
10
|
+
language: python
|
|
11
|
+
rule:
|
|
12
|
+
pattern:
|
|
13
|
+
context: ssc_rm_prefix($V, $P)
|
|
14
|
+
fix: '$V.removeprefix($P)'
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
id: py_drop_suffix_backport
|
|
18
|
+
language: python
|
|
19
|
+
rule:
|
|
20
|
+
pattern:
|
|
21
|
+
context: |-
|
|
22
|
+
def ssc_rm_suffix($$$) -> str:$$$
|
|
23
|
+
fix: ''
|
|
24
|
+
---
|
|
25
|
+
id: py_fix_new_suffix_syntax
|
|
26
|
+
language: python
|
|
27
|
+
rule:
|
|
28
|
+
pattern:
|
|
29
|
+
context: ssc_rm_suffix($V, $P)
|
|
30
|
+
fix: '$V.removesuffix($P)'
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
id: py_drop_prefix_suffix_backport
|
|
34
|
+
language: python
|
|
35
|
+
rule:
|
|
36
|
+
pattern:
|
|
37
|
+
context: |-
|
|
38
|
+
def ssc_rm_prefix_and_suffix($$$) -> str:$$$
|
|
39
|
+
fix: ''
|
|
40
|
+
---
|
|
41
|
+
id: py_fix_new_prefix_suffix_syntax
|
|
42
|
+
language: python
|
|
43
|
+
rule:
|
|
44
|
+
pattern:
|
|
45
|
+
context: ssc_rm_prefix_and_suffix($V, $P1, $P2)
|
|
46
|
+
fix:
|
|
47
|
+
'$V.removeprefix($P1).removesuffix($P2)'
|
|
@@ -4,4 +4,7 @@ from importlib import resources
|
|
|
4
4
|
RULES_PATH = resources.files("ssc_codegen.ast_grep_rules")
|
|
5
5
|
|
|
6
6
|
PY_RULES = str(RULES_PATH.joinpath("py_rules.yml"))
|
|
7
|
+
PY_NEW_RM_PREFIX_SUFFIX_SYNTAX = str(
|
|
8
|
+
RULES_PATH.joinpath("py_drop_prefix_suffix_backport.yml")
|
|
9
|
+
)
|
|
7
10
|
JS_RULES = str(RULES_PATH.joinpath("js_rules.yml"))
|
|
@@ -11,7 +11,11 @@ from ichrome.exceptions import ChromeRuntimeError
|
|
|
11
11
|
from typer import Argument, BadParameter, Option, Typer
|
|
12
12
|
|
|
13
13
|
from ssc_codegen.ast_build import build_ast_module_parser
|
|
14
|
-
from ssc_codegen.cli.ast_grep import
|
|
14
|
+
from ssc_codegen.cli.ast_grep import (
|
|
15
|
+
JS_RULES,
|
|
16
|
+
PY_RULES,
|
|
17
|
+
PY_NEW_RM_PREFIX_SUFFIX_SYNTAX,
|
|
18
|
+
)
|
|
15
19
|
from ssc_codegen.cli.cli_callbacks import cb_check_ssc_files, cb_folder_out
|
|
16
20
|
from ssc_codegen.cli.cli_utils import (
|
|
17
21
|
ConverterLike,
|
|
@@ -168,6 +172,14 @@ def gen_py(
|
|
|
168
172
|
docstring: Annotated[
|
|
169
173
|
bool, Option(help=HELP_DOCSTRING, is_flag=True)
|
|
170
174
|
] = True,
|
|
175
|
+
fmt_prefix_suffix_backport: Annotated[
|
|
176
|
+
bool,
|
|
177
|
+
Option(
|
|
178
|
+
"-frps",
|
|
179
|
+
"--fmt-remove-prefix-suffix",
|
|
180
|
+
help="[PY39+]: replace backport funcs to str.removeprefix(...) and str.removesuffix(...)",
|
|
181
|
+
),
|
|
182
|
+
] = False,
|
|
171
183
|
) -> None:
|
|
172
184
|
converter = import_converter(f"py_{lib.value}")
|
|
173
185
|
if fmt:
|
|
@@ -176,6 +188,11 @@ def gen_py(
|
|
|
176
188
|
"ruff check {} --fix",
|
|
177
189
|
f"ast-grep scan --rule {PY_RULES} -U " + "{}",
|
|
178
190
|
]
|
|
191
|
+
if fmt_prefix_suffix_backport:
|
|
192
|
+
commands.append(
|
|
193
|
+
f"ast-grep scan --rule {PY_NEW_RM_PREFIX_SUFFIX_SYNTAX} -U "
|
|
194
|
+
+ "{}"
|
|
195
|
+
)
|
|
179
196
|
fmt_cmd = create_fmt_cmd(ssc_files, prefix, suffix, out, commands)
|
|
180
197
|
else:
|
|
181
198
|
fmt_cmd = []
|
|
@@ -62,22 +62,44 @@ from ssc_codegen.converters.templates.py_base import IMPORTS_MIN
|
|
|
62
62
|
|
|
63
63
|
CONVERTER = BasePyCodeConverter()
|
|
64
64
|
|
|
65
|
+
"""
|
|
66
|
+
# HtmlElement → сразу принимаем
|
|
67
|
+
if isinstance(document, html.HtmlElement):
|
|
68
|
+
self._document = document
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
# Строка → пробуем парсить
|
|
72
|
+
if isinstance(document, str):
|
|
73
|
+
try:
|
|
74
|
+
self._document = html.fromstring(document.strip() or self.FALLBACK_HTML)
|
|
75
|
+
return
|
|
76
|
+
except (ParserError, ValueError):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
# Всё остальное или ошибка → заглушка
|
|
80
|
+
self._document = html.fromstring(self.FALLBACK_HTML)
|
|
81
|
+
"""
|
|
82
|
+
|
|
65
83
|
|
|
66
84
|
@CONVERTER(StructInitMethod.kind)
|
|
67
85
|
def pre_init(_node: StructInitMethod) -> str:
|
|
68
|
-
|
|
86
|
+
parts = [
|
|
69
87
|
INDENT_METHOD
|
|
70
|
-
+ "def __init__(self, document: Union[str,
|
|
71
|
-
+
|
|
72
|
-
+ "self._document =
|
|
73
|
-
|
|
88
|
+
+ "def __init__(self, document: Union[str, html.HtmlElement]) -> None:",
|
|
89
|
+
INDENT_METHOD * 2 + "if isinstance(document, html.HtmlElement):",
|
|
90
|
+
INDENT_METHOD * 3 + "self._document = document",
|
|
91
|
+
INDENT_METHOD * 2 + "elif isinstance(document, str):",
|
|
92
|
+
INDENT_METHOD * 3
|
|
93
|
+
+ "self._document = html.fromstring(document.strip() or FALLBACK_HTML_STR)",
|
|
94
|
+
# in codegen, passed only str or html.HtmlElement objects
|
|
95
|
+
]
|
|
96
|
+
return "\n".join(parts)
|
|
74
97
|
|
|
75
98
|
|
|
76
99
|
@CONVERTER(StructPreValidateMethod.kind)
|
|
77
100
|
def pre_struct_pre_validate(_node: StructPreValidateMethod) -> str:
|
|
78
101
|
return (
|
|
79
|
-
INDENT_METHOD
|
|
80
|
-
+ "def _pre_validate(self, v: Union[etree._Element, List[etree._Element]]) -> None:"
|
|
102
|
+
INDENT_METHOD + "def _pre_validate(self, v: html.HtmlElement) -> None:"
|
|
81
103
|
)
|
|
82
104
|
|
|
83
105
|
|
|
@@ -85,7 +107,7 @@ def pre_struct_pre_validate(_node: StructPreValidateMethod) -> str:
|
|
|
85
107
|
def pre_struct_part_doc_method(_node: StructPartDocMethod) -> str:
|
|
86
108
|
return (
|
|
87
109
|
INDENT_METHOD
|
|
88
|
-
+ "def _split_doc(self, v:
|
|
110
|
+
+ "def _split_doc(self, v: html.HtmlElement) -> List[html.HtmlElement]:"
|
|
89
111
|
)
|
|
90
112
|
|
|
91
113
|
|
|
@@ -96,13 +118,21 @@ def pre_struct_field_method(node: StructFieldMethod) -> str:
|
|
|
96
118
|
name = MAGIC_METHODS.get(name, name)
|
|
97
119
|
return (
|
|
98
120
|
INDENT_METHOD
|
|
99
|
-
+ f"def _parse_{name}(self, v:
|
|
121
|
+
+ f"def _parse_{name}(self, v: html.HtmlElement) -> {type_}:"
|
|
100
122
|
)
|
|
101
123
|
|
|
102
124
|
|
|
103
125
|
@CONVERTER(ModuleImports.kind)
|
|
104
126
|
def pre_imports(_: ModuleImports) -> str:
|
|
105
|
-
|
|
127
|
+
# lxml throw parse error if passed empty string
|
|
128
|
+
# most mainsteram html parser libs use `FALLBACK_HTML_STR` stub value
|
|
129
|
+
return (
|
|
130
|
+
IMPORTS_MIN
|
|
131
|
+
+ """from lxml import html, etree
|
|
132
|
+
|
|
133
|
+
FALLBACK_HTML_STR = "<html><body></body></html>"
|
|
134
|
+
"""
|
|
135
|
+
)
|
|
106
136
|
|
|
107
137
|
|
|
108
138
|
@CONVERTER(ExprCss.kind)
|
|
@@ -112,7 +142,7 @@ def pre_css(node: ExprCss) -> str:
|
|
|
112
142
|
)
|
|
113
143
|
prv, nxt = prev_next_var(node)
|
|
114
144
|
query = py_get_classvar_hook_or_value(node, "query")
|
|
115
|
-
return indent + f"{nxt} =
|
|
145
|
+
return indent + f"{nxt} = {prv}.cssselect({query})[0]"
|
|
116
146
|
|
|
117
147
|
|
|
118
148
|
@CONVERTER(ExprCssAll.kind)
|
|
@@ -122,7 +152,7 @@ def pre_css_all(node: ExprCssAll) -> str:
|
|
|
122
152
|
)
|
|
123
153
|
prv, nxt = prev_next_var(node)
|
|
124
154
|
query = py_get_classvar_hook_or_value(node, "query")
|
|
125
|
-
return indent + f"{nxt} =
|
|
155
|
+
return indent + f"{nxt} = {prv}.cssselect({query})"
|
|
126
156
|
|
|
127
157
|
|
|
128
158
|
@CONVERTER(ExprXpath.kind)
|
|
@@ -154,8 +184,9 @@ def pre_html_attr(node: ExprGetHtmlAttr) -> str:
|
|
|
154
184
|
keys = node.kwargs["key"]
|
|
155
185
|
if len(keys) == 1:
|
|
156
186
|
key = keys[0]
|
|
157
|
-
return indent + f"{nxt} = {prv}.
|
|
158
|
-
|
|
187
|
+
return indent + f"{nxt} = {prv}.get({key!r})"
|
|
188
|
+
key_accesses = [f"{prv}.get({k!r})" for k in keys]
|
|
189
|
+
return indent + f"{nxt} = [{', '.join(key_accesses)}]"
|
|
159
190
|
|
|
160
191
|
|
|
161
192
|
@CONVERTER(ExprGetHtmlAttrAll.kind)
|
|
@@ -167,8 +198,8 @@ def pre_html_attr_all(node: ExprGetHtmlAttrAll) -> str:
|
|
|
167
198
|
keys = node.kwargs["key"]
|
|
168
199
|
if len(keys) == 1:
|
|
169
200
|
key = keys[0]
|
|
170
|
-
return indent + f"{nxt} = [e.
|
|
171
|
-
return indent + f"{nxt} = [e.
|
|
201
|
+
return indent + f"{nxt} = [e.get({key!r}) for e in {prv}]"
|
|
202
|
+
return indent + f"{nxt} = [e.get(k) for e in {prv} for k in {keys}]"
|
|
172
203
|
|
|
173
204
|
|
|
174
205
|
@CONVERTER(ExprGetHtmlText.kind)
|
|
@@ -177,7 +208,7 @@ def pre_html_text(node: ExprGetHtmlText) -> str:
|
|
|
177
208
|
INDENT_DEFAULT_BODY if have_default_expr(node) else INDENT_METHOD_BODY
|
|
178
209
|
)
|
|
179
210
|
prv, nxt = prev_next_var(node)
|
|
180
|
-
return indent + f"{nxt} =
|
|
211
|
+
return indent + f"{nxt} = {prv}.text_content()"
|
|
181
212
|
|
|
182
213
|
|
|
183
214
|
@CONVERTER(ExprGetHtmlTextAll.kind)
|
|
@@ -186,7 +217,7 @@ def pre_html_text_all(node: ExprGetHtmlTextAll) -> str:
|
|
|
186
217
|
INDENT_DEFAULT_BODY if have_default_expr(node) else INDENT_METHOD_BODY
|
|
187
218
|
)
|
|
188
219
|
prv, nxt = prev_next_var(node)
|
|
189
|
-
return indent + f"{nxt} = [
|
|
220
|
+
return indent + f"{nxt} = [e.text_content() for e in {prv}]"
|
|
190
221
|
|
|
191
222
|
|
|
192
223
|
@CONVERTER(ExprGetHtmlRaw.kind)
|
|
@@ -195,7 +226,7 @@ def pre_html_raw(node: ExprGetHtmlRaw) -> str:
|
|
|
195
226
|
INDENT_DEFAULT_BODY if have_default_expr(node) else INDENT_METHOD_BODY
|
|
196
227
|
)
|
|
197
228
|
prv, nxt = prev_next_var(node)
|
|
198
|
-
return indent + f"{nxt} =
|
|
229
|
+
return indent + f"{nxt} = html.tostring({prv}, encoding='unicode')"
|
|
199
230
|
|
|
200
231
|
|
|
201
232
|
@CONVERTER(ExprGetHtmlRawAll.kind)
|
|
@@ -206,7 +237,7 @@ def pre_html_raw_all(node: ExprGetHtmlRawAll) -> str:
|
|
|
206
237
|
prv, nxt = prev_next_var(node)
|
|
207
238
|
return (
|
|
208
239
|
indent
|
|
209
|
-
+ f"{nxt} = [
|
|
240
|
+
+ f"{nxt} = [html.tostring(e, encoding='unicode') for e in {prv}]"
|
|
210
241
|
)
|
|
211
242
|
|
|
212
243
|
|
|
@@ -220,9 +251,9 @@ def pre_is_css(node: ExprIsCss) -> str:
|
|
|
220
251
|
msg = py_get_classvar_hook_or_value(node, "msg")
|
|
221
252
|
invert = node.kwargs["invert"]
|
|
222
253
|
|
|
223
|
-
expr = f"
|
|
254
|
+
expr = f"{prv}.cssselect({query})"
|
|
224
255
|
if invert:
|
|
225
|
-
expr = f"not
|
|
256
|
+
expr = f"not {expr}"
|
|
226
257
|
|
|
227
258
|
expr = indent + f"assert {expr}, {msg}"
|
|
228
259
|
if is_last_var_no_ret(node):
|
|
@@ -255,19 +286,19 @@ def pre_has_attr(node: ExprHasAttr) -> str:
|
|
|
255
286
|
INDENT_DEFAULT_BODY if have_default_expr(node) else INDENT_METHOD_BODY
|
|
256
287
|
)
|
|
257
288
|
prv, nxt = prev_next_var(node)
|
|
258
|
-
key, msg = node.unpack_args()
|
|
259
289
|
key = py_get_classvar_hook_or_value(node, "key")
|
|
260
290
|
msg = py_get_classvar_hook_or_value(node, "msg")
|
|
261
291
|
invert = node.kwargs["invert"]
|
|
262
292
|
|
|
263
|
-
expr = f"{prv}.
|
|
293
|
+
expr = f"{prv}.get({key}) is not None"
|
|
264
294
|
if invert:
|
|
265
295
|
expr = f"not ({expr})"
|
|
266
296
|
|
|
267
297
|
expr = indent + f"assert {expr}, {msg}"
|
|
268
298
|
if is_last_var_no_ret(node):
|
|
269
299
|
return expr
|
|
270
|
-
|
|
300
|
+
# Исправлено: добавлена запятая в join
|
|
301
|
+
return "\n".join([expr, indent + f"{nxt} = {prv}"])
|
|
271
302
|
|
|
272
303
|
|
|
273
304
|
@CONVERTER(ExprListHasAttr.kind)
|
|
@@ -280,7 +311,7 @@ def pre_list_has_attr(node: ExprListHasAttr) -> str:
|
|
|
280
311
|
msg = py_get_classvar_hook_or_value(node, "msg")
|
|
281
312
|
invert = node.kwargs["invert"]
|
|
282
313
|
|
|
283
|
-
expr = f"all(i.
|
|
314
|
+
expr = f"all(i.get({key}) is not None for i in {prv})"
|
|
284
315
|
if invert:
|
|
285
316
|
expr = f"not ({expr})"
|
|
286
317
|
|
|
@@ -318,7 +349,7 @@ def pre_css_remove(node: ExprCssElementRemove) -> str:
|
|
|
318
349
|
|
|
319
350
|
return (
|
|
320
351
|
indent
|
|
321
|
-
+ f"[e.getparent().remove(e) for e in
|
|
352
|
+
+ f"[e.getparent().remove(e) for e in {prv}.cssselect({query}) if e.getparent() is not None]\n"
|
|
322
353
|
+ indent
|
|
323
354
|
+ f"{nxt} = {prv}"
|
|
324
355
|
)
|
|
@@ -365,9 +396,9 @@ def pre_doc_filter_css(node: FilterDocCss) -> str:
|
|
|
365
396
|
def pre_doc_filter_has_attr(node: FilterDocHasAttr) -> str:
|
|
366
397
|
keys = node.kwargs["keys"]
|
|
367
398
|
if len(keys) == 1:
|
|
368
|
-
expr = f"{keys[0]!r}
|
|
399
|
+
expr = f"e.get({keys[0]!r}) is not None"
|
|
369
400
|
else:
|
|
370
|
-
expr = f"any(i
|
|
401
|
+
expr = f"any(e.get(i) is not None for i in {keys})"
|
|
371
402
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
372
403
|
return "and " + expr
|
|
373
404
|
return expr
|
|
@@ -377,9 +408,9 @@ def pre_doc_filter_has_attr(node: FilterDocHasAttr) -> str:
|
|
|
377
408
|
def pre_doc_filter_attr_eq(node: FilterDocAttrEqual) -> str:
|
|
378
409
|
key, values = node.unpack_args()
|
|
379
410
|
if len(values) == 1:
|
|
380
|
-
expr = f"e.
|
|
411
|
+
expr = f"e.get({key!r}) == {values[0]!r}"
|
|
381
412
|
else:
|
|
382
|
-
expr = f"e.
|
|
413
|
+
expr = f"e.get({key!r}) in {values}"
|
|
383
414
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
384
415
|
return "and " + expr
|
|
385
416
|
return expr
|
|
@@ -389,9 +420,9 @@ def pre_doc_filter_attr_eq(node: FilterDocAttrEqual) -> str:
|
|
|
389
420
|
def pre_doc_filter_attr_contains(node: FilterDocAttrContains) -> str:
|
|
390
421
|
key, values = node.unpack_args()
|
|
391
422
|
if len(values) == 1:
|
|
392
|
-
expr = f"{values[0]!r} in e.
|
|
423
|
+
expr = f"{values[0]!r} in (e.get({key!r}) or '')"
|
|
393
424
|
else:
|
|
394
|
-
expr = f"any(i in e.
|
|
425
|
+
expr = f"any(i in (e.get({key!r}) or '') for i in {values})"
|
|
395
426
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
396
427
|
return "and " + expr
|
|
397
428
|
return expr
|
|
@@ -401,10 +432,10 @@ def pre_doc_filter_attr_contains(node: FilterDocAttrContains) -> str:
|
|
|
401
432
|
def pre_doc_filter_attr_starts(node: FilterDocAttrContains) -> str:
|
|
402
433
|
key, values = node.unpack_args()
|
|
403
434
|
if len(values) == 1:
|
|
404
|
-
expr = f"e.
|
|
435
|
+
expr = f"(e.get({key!r}) or '').startswith({values[0]!r})"
|
|
405
436
|
else:
|
|
406
437
|
# str.startswith() arg allow tuple[str, ...]
|
|
407
|
-
expr = f"e.
|
|
438
|
+
expr = f"(e.get({key!r}) or '').startswith({values})"
|
|
408
439
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
409
440
|
return "and " + expr
|
|
410
441
|
return expr
|
|
@@ -414,10 +445,10 @@ def pre_doc_filter_attr_starts(node: FilterDocAttrContains) -> str:
|
|
|
414
445
|
def pre_doc_filter_attr_ends(node: FilterDocAttrContains) -> str:
|
|
415
446
|
key, values = node.unpack_args()
|
|
416
447
|
if len(values) == 1:
|
|
417
|
-
expr = f"e.
|
|
448
|
+
expr = f"(e.get({key!r}) or '').endswith({values[0]!r})"
|
|
418
449
|
else:
|
|
419
450
|
# str.endswith() arg allow tuple[str, ...]
|
|
420
|
-
expr = f"e.
|
|
451
|
+
expr = f"(e.get({key!r}) or '').endswith({values})"
|
|
421
452
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
422
453
|
return "and " + expr
|
|
423
454
|
return expr
|
|
@@ -427,11 +458,12 @@ def pre_doc_filter_attr_ends(node: FilterDocAttrContains) -> str:
|
|
|
427
458
|
def pre_doc_filter_attr_re(node: FilterDocAttrRegex) -> str:
|
|
428
459
|
key, pattern, ignore_case = node.unpack_args()
|
|
429
460
|
flags = py_regex_flags(ignore_case)
|
|
461
|
+
|
|
462
|
+
attr_value = f"e.get({key!r}) or ''"
|
|
430
463
|
if flags:
|
|
431
|
-
expr = f"re.search({pattern!r},
|
|
464
|
+
expr = f"re.search({pattern!r}, {attr_value}, {flags})"
|
|
432
465
|
else:
|
|
433
|
-
expr = f"re.search({pattern!r},
|
|
434
|
-
expr = f"bool({expr})"
|
|
466
|
+
expr = f"re.search({pattern!r}, {attr_value})"
|
|
435
467
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
436
468
|
return "and " + expr
|
|
437
469
|
return expr
|
|
@@ -442,10 +474,12 @@ def pre_doc_filter_is_regex_text(node: FilterDocIsRegexText) -> str:
|
|
|
442
474
|
pattern, ignore_case = node.unpack_args()
|
|
443
475
|
|
|
444
476
|
flags = py_regex_flags(ignore_case)
|
|
477
|
+
|
|
478
|
+
text_content_call = "e.text_content()"
|
|
445
479
|
if flags:
|
|
446
|
-
expr = f"re.search({pattern!r},
|
|
480
|
+
expr = f"re.search({pattern!r}, {text_content_call}, {flags})"
|
|
447
481
|
else:
|
|
448
|
-
expr = f"re.search({pattern!r},
|
|
482
|
+
expr = f"re.search({pattern!r}, {text_content_call})"
|
|
449
483
|
expr = f"bool({expr})"
|
|
450
484
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
451
485
|
return "and " + expr
|
|
@@ -457,10 +491,11 @@ def pre_doc_filter_is_regex_raw(node: FilterDocIsRegexText) -> str:
|
|
|
457
491
|
pattern, ignore_case = node.unpack_args()
|
|
458
492
|
|
|
459
493
|
flags = py_regex_flags(ignore_case)
|
|
494
|
+
raw_content_call = "etree.tostring(e, encoding='unicode')"
|
|
460
495
|
if flags:
|
|
461
|
-
expr = f"re.search({pattern!r},
|
|
496
|
+
expr = f"re.search({pattern!r}, {raw_content_call}, {flags})"
|
|
462
497
|
else:
|
|
463
|
-
expr = f"re.search({pattern!r},
|
|
498
|
+
expr = f"re.search({pattern!r}, {raw_content_call})"
|
|
464
499
|
expr = f"bool({expr})"
|
|
465
500
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
466
501
|
return "and " + expr
|
|
@@ -470,10 +505,12 @@ def pre_doc_filter_is_regex_raw(node: FilterDocIsRegexText) -> str:
|
|
|
470
505
|
@CONVERTER(FilterDocHasText.kind)
|
|
471
506
|
def pre_doc_filter_has_text(node: FilterDocHasText) -> str:
|
|
472
507
|
values = node.kwargs["values"]
|
|
508
|
+
|
|
509
|
+
text_content_call = "e.text_content()"
|
|
473
510
|
if len(values) == 1:
|
|
474
|
-
expr = f"{values[0]!r} in
|
|
511
|
+
expr = f"{values[0]!r} in {text_content_call}"
|
|
475
512
|
else:
|
|
476
|
-
expr = f"any(i in
|
|
513
|
+
expr = f"any(i in {text_content_call} for i in {values})"
|
|
477
514
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
478
515
|
return "and " + expr
|
|
479
516
|
return expr
|
|
@@ -482,11 +519,12 @@ def pre_doc_filter_has_text(node: FilterDocHasText) -> str:
|
|
|
482
519
|
@CONVERTER(FilterDocHasRaw.kind)
|
|
483
520
|
def pre_doc_filter_has_raw(node: FilterDocHasText) -> str:
|
|
484
521
|
values = node.kwargs["values"]
|
|
522
|
+
|
|
485
523
|
if len(values) == 1:
|
|
486
|
-
expr = f"{values[0]!r} in
|
|
524
|
+
expr = f"{values[0]!r} in html.tostring(e, encoding='unicode')"
|
|
487
525
|
else:
|
|
488
526
|
expr = (
|
|
489
|
-
f"any(i in
|
|
527
|
+
f"any(i in html.tostring(e, encoding='unicode') for i in {values})"
|
|
490
528
|
)
|
|
491
529
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
492
530
|
return "and " + expr
|
|
@@ -26,8 +26,8 @@ def setup_logger(
|
|
|
26
26
|
name: str = "ssc_gen",
|
|
27
27
|
level: int = logging.INFO,
|
|
28
28
|
colored: bool = True,
|
|
29
|
-
fmt: str = "[%(levelname)-8s] %(name)s: %(
|
|
30
|
-
datefmt: str = "%Y-%m-%d %H:%M:%S",
|
|
29
|
+
fmt: str = "[%(levelname)-8s] %(name)s: - %(message)s", # %(asctime)s
|
|
30
|
+
datefmt: str | None = None, # "%Y-%m-%d %H:%M:%S",
|
|
31
31
|
) -> logging.Logger:
|
|
32
32
|
logger = logging.getLogger(name)
|
|
33
33
|
logger.setLevel(level)
|
|
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
|
{ssc_codegen-0.13.1 → ssc_codegen-0.13.3}/ssc_codegen/converters/templates/lua_css_compat.py
RENAMED
|
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
|