ssc_codegen 0.7.7__tar.gz → 0.8.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 (72) hide show
  1. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/PKG-INFO +32 -30
  2. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/README.md +29 -29
  3. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/pyproject.toml +3 -1
  4. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/__init__.py +15 -8
  5. ssc_codegen-0.8.0/ssc_codegen/ast_/__init__.py +32 -0
  6. ssc_codegen-0.8.0/ssc_codegen/ast_/base.py +88 -0
  7. ssc_codegen-0.8.0/ssc_codegen/ast_/nodes_array.py +33 -0
  8. ssc_codegen-0.8.0/ssc_codegen/ast_/nodes_cast.py +70 -0
  9. ssc_codegen-0.8.0/ssc_codegen/ast_/nodes_core.py +234 -0
  10. ssc_codegen-0.8.0/ssc_codegen/ast_/nodes_selectors.py +82 -0
  11. ssc_codegen-0.8.0/ssc_codegen/ast_/nodes_string.py +194 -0
  12. ssc_codegen-0.8.0/ssc_codegen/ast_/nodes_validate.py +90 -0
  13. ssc_codegen-0.8.0/ssc_codegen/ast_build/__init__.py +1 -0
  14. ssc_codegen-0.8.0/ssc_codegen/ast_build/main.py +339 -0
  15. ssc_codegen-0.8.0/ssc_codegen/ast_build/utils.py +96 -0
  16. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/cli/cli_utils.py +4 -12
  17. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/cli/consts.py +0 -6
  18. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/cli/main.py +52 -99
  19. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/cli/runtime_parse_runners.py +1 -1
  20. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/compiler.py +2 -2
  21. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/consts.py +6 -0
  22. ssc_codegen-0.8.0/ssc_codegen/converters/__init__.py +0 -0
  23. ssc_codegen-0.8.0/ssc_codegen/converters/base.py +265 -0
  24. ssc_codegen-0.8.0/ssc_codegen/converters/go_goquery.py +1204 -0
  25. ssc_codegen-0.8.0/ssc_codegen/converters/helpers.py +49 -0
  26. ssc_codegen-0.8.0/ssc_codegen/converters/js_pure.py +641 -0
  27. ssc_codegen-0.8.0/ssc_codegen/converters/py_base.py +841 -0
  28. ssc_codegen-0.8.0/ssc_codegen/converters/py_bs4.py +189 -0
  29. ssc_codegen-0.8.0/ssc_codegen/converters/py_parsel.py +200 -0
  30. ssc_codegen-0.8.0/ssc_codegen/converters/py_selectolax.py +187 -0
  31. ssc_codegen-0.8.0/ssc_codegen/converters/templates/__init__.py +3 -0
  32. ssc_codegen-0.8.0/ssc_codegen/converters/templates/go_goquery.py +411 -0
  33. ssc_codegen-0.8.0/ssc_codegen/converters/templates/js_pure.py +102 -0
  34. ssc_codegen-0.8.0/ssc_codegen/document.py +1110 -0
  35. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/document_utlis.py +58 -44
  36. ssc_codegen-0.8.0/ssc_codegen/json_struct.py +162 -0
  37. ssc_codegen-0.7.7/ssc_codegen/converters/json_to_schema.py → ssc_codegen-0.8.0/ssc_codegen/json_to_scc.py +1 -1
  38. ssc_codegen-0.8.0/ssc_codegen/logs.py +49 -0
  39. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/schema.py +13 -10
  40. ssc_codegen-0.8.0/ssc_codegen/static_checker/__init__.py +56 -0
  41. ssc_codegen-0.8.0/ssc_codegen/static_checker/base.py +88 -0
  42. ssc_codegen-0.8.0/ssc_codegen/static_checker/callbacks.py +363 -0
  43. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/str_utils.py +17 -2
  44. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/tokens.py +42 -9
  45. ssc_codegen-0.7.7/ssc_codegen/ast_build_utils.py +0 -259
  46. ssc_codegen-0.7.7/ssc_codegen/ast_builder.py +0 -336
  47. ssc_codegen-0.7.7/ssc_codegen/ast_ssc.py +0 -1326
  48. ssc_codegen-0.7.7/ssc_codegen/converters/__init__.py +0 -1
  49. ssc_codegen-0.7.7/ssc_codegen/converters/ast_utils.py +0 -70
  50. ssc_codegen-0.7.7/ssc_codegen/converters/base.py +0 -287
  51. ssc_codegen-0.7.7/ssc_codegen/converters/dart_universal_html.py +0 -563
  52. ssc_codegen-0.7.7/ssc_codegen/converters/go_goquery.py +0 -803
  53. ssc_codegen-0.7.7/ssc_codegen/converters/js_pure.py +0 -489
  54. ssc_codegen-0.7.7/ssc_codegen/converters/py_base.py +0 -583
  55. ssc_codegen-0.7.7/ssc_codegen/converters/py_bs4.py +0 -223
  56. ssc_codegen-0.7.7/ssc_codegen/converters/py_parsel.py +0 -224
  57. ssc_codegen-0.7.7/ssc_codegen/converters/py_scrapy.py +0 -219
  58. ssc_codegen-0.7.7/ssc_codegen/converters/py_selectolax.py +0 -192
  59. ssc_codegen-0.7.7/ssc_codegen/converters/templates/dart.py +0 -340
  60. ssc_codegen-0.7.7/ssc_codegen/converters/templates/go.py +0 -987
  61. ssc_codegen-0.7.7/ssc_codegen/converters/templates/js.py +0 -284
  62. ssc_codegen-0.7.7/ssc_codegen/converters/templates/py.py +0 -237
  63. ssc_codegen-0.7.7/ssc_codegen/converters/templates/template_bindings.py +0 -99
  64. ssc_codegen-0.7.7/ssc_codegen/document.py +0 -804
  65. ssc_codegen-0.7.7/ssc_codegen/json_struct.py +0 -176
  66. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/.gitignore +0 -0
  67. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/LICENSE +0 -0
  68. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/_compat.py +0 -0
  69. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/cli/__init__.py +0 -0
  70. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/cli/cli_callbacks.py +0 -0
  71. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/cli/code_callbacks.py +0 -0
  72. {ssc_codegen-0.7.7 → ssc_codegen-0.8.0}/ssc_codegen/selector_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssc_codegen
3
- Version: 0.7.7
3
+ Version: 0.8.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
@@ -19,9 +19,11 @@ Classifier: Topic :: Software Development :: Code Generators
19
19
  Classifier: Topic :: Text Processing :: Markup :: HTML
20
20
  Classifier: Topic :: Utilities
21
21
  Requires-Python: >=3.10
22
+ Requires-Dist: colorama>=0.4.6; sys_platform == 'win32'
22
23
  Requires-Dist: cssselect>=1.2.0
23
24
  Requires-Dist: httpx>=0.28.1
24
25
  Requires-Dist: ichrome>=4.0.4
26
+ Requires-Dist: jinja2>=3.1.6
25
27
  Requires-Dist: lxml>=5.3.0
26
28
  Requires-Dist: parsel>=1.10.0
27
29
  Requires-Dist: typer>=0.15.1
@@ -32,12 +34,12 @@ Description-Content-Type: text/markdown
32
34
 
33
35
  ## Introduction
34
36
 
35
- ssc-gen - based-python DSL language for writing html parsers in dataclass style for converting to targeting language.
37
+ ssc-gen - based-python DSL language for writing html parsers in dataclass style for translating to targeting language.
36
38
 
37
39
  Project solving next problems:
38
40
 
39
- - boilerplate code
40
- - create types (type annotations) and documentation
41
+ - decrease boilerplate code
42
+ - create types (or type annotations if possible) and documentation
41
43
  - simplify code support
42
44
  - portability to other languages
43
45
 
@@ -45,30 +47,28 @@ Project solving next problems:
45
47
 
46
48
  Current support converters
47
49
 
48
-
49
- | Language | Library (html parser backend) | XPath Support | CSS Support | Generated types | Code formatter |
50
- |---------------|--------------------------------------------------------------|---------------|-------------|------------------------------------------|----------------|
51
- | Python (3.8+) | bs4 | N | Y | TypedDict*, list, dict | ruff |
52
- | ... | parsel | Y | Y | ... | - |
53
- | ... | selectolax (modest) | N | Y | ... | - |
54
- | ... | scrapy (possibly use parsel - pass Response.selector object) | Y | Y | ... | - |
55
- | Dart (3) | universal_html | N | Y | record, List, Map | dart format |
56
- | js (ES6) | pure (firefox/chrome) | Y | Y | Array, Map** | - |
57
- | go (1.10+) | goquery | N | Y | struct(json anchors include), array, map | gofmt |
50
+ | Language | Library (html parser backend) | XPath Support | CSS Support | Generated types | Code formatter |
51
+ |---------------|-------------------------------|---------------|-------------|------------------------------------------|----------------|
52
+ | Python (3.8+) | bs4 | N | Y | TypedDict*, list, dict | ruff |
53
+ | ... | parsel | Y | Y | ... | - |
54
+ | ... | selectolax (modest) | N | Y | ... | - |
55
+ | js (ES6) | pure (firefox/chrome/nodejs) | Y | Y | Array, Map** | - |
56
+ | go (1.10+) | goquery | N | Y | struct(json anchors include), array, map | gofmt |
58
57
 
59
58
  - *this annotation type was deliberately chosen as a compromise reasons.
60
- Python has many ways of serialization: `dataclass, namedtuple, attrs, pydantic`
59
+ Python has many ways of serialization: `namedtuple, dataclass, attrs, pydantic, msgspec, etc`
61
60
  - TypedDict is like a build-in dict, but with IDE and linter hint support, and you can easily implement an adapter for the required structure.
62
- - **js not exists build-in serialization methods
61
+ - **js exclude build-in serialization methods
63
62
 
64
63
  ### Limitations
65
64
 
66
65
  For maximum portability of the configuration to the target language:
67
66
 
68
67
  - Use CSS selectors: they are guaranteed to be converted to XPATH
69
- - Unlike javascript, most html parse libs implement [CSS3 selectors standard](https://www.w3.org/TR/selectors-3/)
70
- - basic selectors: (`tag`, `.class`, `#id`)
71
- - combined: (`div p`, `ul > li`, `h2 +p`\[1])
68
+ - Unlike javascript, most html parse libs implement [CSS3 selectors standard](https://www.w3.org/TR/selectors-3/).
69
+ check in target lib complex selectors before implement:
70
+ - basic selectors: (`tag`, `.class`, `#id`, `tag1,tag2`)
71
+ - combined: (`div p`, `ul > li`, `h2 +p`, `title ~head`\[1])
72
72
  - attribute: (`a[href]`, `input[type='text']`)\[2]
73
73
  - pseudo classes: (`:nth-child(n)`, `:first-child`, `:last-child`)\[3]
74
74
  - **often, not support more complex, dynamic styles**: (`:has()`, `:nth-of-type()`, `:where()`, `:is()`)
@@ -177,15 +177,14 @@ Code output looks like this (code formatted by IDE):
177
177
 
178
178
  ```javascript
179
179
  // autogenerated by ssc-gen DO NOT_EDIT
180
- /**
181
- *
180
+ /***
182
181
  *
183
182
  * {
184
183
  * "title": "String",
185
184
  * "a_hrefs": "Array<String>"
186
- * }
187
- */
185
+ * }*/
188
186
  class HelloWorld {
187
+
189
188
  constructor(doc) {
190
189
  if (typeof doc === 'string') {
191
190
  this._doc = new DOMParser().parseFromString(doc, 'text/html');
@@ -196,18 +195,21 @@ class HelloWorld {
196
195
  }
197
196
  }
198
197
 
199
- _parseTitle(value) {
200
- let value1 = value.querySelector('title');
201
- return typeof value1.textContent === "undefined" ? value1.documentElement.textContent : value1.textContent;
198
+ _parseTitle(v) {
199
+ let v0 = v.querySelector('title');
200
+ return typeof v0.textContent === "undefined" ? v0.documentElement.textContent : v0.textContent;
202
201
  }
203
202
 
204
- _parseAHrefs(value) {
205
- let value1 = Array.from(value.querySelectorAll('a'));
206
- return value1.map(e => e.getAttribute('href'));
203
+ _parseAHrefs(v) {
204
+ let v0 = Array.from(v.querySelectorAll('a'));
205
+ return v0.map(e => e.getAttribute('href'));
207
206
  }
208
207
 
209
208
  parse() {
210
- return {title: this._parseTitle(this._doc), a_hrefs: this._parseAHrefs(this._doc)};
209
+ return {
210
+ title: this._parseTitle(this._doc),
211
+ a_hrefs: this._parseAHrefs(this._doc),
212
+ };
211
213
  }
212
214
  }
213
215
  ```
@@ -2,12 +2,12 @@
2
2
 
3
3
  ## Introduction
4
4
 
5
- ssc-gen - based-python DSL language for writing html parsers in dataclass style for converting to targeting language.
5
+ ssc-gen - based-python DSL language for writing html parsers in dataclass style for translating to targeting language.
6
6
 
7
7
  Project solving next problems:
8
8
 
9
- - boilerplate code
10
- - create types (type annotations) and documentation
9
+ - decrease boilerplate code
10
+ - create types (or type annotations if possible) and documentation
11
11
  - simplify code support
12
12
  - portability to other languages
13
13
 
@@ -15,30 +15,28 @@ Project solving next problems:
15
15
 
16
16
  Current support converters
17
17
 
18
-
19
- | Language | Library (html parser backend) | XPath Support | CSS Support | Generated types | Code formatter |
20
- |---------------|--------------------------------------------------------------|---------------|-------------|------------------------------------------|----------------|
21
- | Python (3.8+) | bs4 | N | Y | TypedDict*, list, dict | ruff |
22
- | ... | parsel | Y | Y | ... | - |
23
- | ... | selectolax (modest) | N | Y | ... | - |
24
- | ... | scrapy (possibly use parsel - pass Response.selector object) | Y | Y | ... | - |
25
- | Dart (3) | universal_html | N | Y | record, List, Map | dart format |
26
- | js (ES6) | pure (firefox/chrome) | Y | Y | Array, Map** | - |
27
- | go (1.10+) | goquery | N | Y | struct(json anchors include), array, map | gofmt |
18
+ | Language | Library (html parser backend) | XPath Support | CSS Support | Generated types | Code formatter |
19
+ |---------------|-------------------------------|---------------|-------------|------------------------------------------|----------------|
20
+ | Python (3.8+) | bs4 | N | Y | TypedDict*, list, dict | ruff |
21
+ | ... | parsel | Y | Y | ... | - |
22
+ | ... | selectolax (modest) | N | Y | ... | - |
23
+ | js (ES6) | pure (firefox/chrome/nodejs) | Y | Y | Array, Map** | - |
24
+ | go (1.10+) | goquery | N | Y | struct(json anchors include), array, map | gofmt |
28
25
 
29
26
  - *this annotation type was deliberately chosen as a compromise reasons.
30
- Python has many ways of serialization: `dataclass, namedtuple, attrs, pydantic`
27
+ Python has many ways of serialization: `namedtuple, dataclass, attrs, pydantic, msgspec, etc`
31
28
  - TypedDict is like a build-in dict, but with IDE and linter hint support, and you can easily implement an adapter for the required structure.
32
- - **js not exists build-in serialization methods
29
+ - **js exclude build-in serialization methods
33
30
 
34
31
  ### Limitations
35
32
 
36
33
  For maximum portability of the configuration to the target language:
37
34
 
38
35
  - Use CSS selectors: they are guaranteed to be converted to XPATH
39
- - Unlike javascript, most html parse libs implement [CSS3 selectors standard](https://www.w3.org/TR/selectors-3/)
40
- - basic selectors: (`tag`, `.class`, `#id`)
41
- - combined: (`div p`, `ul > li`, `h2 +p`\[1])
36
+ - Unlike javascript, most html parse libs implement [CSS3 selectors standard](https://www.w3.org/TR/selectors-3/).
37
+ check in target lib complex selectors before implement:
38
+ - basic selectors: (`tag`, `.class`, `#id`, `tag1,tag2`)
39
+ - combined: (`div p`, `ul > li`, `h2 +p`, `title ~head`\[1])
42
40
  - attribute: (`a[href]`, `input[type='text']`)\[2]
43
41
  - pseudo classes: (`:nth-child(n)`, `:first-child`, `:last-child`)\[3]
44
42
  - **often, not support more complex, dynamic styles**: (`:has()`, `:nth-of-type()`, `:where()`, `:is()`)
@@ -147,15 +145,14 @@ Code output looks like this (code formatted by IDE):
147
145
 
148
146
  ```javascript
149
147
  // autogenerated by ssc-gen DO NOT_EDIT
150
- /**
151
- *
148
+ /***
152
149
  *
153
150
  * {
154
151
  * "title": "String",
155
152
  * "a_hrefs": "Array<String>"
156
- * }
157
- */
153
+ * }*/
158
154
  class HelloWorld {
155
+
159
156
  constructor(doc) {
160
157
  if (typeof doc === 'string') {
161
158
  this._doc = new DOMParser().parseFromString(doc, 'text/html');
@@ -166,18 +163,21 @@ class HelloWorld {
166
163
  }
167
164
  }
168
165
 
169
- _parseTitle(value) {
170
- let value1 = value.querySelector('title');
171
- return typeof value1.textContent === "undefined" ? value1.documentElement.textContent : value1.textContent;
166
+ _parseTitle(v) {
167
+ let v0 = v.querySelector('title');
168
+ return typeof v0.textContent === "undefined" ? v0.documentElement.textContent : v0.textContent;
172
169
  }
173
170
 
174
- _parseAHrefs(value) {
175
- let value1 = Array.from(value.querySelectorAll('a'));
176
- return value1.map(e => e.getAttribute('href'));
171
+ _parseAHrefs(v) {
172
+ let v0 = Array.from(v.querySelectorAll('a'));
173
+ return v0.map(e => e.getAttribute('href'));
177
174
  }
178
175
 
179
176
  parse() {
180
- return {title: this._parseTitle(this._doc), a_hrefs: this._parseAHrefs(this._doc)};
177
+ return {
178
+ title: this._parseTitle(this._doc),
179
+ a_hrefs: this._parseAHrefs(this._doc),
180
+ };
181
181
  }
182
182
  }
183
183
  ```
@@ -1,13 +1,15 @@
1
1
  [project]
2
2
  name = "ssc_codegen"
3
- version = "0.7.7"
3
+ version = "0.8.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"
7
7
  dependencies = [
8
+ "colorama>=0.4.6 ; sys_platform == 'win32'",
8
9
  "cssselect>=1.2.0",
9
10
  "httpx>=0.28.1",
10
11
  "ichrome>=4.0.4",
12
+ "jinja2>=3.1.6",
11
13
  "lxml>=5.3.0",
12
14
  "parsel>=1.10.0",
13
15
  "typer>=0.15.1",
@@ -1,15 +1,22 @@
1
- from .document import HTMLDocument, StringDocument, ArrayDocument, AssertDocument, NestedDocument, DefaultDocument, \
1
+ from ssc_codegen.document import HTMLDocument, StringDocument, ArrayDocument, AssertDocument, NestedDocument, \
2
+ DefaultDocument, \
2
3
  NumericDocument, JsonDocument, BooleanDocument
3
- from .json_struct import Json
4
- from .schema import ItemSchema, DictSchema, ListSchema, FlatListSchema
4
+ from ssc_codegen.json_struct import Json
5
+ from ssc_codegen.logs import setup_logger
6
+ from ssc_codegen.schema import ItemSchema, DictSchema, ListSchema, FlatListSchema
7
+
8
+ setup_logger()
9
+
10
+ VERSION = "0.8.0"
5
11
 
6
- VERSION = "0.7.7"
7
12
 
8
13
  class __MISSING(object):
9
14
  pass
10
15
 
16
+
11
17
  _NO_DEFAULT = __MISSING()
12
18
 
19
+
13
20
  class Document(
14
21
  HTMLDocument,
15
22
  StringDocument,
@@ -27,12 +34,12 @@ class Nested(HTMLDocument, NestedDocument, ArrayDocument, AssertDocument):
27
34
  pass
28
35
 
29
36
 
30
- def D(default: None | str | int | float | __MISSING =_NO_DEFAULT) -> Document: # noqa
37
+ def D(default: None | str | int | float | __MISSING = _NO_DEFAULT) -> Document: # noqa
31
38
  """Shortcut as a Document() object
32
39
 
33
40
  :param default: .default() operator shortcut
34
41
  """
35
- if default==_NO_DEFAULT:
42
+ if default == _NO_DEFAULT:
36
43
  return Document()
37
44
  return Document().default(value=default) # type: ignore
38
45
 
@@ -42,12 +49,12 @@ def N() -> Nested: # noqa
42
49
  return Nested()
43
50
 
44
51
 
45
- def R(default: None | str | int | float | __MISSING =_NO_DEFAULT) -> Document: # noqa
52
+ def R(default: None | str | int | float | __MISSING = _NO_DEFAULT) -> Document: # noqa
46
53
  """Shortcut as a Document().raw() object.
47
54
  For regex and format string operations
48
55
 
49
56
  :param default: .default() operator shortcut
50
57
  """
51
- if default==_NO_DEFAULT:
58
+ if default == _NO_DEFAULT:
52
59
  return Document().raw()
53
60
  return Document().default(default).raw() # type: ignore
@@ -0,0 +1,32 @@
1
+ from .base import BaseAstNode
2
+ from .nodes_array import ExprIndex, ExprToListLength, ExprListStringJoin
3
+ from .nodes_cast import ExprNested, ExprJsonify, ExprToInt, ExprToFloat, ExprToListInt, ExprToListFloat, ExprToBool
4
+ from .nodes_core import (ExprReturn,
5
+ ExprNoReturn,
6
+ ExprDefaultValueEnd,
7
+ ExprDefaultValueStart,
8
+ ExprDefaultValueWrapper,
9
+ ModuleProgram,
10
+ Docstring,
11
+ ModuleImports,
12
+ StructInitMethod,
13
+ StructPreValidateMethod,
14
+ StructFieldMethod,
15
+ StructPartDocMethod,
16
+ StartParseMethod,
17
+ StructParser,
18
+ JsonStruct,
19
+ JsonStructField,
20
+ TypeDef,
21
+ TypeDefField,
22
+ ExprCallStructMethod)
23
+ from .nodes_selectors import ExprCss, ExprCssAll, ExprXpathAll, ExprXpath, ExprGetHtmlText, ExprGetHtmlRaw, \
24
+ ExprGetHtmlAttr, ExprGetHtmlAttrAll, ExprGetHtmlRawAll, ExprGetHtmlTextAll
25
+
26
+ from .nodes_string import ExprStringTrim, ExprStringRegex, ExprStringSplit, ExprStringReplace, ExprStringLeftTrim, \
27
+ ExprStringFormat, ExprListStringTrim, ExprStringRegexAll, ExprStringRegexSub, ExprStringRightTrim, \
28
+ ExprListStringFormat, ExprListStringReplace, ExprListStringRightTrim, ExprListStringLeftTrim, \
29
+ ExprListStringRegexSub, \
30
+ ExprStringRmPrefix, ExprStringRmSuffix, ExprStringRmPrefixAndSuffix, ExprListStringRmPrefixAndSuffix, \
31
+ ExprListStringRmPrefix, ExprListStringRmSuffix
32
+ from .nodes_validate import ExprIsCss, ExprIsEqual, ExprIsRegex, ExprIsXpath, ExprIsNotEqual, ExprIsContains
@@ -0,0 +1,88 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import (
3
+ ClassVar,
4
+ Mapping,
5
+ TypeVar,
6
+ Generic,
7
+ Any,
8
+ TypedDict,
9
+ Optional,
10
+ MutableSequence,
11
+ Sequence,
12
+ )
13
+
14
+ from ssc_codegen.tokens import TokenType, VariableType
15
+
16
+ T_ARGUMENTS = TypeVar("T_ARGUMENTS")
17
+ T_MAPPING_FIELD = TypeVar("T_MAPPING_FIELD", bound=Mapping[str, Any])
18
+ T_EMPTY_KWARGS = TypedDict("T_EMPTY_KWARGS", {})
19
+
20
+
21
+ @dataclass(kw_only=True)
22
+ class BaseAstNode(Generic[T_MAPPING_FIELD, T_ARGUMENTS]):
23
+ """base AST container"""
24
+
25
+ kind: ClassVar[TokenType]
26
+ kwargs: T_MAPPING_FIELD = field(default_factory=dict) # type: ignore[assignment]
27
+
28
+ body: MutableSequence["BaseAstNode"] = field(default_factory=list)
29
+ parent: Optional["BaseAstNode"] = field(default=None, repr=False)
30
+
31
+ # used for type check expressions
32
+ accept_type: VariableType = VariableType.ANY
33
+ ret_type: VariableType = VariableType.ANY
34
+ exclude_types: Sequence[VariableType] = field(default_factory=tuple)
35
+
36
+ def unpack_args(self) -> T_ARGUMENTS:
37
+ """extract all values from kwargs field wout keys"""
38
+ return tuple(self.kwargs.values()) # type: ignore
39
+
40
+ # todo: cached property?
41
+ @property
42
+ def index(self) -> int:
43
+ if self.parent:
44
+ return self.parent.body.index(self)
45
+ return -1
46
+
47
+ @property
48
+ def index_next(self) -> int:
49
+ if self.parent and len(self.parent.body) > self.index:
50
+ return self.index + 1
51
+ return -1
52
+
53
+ @property
54
+ def index_prev(self) -> int:
55
+ if self.parent and self.index == 0:
56
+ return -1
57
+ elif self.index == -1:
58
+ return -1
59
+ return self.index - 1
60
+
61
+ def __getattr__(self, name: str) -> Any:
62
+ # modify for provide extract argument from kwargs container (like property)
63
+ try:
64
+ return super().__getattribute__(name)
65
+ except AttributeError as e:
66
+ if self.kwargs.get(name):
67
+ return self.kwargs[name]
68
+ raise e
69
+
70
+
71
+ @dataclass(kw_only=True)
72
+ class _DisableReprBody(BaseAstNode):
73
+ """special class for disable repr unnecessary node fields"""
74
+
75
+ body: MutableSequence[BaseAstNode] = field(default_factory=list, repr=False)
76
+
77
+
78
+ @dataclass(kw_only=True)
79
+ class _DisableReprAcceptAndRetType(BaseAstNode):
80
+ accept_type: VariableType = field(default=VariableType.ANY, repr=False)
81
+ ret_type: VariableType = field(default=VariableType.ANY, repr=False)
82
+
83
+
84
+ @dataclass(kw_only=True)
85
+ class _DisableRepr(_DisableReprBody, _DisableReprAcceptAndRetType):
86
+ body: MutableSequence[BaseAstNode] = field(default_factory=list, repr=False)
87
+ accept_type: VariableType = field(default=VariableType.ANY, repr=False)
88
+ ret_type: VariableType = field(default=VariableType.ANY, repr=False)
@@ -0,0 +1,33 @@
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
+ KW_EXPR_INDEX = TypedDict("KW_EXPR_INDEX", {"index": int})
8
+ ARGS_EXPR_INDEX = tuple[int]
9
+
10
+
11
+ @dataclass(kw_only=True)
12
+ class ExprIndex(BaseAstNode[KW_EXPR_INDEX, ARGS_EXPR_INDEX]):
13
+ kind: ClassVar[TokenType] = TokenType.EXPR_LIST_ANY_INDEX
14
+ accept_type: VariableType = VariableType.LIST_ANY
15
+ ret_type: VariableType = VariableType.ANY
16
+
17
+
18
+ KW_EXPR_JOIN = TypedDict("KW_EXPR_JOIN", {"sep": str})
19
+ ARGS_EXPR_JOIN = tuple[str]
20
+
21
+
22
+ @dataclass(kw_only=True)
23
+ class ExprListStringJoin(BaseAstNode[KW_EXPR_JOIN, ARGS_EXPR_JOIN]):
24
+ kind: ClassVar[TokenType] = TokenType.EXPR_LIST_JOIN
25
+ accept_type: VariableType = VariableType.LIST_STRING
26
+ ret_type: VariableType = VariableType.STRING
27
+
28
+
29
+ @dataclass(kw_only=True)
30
+ class ExprToListLength(BaseAstNode[T_EMPTY_KWARGS, tuple]):
31
+ kind: ClassVar[TokenType] = TokenType.EXPR_LIST_LEN
32
+ accept_type: VariableType = VariableType.LIST_ANY
33
+ ret_type: VariableType = VariableType.INT
@@ -0,0 +1,70 @@
1
+ from dataclasses import dataclass
2
+ from typing import TypedDict, Type, TYPE_CHECKING, TypeAlias, ClassVar
3
+
4
+ from ssc_codegen.tokens import TokenType, VariableType, StructType
5
+ from ssc_codegen.ast_.base import BaseAstNode, T_EMPTY_KWARGS
6
+
7
+ if TYPE_CHECKING:
8
+ from ssc_codegen.schema import BaseSchema
9
+
10
+
11
+ @dataclass(kw_only=True)
12
+ class ExprToInt(BaseAstNode[T_EMPTY_KWARGS, tuple]):
13
+ kind: ClassVar[TokenType] = TokenType.TO_INT
14
+ accept_type: VariableType = VariableType.STRING
15
+ ret_type: VariableType = VariableType.INT
16
+
17
+
18
+ @dataclass(kw_only=True)
19
+ class ExprToListInt(BaseAstNode[T_EMPTY_KWARGS, tuple]):
20
+ kind: ClassVar[TokenType] = TokenType.TO_INT_LIST
21
+ accept_type: VariableType = VariableType.LIST_STRING
22
+ ret_type: VariableType = VariableType.LIST_INT
23
+
24
+
25
+ @dataclass(kw_only=True)
26
+ class ExprToFloat(BaseAstNode[T_EMPTY_KWARGS, tuple]):
27
+ kind: ClassVar[TokenType] = TokenType.TO_FLOAT
28
+ accept_type: VariableType = VariableType.STRING
29
+ ret_type: VariableType = VariableType.FLOAT
30
+
31
+
32
+ @dataclass(kw_only=True)
33
+ class ExprToListFloat(BaseAstNode[T_EMPTY_KWARGS, tuple]):
34
+ kind: ClassVar[TokenType] = TokenType.TO_FLOAT_LIST
35
+ accept_type: VariableType = VariableType.LIST_STRING
36
+ ret_type: VariableType = VariableType.LIST_FLOAT
37
+
38
+
39
+ @dataclass(kw_only=True)
40
+ class ExprToBool(BaseAstNode[T_EMPTY_KWARGS, tuple]):
41
+ kind: ClassVar[TokenType] = TokenType.TO_BOOL
42
+ accept_type: VariableType = VariableType.ANY
43
+ ret_type: VariableType = VariableType.BOOL
44
+
45
+
46
+ _T_SCHEMA: TypeAlias = Type["BaseSchema"]
47
+ KW_EXPR_NESTED = TypedDict(
48
+ "KW_EXPR_NESTED", {"schema_name": str, "schema_type": StructType}
49
+ )
50
+ EXPR_NESTED_ARGS = tuple[str, StructType]
51
+
52
+
53
+ @dataclass(kw_only=True)
54
+ class ExprNested(BaseAstNode[KW_EXPR_NESTED, EXPR_NESTED_ARGS]):
55
+ kind: ClassVar[TokenType] = TokenType.EXPR_NESTED
56
+ accept_type: VariableType = VariableType.DOCUMENT
57
+ ret_type: VariableType = VariableType.NESTED
58
+
59
+
60
+ KW_EXPR_JSONIFY = TypedDict(
61
+ "KW_EXPR_JSONIFY", {"json_struct_name": str, "is_array": bool}
62
+ )
63
+ ARGS_EXPR_JSONIFY = tuple[str, bool]
64
+
65
+
66
+ @dataclass(kw_only=True)
67
+ class ExprJsonify(BaseAstNode[KW_EXPR_JSONIFY, ARGS_EXPR_JSONIFY]):
68
+ kind: ClassVar[TokenType] = TokenType.TO_JSON
69
+ accept_type: VariableType = VariableType.STRING
70
+ ret_type: VariableType = VariableType.JSON