# -*- 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