def load_config(m: Module, filename: Symbol) -> Symbol: LoadConfig = Symbol("LoadConfig") # todo: import config = Symbol("config") err = Symbol("err") m.stmt("{}, {} := {}", config, err, LoadConfig(filename)) m.stmt("if {} != nil {{", err) with m.scope(): m.stmt("return err") m.stmt("}") return config
def main(mod: ModuleType) -> None: def _guess_kind( cls: t.Any, *, _builtins=set(id(v) for v in sys.modules["builtins"].__dict__.values()), ) -> t.Optional[Kind]: # is module? if hasattr(cls, "__loader__"): return None # is typed user_defined_type class or callable? if not hasattr(cls, "__name__"): return None if id(cls) in _builtins: return None if not callable(cls): return None if inspect.isclass(cls): return "object" return None w = runtime.get_walker( mod, recursive=True, aggressive=True, _guess_kind=_guess_kind ) m = Module() o = sys.stderr if bool(os.environ.get("DEBUG", "")) else sys.stdout ctx = Context(w, is_minimum=bool(os.environ.get("MINIMUM", ""))) print(emit(ctx, m=m), file=o)
def gen(data: Data, *, m=None, indent=" "): m = m or Module(indent=indent) m.stmt("definitions:") with m.scope(): m.stmt(f"{data.en_name}:") with m.scope(): m.stmt(f"description: {data.ja_name}") m.stmt("type: string") m.stmt("enum:") with m.scope(): for c in data.categories: m.stmt(f"- {c.en_name}") m.stmt("x-ja-enum:") with m.scope(): for c in data.categories: m.stmt(f"- {c.ja_name}") m.sep() m.stmt("parameters:") with m.scope(): m.stmt(f"{data.en_name}:") with m.scope(): m.stmt(f"name: {data.en_name}") m.stmt("in: query") m.stmt(f"description: {data.ja_name}") m.stmt("type: string") m.stmt("enum:") with m.scope(): for c in data.categories: m.stmt(f"- {c.en_name}") m.stmt("x-ja-enum:") with m.scope(): for c in data.categories: m.stmt(f"- {c.ja_name}") return m
def transform(source: str, *, indent="\t"): from prestring.text import Module m = Module(indent=indent) m.stmt("from {} import {}", m.__class__.__module__, m.__class__.__name__) m.stmt("m = Module(indent={!r})", indent) m = _transform(source, m=m, indent=indent) m.stmt("print(m)") return m
def gen(data: Enum, *, m=None, indent=" "): m = m or Module(indent=indent) m.stmt("definitions:") with m.scope(): data.gen_definition(m=m) m.sep() m.stmt("parameters:") with m.scope(): data.gen_parameter(m=m) return m
def run(m: Module) -> Module: filename = Symbol("filename") m.stmt("func Run({}: string) error {{", filename) # todo: with type with m.scope(): config = load_config(m, filename) m.stmt("return doSomething({})", config) # or "return nil" m.stmt("}") return m
def gen(fn: t.Callable[..., t.Any], *, m=None, indent="\t"): m = m or Module(indent=indent) m.stmt("package main") m.sep() m.stmt("import (") with m.scope(): m.stmt('"flag"') m.stmt('"fmt"') m.stmt('"log"') m.stmt(")") m.sep() spec = inspect.getfullargspec(fn) m.stmt("var (") with m.scope(): for name, val in spec.annotations.items(): if name == "return": continue m.stmt("""{} = {},""", name, resolve(name, val)) m.stmt(")") m.sep() m.stmt("func main() {") with m.scope(): m.stmt("flag.Parse()") m.stmt("if err := run(); err != nil {") with m.scope(): m.stmt('log.Fatalf("+%v", err)') m.stmt("}") m.stmt("}") m.sep() m.stmt("func run() error {") with m.scope(): for name in spec.annotations.keys(): if name == "return": continue m.stmt("{} := *{}", name, name) m.sep() fn(m) m.stmt("return nil") m.stmt("}") return m
def gen(typ: t.Type[t.Any], *, m=None, indent=" "): m = m or Module(indent=indent) def_name = get_name(typ) def_description = get_description(typ) m.stmt("definitions:") with m.scope(): m.stmt(f"{def_name}:") with m.scope(): m.stmt(f"description: {def_description}") m.stmt("type: string") m.stmt("enum:") with m.scope(): for name in get_args(typ): m.stmt(f"- {name}") metadata = get_metadata(typ) if Labels in metadata: m.stmt("x-ja-enum:") with m.scope(): for name in metadata[Labels].labels: m.stmt(f"- {name}") m.sep() m.stmt("parameters:") with m.scope(): m.stmt(f"{def_name}:") with m.scope(): m.stmt(f"name: {name}") m.stmt("in: query") m.stmt(f"description: {def_description}") m.stmt("type: string") m.stmt("enum:") with m.scope(): for name in get_args(typ): m.stmt(f"- {name}") metadata = get_metadata(typ) if Labels in metadata: m.stmt("x-ja-enum:") with m.scope(): for name in metadata[Labels].labels: m.stmt(f"- {name}") return m
def gen(typ: t.Type[t.Any], *, m=None, indent="\t"): m = m or Module(indent=indent) m.stmt("package main") m.sep() m.stmt("import (") with m.scope(): m.stmt('"flag"') m.stmt('"fmt"') m.stmt('"log"') m.stmt(")") m.sep() hints = t.get_type_hints(typ) m.stmt("var (") with m.scope(): for name, val in hints.items(): m.stmt("""{} = {},""", name, resolve(name, val)) m.stmt(")") m.sep() m.stmt("func main() {") with m.scope(): m.stmt("flag.Parse()") m.stmt("if err := run(); err != nil {") with m.scope(): m.stmt('log.Fatalf("+%v", err)') m.stmt("}") m.stmt("}") m.sep() m.stmt("func run() error {") with m.scope(): for name in hints.keys(): m.stmt("{} := *{}", name, name) m.sep() m = typ.emit(m) m.stmt("return nil") m.stmt("}") return m
from prestring.text import Module m = Module(indent='\t') m.stmt('package main') m.sep() m.stmt('import (') with m.scope(): m.stmt('"log"') m.stmt('"net/http"') m.stmt('"os"') m.sep() m.stmt('"github.com/gin-gonic/gin"') m.stmt('_ "github.com/heroku/x/hmetrics/onload"') m.stmt(')') m.sep() m.stmt('func main() {') with m.scope(): m.stmt('port := os.Getenv("PORT")') m.sep() m.stmt('if port == "" {') with m.scope(): m.stmt('log.Fatal("$PORT must be set")') m.stmt('}') m.sep() m.stmt('router := gin.New()') m.stmt('router.Use(gin.Logger())') m.stmt('router.LoadHTMLGlob("templates/*.tmpl.html")') m.stmt('router.Static("/static", "static")') m.sep() m.stmt('router.GET("/", func(c *gin.Context) {') with m.scope(): m.stmt('c.HTML(http.StatusOK, "index.tmpl.html", nil)')
def emit_node(ctx: Context, cls: t.Type[t.Any], *, m: Module) -> Module: w = ctx.walker name = _get_type_name(cls) labelname = name if cls.mro()[1] == tx.Protocol: labelname = f"<<Interface>> {name}" methods = [] attributes = [] # todo: cache? seen = set() for parent_cls in _get_mro(cls): for attrname, _, _ in w.walk_fields(parent_cls, ignore_private=True): seen.add(attrname) for attrname, info, metadata in w.walk_fields(cls, ignore_private=True): if attrname in seen: continue if info.user_defined_type is not None: continue attributes.append(f"+ {attrname}: {_get_type_name(info.type_)}") # todo: walk_methods(cls, ignore_private=True) for attrname, attr in cls.__dict__.items(): if attrname.startswith("_"): continue if not callable(attr): continue # for inspect.signature() with PEP563 attr.__annotations__ = t.get_type_hints(attr) methods.append(f"+ {_get_method_signature(attrname, attr)}") m.stmt(f"{name} [") with m.scope(): m.stmt('shape = "none"') m.stmt(f'URL = "#{_get_html_id(name)}"') if ctx.is_minimum: m.stmt( f'label = <<TABLE BGCOLOR="gray95" BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="6" ><TR><TD>{labelname}</TD></TR></TABLE>>' ) else: m.stmt( f'label = <<TABLE BGCOLOR="gray95" BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="6" ><TR><TD>{labelname}</TD></TR><TR><TD ALIGN="LEFT" BALIGN="LEFT">{"<BR/>".join(attributes)}</TD></TR><TR><TD ALIGN="LEFT" BALIGN="LEFT">{"<BR/>".join(methods)}</TD></TR></TABLE>>' ) m.stmt("]") m.sep() return m
from prestring.text import Module from prestring.codeobject import Symbol def run(m: Module) -> Module: filename = Symbol("filename") m.stmt("func Run({}: string) error {{", filename) # todo: with type with m.scope(): config = load_config(m, filename) m.stmt("return doSomething({})", config) # or "return nil" m.stmt("}") return m def load_config(m: Module, filename: Symbol) -> Symbol: LoadConfig = Symbol("LoadConfig") # todo: import config = Symbol("config") err = Symbol("err") m.stmt("{}, {} := {}", config, err, LoadConfig(filename)) m.stmt("if {} != nil {{", err) with m.scope(): m.stmt("return err") m.stmt("}") return config print(run(Module(indent="\t")))
def emit(targets, *, resources): m = Module(indent=" ") w = get_walker([]) for i, cls in enumerate(targets): name = resources[id(cls)] # xxx: m.stmt("resource {} {} {{", to_val(get_name(cls)), to_val(name)) with m.scope(): for name, info, metadata in w.for_type(cls).walk(): value = metadata["default"] if value is None: continue m.stmt("{} = {}", name, to_val(value)) m.stmt("}") if i < len(targets) - 1: m.sep() return m
def emit(deps: t.Dict[str, t.List[str]], *, filename: t.Optional[str] = None) -> str: from prestring.text import Module deps = { name: { "task": x["task"].rsplit(".", 1)[-1], "depends": sorted(x["depends"]) } for name, x in deps.items() } suffix = "" if filename is None else f" -f {filename}" m = Module(indent="\t") m.stmt(f"DEP ?= {' '.join(deps.keys())}") m.stmt( f"PRE ?= {' '.join(['.pre/' + k.replace('/', '__') for k in deps.keys()])}" ) m.sep() m.stmt(f'CONT ?= PRE=$< DEP="" $(MAKE) _gen{suffix}') m.stmt("BULK_ACTION = .pre/bulk.action") m.sep() m.stmt("# goal task") m.stmt("default:") with m.scope(): m.stmt(f'@CONT="exit 0" $(MAKE) _gen{suffix}') m.sep() m.stmt("_gen: .pre $(DEP)") with m.scope(): m.stmt("@echo '**' $(PRE) '**' > /dev/stderr") m.stmt( "( $(foreach p,$(PRE),{ test $(p) -nt $(subst __,/,$(patsubst .pre/%,%,$(p))) && cat $(p); }; ) ) | sort | uniq > $(BULK_ACTION) || exit 0" ) m.stmt( """test -n "$$(cat $(BULK_ACTION))" && NOCHECK=1 python definitions.py $$(cat $(BULK_ACTION) | tr '\\n' ' ') || exit 0""" ) m.sep() m.stmt("# .pre files (sentinel)") for name, metadata in deps.items(): task = metadata["task"].split(".", 1)[-1] args = metadata["depends"] pre_file = f".pre/{name.replace('/', '__')}" m.stmt(f"{pre_file}: {' '.join(args)}") with m.scope(): m.stmt(f'echo "generate {task} -" > $@') m.sep() m.stmt("# actual dependencies") for name, metadata in deps.items(): task = metadata["task"].split(".", 1)[-1] args = metadata["depends"] pre_file = f".pre/{name.replace('/', '__')}" m.stmt(f"{name}: {pre_file}") with m.scope(): m.stmt(f"@$(CONT)") m.sep() m.stmt(".pre:") with m.scope(): m.stmt("mkdir -p $@") return str(m)
m.stmt("flag.Parse()") m.stmt("if err := run(); err != nil {") with m.scope(): m.stmt('log.Fatalf("+%v", err)') m.stmt("}") m.stmt("}") m.sep() m.stmt("func run() error {") with m.scope(): for name in spec.annotations.keys(): if name == "return": continue m.stmt("{} := *{}", name, name) m.sep() fn(m) m.stmt("return nil") m.stmt("}") return m def use(*, name: str, age: int) -> None: def gen(m): pass return gen m = Module(indent="\t") print(gen(use, m=m))
def emit(cls, m: Module) -> Module: m.stmt("// do something") return m
def graph(d, pkg): import queue from prestring.text import Module m = Module() q = queue.Queue() q.put(pkg) seen = set() m.stmt("digraph {") with m.scope(): while not q.empty(): pkg = q.get() if pkg in seen: continue seen.add(pkg) m.stmt(f"// {pkg}") for next_pkg in d[pkg]: m.stmt(f"{pkg.replace('/', '_')} -> {next_pkg.replace('/', '_')}") q.put(next_pkg) m.sep() m.stmt("}") print(m)
return m.scope() def parameters(self, *, m=None): m = m or self.m m.stmt("parameters:") return m.scope() if __name__ == "__main__": season = Enum( name="season", description="四季", choices=[ Choice(label="春", name="spring"), Choice(label="夏", name="summer"), Choice(label="秋", name="autumn"), Choice(label="冬", name="winter"), ], ) m = Module(indent=" ") dsl = DSL(m) with dsl.definitions(): dsl.enum.definition(season) m.sep() with dsl.parameters(): dsl.enum.parameter(season) print(m)
def run() -> Module: r = get_resolver() m = Module(indent="\t") classes = [Person, Person2] for item in walk(classes): m.stmt(f"type {goname(item.cls.__name__)} struct {{") with m.scope(): for name, typeinfo, _metadata in item.fields: metadata = t.cast(Metadata, _metadata) if metadata.get("default") == MISSING: metadata.pop("default") try: gotype = r.resolve_gotype( typeinfo.normalized) # todo: pointer except KeyError: gotype = goname(typeinfo.normalized.__name__) if metadata.get("pointer", False): gotype = f"*{gotype}" if metadata.get("inline", False): m.append(gotype) else: m.append(f"{goname(name)} {gotype}") if metadata: m.stmt(f" // {metadata}") else: m.stmt("") m.stmt("}") m.sep() return m
def emit(ctx: Context, *, m: t.Optional[Module] = None) -> Module: w = ctx.walker m = m or Module() m.stmt("digraph G {") with m.scope(): # setup m.stmt("graph [") with m.scope(): m.stmt("compound = true") m.stmt("]") m.sep() m.stmt("node [") with m.scope(): m.stmt('shape = "record"') m.stmt("]") m.sep() m.stmt("edge [") with m.scope(): m.stmt('dir = "back"') m.stmt('arrowtail = "empty"') m.stmt("arrowsize = 0.65") m.stmt("]") m.sep() has_inheritance_types: t.List[t.Type[t.Any]] = [] relations: t.List[ t.Tuple[str, t.Type[t.Any], t.Type[t.Any]] ] = [] # name, from, to for cls in w.walk(): emit_node(ctx, cls, m=m) if len(_get_mro(cls)) > 0: has_inheritance_types.append(cls) for parent_cls in _get_mro(cls): w.append(parent_cls) # todo: cache? seen = set() for parent_cls in _get_mro(cls): for attrname, _, _ in w.walk_fields(parent_cls, ignore_private=True): seen.add(attrname) for attrname, info, metadata in w.walk_fields(cls, ignore_private=True): if attrname in seen: continue if info.user_defined_type is None: continue from_ = cls to = info.user_defined_type # todo: 1,0,*,? from_n = "" to_n = "" if info.is_container and info.container_type in ("list", "tuple"): to_n = "*" label = metadata.get("label") relations.append((attrname, from_, to, (from_n, to_n, label))) # emit link (inheritance) for cls in has_inheritance_types: direct_parent_cls = cls.mro()[1] if _is_interface(cls): m.stmt( f'{_get_type_name(direct_parent_cls)} -> {_get_type_name(cls)} [style="dashed"]' ) else: m.stmt(f"{_get_type_name(direct_parent_cls)} -> {_get_type_name(cls)}") # emit link (relation) if len(relations) > 0: m.sep() m.stmt("edge [") with m.scope(): m.stmt("constraint = false") m.stmt("minlen = 3") # todo: more styles m.stmt('arrowtail = "none"') # m.stmt('arrowtail = "normal"') # m.stmt('headlabel = "*"') # m.stmt('taillabel = "1"') m.stmt("]") for relname, from_, to, (from_n, to_n, label) in relations: attrs = [] if label is not None: attrs.append(f'label = "{label}"') if to_n: attrs.append(f'headlabel = "{to_n}"') if attrs: attrs_str = f" [{', '.join(attrs)}]" else: attrs_str = "" m.stmt(f"{_get_type_name(from_)} -> {_get_type_name(to)}{attrs_str}") m.stmt("}") return m
def emit2(targets, *, resources): m = Module(indent=" ") w = get_walker([]) m.stmt('include classpath("application.conf")') m.stmt("queues {") with m.scope(): for i, cls in enumerate(targets): name = resources[id(cls)] # xxx: m.stmt("{} {{", name) with m.scope(): for name, info, metadata in w.for_type(cls).walk(): value = metadata["default"] if value is None: continue if isinstance(value, int): m.stmt("{} = {} seconds", name, value) else: m.stmt("{} = {}", name, to_val(value)) m.stmt("}") if i < len(targets) - 1: m.sep() m.stmt("}") return m
from __future__ import annotations from prestring.text import Module from prestring.go import goname from egoist.go.resolver import get_resolver from walker import walk class Person: name: str age: int info: Info class Info: memo: str r = get_resolver() m = Module(indent="\t") for item in walk([Person]): m.stmt(f"type {goname(item.cls.__name__)} struct {{") with m.scope(): for name, typeinfo, metadata in item.fields: gotype = r.resolve_gotype(typeinfo.normalized) # todo: pointer m.stmt(f"{goname(name)} {gotype}") m.stmt("}") print(m)