From 85f83f46d543a37d292ad07bd1508130a500598c Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Thu, 18 Mar 2021 13:02:16 +0100 Subject: [PATCH] [Script] Enhanced and added future compatibility for API export. * Python >= 3.9 required for API export. --- flaschengeist/plugins/pricelist/models.py | 20 +++--- run_flaschengeist | 79 ++++++++++++++--------- 2 files changed, 58 insertions(+), 41 deletions(-) diff --git a/flaschengeist/plugins/pricelist/models.py b/flaschengeist/plugins/pricelist/models.py index 5adab4d..2a7e182 100644 --- a/flaschengeist/plugins/pricelist/models.py +++ b/flaschengeist/plugins/pricelist/models.py @@ -104,13 +104,13 @@ class DrinkPriceVolume(db.Model, ModelSerializeMixin): __tablename__ = "drink_price_volume" id: int = db.Column("id", db.Integer, primary_key=True) - volume: float = db.Column(db.Numeric(precision=5, scale=2, asdecimal=False)) - prices: [DrinkPrice] = db.relationship(DrinkPrice, back_populates="volume", cascade="all,delete,delete-orphan") - ingredients: Union[DrinkIngredient, ExtraIngredient] = [] - # TODO: Really protected or just not exported (e.g. name_)? - _ingredients: [Ingredient] = db.relationship("Ingredient", foreign_keys=Ingredient.volume_id) - drink_id = db.Column(db.Integer, db.ForeignKey("drink.id"), nullable=False) + volume: float = db.Column(db.Numeric(precision=5, scale=2, asdecimal=False)) + ingredients: list[DrinkIngredient, ExtraIngredient] = [] + + prices: list[DrinkPrice] = db.relationship(DrinkPrice, back_populates="volume", cascade="all,delete,delete-orphan") + # TODO: Really protected or just not exported (e.g. name_)? + _ingredients: list[Ingredient] = db.relationship("Ingredient", foreign_keys=Ingredient.volume_id) class Drink(db.Model, ModelSerializeMixin): @@ -127,8 +127,8 @@ class Drink(db.Model, ModelSerializeMixin): cost_price_pro_volume: Optional[float] = db.Column(db.Numeric(precision=5, scale=3, asdecimal=False)) cost_price_package_netto: Optional[float] = db.Column(db.Numeric(precision=5, scale=3, asdecimal=False)) - tags: list[Tag] = db.relationship("Tag", secondary=drink_tag_association, cascade="save-update, merge") - type_id_ = db.Column("type_id", db.Integer, db.ForeignKey("drink_type.id")) - type: DrinkType = db.relationship("DrinkType") + _type_id = db.Column("type_id", db.Integer, db.ForeignKey("drink_type.id")) - volumes: [DrinkPriceVolume] = db.relationship(DrinkPriceVolume) + tags: list[Tag] = db.relationship("Tag", secondary=drink_tag_association, cascade="save-update, merge") + type: DrinkType = db.relationship("DrinkType", foreign_keys=[_type_id]) + volumes: list[DrinkPriceVolume] = db.relationship(DrinkPriceVolume) diff --git a/run_flaschengeist b/run_flaschengeist index 522fbd0..1e0e9c5 100644 --- a/run_flaschengeist +++ b/run_flaschengeist @@ -1,7 +1,9 @@ #!/usr/bin/python3 +from __future__ import annotations # TODO: Remove if python requirement is >= 3.10 import inspect import argparse import sys + import pkg_resources from flaschengeist.config import config @@ -27,7 +29,8 @@ class PrefixMiddleware(object): class InterfaceGenerator: known = [] classes = {} - mapper = {"str": "string", "int": "number", "float": "number", "date": "Date", "datetime": "Date", "NoneType": "null"} + mapper = {"str": "string", "int": "number", "float": "number", "date": "Date", "datetime": "Date", + "NoneType": "null"} def __init__(self, namespace, filename): self.basename = "" @@ -36,51 +39,65 @@ class InterfaceGenerator: self.this_type = None def pytype(self, cls): - if isinstance(cls, list): - return "", "Array<{}>".format(self.pytype(cls[0])[1]) - if sys.version_info >= (3, 8): - import typing + a = self._pytype(cls) + print(f"{cls} -> {a}") + return a - if isinstance(cls, typing.ForwardRef): - return "", "this" if cls.__forward_arg__ == self.this_type else cls.__forward_arg__ - if typing.get_origin(cls) == typing.Union: - types = typing.get_args(cls) - if len(types) == 2 and types[-1] is type(None): - return "?", self.pytype(types[0])[1] - else: - return "", "|".join([self.pytype(pt)[1] for pt in types]) - if hasattr(cls, "__name__"): - if cls.__name__ in self.mapper: - return "", self.mapper[cls.__name__] + def _pytype(self, cls): + import typing + origin = typing.get_origin(cls) + arguments = typing.get_args(cls) + + if origin is typing.ForwardRef: # isinstance(cls, typing.ForwardRef): + return "", "this" if cls.__forward_arg__ == self.this_type else cls.__forward_arg__ + if origin is typing.Union: + print(f"A1: {arguments[1]}") + if len(arguments) == 2 and arguments[1] is type(None): + return "?", self.pytype(arguments[0])[1] else: - return "", cls.__name__ + return "", "|".join([self.pytype(pt)[1] for pt in arguments]) + if origin is list: + return "", "Array<{}>".format("|".join([self.pytype(a_type)[1] for a_type in arguments])) + + name = cls.__name__ if hasattr(cls, "__name__") else cls if isinstance(cls, str) else None + if name is not None: + if name in self.mapper: + return "", self.mapper[name] + else: + return "", name print( - "WARNING: This python version might not detect all types (try >= 3.8). Could not identify >{}<".format(cls) + "WARNING: This python version might not detect all types (try >= 3.9). Could not identify >{}<".format(cls) ) return "?", "any" def walker(self, module): + if sys.version_info < (3, 9): + raise RuntimeError("Python >= 3.9 is required to export API") + import typing + if ( - inspect.ismodule(module[1]) - and module[1].__name__.startswith(self.basename) - and module[1].__name__ not in self.known + 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__") + 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__") ): self.this_type = module[0] - d = { - param: self.pytype(ptype) - for param, ptype in module[1].__annotations__.items() - if not param.startswith("_") and not param.endswith("_") - } + print("\n\n" + module[0] + "\n") + d = {} + for param, ptype in typing.get_type_hints(module[1], globalns=None, localns=None).items(): + if not param.startswith("_") and not param.endswith("_"): + print(f"{param} ::: {ptype}") + d[param] = self.pytype(ptype) + if len(d) == 1: key, value = d.popitem() self.classes[module[0]] = value[1]