#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Utility function for `qmat`
"""
import inspect
import pkgutil
import functools
from time import time
[docs]
def checkOverriding(cls, name, isProperty=True):
"""Check if a class overrides a method with a given name"""
method = getattr(cls, name)
parent = getattr(cls.mro()[-2], name)
assert method != parent, \
f"{name} method must be overriden in {cls.__name__} class"
if isProperty:
assert type(method) == property, \
f"{name} method must be a property in {cls.__name__} class"
else:
pass
# TODO : check that signatures are the same
[docs]
def checkGenericConstr(cls):
"""Check if a class implement a constructor with a `**kwargs` generic parameter"""
sig = inspect.signature(cls.__init__)
try:
par = sig.parameters["kwargs"]
assert par.kind == par.VAR_KEYWORD
except (KeyError, AssertionError):
raise AssertionError(f"{cls.__name__} class requires **kwargs in its constructor")
[docs]
def storeAlias(cls, dico, alias):
"""Store a class into a dictionary with a given alias"""
assert alias not in dico, f"{alias} alias already registered in {dico}"
dico[alias] = cls
[docs]
def storeClass(cls, dico):
"""Store a class into a dictionary"""
storeAlias(cls, dico, cls.__name__)
aliases = getattr(cls, "aliases", None)
if aliases:
assert isinstance(aliases, list), \
f"aliases must be a list in class {cls.__name__}"
for alias in aliases:
storeAlias(cls, dico, alias)
[docs]
def importAll(localVars, __all__, __path__, __name__, __import__):
"""Import all submodules in the current (sub-)package"""
__all__ += [var for var in localVars.keys() if not var.startswith('__')]
for _, moduleName, _ in pkgutil.walk_packages(__path__):
__all__.append(moduleName)
__import__(__name__+'.'+moduleName)
[docs]
def getClasses(dico, module=None):
"""Retrieve all classes stored into a dictionary, filtering aliases"""
classes = {}
if module is None:
check = lambda cls: True
else:
check = lambda cls: cls.__module__.endswith("."+module)
for key, cls in dico.items():
if cls not in classes.values() and check(cls):
classes[key] = cls
return classes
[docs]
def useQGen(__init__):
r"""
Wrapper to extract :math:`Q_\Delta`-generator parameters from `kwargs` arguments,
using either a :math:`Q`-generator `qGen` or separately given parameters.
"""
pNames = [p.name for p in inspect.signature(__init__).parameters.values()
if (p.kind == p.POSITIONAL_OR_KEYWORD) and p.name != "self"]
@functools.wraps(__init__)
def wrapper(self, *args, **kwargs):
if "coll" in kwargs:
# TODO : remove in future version
import warnings
warnings.warn("using the `coll` argument is deprecated. Use `qGen` instead!", DeprecationWarning)
assert "qGen" not in kwargs, "`coll` and `qGen` given together, that's an ambiguous call !"
kwargs["qGen"] = kwargs.pop("coll")
params = {name: value for name, value in zip(pNames, args)}
qGen = kwargs.pop("qGen", None)
params.update(kwargs)
if qGen is not None:
qGenParams = self.extractParams(qGen)
qGenParams.update(params)
params = qGenParams
__init__(self, **params)
return wrapper
[docs]
class Timer():
"""
Utility Timer class, that can be used as follow :
>>> with Timer("stuff"): # prints "Starting stuff ...
>>> # ... do stuff
>>> # prints " -- tWall : {tWall}s
The description at the end can be replaced using the `descr`
constructor parameter, and the final wall time can be scaled
using the `scale` parameter. Can also be used like this :
>>> clock = Timer("stuff")
>>> clock.start()
>>> # ... do stuff
>>> clock.stop()
>>> tWall = clock.tWall
"""
def __init__(self, name, scale=1, descr="tWall"):
self.name = name
self.scale = scale
self.descr = descr
[docs]
def start(self):
print(f"Starting {self.name} ...")
self.tStart = time()
[docs]
def stop(self):
self.tWall = time() - self.tStart
self.tWall /= self.scale
print(f" -- {self.descr} : {self.tWall:1.2e}s")
def __enter__(self):
self.start()
def __exit__(self, exc_type, exc_value, traceback):
self.stop()