selectolax-schema 0.1.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.
Files changed (26) hide show
  1. selectolax_schema-0.1.0/LICENSE +22 -0
  2. selectolax_schema-0.1.0/PKG-INFO +299 -0
  3. selectolax_schema-0.1.0/README.md +283 -0
  4. selectolax_schema-0.1.0/pyproject.toml +37 -0
  5. selectolax_schema-0.1.0/setup.cfg +4 -0
  6. selectolax_schema-0.1.0/src/selectolax_schema/__init__.py +54 -0
  7. selectolax_schema-0.1.0/src/selectolax_schema/cli.py +75 -0
  8. selectolax_schema-0.1.0/src/selectolax_schema/core.py +986 -0
  9. selectolax_schema-0.1.0/src/selectolax_schema/types.py +173 -0
  10. selectolax_schema-0.1.0/src/selectolax_schema.egg-info/PKG-INFO +299 -0
  11. selectolax_schema-0.1.0/src/selectolax_schema.egg-info/SOURCES.txt +24 -0
  12. selectolax_schema-0.1.0/src/selectolax_schema.egg-info/dependency_links.txt +1 -0
  13. selectolax_schema-0.1.0/src/selectolax_schema.egg-info/entry_points.txt +2 -0
  14. selectolax_schema-0.1.0/src/selectolax_schema.egg-info/requires.txt +5 -0
  15. selectolax_schema-0.1.0/src/selectolax_schema.egg-info/top_level.txt +1 -0
  16. selectolax_schema-0.1.0/tests/test_callback_transform_examples.py +85 -0
  17. selectolax_schema-0.1.0/tests/test_changelog_script.py +136 -0
  18. selectolax_schema-0.1.0/tests/test_extractor.py +250 -0
  19. selectolax_schema-0.1.0/tests/test_json_schema.py +57 -0
  20. selectolax_schema-0.1.0/tests/test_more_scenarios.py +229 -0
  21. selectolax_schema-0.1.0/tests/test_output_schema_override.py +64 -0
  22. selectolax_schema-0.1.0/tests/test_release_scripts.py +40 -0
  23. selectolax_schema-0.1.0/tests/test_selectolax_backend.py +93 -0
  24. selectolax_schema-0.1.0/tests/test_spec_validation.py +195 -0
  25. selectolax_schema-0.1.0/tests/test_typed_schema.py +198 -0
  26. selectolax_schema-0.1.0/tests/test_version.py +22 -0
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 aimscrape
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,299 @@
1
+ Metadata-Version: 2.4
2
+ Name: selectolax-schema
3
+ Version: 0.1.0
4
+ Summary: Schema-driven HTML extraction powered by selectolax.
5
+ License-Expression: MIT
6
+ Project-URL: Repository, https://github.com/aimscrape/selectolax-schema
7
+ Project-URL: Issues, https://github.com/aimscrape/selectolax-schema/issues
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: selectolax>=0.3.0
12
+ Requires-Dist: PyYAML>=6.0.0
13
+ Provides-Extra: dev
14
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
15
+ Dynamic: license-file
16
+
17
+ # selectolax-schema
18
+
19
+ Schema-driven HTML extractor powered by selectolax's maintained Lexbor backend.
20
+ Define extraction specs in Python (with full IDE type hints) or YAML, and get
21
+ structured JSON out.
22
+
23
+ The field model and observable API mirror `scrapling-schema` 1.1.3; the Python
24
+ package and CLI are named `selectolax_schema` and `selectolax-schema`.
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ pip install selectolax-schema
30
+ ```
31
+
32
+ ## Requirements
33
+
34
+ - Python >= 3.10
35
+ - [selectolax](https://github.com/rushter/selectolax) >= 0.3.0
36
+ - PyYAML >= 6.0
37
+
38
+ ## Python API
39
+
40
+ ### Python type spec (recommended)
41
+
42
+ ```python
43
+ from selectolax_schema import Schema, Field, Options, Clear, RegexSub
44
+
45
+ spec = Schema(
46
+ options=Options(clear=Clear(remove_tags=["script", "style"])),
47
+ fields={
48
+ "products": Field(
49
+ css=".product",
50
+ type="array<object>",
51
+ fields={
52
+ "sku": Field(css="SELF", type="string", attr="data-sku"),
53
+ "name": Field(css=".name", type="string"),
54
+ "url": Field(css="a.link", type="string", attr="href"),
55
+ "price": Field(css=".price", type="number", transform=[
56
+ RegexSub(pattern=r"[^0-9.]+"),
57
+ ]),
58
+ "tags": Field(css=".tags li", type="array<string>"),
59
+ },
60
+ )
61
+ },
62
+ )
63
+
64
+ result = spec.extract(html)
65
+ json_schema = spec.json_schema(title="Products")
66
+ ```
67
+
68
+ ### Boolean fields (`type: "boolean"`)
69
+
70
+ Boolean output is derived from `type`, not a transform. The extractor coerces common truthy/falsey values:
71
+ - truthy: `"true"`, `"t"`, `"yes"`, `"y"`, `"on"`, `"1"` (case-insensitive, surrounding whitespace ignored)
72
+ - falsey: `"false"`, `"f"`, `"no"`, `"n"`, `"off"`, `"0"`
73
+ - numbers: `1` → `True`, `0` → `False` (other numbers become `None`)
74
+
75
+ Python example:
76
+
77
+ ```python
78
+ from selectolax_schema import Schema, Field
79
+
80
+ html = "<span class='in-stock'> yes </span>"
81
+ spec = Schema(fields={"in_stock": Field(css=".in-stock", type="boolean")})
82
+
83
+ data = spec.extract(html)
84
+ assert data["in_stock"] is True
85
+ ```
86
+
87
+ If you want invalid/missing values to fail fast, set `nullable=False`:
88
+
89
+ ```python
90
+ from selectolax_schema import Schema, Field, ValidationError
91
+
92
+ html = "<span class='in-stock'> maybe </span>"
93
+ spec = Schema(fields={"in_stock": Field(css=".in-stock", type="boolean", nullable=False)})
94
+
95
+ try:
96
+ spec.extract(html)
97
+ except ValidationError:
98
+ pass
99
+ ```
100
+
101
+ ### YAML spec
102
+
103
+ ```yaml
104
+ options:
105
+ clear:
106
+ remove_tags: ["script", "style"]
107
+
108
+ fields:
109
+ products:
110
+ css: ".product"
111
+ type: "array<object>"
112
+ fields:
113
+ sku:
114
+ css: "SELF"
115
+ type: "string"
116
+ attr: "data-sku"
117
+ name:
118
+ css: ".name"
119
+ type: "string"
120
+ price:
121
+ css: ".price"
122
+ type: "number"
123
+ transform:
124
+ - regex_sub: { pattern: "[^0-9.]+", repl: "" }
125
+ in_stock:
126
+ css: ".in-stock"
127
+ type: "boolean"
128
+ ```
129
+
130
+ ```python
131
+ from selectolax_schema import extract_from_yaml
132
+
133
+ result = extract_from_yaml(html, yaml_spec)
134
+ ```
135
+
136
+ ## CLI
137
+
138
+ ```bash
139
+ selectolax-schema --version
140
+ selectolax-schema --spec spec.yml --html-file page.html
141
+ selectolax-schema --spec spec.yml --schema
142
+ ```
143
+
144
+ The installed package version is also available in Python:
145
+
146
+ ```python
147
+ from selectolax_schema import __version__
148
+ ```
149
+
150
+ ## Field reference
151
+
152
+ | param | type | description |
153
+ | ----------- | ------ | ------------------------------------------------------------ |
154
+ | `css` | `str` | CSS selector. Use `"SELF"` to select the context node itself |
155
+ | `attr` | `str` | Extract an attribute value (or special `"innerHTML"`) |
156
+ | `type` | `str` | Output type: `"string"|"integer"|"number"|"boolean"|"object"|"array<string>"|"array<integer>"|"array<number>"|"array<boolean>"|"array<object>"` |
157
+ | `nullable` | `bool` | If `false`, missing values raise `ValidationError` |
158
+ | `defaultValue` | `any` | Fallback value used when the extracted value is empty |
159
+ | `fields` | `dict` | Nested fields (for `object` / `array<object>`) |
160
+ | `transform` | `list` | Transform pipeline (see below) |
161
+ | `callback` | `callable` | Field-level post-processing hook (Python API only) |
162
+ | `outputSchema` | `dict` | Override JSON Schema for this field (useful when `callback` changes the output type/shape) |
163
+ | `required` | `bool` | Raise `ValidationError` if value is empty |
164
+
165
+ Notes:
166
+ - `type` is required for every field.
167
+ - Arrays must use `type: "array<...>"` (no `items:` and no `list:`).
168
+ - `attr` supports special values:
169
+ - `"innerHTML"`: extract HTML string from the selected node.
170
+ - `"ownText"`: extract direct text for the selected node (excludes descendant text).
171
+
172
+ ## Transform reference
173
+
174
+ | transform | shorthand | description |
175
+ | ------------------------- | --------- | --------------------------------------------- |
176
+ | `RegexSub(pattern, repl)` | — | Regex substitution |
177
+ | `Split(delimiter)` | — | Split string into array items (requires `type:"array<...>"`) |
178
+
179
+ Notes:
180
+ - String outputs are stripped automatically (no transform needed).
181
+ - Use field-level `defaultValue` for fallbacks (defaults are not supported inside transforms).
182
+
183
+ ## When to use `transform` vs `callback`
184
+
185
+ Both are meant for post-processing, but they work at different levels and have different ergonomics.
186
+
187
+ ### Use `transform` for value-centric pipelines
188
+
189
+ Good fit when you want a predictable, reusable pipeline on a single extracted value (e.g., regex cleanup, split).
190
+
191
+ Order of operations (scalar fields):
192
+ 1. Extract raw string
193
+ 2. Apply `transform` pipeline
194
+ 3. Apply `type` coercion (`number`/`integer`/`boolean`)
195
+ 4. Apply `callback` (if any)
196
+
197
+ Example: remove currency symbols before coercing to `number`:
198
+
199
+ ```python
200
+ from selectolax_schema import Schema, Field, RegexSub
201
+
202
+ spec = Schema(
203
+ fields={
204
+ "price": Field(
205
+ css=".price",
206
+ type="number",
207
+ transform=[RegexSub(pattern=r"[^0-9.]+", repl="")],
208
+ )
209
+ }
210
+ )
211
+ data = spec.extract(html)
212
+ ```
213
+
214
+ ### Use `callback` for whole-field logic (filtering, sorting, aggregation)
215
+
216
+ `callback` receives the final extracted value for the field:
217
+ - scalar field → the scalar value (`str|int|float|bool|None`)
218
+ - `array<...>` field → the whole list
219
+ - `object` field → the whole dict
220
+
221
+ This is a better fit for list-level operations or aggregations.
222
+
223
+ #### When `callback` changes the output type/shape
224
+
225
+ `callback` is an arbitrary Python function, so the library cannot reliably infer a JSON Schema for its return value.
226
+ If your callback changes the type/shape (e.g., list → object, object → string), set `outputSchema` on the field to
227
+ keep `spec.json_schema()` in sync with the actual output.
228
+
229
+ Example: filter a list of objects (keep only items you care about):
230
+
231
+ ```python
232
+ from selectolax_schema.types import Schema, Field
233
+
234
+ def keep_only_a(items: list[dict]) -> list[dict]:
235
+ return [item for item in items if "A" in item["name"]]
236
+
237
+ spec = Schema(
238
+ fields={
239
+ "products": Field(
240
+ css=".item",
241
+ type="array<object>",
242
+ callback=keep_only_a,
243
+ fields={
244
+ "name": Field(css=".name", type="string"),
245
+ },
246
+ )
247
+ }
248
+ )
249
+ data = spec.extract(html)
250
+ ```
251
+
252
+ ### `array<object>` special case: `transform` is per-item
253
+
254
+ For `type: "array<object>"`, `transform` is applied to each extracted object (each list element).
255
+ If a transform returns `None`, the item is dropped from the list.
256
+
257
+ ```python
258
+ from selectolax_schema import extract
259
+
260
+ def drop_product_a(item: dict) -> dict | None:
261
+ return None if item.get("name") == "Product A" else item
262
+
263
+ spec = {
264
+ "fields": {
265
+ "products": {
266
+ "css": ".item",
267
+ "type": "array<object>",
268
+ "transform": [drop_product_a],
269
+ "fields": {"name": {"css": ".name", "type": "string"}},
270
+ }
271
+ }
272
+ }
273
+ data = extract(html, spec)
274
+ ```
275
+
276
+ ### YAML note
277
+
278
+ YAML specs support only the built-in transform steps (e.g., `regex_sub`, `split`).
279
+ Python callables (`transform: [my_fn]` / `callback: my_fn`) are only supported via the Python API (typed `Schema/Field` or a Python `dict` spec), not via YAML text.
280
+
281
+ ## Testing
282
+
283
+ Install the dev dependencies (in a virtualenv) and run the test suite:
284
+
285
+ ```bash
286
+ python -m pip install -e ".[dev]"
287
+ python -m pytest
288
+ ```
289
+
290
+ Run a single test file:
291
+
292
+ ```bash
293
+ python -m pytest tests/test_extractor.py
294
+ ```
295
+
296
+ ## License
297
+
298
+ [MIT](LICENSE)
299
+
@@ -0,0 +1,283 @@
1
+ # selectolax-schema
2
+
3
+ Schema-driven HTML extractor powered by selectolax's maintained Lexbor backend.
4
+ Define extraction specs in Python (with full IDE type hints) or YAML, and get
5
+ structured JSON out.
6
+
7
+ The field model and observable API mirror `scrapling-schema` 1.1.3; the Python
8
+ package and CLI are named `selectolax_schema` and `selectolax-schema`.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pip install selectolax-schema
14
+ ```
15
+
16
+ ## Requirements
17
+
18
+ - Python >= 3.10
19
+ - [selectolax](https://github.com/rushter/selectolax) >= 0.3.0
20
+ - PyYAML >= 6.0
21
+
22
+ ## Python API
23
+
24
+ ### Python type spec (recommended)
25
+
26
+ ```python
27
+ from selectolax_schema import Schema, Field, Options, Clear, RegexSub
28
+
29
+ spec = Schema(
30
+ options=Options(clear=Clear(remove_tags=["script", "style"])),
31
+ fields={
32
+ "products": Field(
33
+ css=".product",
34
+ type="array<object>",
35
+ fields={
36
+ "sku": Field(css="SELF", type="string", attr="data-sku"),
37
+ "name": Field(css=".name", type="string"),
38
+ "url": Field(css="a.link", type="string", attr="href"),
39
+ "price": Field(css=".price", type="number", transform=[
40
+ RegexSub(pattern=r"[^0-9.]+"),
41
+ ]),
42
+ "tags": Field(css=".tags li", type="array<string>"),
43
+ },
44
+ )
45
+ },
46
+ )
47
+
48
+ result = spec.extract(html)
49
+ json_schema = spec.json_schema(title="Products")
50
+ ```
51
+
52
+ ### Boolean fields (`type: "boolean"`)
53
+
54
+ Boolean output is derived from `type`, not a transform. The extractor coerces common truthy/falsey values:
55
+ - truthy: `"true"`, `"t"`, `"yes"`, `"y"`, `"on"`, `"1"` (case-insensitive, surrounding whitespace ignored)
56
+ - falsey: `"false"`, `"f"`, `"no"`, `"n"`, `"off"`, `"0"`
57
+ - numbers: `1` → `True`, `0` → `False` (other numbers become `None`)
58
+
59
+ Python example:
60
+
61
+ ```python
62
+ from selectolax_schema import Schema, Field
63
+
64
+ html = "<span class='in-stock'> yes </span>"
65
+ spec = Schema(fields={"in_stock": Field(css=".in-stock", type="boolean")})
66
+
67
+ data = spec.extract(html)
68
+ assert data["in_stock"] is True
69
+ ```
70
+
71
+ If you want invalid/missing values to fail fast, set `nullable=False`:
72
+
73
+ ```python
74
+ from selectolax_schema import Schema, Field, ValidationError
75
+
76
+ html = "<span class='in-stock'> maybe </span>"
77
+ spec = Schema(fields={"in_stock": Field(css=".in-stock", type="boolean", nullable=False)})
78
+
79
+ try:
80
+ spec.extract(html)
81
+ except ValidationError:
82
+ pass
83
+ ```
84
+
85
+ ### YAML spec
86
+
87
+ ```yaml
88
+ options:
89
+ clear:
90
+ remove_tags: ["script", "style"]
91
+
92
+ fields:
93
+ products:
94
+ css: ".product"
95
+ type: "array<object>"
96
+ fields:
97
+ sku:
98
+ css: "SELF"
99
+ type: "string"
100
+ attr: "data-sku"
101
+ name:
102
+ css: ".name"
103
+ type: "string"
104
+ price:
105
+ css: ".price"
106
+ type: "number"
107
+ transform:
108
+ - regex_sub: { pattern: "[^0-9.]+", repl: "" }
109
+ in_stock:
110
+ css: ".in-stock"
111
+ type: "boolean"
112
+ ```
113
+
114
+ ```python
115
+ from selectolax_schema import extract_from_yaml
116
+
117
+ result = extract_from_yaml(html, yaml_spec)
118
+ ```
119
+
120
+ ## CLI
121
+
122
+ ```bash
123
+ selectolax-schema --version
124
+ selectolax-schema --spec spec.yml --html-file page.html
125
+ selectolax-schema --spec spec.yml --schema
126
+ ```
127
+
128
+ The installed package version is also available in Python:
129
+
130
+ ```python
131
+ from selectolax_schema import __version__
132
+ ```
133
+
134
+ ## Field reference
135
+
136
+ | param | type | description |
137
+ | ----------- | ------ | ------------------------------------------------------------ |
138
+ | `css` | `str` | CSS selector. Use `"SELF"` to select the context node itself |
139
+ | `attr` | `str` | Extract an attribute value (or special `"innerHTML"`) |
140
+ | `type` | `str` | Output type: `"string"|"integer"|"number"|"boolean"|"object"|"array<string>"|"array<integer>"|"array<number>"|"array<boolean>"|"array<object>"` |
141
+ | `nullable` | `bool` | If `false`, missing values raise `ValidationError` |
142
+ | `defaultValue` | `any` | Fallback value used when the extracted value is empty |
143
+ | `fields` | `dict` | Nested fields (for `object` / `array<object>`) |
144
+ | `transform` | `list` | Transform pipeline (see below) |
145
+ | `callback` | `callable` | Field-level post-processing hook (Python API only) |
146
+ | `outputSchema` | `dict` | Override JSON Schema for this field (useful when `callback` changes the output type/shape) |
147
+ | `required` | `bool` | Raise `ValidationError` if value is empty |
148
+
149
+ Notes:
150
+ - `type` is required for every field.
151
+ - Arrays must use `type: "array<...>"` (no `items:` and no `list:`).
152
+ - `attr` supports special values:
153
+ - `"innerHTML"`: extract HTML string from the selected node.
154
+ - `"ownText"`: extract direct text for the selected node (excludes descendant text).
155
+
156
+ ## Transform reference
157
+
158
+ | transform | shorthand | description |
159
+ | ------------------------- | --------- | --------------------------------------------- |
160
+ | `RegexSub(pattern, repl)` | — | Regex substitution |
161
+ | `Split(delimiter)` | — | Split string into array items (requires `type:"array<...>"`) |
162
+
163
+ Notes:
164
+ - String outputs are stripped automatically (no transform needed).
165
+ - Use field-level `defaultValue` for fallbacks (defaults are not supported inside transforms).
166
+
167
+ ## When to use `transform` vs `callback`
168
+
169
+ Both are meant for post-processing, but they work at different levels and have different ergonomics.
170
+
171
+ ### Use `transform` for value-centric pipelines
172
+
173
+ Good fit when you want a predictable, reusable pipeline on a single extracted value (e.g., regex cleanup, split).
174
+
175
+ Order of operations (scalar fields):
176
+ 1. Extract raw string
177
+ 2. Apply `transform` pipeline
178
+ 3. Apply `type` coercion (`number`/`integer`/`boolean`)
179
+ 4. Apply `callback` (if any)
180
+
181
+ Example: remove currency symbols before coercing to `number`:
182
+
183
+ ```python
184
+ from selectolax_schema import Schema, Field, RegexSub
185
+
186
+ spec = Schema(
187
+ fields={
188
+ "price": Field(
189
+ css=".price",
190
+ type="number",
191
+ transform=[RegexSub(pattern=r"[^0-9.]+", repl="")],
192
+ )
193
+ }
194
+ )
195
+ data = spec.extract(html)
196
+ ```
197
+
198
+ ### Use `callback` for whole-field logic (filtering, sorting, aggregation)
199
+
200
+ `callback` receives the final extracted value for the field:
201
+ - scalar field → the scalar value (`str|int|float|bool|None`)
202
+ - `array<...>` field → the whole list
203
+ - `object` field → the whole dict
204
+
205
+ This is a better fit for list-level operations or aggregations.
206
+
207
+ #### When `callback` changes the output type/shape
208
+
209
+ `callback` is an arbitrary Python function, so the library cannot reliably infer a JSON Schema for its return value.
210
+ If your callback changes the type/shape (e.g., list → object, object → string), set `outputSchema` on the field to
211
+ keep `spec.json_schema()` in sync with the actual output.
212
+
213
+ Example: filter a list of objects (keep only items you care about):
214
+
215
+ ```python
216
+ from selectolax_schema.types import Schema, Field
217
+
218
+ def keep_only_a(items: list[dict]) -> list[dict]:
219
+ return [item for item in items if "A" in item["name"]]
220
+
221
+ spec = Schema(
222
+ fields={
223
+ "products": Field(
224
+ css=".item",
225
+ type="array<object>",
226
+ callback=keep_only_a,
227
+ fields={
228
+ "name": Field(css=".name", type="string"),
229
+ },
230
+ )
231
+ }
232
+ )
233
+ data = spec.extract(html)
234
+ ```
235
+
236
+ ### `array<object>` special case: `transform` is per-item
237
+
238
+ For `type: "array<object>"`, `transform` is applied to each extracted object (each list element).
239
+ If a transform returns `None`, the item is dropped from the list.
240
+
241
+ ```python
242
+ from selectolax_schema import extract
243
+
244
+ def drop_product_a(item: dict) -> dict | None:
245
+ return None if item.get("name") == "Product A" else item
246
+
247
+ spec = {
248
+ "fields": {
249
+ "products": {
250
+ "css": ".item",
251
+ "type": "array<object>",
252
+ "transform": [drop_product_a],
253
+ "fields": {"name": {"css": ".name", "type": "string"}},
254
+ }
255
+ }
256
+ }
257
+ data = extract(html, spec)
258
+ ```
259
+
260
+ ### YAML note
261
+
262
+ YAML specs support only the built-in transform steps (e.g., `regex_sub`, `split`).
263
+ Python callables (`transform: [my_fn]` / `callback: my_fn`) are only supported via the Python API (typed `Schema/Field` or a Python `dict` spec), not via YAML text.
264
+
265
+ ## Testing
266
+
267
+ Install the dev dependencies (in a virtualenv) and run the test suite:
268
+
269
+ ```bash
270
+ python -m pip install -e ".[dev]"
271
+ python -m pytest
272
+ ```
273
+
274
+ Run a single test file:
275
+
276
+ ```bash
277
+ python -m pytest tests/test_extractor.py
278
+ ```
279
+
280
+ ## License
281
+
282
+ [MIT](LICENSE)
283
+
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "selectolax-schema"
7
+ version = "0.1.0"
8
+ description = "Schema-driven HTML extraction powered by selectolax."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ dependencies = [
14
+ "selectolax>=0.3.0",
15
+ "PyYAML>=6.0.0",
16
+ ]
17
+
18
+ [project.urls]
19
+ Repository = "https://github.com/aimscrape/selectolax-schema"
20
+ Issues = "https://github.com/aimscrape/selectolax-schema/issues"
21
+
22
+ [project.optional-dependencies]
23
+ dev = [
24
+ "pytest>=8.0.0",
25
+ ]
26
+
27
+ [project.scripts]
28
+ selectolax-schema = "selectolax_schema.cli:main"
29
+
30
+ [tool.pytest.ini_options]
31
+ testpaths = ["tests"]
32
+
33
+ [tool.setuptools]
34
+ package-dir = {"" = "src"}
35
+
36
+ [tool.setuptools.packages.find]
37
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,54 @@
1
+ from importlib.metadata import PackageNotFoundError, version
2
+
3
+ from .core import (
4
+ ExtractError,
5
+ ValidationError,
6
+ extract,
7
+ extract_from_file,
8
+ extract_from_yaml,
9
+ schema,
10
+ schema_from_file,
11
+ schema_from_yaml,
12
+ )
13
+ from .types import (
14
+ Clear,
15
+ Field,
16
+ FieldType,
17
+ Options,
18
+ RegexSub,
19
+ ScalarType,
20
+ ArrayType,
21
+ Schema,
22
+ Split,
23
+ TransformStep,
24
+ )
25
+
26
+ try:
27
+ __version__ = version("selectolax-schema")
28
+ except PackageNotFoundError:
29
+ # Keep source-tree imports usable before the distribution is installed.
30
+ __version__ = "0+unknown"
31
+
32
+ __all__ = [
33
+ "__version__",
34
+ "ExtractError",
35
+ "ValidationError",
36
+ "extract",
37
+ "extract_from_file",
38
+ "extract_from_yaml",
39
+ "schema",
40
+ "schema_from_file",
41
+ "schema_from_yaml",
42
+ # types
43
+ "Schema",
44
+ "Field",
45
+ "FieldType",
46
+ "ScalarType",
47
+ "ArrayType",
48
+ "Options",
49
+ "Clear",
50
+ "RegexSub",
51
+ "Split",
52
+ "TransformStep",
53
+ ]
54
+