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.

Files changed (67) hide show
  1. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/PKG-INFO +5 -3
  2. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/README.md +4 -2
  3. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/pyproject.toml +1 -1
  4. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/__init__.py +8 -4
  5. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/_compat.py +6 -3
  6. ssc_codegen-0.12.0/ssc_codegen/ast_/__init__.py +129 -0
  7. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_/base.py +10 -4
  8. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_core.py +4 -4
  9. ssc_codegen-0.12.0/ssc_codegen/ast_/nodes_filter.py +285 -0
  10. ssc_codegen-0.12.0/ssc_codegen/ast_build/builder.py +426 -0
  11. ssc_codegen-0.12.0/ssc_codegen/ast_build/main.py +91 -0
  12. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_build/utils.py +1 -1
  13. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/compiler.py +5 -23
  14. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/base.py +119 -23
  15. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/go_goquery.py +765 -202
  16. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/helpers.py +24 -18
  17. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/js_pure.py +423 -69
  18. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/lua_htmlparser.py +147 -64
  19. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/py_base.py +152 -105
  20. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/py_bs4.py +172 -0
  21. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/py_lxml.py +180 -0
  22. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/py_parsel.py +183 -2
  23. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/py_selectolax.py +175 -0
  24. ssc_codegen-0.12.0/ssc_codegen/converters/templates/go_goquery.py +440 -0
  25. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/js_pure.py +0 -31
  26. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/document.py +457 -32
  27. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/json_struct.py +2 -2
  28. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/json_to_scc.py +3 -2
  29. ssc_codegen-0.12.0/ssc_codegen/schema.py +354 -0
  30. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/static_checker/__init__.py +7 -2
  31. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/static_checker/base.py +4 -2
  32. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/static_checker/callbacks.py +80 -6
  33. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/tokens.py +22 -1
  34. ssc_codegen-0.10.2/ssc_codegen/ast_/__init__.py +0 -40
  35. ssc_codegen-0.10.2/ssc_codegen/ast_/nodes_filter.py +0 -151
  36. ssc_codegen-0.10.2/ssc_codegen/ast_build/main.py +0 -516
  37. ssc_codegen-0.10.2/ssc_codegen/converters/templates/go_goquery.py +0 -737
  38. ssc_codegen-0.10.2/ssc_codegen/schema.py +0 -310
  39. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/.gitignore +0 -0
  40. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/LICENSE +0 -0
  41. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_array.py +0 -0
  42. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_cast.py +0 -0
  43. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_selectors.py +0 -0
  44. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_string.py +0 -0
  45. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_/nodes_validate.py +0 -0
  46. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_build/__init__.py +0 -0
  47. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_grep_rules/js_rules.yml +0 -0
  48. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/ast_grep_rules/py_rules.yml +0 -0
  49. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/__init__.py +0 -0
  50. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/ast_grep.py +0 -0
  51. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/cli_callbacks.py +0 -0
  52. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/cli_utils.py +0 -0
  53. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/code_callbacks.py +0 -0
  54. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/consts.py +0 -0
  55. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/main.py +0 -0
  56. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/cli/runtime_parse_runners.py +0 -0
  57. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/__init__.py +0 -0
  58. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/__init__.py +0 -0
  59. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/lua_base.py +0 -0
  60. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/lua_css_compat.py +0 -0
  61. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/lua_re_compat.py +0 -0
  62. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/converters/templates/py_base.py +0 -0
  63. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/document_utlis.py +0 -0
  64. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/logs.py +0 -0
  65. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/pseudo_selectors.py +0 -0
  66. {ssc_codegen-0.10.2 → ssc_codegen-0.12.0}/ssc_codegen/selector_utils.py +0 -0
  67. {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.10.2
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
- - [LLM]("llm.md") experimental prompt for generate code
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
- - [LLM]("llm.md") experimental prompt for generate code
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,6 +1,6 @@
1
1
  [project]
2
2
  name = "ssc_codegen"
3
- version = "0.10.2"
3
+ version = "0.12.0"
4
4
  description = "Python-dsl code converter to html parser for web scraping "
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -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.2"
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 CV(value, self_cls_path: str | None = None, *, returns: bool = False) -> ClassVarDocument:
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
- "KW_EXPR_CALL_LITERAL",
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
- "KW_LITERAL",
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