Repository navigation
Expand file tree
/
Copy pathupdate_export_examples.py
More file actions
103 lines (93 loc) · 5.22 KB
/
Copy pathupdate_export_examples.py
File metadata and controls
103 lines (93 loc) · 5.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#!/usr/bin/env python3
"""Regenerate the example command + output blocks on the export reference pages.
Runs `datacontract export <format>` against the example contracts in examples/
and injects the real output into docs/docs/exports/<format>.md between the
AUTOGENERATED markers. Re-run whenever an exporter's output changes.
python update_export_examples.py
"""
import re
import shlex
import shutil
import tempfile
from pathlib import Path
from docs_examples import DOCS, EXAMPLES_URL, REPO, fence, run_cli, upsert
# format -> (command, fence language, max lines). The command runs as shown, in a copy of examples/<stem>/,
# the folder named after its contract file (orders.odcs.yaml -> examples/orders/).
EXPORTS = {
"sql": ("datacontract export sql orders.odcs.yaml --output orders.sql", "sql", None),
"sql-query": ("datacontract export sql-query orders.odcs.yaml --schema-name orders", "sql", None),
"dbt-models": ("datacontract export dbt-models orders.odcs.yaml", "yaml", 40),
"dbt-sources": ("datacontract export dbt-sources orders.odcs.yaml", "yaml", 40),
"dbt-staging-sql": ("datacontract export dbt-staging-sql orders.odcs.yaml --schema-name orders", "sql", None),
"avro": ("datacontract export avro orders.odcs.yaml --schema-name orders --output orders.avsc", "json", None),
"avro-idl": ("datacontract export avro-idl orders.odcs.yaml --output orders.avdl", "text", None),
"jsonschema": (
"datacontract export jsonschema orders.odcs.yaml --schema-name orders --output orders.schema.json",
"json",
None,
),
"pydantic-model": ("datacontract export pydantic-model orders.odcs.yaml --output orders.py", "python", None),
"protobuf": ("datacontract export protobuf orders.odcs.yaml --output orders.proto", "protobuf", None),
"xsd": ("datacontract export xsd orders.odcs.yaml --output orders.xsd", "xml", 40),
"odcs": ("datacontract export odcs orders.odcs.yaml --output orders.normalized.yaml", "yaml", 32),
# rdf is intentionally not managed here: its Turtle serialization orders
# triples non-deterministically, so a regenerated example would churn on
# every run. exports/rdf.md keeps a committed, hand-checked example.
"markdown": ("datacontract export markdown orders.odcs.yaml --output orders.md", "markdown", 34),
"mermaid": ("datacontract export mermaid orders.odcs.yaml", "mermaid", None),
"bigquery": (
"datacontract export bigquery orders.odcs.yaml --schema-name orders --server bigquery --output orders.bigquery.json",
"json",
None,
),
"dbml": ("datacontract export dbml orders.odcs.yaml --output orders.dbml", "text", None),
"go": ("datacontract export go orders.odcs.yaml --output orders.go", "go", None),
"spark": ("datacontract export spark orders.odcs.yaml", "python", 34),
"sqlalchemy": ("datacontract export sqlalchemy orders.odcs.yaml --output orders_models.py", "python", None),
"iceberg": (
"datacontract export iceberg orders.odcs.yaml --schema-name orders --output orders.iceberg.json",
"json",
None,
),
"sodacl": ("datacontract export sodacl orders.odcs.yaml --output sodacl.yaml", "yaml", 32),
"great-expectations": (
"datacontract export great-expectations orders.odcs.yaml --schema-name orders",
"json",
None,
),
"data-caterer": ("datacontract export data-caterer orders.odcs.yaml", "yaml", None),
"dcs": ("datacontract export dcs orders.odcs.yaml --output datacontract.yaml", "yaml", 34),
"dqx": ("datacontract export dqx user_interactions.odcs.yaml --schema-name user_interactions", "yaml", None),
}
# The hand-written example block to replace on the first run (before markers exist):
# the command fence, the "Running this against …" sentence, and the output fence.
FALLBACK = re.compile(
r"```bash\n.*?\n```\n\nRunning this against the [^\n]*\n\n```[a-z-]*\n.*?\n```",
re.DOTALL,
)
def main() -> None:
for fmt, (command, lang, max_lines) in EXPORTS.items():
args = shlex.split(command)[1:]
contract = next(arg for arg in args if arg.endswith(".odcs.yaml"))
label = contract.removesuffix(".odcs.yaml")
with tempfile.TemporaryDirectory() as tmp:
shutil.copytree(REPO / "examples" / label, tmp, dirs_exist_ok=True)
output = run_cli(args, cwd=tmp)
if "--output" in args:
output = (Path(tmp) / args[args.index("--output") + 1]).read_text().strip("\n")
if fmt == "dbml":
# Drop the "Generated at <date> by datacontract-cli version <x>"
# header so the committed example stays deterministic.
output = re.sub(r"^/\*\nGenerated at .*?\*/\n", "", output, flags=re.DOTALL)
url = f"{EXAMPLES_URL}/{label}/{contract}"
excerpt = bool(max_lines and len(output.split("\n")) > max_lines)
verb = "produces (excerpt)" if excerpt else "produces"
block = (
f"```bash\n{command}\n```\n\n"
f"Running this against the [example `{label}` contract]({url}) {verb}:\n\n"
f"{fence(lang, output, max_lines)}"
)
upsert(DOCS / "exports" / f"{fmt}.md", block, FALLBACK)
print(f"ok exports/{fmt}.md{' (excerpt)' if excerpt else ''}")
if __name__ == "__main__":
main()