Source code for sknano.core._meta

# -*- coding: utf-8 -*-
"""
======================================================================
Meta functions and class (:mod:`sknano.core._meta`)
======================================================================

.. currentmodule:: sknano.core._meta

Contents
========

"""
from __future__ import absolute_import, division, print_function

from functools import wraps
import inspect
import sys
import time
import warnings
#warnings.resetwarnings()

import numpy as np
from numpy.compat import formatargspec, getargspec

__all__ = ['check_type', 'deprecated', 'get_object_signature', 'memoize',
           'method_function', 'methodfunc', 'removed_package_warning',
           'timethis', 'with_doc']


[docs]def check_type(value, allowed_types=()): """Check object `value` type against tuple of `allowed_types`. Parameters ---------- value : `object` allowed_types : tuple tuple of allowed classes and/or types Raises ------ `TypeError` If `value` fails `isinstance` check against `allowed_types`. """ if not isinstance(value, allowed_types): raise TypeError('{} does not have an allowed type.\n'.format(value) + '(Allowed Type(s): {})'.format(allowed_types))
[docs]def deprecated(replacement=None): """Deprecated decorator. A decorator which can be used to mark functions as deprecated. replacement is a callable that will be called with the same args as the decorated function. Source: http://code.activestate.com/recipes/577819-deprecated-decorator/ Original Author: Giampaolo Rodola' <g.rodola [AT] gmail [DOT] com> License: MIT Parameters ---------- replacement : callable, optional Returns ------- decorator : decorator function """ def decorate(func): msg = "{!s} is deprecated".format(func.__name__) if replacement is not None: msg += "; use {!s} instead".format(replacement) if func.__doc__ is None: func.__doc__ = msg @wraps(func) def wrapper(*args, **kwargs): warnings.warn_explicit( "Call to deprecated function {}".format(func.__name__), category=DeprecationWarning, filename=func.__code__.co_filename, lineno=func.__code__.co_firstlineno + 1 ) return func(*args, **kwargs) return wrapper return decorate
[docs]def get_object_signature(obj): """ Get the signature from obj """ try: sig = formatargspec(*getargspec(obj)) except TypeError: sig = '' return sig
[docs]def memoize(f, cache={}): """memoization function to cache dict mapping""" @wraps(f) def g(*args, **kwargs): key = (f, tuple(args), frozenset(list(kwargs.items()))) if key not in cache: cache[key] = f(*args, **kwargs) return cache[key].copy() return g
[docs]def method_function(module, classobj):
# get the module as an object moduleobj = sys.modules[module] # Iterate over the methods of the class and dynamically create a function # for each method that calls the method and add it to the current module for member in inspect.getmembers(classobj, predicate=inspect.ismethod): method = member[0] # get the bound method func = getattr(classobj, method) # add the function to the current module setattr(moduleobj, method, func)
[docs]class methodfunc(object): """Define functions from existing class methods. This class is based off of the the `numpy` :class:`~numpy:numpy.ma._frommethod` class. Parameters ---------- classname : str Name of the class containing `methodname` methodname : str Name of the method to transform. """ def __init__(self, classobj, methodname, reversed=False): self._classobj = classobj self.__name__ = methodname self.__doc__ = self.getdoc() self.reversed = reversed
[docs] def getdoc(self): "Return the doc of the function (from the doc of the method)." meth = getattr(self._classobj, self.__name__, None) or \ getattr(np, self.__name__, None) signature = self.__name__ + get_object_signature(meth) if meth is not None: doc = """ {}\n{}""".format(signature, getattr(meth, '__doc__', None)) return doc
[docs] def __call__(self, a, *args, **params): if self.reversed: args = list(args) arr = args[0] args[0] = a a = arr # Get the method from the array (if possible) method_name = self.__name__ method = getattr(a, method_name, None) if method is not None: return method(*args, **params) # Still here ? Then a is not a classobj method = getattr(self._classobj, method_name, None) if method is not None: return method(self._classobj(a), *args, **params) # Still here ? OK, let's call the corresponding np function method = getattr(np, method_name) return method(a, *args, **params)
def removed_package_warning(oldpkg, newpkg=None): msg = '\n\n#TODO: UPDATE THIS WARNING MESSAGE.\n' #msg = '\n\n{:<74.74}\n'.format('{:*^80}'.format(' NB ')) # "As of version 0.3, the {!r} package was ".format(oldpkg) #if newpkg is None: # msg += "removed.\n" #else: # msg += "renamed.\n\n" + \ # "Replace imports from: {!r}\n".format(oldpkg) + \ # "with imports from: {!r}\n".format(newpkg) #msg += "Also review the module specific Warnings issued by\n" + \ # "module imports from a deprecated package location, as\n" + \ # "in some cases the module code may have moved to a different\n" + \ # "package entirely.\n\n" #msg += "Please update your code accordingly as these warnings and\n" + \ # "associated fallback code will likely be removed in a future version." #msg += "\n{}\n".format(74 * '*') warnings.warn(msg, ImportWarning)
[docs]def timethis(func): """Decorator that reports execution time.""" @wraps(func) def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(func.__name__, end-start) return result return wrapper
[docs]class with_doc(object): """Decorator class to combine class method docstrings. Parameters ---------- method : method object """ def __init__(self, method, use_header=True): self.method = method if use_header: self.header = "\n{}Notes\n{}\n".format(4 * ' ', 4 * ' ', 4 * ' ') else: self.header = ''
[docs] def __call__(self, new_method): new_doc = new_method.__doc__ original_doc = self.method.__doc__ header = self.header if original_doc and new_doc: new_method.__doc__ = \ '\n'.join([''.join([4 * ' ', s]) for s in (original_doc, header, new_doc)]) elif original_doc: new_method.__doc__ = original_doc return new_method