90 lines
3.6 KiB
Python
90 lines
3.6 KiB
Python
"""
|
|
python3 -m docgen view <ir.json> [views...] [-o out.json]
|
|
|
|
IR -> a smaller IR. Views compose, applied in a fixed order: overview, the drops,
|
|
only, subtree, around, depth. Each produces a document that still validates.
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from .common import PROG, Abort, emit, read_json
|
|
|
|
|
|
def main(argv) -> int:
|
|
p = argparse.ArgumentParser(prog=f"{PROG} view")
|
|
p.add_argument("ir", type=Path)
|
|
p.add_argument("--output", "-o", type=Path)
|
|
p.add_argument("--overview", action="store_true",
|
|
help="The default view for this source type. Usually what you want.")
|
|
p.add_argument("--drop-stdlib", action="store_true", help="Remove stdlib externals.")
|
|
p.add_argument("--drop-builtins", action="store_true", help="Remove builtin externals.")
|
|
p.add_argument("--drop-external", action="store_true", help="Remove every unresolved name.")
|
|
p.add_argument("--only", action="append", default=[], help="Keep only this kind. Repeatable.")
|
|
p.add_argument("--drop", action="append", default=[], help="Remove this kind. Repeatable.")
|
|
p.add_argument("--subtree", help="Just this node id and its contents.")
|
|
p.add_argument("--around", help="This node id and its neighbours.")
|
|
p.add_argument("--hops", type=int, default=1)
|
|
p.add_argument("--depth", type=int, help="Collapse to this containment depth.")
|
|
p.add_argument("--split", action="store_true",
|
|
help="Write one document per subsystem into OUT/ (a directory).")
|
|
p.add_argument("--shape", action="store_true",
|
|
help="Report what this will look like, and write nothing.")
|
|
args = p.parse_args(argv)
|
|
|
|
from ..ir import check
|
|
from ..ops import filter as F
|
|
|
|
# Read without validating first: a view is often how an oversized document
|
|
# is made readable, and the result is validated before it is written.
|
|
ir = read_json(args.ir)
|
|
before = (len(ir["nodes"]), len(ir["edges"]))
|
|
|
|
if args.overview:
|
|
ir = F.overview(ir)
|
|
if args.drop_stdlib:
|
|
ir = F.drop_stdlib(ir)
|
|
if args.drop_builtins:
|
|
ir = F.drop_builtins(ir)
|
|
if args.drop_external:
|
|
ir = F.drop_external(ir)
|
|
if args.drop:
|
|
ir = F.drop_kinds(ir, args.drop)
|
|
if args.only:
|
|
ir = F.only_kinds(ir, args.only)
|
|
if args.subtree:
|
|
ir = F.subtree(ir, args.subtree)
|
|
if args.around:
|
|
ir = F.neighbourhood(ir, args.around, hops=args.hops)
|
|
if args.depth is not None:
|
|
ir = F.collapse_to_depth(ir, args.depth)
|
|
|
|
if args.shape:
|
|
for k, v in F.shape(ir).items():
|
|
print(f" {k:<14} {v}")
|
|
return 0
|
|
|
|
if args.split:
|
|
if not args.output:
|
|
raise Abort("--split needs -o DIRECTORY")
|
|
args.output.mkdir(parents=True, exist_ok=True)
|
|
for name, part in F.split(ir).items():
|
|
target = args.output / f"{name}.json"
|
|
target.write_text(json.dumps(part, indent=2) + "\n")
|
|
sh = F.shape(part)
|
|
print(f" {name:<16} {sh['nodes']:>4} nodes {sh['edges']:>4} edges -> {target}")
|
|
return 0
|
|
|
|
problems = check(ir)
|
|
if problems:
|
|
# A view that produces an invalid document is a bug in the view, and it
|
|
# must not be written out for an emitter to trip over later.
|
|
raise Abort(f"the view produced an invalid IR ({len(problems)}):\n "
|
|
+ "\n ".join(problems[:5]))
|
|
|
|
emit(json.dumps(ir, indent=2) + "\n", args.output,
|
|
f" {before[0]} nodes, {before[1]} edges -> "
|
|
f"{len(ir['nodes'])} nodes, {len(ir['edges'])} edges -> {args.output}")
|
|
return 0
|