ssc_codegen 0.1.12__tar.gz → 0.2.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 (62) hide show
  1. ssc_codegen-0.2.0/PKG-INFO +77 -0
  2. ssc_codegen-0.2.0/README.md +52 -0
  3. {ssc_codegen-0.1.12 → ssc_codegen-0.2.0}/pyproject.toml +15 -10
  4. ssc_codegen-0.2.0/ssc_codegen/__init__.py +4 -0
  5. ssc_codegen-0.2.0/ssc_codegen/_cli.py +175 -0
  6. ssc_codegen-0.2.0/ssc_codegen/converters/base.py +65 -0
  7. ssc_codegen-0.2.0/ssc_codegen/converters/dart_universal_html.py +382 -0
  8. ssc_codegen-0.2.0/ssc_codegen/converters/py_bs4.py +331 -0
  9. ssc_codegen-0.2.0/ssc_codegen/converters/py_parsel.py +328 -0
  10. ssc_codegen-0.2.0/ssc_codegen/converters/py_scrapy.py +330 -0
  11. ssc_codegen-0.2.0/ssc_codegen/converters/py_selectolax.py +318 -0
  12. ssc_codegen-0.2.0/ssc_codegen/converters/utils.py +9 -0
  13. ssc_codegen-0.2.0/ssc_codegen/document.py +358 -0
  14. ssc_codegen-0.2.0/ssc_codegen/generator.py +200 -0
  15. ssc_codegen-0.2.0/ssc_codegen/objects.py +203 -0
  16. ssc_codegen-0.2.0/ssc_codegen/queries.py +65 -0
  17. ssc_codegen-0.2.0/ssc_codegen/schemas.py +79 -0
  18. ssc_codegen-0.2.0/ssc_codegen/template.py +96 -0
  19. ssc_codegen-0.2.0/ssc_codegen/templates/dart/universal_html/baseStruct.j2 +41 -0
  20. ssc_codegen-0.2.0/ssc_codegen/templates/dart/universal_html/dictStruct.j2 +54 -0
  21. ssc_codegen-0.2.0/ssc_codegen/templates/dart/universal_html/itemStruct.j2 +45 -0
  22. ssc_codegen-0.2.0/ssc_codegen/templates/dart/universal_html/listStruct.j2 +55 -0
  23. ssc_codegen-0.2.0/ssc_codegen/templates/python/bs4/baseStruct.j2 +38 -0
  24. ssc_codegen-0.2.0/ssc_codegen/templates/python/bs4/dictStruct.j2 +39 -0
  25. ssc_codegen-0.2.0/ssc_codegen/templates/python/bs4/itemStruct.j2 +35 -0
  26. ssc_codegen-0.2.0/ssc_codegen/templates/python/bs4/listStruct.j2 +43 -0
  27. ssc_codegen-0.2.0/ssc_codegen/templates/python/parsel/baseStruct.j2 +38 -0
  28. ssc_codegen-0.2.0/ssc_codegen/templates/python/parsel/dictStruct.j2 +39 -0
  29. ssc_codegen-0.2.0/ssc_codegen/templates/python/parsel/itemStruct.j2 +36 -0
  30. ssc_codegen-0.2.0/ssc_codegen/templates/python/parsel/listStruct.j2 +44 -0
  31. ssc_codegen-0.2.0/ssc_codegen/templates/python/scrapy/baseStruct.j2 +41 -0
  32. ssc_codegen-0.2.0/ssc_codegen/templates/python/scrapy/dictStruct.j2 +39 -0
  33. ssc_codegen-0.2.0/ssc_codegen/templates/python/scrapy/itemStruct.j2 +35 -0
  34. ssc_codegen-0.2.0/ssc_codegen/templates/python/scrapy/listStruct.j2 +44 -0
  35. ssc_codegen-0.2.0/ssc_codegen/templates/python/selectolax/baseStruct.j2 +38 -0
  36. ssc_codegen-0.2.0/ssc_codegen/templates/python/selectolax/dictStruct.j2 +39 -0
  37. ssc_codegen-0.2.0/ssc_codegen/templates/python/selectolax/itemStruct.j2 +36 -0
  38. ssc_codegen-0.2.0/ssc_codegen/templates/python/selectolax/listStruct.j2 +43 -0
  39. ssc_codegen-0.2.0/ssc_codegen/validate.py +84 -0
  40. ssc_codegen-0.1.12/PKG-INFO +0 -293
  41. ssc_codegen-0.1.12/README.md +0 -268
  42. ssc_codegen-0.1.12/ssc_codegen/analyzer.py +0 -309
  43. ssc_codegen-0.1.12/ssc_codegen/cli.py +0 -119
  44. ssc_codegen-0.1.12/ssc_codegen/configs/__init__.py +0 -0
  45. ssc_codegen-0.1.12/ssc_codegen/configs/codegen_tools.py +0 -300
  46. ssc_codegen-0.1.12/ssc_codegen/configs/dart/__init__.py +0 -0
  47. ssc_codegen-0.1.12/ssc_codegen/configs/dart/dart_html.j2 +0 -98
  48. ssc_codegen-0.1.12/ssc_codegen/configs/dart/dart_html.py +0 -254
  49. ssc_codegen-0.1.12/ssc_codegen/configs/python/__init__.py +0 -0
  50. ssc_codegen-0.1.12/ssc_codegen/configs/python/python_any.j2 +0 -75
  51. ssc_codegen-0.1.12/ssc_codegen/configs/python/python_bs4.py +0 -202
  52. ssc_codegen-0.1.12/ssc_codegen/configs/python/python_parsel.py +0 -185
  53. ssc_codegen-0.1.12/ssc_codegen/exceptions.py +0 -28
  54. ssc_codegen-0.1.12/ssc_codegen/lexer.py +0 -216
  55. ssc_codegen-0.1.12/ssc_codegen/objects.py +0 -259
  56. ssc_codegen-0.1.12/ssc_codegen/parser.py +0 -88
  57. ssc_codegen-0.1.12/ssc_codegen/render.py +0 -37
  58. ssc_codegen-0.1.12/ssc_codegen/template_utils.py +0 -78
  59. ssc_codegen-0.1.12/ssc_codegen/tools.py +0 -36
  60. ssc_codegen-0.1.12/ssc_codegen/yaml_parser.py +0 -149
  61. {ssc_codegen-0.1.12 → ssc_codegen-0.2.0}/LICENSE +0 -0
  62. {ssc_codegen-0.1.12/ssc_codegen → ssc_codegen-0.2.0/ssc_codegen/converters}/__init__.py +0 -0
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.1
2
+ Name: ssc_codegen
3
+ Version: 0.2.0
4
+ Summary: generate selector schemas classes from dsl-like language based on python
5
+ License: MIT
6
+ Author: vypivshiy
7
+ Requires-Python: >=3.10,<4.0
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: Implementation :: CPython
16
+ Classifier: Topic :: Internet
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Topic :: Text Processing
19
+ Requires-Dist: Jinja2 (>=3.1.2,<4.0.0)
20
+ Requires-Dist: PyYAML (>=6.0.1,<7.0.0)
21
+ Requires-Dist: cssselect (>=1.2.0,<2.0.0)
22
+ Requires-Dist: lxml (>=4.9.3,<5.0.0)
23
+ Description-Content-Type: text/markdown
24
+
25
+ # Selector schema codegen
26
+
27
+ ssc_codegen - generator of parsers for various programming languages (for html priority) using
28
+ python-DSL configurations with built-in declarative language.
29
+
30
+ Designed to port parsers to various programming languages and libs
31
+
32
+ # Motivation
33
+ - ~~interesting in practice write DSL-like language~~
34
+ - decrease boilerplate code for web-parsers
35
+ - write once - convert to other mainstream http parser libs
36
+ - minimal operations for easy add another libs and languages in future
37
+ - include css, xpath, attributes operations, regex, minimal string formatting operations
38
+ - pre validate css/xpath queries and logic before generate code
39
+ - standardisation: generate classes with minimal dependencies and documented parsed signature
40
+
41
+ ## Install
42
+
43
+ ### pipx (recommended for CLI usage)
44
+
45
+ ```shell
46
+ pipx install ssc_codegen
47
+ ```
48
+
49
+ ### pip
50
+
51
+ ```shell
52
+ pip install ssc_codegen
53
+ ```
54
+
55
+ ## Supported libs and languages
56
+
57
+ | language | lib | xpath | css | formatter |
58
+ |----------|--------------------------------------------------------------|-------|-----|-------------|
59
+ | python | bs4 | NO | YES | black |
60
+ | - | parsel | YES | YES | - |
61
+ | - | selectolax (modest) | NO | YES | - |
62
+ | - | scrapy (based on parsel, but class init argument - Response) | YES | YES | - |
63
+ | dart | universal_html | NO | YES | dart format |
64
+
65
+ ### Quickstart
66
+
67
+ see [example](example) and read code with comments
68
+
69
+ ### Recommendations
70
+
71
+ - usage css selector: they can be **guaranteed** converted to xpath (if target language not support CSS selectors)
72
+ - usage simple operations for more compatibility other libraries.
73
+ - Some libraries may not fully support selector specifications
74
+ - for example, `#product_description+ p` selector in `parsel` works fine, but not works in `selectolax`, `dart` libs
75
+ - there is a xpath to css converter for simple queries **without guarantees of functionality**.
76
+ For example, in css there is no analogue of `contains` from xpath, etc.
77
+
@@ -0,0 +1,52 @@
1
+ # Selector schema codegen
2
+
3
+ ssc_codegen - generator of parsers for various programming languages (for html priority) using
4
+ python-DSL configurations with built-in declarative language.
5
+
6
+ Designed to port parsers to various programming languages and libs
7
+
8
+ # Motivation
9
+ - ~~interesting in practice write DSL-like language~~
10
+ - decrease boilerplate code for web-parsers
11
+ - write once - convert to other mainstream http parser libs
12
+ - minimal operations for easy add another libs and languages in future
13
+ - include css, xpath, attributes operations, regex, minimal string formatting operations
14
+ - pre validate css/xpath queries and logic before generate code
15
+ - standardisation: generate classes with minimal dependencies and documented parsed signature
16
+
17
+ ## Install
18
+
19
+ ### pipx (recommended for CLI usage)
20
+
21
+ ```shell
22
+ pipx install ssc_codegen
23
+ ```
24
+
25
+ ### pip
26
+
27
+ ```shell
28
+ pip install ssc_codegen
29
+ ```
30
+
31
+ ## Supported libs and languages
32
+
33
+ | language | lib | xpath | css | formatter |
34
+ |----------|--------------------------------------------------------------|-------|-----|-------------|
35
+ | python | bs4 | NO | YES | black |
36
+ | - | parsel | YES | YES | - |
37
+ | - | selectolax (modest) | NO | YES | - |
38
+ | - | scrapy (based on parsel, but class init argument - Response) | YES | YES | - |
39
+ | dart | universal_html | NO | YES | dart format |
40
+
41
+ ### Quickstart
42
+
43
+ see [example](example) and read code with comments
44
+
45
+ ### Recommendations
46
+
47
+ - usage css selector: they can be **guaranteed** converted to xpath (if target language not support CSS selectors)
48
+ - usage simple operations for more compatibility other libraries.
49
+ - Some libraries may not fully support selector specifications
50
+ - for example, `#product_description+ p` selector in `parsel` works fine, but not works in `selectolax`, `dart` libs
51
+ - there is a xpath to css converter for simple queries **without guarantees of functionality**.
52
+ For example, in css there is no analogue of `contains` from xpath, etc.
@@ -1,7 +1,7 @@
1
1
  [tool.poetry]
2
2
  name = "ssc_codegen"
3
- version = "0.1.12"
4
- description = "generate selector schemas classes from yaml config and DSL-lang script"
3
+ version = "0.2.0"
4
+ description = "generate selector schemas classes from dsl-like language based on python"
5
5
  authors = ["vypivshiy"]
6
6
  readme = "README.md"
7
7
  license = "MIT"
@@ -17,6 +17,18 @@ classifiers = [
17
17
  "Topic :: Text Processing"
18
18
  ]
19
19
 
20
+ [tool.poetry.group.dev.dependencies]
21
+ bs4 = "^0.0.2"
22
+ selectolax = "^0.3.20"
23
+ parsel = "^1.8.1"
24
+ pytest = "^8.0.0"
25
+ mypy = "^1.6.1"
26
+ black = "^23.10.1"
27
+ isort = "^5.12.0"
28
+ ruff = "^0.1.4"
29
+ pre-commit = "^3.5.0"
30
+ scrapy = "^2.11.0"
31
+
20
32
  [project.urls]
21
33
  Documentation = "https://github.com/vypivshiy/selector_schema_codegen#readme"
22
34
  Issues = "https://github.com/vypivshiy/selector_schema_codegen/issues"
@@ -30,16 +42,9 @@ Jinja2 = "^3.1.2"
30
42
  cssselect = "^1.2.0"
31
43
  lxml = "^4.9.3"
32
44
 
33
- [tool.poetry.dev-dependencies]
34
- mypy = "^1.6.1"
35
- black = "^23.10.1"
36
- isort = "^5.12.0"
37
- ruff = "^0.1.4"
38
- pre-commit = "^3.5.0"
39
- pytest = "^7.4.3"
40
45
 
41
46
  [tool.poetry.scripts]
42
- ssc-gen="ssc_codegen.cli:main"
47
+ ssc-gen="ssc_codegen._cli:main"
43
48
 
44
49
  [build-system]
45
50
  requires = ["poetry-core>=1.0.0"]
@@ -0,0 +1,4 @@
1
+ from .template import render_code
2
+ from .schemas import BaseSchemaStrategy, ItemSchema, DictSchema, ListSchema
3
+ from .document import Document
4
+ from .validate import assert_
@@ -0,0 +1,175 @@
1
+ import subprocess
2
+ from pathlib import Path
3
+ from typing import NamedTuple
4
+ import argparse
5
+ import importlib.util
6
+
7
+ from ssc_codegen import render_code
8
+ from ssc_codegen.converters import py_scrapy, py_bs4, py_parsel, py_selectolax, dart_universal_html
9
+ from ssc_codegen.schemas import BaseSchemaStrategy
10
+
11
+
12
+ _CONVERTER_NAMESPACES = {
13
+ "py.bs4": py_bs4.converter,
14
+ "py.parsel": py_parsel.converter,
15
+ "py.selectolax": py_selectolax.converter,
16
+ "py.scrapy": py_scrapy.converter,
17
+ "dart": dart_universal_html.converter,
18
+ }
19
+
20
+
21
+ _EXT_PYTONIC = {"py.bs4", "py.parsel", "py.selectolax", "py.scrapy"}
22
+ _EXT_DART = {"dart", }
23
+
24
+ # formatters
25
+ _FMT_DART = "dart format {}"
26
+ _FMT_PYTHON = "black {}"
27
+
28
+
29
+ class ArgsNamespace(NamedTuple):
30
+ scenario: str
31
+ lang: str
32
+ out: str
33
+ OVERWRITE: bool
34
+ XPATH_TO_CSS: bool
35
+ CSS_TO_XPATH: bool
36
+ XPATH_PREFIX: str
37
+ NO_FORMAT: bool
38
+
39
+
40
+ def parse_arguments() -> ArgsNamespace:
41
+ parser = argparse.ArgumentParser(
42
+ prog="Selector schema generator",
43
+ description="Generate selector schemas from config file",
44
+ usage="ssc-gen my_conf.py py.bs4 -o out_file",
45
+ )
46
+
47
+ parser.add_argument("scenario", help="ssc_gen python file")
48
+ parser.add_argument(
49
+ "lang",
50
+ choices=list(_CONVERTER_NAMESPACES.keys()),
51
+ help="Programming language and lib choice",
52
+ )
53
+
54
+ parser.add_argument(
55
+ "-o",
56
+ "--out",
57
+ default=f"schema.{{}}",
58
+ help="Output directory (default: current working directory/schema.<ext>)",
59
+ )
60
+ parser.add_argument(
61
+ "-y",
62
+ dest="OVERWRITE",
63
+ default=False,
64
+ action="store_true",
65
+ help="Suggest overwrite file",
66
+ )
67
+ parser.add_argument("--xpath-to-css",
68
+ dest="XPATH_TO_CSS",
69
+ default=False,
70
+ action="store_true",
71
+ help="Convert xpath selectors to css (work not guaranteed)")
72
+
73
+ parser.add_argument("--css-to-xpath",
74
+ dest="CSS_TO_XPATH",
75
+ default=False,
76
+ action="store_true",
77
+ help="convert css selectors to xpath")
78
+ parser.add_argument("--xpath-prefix",
79
+ dest="XPATH_PREFIX",
80
+ default='descendant-or-self::',
81
+ type=str,
82
+ help="Xpath prefix for --css-to-xpath autoconverter. Default `descendant-or-self::`")
83
+
84
+ parser.add_argument(
85
+ "--no-format",
86
+ dest="NO_FORMAT",
87
+ default=False,
88
+ action="store_true",
89
+ help="Disable code format by third party solutions",
90
+ )
91
+
92
+ namespace: ArgsNamespace = parser.parse_args() # type: ignore
93
+ return namespace
94
+
95
+
96
+ def _extract_schemas(path: str):
97
+ spec = importlib.util.spec_from_file_location("script_module", path)
98
+ module = importlib.util.module_from_spec(spec)
99
+ spec.loader.exec_module(module)
100
+ schemas: list[BaseSchemaStrategy] = []
101
+ # Check if __all__ is defined in the module
102
+ if hasattr(module, '__all__'):
103
+ for obj_name in module.__all__:
104
+ obj = getattr(module, obj_name)
105
+ if issubclass(obj, BaseSchemaStrategy):
106
+ print("Founded", f"'{obj.__name__}'")
107
+ schemas.append(obj())
108
+ else:
109
+ print("Not founded objects, exit")
110
+ exit(1)
111
+
112
+ if len(schemas) == 0:
113
+ print("Not founded schemas, exit")
114
+ exit(1)
115
+
116
+ return schemas
117
+
118
+
119
+ def _suggest_overwrite(path_out: str, overwrite: bool):
120
+ if Path(path_out).exists() and not overwrite:
121
+ choice = input("Overwrite? (y/n)? ")
122
+ if choice.lower() != "y":
123
+ print("Cancel operation")
124
+ exit(1)
125
+
126
+
127
+ def _file_out(out: str, lang: str):
128
+ if out == "schema.{}":
129
+ if lang in _EXT_PYTONIC:
130
+ ext = "py"
131
+ elif lang in _EXT_DART:
132
+ ext = "dart"
133
+ else:
134
+ print("not founded extension, exit")
135
+ exit(1)
136
+ return out.format(ext)
137
+ return out
138
+
139
+
140
+ def main():
141
+ args = parse_arguments()
142
+ if args.CSS_TO_XPATH and args.XPATH_TO_CSS:
143
+ print('Should be choice --css-to-xpath or --xpath-to-css')
144
+ exit(1)
145
+
146
+ converter = _CONVERTER_NAMESPACES.get(args.lang)
147
+ print(f"Load '{args.lang}' converter")
148
+ schemas = _extract_schemas(args.scenario)
149
+ output = _file_out(args.out, args.lang)
150
+ _suggest_overwrite(output, args.OVERWRITE)
151
+
152
+ print("Render code")
153
+ if args.CSS_TO_XPATH:
154
+ print(F"convert CSS to XPATH with prefix '{args.XPATH_PREFIX}'")
155
+ elif args.XPATH_TO_CSS:
156
+ print(f"convert XPATH to CSS")
157
+
158
+ code = render_code(converter,
159
+ *schemas,
160
+ css_to_xpath=args.CSS_TO_XPATH,
161
+ xpath_to_css=args.XPATH_TO_CSS,
162
+ xpath_prefix=args.XPATH_PREFIX)
163
+ print(f"Save to {output}")
164
+ Path(output).write_text(code)
165
+ if not args.NO_FORMAT:
166
+ print("Format code...")
167
+ if args.lang.startswith('py.'):
168
+ subprocess.call(_FMT_PYTHON.format(output), shell=True)
169
+ elif args.lang == "dart":
170
+ subprocess.call(_FMT_DART.format(output), shell=True)
171
+ print("done.")
172
+
173
+
174
+ if __name__ == '__main__':
175
+ main()
@@ -0,0 +1,65 @@
1
+ from functools import partial
2
+
3
+ from ssc_codegen.objects import TokenType, Node
4
+
5
+
6
+ __all__ = ['CodeConverter', "VAR_L", "VAR_R"]
7
+
8
+
9
+ def var_left(node: Node, prefix: str) -> str:
10
+ """get next variable name"""
11
+ return prefix.format(node.id or 0)
12
+
13
+
14
+ def var_right(node: Node, prefix: str) -> str:
15
+ """get previous variable name"""
16
+ return prefix.format(node.prev_node.id or 0)
17
+
18
+
19
+ # Shortcuts hooks variable name
20
+ VAR_L = partial(var_left, prefix='var_{}')
21
+ VAR_R = partial(var_right, prefix='var_{}')
22
+
23
+
24
+ class CodeConverter:
25
+ """Translate Expressions into the parser code"""
26
+
27
+ def __init__(self,
28
+ indent: str = " " * 4,
29
+ end: str = "",
30
+ intent_inner_try: str = " " * 4 * 2,
31
+ templates_path: str = ""):
32
+
33
+ self.indent = indent
34
+ self.end = end
35
+ self.indent_inner_try = intent_inner_try
36
+ self.definitions: Dict[TokenType, Callable[[Node], str]] = {} # type: ignore
37
+ self._templates_path: str = templates_path
38
+
39
+ self._in_inner_try: bool = False
40
+
41
+ @property
42
+ def templates_path(self) -> str:
43
+ return self._templates_path
44
+
45
+ def __call__(self, for_definition: TokenType):
46
+ def decorator(func):
47
+ self.definitions[for_definition] = func
48
+ return func
49
+
50
+ return decorator
51
+
52
+ def convert(self, node: Node) -> str:
53
+ if callback := self.definitions.get(node.token):
54
+ if node.token == TokenType.OP_DEFAULT_START:
55
+ self._in_inner_try = True
56
+ return self.indent + callback(node)
57
+
58
+ elif node.token == TokenType.OP_DEFAULT_END:
59
+ self._in_inner_try = False
60
+ return self.indent + callback(node)
61
+
62
+ if self._in_inner_try:
63
+ return self.indent_inner_try + callback(node) + self.end
64
+ return self.indent + callback(node) + self.end
65
+ return ""