def run(*, path: str, disable_docstring) -> None: d = loading.loadfile(path) m = Module() a = Accessor(d, m, disable_docstring=disable_docstring) m.import_("typing", as_="t") m.sep() m.stmt("AnyService = t.Any # TODO") m.stmt("AnyResource = t.Any # TODO") m.sep() for rname, resource in a.resources.items(): with m.class_(titleize(rname), ""): with m.method("__init__", "resource: AnyResource"): m.stmt("self.internal = resource") m.stmt("# methods") for mname, method, params in a.iterate_methods(resource): with m.method(mname, params): a.emit_docstring(method["description"]) m.stmt( f"""# {method["httpMethod"]}: {method["flatPath"]}""") m.stmt(f"""# id: {method["id"]}""") m.stmt(f"return self.internal.{mname}({params})") m.stmt("# nested resources") for srname, subresource in a.iterate_nested_resources(resource): with m.method(srname): m.stmt(f"return self.internal.{srname}({params})") # m.stmt("# nested resources") # for mname, subresource in resource.get("resources", {}).items(): # params = LParams() # for is_positional, (pname, parameter) in itertools.zip_longest(subresource.get("parameterOrder", []), subresource.get("parameters", {}).items()): # if is_positional: # params.append(pname) # TODO type: # else: # params[pname] = None # TODO type: # with m.method(mname, params): # docstring(subresource["description"]) # m.stmt(f"""# id: {subresource["id"]}""") # m.stmt(f"return self.{mname}({params})") with m.class_("Service"): with m.method("__init__", "service: AnyService"): m.stmt("self.internal = service") for rname in a.resources.keys(): with m.method(rname, return_type=titleize(rname)): m.stmt(f"return {titleize(rname)}(self.internal.{rname}())") with m.def_("build", "*args", "**kwargs", return_type="Service"): m.stmt("# TODO: use the signature of googleapiclient.discovery.build") m.submodule().from_("googleapiclient.discovery", "build") m.stmt( f"return Service(build({a.name!r}, {a.version!r}, *args, **kwargs))" ) print(m)
def emit(ctx: Context): from prestring.python import Module m = Module() for name, cls in ctx.types.items(): with m.class_(name): # TODO: omit class inheritance for field_name, field_type in t.get_type_hints(cls).items(): # TODO: to pytype m.stmt(f"{field_name}: {field_type.__name__}") return m
from prestring.python import Module from prestring.utils import LParams m = Module() with m.class_("A0"): params0 = LParams() with m.method("f0", params0): m.stmt("pass") with m.class_("A1"): params1 = LParams() with m.method("f1", params1): m.stmt("pass") params1.append_tail("*args") print(m) # class A0: # def f0(self): <- this (not f0(self,)) # pass # class A1: # def f1(self, *args): # pass
import inspect from yaml.constructor import Constructor from prestring.python import Module m = Module() m.from_("yaml.constructor", "Constructor") m.sep() with m.class_("WrappedConstructor", "Constructor"): with m.def_("wrap", "self", "path", "name", "node", "r"): with m.if_("r is None"): m.stmt("return r") m.stmt('# print("@", id(r), repr(r))') m.stmt("mem[id(r)] = node") m.stmt("return r") seen = set() for cls in Constructor.mro(): for name, attr in cls.__dict__.items(): if name in seen: continue seen.add(name) if name.startswith("construct_") and callable(attr): sigs = inspect.signature(attr) m.stmt("def {}{}:", name, sigs) with m.scope(): args = [] for v in sigs.parameters.values(): if v.name == "self": continue if v.default is inspect._empty: args.append(str(v))
val: int, default: int = 0, *, nickname: t.Optional[str] = None, debug: bool = False, **metadata: t.Optional[t.Any], ) -> None: pass m = Module() m.toplevel = m.submodule(import_unique=True) m.sep() spec = fnspec(f) with m.class_("F"): for name, typ, kind in spec.parameters: if typ.__module__ != "builtins": m.toplevel.import_(typ.__module__) info = typeinfo(typ) rhs = spec.type_str_of(info.normalized) if info.is_optional: rhs = LazyFormat("typing.Optional[{}]", rhs) if kind == "var_kw": rhs = LazyFormat("typing.Dict[str, {}]", rhs) elif kind == "var_args": rhs = LazyFormat("typing.List[{}]", rhs) elif kind == "kw_defaults" or kind == "args_defaults": rhs = LazyFormat("{} = {}", rhs, spec.default_of(name))
*, nickname: t.Optional[str] = None, debug: bool = False, **metadata: t.Optional[t.Any], ) -> None: pass m = Module() m.toplevel = m.submodule(import_unique=True) m.sep() # todo: alias for fn in [f, g]: spec = fnspec(fn) with m.class_(pascalcase(spec.name)): for name, typ, kind in spec.parameters: if typ.__module__ != "builtins": m.toplevel.import_(typ.__module__) info = typeinfo(typ) rhs = spec.type_str_of(info.normalized) if info.is_optional: rhs = LazyFormat("typing.Optional[{}]", rhs) if kind == "var_kw": rhs = LazyFormat("typing.Dict[str, {}]", rhs) elif kind == "var_args": rhs = LazyFormat("typing.List[{}]", rhs) elif kind == "kw_defaults" or kind == "args_defaults": rhs = LazyFormat("{} = {}", rhs, spec.default_of(name))
def Person(m: Module, name: str) -> Module: # todo: import with m.class_(name, "BaseModel"): m.stmt("name: str") m.stmt("age : int = 0") return m
def gen_class_code(name: str, fields: t.List[V]) -> str: m = Module() with m.class_(name): _gen_init(m, name=name, fields=fields) _gen_repr(m, name=name, fields=fields) return str(m)
d = loading.loadfile(path / ("data/sqs/2012-11-05/service-2.json")) """ operations: <name>: name: <> input: {"shapee": <>} output: {"shape": <>, "resultWrapper": <>} errors: {} documentation """ m = Module() m.from_("__future__").import_("annotations") m.sep() m.stmt("# operations") with m.class_("SQS"): for name, sd in d["operations"].items(): with m.def_( name, f"input: {sd['input']['shape']}", return_type=sd["output"]["shape"] if "output" in sd else None, ): m.stmt("...") m.stmt("# shapes") with m.class_("SQS"): for name, sd in d["shapes"].items(): with m.class_(name): # structure, type m.stmt("pass") print(m)
import typing as t from prestring.python import Module from prestring.naming import titleize from monogusa.web.codegen._fnspec import fnspec def hello(name: str, *, age: int, nickname: t.Optional[str] = None) -> t.Dict[str, t.Any]: pass m = Module() fn = hello spec = fnspec(fn) with m.class_(titleize(fn.__name__)): if len(spec.keyword_arguments) == 0: m.stmt("pass") for name, typ, kind in spec.parameters: if kind.endswith("defaults"): m.stmt("{}: {} = {}", name, spec.type_str_of(typ), spec.default_str_of(name)) else: m.stmt("{}: {}", name, spec.type_str_of(typ)) print(m)
from prestring.python import Module m = Module() dataclasses = m.import_("dataclasses") m.stmt("@{}", dataclasses.dataclass) with m.class_("Person") as Person: m.stmt("name: str") m.stmt("age: int") m.stmt(Person(name="foo", age=20)) print(m)
def Person(m: Module) -> None: with m.class_("Person"): m.stmt("name: str")
from prestring.python import Module # 何が嫌だったのかを整理してみるか。 m = Module() # こういう感じでクラス定義をするんだけれど。このクラスを利用する関数を書きづらい。 with m.class_("Person"): m.stmt("name: str") # ここで "Person" って何? importされているの?と感じてしまう。 with m.def_("print_name", "p: Person"): m.stmt("print(p.name)") # そしてここで定義したprint_name()も利用できない。何がおきているかと言うと。 # # - "Person"というクラス定義を生成する記述は値ではないので持ち運べない # - "print_name"という関数定義を生成する記述は値ではないので持ち運べない # # どれも値として扱えないことが問題? 例えば以下の様に関数で包むのはどうだろう。 def Person(m: Module) -> None: with m.class_("Person"): m.stmt("name: str") # 何が嫌なのかと言えば、関数名とクラス名を二度書かないといけないと感じる点。 # ただ今度は値として持つことができる。 # 次にやりたいことはなんだろう?その値を使ってのコードとはなんのことだろう? #
from prestring.python import Module from prestring.utils import LParams m = Module() with m.class_("A0"): params0 = LParams() with m.method("f0", params0): m.stmt("pass") with m.class_("A1"): params1 = LParams() with m.method("f1", params1): m.stmt("pass") params1.append_tail("*args") print(m) # class A0: # def f0(self): <- this (not f0(self,)) # pass # class A1: # def f1(self, *args): # pass
def emit(filename: str, *, use_fullname: bool = False) -> None: from dictknife import loading from prestring.python import Module from detector import detect, Object, TypeInfo, ZERO, generate_annotations name_map = {} m = Module() m.toplevel = m.submodule() m.sep() def _pytype(info: TypeInfo, *, m=m, aliases: t.Dict[str, str] = {"typing": "t"}): if info is ZERO: module = aliases.get(t.__name__) or t.__name__ return f"{module}.Any" if hasattr(info, "base"): module = aliases.get(info.base.__module__) or info.base.__module__ m.toplevel.import_(info.base.__module__, aliases.get(info.base.__module__)) if info.base is t.Optional: return f"{module}.Optional[{_pytype(info.item)}]" elif info.base is t.List: return f"{module}.List[{_pytype(info.item)}]" elif hasattr(info, "type"): module = aliases.get(info.type.__module__) or info.type.__module__ prefix = module + "." if module == "builtins": prefix = "" else: m.toplevel.import_(info.type.__module__, aliases.get(info.type.__module__)) return prefix + info.type.__name__ try: return name_map[id(info)] except KeyError: # FIXME: bug import sys print(f"something wrong: {info}", file=sys.stderr) return "UNKNOWN" d = loading.loadfile(filename) result = detect(d) annotations = generate_annotations(result, use_fullname=use_fullname, toplevel_name="toplevel") for info in result.history: if isinstance(info, Object): metadata = annotations["/".join(info.path)] name = metadata.get("after", metadata["before"])["name"] name_map[id(info)] = name m.stmt(f"# from: {'/'.join(info.path)}") with m.class_(name): for name, sub_info in info.props.items(): m.stmt("{}: {}", name, _pytype(sub_info)) print(m)