ssc_codegen 0.31.0__tar.gz → 0.32.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.
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/.gitignore +5 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/PKG-INFO +39 -24
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/README.md +36 -21
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/pyproject.toml +11 -3
- ssc_codegen-0.32.0/ssc_codegen/ast/__init__.py +268 -0
- ssc_codegen-0.32.0/ssc_codegen/ast/array.py +76 -0
- ssc_codegen-0.32.0/ssc_codegen/ast/base.py +52 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/ast/cast.py +31 -11
- ssc_codegen-0.32.0/ssc_codegen/ast/control.py +94 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/ast/extract.py +19 -7
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/ast/helpers.py +0 -3
- ssc_codegen-0.32.0/ssc_codegen/ast/jsondef.py +31 -0
- ssc_codegen-0.32.0/ssc_codegen/ast/module.py +114 -0
- ssc_codegen-0.32.0/ssc_codegen/ast/predicate_containers.py +65 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/ast/predicate_ops.py +0 -43
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/ast/regex.py +23 -8
- ssc_codegen-0.32.0/ssc_codegen/ast/rest.py +82 -0
- ssc_codegen-0.32.0/ssc_codegen/ast/selectors.py +97 -0
- ssc_codegen-0.32.0/ssc_codegen/ast/string.py +221 -0
- ssc_codegen-0.32.0/ssc_codegen/ast/struct.py +752 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/ast/typedef.py +3 -12
- ssc_codegen-0.32.0/ssc_codegen/ast/types.py +51 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/core/contexts.py +2 -44
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/core/expressions.py +429 -216
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/core/format.py +0 -11
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/core/linter.py +1504 -1491
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/core/module_handler.py +28 -80
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/core/predicates.py +68 -132
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/core/reader.py +114 -116
- ssc_codegen-0.32.0/ssc_codegen/core/rest_artifacts.py +138 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/core/struct_parser.py +30 -40
- ssc_codegen-0.32.0/ssc_codegen/core/type_checking.py +379 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/exceptions.py +0 -5
- ssc_codegen-0.32.0/ssc_codegen/explore.py +1540 -0
- ssc_codegen-0.32.0/ssc_codegen/generation/__init__.py +0 -0
- ssc_codegen-0.32.0/ssc_codegen/generation/builder.py +73 -0
- ssc_codegen-0.32.0/ssc_codegen/generation/runtime.py +161 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/health.py +10 -8
- ssc_codegen-0.32.0/ssc_codegen/main.py +1110 -0
- ssc_codegen-0.31.0/ssc_codegen/converters/helpers.py → ssc_codegen-0.32.0/ssc_codegen/naming.py +8 -33
- ssc_codegen-0.32.0/ssc_codegen/request_spec.py +179 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/__init__.py +0 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/golang/__init__.py +5 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/golang/http_libs/__init__.py +0 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/golang/http_libs/base.py +27 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/golang/http_libs/nethttp.py +64 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/golang/literals.py +48 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/golang/regex.py +26 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/golang/rest.py +489 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/golang/runtime.py +532 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/golang/visitor.py +1835 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/javascript/__init__.py +5 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/javascript/http_libs/__init__.py +0 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/javascript/http_libs/axios.py +34 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/javascript/http_libs/base.py +19 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/javascript/http_libs/fetch.py +31 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/javascript/rest.py +519 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/javascript/visitor.py +1584 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/profile.py +18 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/python/__init__.py +12 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/python/html_libs/__init__.py +0 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/python/html_libs/base.py +146 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/python/html_libs/bs4.py +218 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/python/html_libs/lxml.py +277 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/python/html_libs/parsel.py +269 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/python/html_libs/slax.py +222 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/python/http_libs/__init__.py +0 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/python/http_libs/aiohttp.py +179 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/python/http_libs/base.py +82 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/python/http_libs/httpx.py +130 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/python/http_libs/requests.py +130 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/python/rest.py +505 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/python/visitor.py +1366 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/resolver.py +121 -0
- ssc_codegen-0.32.0/ssc_codegen/targets/spec.py +15 -0
- ssc_codegen-0.32.0/ssc_codegen/traversal/__init__.py +0 -0
- ssc_codegen-0.32.0/ssc_codegen/traversal/context.py +47 -0
- ssc_codegen-0.32.0/ssc_codegen/traversal/utils.py +101 -0
- ssc_codegen-0.32.0/ssc_codegen/traversal/walker.py +335 -0
- ssc_codegen-0.31.0/ssc_codegen/ast/__init__.py +0 -193
- ssc_codegen-0.31.0/ssc_codegen/ast/array.py +0 -56
- ssc_codegen-0.31.0/ssc_codegen/ast/base.py +0 -24
- ssc_codegen-0.31.0/ssc_codegen/ast/control.py +0 -82
- ssc_codegen-0.31.0/ssc_codegen/ast/jsondef.py +0 -43
- ssc_codegen-0.31.0/ssc_codegen/ast/module.py +0 -108
- ssc_codegen-0.31.0/ssc_codegen/ast/predicate_containers.py +0 -44
- ssc_codegen-0.31.0/ssc_codegen/ast/selectors.py +0 -55
- ssc_codegen-0.31.0/ssc_codegen/ast/string.py +0 -158
- ssc_codegen-0.31.0/ssc_codegen/ast/struct.py +0 -509
- ssc_codegen-0.31.0/ssc_codegen/ast/transform.py +0 -63
- ssc_codegen-0.31.0/ssc_codegen/ast/types.py +0 -80
- ssc_codegen-0.31.0/ssc_codegen/converters/base.py +0 -333
- ssc_codegen-0.31.0/ssc_codegen/converters/js_pure.py +0 -2085
- ssc_codegen-0.31.0/ssc_codegen/converters/py_bs4.py +0 -1693
- ssc_codegen-0.31.0/ssc_codegen/converters/py_lxml.py +0 -515
- ssc_codegen-0.31.0/ssc_codegen/converters/py_parsel.py +0 -473
- ssc_codegen-0.31.0/ssc_codegen/converters/py_render.py +0 -231
- ssc_codegen-0.31.0/ssc_codegen/converters/py_slax.py +0 -421
- ssc_codegen-0.31.0/ssc_codegen/converters/runtime/__init__.py +0 -7
- ssc_codegen-0.31.0/ssc_codegen/converters/runtime/_helpers.py +0 -35
- ssc_codegen-0.31.0/ssc_codegen/converters/runtime/js_base.py +0 -24
- ssc_codegen-0.31.0/ssc_codegen/converters/runtime/py_base.py +0 -67
- ssc_codegen-0.31.0/ssc_codegen/converters/runtime/py_lxml.py +0 -7
- ssc_codegen-0.31.0/ssc_codegen/converters/runtime/py_rest.py +0 -133
- ssc_codegen-0.31.0/ssc_codegen/core/type_checking.py +0 -401
- ssc_codegen-0.31.0/ssc_codegen/document_utils.py +0 -114
- ssc_codegen-0.31.0/ssc_codegen/kdl/__init__.py +0 -61
- ssc_codegen-0.31.0/ssc_codegen/kdl/dict_reader.py +0 -56
- ssc_codegen-0.31.0/ssc_codegen/kdl/parser.py +0 -1368
- ssc_codegen-0.31.0/ssc_codegen/kdl/reader.py +0 -429
- ssc_codegen-0.31.0/ssc_codegen/main.py +0 -739
- ssc_codegen-0.31.0/ssc_codegen/pseudo_selectors.py +0 -39
- ssc_codegen-0.31.0/ssc_codegen/request_spec.py +0 -296
- ssc_codegen-0.31.0/ssc_codegen/selector_utils.py +0 -8
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/LICENSE +0 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/__init__.py +0 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/_logging.py +0 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/core/__init__.py +0 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/parsers/__init__.py +0 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/parsers/curl.py +0 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/parsers/http.py +0 -0
- {ssc_codegen-0.31.0 → ssc_codegen-0.32.0}/ssc_codegen/regex_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.32.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
|
|
@@ -22,9 +22,9 @@ Requires-Python: >=3.10
|
|
|
22
22
|
Requires-Dist: bs4>=0.0.2
|
|
23
23
|
Requires-Dist: colorama>=0.4.6; sys_platform == 'win32'
|
|
24
24
|
Requires-Dist: cssselect>=1.2.0
|
|
25
|
-
Requires-Dist:
|
|
25
|
+
Requires-Dist: httpx>=0.28.1
|
|
26
|
+
Requires-Dist: kdlquery>=1.3.0
|
|
26
27
|
Requires-Dist: lxml>=5.3.0
|
|
27
|
-
Requires-Dist: pyyaml>=6.0
|
|
28
28
|
Requires-Dist: soupsieve>=2.6
|
|
29
29
|
Requires-Dist: typer>0.26
|
|
30
30
|
Requires-Dist: typing-extensions; python_version < '3.11'
|
|
@@ -69,7 +69,7 @@ uv tool install ssc_codegen
|
|
|
69
69
|
Generate Python parser:
|
|
70
70
|
|
|
71
71
|
```bash
|
|
72
|
-
ssc-gen generate books.kdl -
|
|
72
|
+
ssc-gen generate books.kdl -l python -L bs4 -o ./output
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
## Usage
|
|
@@ -77,17 +77,24 @@ ssc-gen generate books.kdl -t py-bs4 -o ./output
|
|
|
77
77
|
### Generate code
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
|
-
# single file
|
|
81
|
-
ssc-gen generate schema.kdl -
|
|
80
|
+
# single file (Python + bs4)
|
|
81
|
+
ssc-gen generate schema.kdl -l python -L bs4 -o ./output
|
|
82
82
|
|
|
83
|
-
# all .kdl files in a directory
|
|
84
|
-
ssc-gen generate examples/ -
|
|
83
|
+
# all .kdl files in a directory (JavaScript)
|
|
84
|
+
ssc-gen generate examples/ -l js -o ./output
|
|
85
85
|
|
|
86
86
|
# with custom package name
|
|
87
|
-
ssc-gen generate schema.kdl -
|
|
87
|
+
ssc-gen generate schema.kdl -l python -L bs4 -o ./parsers --package scraper
|
|
88
|
+
|
|
89
|
+
# with @request support (REST/HTTP codegen)
|
|
90
|
+
ssc-gen generate schema.kdl -l python -L bs4 -o ./out --http-client httpx
|
|
91
|
+
|
|
92
|
+
# extract helper functions into a separate runtime module
|
|
93
|
+
ssc-gen generate schema.kdl -l python -L bs4 -o ./out -R
|
|
88
94
|
```
|
|
89
95
|
|
|
90
|
-
|
|
96
|
+
Languages (`--lang / -l`): `python`, `js`.
|
|
97
|
+
HTML libraries (`--lib / -L`, Python only): `bs4` (default), `lxml`, `parsel`, `slax`.
|
|
91
98
|
|
|
92
99
|
### Lint schemas
|
|
93
100
|
|
|
@@ -106,10 +113,10 @@ ssc-gen check examples/
|
|
|
106
113
|
|
|
107
114
|
```bash
|
|
108
115
|
# from file
|
|
109
|
-
ssc-gen run examples/booksToScrape.kdl:MainCatalogue -
|
|
116
|
+
ssc-gen run examples/booksToScrape.kdl:MainCatalogue -l python -L bs4 -i page.html
|
|
110
117
|
|
|
111
118
|
# from stdin
|
|
112
|
-
curl https://books.toscrape.com/ | ssc-gen run examples/booksToScrape.kdl:MainCatalogue -
|
|
119
|
+
curl https://books.toscrape.com/ | ssc-gen run examples/booksToScrape.kdl:MainCatalogue -l python -L bs4
|
|
113
120
|
```
|
|
114
121
|
|
|
115
122
|
### Health check (verify selectors match elements)
|
|
@@ -124,27 +131,35 @@ curl https://books.toscrape.com/ | ssc-gen health examples/booksToScrape.kdl:Mai
|
|
|
124
131
|
|
|
125
132
|
## Documentation
|
|
126
133
|
|
|
127
|
-
- [Quick start](
|
|
128
|
-
- [Syntax and file structure](
|
|
129
|
-
- [Type system](
|
|
130
|
-
- [Pipeline operations](
|
|
131
|
-
- [Predicates and logic](
|
|
132
|
-
- [JSON schemas and jsonify](
|
|
133
|
-
- [
|
|
134
|
-
- [LLM-compact reference](
|
|
134
|
+
- [Quick start](docs/guide.md)
|
|
135
|
+
- [Syntax and file structure](docs/syntax.md)
|
|
136
|
+
- [Type system](docs/types.md)
|
|
137
|
+
- [Pipeline operations](docs/operations.md)
|
|
138
|
+
- [Predicates and logic](docs/predicates.md)
|
|
139
|
+
- [JSON schemas and jsonify](docs/json.md)
|
|
140
|
+
- [@request — HTTP constructor + REST clients](docs/learn/10-request.md)
|
|
141
|
+
- [LLM-compact reference](docs/llm.txt) -- full DSL spec in one file for LLM context
|
|
135
142
|
- [Examples](examples/)
|
|
136
143
|
|
|
137
144
|
## LLM integration
|
|
138
145
|
|
|
139
|
-
LLM agents can generate and validate `.kdl` schemas automatically using the
|
|
146
|
+
LLM agents can generate and validate `.kdl` schemas automatically using the
|
|
147
|
+
linter feedback loop.
|
|
140
148
|
|
|
141
149
|
### In chats (ChatGPT, Claude, etc.)
|
|
142
150
|
|
|
143
|
-
Use [SYSTEM_PROMPT.md](SYSTEM_PROMPT.md) as system prompt. After generation,
|
|
151
|
+
Use [SYSTEM_PROMPT.md](SYSTEM_PROMPT.md) as system prompt. After generation,
|
|
152
|
+
run `ssc-gen check -f json` and send errors back to the LLM for correction.
|
|
153
|
+
|
|
154
|
+
### In AI-powered IDEs (Claude Code, Cursor, opencode, etc.)
|
|
144
155
|
|
|
145
|
-
|
|
156
|
+
Use the skills under [.agents/skills/](.agents/skills/):
|
|
146
157
|
|
|
147
|
-
|
|
158
|
+
- **`sscgen-dsl`** — HTML scraping schemas (`css`, `text`, `(item)struct`,
|
|
159
|
+
`(list)struct`, `(table)struct`, predicates, `jsonify`).
|
|
160
|
+
- **`sscgen-rest`** — REST/JSON API clients (`(rest)struct`, `@request`,
|
|
161
|
+
`@error`, typed placeholders).
|
|
162
|
+
- **`sscgen-openapi`** — convert OpenAPI/Swagger specs into `.kdl`.
|
|
148
163
|
|
|
149
164
|
## Development
|
|
150
165
|
|
|
@@ -37,7 +37,7 @@ uv tool install ssc_codegen
|
|
|
37
37
|
Generate Python parser:
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
ssc-gen generate books.kdl -
|
|
40
|
+
ssc-gen generate books.kdl -l python -L bs4 -o ./output
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
## Usage
|
|
@@ -45,17 +45,24 @@ ssc-gen generate books.kdl -t py-bs4 -o ./output
|
|
|
45
45
|
### Generate code
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
# single file
|
|
49
|
-
ssc-gen generate schema.kdl -
|
|
48
|
+
# single file (Python + bs4)
|
|
49
|
+
ssc-gen generate schema.kdl -l python -L bs4 -o ./output
|
|
50
50
|
|
|
51
|
-
# all .kdl files in a directory
|
|
52
|
-
ssc-gen generate examples/ -
|
|
51
|
+
# all .kdl files in a directory (JavaScript)
|
|
52
|
+
ssc-gen generate examples/ -l js -o ./output
|
|
53
53
|
|
|
54
54
|
# with custom package name
|
|
55
|
-
ssc-gen generate schema.kdl -
|
|
55
|
+
ssc-gen generate schema.kdl -l python -L bs4 -o ./parsers --package scraper
|
|
56
|
+
|
|
57
|
+
# with @request support (REST/HTTP codegen)
|
|
58
|
+
ssc-gen generate schema.kdl -l python -L bs4 -o ./out --http-client httpx
|
|
59
|
+
|
|
60
|
+
# extract helper functions into a separate runtime module
|
|
61
|
+
ssc-gen generate schema.kdl -l python -L bs4 -o ./out -R
|
|
56
62
|
```
|
|
57
63
|
|
|
58
|
-
|
|
64
|
+
Languages (`--lang / -l`): `python`, `js`.
|
|
65
|
+
HTML libraries (`--lib / -L`, Python only): `bs4` (default), `lxml`, `parsel`, `slax`.
|
|
59
66
|
|
|
60
67
|
### Lint schemas
|
|
61
68
|
|
|
@@ -74,10 +81,10 @@ ssc-gen check examples/
|
|
|
74
81
|
|
|
75
82
|
```bash
|
|
76
83
|
# from file
|
|
77
|
-
ssc-gen run examples/booksToScrape.kdl:MainCatalogue -
|
|
84
|
+
ssc-gen run examples/booksToScrape.kdl:MainCatalogue -l python -L bs4 -i page.html
|
|
78
85
|
|
|
79
86
|
# from stdin
|
|
80
|
-
curl https://books.toscrape.com/ | ssc-gen run examples/booksToScrape.kdl:MainCatalogue -
|
|
87
|
+
curl https://books.toscrape.com/ | ssc-gen run examples/booksToScrape.kdl:MainCatalogue -l python -L bs4
|
|
81
88
|
```
|
|
82
89
|
|
|
83
90
|
### Health check (verify selectors match elements)
|
|
@@ -92,27 +99,35 @@ curl https://books.toscrape.com/ | ssc-gen health examples/booksToScrape.kdl:Mai
|
|
|
92
99
|
|
|
93
100
|
## Documentation
|
|
94
101
|
|
|
95
|
-
- [Quick start](
|
|
96
|
-
- [Syntax and file structure](
|
|
97
|
-
- [Type system](
|
|
98
|
-
- [Pipeline operations](
|
|
99
|
-
- [Predicates and logic](
|
|
100
|
-
- [JSON schemas and jsonify](
|
|
101
|
-
- [
|
|
102
|
-
- [LLM-compact reference](
|
|
102
|
+
- [Quick start](docs/guide.md)
|
|
103
|
+
- [Syntax and file structure](docs/syntax.md)
|
|
104
|
+
- [Type system](docs/types.md)
|
|
105
|
+
- [Pipeline operations](docs/operations.md)
|
|
106
|
+
- [Predicates and logic](docs/predicates.md)
|
|
107
|
+
- [JSON schemas and jsonify](docs/json.md)
|
|
108
|
+
- [@request — HTTP constructor + REST clients](docs/learn/10-request.md)
|
|
109
|
+
- [LLM-compact reference](docs/llm.txt) -- full DSL spec in one file for LLM context
|
|
103
110
|
- [Examples](examples/)
|
|
104
111
|
|
|
105
112
|
## LLM integration
|
|
106
113
|
|
|
107
|
-
LLM agents can generate and validate `.kdl` schemas automatically using the
|
|
114
|
+
LLM agents can generate and validate `.kdl` schemas automatically using the
|
|
115
|
+
linter feedback loop.
|
|
108
116
|
|
|
109
117
|
### In chats (ChatGPT, Claude, etc.)
|
|
110
118
|
|
|
111
|
-
Use [SYSTEM_PROMPT.md](SYSTEM_PROMPT.md) as system prompt. After generation,
|
|
119
|
+
Use [SYSTEM_PROMPT.md](SYSTEM_PROMPT.md) as system prompt. After generation,
|
|
120
|
+
run `ssc-gen check -f json` and send errors back to the LLM for correction.
|
|
121
|
+
|
|
122
|
+
### In AI-powered IDEs (Claude Code, Cursor, opencode, etc.)
|
|
112
123
|
|
|
113
|
-
|
|
124
|
+
Use the skills under [.agents/skills/](.agents/skills/):
|
|
114
125
|
|
|
115
|
-
|
|
126
|
+
- **`sscgen-dsl`** — HTML scraping schemas (`css`, `text`, `(item)struct`,
|
|
127
|
+
`(list)struct`, `(table)struct`, predicates, `jsonify`).
|
|
128
|
+
- **`sscgen-rest`** — REST/JSON API clients (`(rest)struct`, `@request`,
|
|
129
|
+
`@error`, typed placeholders).
|
|
130
|
+
- **`sscgen-openapi`** — convert OpenAPI/Swagger specs into `.kdl`.
|
|
116
131
|
|
|
117
132
|
## Development
|
|
118
133
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ssc_codegen"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.32.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"
|
|
@@ -12,8 +12,8 @@ dependencies = [
|
|
|
12
12
|
"soupsieve>=2.6",
|
|
13
13
|
"typer>0.26",
|
|
14
14
|
"typing_extensions;python_version<'3.11'",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
15
|
+
"kdlquery>=1.3.0",
|
|
16
|
+
"httpx>=0.28.1",
|
|
17
17
|
]
|
|
18
18
|
|
|
19
19
|
classifiers = [
|
|
@@ -54,6 +54,8 @@ packages = ["ssc_codegen"]
|
|
|
54
54
|
|
|
55
55
|
[dependency-groups]
|
|
56
56
|
dev = [
|
|
57
|
+
"aiohttp>=3.9,<3.12", # aioresponses 0.7.x breaks on 3.12+ (stream_writer)
|
|
58
|
+
"aioresponses>=0.7",
|
|
57
59
|
"bs4>=0.0.2",
|
|
58
60
|
"coverage>=7.6.12",
|
|
59
61
|
"httpx>=0.28.1",
|
|
@@ -63,6 +65,7 @@ dev = [
|
|
|
63
65
|
"parsel>=1.9.1",
|
|
64
66
|
"pytest>=8.3.4",
|
|
65
67
|
"respx>=0.23.1",
|
|
68
|
+
"responses>=0.25",
|
|
66
69
|
"ruff>=0.9.3",
|
|
67
70
|
"selectolax>=0.3.27",
|
|
68
71
|
]
|
|
@@ -88,6 +91,11 @@ disallow_untyped_defs = false
|
|
|
88
91
|
module = ["colorama", "lxml", "lxml.*"]
|
|
89
92
|
ignore_missing_imports = true
|
|
90
93
|
|
|
94
|
+
[[tool.mypy.overrides]]
|
|
95
|
+
# Large visitor classes with match statements may need relaxed return checks
|
|
96
|
+
module = ["ssc_codegen.targets.python.visitor", "ssc_codegen.targets.javascript.visitor"]
|
|
97
|
+
disable_error_code = ["return", "return-value"]
|
|
98
|
+
|
|
91
99
|
|
|
92
100
|
[tool.coverage.report]
|
|
93
101
|
exclude_also = [
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# mypy: ignore-errors
|
|
2
|
+
"""
|
|
3
|
+
AST nodes for the KDL Schema DSL.
|
|
4
|
+
|
|
5
|
+
Import everything from here:
|
|
6
|
+
from kdl_ast import Module, Field, CssSelect, ...
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .types import VariableType, StructType, TypeInfo, VT
|
|
10
|
+
|
|
11
|
+
from kdlquery.parser import Span
|
|
12
|
+
|
|
13
|
+
from .base import Node
|
|
14
|
+
|
|
15
|
+
from .module import (
|
|
16
|
+
Module,
|
|
17
|
+
CodeStartHook,
|
|
18
|
+
CodeEndHook,
|
|
19
|
+
Docstring,
|
|
20
|
+
Utilities,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from .typedef import TypeDef, TypeDefField
|
|
24
|
+
|
|
25
|
+
from .jsondef import JsonDef, JsonDefField
|
|
26
|
+
|
|
27
|
+
from .rest import (
|
|
28
|
+
ResultVariantDef,
|
|
29
|
+
ResultAliasDef,
|
|
30
|
+
MatcherEntry,
|
|
31
|
+
MatcherListDef,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
from .struct import (
|
|
35
|
+
StructBase,
|
|
36
|
+
Struct,
|
|
37
|
+
StructRest,
|
|
38
|
+
StructDocstring,
|
|
39
|
+
PreValidate,
|
|
40
|
+
CheckMethod,
|
|
41
|
+
Init,
|
|
42
|
+
InitFieldCall,
|
|
43
|
+
InitField,
|
|
44
|
+
SplitDoc,
|
|
45
|
+
Key,
|
|
46
|
+
Value,
|
|
47
|
+
TableConfig,
|
|
48
|
+
TableRows,
|
|
49
|
+
TableMatchKey,
|
|
50
|
+
RequestHttp,
|
|
51
|
+
MethodBase,
|
|
52
|
+
MethodFetch,
|
|
53
|
+
MethodRest,
|
|
54
|
+
ErrorResponse,
|
|
55
|
+
PlaceholderSpec,
|
|
56
|
+
PlaceholderTemplate,
|
|
57
|
+
Field,
|
|
58
|
+
StartParse,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
from .selectors import (
|
|
62
|
+
CssSelect,
|
|
63
|
+
CssSelectAll,
|
|
64
|
+
XpathSelect,
|
|
65
|
+
XpathSelectAll,
|
|
66
|
+
CssRemove,
|
|
67
|
+
XpathRemove,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
from .extract import Text, Raw, Attr
|
|
71
|
+
|
|
72
|
+
from .string import (
|
|
73
|
+
Trim,
|
|
74
|
+
Ltrim,
|
|
75
|
+
Rtrim,
|
|
76
|
+
NormalizeSpace,
|
|
77
|
+
RmPrefix,
|
|
78
|
+
RmSuffix,
|
|
79
|
+
RmPrefixSuffix,
|
|
80
|
+
Fmt,
|
|
81
|
+
Repl,
|
|
82
|
+
ReplMap,
|
|
83
|
+
Lower,
|
|
84
|
+
Upper,
|
|
85
|
+
Split,
|
|
86
|
+
Join,
|
|
87
|
+
Unescape,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
from .regex import Re, ReAll, ReSub
|
|
91
|
+
|
|
92
|
+
from .array import Index, Slice, Len, Unique
|
|
93
|
+
|
|
94
|
+
from .cast import ToInt, ToFloat, ToBool, Jsonify, Nested
|
|
95
|
+
|
|
96
|
+
from .control import Self, Fallback, Return
|
|
97
|
+
|
|
98
|
+
from .predicate_containers import Filter, Assert, Match
|
|
99
|
+
|
|
100
|
+
from .predicate_ops import (
|
|
101
|
+
PredEq,
|
|
102
|
+
PredNe,
|
|
103
|
+
PredStarts,
|
|
104
|
+
PredEnds,
|
|
105
|
+
PredContains,
|
|
106
|
+
PredRe,
|
|
107
|
+
PredReAny,
|
|
108
|
+
PredReAll,
|
|
109
|
+
PredCss,
|
|
110
|
+
PredXpath,
|
|
111
|
+
PredHasAttr,
|
|
112
|
+
PredCountEq,
|
|
113
|
+
PredCountGt,
|
|
114
|
+
PredCountLt,
|
|
115
|
+
PredCountNe,
|
|
116
|
+
PredCountGe,
|
|
117
|
+
PredCountLe,
|
|
118
|
+
PredCountRange,
|
|
119
|
+
PredAttrEnds,
|
|
120
|
+
PredAttrEq,
|
|
121
|
+
PredAttrNe,
|
|
122
|
+
PredAttrRe,
|
|
123
|
+
PredAttrStarts,
|
|
124
|
+
PredAttrContains,
|
|
125
|
+
PredTextContains,
|
|
126
|
+
PredTextEnds,
|
|
127
|
+
PredTextRe,
|
|
128
|
+
PredTextStarts,
|
|
129
|
+
LogicNot,
|
|
130
|
+
LogicAnd,
|
|
131
|
+
LogicOr,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
__all__ = [
|
|
135
|
+
# types
|
|
136
|
+
"VariableType",
|
|
137
|
+
"StructType",
|
|
138
|
+
"TypeInfo",
|
|
139
|
+
"VT",
|
|
140
|
+
# source location
|
|
141
|
+
"Span",
|
|
142
|
+
# base
|
|
143
|
+
"Node",
|
|
144
|
+
# module
|
|
145
|
+
"Module",
|
|
146
|
+
"CodeStartHook",
|
|
147
|
+
"CodeEndHook",
|
|
148
|
+
"Docstring",
|
|
149
|
+
"Utilities",
|
|
150
|
+
# typedef
|
|
151
|
+
"TypeDef",
|
|
152
|
+
"TypeDefField",
|
|
153
|
+
# jsondef
|
|
154
|
+
"JsonDef",
|
|
155
|
+
"JsonDefField",
|
|
156
|
+
# struct
|
|
157
|
+
"StructBase",
|
|
158
|
+
"Struct",
|
|
159
|
+
"StructRest",
|
|
160
|
+
"StructDocstring",
|
|
161
|
+
"PreValidate",
|
|
162
|
+
"CheckMethod",
|
|
163
|
+
"Init",
|
|
164
|
+
"InitFieldCall",
|
|
165
|
+
"InitField",
|
|
166
|
+
"SplitDoc",
|
|
167
|
+
"Key",
|
|
168
|
+
"Value",
|
|
169
|
+
"TableConfig",
|
|
170
|
+
"TableRows",
|
|
171
|
+
"TableMatchKey",
|
|
172
|
+
"RequestHttp",
|
|
173
|
+
"MethodBase",
|
|
174
|
+
"MethodFetch",
|
|
175
|
+
"MethodRest",
|
|
176
|
+
"ErrorResponse",
|
|
177
|
+
"PlaceholderSpec",
|
|
178
|
+
"PlaceholderTemplate",
|
|
179
|
+
"Field",
|
|
180
|
+
"StartParse",
|
|
181
|
+
# REST result artifacts (synthesized)
|
|
182
|
+
"ResultVariantDef",
|
|
183
|
+
"ResultAliasDef",
|
|
184
|
+
"MatcherEntry",
|
|
185
|
+
"MatcherListDef",
|
|
186
|
+
# selectors
|
|
187
|
+
"CssSelect",
|
|
188
|
+
"CssSelectAll",
|
|
189
|
+
"XpathSelect",
|
|
190
|
+
"XpathSelectAll",
|
|
191
|
+
"CssRemove",
|
|
192
|
+
"XpathRemove",
|
|
193
|
+
# extract
|
|
194
|
+
"Text",
|
|
195
|
+
"Raw",
|
|
196
|
+
"Attr",
|
|
197
|
+
# string
|
|
198
|
+
"Trim",
|
|
199
|
+
"Ltrim",
|
|
200
|
+
"Rtrim",
|
|
201
|
+
"NormalizeSpace",
|
|
202
|
+
"RmPrefix",
|
|
203
|
+
"RmSuffix",
|
|
204
|
+
"RmPrefixSuffix",
|
|
205
|
+
"Fmt",
|
|
206
|
+
"Repl",
|
|
207
|
+
"ReplMap",
|
|
208
|
+
"Lower",
|
|
209
|
+
"Upper",
|
|
210
|
+
"Split",
|
|
211
|
+
"Join",
|
|
212
|
+
"Unescape",
|
|
213
|
+
# regex
|
|
214
|
+
"Re",
|
|
215
|
+
"ReAll",
|
|
216
|
+
"ReSub",
|
|
217
|
+
# array
|
|
218
|
+
"Index",
|
|
219
|
+
"Slice",
|
|
220
|
+
"Len",
|
|
221
|
+
"Unique",
|
|
222
|
+
# cast
|
|
223
|
+
"ToInt",
|
|
224
|
+
"ToFloat",
|
|
225
|
+
"ToBool",
|
|
226
|
+
"Jsonify",
|
|
227
|
+
"Nested",
|
|
228
|
+
# control
|
|
229
|
+
"Self",
|
|
230
|
+
"Fallback",
|
|
231
|
+
"Return",
|
|
232
|
+
# predicate containers
|
|
233
|
+
"Filter",
|
|
234
|
+
"Assert",
|
|
235
|
+
"Match",
|
|
236
|
+
# predicate ops
|
|
237
|
+
"PredEq",
|
|
238
|
+
"PredNe",
|
|
239
|
+
"PredStarts",
|
|
240
|
+
"PredEnds",
|
|
241
|
+
"PredContains",
|
|
242
|
+
"PredRe",
|
|
243
|
+
"PredReAny",
|
|
244
|
+
"PredReAll",
|
|
245
|
+
"PredCss",
|
|
246
|
+
"PredXpath",
|
|
247
|
+
"PredHasAttr",
|
|
248
|
+
"PredAttrEq",
|
|
249
|
+
"PredAttrNe",
|
|
250
|
+
"PredAttrStarts",
|
|
251
|
+
"PredAttrEnds",
|
|
252
|
+
"PredAttrContains",
|
|
253
|
+
"PredAttrRe",
|
|
254
|
+
"PredTextStarts",
|
|
255
|
+
"PredTextEnds",
|
|
256
|
+
"PredTextContains",
|
|
257
|
+
"PredTextRe",
|
|
258
|
+
"PredCountEq",
|
|
259
|
+
"PredCountGt",
|
|
260
|
+
"PredCountLt",
|
|
261
|
+
"PredCountNe",
|
|
262
|
+
"PredCountGe",
|
|
263
|
+
"PredCountLe",
|
|
264
|
+
"PredCountRange",
|
|
265
|
+
"LogicNot",
|
|
266
|
+
"LogicAnd",
|
|
267
|
+
"LogicOr",
|
|
268
|
+
]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
|
|
4
|
+
from .base import Node
|
|
5
|
+
from .types import TypeInfo, VariableType
|
|
6
|
+
|
|
7
|
+
# Index, First, Last, Slice accept LIST_AUTO / return AUTO or LIST_AUTO.
|
|
8
|
+
# Concrete types are resolved by the builder from the cursor type via
|
|
9
|
+
# VariableType.scalar / VariableType.as_list helpers.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class Index(Node):
|
|
14
|
+
"""
|
|
15
|
+
Returns element at position i.
|
|
16
|
+
Negative index counts from end.
|
|
17
|
+
Accepts list, returns scalar.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
i: int = 0
|
|
21
|
+
accept_type_info: TypeInfo = field(
|
|
22
|
+
default_factory=lambda: TypeInfo(base=VariableType.AUTO)
|
|
23
|
+
)
|
|
24
|
+
ret_type_info: TypeInfo = field(
|
|
25
|
+
default_factory=lambda: TypeInfo(base=VariableType.AUTO)
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class Slice(Node):
|
|
31
|
+
"""
|
|
32
|
+
Returns sublist [start:end].
|
|
33
|
+
Accepts list, returns list.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
start: int = 0
|
|
37
|
+
end: int = 0
|
|
38
|
+
accept_type_info: TypeInfo = field(
|
|
39
|
+
default_factory=lambda: TypeInfo(base=VariableType.AUTO)
|
|
40
|
+
)
|
|
41
|
+
ret_type_info: TypeInfo = field(
|
|
42
|
+
default_factory=lambda: TypeInfo(base=VariableType.AUTO, is_array=True)
|
|
43
|
+
)
|
|
44
|
+
is_array: bool = True
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class Len(Node):
|
|
49
|
+
"""Returns list length as INT."""
|
|
50
|
+
|
|
51
|
+
accept_type_info: TypeInfo = field(
|
|
52
|
+
default_factory=lambda: TypeInfo(base=VariableType.AUTO)
|
|
53
|
+
)
|
|
54
|
+
ret_type_info: TypeInfo = field(
|
|
55
|
+
default_factory=lambda: TypeInfo(base=VariableType.INT)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass
|
|
60
|
+
class Unique(Node):
|
|
61
|
+
"""
|
|
62
|
+
Removes duplicate strings from list.
|
|
63
|
+
keep_order=True — preserves original order (default: False).
|
|
64
|
+
STRING → STRING with is_array=True.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
keep_order: bool = False
|
|
68
|
+
accept_type_info: TypeInfo = field(
|
|
69
|
+
default_factory=lambda: TypeInfo(base=VariableType.STRING)
|
|
70
|
+
)
|
|
71
|
+
ret_type_info: TypeInfo = field(
|
|
72
|
+
default_factory=lambda: TypeInfo(
|
|
73
|
+
base=VariableType.STRING, is_array=True
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
is_array: bool = True
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
|
|
4
|
+
from kdlquery.parser import Span
|
|
5
|
+
|
|
6
|
+
from .types import TypeInfo, VariableType
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class Node:
|
|
11
|
+
"""
|
|
12
|
+
Base AST node.
|
|
13
|
+
|
|
14
|
+
Canonical type metadata:
|
|
15
|
+
ret_type_info — resolved type of the value this node RETURNS.
|
|
16
|
+
accept_type_info — resolved type this node ACCEPTS as input.
|
|
17
|
+
is_array — list context flag for pipeline expression nodes
|
|
18
|
+
(kept in sync with ret_type_info.is_array by the builder).
|
|
19
|
+
parent — back-reference, excluded from repr to avoid cycles.
|
|
20
|
+
body — child nodes (pipeline body, struct body, etc.).
|
|
21
|
+
span — optional source location carried from KdlNode.span.
|
|
22
|
+
Populated lazily by parsers that need position info
|
|
23
|
+
in codegen (Assert, Re). None for nodes that never
|
|
24
|
+
use source location.
|
|
25
|
+
|
|
26
|
+
The scalar ``accept`` / ``ret`` views and the legacy ``type_info`` name are
|
|
27
|
+
exposed as read-only backport properties derived from the TypeInfo fields.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
ret_type_info: TypeInfo = field(
|
|
31
|
+
default_factory=lambda: TypeInfo(base=VariableType.AUTO)
|
|
32
|
+
)
|
|
33
|
+
accept_type_info: TypeInfo = field(
|
|
34
|
+
default_factory=lambda: TypeInfo(base=VariableType.AUTO)
|
|
35
|
+
)
|
|
36
|
+
is_array: bool = False
|
|
37
|
+
parent: Node | None = field(default=None, repr=False)
|
|
38
|
+
body: list[Node] = field(default_factory=list)
|
|
39
|
+
span: Span | None = field(default=None, repr=False, compare=False)
|
|
40
|
+
|
|
41
|
+
# ── backport properties (deprecated: prefer the TypeInfo fields) ───────────
|
|
42
|
+
@property
|
|
43
|
+
def ret(self) -> VariableType:
|
|
44
|
+
return self.ret_type_info.base
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def accept(self) -> VariableType:
|
|
48
|
+
return self.accept_type_info.base
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def type_info(self) -> TypeInfo:
|
|
52
|
+
return self.ret_type_info
|