ssc_codegen 0.8.23__tar.gz → 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ssc_codegen might be problematic. Click here for more details.
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/.gitignore +1 -1
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/PKG-INFO +1 -1
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/pyproject.toml +1 -1
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/__init__.py +2 -2
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_build/utils.py +2 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/js_pure.py +3 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/py_base.py +21 -4
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/templates/js_pure.py +9 -1
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/schema.py +23 -3
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/static_checker/__init__.py +3 -2
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/static_checker/callbacks.py +20 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/tokens.py +1 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/LICENSE +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/README.md +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/_compat.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/__init__.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/base.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/nodes_array.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/nodes_cast.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/nodes_core.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/nodes_filter.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/nodes_selectors.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/nodes_string.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/nodes_validate.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_build/__init__.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_build/main.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/cli/__init__.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/cli/cli_callbacks.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/cli/cli_utils.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/cli/code_callbacks.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/cli/consts.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/cli/main.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/cli/runtime_parse_runners.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/compiler.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/consts.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/__init__.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/base.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/go_goquery.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/helpers.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/py_bs4.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/py_parsel.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/py_selectolax.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/templates/__init__.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/templates/go_goquery.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/document.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/document_utlis.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/json_struct.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/json_to_scc.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/logs.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/pseudo_selectors.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/selector_utils.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/static_checker/base.py +0 -0
- {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/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.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Python-dsl code converter to html parser for web scraping
|
|
5
5
|
Project-URL: Documentation, https://github.com/vypivshiy/selector_schema_codegen#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/vypivshiy/selector_schema_codegen/issues
|
|
@@ -3,11 +3,11 @@ from ssc_codegen.document import HTMLDocument, StringDocument, ArrayDocument, As
|
|
|
3
3
|
NumericDocument, JsonDocument, BooleanDocument, DocumentFilter
|
|
4
4
|
from ssc_codegen.json_struct import Json
|
|
5
5
|
from ssc_codegen.logs import setup_logger
|
|
6
|
-
from ssc_codegen.schema import ItemSchema, DictSchema, ListSchema, FlatListSchema
|
|
6
|
+
from ssc_codegen.schema import ItemSchema, DictSchema, ListSchema, FlatListSchema, AccUniqueListSchema
|
|
7
7
|
|
|
8
8
|
setup_logger()
|
|
9
9
|
|
|
10
|
-
VERSION = "0.
|
|
10
|
+
VERSION = "0.9.0"
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class __MISSING(object):
|
|
@@ -13,6 +13,7 @@ from ssc_codegen.schema import (
|
|
|
13
13
|
DictSchema,
|
|
14
14
|
ItemSchema,
|
|
15
15
|
FlatListSchema,
|
|
16
|
+
AccUniqueListSchema,
|
|
16
17
|
)
|
|
17
18
|
from ssc_codegen.tokens import VariableType
|
|
18
19
|
|
|
@@ -59,6 +60,7 @@ def is_template_schema_cls(cls: object) -> bool:
|
|
|
59
60
|
DictSchema,
|
|
60
61
|
ListSchema,
|
|
61
62
|
BaseSchema,
|
|
63
|
+
AccUniqueListSchema,
|
|
62
64
|
)
|
|
63
65
|
|
|
64
66
|
|
|
@@ -128,6 +128,7 @@ from ssc_codegen.converters.templates.js_pure import (
|
|
|
128
128
|
J2_PRE_XPATH_ALL,
|
|
129
129
|
J2_PRE_STR_TRIM,
|
|
130
130
|
J2_IS_XPATH,
|
|
131
|
+
J2_START_PARSE_ACC_LIST,
|
|
131
132
|
)
|
|
132
133
|
from ssc_codegen.str_utils import (
|
|
133
134
|
wrap_backtick,
|
|
@@ -283,6 +284,8 @@ def pre_start_parse(node: StartParseMethod) -> str:
|
|
|
283
284
|
code += J2_START_PARSE_DICT
|
|
284
285
|
case StructType.FLAT_LIST:
|
|
285
286
|
code += J2_START_PARSE_FLAT_LIST
|
|
287
|
+
case StructType.ACC_LIST:
|
|
288
|
+
code += J2_START_PARSE_ACC_LIST.render(exprs=exprs)
|
|
286
289
|
case _:
|
|
287
290
|
assert_never(node.parent.struct_type) # type: ignore
|
|
288
291
|
return code
|
|
@@ -351,6 +351,9 @@ def pre_typedef(node: TypeDef) -> str:
|
|
|
351
351
|
return f"T_{name} = List[{type_}]"
|
|
352
352
|
case StructType.ITEM | StructType.LIST:
|
|
353
353
|
return f'T_{name} = TypedDict("T_{name}", ' + "{"
|
|
354
|
+
case StructType.ACC_LIST:
|
|
355
|
+
# current impl support only str type
|
|
356
|
+
return f"T_{name} = List[str]"
|
|
354
357
|
case _:
|
|
355
358
|
assert_never(struct_type)
|
|
356
359
|
raise NotImplementedError() # noqa
|
|
@@ -359,9 +362,7 @@ def pre_typedef(node: TypeDef) -> str:
|
|
|
359
362
|
def post_typedef(node: TypeDef) -> str:
|
|
360
363
|
_, struct_type = node.unpack_args()
|
|
361
364
|
match struct_type:
|
|
362
|
-
case StructType.DICT:
|
|
363
|
-
return ""
|
|
364
|
-
case StructType.FLAT_LIST:
|
|
365
|
+
case StructType.DICT | StructType.FLAT_LIST | StructType.ACC_LIST:
|
|
365
366
|
return ""
|
|
366
367
|
# close TypedDict type
|
|
367
368
|
case StructType.ITEM | StructType.LIST:
|
|
@@ -375,7 +376,11 @@ def pre_typedef_field(node: TypeDefField) -> str:
|
|
|
375
376
|
name, var_type, cls_nested, cls_nested_type = node.unpack_args()
|
|
376
377
|
node.parent = cast(TypeDef, node.parent)
|
|
377
378
|
# skip generated types
|
|
378
|
-
if node.parent.struct_type in (
|
|
379
|
+
if node.parent.struct_type in (
|
|
380
|
+
StructType.DICT,
|
|
381
|
+
StructType.FLAT_LIST,
|
|
382
|
+
StructType.ACC_LIST,
|
|
383
|
+
):
|
|
379
384
|
return ""
|
|
380
385
|
# always string
|
|
381
386
|
elif name == "__KEY__":
|
|
@@ -485,6 +490,18 @@ def post_start_parse(node: StartParseMethod) -> str:
|
|
|
485
490
|
continue
|
|
486
491
|
code += f"{name!r}: self._parse_{name}(self._document),"
|
|
487
492
|
code += "}"
|
|
493
|
+
case StructType.ACC_LIST:
|
|
494
|
+
code += INDENT_METHOD_BODY + "return list(dict.fromkeys("
|
|
495
|
+
methods = []
|
|
496
|
+
for expr in node.body:
|
|
497
|
+
name = expr.kwargs["name"]
|
|
498
|
+
if name.startswith("__"):
|
|
499
|
+
continue
|
|
500
|
+
# acc list of str (all list are flatten)
|
|
501
|
+
methods.append(f"self._parse_{name}(self._document)")
|
|
502
|
+
code += " + ".join(methods)
|
|
503
|
+
code += "))"
|
|
504
|
+
|
|
488
505
|
case StructType.LIST:
|
|
489
506
|
# return [{...} for el in self._split_doc(self.document)]
|
|
490
507
|
code += INDENT_METHOD_BODY + "return [{"
|
|
@@ -13,7 +13,15 @@ throw new Error("Invalid input: Expected a Document, Element, or string");}
|
|
|
13
13
|
|
|
14
14
|
J2_START_PARSE_DICT = "return Array.from(this._splitDoc(this._doc)).reduce((item, e) => (item[this._parseKey(e)] = this._parseValue(e), item), {});"
|
|
15
15
|
J2_START_PARSE_FLAT_LIST = "return Array.from(this._splitDoc(this._doc)).map((e) => this._parseItem(e));"
|
|
16
|
-
|
|
16
|
+
J2_START_PARSE_ACC_LIST = Template(
|
|
17
|
+
"""
|
|
18
|
+
return [...new Set([
|
|
19
|
+
{% for expr in exprs %}
|
|
20
|
+
this._parse{{ expr.upper_name }}(this._doc),
|
|
21
|
+
{% endfor %}
|
|
22
|
+
].flat())];
|
|
23
|
+
"""
|
|
24
|
+
)
|
|
17
25
|
J2_START_PARSE_ITEM = Template("""return {
|
|
18
26
|
{% for expr in exprs %}
|
|
19
27
|
{{ expr.name }}: this._parse{{ expr.upper_name }}(this._doc),
|
|
@@ -22,19 +22,29 @@ class BaseSchema:
|
|
|
22
22
|
__SCHEMA_TYPE__: StructType = NotImplemented
|
|
23
23
|
|
|
24
24
|
__SIGNATURE__: dict | list = NotImplemented
|
|
25
|
-
"""manually write signature
|
|
25
|
+
"""manually write signature for docstring"""
|
|
26
26
|
|
|
27
27
|
__PRE_VALIDATE__: _T_OPT_FIELD = MISSING_FIELD
|
|
28
|
+
"""Optional method for pre validation input document
|
|
29
|
+
|
|
30
|
+
if checks not passed - throw error
|
|
31
|
+
"""
|
|
28
32
|
|
|
29
33
|
# DICT, LIST, FLAT_LIST HOOK
|
|
30
34
|
__SPLIT_DOC__: _T_OPT_FIELD = MISSING_FIELD
|
|
35
|
+
"""ListSchema, DictSchema, FlatListSchema method for write split document to parts of elements
|
|
36
|
+
"""
|
|
31
37
|
|
|
32
38
|
# FLAT LIST HOOK
|
|
33
39
|
__ITEM__: _T_OPT_FIELD = MISSING_FIELD
|
|
40
|
+
"""FlatListSchema item method for parse items for array"""
|
|
34
41
|
|
|
35
42
|
# DICT HOOKS
|
|
36
43
|
__KEY__: _T_OPT_FIELD = MISSING_FIELD
|
|
44
|
+
"""DictSchema method for parse key """
|
|
45
|
+
|
|
37
46
|
__VALUE__: _T_OPT_FIELD = MISSING_FIELD
|
|
47
|
+
"""DictSchema method for parse value"""
|
|
38
48
|
|
|
39
49
|
__EXCLUDE_SIGNATURE__: _T_OPT_FIELD = MISSING_FIELD
|
|
40
50
|
|
|
@@ -61,6 +71,9 @@ class BaseSchema:
|
|
|
61
71
|
signature = cls._get_list_signature()
|
|
62
72
|
case StructType.ITEM:
|
|
63
73
|
signature = cls._get_item_signature()
|
|
74
|
+
|
|
75
|
+
case StructType.ACC_LIST:
|
|
76
|
+
signature = [VariableType.STRING, "..."]
|
|
64
77
|
case _:
|
|
65
78
|
# code unreached
|
|
66
79
|
raise TypeError("Unknown schema type")
|
|
@@ -117,8 +130,11 @@ class BaseSchema:
|
|
|
117
130
|
signature = []
|
|
118
131
|
field = cls.__ITEM__
|
|
119
132
|
if field.stack_last_ret == VariableType.NESTED:
|
|
120
|
-
|
|
121
|
-
|
|
133
|
+
name = [e for e in field.stack if e.kind == TokenType.EXPR_NESTED][
|
|
134
|
+
0
|
|
135
|
+
].kwargs["schema_name"]
|
|
136
|
+
nested_class = cls.__NESTED_SCHEMAS__[name].__class_signature__()
|
|
137
|
+
signature.append(nested_class)
|
|
122
138
|
elif field.stack_last_ret == VariableType.JSON:
|
|
123
139
|
signature[k] = json_struct_to_signature(v.stack[-1].value) # noqa
|
|
124
140
|
else:
|
|
@@ -208,3 +224,7 @@ class DictSchema(BaseSchema):
|
|
|
208
224
|
|
|
209
225
|
class FlatListSchema(BaseSchema):
|
|
210
226
|
__SCHEMA_TYPE__ = StructType.FLAT_LIST
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class AccUniqueListSchema(BaseSchema):
|
|
230
|
+
__SCHEMA_TYPE__ = StructType.ACC_LIST
|
|
@@ -6,7 +6,7 @@ from ssc_codegen.static_checker.callbacks import analyze_schema_item_field, anal
|
|
|
6
6
|
analyze_schema_value_field, analyze_field_default_value, analyze_field_type_static, analyze_field_html_queries, \
|
|
7
7
|
analyze_schema_split_doc_field, analyze_field_split_doc_ret_type, CB_SCHEMA, CB_DOCUMENT, analyze_regex_expr, \
|
|
8
8
|
analyze_field_key_ret_type, analyze_other_field_type, analyze_dict_schema_fields, analyze_flat_list_schema_fields, \
|
|
9
|
-
analyze_jsonify_expr
|
|
9
|
+
analyze_jsonify_expr, analyze_schema_acc_list
|
|
10
10
|
|
|
11
11
|
LOGGER = logging.getLogger("ssc_gen")
|
|
12
12
|
|
|
@@ -16,7 +16,8 @@ _DEFAULT_CB_SCHEMAS = (
|
|
|
16
16
|
analyze_schema_key_field,
|
|
17
17
|
analyze_schema_item_field,
|
|
18
18
|
analyze_dict_schema_fields,
|
|
19
|
-
analyze_flat_list_schema_fields
|
|
19
|
+
analyze_flat_list_schema_fields,
|
|
20
|
+
analyze_schema_acc_list
|
|
20
21
|
)
|
|
21
22
|
|
|
22
23
|
_DEFAULT_CB_DOCUMENTS = (
|
|
@@ -66,6 +66,26 @@ def analyze_flat_list_schema_fields(sc: Type["BaseSchema"]) -> AnalyzeResult:
|
|
|
66
66
|
return AnalyzeResult.ok()
|
|
67
67
|
|
|
68
68
|
|
|
69
|
+
def analyze_schema_acc_list(sc: Type["BaseSchema"]) -> AnalyzeResult:
|
|
70
|
+
# current impl support only LIST_STRING expr
|
|
71
|
+
if sc.__SCHEMA_TYPE__ != StructType.ACC_LIST:
|
|
72
|
+
return AnalyzeResult.ok()
|
|
73
|
+
skip_fields = {"__SPLIT_DOC__", "__PRE_VALIDATE__"}
|
|
74
|
+
err_fields = {
|
|
75
|
+
name: field
|
|
76
|
+
for name, field in sc.__get_mro_fields__().items()
|
|
77
|
+
if name not in skip_fields
|
|
78
|
+
and field.stack_last_ret != VariableType.LIST_STRING
|
|
79
|
+
}
|
|
80
|
+
if err_fields:
|
|
81
|
+
msg = ""
|
|
82
|
+
for name, field in err_fields.items():
|
|
83
|
+
msg += f"{sc.__name__}.{name} expected type(s) {VariableType.LIST_STRING.name}, got {field.stack_last_ret}"
|
|
84
|
+
msg += "\n"
|
|
85
|
+
return AnalyzeResult.error(msg)
|
|
86
|
+
return AnalyzeResult.ok()
|
|
87
|
+
|
|
88
|
+
|
|
69
89
|
# schema check segment
|
|
70
90
|
def analyze_schema_split_doc_field(sc: Type["BaseSchema"]) -> AnalyzeResult:
|
|
71
91
|
if sc.__SCHEMA_TYPE__ in (
|
|
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
|
|
File without changes
|