ssc_codegen 0.13.1__tar.gz → 0.13.2__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.13.1 → ssc_codegen-0.13.2}/PKG-INFO +1 -1
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/pyproject.toml +1 -1
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/__init__.py +1 -1
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/ast_build/main.py +4 -1
- ssc_codegen-0.13.2/ssc_codegen/ast_grep_rules/py_drop_prefix_suffix_backport.yml +47 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/cli/ast_grep.py +1 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/cli/main.py +8 -1
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/py_lxml.py +52 -45
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/templates/lua_re_compat.py +1 -1
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/logs.py +2 -2
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/.gitignore +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/LICENSE +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/README.md +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/_compat.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/ast_/__init__.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/ast_/base.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/ast_/nodes_array.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/ast_/nodes_cast.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/ast_/nodes_core.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/ast_/nodes_filter.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/ast_/nodes_selectors.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/ast_/nodes_string.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/ast_/nodes_validate.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/ast_build/__init__.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/ast_build/builder.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/ast_build/utils.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/ast_grep_rules/js_rules.yml +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/ast_grep_rules/py_rules.yml +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/cli/__init__.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/cli/cli_callbacks.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/cli/cli_utils.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/cli/code_callbacks.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/cli/consts.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/cli/runtime_parse_runners.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/compiler.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/__init__.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/base.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/go_goquery.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/helpers.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/js_pure.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/lua_htmlparser.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/py_base.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/py_bs4.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/py_parsel.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/py_selectolax.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/templates/__init__.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/templates/go_goquery.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/templates/js_pure.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/templates/lua_base.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/templates/lua_css_compat.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/converters/templates/py_base.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/document.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/document_utlis.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/json_struct.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/json_to_scc.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/pseudo_selectors.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/schema.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/selector_utils.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/static_checker/__init__.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/static_checker/base.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/static_checker/callbacks.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/ssc_codegen/str_utils.py +0 -0
- {ssc_codegen-0.13.1 → ssc_codegen-0.13.2}/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.2
|
|
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,5 @@ 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(RULES_PATH.joinpath("py_drop_prefix_suffix_backport.yml"))
|
|
7
8
|
JS_RULES = str(RULES_PATH.joinpath("js_rules.yml"))
|
|
@@ -11,7 +11,7 @@ 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 JS_RULES, PY_RULES
|
|
14
|
+
from ssc_codegen.cli.ast_grep import JS_RULES, PY_RULES, PY_NEW_RM_PREFIX_SUFFIX_SYNTAX
|
|
15
15
|
from ssc_codegen.cli.cli_callbacks import cb_check_ssc_files, cb_folder_out
|
|
16
16
|
from ssc_codegen.cli.cli_utils import (
|
|
17
17
|
ConverterLike,
|
|
@@ -168,6 +168,9 @@ def gen_py(
|
|
|
168
168
|
docstring: Annotated[
|
|
169
169
|
bool, Option(help=HELP_DOCSTRING, is_flag=True)
|
|
170
170
|
] = True,
|
|
171
|
+
fmt_prefix_suffix_backport: Annotated[
|
|
172
|
+
bool, Option("-frps", '--fmt-remove-prefix-suffix',
|
|
173
|
+
help="[PY39+]: replace backport funcs to str.removeprefix(...) and str.removesuffix(...)")] = False
|
|
171
174
|
) -> None:
|
|
172
175
|
converter = import_converter(f"py_{lib.value}")
|
|
173
176
|
if fmt:
|
|
@@ -176,10 +179,14 @@ def gen_py(
|
|
|
176
179
|
"ruff check {} --fix",
|
|
177
180
|
f"ast-grep scan --rule {PY_RULES} -U " + "{}",
|
|
178
181
|
]
|
|
182
|
+
if fmt_prefix_suffix_backport:
|
|
183
|
+
commands.append(f"ast-grep scan --rule {PY_NEW_RM_PREFIX_SUFFIX_SYNTAX} -U " + "{}")
|
|
179
184
|
fmt_cmd = create_fmt_cmd(ssc_files, prefix, suffix, out, commands)
|
|
180
185
|
else:
|
|
181
186
|
fmt_cmd = []
|
|
182
187
|
|
|
188
|
+
|
|
189
|
+
|
|
183
190
|
generate_code(
|
|
184
191
|
converter=converter,
|
|
185
192
|
out=out,
|
|
@@ -67,17 +67,16 @@ CONVERTER = BasePyCodeConverter()
|
|
|
67
67
|
def pre_init(_node: StructInitMethod) -> str:
|
|
68
68
|
return (
|
|
69
69
|
INDENT_METHOD
|
|
70
|
-
+ "def __init__(self, document: Union[str,
|
|
70
|
+
+ "def __init__(self, document: Union[str, html.HtmlElement]) -> None:\n"
|
|
71
71
|
+ INDENT_METHOD_BODY
|
|
72
|
-
+ "self._document =
|
|
72
|
+
+ "self._document = html.fromstring(document) if isinstance(document, str) else document"
|
|
73
73
|
)
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
@CONVERTER(StructPreValidateMethod.kind)
|
|
77
77
|
def pre_struct_pre_validate(_node: StructPreValidateMethod) -> str:
|
|
78
78
|
return (
|
|
79
|
-
INDENT_METHOD
|
|
80
|
-
+ "def _pre_validate(self, v: Union[etree._Element, List[etree._Element]]) -> None:"
|
|
79
|
+
INDENT_METHOD + "def _pre_validate(self, v: html.HtmlElement) -> None:"
|
|
81
80
|
)
|
|
82
81
|
|
|
83
82
|
|
|
@@ -85,7 +84,7 @@ def pre_struct_pre_validate(_node: StructPreValidateMethod) -> str:
|
|
|
85
84
|
def pre_struct_part_doc_method(_node: StructPartDocMethod) -> str:
|
|
86
85
|
return (
|
|
87
86
|
INDENT_METHOD
|
|
88
|
-
+ "def _split_doc(self, v:
|
|
87
|
+
+ "def _split_doc(self, v: html.HtmlElement) -> List[html.HtmlElement]:"
|
|
89
88
|
)
|
|
90
89
|
|
|
91
90
|
|
|
@@ -96,13 +95,13 @@ def pre_struct_field_method(node: StructFieldMethod) -> str:
|
|
|
96
95
|
name = MAGIC_METHODS.get(name, name)
|
|
97
96
|
return (
|
|
98
97
|
INDENT_METHOD
|
|
99
|
-
+ f"def _parse_{name}(self, v:
|
|
98
|
+
+ f"def _parse_{name}(self, v: html.HtmlElement) -> {type_}:"
|
|
100
99
|
)
|
|
101
100
|
|
|
102
101
|
|
|
103
102
|
@CONVERTER(ModuleImports.kind)
|
|
104
103
|
def pre_imports(_: ModuleImports) -> str:
|
|
105
|
-
return IMPORTS_MIN + """from lxml import
|
|
104
|
+
return IMPORTS_MIN + """from lxml import html, etree"""
|
|
106
105
|
|
|
107
106
|
|
|
108
107
|
@CONVERTER(ExprCss.kind)
|
|
@@ -112,7 +111,7 @@ def pre_css(node: ExprCss) -> str:
|
|
|
112
111
|
)
|
|
113
112
|
prv, nxt = prev_next_var(node)
|
|
114
113
|
query = py_get_classvar_hook_or_value(node, "query")
|
|
115
|
-
return indent + f"{nxt} =
|
|
114
|
+
return indent + f"{nxt} = {prv}.cssselect({query})[0]"
|
|
116
115
|
|
|
117
116
|
|
|
118
117
|
@CONVERTER(ExprCssAll.kind)
|
|
@@ -122,7 +121,7 @@ def pre_css_all(node: ExprCssAll) -> str:
|
|
|
122
121
|
)
|
|
123
122
|
prv, nxt = prev_next_var(node)
|
|
124
123
|
query = py_get_classvar_hook_or_value(node, "query")
|
|
125
|
-
return indent + f"{nxt} =
|
|
124
|
+
return indent + f"{nxt} = {prv}.cssselect({query})"
|
|
126
125
|
|
|
127
126
|
|
|
128
127
|
@CONVERTER(ExprXpath.kind)
|
|
@@ -154,8 +153,9 @@ def pre_html_attr(node: ExprGetHtmlAttr) -> str:
|
|
|
154
153
|
keys = node.kwargs["key"]
|
|
155
154
|
if len(keys) == 1:
|
|
156
155
|
key = keys[0]
|
|
157
|
-
return indent + f"{nxt} = {prv}.
|
|
158
|
-
|
|
156
|
+
return indent + f"{nxt} = {prv}.get({key!r})"
|
|
157
|
+
key_accesses = [f"{prv}.get({k!r})" for k in keys]
|
|
158
|
+
return indent + f"{nxt} = [{', '.join(key_accesses)}]"
|
|
159
159
|
|
|
160
160
|
|
|
161
161
|
@CONVERTER(ExprGetHtmlAttrAll.kind)
|
|
@@ -167,8 +167,8 @@ def pre_html_attr_all(node: ExprGetHtmlAttrAll) -> str:
|
|
|
167
167
|
keys = node.kwargs["key"]
|
|
168
168
|
if len(keys) == 1:
|
|
169
169
|
key = keys[0]
|
|
170
|
-
return indent + f"{nxt} = [e.
|
|
171
|
-
return indent + f"{nxt} = [e.
|
|
170
|
+
return indent + f"{nxt} = [e.get({key!r}) for e in {prv}]"
|
|
171
|
+
return indent + f"{nxt} = [e.get(k) for e in {prv} for k in {keys}]"
|
|
172
172
|
|
|
173
173
|
|
|
174
174
|
@CONVERTER(ExprGetHtmlText.kind)
|
|
@@ -177,7 +177,7 @@ def pre_html_text(node: ExprGetHtmlText) -> str:
|
|
|
177
177
|
INDENT_DEFAULT_BODY if have_default_expr(node) else INDENT_METHOD_BODY
|
|
178
178
|
)
|
|
179
179
|
prv, nxt = prev_next_var(node)
|
|
180
|
-
return indent + f"{nxt} =
|
|
180
|
+
return indent + f"{nxt} = {prv}.text_content()"
|
|
181
181
|
|
|
182
182
|
|
|
183
183
|
@CONVERTER(ExprGetHtmlTextAll.kind)
|
|
@@ -186,7 +186,7 @@ def pre_html_text_all(node: ExprGetHtmlTextAll) -> str:
|
|
|
186
186
|
INDENT_DEFAULT_BODY if have_default_expr(node) else INDENT_METHOD_BODY
|
|
187
187
|
)
|
|
188
188
|
prv, nxt = prev_next_var(node)
|
|
189
|
-
return indent + f"{nxt} = [
|
|
189
|
+
return indent + f"{nxt} = [e.text_content() for e in {prv}]"
|
|
190
190
|
|
|
191
191
|
|
|
192
192
|
@CONVERTER(ExprGetHtmlRaw.kind)
|
|
@@ -195,7 +195,7 @@ def pre_html_raw(node: ExprGetHtmlRaw) -> str:
|
|
|
195
195
|
INDENT_DEFAULT_BODY if have_default_expr(node) else INDENT_METHOD_BODY
|
|
196
196
|
)
|
|
197
197
|
prv, nxt = prev_next_var(node)
|
|
198
|
-
return indent + f"{nxt} =
|
|
198
|
+
return indent + f"{nxt} = html.tostring({prv}, encoding='unicode')"
|
|
199
199
|
|
|
200
200
|
|
|
201
201
|
@CONVERTER(ExprGetHtmlRawAll.kind)
|
|
@@ -206,7 +206,7 @@ def pre_html_raw_all(node: ExprGetHtmlRawAll) -> str:
|
|
|
206
206
|
prv, nxt = prev_next_var(node)
|
|
207
207
|
return (
|
|
208
208
|
indent
|
|
209
|
-
+ f"{nxt} = [
|
|
209
|
+
+ f"{nxt} = [html.tostring(e, encoding='unicode') for e in {prv}]"
|
|
210
210
|
)
|
|
211
211
|
|
|
212
212
|
|
|
@@ -220,9 +220,9 @@ def pre_is_css(node: ExprIsCss) -> str:
|
|
|
220
220
|
msg = py_get_classvar_hook_or_value(node, "msg")
|
|
221
221
|
invert = node.kwargs["invert"]
|
|
222
222
|
|
|
223
|
-
expr = f"
|
|
223
|
+
expr = f"{prv}.cssselect({query})"
|
|
224
224
|
if invert:
|
|
225
|
-
expr = f"not
|
|
225
|
+
expr = f"not {expr}"
|
|
226
226
|
|
|
227
227
|
expr = indent + f"assert {expr}, {msg}"
|
|
228
228
|
if is_last_var_no_ret(node):
|
|
@@ -255,19 +255,19 @@ def pre_has_attr(node: ExprHasAttr) -> str:
|
|
|
255
255
|
INDENT_DEFAULT_BODY if have_default_expr(node) else INDENT_METHOD_BODY
|
|
256
256
|
)
|
|
257
257
|
prv, nxt = prev_next_var(node)
|
|
258
|
-
key, msg = node.unpack_args()
|
|
259
258
|
key = py_get_classvar_hook_or_value(node, "key")
|
|
260
259
|
msg = py_get_classvar_hook_or_value(node, "msg")
|
|
261
260
|
invert = node.kwargs["invert"]
|
|
262
261
|
|
|
263
|
-
expr = f"{prv}.
|
|
262
|
+
expr = f"{prv}.get({key}) is not None"
|
|
264
263
|
if invert:
|
|
265
264
|
expr = f"not ({expr})"
|
|
266
265
|
|
|
267
266
|
expr = indent + f"assert {expr}, {msg}"
|
|
268
267
|
if is_last_var_no_ret(node):
|
|
269
268
|
return expr
|
|
270
|
-
|
|
269
|
+
# Исправлено: добавлена запятая в join
|
|
270
|
+
return "\n".join([expr, indent + f"{nxt} = {prv}"])
|
|
271
271
|
|
|
272
272
|
|
|
273
273
|
@CONVERTER(ExprListHasAttr.kind)
|
|
@@ -280,7 +280,7 @@ def pre_list_has_attr(node: ExprListHasAttr) -> str:
|
|
|
280
280
|
msg = py_get_classvar_hook_or_value(node, "msg")
|
|
281
281
|
invert = node.kwargs["invert"]
|
|
282
282
|
|
|
283
|
-
expr = f"all(i.
|
|
283
|
+
expr = f"all(i.get({key}) is not None for i in {prv})"
|
|
284
284
|
if invert:
|
|
285
285
|
expr = f"not ({expr})"
|
|
286
286
|
|
|
@@ -318,7 +318,7 @@ def pre_css_remove(node: ExprCssElementRemove) -> str:
|
|
|
318
318
|
|
|
319
319
|
return (
|
|
320
320
|
indent
|
|
321
|
-
+ f"[e.getparent().remove(e) for e in
|
|
321
|
+
+ f"[e.getparent().remove(e) for e in {prv}.cssselect({query}) if e.getparent() is not None]\n"
|
|
322
322
|
+ indent
|
|
323
323
|
+ f"{nxt} = {prv}"
|
|
324
324
|
)
|
|
@@ -365,9 +365,9 @@ def pre_doc_filter_css(node: FilterDocCss) -> str:
|
|
|
365
365
|
def pre_doc_filter_has_attr(node: FilterDocHasAttr) -> str:
|
|
366
366
|
keys = node.kwargs["keys"]
|
|
367
367
|
if len(keys) == 1:
|
|
368
|
-
expr = f"{keys[0]!r}
|
|
368
|
+
expr = f"e.get({keys[0]!r}) is not None"
|
|
369
369
|
else:
|
|
370
|
-
expr = f"any(i
|
|
370
|
+
expr = f"any(e.get(i) is not None for i in {keys})"
|
|
371
371
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
372
372
|
return "and " + expr
|
|
373
373
|
return expr
|
|
@@ -377,9 +377,9 @@ def pre_doc_filter_has_attr(node: FilterDocHasAttr) -> str:
|
|
|
377
377
|
def pre_doc_filter_attr_eq(node: FilterDocAttrEqual) -> str:
|
|
378
378
|
key, values = node.unpack_args()
|
|
379
379
|
if len(values) == 1:
|
|
380
|
-
expr = f"e.
|
|
380
|
+
expr = f"e.get({key!r}) == {values[0]!r}"
|
|
381
381
|
else:
|
|
382
|
-
expr = f"e.
|
|
382
|
+
expr = f"e.get({key!r}) in {values}"
|
|
383
383
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
384
384
|
return "and " + expr
|
|
385
385
|
return expr
|
|
@@ -389,9 +389,9 @@ def pre_doc_filter_attr_eq(node: FilterDocAttrEqual) -> str:
|
|
|
389
389
|
def pre_doc_filter_attr_contains(node: FilterDocAttrContains) -> str:
|
|
390
390
|
key, values = node.unpack_args()
|
|
391
391
|
if len(values) == 1:
|
|
392
|
-
expr = f"{values[0]!r} in e.
|
|
392
|
+
expr = f"{values[0]!r} in (e.get({key!r}) or '')"
|
|
393
393
|
else:
|
|
394
|
-
expr = f"any(i in e.
|
|
394
|
+
expr = f"any(i in (e.get({key!r}) or '') for i in {values})"
|
|
395
395
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
396
396
|
return "and " + expr
|
|
397
397
|
return expr
|
|
@@ -401,10 +401,10 @@ def pre_doc_filter_attr_contains(node: FilterDocAttrContains) -> str:
|
|
|
401
401
|
def pre_doc_filter_attr_starts(node: FilterDocAttrContains) -> str:
|
|
402
402
|
key, values = node.unpack_args()
|
|
403
403
|
if len(values) == 1:
|
|
404
|
-
expr = f"e.
|
|
404
|
+
expr = f"(e.get({key!r}) or '').startswith({values[0]!r})"
|
|
405
405
|
else:
|
|
406
406
|
# str.startswith() arg allow tuple[str, ...]
|
|
407
|
-
expr = f"e.
|
|
407
|
+
expr = f"(e.get({key!r}) or '').startswith({values})"
|
|
408
408
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
409
409
|
return "and " + expr
|
|
410
410
|
return expr
|
|
@@ -414,10 +414,10 @@ def pre_doc_filter_attr_starts(node: FilterDocAttrContains) -> str:
|
|
|
414
414
|
def pre_doc_filter_attr_ends(node: FilterDocAttrContains) -> str:
|
|
415
415
|
key, values = node.unpack_args()
|
|
416
416
|
if len(values) == 1:
|
|
417
|
-
expr = f"e.
|
|
417
|
+
expr = f"(e.get({key!r}) or '').endswith({values[0]!r})"
|
|
418
418
|
else:
|
|
419
419
|
# str.endswith() arg allow tuple[str, ...]
|
|
420
|
-
expr = f"e.
|
|
420
|
+
expr = f"(e.get({key!r}) or '').endswith({values})"
|
|
421
421
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
422
422
|
return "and " + expr
|
|
423
423
|
return expr
|
|
@@ -427,11 +427,12 @@ def pre_doc_filter_attr_ends(node: FilterDocAttrContains) -> str:
|
|
|
427
427
|
def pre_doc_filter_attr_re(node: FilterDocAttrRegex) -> str:
|
|
428
428
|
key, pattern, ignore_case = node.unpack_args()
|
|
429
429
|
flags = py_regex_flags(ignore_case)
|
|
430
|
+
|
|
431
|
+
attr_value = f"e.get({key!r}) or ''"
|
|
430
432
|
if flags:
|
|
431
|
-
expr = f"re.search({pattern!r},
|
|
433
|
+
expr = f"re.search({pattern!r}, {attr_value}, {flags})"
|
|
432
434
|
else:
|
|
433
|
-
expr = f"re.search({pattern!r},
|
|
434
|
-
expr = f"bool({expr})"
|
|
435
|
+
expr = f"re.search({pattern!r}, {attr_value})"
|
|
435
436
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
436
437
|
return "and " + expr
|
|
437
438
|
return expr
|
|
@@ -442,10 +443,12 @@ def pre_doc_filter_is_regex_text(node: FilterDocIsRegexText) -> str:
|
|
|
442
443
|
pattern, ignore_case = node.unpack_args()
|
|
443
444
|
|
|
444
445
|
flags = py_regex_flags(ignore_case)
|
|
446
|
+
|
|
447
|
+
text_content_call = "e.text_content()"
|
|
445
448
|
if flags:
|
|
446
|
-
expr = f"re.search({pattern!r},
|
|
449
|
+
expr = f"re.search({pattern!r}, {text_content_call}, {flags})"
|
|
447
450
|
else:
|
|
448
|
-
expr = f"re.search({pattern!r},
|
|
451
|
+
expr = f"re.search({pattern!r}, {text_content_call})"
|
|
449
452
|
expr = f"bool({expr})"
|
|
450
453
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
451
454
|
return "and " + expr
|
|
@@ -457,10 +460,11 @@ def pre_doc_filter_is_regex_raw(node: FilterDocIsRegexText) -> str:
|
|
|
457
460
|
pattern, ignore_case = node.unpack_args()
|
|
458
461
|
|
|
459
462
|
flags = py_regex_flags(ignore_case)
|
|
463
|
+
raw_content_call = "etree.tostring(e, encoding='unicode')"
|
|
460
464
|
if flags:
|
|
461
|
-
expr = f"re.search({pattern!r},
|
|
465
|
+
expr = f"re.search({pattern!r}, {raw_content_call}, {flags})"
|
|
462
466
|
else:
|
|
463
|
-
expr = f"re.search({pattern!r},
|
|
467
|
+
expr = f"re.search({pattern!r}, {raw_content_call})"
|
|
464
468
|
expr = f"bool({expr})"
|
|
465
469
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
466
470
|
return "and " + expr
|
|
@@ -470,10 +474,12 @@ def pre_doc_filter_is_regex_raw(node: FilterDocIsRegexText) -> str:
|
|
|
470
474
|
@CONVERTER(FilterDocHasText.kind)
|
|
471
475
|
def pre_doc_filter_has_text(node: FilterDocHasText) -> str:
|
|
472
476
|
values = node.kwargs["values"]
|
|
477
|
+
|
|
478
|
+
text_content_call = "e.text_content()"
|
|
473
479
|
if len(values) == 1:
|
|
474
|
-
expr = f"{values[0]!r} in
|
|
480
|
+
expr = f"{values[0]!r} in {text_content_call}"
|
|
475
481
|
else:
|
|
476
|
-
expr = f"any(i in
|
|
482
|
+
expr = f"any(i in {text_content_call} for i in {values})"
|
|
477
483
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
478
484
|
return "and " + expr
|
|
479
485
|
return expr
|
|
@@ -482,11 +488,12 @@ def pre_doc_filter_has_text(node: FilterDocHasText) -> str:
|
|
|
482
488
|
@CONVERTER(FilterDocHasRaw.kind)
|
|
483
489
|
def pre_doc_filter_has_raw(node: FilterDocHasText) -> str:
|
|
484
490
|
values = node.kwargs["values"]
|
|
491
|
+
|
|
485
492
|
if len(values) == 1:
|
|
486
|
-
expr = f"{values[0]!r} in
|
|
493
|
+
expr = f"{values[0]!r} in html.tostring(e, encoding='unicode')"
|
|
487
494
|
else:
|
|
488
495
|
expr = (
|
|
489
|
-
f"any(i in
|
|
496
|
+
f"any(i in html.tostring(e, encoding='unicode') for i in {values})"
|
|
490
497
|
)
|
|
491
498
|
if not is_first_node_cond(node) and is_prev_node_atomic_cond(node):
|
|
492
499
|
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.2}/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
|