From 56a0a8e06a8ac5d8e57f10c3418fd0a87c344042 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Mon, 2 Nov 2020 04:38:38 +0100 Subject: [PATCH] [System][Script] Improved run_flaschengeist to also export plugin models --- flaschengeist/plugins/__init__.py | 3 +- flaschengeist/plugins/balance/__init__.py | 6 +- run_flaschengeist | 125 +++++++++++++--------- 3 files changed, 78 insertions(+), 56 deletions(-) diff --git a/flaschengeist/plugins/__init__.py b/flaschengeist/plugins/__init__.py index d8b779a..7e13aca 100644 --- a/flaschengeist/plugins/__init__.py +++ b/flaschengeist/plugins/__init__.py @@ -17,7 +17,8 @@ Args: class Plugin: - """Base class for all Plugins""" + """Base class for all Plugins + If your class uses custom models add a static property called ``models``""" def __init__(self, config=None, blueprint=None, permissions=[]): """Constructor called by create_app diff --git a/flaschengeist/plugins/balance/__init__.py b/flaschengeist/plugins/balance/__init__.py index 0d29b26..1b89504 100644 --- a/flaschengeist/plugins/balance/__init__.py +++ b/flaschengeist/plugins/balance/__init__.py @@ -15,13 +15,15 @@ from flaschengeist.decorator import login_required from flaschengeist.controller import userController from flaschengeist.plugins import Plugin, update_user_hook -from . import balance_controller, permissions +from . import balance_controller, permissions, models MonkeyPatch.patch_fromisoformat() balance_bp = Blueprint("balance", __name__) class BalancePlugin(Plugin): + models = models + def __init__(self, config): super().__init__(blueprint=balance_bp, permissions=permissions.permissions) @@ -34,8 +36,6 @@ class BalancePlugin(Plugin): def install(self): from flaschengeist.database import db - from .models import Transaction - db.create_all() diff --git a/run_flaschengeist b/run_flaschengeist index f9951e9..b9d7429 100644 --- a/run_flaschengeist +++ b/run_flaschengeist @@ -1,6 +1,70 @@ #!/usr/bin/python3 import inspect import argparse +import pkg_resources + + +class InterfaceGenerator: + known = [] + classes = {} + mapper = {"str": "string", "int": "number", "float": "number", "datetime": "Date"} + + def __init__(self, namespace, filename): + self.basename = "" + self.namespace = namespace + self.filename = filename + + def pytype(self, cls): + if isinstance(cls, list): + return "", "Array<{}>".format(self.pytype(cls[0])[1]) + # if typing.get_origin(cls) is typing.Optional: + # return "?", pytype(typing.get_args(cls)[1]) + if hasattr(cls, "__name__"): + if cls.__name__ in self.mapper: + return "", self.mapper[cls.__name__] + else: + return "", cls.__name__ + return "?", "any" + + def walker(self, module): + if inspect.ismodule(module[1]) and module[1].__name__.startswith(self.basename) and module[1].__name__ not in self.known: + self.known.append(module[1].__name__) + for cls in inspect.getmembers(module[1], lambda x: inspect.isclass(x) or inspect.ismodule(x)): + self.walker(cls) + elif ( + inspect.isclass(module[1]) + and module[1].__module__.startswith(self.basename) + and module[0] not in self.classes + and not module[0].startswith("_") + and hasattr(module[1], "__annotations__") + ): + d = { + param: self.pytype(ptype) + for param, ptype in module[1].__annotations__.items() + if not param.startswith("_") and not param.endswith("_") + } + if len(d) == 1: + key, value = d.popitem() + self.classes[module[0]] = value[1] + else: + self.classes[module[0]] = d + + def run(self, models): + self.basename = models.__name__ + self.walker(("models", models)) + + def write(self): + with open(self.filename, "w") as file: + file.write("declare namespace {} {{\n".format(self.namespace)) + for cls, params in self.classes.items(): + if isinstance(params, str): + file.write("\ttype {} = {};\n".format(cls, params)) + else: + file.write("\tinterface {} {{\n".format(cls)) + for name in params: + file.write("\t\t{}{}: {};\n".format(name, *params[name])) + file.write("\t}\n") + file.write("}\n") def install(arguments): @@ -29,62 +93,18 @@ def run(arguments): def export(arguments): import flaschengeist.models as models - - known = [] - classes = {} - - def pytype(cls): - if isinstance(cls, list): - return "", "Array<{}>".format(pytype(cls[0])[1]) - # if typing.get_origin(cls) is typing.Optional: - # return "?", pytype(typing.get_args(cls)[1]) - mapper = {"str": "string", "int": "number", "float": "number", "datetime": "Date"} - if hasattr(cls, "__name__"): - if cls.__name__ in mapper: - return "", mapper[cls.__name__] - else: - return "", cls.__name__ - return "?", "any" - - def walker(mod): - if inspect.ismodule(mod[1]) and mod[1].__name__.startswith(models.__name__) and mod[1].__name__ not in known: - known.append(mod[1].__name__) - for cls in inspect.getmembers(mod[1], lambda x: inspect.isclass(x) or inspect.ismodule(x)): - walker(cls) - elif ( - inspect.isclass(mod[1]) - and mod[1].__module__.startswith(models.__name__) - and mod[0] not in classes - and not mod[0].startswith("_") - and hasattr(mod[1], "__annotations__") - ): - d = { - param: pytype(ptype) - for param, ptype in mod[1].__annotations__.items() - if not param.startswith("_") and not param.endswith("_") - } - if len(d) == 1: - key, value = d.popitem() - classes[mod[0]] = value[1] - else: - classes[mod[0]] = d - from flaschengeist.app import create_app app = create_app() with app.app_context(): - walker(("models", models)) - with open(arguments.file, "w") as file: - file.write("declare namespace {} \{\n".format(arguments.namespace)) - for cls, params in classes.items(): - if isinstance(params, str): - file.write("\ttype {} = {};\n".format(cls, params)) - else: - file.write("\tinterface {} {{\n".format(cls)) - for name in params: - file.write("\t\t{}{}: {};\n".format(name, *params[name])) - file.write("\t}\n") - file.write("}\n") + gen = InterfaceGenerator(arguments.namespace, arguments.file) + gen.run(models) + if arguments.plugins: + for entry_point in pkg_resources.iter_entry_points("flaschengeist.plugin"): + plg = entry_point.load() + if hasattr(plg, "models"): + gen.run(plg.models) + gen.write() if __name__ == "__main__": @@ -105,6 +125,7 @@ if __name__ == "__main__": parser_export.set_defaults(func=export) parser_export.add_argument("--file", help="Filename where to save", default="flaschengeist.d.ts") parser_export.add_argument("--namespace", help="Namespace of declarations", default="FG") + parser_export.add_argument("--plugins", help="Also export plugins", action="store_true") args = parser.parse_args() args.func(args)