ssc_codegen 0.11.0__tar.gz → 0.12.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.11.0 → ssc_codegen-0.12.0}/PKG-INFO +1 -1
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/pyproject.toml +1 -1
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/__init__.py +7 -2
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/_compat.py +6 -3
- ssc_codegen-0.12.0/ssc_codegen/ast_/__init__.py +129 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/ast_/base.py +3 -3
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_core.py +4 -4
- ssc_codegen-0.12.0/ssc_codegen/ast_/nodes_filter.py +285 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/ast_build/builder.py +10 -6
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/ast_build/utils.py +1 -1
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/base.py +3 -2
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/go_goquery.py +765 -202
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/helpers.py +13 -15
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/js_pure.py +183 -9
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/lua_htmlparser.py +38 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/py_base.py +3 -3
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/py_bs4.py +172 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/py_lxml.py +180 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/py_parsel.py +183 -2
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/py_selectolax.py +175 -0
- ssc_codegen-0.12.0/ssc_codegen/converters/templates/go_goquery.py +440 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/document.py +433 -21
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/json_struct.py +2 -2
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/json_to_scc.py +3 -2
- ssc_codegen-0.12.0/ssc_codegen/schema.py +354 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/static_checker/__init__.py +5 -2
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/static_checker/callbacks.py +26 -2
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/tokens.py +22 -1
- ssc_codegen-0.11.0/ssc_codegen/ast_/__init__.py +0 -40
- ssc_codegen-0.11.0/ssc_codegen/ast_/nodes_filter.py +0 -151
- ssc_codegen-0.11.0/ssc_codegen/converters/templates/go_goquery.py +0 -737
- ssc_codegen-0.11.0/ssc_codegen/schema.py +0 -310
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/.gitignore +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/LICENSE +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/README.md +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_array.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_cast.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_selectors.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_string.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_validate.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/ast_build/__init__.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/ast_build/main.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/ast_grep_rules/js_rules.yml +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/ast_grep_rules/py_rules.yml +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/cli/__init__.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/cli/ast_grep.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/cli/cli_callbacks.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/cli/cli_utils.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/cli/code_callbacks.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/cli/consts.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/cli/main.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/cli/runtime_parse_runners.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/compiler.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/__init__.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/__init__.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/js_pure.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/lua_base.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/lua_css_compat.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/lua_re_compat.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/py_base.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/document_utlis.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/logs.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/pseudo_selectors.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/selector_utils.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.0}/ssc_codegen/static_checker/base.py +0 -0
- {ssc_codegen-0.11.0 → ssc_codegen-0.12.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.12.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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from ssc_codegen.document import HTMLDocument, ClassVarDocument, StringDocument, ArrayDocument, AssertDocument, NestedDocument, \
|
|
1
|
+
from ssc_codegen.document import DocumentElementsFilter, HTMLDocument, ClassVarDocument, StringDocument, ArrayDocument, AssertDocument, NestedDocument, \
|
|
2
2
|
DefaultDocument, \
|
|
3
3
|
NumericDocument, JsonDocument, BooleanDocument, DocumentFilter
|
|
4
4
|
from ssc_codegen.json_struct import Json
|
|
@@ -7,7 +7,7 @@ from ssc_codegen.schema import ItemSchema, DictSchema, ListSchema, FlatListSchem
|
|
|
7
7
|
|
|
8
8
|
setup_logger()
|
|
9
9
|
|
|
10
|
-
VERSION = "0.
|
|
10
|
+
VERSION = "0.12.0"
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class __MISSING(object):
|
|
@@ -68,6 +68,11 @@ def F() -> DocumentFilter:
|
|
|
68
68
|
return DocumentFilter()
|
|
69
69
|
|
|
70
70
|
|
|
71
|
+
def FE() -> DocumentElementsFilter:
|
|
72
|
+
"""Shortcut as a DocumentElementsFilter() object"""
|
|
73
|
+
return DocumentElementsFilter()
|
|
74
|
+
|
|
75
|
+
|
|
71
76
|
def CV(value: int | str | float | bool | None | list, self_cls_path: str | None = None, *, returns: bool = False) -> ClassVarDocument:
|
|
72
77
|
"""Shortcut as ClassVarDocument(value, self_cls, parse_returns) object
|
|
73
78
|
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
import warnings
|
|
3
3
|
import functools
|
|
4
|
+
from typing import Callable, ParamSpec, TypeVar
|
|
4
5
|
|
|
6
|
+
P = ParamSpec("P")
|
|
7
|
+
R = TypeVar("R")
|
|
5
8
|
|
|
6
9
|
if sys.version_info >= (3, 11):
|
|
7
10
|
from enum import StrEnum
|
|
@@ -12,10 +15,10 @@ else:
|
|
|
12
15
|
pass
|
|
13
16
|
|
|
14
17
|
|
|
15
|
-
def deprecated(reason=""):
|
|
16
|
-
def decorator(func):
|
|
18
|
+
def deprecated(reason: str = "") -> Callable[[Callable[P, R]], Callable[P, R]]:
|
|
19
|
+
def decorator(func: Callable[P, R]) -> Callable[P, R]:
|
|
17
20
|
@functools.wraps(func)
|
|
18
|
-
def wrapper(*args, **kwargs):
|
|
21
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
19
22
|
warnings.warn(
|
|
20
23
|
f"Function {func.__name__} is deprecated. {reason}",
|
|
21
24
|
category=DeprecationWarning,
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from .base import BaseAstNode
|
|
2
|
+
from .nodes_array import (
|
|
3
|
+
ExprIndex,
|
|
4
|
+
ExprToListLength,
|
|
5
|
+
ExprListStringJoin,
|
|
6
|
+
ExprListUnique
|
|
7
|
+
)
|
|
8
|
+
from .nodes_cast import (
|
|
9
|
+
ExprNested,
|
|
10
|
+
ExprJsonify,
|
|
11
|
+
ExprToInt,
|
|
12
|
+
ExprToFloat,
|
|
13
|
+
ExprToListInt,
|
|
14
|
+
ExprToListFloat,
|
|
15
|
+
ExprToBool,
|
|
16
|
+
ExprJsonifyDynamic
|
|
17
|
+
)
|
|
18
|
+
from .nodes_core import (
|
|
19
|
+
ExprReturn,
|
|
20
|
+
ExprNoReturn,
|
|
21
|
+
ExprDefaultValueEnd,
|
|
22
|
+
ExprDefaultValueStart,
|
|
23
|
+
ExprDefaultValueWrapper,
|
|
24
|
+
ModuleProgram,
|
|
25
|
+
Docstring,
|
|
26
|
+
ModuleImports,
|
|
27
|
+
StructInitMethod,
|
|
28
|
+
StructPreValidateMethod,
|
|
29
|
+
StructFieldMethod,
|
|
30
|
+
StructPartDocMethod,
|
|
31
|
+
StartParseMethod,
|
|
32
|
+
StructParser,
|
|
33
|
+
JsonStruct,
|
|
34
|
+
JsonStructField,
|
|
35
|
+
TypeDef,
|
|
36
|
+
TypeDefField,
|
|
37
|
+
ExprCallStructMethod,
|
|
38
|
+
ExprClassVar,
|
|
39
|
+
ExprCallStructClassVar,
|
|
40
|
+
CodeStart,
|
|
41
|
+
CodeEnd
|
|
42
|
+
)
|
|
43
|
+
from .nodes_filter import (
|
|
44
|
+
FilterOr,
|
|
45
|
+
FilterAnd,
|
|
46
|
+
FilterNot,
|
|
47
|
+
FilterNotEqual,
|
|
48
|
+
FilterEqual,
|
|
49
|
+
FilterStrIn,
|
|
50
|
+
FilterStrStarts,
|
|
51
|
+
FilterStrEnds,
|
|
52
|
+
FilterStrRe,
|
|
53
|
+
ExprFilter,
|
|
54
|
+
FilterStrLenEq,
|
|
55
|
+
FilterStrLenNe,
|
|
56
|
+
FilterStrLenLt,
|
|
57
|
+
FilterStrLenLe,
|
|
58
|
+
FilterStrLenGt,
|
|
59
|
+
FilterStrLenGe,
|
|
60
|
+
ExprDocumentFilter,
|
|
61
|
+
FilterDocCss,
|
|
62
|
+
FilterDocHasAttr,
|
|
63
|
+
FilterDocHasRaw,
|
|
64
|
+
FilterDocHasText,
|
|
65
|
+
FilterDocIsRegexRaw,
|
|
66
|
+
FilterDocIsRegexText,
|
|
67
|
+
FilterDocXpath,
|
|
68
|
+
FilterDocAttrContains,
|
|
69
|
+
FilterDocAttrEnds,
|
|
70
|
+
FilterDocAttrEqual,
|
|
71
|
+
FilterDocAttrRegex,
|
|
72
|
+
FilterDocAttrStarts,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
from .nodes_selectors import (
|
|
76
|
+
ExprCss,
|
|
77
|
+
ExprCssAll,
|
|
78
|
+
ExprXpathAll,
|
|
79
|
+
ExprXpath,
|
|
80
|
+
ExprGetHtmlText,
|
|
81
|
+
ExprGetHtmlRaw,
|
|
82
|
+
ExprGetHtmlAttr,
|
|
83
|
+
ExprGetHtmlAttrAll,
|
|
84
|
+
ExprGetHtmlRawAll,
|
|
85
|
+
ExprGetHtmlTextAll,
|
|
86
|
+
ExprMapAttrs,
|
|
87
|
+
ExprMapAttrsAll,
|
|
88
|
+
ExprCssElementRemove,
|
|
89
|
+
ExprXpathElementRemove
|
|
90
|
+
)
|
|
91
|
+
from .nodes_string import (
|
|
92
|
+
ExprStringTrim,
|
|
93
|
+
ExprStringRegex,
|
|
94
|
+
ExprStringSplit,
|
|
95
|
+
ExprStringReplace,
|
|
96
|
+
ExprStringLeftTrim,
|
|
97
|
+
ExprStringFormat,
|
|
98
|
+
ExprListStringTrim,
|
|
99
|
+
ExprStringRegexAll,
|
|
100
|
+
ExprStringRegexSub,
|
|
101
|
+
ExprStringRightTrim,
|
|
102
|
+
ExprListStringFormat,
|
|
103
|
+
ExprListStringReplace,
|
|
104
|
+
ExprListStringRightTrim,
|
|
105
|
+
ExprListStringLeftTrim,
|
|
106
|
+
ExprListStringRegexSub,
|
|
107
|
+
ExprStringRmPrefix,
|
|
108
|
+
ExprStringRmSuffix,
|
|
109
|
+
ExprStringRmPrefixAndSuffix,
|
|
110
|
+
ExprListStringRmPrefixAndSuffix,
|
|
111
|
+
ExprListStringRmPrefix,
|
|
112
|
+
ExprListStringRmSuffix,
|
|
113
|
+
ExprStringMapReplace,
|
|
114
|
+
ExprListStringMapReplace,
|
|
115
|
+
ExprStringUnescape,
|
|
116
|
+
ExprListStringUnescape
|
|
117
|
+
)
|
|
118
|
+
from .nodes_validate import (
|
|
119
|
+
ExprIsCss,
|
|
120
|
+
ExprIsEqual,
|
|
121
|
+
ExprStringIsRegex,
|
|
122
|
+
ExprIsXpath,
|
|
123
|
+
ExprIsNotEqual,
|
|
124
|
+
ExprIsContains,
|
|
125
|
+
ExprListStringAnyRegex,
|
|
126
|
+
ExprListStringAllRegex,
|
|
127
|
+
ExprHasAttr,
|
|
128
|
+
ExprListHasAttr
|
|
129
|
+
)
|
|
@@ -98,10 +98,10 @@ class BaseAstNode(Generic[T_MAPPING_FIELD, T_ARGUMENTS]):
|
|
|
98
98
|
|
|
99
99
|
def find_node_all(
|
|
100
100
|
self, func: Callable[["BaseAstNode"], bool]
|
|
101
|
-
) -> "BaseAstNode":
|
|
101
|
+
) -> list["BaseAstNode"]:
|
|
102
102
|
"""find nodes in body by func filter"""
|
|
103
103
|
if len(self.body) == 0:
|
|
104
|
-
return []
|
|
104
|
+
return [] # type: ignore
|
|
105
105
|
return [i for i in self.body if i.kind == func(i)]
|
|
106
106
|
|
|
107
107
|
def find_node_by_token(
|
|
@@ -112,7 +112,7 @@ class BaseAstNode(Generic[T_MAPPING_FIELD, T_ARGUMENTS]):
|
|
|
112
112
|
|
|
113
113
|
def find_nodes_by_token(self, token_type: TokenType) -> list["BaseAstNode"]:
|
|
114
114
|
"""find nodes by TokenType"""
|
|
115
|
-
return self.find_nodes_by_token(lambda n: n.kind == token_type)
|
|
115
|
+
return self.find_nodes_by_token(lambda n: n.kind == token_type) # type: ignore[arg-type]
|
|
116
116
|
|
|
117
117
|
|
|
118
118
|
@dataclass(kw_only=True)
|
|
@@ -107,7 +107,7 @@ class ExprCallStructMethod(
|
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
KW_EXPR_CALL_CLASSVAR = TypedDict(
|
|
110
|
-
"
|
|
110
|
+
"KW_EXPR_CALL_CLASSVAR",
|
|
111
111
|
{"struct_name": str, "field_name": str, "type": VariableType},
|
|
112
112
|
)
|
|
113
113
|
ARGS_EXPR_CALL_CLASSVAR = tuple[str, str, VariableType]
|
|
@@ -253,7 +253,7 @@ class ExprNoReturn(_DisableReprBody, BaseAstNode[T_EMPTY_KWARGS, tuple]):
|
|
|
253
253
|
# regex pattern auto converts to string
|
|
254
254
|
T_CLASSVAR = int | str | bool | float | None
|
|
255
255
|
KW_CLASSVAR = TypedDict(
|
|
256
|
-
"
|
|
256
|
+
"KW_CLASSVAR",
|
|
257
257
|
{
|
|
258
258
|
"value": T_CLASSVAR,
|
|
259
259
|
"struct_name": str,
|
|
@@ -262,7 +262,7 @@ KW_CLASSVAR = TypedDict(
|
|
|
262
262
|
"is_regex": bool,
|
|
263
263
|
},
|
|
264
264
|
)
|
|
265
|
-
ARGS_CLASSVAR = tuple[T_CLASSVAR, str, str]
|
|
265
|
+
ARGS_CLASSVAR = tuple[T_CLASSVAR, str, str, bool, bool]
|
|
266
266
|
|
|
267
267
|
|
|
268
268
|
@dataclass(kw_only=True)
|
|
@@ -286,7 +286,7 @@ class ExprClassVar(_DisableReprBody, BaseAstNode[KW_CLASSVAR, ARGS_CLASSVAR]):
|
|
|
286
286
|
return self.kwargs["value"]
|
|
287
287
|
|
|
288
288
|
@value.setter
|
|
289
|
-
def value(self, val) -> None:
|
|
289
|
+
def value(self, val: T_CLASSVAR) -> None:
|
|
290
290
|
self.kwargs["value"] = val
|
|
291
291
|
|
|
292
292
|
@property
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import TypedDict, ClassVar
|
|
3
|
+
|
|
4
|
+
from ssc_codegen.ast_.base import BaseAstNode, T_EMPTY_KWARGS
|
|
5
|
+
from ssc_codegen.tokens import TokenType, VariableType
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# TODO: static analyzer for filter operations
|
|
9
|
+
# TODO: implement operators for DOCUMENT, INT, FLOAT
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(kw_only=True)
|
|
13
|
+
class FilterOr(BaseAstNode[T_EMPTY_KWARGS, tuple]):
|
|
14
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_OR
|
|
15
|
+
# cast for reuse in Elements filters
|
|
16
|
+
# real accept: VariableType.STRING, VariableType.DOCUMENT
|
|
17
|
+
accept_type: VariableType = VariableType.ANY
|
|
18
|
+
ret_type: VariableType = VariableType.ANY
|
|
19
|
+
# TODO: typing body accept Filter-like nodes
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(kw_only=True)
|
|
23
|
+
class FilterAnd(BaseAstNode[T_EMPTY_KWARGS, tuple]):
|
|
24
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_AND
|
|
25
|
+
accept_type: VariableType = VariableType.ANY
|
|
26
|
+
ret_type: VariableType = VariableType.ANY
|
|
27
|
+
# TODO: typing body accept Filter-like nodes
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(kw_only=True)
|
|
31
|
+
class FilterNot(BaseAstNode[T_EMPTY_KWARGS, tuple]):
|
|
32
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_NOT
|
|
33
|
+
accept_type: VariableType = VariableType.ANY
|
|
34
|
+
ret_type: VariableType = VariableType.ANY
|
|
35
|
+
# TODO: typing body accept Filter-like nodes
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
KW_STR_IN = TypedDict("KW_STR_IN", {"substr": tuple[str, ...]})
|
|
39
|
+
ARGS_STR_IN = tuple[tuple[str, ...]]
|
|
40
|
+
|
|
41
|
+
KW_STR_STARTS_OR_ENDS = TypedDict(
|
|
42
|
+
"KW_STR_STARTS_OR_ENDS", {"substr": tuple[str, ...]}
|
|
43
|
+
)
|
|
44
|
+
ARGS_STR_STARTS_OR_ENDS = tuple[tuple[str, ...]]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(kw_only=True)
|
|
48
|
+
class FilterStrIn(BaseAstNode[KW_STR_IN, ARGS_STR_IN]):
|
|
49
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_STR_IN
|
|
50
|
+
accept_type: VariableType = VariableType.STRING
|
|
51
|
+
ret_type: VariableType = VariableType.STRING
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass(kw_only=True)
|
|
55
|
+
class FilterStrStarts(
|
|
56
|
+
BaseAstNode[KW_STR_STARTS_OR_ENDS, ARGS_STR_STARTS_OR_ENDS]
|
|
57
|
+
):
|
|
58
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_STR_STARTS
|
|
59
|
+
accept_type: VariableType = VariableType.STRING
|
|
60
|
+
ret_type: VariableType = VariableType.STRING
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass(kw_only=True)
|
|
64
|
+
class FilterStrEnds(
|
|
65
|
+
BaseAstNode[KW_STR_STARTS_OR_ENDS, ARGS_STR_STARTS_OR_ENDS]
|
|
66
|
+
):
|
|
67
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_STR_ENDS
|
|
68
|
+
accept_type: VariableType = VariableType.STRING
|
|
69
|
+
ret_type: VariableType = VariableType.STRING
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
KW_STR_RE = TypedDict("KW_STR_RE", {"pattern": str, "ignore_case": bool})
|
|
73
|
+
ARGS_STR_RE = tuple[str, bool]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass(kw_only=True)
|
|
77
|
+
class FilterStrRe(BaseAstNode[KW_STR_RE, ARGS_STR_RE]):
|
|
78
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_STR_RE
|
|
79
|
+
accept_type: VariableType = VariableType.STRING
|
|
80
|
+
ret_type: VariableType = VariableType.STRING
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
KW_STR_LEN = TypedDict("KW_STR_LEN", {"length": int})
|
|
84
|
+
ARGS_STR_LEN = tuple[int]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(kw_only=True)
|
|
88
|
+
class FilterStrLenEq(BaseAstNode[KW_STR_LEN, ARGS_STR_LEN]):
|
|
89
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_STR_LEN_EQ
|
|
90
|
+
accept_type: VariableType = VariableType.STRING
|
|
91
|
+
ret_type: VariableType = VariableType.STRING
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass(kw_only=True)
|
|
95
|
+
class FilterStrLenNe(BaseAstNode[KW_STR_LEN, ARGS_STR_LEN]):
|
|
96
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_STR_LEN_NE
|
|
97
|
+
accept_type: VariableType = VariableType.STRING
|
|
98
|
+
ret_type: VariableType = VariableType.STRING
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass(kw_only=True)
|
|
102
|
+
class FilterStrLenLt(BaseAstNode[KW_STR_LEN, ARGS_STR_LEN]):
|
|
103
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_STR_LEN_LT
|
|
104
|
+
accept_type: VariableType = VariableType.STRING
|
|
105
|
+
ret_type: VariableType = VariableType.STRING
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass(kw_only=True)
|
|
109
|
+
class FilterStrLenLe(BaseAstNode[KW_STR_LEN, ARGS_STR_LEN]):
|
|
110
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_STR_LEN_LE
|
|
111
|
+
accept_type: VariableType = VariableType.STRING
|
|
112
|
+
ret_type: VariableType = VariableType.STRING
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@dataclass(kw_only=True)
|
|
116
|
+
class FilterStrLenGt(BaseAstNode[KW_STR_LEN, ARGS_STR_LEN]):
|
|
117
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_STR_LEN_GT
|
|
118
|
+
accept_type: VariableType = VariableType.STRING
|
|
119
|
+
ret_type: VariableType = VariableType.STRING
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@dataclass(kw_only=True)
|
|
123
|
+
class FilterStrLenGe(BaseAstNode[KW_STR_LEN, ARGS_STR_LEN]):
|
|
124
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_STR_LEN_GE
|
|
125
|
+
accept_type: VariableType = VariableType.STRING
|
|
126
|
+
ret_type: VariableType = VariableType.STRING
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# TODO: provide API for int, float, etc
|
|
130
|
+
KW_STR_EQ_OR_NE = TypedDict("KW_STR_EQ_OR_NE", {"values": tuple[str, ...]})
|
|
131
|
+
ARGS_STR_EQ_OR_NE = tuple[tuple[str, ...]]
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@dataclass(kw_only=True)
|
|
135
|
+
class FilterEqual(BaseAstNode[KW_STR_EQ_OR_NE, ARGS_STR_EQ_OR_NE]):
|
|
136
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_EQ
|
|
137
|
+
accept_type: VariableType = VariableType.STRING
|
|
138
|
+
ret_type: VariableType = VariableType.STRING
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass(kw_only=True)
|
|
142
|
+
class FilterNotEqual(BaseAstNode[KW_STR_EQ_OR_NE, ARGS_STR_EQ_OR_NE]):
|
|
143
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_NE
|
|
144
|
+
accept_type: VariableType = VariableType.STRING
|
|
145
|
+
ret_type: VariableType = VariableType.STRING
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@dataclass(kw_only=True)
|
|
149
|
+
class ExprFilter(BaseAstNode[T_EMPTY_KWARGS, tuple]):
|
|
150
|
+
kind: ClassVar[TokenType] = TokenType.EXPR_FILTER
|
|
151
|
+
accept_type: VariableType = VariableType.LIST_STRING
|
|
152
|
+
ret_type: VariableType = VariableType.LIST_STRING
|
|
153
|
+
# TODO: typing body accept Filter-like nodes
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# Elements filters
|
|
157
|
+
@dataclass(kw_only=True)
|
|
158
|
+
class ExprDocumentFilter(BaseAstNode[T_EMPTY_KWARGS, tuple]):
|
|
159
|
+
kind: ClassVar[TokenType] = TokenType.EXPR_DOC_FILTER
|
|
160
|
+
accept_type: VariableType = VariableType.LIST_DOCUMENT
|
|
161
|
+
ret_type: VariableType = VariableType.LIST_DOCUMENT
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
KW_FILTER_CSS_OR_XPATH = TypedDict("KW_FILTER_CSS", {"query": str})
|
|
165
|
+
ARGS_FILTER_CSS_OR_XPATH = tuple[str]
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@dataclass(kw_only=True)
|
|
169
|
+
class FilterDocCss(
|
|
170
|
+
BaseAstNode[KW_FILTER_CSS_OR_XPATH, ARGS_FILTER_CSS_OR_XPATH]
|
|
171
|
+
):
|
|
172
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_DOC_CSS
|
|
173
|
+
accept_type: VariableType = VariableType.DOCUMENT
|
|
174
|
+
ret_type: VariableType = VariableType.DOCUMENT
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@dataclass(kw_only=True)
|
|
178
|
+
class FilterDocXpath(
|
|
179
|
+
BaseAstNode[KW_FILTER_CSS_OR_XPATH, ARGS_FILTER_CSS_OR_XPATH]
|
|
180
|
+
):
|
|
181
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_DOC_XPATH
|
|
182
|
+
accept_type: VariableType = VariableType.DOCUMENT
|
|
183
|
+
ret_type: VariableType = VariableType.DOCUMENT
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
KW_FILTER_HAS_TEXT = TypedDict(
|
|
187
|
+
"KW_FILTER_HAS_TEXT", {"values": tuple[str, ...]}
|
|
188
|
+
)
|
|
189
|
+
ARGS_FILTER_HAS_TEXT = tuple[tuple[str, ...]]
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@dataclass(kw_only=True)
|
|
193
|
+
class FilterDocHasText(BaseAstNode[KW_FILTER_HAS_TEXT, ARGS_FILTER_HAS_TEXT]):
|
|
194
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_DOC_HAS_TEXT
|
|
195
|
+
accept_type: VariableType = VariableType.DOCUMENT
|
|
196
|
+
ret_type: VariableType = VariableType.DOCUMENT
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@dataclass(kw_only=True)
|
|
200
|
+
class FilterDocHasRaw(BaseAstNode[KW_FILTER_HAS_TEXT, ARGS_FILTER_HAS_TEXT]):
|
|
201
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_DOC_HAS_RAW
|
|
202
|
+
accept_type: VariableType = VariableType.DOCUMENT
|
|
203
|
+
ret_type: VariableType = VariableType.DOCUMENT
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
KW_FILTER_HAS_ATTR = TypedDict("KW_FILTER_HAS_ATTR", {"keys": tuple[str, ...]})
|
|
207
|
+
ARGS_FILTER_HAS_ATTR = tuple[tuple[str, ...]]
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@dataclass(kw_only=True)
|
|
211
|
+
class FilterDocHasAttr(BaseAstNode[KW_FILTER_HAS_ATTR, ARGS_FILTER_HAS_ATTR]):
|
|
212
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_DOC_HAS_ATTR
|
|
213
|
+
accept_type: VariableType = VariableType.DOCUMENT
|
|
214
|
+
ret_type: VariableType = VariableType.DOCUMENT
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
KW_FILTER_ATTR = TypedDict(
|
|
218
|
+
"KW_FILTER_ATTR", {"key": str, "values": tuple[str, ...]}
|
|
219
|
+
)
|
|
220
|
+
ARGS_FILTER_ATTR = tuple[str, tuple[str, ...]]
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@dataclass(kw_only=True)
|
|
224
|
+
class FilterDocAttrEqual(BaseAstNode[KW_FILTER_ATTR, ARGS_FILTER_ATTR]):
|
|
225
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_DOC_ATTR_EQ
|
|
226
|
+
accept_type: VariableType = VariableType.DOCUMENT
|
|
227
|
+
ret_type: VariableType = VariableType.DOCUMENT
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@dataclass(kw_only=True)
|
|
231
|
+
class FilterDocAttrStarts(BaseAstNode[KW_FILTER_ATTR, ARGS_FILTER_ATTR]):
|
|
232
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_DOC_ATTR_STARTS
|
|
233
|
+
accept_type: VariableType = VariableType.DOCUMENT
|
|
234
|
+
ret_type: VariableType = VariableType.DOCUMENT
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@dataclass(kw_only=True)
|
|
238
|
+
class FilterDocAttrEnds(BaseAstNode[KW_FILTER_ATTR, ARGS_FILTER_ATTR]):
|
|
239
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_DOC_ATTR_ENDS
|
|
240
|
+
accept_type: VariableType = VariableType.DOCUMENT
|
|
241
|
+
ret_type: VariableType = VariableType.DOCUMENT
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@dataclass(kw_only=True)
|
|
245
|
+
class FilterDocAttrContains(BaseAstNode[KW_FILTER_ATTR, ARGS_FILTER_ATTR]):
|
|
246
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_DOC_ATTR_CONTAINS
|
|
247
|
+
accept_type: VariableType = VariableType.DOCUMENT
|
|
248
|
+
ret_type: VariableType = VariableType.DOCUMENT
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
KW_FILTER_ATTR_RE = TypedDict(
|
|
252
|
+
"KW_FILTER_ATTR", {"key": str, "pattern": str, "ignore_case": bool}
|
|
253
|
+
)
|
|
254
|
+
ARGS_FILTER_ATTR_RE = tuple[str, str, bool]
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@dataclass(kw_only=True)
|
|
258
|
+
class FilterDocAttrRegex(BaseAstNode[KW_FILTER_ATTR_RE, ARGS_FILTER_ATTR_RE]):
|
|
259
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_DOC_ATTR_RE_MATCH
|
|
260
|
+
accept_type: VariableType = VariableType.DOCUMENT
|
|
261
|
+
ret_type: VariableType = VariableType.DOCUMENT
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
KW_FILTER_IS_REGEX = TypedDict(
|
|
265
|
+
"KW_FILTER_IS_REGEX", {"pattern": str, "ignore_case": bool}
|
|
266
|
+
)
|
|
267
|
+
ARGS_FILTER_IS_REGEX = tuple[str, bool]
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@dataclass(kw_only=True)
|
|
271
|
+
class FilterDocIsRegexText(
|
|
272
|
+
BaseAstNode[KW_FILTER_IS_REGEX, ARGS_FILTER_IS_REGEX]
|
|
273
|
+
):
|
|
274
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_DOC_IS_RE_TEXT
|
|
275
|
+
accept_type: VariableType = VariableType.DOCUMENT
|
|
276
|
+
ret_type: VariableType = VariableType.DOCUMENT
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@dataclass(kw_only=True)
|
|
280
|
+
class FilterDocIsRegexRaw(
|
|
281
|
+
BaseAstNode[KW_FILTER_IS_REGEX, ARGS_FILTER_IS_REGEX]
|
|
282
|
+
):
|
|
283
|
+
kind: ClassVar[TokenType] = TokenType.FILTER_DOC_IS_RE_RAW
|
|
284
|
+
accept_type: VariableType = VariableType.DOCUMENT
|
|
285
|
+
ret_type: VariableType = VariableType.DOCUMENT
|
|
@@ -150,7 +150,7 @@ class AstBuilder:
|
|
|
150
150
|
|
|
151
151
|
def _literal_struct_node(self, schema: Type[BaseSchema]) -> StructParser:
|
|
152
152
|
# dont required TypeDef struct generate
|
|
153
|
-
literals = schema.
|
|
153
|
+
literals = schema.__get_mro_classvars__().copy()
|
|
154
154
|
st = StructParser(
|
|
155
155
|
kwargs={
|
|
156
156
|
"name": schema.__name__,
|
|
@@ -175,7 +175,7 @@ class AstBuilder:
|
|
|
175
175
|
parent = node_method
|
|
176
176
|
for expr in node_method.body:
|
|
177
177
|
expr.parent = parent
|
|
178
|
-
if expr.kind
|
|
178
|
+
if expr.kind in (TokenType.EXPR_FILTER, TokenType.EXPR_DOC_FILTER):
|
|
179
179
|
self._ref_filter_expr(expr, expr.body)
|
|
180
180
|
|
|
181
181
|
def _unwrap_default_node(
|
|
@@ -251,7 +251,7 @@ class AstBuilder:
|
|
|
251
251
|
exprs.append(ExprNoReturn())
|
|
252
252
|
method.body.extend(exprs)
|
|
253
253
|
self._ref_method_exprs(method)
|
|
254
|
-
node_start_parse.body.append(ExprCallStructMethod(kwargs={"type": VariableType.NULL, "name": "__PRE_VALIDATE__",}))
|
|
254
|
+
node_start_parse.body.append(ExprCallStructMethod(kwargs={"type": VariableType.NULL, "name": "__PRE_VALIDATE__",}, ret_type=VariableType.NULL))
|
|
255
255
|
return method
|
|
256
256
|
# fmt: on
|
|
257
257
|
|
|
@@ -270,7 +270,11 @@ class AstBuilder:
|
|
|
270
270
|
exprs.append(ExprReturn(ret_type=VariableType.LIST_DOCUMENT))
|
|
271
271
|
method.body.extend(exprs)
|
|
272
272
|
self._ref_method_exprs(method)
|
|
273
|
-
node_start_parse.body.append(
|
|
273
|
+
node_start_parse.body.append(
|
|
274
|
+
ExprCallStructMethod(
|
|
275
|
+
kwargs={"type": VariableType.LIST_DOCUMENT, "name": "__SPLIT_DOC__",},
|
|
276
|
+
ret_type=VariableType.LIST_DOCUMENT)
|
|
277
|
+
)
|
|
274
278
|
return method
|
|
275
279
|
# fmt: on
|
|
276
280
|
|
|
@@ -314,7 +318,7 @@ class AstBuilder:
|
|
|
314
318
|
self._ref_method_exprs(method)
|
|
315
319
|
st_fields.append(method)
|
|
316
320
|
node_start_parse.body.append(
|
|
317
|
-
ExprCallStructMethod(kwargs={"type": exprs[-1].ret_type, "name": field_name,}))
|
|
321
|
+
ExprCallStructMethod(kwargs={"type": exprs[-1].ret_type, "name": field_name,}, ret_type=exprs[-1].ret_type))
|
|
318
322
|
return st_fields
|
|
319
323
|
# fmt: on
|
|
320
324
|
|
|
@@ -368,7 +372,7 @@ class AstBuilder:
|
|
|
368
372
|
kwargs={"name": schema.__name__, "struct_type": schema.__SCHEMA_TYPE__, "docstring": docstring if self.gen_docstr else "",},
|
|
369
373
|
parent=self.module,
|
|
370
374
|
)
|
|
371
|
-
literals = schema.
|
|
375
|
+
literals = schema.__get_mro_classvars__().copy()
|
|
372
376
|
|
|
373
377
|
for literal in literals.values():
|
|
374
378
|
st.body.append(literal.expr())
|
|
@@ -151,5 +151,5 @@ def exec_module_code(path: str | Path, add_sys_path: bool = True) -> ModuleType:
|
|
|
151
151
|
def is_literals_only_schema(schema: Type[BaseSchema]) -> bool:
|
|
152
152
|
return (
|
|
153
153
|
len(schema.__get_mro_fields__()) == 0
|
|
154
|
-
and len(schema.
|
|
154
|
+
and len(schema.__get_mro_classvars__()) > 0
|
|
155
155
|
)
|
|
@@ -154,8 +154,8 @@ class BaseCodeConverter:
|
|
|
154
154
|
if for_struct_definition:
|
|
155
155
|
self.post_definitions[
|
|
156
156
|
(for_definition, for_struct_definition)
|
|
157
|
-
] = post_callback
|
|
158
|
-
self.post_definitions[for_definition] = post_callback
|
|
157
|
+
] = post_callback # type: ignore[assignment]
|
|
158
|
+
self.post_definitions[for_definition] = post_callback # type: ignore[assignment]
|
|
159
159
|
|
|
160
160
|
return decorator
|
|
161
161
|
|
|
@@ -277,6 +277,7 @@ class BaseCodeConverter:
|
|
|
277
277
|
|
|
278
278
|
case (
|
|
279
279
|
TokenType.EXPR_FILTER
|
|
280
|
+
| TokenType.EXPR_DOC_FILTER
|
|
280
281
|
| TokenType.FILTER_OR
|
|
281
282
|
| TokenType.FILTER_AND
|
|
282
283
|
| TokenType.FILTER_NOT
|