Source code for Muscat.Helpers.Factory

# -*- coding: utf-8 -*-
#
# This file is subject to the terms and conditions defined in
# file 'LICENSE.txt', which is part of this source code package.
#

from typing import Iterable, Set, Type, Dict, Any, Optional, Tuple, List, Callable

import Muscat.Helpers.ParserHelper as PH


[docs] class Factory(): """Base class to build custom factories str -> object instance To use this factory you must create a class like this def RegisterClass(name, classType, constructor=None, withError = True): return ImplicitGeometryFactory.RegisterClass(name,classType, constructor=constructor, withError = withError ) def Create(name,ops=None): return ImplicitGeometryFactory.Create(name,ops) class ImplicitGeometryFactory(Factory): _Catalog = {} _SetCatalog = set() def __init__(self): super().__init__() And use the functions to register and create objects RegisterClass("Offset",ImplicitGeometryOffset,CreateImplicitGeometryOffset) or (with a special constructor) RegisterClass("Offset",ImplicitGeometryOffset) And to create a class res = Create(name,ops) """ def __init__(self): super().__init__()
[docs] @classmethod def keys(cls) -> Iterable[str]: """Recover the keys (str) for all the object in the factor Returns ------- Iterable[str] key entries """ return cls._Catalog.keys()
[docs] @classmethod def AllEntries(cls) -> Set: """Return the internal set containing all the entries in the form : (key, class, constructor) := Tuple[str,Type,Callable[[Dict],Any]] """ return cls._SetCatalog
[docs] @classmethod def RegisterClass(cls, name: str, classType: Type, constructor: Optional[Callable[[Dict], Any]] = None, withError: bool = True) -> None: """Register a class and a constructor using the string name Parameters ---------- name : str key to retrieve the instances of classType classType : Type A type of a class constructor : callable[[Dict],Any], optional the constructor , by default None withError : bool, optional if false then the user can override a existent class/constructor , by default True """ if name in cls._Catalog and withError: raise (Exception("Class " + str(name) + " already in the catalog")) cls._Catalog[name] = (classType, constructor) if hasattr(cls, "_SetCatalog"): cls._SetCatalog.add((name, classType, constructor))
[docs] @classmethod def GetClass(cls, name: str) -> Type: """Return the class associated to name Parameters ---------- name : str the key Returns ------- Type the type """ classType, classConstructor = cls._Catalog[name] return classType
[docs] @classmethod def GetConstructor(cls, name: str) -> Callable[[Dict], Any]: """Return the constructor associated to name Parameters ---------- name : str the key Returns ------- Callable[[Dict],Any] the constructor """ classType, classConstructor = cls._Catalog[name] return classConstructor
[docs] @classmethod def GetAvailableFor(cls, name: str) -> List[Tuple[Type, Callable[[Dict], Any]]]: """Return the list of all the entries associated to the key name Parameters ---------- name : str the key Returns ------- List[Tuple[Type,Callable[[Dict],Any]]] the list of (class, constructors) """ return [(obj, const) for key, obj, const in cls._SetCatalog if key == name]
[docs] @classmethod def Create(cls, name: str, ops: Optional[Dict] = None, propertiesAssign: bool = True) -> Any: """Create a instance of a class associated to the key name Parameters ---------- name : str the key ops : Optional[Dict], optional options in the form of a dict to pass to the constructor, by default None propertiesAssign : bool, optional if the class constructor is None and propertiesAssign is True then use ParserHelpers.ReadProperties to push the data into the object, by default True Returns ------- Any an instance of a the type associated to the key name """ res = None if name in cls._Catalog: classType, classConstructor = cls._Catalog[name] if classConstructor is None: try: res = classType() except Exception as e: print(classType) print("Error creating class (" + name + "):" + str(classType) + ". ") raise (e) if propertiesAssign and ops is not None: PH.ReadProperties(ops, ops, res) else: res = classConstructor(ops) return res raise (Exception("Unable to create object of type " + str(name) + "\n Possible object are :" + str(list(cls._Catalog.keys()))))
[docs] @classmethod def PrintAvailable(cls, fullDetails: bool = False) -> None: """print all the available entries in the factory Parameters ---------- fullDetails : bool, optional True to print the docstring of every class, by default False """ def PrintDoctring(name, obj, doc_or_type="doc", indent: int = 0) -> None: if doc_or_type == "doc": doc = obj.__doc__ if doc is None: print(" "*indent, name, ": ") else: print(" "*indent, name, " : ", doc) else: print(" "*indent + name + " : (", str(type(obj)) + ")") for name, cl, cons in cls._SetCatalog: if cons is not None: if cons.__doc__ is not None: obj = cons else: obj = cl() else: obj = cl() print("---------------------------------------------------------") if (cl, cons) in cls._Catalog.values(): print(" vvvvvv default for '"+name+"' vvvvv ") PrintDoctring(name, obj, indent=0) if fullDetails: if hasattr(obj, '__dict__') and hasattr(obj.__dict__, '__iter__'): for propName in obj.__dict__: if propName[0] == "_": continue PrintDoctring(propName, obj.__dict__[propName], "type", 6)
[docs] def CheckIntegrity(GUI:bool=False) -> str: class DummyFactory(Factory): _Catalog = {} _SetCatalog = set() def __init__(self): super().__init__() fact = DummyFactory() print(fact.keys()) print(fact.AllEntries()) fact.RegisterClass("str", str) ok = True fact.RegisterClass("str", str, withError=False) try: fact.RegisterClass("str", str) ok = False # pragma: no cover except: pass assert ok print(fact.GetClass("str")) print(fact.GetConstructor("str")) print(fact.GetAvailableFor("str")) print(fact.Create("str")) ok = True try: print(fact.Create("Million Dollars")) ok = False # pragma: no cover except: pass assert ok def DummyConstructor(ops): """documentation for this dummy constructor """ return str(round(ops["str"])) class DummyClass(): def __init__(self): self.str = "" self._internalInstance_ = 1 def DummyWithoutDocumentation(ops): return str(round(ops["str"])) fact.RegisterClass("rounded_str", str, DummyConstructor) print(fact.Create("rounded_str", ops={"str":3.5})) fact.RegisterClass("rounded_str", DummyClass, DummyWithoutDocumentation, withError=False) print(fact.Create("rounded_str", ops={"str":3.5})) fact.PrintAvailable(fullDetails=True) fact.RegisterClass("DummyClass22", DummyClass) fact.Create("DummyClass22", ops={"str": "3.5"}) ok = True try: a = 5 fact.RegisterClass("numpy", a) print(fact.Create("numpy")) ok = False # pragma: no cover except: pass assert ok return "ok"
if __name__ == '__main__': print(CheckIntegrity(True)) # pragma: no cover