[Script] Enhanced and added future compatibility for API export.
* Python >= 3.9 required for API export.
This commit is contained in:
		
							parent
							
								
									900b5efff5
								
							
						
					
					
						commit
						85f83f46d5
					
				| 
						 | 
					@ -104,13 +104,13 @@ class DrinkPriceVolume(db.Model, ModelSerializeMixin):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    __tablename__ = "drink_price_volume"
 | 
					    __tablename__ = "drink_price_volume"
 | 
				
			||||||
    id: int = db.Column("id", db.Integer, primary_key=True)
 | 
					    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)
 | 
					    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):
 | 
					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_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))
 | 
					    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_id_ = db.Column("type_id", db.Integer, db.ForeignKey("drink_type.id"))
 | 
					 | 
				
			||||||
    type: DrinkType = db.relationship("DrinkType")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,9 @@
 | 
				
			||||||
#!/usr/bin/python3
 | 
					#!/usr/bin/python3
 | 
				
			||||||
 | 
					from __future__ import annotations  # TODO: Remove if python requirement is >= 3.10
 | 
				
			||||||
import inspect
 | 
					import inspect
 | 
				
			||||||
import argparse
 | 
					import argparse
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pkg_resources
 | 
					import pkg_resources
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from flaschengeist.config import config
 | 
					from flaschengeist.config import config
 | 
				
			||||||
| 
						 | 
					@ -27,7 +29,8 @@ class PrefixMiddleware(object):
 | 
				
			||||||
class InterfaceGenerator:
 | 
					class InterfaceGenerator:
 | 
				
			||||||
    known = []
 | 
					    known = []
 | 
				
			||||||
    classes = {}
 | 
					    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):
 | 
					    def __init__(self, namespace, filename):
 | 
				
			||||||
        self.basename = ""
 | 
					        self.basename = ""
 | 
				
			||||||
| 
						 | 
					@ -36,51 +39,65 @@ class InterfaceGenerator:
 | 
				
			||||||
        self.this_type = None
 | 
					        self.this_type = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def pytype(self, cls):
 | 
					    def pytype(self, cls):
 | 
				
			||||||
        if isinstance(cls, list):
 | 
					        a = self._pytype(cls)
 | 
				
			||||||
            return "", "Array<{}>".format(self.pytype(cls[0])[1])
 | 
					        print(f"{cls} -> {a}")
 | 
				
			||||||
        if sys.version_info >= (3, 8):
 | 
					        return a
 | 
				
			||||||
            import typing
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if isinstance(cls, typing.ForwardRef):
 | 
					    def _pytype(self, cls):
 | 
				
			||||||
                return "", "this" if cls.__forward_arg__ == self.this_type else cls.__forward_arg__
 | 
					        import typing
 | 
				
			||||||
            if typing.get_origin(cls) == typing.Union:
 | 
					        origin = typing.get_origin(cls)
 | 
				
			||||||
                types = typing.get_args(cls)
 | 
					        arguments = typing.get_args(cls)
 | 
				
			||||||
                if len(types) == 2 and types[-1] is type(None):
 | 
					
 | 
				
			||||||
                    return "?", self.pytype(types[0])[1]
 | 
					        if origin is typing.ForwardRef:  # isinstance(cls, typing.ForwardRef):
 | 
				
			||||||
                else:
 | 
					            return "", "this" if cls.__forward_arg__ == self.this_type else cls.__forward_arg__
 | 
				
			||||||
                    return "", "|".join([self.pytype(pt)[1] for pt in types])
 | 
					        if origin is typing.Union:
 | 
				
			||||||
        if hasattr(cls, "__name__"):
 | 
					            print(f"A1:  {arguments[1]}")
 | 
				
			||||||
            if cls.__name__ in self.mapper:
 | 
					            if len(arguments) == 2 and arguments[1] is type(None):
 | 
				
			||||||
                return "", self.mapper[cls.__name__]
 | 
					                return "?", self.pytype(arguments[0])[1]
 | 
				
			||||||
            else:
 | 
					            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(
 | 
					        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"
 | 
					        return "?", "any"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def walker(self, module):
 | 
					    def walker(self, module):
 | 
				
			||||||
 | 
					        if sys.version_info < (3, 9):
 | 
				
			||||||
 | 
					            raise RuntimeError("Python >= 3.9 is required to export API")
 | 
				
			||||||
 | 
					        import typing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (
 | 
					        if (
 | 
				
			||||||
            inspect.ismodule(module[1])
 | 
					                inspect.ismodule(module[1])
 | 
				
			||||||
            and module[1].__name__.startswith(self.basename)
 | 
					                and module[1].__name__.startswith(self.basename)
 | 
				
			||||||
            and module[1].__name__ not in self.known
 | 
					                and module[1].__name__ not in self.known
 | 
				
			||||||
        ):
 | 
					        ):
 | 
				
			||||||
            self.known.append(module[1].__name__)
 | 
					            self.known.append(module[1].__name__)
 | 
				
			||||||
            for cls in inspect.getmembers(module[1], lambda x: inspect.isclass(x) or inspect.ismodule(x)):
 | 
					            for cls in inspect.getmembers(module[1], lambda x: inspect.isclass(x) or inspect.ismodule(x)):
 | 
				
			||||||
                self.walker(cls)
 | 
					                self.walker(cls)
 | 
				
			||||||
        elif (
 | 
					        elif (
 | 
				
			||||||
            inspect.isclass(module[1])
 | 
					                inspect.isclass(module[1])
 | 
				
			||||||
            and module[1].__module__.startswith(self.basename)
 | 
					                and module[1].__module__.startswith(self.basename)
 | 
				
			||||||
            and module[0] not in self.classes
 | 
					                and module[0] not in self.classes
 | 
				
			||||||
            and not module[0].startswith("_")
 | 
					                and not module[0].startswith("_")
 | 
				
			||||||
            and hasattr(module[1], "__annotations__")
 | 
					                and hasattr(module[1], "__annotations__")
 | 
				
			||||||
        ):
 | 
					        ):
 | 
				
			||||||
            self.this_type = module[0]
 | 
					            self.this_type = module[0]
 | 
				
			||||||
            d = {
 | 
					            print("\n\n" + module[0] + "\n")
 | 
				
			||||||
                param: self.pytype(ptype)
 | 
					            d = {}
 | 
				
			||||||
                for param, ptype in module[1].__annotations__.items()
 | 
					            for param, ptype in typing.get_type_hints(module[1], globalns=None, localns=None).items():
 | 
				
			||||||
                if not param.startswith("_") and not param.endswith("_")
 | 
					                if not param.startswith("_") and not param.endswith("_"):
 | 
				
			||||||
            }
 | 
					                    print(f"{param} ::: {ptype}")
 | 
				
			||||||
 | 
					                    d[param] = self.pytype(ptype)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if len(d) == 1:
 | 
					            if len(d) == 1:
 | 
				
			||||||
                key, value = d.popitem()
 | 
					                key, value = d.popitem()
 | 
				
			||||||
                self.classes[module[0]] = value[1]
 | 
					                self.classes[module[0]] = value[1]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue