ssc_codegen 0.10.2__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.10.2 → ssc_codegen-0.12.0}/PKG-INFO +5 -3
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/README.md +4 -2
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/pyproject.toml +1 -1
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/__init__.py +8 -4
- {ssc_codegen-0.10.2 → 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.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_/base.py +10 -4
- {ssc_codegen-0.10.2 → 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.12.0/ssc_codegen/ast_build/builder.py +426 -0
- ssc_codegen-0.12.0/ssc_codegen/ast_build/main.py +91 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_build/utils.py +1 -1
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/compiler.py +5 -23
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/base.py +119 -23
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/go_goquery.py +765 -202
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/helpers.py +24 -18
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/js_pure.py +423 -69
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/lua_htmlparser.py +147 -64
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/py_base.py +152 -105
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/py_bs4.py +172 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/py_lxml.py +180 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/py_parsel.py +183 -2
- {ssc_codegen-0.10.2 → 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.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/js_pure.py +0 -31
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/document.py +457 -32
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/json_struct.py +2 -2
- {ssc_codegen-0.10.2 → 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.10.2 → ssc_codegen-0.12.0}/ssc_codegen/static_checker/__init__.py +7 -2
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/static_checker/base.py +4 -2
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/static_checker/callbacks.py +80 -6
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/tokens.py +22 -1
- ssc_codegen-0.10.2/ssc_codegen/ast_/__init__.py +0 -40
- ssc_codegen-0.10.2/ssc_codegen/ast_/nodes_filter.py +0 -151
- ssc_codegen-0.10.2/ssc_codegen/ast_build/main.py +0 -516
- ssc_codegen-0.10.2/ssc_codegen/converters/templates/go_goquery.py +0 -737
- ssc_codegen-0.10.2/ssc_codegen/schema.py +0 -310
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/.gitignore +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/LICENSE +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_array.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_cast.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_selectors.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_string.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_validate.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_build/__init__.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_grep_rules/js_rules.yml +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_grep_rules/py_rules.yml +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/__init__.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/ast_grep.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/cli_callbacks.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/cli_utils.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/code_callbacks.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/consts.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/main.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/runtime_parse_runners.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/__init__.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/__init__.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/lua_base.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/lua_css_compat.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/lua_re_compat.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/py_base.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/document_utlis.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/logs.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/pseudo_selectors.py +0 -0
- {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/selector_utils.py +0 -0
- {ssc_codegen-0.10.2 → 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
|
|
@@ -248,8 +248,10 @@ You can use any html source:
|
|
|
248
248
|
## See also
|
|
249
249
|
|
|
250
250
|
|
|
251
|
-
- [Brief](docs/briefing.md) about css selectors and regular expressions
|
|
252
|
-
- [
|
|
251
|
+
- [Brief](docs/briefing.md) about css selectors and regular expressions
|
|
252
|
+
- [Explain](docs/explain.md) short document on how to understand DSL syntax
|
|
253
|
+
- [LLM](llm.md) experimental prompt for generate code
|
|
254
|
+
- [Explain](docs/explain.md) short note how to explain and read sscgen schema configs
|
|
253
255
|
- [Quickstart](docs/quickstart.md) about css selectors and regular expressions.
|
|
254
256
|
- [Tutorial](docs/tutorial.md) basic usage ssc-gen
|
|
255
257
|
- [AST reference](docs/ast_reference.md) about generation code from AST
|
|
@@ -211,8 +211,10 @@ You can use any html source:
|
|
|
211
211
|
## See also
|
|
212
212
|
|
|
213
213
|
|
|
214
|
-
- [Brief](docs/briefing.md) about css selectors and regular expressions
|
|
215
|
-
- [
|
|
214
|
+
- [Brief](docs/briefing.md) about css selectors and regular expressions
|
|
215
|
+
- [Explain](docs/explain.md) short document on how to understand DSL syntax
|
|
216
|
+
- [LLM](llm.md) experimental prompt for generate code
|
|
217
|
+
- [Explain](docs/explain.md) short note how to explain and read sscgen schema configs
|
|
216
218
|
- [Quickstart](docs/quickstart.md) about css selectors and regular expressions.
|
|
217
219
|
- [Tutorial](docs/tutorial.md) basic usage ssc-gen
|
|
218
220
|
- [AST reference](docs/ast_reference.md) about generation code from AST
|
|
@@ -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,7 +68,12 @@ def F() -> DocumentFilter:
|
|
|
68
68
|
return DocumentFilter()
|
|
69
69
|
|
|
70
70
|
|
|
71
|
-
def
|
|
71
|
+
def FE() -> DocumentElementsFilter:
|
|
72
|
+
"""Shortcut as a DocumentElementsFilter() object"""
|
|
73
|
+
return DocumentElementsFilter()
|
|
74
|
+
|
|
75
|
+
|
|
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
|
|
|
74
79
|
pass `self_cls_path` argument if need use this var inner schema context:
|
|
@@ -90,5 +95,4 @@ class A(ItemShema):
|
|
|
90
95
|
```
|
|
91
96
|
|
|
92
97
|
"""
|
|
93
|
-
|
|
94
98
|
return ClassVarDocument(value, self_cls_path, parse_returns=returns)
|
|
@@ -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
|
+
)
|
|
@@ -45,10 +45,16 @@ class BaseAstNode(Generic[T_MAPPING_FIELD, T_ARGUMENTS]):
|
|
|
45
45
|
"""extract all values from kwargs field wout keys"""
|
|
46
46
|
return tuple(self.kwargs.values()) # type: ignore
|
|
47
47
|
|
|
48
|
-
# todo: cached property?
|
|
49
48
|
@property
|
|
50
49
|
def index(self) -> int:
|
|
51
50
|
if self.parent:
|
|
51
|
+
# self.parent.body.index(self) add side effect if expr has same operations:
|
|
52
|
+
# 0 1 1 1
|
|
53
|
+
# D().raw().trim().trim().trim()
|
|
54
|
+
# use compare by id
|
|
55
|
+
for i, child in enumerate(self.parent.body):
|
|
56
|
+
if id(child) == id(self):
|
|
57
|
+
return i
|
|
52
58
|
return self.parent.body.index(self)
|
|
53
59
|
return -1
|
|
54
60
|
|
|
@@ -92,10 +98,10 @@ class BaseAstNode(Generic[T_MAPPING_FIELD, T_ARGUMENTS]):
|
|
|
92
98
|
|
|
93
99
|
def find_node_all(
|
|
94
100
|
self, func: Callable[["BaseAstNode"], bool]
|
|
95
|
-
) -> "BaseAstNode":
|
|
101
|
+
) -> list["BaseAstNode"]:
|
|
96
102
|
"""find nodes in body by func filter"""
|
|
97
103
|
if len(self.body) == 0:
|
|
98
|
-
return []
|
|
104
|
+
return [] # type: ignore
|
|
99
105
|
return [i for i in self.body if i.kind == func(i)]
|
|
100
106
|
|
|
101
107
|
def find_node_by_token(
|
|
@@ -106,7 +112,7 @@ class BaseAstNode(Generic[T_MAPPING_FIELD, T_ARGUMENTS]):
|
|
|
106
112
|
|
|
107
113
|
def find_nodes_by_token(self, token_type: TokenType) -> list["BaseAstNode"]:
|
|
108
114
|
"""find nodes by TokenType"""
|
|
109
|
-
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]
|
|
110
116
|
|
|
111
117
|
|
|
112
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
|