ssc_codegen 0.8.12__tar.gz → 0.8.14__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.12 → ssc_codegen-0.8.14}/PKG-INFO +1 -1
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/pyproject.toml +1 -1
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/__init__.py +4 -3
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/ast_/nodes_core.py +2 -2
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/ast_build/main.py +50 -16
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/cli/main.py +1 -5
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/converters/go_goquery.py +16 -5
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/converters/js_pure.py +2 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/converters/py_base.py +2 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/document.py +1 -1
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/document_utlis.py +8 -2
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/static_checker/callbacks.py +21 -2
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/.gitignore +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/LICENSE +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/README.md +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/_compat.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/ast_/__init__.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/ast_/base.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/ast_/nodes_array.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/ast_/nodes_cast.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/ast_/nodes_selectors.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/ast_/nodes_string.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/ast_/nodes_validate.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/ast_build/__init__.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/ast_build/utils.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/cli/__init__.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/cli/cli_callbacks.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/cli/cli_utils.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/cli/code_callbacks.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/cli/consts.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/cli/runtime_parse_runners.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/compiler.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/consts.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/converters/__init__.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/converters/base.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/converters/helpers.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/converters/py_bs4.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/converters/py_parsel.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/converters/py_selectolax.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/converters/templates/__init__.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/converters/templates/go_goquery.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/converters/templates/js_pure.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/json_struct.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/json_to_scc.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/logs.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/pseudo_selectors.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/schema.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/selector_utils.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/static_checker/__init__.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/static_checker/base.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/str_utils.py +0 -0
- {ssc_codegen-0.8.12 → ssc_codegen-0.8.14}/ssc_codegen/tokens.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.14
|
|
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
|
|
@@ -7,10 +7,11 @@ from ssc_codegen.schema import ItemSchema, DictSchema, ListSchema, FlatListSchem
|
|
|
7
7
|
|
|
8
8
|
setup_logger()
|
|
9
9
|
|
|
10
|
-
VERSION = "0.8.
|
|
10
|
+
VERSION = "0.8.14"
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class __MISSING(object):
|
|
14
|
+
"""special marker for mark is not passed default value"""
|
|
14
15
|
pass
|
|
15
16
|
|
|
16
17
|
|
|
@@ -34,7 +35,7 @@ class Nested(HTMLDocument, NestedDocument, ArrayDocument, AssertDocument):
|
|
|
34
35
|
pass
|
|
35
36
|
|
|
36
37
|
|
|
37
|
-
def D(default: None | str | int | float | __MISSING = _NO_DEFAULT) -> Document: # noqa
|
|
38
|
+
def D(default: None | str | int | float | list | __MISSING = _NO_DEFAULT) -> Document: # noqa
|
|
38
39
|
"""Shortcut as a Document() object
|
|
39
40
|
|
|
40
41
|
:param default: .default() operator shortcut
|
|
@@ -49,7 +50,7 @@ def N() -> Nested: # noqa
|
|
|
49
50
|
return Nested()
|
|
50
51
|
|
|
51
52
|
|
|
52
|
-
def R(default: None | str | int | float | __MISSING = _NO_DEFAULT) -> Document: # noqa
|
|
53
|
+
def R(default: None | str | int | float | list | __MISSING = _NO_DEFAULT) -> Document: # noqa
|
|
53
54
|
"""Shortcut as a Document().raw() object.
|
|
54
55
|
For regex and format string operations
|
|
55
56
|
|
|
@@ -194,9 +194,9 @@ class ModuleProgram(
|
|
|
194
194
|
|
|
195
195
|
|
|
196
196
|
KW_AST_DEFAULT = TypedDict(
|
|
197
|
-
"KW_AST_DEFAULT", {"value": str | int | float | bool | None}
|
|
197
|
+
"KW_AST_DEFAULT", {"value": str | int | float | bool | list | None}
|
|
198
198
|
)
|
|
199
|
-
ARGS_AST_DEFAULT = tuple[str | int | float | bool | None]
|
|
199
|
+
ARGS_AST_DEFAULT = tuple[str | int | float | bool | list | None]
|
|
200
200
|
|
|
201
201
|
|
|
202
202
|
@dataclass(kw_only=True)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import warnings
|
|
1
2
|
from pathlib import Path
|
|
2
3
|
from typing import Type, cast
|
|
3
|
-
from typing_extensions import assert_never
|
|
4
4
|
|
|
5
5
|
from ssc_codegen import Json
|
|
6
6
|
from ssc_codegen.ast_ import (
|
|
@@ -45,7 +45,6 @@ from ssc_codegen.tokens import TokenType, VariableType, StructType
|
|
|
45
45
|
def build_ast_module_parser(
|
|
46
46
|
path: str | Path,
|
|
47
47
|
*,
|
|
48
|
-
docstring_class_top: bool = False,
|
|
49
48
|
css_to_xpath: bool = False,
|
|
50
49
|
xpath_to_css: bool = False,
|
|
51
50
|
) -> ModuleProgram:
|
|
@@ -78,15 +77,32 @@ def build_ast_module_parser(
|
|
|
78
77
|
json_structs = extract_json_structs_from_module(py_module)
|
|
79
78
|
# TODO: API for build single json
|
|
80
79
|
ast_jsons = build_ast_json(ast_module, *json_structs)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
# TODO: implement analyze-only AST function
|
|
81
|
+
try:
|
|
82
|
+
ast_schemas = [
|
|
83
|
+
build_ast_struct_parser(
|
|
84
|
+
sc,
|
|
85
|
+
ast_module,
|
|
86
|
+
css_to_xpath=css_to_xpath,
|
|
87
|
+
xpath_to_css=xpath_to_css,
|
|
88
|
+
)
|
|
89
|
+
for sc in schemas
|
|
90
|
+
]
|
|
91
|
+
except SyntaxError:
|
|
92
|
+
# get all schemas exceptions and throw to logs:
|
|
93
|
+
errors = []
|
|
94
|
+
for sc in schemas:
|
|
95
|
+
try:
|
|
96
|
+
build_ast_struct_parser(
|
|
97
|
+
sc,
|
|
98
|
+
ast_module,
|
|
99
|
+
css_to_xpath=css_to_xpath,
|
|
100
|
+
xpath_to_css=xpath_to_css,
|
|
101
|
+
)
|
|
102
|
+
except SyntaxError as e:
|
|
103
|
+
errors.append(e)
|
|
104
|
+
raise SyntaxError(f"{path}: Founded errors: {len(errors)}")
|
|
105
|
+
|
|
90
106
|
# TODO: API for build single typedef
|
|
91
107
|
ast_typedefs = build_ast_typedef(ast_module, *ast_schemas)
|
|
92
108
|
module_body.extend(ast_jsons)
|
|
@@ -174,7 +190,6 @@ def build_ast_struct_parser(
|
|
|
174
190
|
) -> StructParser:
|
|
175
191
|
errors_count = run_analyze_schema(schema)
|
|
176
192
|
if errors_count > 0:
|
|
177
|
-
# todo: exc
|
|
178
193
|
msg = f"{schema.__name__} founded errors: {errors_count}"
|
|
179
194
|
raise SyntaxError(msg)
|
|
180
195
|
docstring = (
|
|
@@ -238,8 +253,7 @@ def _fetch_field_nodes(
|
|
|
238
253
|
)
|
|
239
254
|
# TODO: add ast tests
|
|
240
255
|
# in inheritance schemas, child classes use same fields are used as in the parent class
|
|
241
|
-
# avoid duplicate ExprReturn node
|
|
242
|
-
# second case used in ExprDefaultValueWrapper cases
|
|
256
|
+
# avoid duplicate ExprReturn or ExprDefaultValueWrapper node
|
|
243
257
|
if (
|
|
244
258
|
document.stack[-1].kind != ExprReturn.kind
|
|
245
259
|
and document.stack[-1].kind != TokenType.EXPR_DEFAULT_END
|
|
@@ -296,7 +310,27 @@ def _unwrap_default_node(
|
|
|
296
310
|
default_type = VariableType.OPTIONAL_LIST_FLOAT
|
|
297
311
|
# TODO: warning for BOOL type
|
|
298
312
|
case _:
|
|
299
|
-
|
|
313
|
+
warnings.warn(
|
|
314
|
+
f"'None' default value not allowed return type '{ret_type.name}'. ",
|
|
315
|
+
category=SyntaxWarning,
|
|
316
|
+
)
|
|
317
|
+
default_type = VariableType.ANY
|
|
318
|
+
elif isinstance(value, list):
|
|
319
|
+
# todo: check if empty list passed
|
|
320
|
+
match ret_type:
|
|
321
|
+
case VariableType.LIST_STRING:
|
|
322
|
+
default_type = VariableType.LIST_STRING
|
|
323
|
+
case VariableType.LIST_INT:
|
|
324
|
+
default_type = VariableType.LIST_INT
|
|
325
|
+
case VariableType.LIST_FLOAT:
|
|
326
|
+
default_type = VariableType.LIST_FLOAT
|
|
327
|
+
case _:
|
|
328
|
+
warnings.warn(
|
|
329
|
+
f"`empty list` default value not allowed return type `{ret_type.name}`. "
|
|
330
|
+
f"Expected types `{(VariableType.LIST_STRING.name, VariableType.LIST_INT, VariableType.LIST_FLOAT)}`",
|
|
331
|
+
category=SyntaxWarning,
|
|
332
|
+
)
|
|
333
|
+
default_type = VariableType.ANY
|
|
300
334
|
expr_default_start = ExprDefaultValueStart(kwargs={"value": value})
|
|
301
335
|
expr_default_end = ExprDefaultValueEnd(
|
|
302
336
|
kwargs={"value": value}, ret_type=default_type
|
|
@@ -316,7 +350,7 @@ def _try_fetch_split_doc_node(
|
|
|
316
350
|
method = StructPartDocMethod(parent=st_ref)
|
|
317
351
|
|
|
318
352
|
# in inheritance schemas, child classes use same fields are used as in the parent class
|
|
319
|
-
#
|
|
353
|
+
# avoid duplicate ExprReturn or ExprDefaultValueWrapper node
|
|
320
354
|
if (
|
|
321
355
|
split_doc.stack[-1].kind != ExprReturn.kind
|
|
322
356
|
and split_doc.stack[-1].kind != TokenType.EXPR_DEFAULT_END
|
|
@@ -80,7 +80,6 @@ def generate_code(
|
|
|
80
80
|
comment_str: str,
|
|
81
81
|
fmt_cmd: list[str],
|
|
82
82
|
code_cb: Callable[[list[str]], str] = lambda c: "\n".join(c),
|
|
83
|
-
docstring_class_top: bool = False,
|
|
84
83
|
variables_patches: dict[str, str] | None = None,
|
|
85
84
|
css_to_xpath: bool = False,
|
|
86
85
|
xpath_to_css: bool = False,
|
|
@@ -112,7 +111,6 @@ def generate_code(
|
|
|
112
111
|
LOGGER.info("Make AST %s...", file_cfg.name)
|
|
113
112
|
ast_module = build_ast_module_parser(
|
|
114
113
|
file_cfg,
|
|
115
|
-
docstring_class_top=docstring_class_top,
|
|
116
114
|
css_to_xpath=css_to_xpath,
|
|
117
115
|
xpath_to_css=xpath_to_css,
|
|
118
116
|
)
|
|
@@ -220,7 +218,6 @@ def gen_js(
|
|
|
220
218
|
suffix=suffix,
|
|
221
219
|
comment_str=f"// {COMMENT_STRING}",
|
|
222
220
|
fmt_cmd=fmt_cmd,
|
|
223
|
-
docstring_class_top=True,
|
|
224
221
|
xpath_to_css=to_css,
|
|
225
222
|
css_to_xpath=to_xpath,
|
|
226
223
|
debug_instructions=debug,
|
|
@@ -272,7 +269,6 @@ def gen_go(
|
|
|
272
269
|
fmt_cmd=fmt_cmd,
|
|
273
270
|
# todo: better API for code callbacks
|
|
274
271
|
code_cb=CB_GO_CODE,
|
|
275
|
-
docstring_class_top=True,
|
|
276
272
|
variables_patches={"PACKAGE": package or out.name},
|
|
277
273
|
xpath_to_css=to_css,
|
|
278
274
|
css_to_xpath=to_xpath,
|
|
@@ -478,7 +474,7 @@ def parse_from_chrome_(
|
|
|
478
474
|
if not url.startswith("http"):
|
|
479
475
|
raise typer.BadParameter(f"`{url}` not a http url")
|
|
480
476
|
|
|
481
|
-
ast = build_ast_module_parser(schema_config
|
|
477
|
+
ast = build_ast_module_parser(schema_config) # type: ignore
|
|
482
478
|
code_parts = JS_CONVERTER.convert_program(ast)
|
|
483
479
|
code = CB_JS_CODE(code_parts)
|
|
484
480
|
code += f"; JSON.stringify((new {cls_target}(document).parse()))"
|
|
@@ -244,13 +244,20 @@ DOCSTR = "// "
|
|
|
244
244
|
CONVERTER = BaseCodeConverter(debug_comment_prefix="// ")
|
|
245
245
|
|
|
246
246
|
|
|
247
|
-
def py_var_to_go_var(
|
|
247
|
+
def py_var_to_go_var(
|
|
248
|
+
item: None | str | int | float | list, ret_type: VariableType
|
|
249
|
+
) -> str | int | float:
|
|
248
250
|
if item is None:
|
|
249
251
|
item = "nil"
|
|
250
252
|
elif isinstance(item, str):
|
|
251
253
|
item = wrap_double_quotes(item)
|
|
252
254
|
elif isinstance(item, bool):
|
|
253
255
|
item = "true" if item else "false"
|
|
256
|
+
elif isinstance(item, list):
|
|
257
|
+
# in AST static check step check if return type is
|
|
258
|
+
# LIST_STRING, LIST_INT, LIST_FLOAT
|
|
259
|
+
type_ = TYPES[ret_type]
|
|
260
|
+
item = f"make({type_}, 0)"
|
|
254
261
|
return item
|
|
255
262
|
|
|
256
263
|
|
|
@@ -576,7 +583,8 @@ def post_start_parse_method(node: StartParseMethod) -> str:
|
|
|
576
583
|
def pre_default_start(node: ExprDefaultValueStart) -> str:
|
|
577
584
|
prv, nxt = prev_next_var(node)
|
|
578
585
|
value = node.kwargs["value"]
|
|
579
|
-
|
|
586
|
+
ret_type = get_last_ret_type(node)
|
|
587
|
+
value = py_var_to_go_var(value, ret_type)
|
|
580
588
|
return J2_PRE_DEFAULT_START.render(prv=prv, nxt=nxt, value=value)
|
|
581
589
|
|
|
582
590
|
|
|
@@ -797,7 +805,8 @@ def pre_is_equal(node: ExprIsEqual) -> str:
|
|
|
797
805
|
item, msg = node.unpack_args()
|
|
798
806
|
|
|
799
807
|
msg = wrap_double_quotes(msg)
|
|
800
|
-
|
|
808
|
+
ret_type = get_last_ret_type(node)
|
|
809
|
+
item = py_var_to_go_var(item, ret_type)
|
|
801
810
|
|
|
802
811
|
context = {
|
|
803
812
|
"prv": prv,
|
|
@@ -819,7 +828,8 @@ def pre_is_not_equal(node: ExprIsNotEqual) -> str:
|
|
|
819
828
|
item, msg = node.unpack_args()
|
|
820
829
|
|
|
821
830
|
msg = wrap_double_quotes(msg) if msg else '""'
|
|
822
|
-
|
|
831
|
+
ret_type = get_last_ret_type(node)
|
|
832
|
+
item = py_var_to_go_var(item, ret_type)
|
|
823
833
|
|
|
824
834
|
context = {
|
|
825
835
|
"prv": prv,
|
|
@@ -841,7 +851,8 @@ def pre_is_contains(node: ExprIsContains) -> str:
|
|
|
841
851
|
item, msg = node.unpack_args()
|
|
842
852
|
|
|
843
853
|
msg = wrap_double_quotes(msg) if msg else '""'
|
|
844
|
-
|
|
854
|
+
ret_type = get_last_ret_type(node)
|
|
855
|
+
item = py_var_to_go_var(item, ret_type)
|
|
845
856
|
|
|
846
857
|
context = {
|
|
847
858
|
"prv": prv,
|
|
@@ -265,6 +265,8 @@ def pre_default_end(node: ExprDefaultValueEnd) -> str:
|
|
|
265
265
|
value = repr(value)
|
|
266
266
|
elif isinstance(value, bool):
|
|
267
267
|
value = "true" if value else "false"
|
|
268
|
+
elif isinstance(value, list):
|
|
269
|
+
value = "[]"
|
|
268
270
|
return f"}}catch(Error) {{ return {value}; }}"
|
|
269
271
|
|
|
270
272
|
|
|
@@ -115,7 +115,7 @@ class BaseDocument:
|
|
|
115
115
|
|
|
116
116
|
|
|
117
117
|
class DefaultDocument(BaseDocument):
|
|
118
|
-
def default(self, value: str | int | float | None) -> Self:
|
|
118
|
+
def default(self, value: str | int | float | list | None) -> Self:
|
|
119
119
|
"""Set default value. Accept string, int, float or None.
|
|
120
120
|
Should be a first else raise SyntaxError
|
|
121
121
|
|
|
@@ -31,6 +31,8 @@ CM1_RX = r"(?m)(?<!\\)((\\{2})*)#.*$"
|
|
|
31
31
|
CM2_RX = r"(\\)?((\\{2})*)(#)"
|
|
32
32
|
WS_RX = r"(\\)?((\\{2})*)(\s)\s*"
|
|
33
33
|
|
|
34
|
+
RE_CAPTURED_GROUPS = re.compile(r"(?<!\()\((?!\?:)[^)]+\)")
|
|
35
|
+
|
|
34
36
|
|
|
35
37
|
def is_ignore_case_regex(pattern: str | Pattern) -> bool:
|
|
36
38
|
if isinstance(pattern, str):
|
|
@@ -76,10 +78,14 @@ def analyze_re_expression(
|
|
|
76
78
|
try:
|
|
77
79
|
re_pattern = re.compile(pattern)
|
|
78
80
|
if not allow_empty_groups and re_pattern.groups == 0:
|
|
79
|
-
msg = f"`{re_pattern.pattern}` pattern groups is empty"
|
|
81
|
+
msg = f"`{re_pattern.pattern}` pattern groups is empty."
|
|
82
|
+
msg += "\nTIP: maybe you remember wrap pattern to brackets `()`?"
|
|
80
83
|
return AnalyzeResult.error(msg)
|
|
81
84
|
elif max_groups != -1 and re_pattern.groups > max_groups:
|
|
82
|
-
|
|
85
|
+
captured_groups = RE_CAPTURED_GROUPS.findall(re_pattern.pattern)
|
|
86
|
+
msg = f"`{re_pattern.pattern}` too many groups in pattern, expected groups count: {max_groups}."
|
|
87
|
+
msg += f"\nTIP: fix regular expression for extract {max_groups}:"
|
|
88
|
+
msg += f"\nGroups founded: {captured_groups}"
|
|
83
89
|
return AnalyzeResult.error(msg)
|
|
84
90
|
except re.error as e:
|
|
85
91
|
msg = f"`{pattern}` wrong regex pattern syntax: {e!r}"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import warnings
|
|
1
2
|
from typing import Callable, Type, TYPE_CHECKING
|
|
2
3
|
|
|
3
4
|
from cssselect import SelectorSyntaxError
|
|
@@ -171,7 +172,7 @@ def analyze_field_default_value(
|
|
|
171
172
|
|
|
172
173
|
elif (index := default_expr_pos[0][0]) and index != 0:
|
|
173
174
|
return AnalyzeResult.error(
|
|
174
|
-
f"{sc.__name__}.{name} # default expr should be a first, not {index}"
|
|
175
|
+
f"{sc.__name__}.{name} # default expr should be a first, not {index} position"
|
|
175
176
|
)
|
|
176
177
|
|
|
177
178
|
elif name in ("__PRE_VALIDATE__", "__SPLIT_DOC__"):
|
|
@@ -203,12 +204,30 @@ def analyze_field_default_value(
|
|
|
203
204
|
default_ast_type = VariableType.INT
|
|
204
205
|
elif isinstance(default_value, float):
|
|
205
206
|
default_ast_type = VariableType.FLOAT
|
|
207
|
+
elif isinstance(default_value, list):
|
|
208
|
+
if len(default_value) != 0:
|
|
209
|
+
warnings.warn(
|
|
210
|
+
f"{sc.__name__}.{name} # not supported pass values to list",
|
|
211
|
+
category=SyntaxWarning,
|
|
212
|
+
)
|
|
213
|
+
default_ast_type = ret_type
|
|
206
214
|
else:
|
|
207
215
|
return AnalyzeResult.error(
|
|
208
216
|
f"{sc.__name__}.{name} # Unsupported default value: `{default_value!r}`<{type(default_value).__name__}>"
|
|
209
217
|
)
|
|
210
218
|
|
|
211
|
-
if default_ast_type
|
|
219
|
+
if isinstance(default_value, list) and default_ast_type not in (
|
|
220
|
+
VariableType.LIST_STRING,
|
|
221
|
+
VariableType.LIST_INT,
|
|
222
|
+
VariableType.LIST_FLOAT,
|
|
223
|
+
):
|
|
224
|
+
return AnalyzeResult.error(
|
|
225
|
+
f"{sc.__name__}.{name} # default({default_value!r}) wrong last list return type expr"
|
|
226
|
+
f" (expected type(s) "
|
|
227
|
+
f"`{(VariableType.LIST_STRING.name, VariableType.LIST_FLOAT.name, VariableType.LIST_INT.name)}` "
|
|
228
|
+
f"got `{ret_type.name}`)"
|
|
229
|
+
)
|
|
230
|
+
elif default_ast_type != ret_type:
|
|
212
231
|
return AnalyzeResult.error(
|
|
213
232
|
f"{sc.__name__}.{name} # default({default_value!r}) wrong last return type expr"
|
|
214
233
|
f" (expected type `{default_ast_type.name}` got `{ret_type.name}`)"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|