ssc_codegen 0.31.0__tar.gz → 0.32.1__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.
Files changed (122) hide show
  1. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/.gitignore +5 -0
  2. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/PKG-INFO +39 -24
  3. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/README.md +36 -21
  4. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/pyproject.toml +11 -3
  5. ssc_codegen-0.32.1/ssc_codegen/ast/__init__.py +268 -0
  6. ssc_codegen-0.32.1/ssc_codegen/ast/array.py +76 -0
  7. ssc_codegen-0.32.1/ssc_codegen/ast/base.py +52 -0
  8. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/ast/cast.py +31 -11
  9. ssc_codegen-0.32.1/ssc_codegen/ast/control.py +94 -0
  10. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/ast/extract.py +19 -7
  11. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/ast/helpers.py +0 -3
  12. ssc_codegen-0.32.1/ssc_codegen/ast/jsondef.py +31 -0
  13. ssc_codegen-0.32.1/ssc_codegen/ast/module.py +114 -0
  14. ssc_codegen-0.32.1/ssc_codegen/ast/predicate_containers.py +65 -0
  15. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/ast/predicate_ops.py +0 -43
  16. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/ast/regex.py +23 -8
  17. ssc_codegen-0.32.1/ssc_codegen/ast/rest.py +82 -0
  18. ssc_codegen-0.32.1/ssc_codegen/ast/selectors.py +97 -0
  19. ssc_codegen-0.32.1/ssc_codegen/ast/string.py +221 -0
  20. ssc_codegen-0.32.1/ssc_codegen/ast/struct.py +760 -0
  21. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/ast/typedef.py +3 -12
  22. ssc_codegen-0.32.1/ssc_codegen/ast/types.py +51 -0
  23. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/core/contexts.py +2 -44
  24. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/core/expressions.py +429 -216
  25. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/core/format.py +0 -11
  26. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/core/linter.py +1504 -1491
  27. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/core/module_handler.py +28 -80
  28. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/core/predicates.py +68 -132
  29. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/core/reader.py +114 -116
  30. ssc_codegen-0.32.1/ssc_codegen/core/rest_artifacts.py +138 -0
  31. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/core/struct_parser.py +79 -47
  32. ssc_codegen-0.32.1/ssc_codegen/core/type_checking.py +379 -0
  33. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/exceptions.py +0 -5
  34. ssc_codegen-0.32.1/ssc_codegen/explore.py +1540 -0
  35. ssc_codegen-0.32.1/ssc_codegen/generation/__init__.py +0 -0
  36. ssc_codegen-0.32.1/ssc_codegen/generation/builder.py +73 -0
  37. ssc_codegen-0.32.1/ssc_codegen/generation/runtime.py +161 -0
  38. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/health.py +10 -8
  39. ssc_codegen-0.32.1/ssc_codegen/main.py +1110 -0
  40. ssc_codegen-0.31.0/ssc_codegen/converters/helpers.py → ssc_codegen-0.32.1/ssc_codegen/naming.py +8 -33
  41. ssc_codegen-0.32.1/ssc_codegen/request_spec.py +179 -0
  42. ssc_codegen-0.32.1/ssc_codegen/targets/__init__.py +0 -0
  43. ssc_codegen-0.32.1/ssc_codegen/targets/golang/__init__.py +5 -0
  44. ssc_codegen-0.32.1/ssc_codegen/targets/golang/http_libs/__init__.py +0 -0
  45. ssc_codegen-0.32.1/ssc_codegen/targets/golang/http_libs/base.py +27 -0
  46. ssc_codegen-0.32.1/ssc_codegen/targets/golang/http_libs/nethttp.py +64 -0
  47. ssc_codegen-0.32.1/ssc_codegen/targets/golang/literals.py +48 -0
  48. ssc_codegen-0.32.1/ssc_codegen/targets/golang/regex.py +26 -0
  49. ssc_codegen-0.32.1/ssc_codegen/targets/golang/rest.py +496 -0
  50. ssc_codegen-0.32.1/ssc_codegen/targets/golang/runtime.py +532 -0
  51. ssc_codegen-0.32.1/ssc_codegen/targets/golang/visitor.py +1837 -0
  52. ssc_codegen-0.32.1/ssc_codegen/targets/javascript/__init__.py +5 -0
  53. ssc_codegen-0.32.1/ssc_codegen/targets/javascript/http_libs/__init__.py +0 -0
  54. ssc_codegen-0.32.1/ssc_codegen/targets/javascript/http_libs/axios.py +34 -0
  55. ssc_codegen-0.32.1/ssc_codegen/targets/javascript/http_libs/base.py +19 -0
  56. ssc_codegen-0.32.1/ssc_codegen/targets/javascript/http_libs/fetch.py +31 -0
  57. ssc_codegen-0.32.1/ssc_codegen/targets/javascript/rest.py +529 -0
  58. ssc_codegen-0.32.1/ssc_codegen/targets/javascript/visitor.py +1584 -0
  59. ssc_codegen-0.32.1/ssc_codegen/targets/profile.py +18 -0
  60. ssc_codegen-0.32.1/ssc_codegen/targets/python/__init__.py +12 -0
  61. ssc_codegen-0.32.1/ssc_codegen/targets/python/html_libs/__init__.py +0 -0
  62. ssc_codegen-0.32.1/ssc_codegen/targets/python/html_libs/base.py +146 -0
  63. ssc_codegen-0.32.1/ssc_codegen/targets/python/html_libs/bs4.py +218 -0
  64. ssc_codegen-0.32.1/ssc_codegen/targets/python/html_libs/lxml.py +277 -0
  65. ssc_codegen-0.32.1/ssc_codegen/targets/python/html_libs/parsel.py +269 -0
  66. ssc_codegen-0.32.1/ssc_codegen/targets/python/html_libs/slax.py +222 -0
  67. ssc_codegen-0.32.1/ssc_codegen/targets/python/http_libs/__init__.py +0 -0
  68. ssc_codegen-0.32.1/ssc_codegen/targets/python/http_libs/aiohttp.py +179 -0
  69. ssc_codegen-0.32.1/ssc_codegen/targets/python/http_libs/base.py +82 -0
  70. ssc_codegen-0.32.1/ssc_codegen/targets/python/http_libs/httpx.py +130 -0
  71. ssc_codegen-0.32.1/ssc_codegen/targets/python/http_libs/requests.py +130 -0
  72. ssc_codegen-0.32.1/ssc_codegen/targets/python/rest.py +508 -0
  73. ssc_codegen-0.32.1/ssc_codegen/targets/python/visitor.py +1366 -0
  74. ssc_codegen-0.32.1/ssc_codegen/targets/resolver.py +121 -0
  75. ssc_codegen-0.32.1/ssc_codegen/targets/spec.py +15 -0
  76. ssc_codegen-0.32.1/ssc_codegen/traversal/__init__.py +0 -0
  77. ssc_codegen-0.32.1/ssc_codegen/traversal/context.py +47 -0
  78. ssc_codegen-0.32.1/ssc_codegen/traversal/utils.py +101 -0
  79. ssc_codegen-0.32.1/ssc_codegen/traversal/walker.py +335 -0
  80. ssc_codegen-0.31.0/ssc_codegen/ast/__init__.py +0 -193
  81. ssc_codegen-0.31.0/ssc_codegen/ast/array.py +0 -56
  82. ssc_codegen-0.31.0/ssc_codegen/ast/base.py +0 -24
  83. ssc_codegen-0.31.0/ssc_codegen/ast/control.py +0 -82
  84. ssc_codegen-0.31.0/ssc_codegen/ast/jsondef.py +0 -43
  85. ssc_codegen-0.31.0/ssc_codegen/ast/module.py +0 -108
  86. ssc_codegen-0.31.0/ssc_codegen/ast/predicate_containers.py +0 -44
  87. ssc_codegen-0.31.0/ssc_codegen/ast/selectors.py +0 -55
  88. ssc_codegen-0.31.0/ssc_codegen/ast/string.py +0 -158
  89. ssc_codegen-0.31.0/ssc_codegen/ast/struct.py +0 -509
  90. ssc_codegen-0.31.0/ssc_codegen/ast/transform.py +0 -63
  91. ssc_codegen-0.31.0/ssc_codegen/ast/types.py +0 -80
  92. ssc_codegen-0.31.0/ssc_codegen/converters/base.py +0 -333
  93. ssc_codegen-0.31.0/ssc_codegen/converters/js_pure.py +0 -2085
  94. ssc_codegen-0.31.0/ssc_codegen/converters/py_bs4.py +0 -1693
  95. ssc_codegen-0.31.0/ssc_codegen/converters/py_lxml.py +0 -515
  96. ssc_codegen-0.31.0/ssc_codegen/converters/py_parsel.py +0 -473
  97. ssc_codegen-0.31.0/ssc_codegen/converters/py_render.py +0 -231
  98. ssc_codegen-0.31.0/ssc_codegen/converters/py_slax.py +0 -421
  99. ssc_codegen-0.31.0/ssc_codegen/converters/runtime/__init__.py +0 -7
  100. ssc_codegen-0.31.0/ssc_codegen/converters/runtime/_helpers.py +0 -35
  101. ssc_codegen-0.31.0/ssc_codegen/converters/runtime/js_base.py +0 -24
  102. ssc_codegen-0.31.0/ssc_codegen/converters/runtime/py_base.py +0 -67
  103. ssc_codegen-0.31.0/ssc_codegen/converters/runtime/py_lxml.py +0 -7
  104. ssc_codegen-0.31.0/ssc_codegen/converters/runtime/py_rest.py +0 -133
  105. ssc_codegen-0.31.0/ssc_codegen/core/type_checking.py +0 -401
  106. ssc_codegen-0.31.0/ssc_codegen/document_utils.py +0 -114
  107. ssc_codegen-0.31.0/ssc_codegen/kdl/__init__.py +0 -61
  108. ssc_codegen-0.31.0/ssc_codegen/kdl/dict_reader.py +0 -56
  109. ssc_codegen-0.31.0/ssc_codegen/kdl/parser.py +0 -1368
  110. ssc_codegen-0.31.0/ssc_codegen/kdl/reader.py +0 -429
  111. ssc_codegen-0.31.0/ssc_codegen/main.py +0 -739
  112. ssc_codegen-0.31.0/ssc_codegen/pseudo_selectors.py +0 -39
  113. ssc_codegen-0.31.0/ssc_codegen/request_spec.py +0 -296
  114. ssc_codegen-0.31.0/ssc_codegen/selector_utils.py +0 -8
  115. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/LICENSE +0 -0
  116. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/__init__.py +0 -0
  117. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/_logging.py +0 -0
  118. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/core/__init__.py +0 -0
  119. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/parsers/__init__.py +0 -0
  120. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/parsers/curl.py +0 -0
  121. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/parsers/http.py +0 -0
  122. {ssc_codegen-0.31.0 → ssc_codegen-0.32.1}/ssc_codegen/regex_utils.py +0 -0
@@ -150,5 +150,10 @@ test_schemas/
150
150
  node_modules/
151
151
  package-lock.json
152
152
 
153
+ # Go (test/research scratch)
154
+ go.work
155
+ go.work.sum
156
+ /tests/go/scratch/
157
+
153
158
  # slopmachines
154
159
  .claude/*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssc_codegen
3
- Version: 0.31.0
3
+ Version: 0.32.1
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: kdlquery>=1.1.2
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 -t py-bs4 -o ./output
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 -t py-bs4 -o ./output
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/ -t js-pure -o ./output
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 -t py-bs4 -o ./parsers --package scraper
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
- Targets: `py-bs4`, `py-lxml`, `py-parsel`, `py-slax`, `js-pure`
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 -t py-bs4 -i page.html
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 -t py-bs4
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](docs2/guide.md)
128
- - [Syntax and file structure](docs2/syntax.md)
129
- - [Type system](docs2/types.md)
130
- - [Pipeline operations](docs2/operations.md)
131
- - [Predicates and logic](docs2/predicates.md)
132
- - [JSON schemas and jsonify](docs2/json.md)
133
- - [Transforms and dsl blocks](docs2/transforms.md)
134
- - [LLM-compact reference](docs2/llm.txt) -- full DSL spec in one file for LLM context
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 linter feedback loop.
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, run `ssc-gen check -f json` and send errors back to the LLM for correction.
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
- ### In AI-powered IDEs (Claude Code, Cursor, etc.)
156
+ Use the skills under [.agents/skills/](.agents/skills/):
146
157
 
147
- Use the [kdl-schema-dsl](.agents/skills/kdl-schema-dsl) skill for automatic generation, validation, and iteration.
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 -t py-bs4 -o ./output
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 -t py-bs4 -o ./output
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/ -t js-pure -o ./output
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 -t py-bs4 -o ./parsers --package scraper
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
- Targets: `py-bs4`, `py-lxml`, `py-parsel`, `py-slax`, `js-pure`
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 -t py-bs4 -i page.html
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 -t py-bs4
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](docs2/guide.md)
96
- - [Syntax and file structure](docs2/syntax.md)
97
- - [Type system](docs2/types.md)
98
- - [Pipeline operations](docs2/operations.md)
99
- - [Predicates and logic](docs2/predicates.md)
100
- - [JSON schemas and jsonify](docs2/json.md)
101
- - [Transforms and dsl blocks](docs2/transforms.md)
102
- - [LLM-compact reference](docs2/llm.txt) -- full DSL spec in one file for LLM context
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 linter feedback loop.
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, run `ssc-gen check -f json` and send errors back to the LLM for correction.
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
- ### In AI-powered IDEs (Claude Code, Cursor, etc.)
124
+ Use the skills under [.agents/skills/](.agents/skills/):
114
125
 
115
- Use the [kdl-schema-dsl](.agents/skills/kdl-schema-dsl) skill for automatic generation, validation, and iteration.
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.31.0"
3
+ version = "0.32.1"
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
- "pyyaml>=6.0",
16
- "kdlquery>=1.1.2",
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