Skip to content

Commit c8ab557

Browse files
author
julka
committed
Add codegen
1 parent 8437192 commit c8ab557

5 files changed

Lines changed: 62 additions & 12 deletions

File tree

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ Run the script:
8282
python main.py
8383
```
8484

85-
### With typed parameters (optional)
85+
### With typed parameters (recommended)
8686

87-
For IDE autocomplete and parameter validation, run the type generator after installing:
87+
For IDE autocomplete and parameter validation, run the type generator once after installing. Dict-based usage works without this step, but typed classes require it:
8888

8989
```bash
9090
python -m decodo.codegen.codegen

‎src/decodo/codegen/codegen.py‎

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,63 @@
11
from __future__ import annotations
22

3+
import argparse
34
import os
45
import shutil
6+
import sys
7+
from pathlib import Path
58

69
from .web_scraping_api.generate_parameters import generate_parameters_file
710
from .web_scraping_api.generate_targets import generate_targets_enum_file, generate_targets_file
8-
from .web_scraping_api.shared import local_ir_path, out_dir
11+
from .web_scraping_api.shared import local_ir_path, out_dir as _default_out_dir
12+
13+
14+
def _in_site_packages() -> bool:
15+
return "site-packages" in str(Path(__file__).resolve())
916

1017

1118
def main() -> None:
19+
parser = argparse.ArgumentParser(
20+
prog="python -m decodo.codegen.codegen",
21+
description="Generate typed parameter classes from the Decodo IR schema.",
22+
)
23+
parser.add_argument(
24+
"--out-dir",
25+
metavar="PATH",
26+
default=None,
27+
help=(
28+
"Directory to write generated files into. "
29+
"Defaults to the package's built-in generated/ directory "
30+
"(editable installs only). Required for non-editable installs."
31+
),
32+
)
33+
args = parser.parse_args()
34+
35+
if args.out_dir is not None:
36+
out_dir = str(Path(args.out_dir).resolve())
37+
elif _in_site_packages():
38+
print(
39+
"error: running from a non-editable install.\n"
40+
"Generated files would be written into site-packages and silently reverted\n"
41+
"on the next `pip install --upgrade`.\n"
42+
"Use --out-dir <path> to specify a project-local output directory.",
43+
file=sys.stderr,
44+
)
45+
sys.exit(1)
46+
else:
47+
out_dir = _default_out_dir
48+
1249
os.makedirs(out_dir, exist_ok=True)
1350
init_path = os.path.join(out_dir, "__init__.py")
1451
if not os.path.exists(init_path):
1552
open(init_path, "w").close()
1653

17-
generate_parameters_file()
18-
generate_targets_enum_file()
19-
generate_targets_file()
54+
generate_parameters_file(out_dir)
55+
# The Target enum lives in decodo/targets.py (committed source). Only regenerate
56+
# it when we have write access to the source tree (editable install). Non-editable
57+
# installs already ship the enum baked into the package.
58+
if not _in_site_packages():
59+
generate_targets_enum_file()
60+
generate_targets_file(out_dir)
2061

2162
# Copy the downloaded IR JSON into generated/ so BundledSchema can load
2263
# schemas directly at runtime without a generated Python file.

‎src/decodo/codegen/web_scraping_api/generate_parameters.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,14 @@ def get_parameters_file(api: WebScrapingApiIR) -> str:
108108
return "\n".join(lines)
109109

110110

111-
def generate_parameters_file() -> None:
111+
def generate_parameters_file(dest_dir: str | None = None) -> None:
112112
ir = fetch_intermediate_representation()
113113
api = ir["apis"]["webScrapingApi"]
114114
file_contents = get_parameters_file(api)
115115

116-
os.makedirs(out_dir, exist_ok=True)
117-
out_path = os.path.join(out_dir, "parameters.py")
116+
target = dest_dir if dest_dir is not None else out_dir
117+
os.makedirs(target, exist_ok=True)
118+
out_path = os.path.join(target, "parameters.py")
118119
with open(out_path, "w", encoding="utf-8") as f:
119120
f.write(file_contents)
120121

‎src/decodo/codegen/web_scraping_api/generate_targets.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,13 +207,14 @@ def generate_targets_enum_file() -> None:
207207
print(f" {targets_enum_path} ({target_count} targets)")
208208

209209

210-
def generate_targets_file() -> None:
210+
def generate_targets_file(dest_dir: str | None = None) -> None:
211211
ir = fetch_intermediate_representation()
212212
api = ir["apis"]["webScrapingApi"]
213213
file_contents = _get_targets_file_contents(api)
214214

215-
os.makedirs(out_dir, exist_ok=True)
216-
out_path = os.path.join(out_dir, "targets.py")
215+
target = dest_dir if dest_dir is not None else out_dir
216+
os.makedirs(target, exist_ok=True)
217+
out_path = os.path.join(target, "targets.py")
217218
with open(out_path, "w", encoding="utf-8") as f:
218219
f.write(file_contents)
219220

‎src/decodo/schema/bundled_schema.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import json
4+
import warnings
45
from pathlib import Path
56
from typing import Any, ClassVar, cast
67

@@ -42,6 +43,12 @@ def __init__(self) -> None:
4243
}
4344
self._target_meta = _target_meta if _target_meta is not None else build_target_meta(api_targets)
4445
else:
46+
warnings.warn(
47+
"Decodo IR schema not found — payload validation is disabled. "
48+
"Run: python -m decodo.codegen.codegen",
49+
RuntimeWarning,
50+
stacklevel=2,
51+
)
4552
self._request_schemas = {}
4653
self._target_meta = _target_meta or {}
4754

0 commit comments

Comments
 (0)