ssc_codegen 0.29.6__tar.gz → 0.31.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.29.6 → ssc_codegen-0.31.0}/PKG-INFO +3 -3
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/pyproject.toml +3 -5
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/__init__.py +6 -2
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/struct.py +104 -50
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/converters/js_pure.py +148 -179
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/converters/py_bs4.py +62 -309
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/converters/py_lxml.py +21 -17
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/converters/py_parsel.py +12 -6
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/converters/py_render.py +2 -2
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/converters/py_slax.py +12 -6
- ssc_codegen-0.31.0/ssc_codegen/converters/runtime/__init__.py +7 -0
- ssc_codegen-0.31.0/ssc_codegen/converters/runtime/_helpers.py +35 -0
- ssc_codegen-0.31.0/ssc_codegen/converters/runtime/js_base.py +24 -0
- ssc_codegen-0.31.0/ssc_codegen/converters/runtime/py_base.py +67 -0
- ssc_codegen-0.31.0/ssc_codegen/converters/runtime/py_lxml.py +7 -0
- ssc_codegen-0.31.0/ssc_codegen/converters/runtime/py_rest.py +133 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/core/__init__.py +3 -3
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/core/contexts.py +197 -235
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/core/expressions.py +4 -13
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/core/format.py +361 -361
- ssc_codegen-0.31.0/ssc_codegen/core/linter.py +1491 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/core/module_handler.py +9 -49
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/core/predicates.py +2 -2
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/core/reader.py +116 -164
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/core/struct_parser.py +48 -48
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/core/type_checking.py +401 -401
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/kdl/__init__.py +8 -5
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/main.py +1 -1
- ssc_codegen-0.29.6/ssc_codegen/core/adapter.py +0 -25
- ssc_codegen-0.29.6/ssc_codegen/core/linting.py +0 -1179
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/.gitignore +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/LICENSE +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/README.md +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/__init__.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/_logging.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/array.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/base.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/cast.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/control.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/extract.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/helpers.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/jsondef.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/module.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/predicate_containers.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/predicate_ops.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/regex.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/selectors.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/string.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/transform.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/typedef.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/ast/types.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/converters/base.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/converters/helpers.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/document_utils.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/exceptions.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/health.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/kdl/dict_reader.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/kdl/parser.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/kdl/reader.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/parsers/__init__.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/parsers/curl.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/parsers/http.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/pseudo_selectors.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/regex_utils.py +0 -0
- {ssc_codegen-0.29.6/ssc_codegen/converters → ssc_codegen-0.31.0/ssc_codegen}/request_spec.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.31.0}/ssc_codegen/selector_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ssc_codegen
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.31.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
|
|
@@ -20,13 +20,13 @@ Classifier: Topic :: Text Processing :: Markup :: HTML
|
|
|
20
20
|
Classifier: Topic :: Utilities
|
|
21
21
|
Requires-Python: >=3.10
|
|
22
22
|
Requires-Dist: bs4>=0.0.2
|
|
23
|
-
Requires-Dist: click<8.2.0
|
|
24
23
|
Requires-Dist: colorama>=0.4.6; sys_platform == 'win32'
|
|
25
24
|
Requires-Dist: cssselect>=1.2.0
|
|
25
|
+
Requires-Dist: kdlquery>=1.1.2
|
|
26
26
|
Requires-Dist: lxml>=5.3.0
|
|
27
27
|
Requires-Dist: pyyaml>=6.0
|
|
28
28
|
Requires-Dist: soupsieve>=2.6
|
|
29
|
-
Requires-Dist: typer
|
|
29
|
+
Requires-Dist: typer>0.26
|
|
30
30
|
Requires-Dist: typing-extensions; python_version < '3.11'
|
|
31
31
|
Description-Content-Type: text/markdown
|
|
32
32
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ssc_codegen"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.31.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"
|
|
@@ -10,12 +10,10 @@ dependencies = [
|
|
|
10
10
|
"cssselect>=1.2.0",
|
|
11
11
|
"lxml>=5.3.0",
|
|
12
12
|
"soupsieve>=2.6",
|
|
13
|
-
"typer
|
|
13
|
+
"typer>0.26",
|
|
14
14
|
"typing_extensions;python_version<'3.11'",
|
|
15
|
-
# https://github.com/fastapi/typer/discussions/1215
|
|
16
|
-
# https://github.com/fastapi/typer/pull/1145
|
|
17
|
-
"click<8.2.0",
|
|
18
15
|
"pyyaml>=6.0",
|
|
16
|
+
"kdlquery>=1.1.2",
|
|
19
17
|
]
|
|
20
18
|
|
|
21
19
|
classifiers = [
|
|
@@ -43,7 +43,10 @@ from .struct import (
|
|
|
43
43
|
TableConfig,
|
|
44
44
|
TableRow,
|
|
45
45
|
TableMatchKey,
|
|
46
|
-
|
|
46
|
+
RequestHttp,
|
|
47
|
+
MethodBase,
|
|
48
|
+
MethodFetch,
|
|
49
|
+
MethodRest,
|
|
47
50
|
ErrorResponse,
|
|
48
51
|
PlaceholderSpec,
|
|
49
52
|
Field,
|
|
@@ -150,7 +153,8 @@ __all__ = [
|
|
|
150
153
|
"Init", "InitField", "SplitDoc",
|
|
151
154
|
"Key", "Value",
|
|
152
155
|
"TableConfig", "TableRow", "TableMatchKey",
|
|
153
|
-
"
|
|
156
|
+
"RequestHttp", "MethodBase", "MethodFetch", "MethodRest",
|
|
157
|
+
"ErrorResponse", "PlaceholderSpec", "Field", "StartParse",
|
|
154
158
|
# selectors
|
|
155
159
|
"CssSelect", "CssSelectAll",
|
|
156
160
|
"XpathSelect", "XpathSelectAll",
|
|
@@ -49,6 +49,106 @@ def parse_placeholder(match: "_re.Match[str]") -> PlaceholderSpec:
|
|
|
49
49
|
)
|
|
50
50
|
|
|
51
51
|
|
|
52
|
+
# ── Request/Method nodes ──────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _iter_http_strings(http: RequestHttp):
|
|
56
|
+
"""Yield every string value that may contain placeholders."""
|
|
57
|
+
yield http.url
|
|
58
|
+
yield from http.headers.values()
|
|
59
|
+
yield from http.cookies.values()
|
|
60
|
+
yield from http.params.values()
|
|
61
|
+
if isinstance(http.body, str) and http.body:
|
|
62
|
+
yield http.body
|
|
63
|
+
elif isinstance(http.body, dict):
|
|
64
|
+
yield from _iter_dict_strings(http.body)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _iter_dict_strings(d: dict):
|
|
68
|
+
for v in d.values():
|
|
69
|
+
if isinstance(v, str):
|
|
70
|
+
yield v
|
|
71
|
+
elif isinstance(v, dict):
|
|
72
|
+
yield from _iter_dict_strings(v)
|
|
73
|
+
elif isinstance(v, list):
|
|
74
|
+
for item in v:
|
|
75
|
+
if isinstance(item, str):
|
|
76
|
+
yield item
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class RequestHttp(Node):
|
|
81
|
+
"""Parsed HTTP request — child node of MethodBase.
|
|
82
|
+
|
|
83
|
+
Created once at parse time from ``parse_to_spec(raw_payload)``;
|
|
84
|
+
converters read fields directly instead of re-parsing raw_payload.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
method: str = "GET"
|
|
88
|
+
url: str = ""
|
|
89
|
+
headers: dict[str, str] = field(default_factory=dict)
|
|
90
|
+
cookies: dict[str, str] = field(default_factory=dict)
|
|
91
|
+
params: dict[str, str] = field(default_factory=dict)
|
|
92
|
+
body_kind: str = "empty" # "empty" | "json" | "form" | "raw"
|
|
93
|
+
body: str | dict | None = None
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def placeholders(self) -> list[PlaceholderSpec]:
|
|
97
|
+
"""Unique placeholders across all string fields."""
|
|
98
|
+
seen: set[str] = set()
|
|
99
|
+
result: list[PlaceholderSpec] = []
|
|
100
|
+
for text in _iter_http_strings(self):
|
|
101
|
+
for m in PLACEHOLDER_RE.finditer(text):
|
|
102
|
+
spec = parse_placeholder(m)
|
|
103
|
+
if spec.name not in seen:
|
|
104
|
+
seen.add(spec.name)
|
|
105
|
+
result.append(spec)
|
|
106
|
+
return result
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@dataclass
|
|
110
|
+
class MethodBase(Node):
|
|
111
|
+
"""Base class for @request method nodes."""
|
|
112
|
+
|
|
113
|
+
name: str = "" # method name suffix; "" = default fetch()
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def http_request(self) -> RequestHttp:
|
|
117
|
+
return [n for n in self.body if isinstance(n, RequestHttp)][0]
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def placeholders(self) -> list[PlaceholderSpec]:
|
|
121
|
+
return self.http_request.placeholders
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def placeholder_names(self) -> list[str]:
|
|
125
|
+
return [p.name for p in self.placeholders]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@dataclass
|
|
129
|
+
class MethodFetch(MethodBase):
|
|
130
|
+
"""Fetch shortcut for regular schemas (Item/List/etc).
|
|
131
|
+
|
|
132
|
+
``fetch()`` returns a parser instance constructed from the HTTP response body.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
response_path: str = "" # dot-notation JSON path, e.g. "payload.html"
|
|
136
|
+
response_join: str = (
|
|
137
|
+
"" # join separator when response-path resolves to list[str]
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass
|
|
142
|
+
class MethodRest(MethodBase):
|
|
143
|
+
"""REST method for ``type=rest`` schemas.
|
|
144
|
+
|
|
145
|
+
Method returns ``Ok/TransportErr`` union; no HTML parsing.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
doc: str = "" # per-method docstring
|
|
149
|
+
response_schema: str = "" # json schema name for typed 2xx response
|
|
150
|
+
|
|
151
|
+
|
|
52
152
|
# ── Base struct ──────────────────────────────────────────────────────────────
|
|
53
153
|
|
|
54
154
|
|
|
@@ -68,15 +168,15 @@ class StructBase(Node):
|
|
|
68
168
|
return self.body[0] # type: ignore
|
|
69
169
|
|
|
70
170
|
@property
|
|
71
|
-
def request_config(self) ->
|
|
171
|
+
def request_config(self) -> MethodBase | None:
|
|
72
172
|
for node in self.body:
|
|
73
|
-
if isinstance(node,
|
|
173
|
+
if isinstance(node, MethodBase):
|
|
74
174
|
return node
|
|
75
175
|
return None
|
|
76
176
|
|
|
77
177
|
@property
|
|
78
|
-
def request_configs(self) -> list[
|
|
79
|
-
return [n for n in self.body if isinstance(n,
|
|
178
|
+
def request_configs(self) -> list[MethodBase]:
|
|
179
|
+
return [n for n in self.body if isinstance(n, MethodBase)]
|
|
80
180
|
|
|
81
181
|
@property
|
|
82
182
|
def use_request(self) -> bool:
|
|
@@ -350,52 +450,6 @@ class TableMatchKey(Node):
|
|
|
350
450
|
ret: VariableType = field(default=VariableType.STRING)
|
|
351
451
|
|
|
352
452
|
|
|
353
|
-
@dataclass
|
|
354
|
-
class RequestConfig(Node):
|
|
355
|
-
"""
|
|
356
|
-
Optional transport layer config for a struct.
|
|
357
|
-
DSL: @request [name="suffix"] [response-path="..."] [response-join="..."]
|
|
358
|
-
[response=JsonSchema] [doc="..."] \"""...\"""
|
|
359
|
-
|
|
360
|
-
raw_payload stores the verbatim curl or raw HTTP string (with {{placeholders}}).
|
|
361
|
-
Transport normalization (curl/HTTP parse → kwargs) happens at converter stage.
|
|
362
|
-
|
|
363
|
-
name="" (unnamed) generates fetch()/async_fetch().
|
|
364
|
-
name="by-id" generates fetch_by_id()/async_fetch_by_id() (Python)
|
|
365
|
-
or fetchById() (JS).
|
|
366
|
-
|
|
367
|
-
response_schema (type=rest only): json schema name for typed 2xx response.
|
|
368
|
-
Empty string = void return.
|
|
369
|
-
doc (type=rest only): per-method docstring.
|
|
370
|
-
"""
|
|
371
|
-
|
|
372
|
-
raw_payload: str = ""
|
|
373
|
-
response_path: str = "" # dot-notation JSON path, e.g. "payload.html"
|
|
374
|
-
response_join: str = (
|
|
375
|
-
"" # join separator when response-path resolves to list[str]
|
|
376
|
-
)
|
|
377
|
-
name: str = "" # method name suffix; "" = default fetch()
|
|
378
|
-
response_schema: str = "" # type=rest: json schema for 2xx body
|
|
379
|
-
doc: str = "" # type=rest: per-method docstring
|
|
380
|
-
|
|
381
|
-
@property
|
|
382
|
-
def placeholders(self) -> list[PlaceholderSpec]:
|
|
383
|
-
"""Unique placeholders in declaration order (dedup by name)."""
|
|
384
|
-
seen: set[str] = set()
|
|
385
|
-
result: list[PlaceholderSpec] = []
|
|
386
|
-
for m in PLACEHOLDER_RE.finditer(self.raw_payload):
|
|
387
|
-
spec = parse_placeholder(m)
|
|
388
|
-
if spec.name not in seen:
|
|
389
|
-
seen.add(spec.name)
|
|
390
|
-
result.append(spec)
|
|
391
|
-
return result
|
|
392
|
-
|
|
393
|
-
@property
|
|
394
|
-
def placeholder_names(self) -> list[str]:
|
|
395
|
-
"""Unique placeholder names in declaration order."""
|
|
396
|
-
return [p.name for p in self.placeholders]
|
|
397
|
-
|
|
398
|
-
|
|
399
453
|
@dataclass
|
|
400
454
|
class ErrorResponse(Node):
|
|
401
455
|
"""
|