You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-3-lib/pym/calculate/contrib/passlib/utils/decor.py

233 lines
7.4 KiB

"""
passlib.utils.decor -- helper decorators & properties
"""
#=============================================================================
# imports
#=============================================================================
# core
import logging
log = logging.getLogger(__name__)
from functools import wraps, update_wrapper
import types
from warnings import warn
# site
# pkg
from passlib.utils.compat import PY3
# local
__all__ = [
"classproperty",
"hybrid_method",
"memoize_single_value",
"memoized_property",
"deprecated_function",
"deprecated_method",
]
#=============================================================================
# class-level decorators
#=============================================================================
class classproperty(object):
"""Function decorator which acts like a combination of classmethod+property (limited to read-only properties)"""
def __init__(self, func):
self.im_func = func
def __get__(self, obj, cls):
return self.im_func(cls)
@property
def __func__(self):
"""py3 compatible alias"""
return self.im_func
class hybrid_method(object):
"""
decorator which invokes function with class if called as class method,
and with object if called at instance level.
"""
def __init__(self, func):
self.func = func
update_wrapper(self, func)
def __get__(self, obj, cls):
if obj is None:
obj = cls
if PY3:
return types.MethodType(self.func, obj)
else:
return types.MethodType(self.func, obj, cls)
#=============================================================================
# memoization
#=============================================================================
def memoize_single_value(func):
"""
decorator for function which takes no args,
and memoizes result. exposes a ``.clear_cache`` method
to clear the cached value.
"""
cache = {}
@wraps(func)
def wrapper():
try:
return cache[True]
except KeyError:
pass
value = cache[True] = func()
return value
def clear_cache():
cache.pop(True, None)
wrapper.clear_cache = clear_cache
return wrapper
class memoized_property(object):
"""
decorator which invokes method once, then replaces attr with result
"""
def __init__(self, func):
self.__func__ = func
self.__name__ = func.__name__
self.__doc__ = func.__doc__
def __get__(self, obj, cls):
if obj is None:
return self
value = self.__func__(obj)
setattr(obj, self.__name__, value)
return value
if not PY3:
@property
def im_func(self):
"""py2 alias"""
return self.__func__
def clear_cache(self, obj):
"""
class-level helper to clear stored value (if any).
usage: :samp:`type(self).{attr}.clear_cache(self)`
"""
obj.__dict__.pop(self.__name__, None)
def peek_cache(self, obj, default=None):
"""
class-level helper to peek at stored value
usage: :samp:`value = type(self).{attr}.clear_cache(self)`
"""
return obj.__dict__.get(self.__name__, default)
# works but not used
##class memoized_class_property(object):
## """function decorator which calls function as classmethod,
## and replaces itself with result for current and all future invocations.
## """
## def __init__(self, func):
## self.im_func = func
##
## def __get__(self, obj, cls):
## func = self.im_func
## value = func(cls)
## setattr(cls, func.__name__, value)
## return value
##
## @property
## def __func__(self):
## "py3 compatible alias"
#=============================================================================
# deprecation
#=============================================================================
def deprecated_function(msg=None, deprecated=None, removed=None, updoc=True,
replacement=None, _is_method=False,
func_module=None):
"""decorator to deprecate a function.
:arg msg: optional msg, default chosen if omitted
:kwd deprecated: version when function was first deprecated
:kwd removed: version when function will be removed
:kwd replacement: alternate name / instructions for replacing this function.
:kwd updoc: add notice to docstring (default ``True``)
"""
if msg is None:
if _is_method:
msg = "the method %(mod)s.%(klass)s.%(name)s() is deprecated"
else:
msg = "the function %(mod)s.%(name)s() is deprecated"
if deprecated:
msg += " as of Passlib %(deprecated)s"
if removed:
msg += ", and will be removed in Passlib %(removed)s"
if replacement:
msg += ", use %s instead" % replacement
msg += "."
def build(func):
is_classmethod = _is_method and isinstance(func, classmethod)
if is_classmethod:
# NOTE: PY26 doesn't support "classmethod().__func__" directly...
func = func.__get__(None, type).__func__
opts = dict(
mod=func_module or func.__module__,
name=func.__name__,
deprecated=deprecated,
removed=removed,
)
if _is_method:
def wrapper(*args, **kwds):
tmp = opts.copy()
klass = args[0] if is_classmethod else args[0].__class__
tmp.update(klass=klass.__name__, mod=klass.__module__)
warn(msg % tmp, DeprecationWarning, stacklevel=2)
return func(*args, **kwds)
else:
text = msg % opts
def wrapper(*args, **kwds):
warn(text, DeprecationWarning, stacklevel=2)
return func(*args, **kwds)
update_wrapper(wrapper, func)
if updoc and (deprecated or removed) and \
wrapper.__doc__ and ".. deprecated::" not in wrapper.__doc__:
txt = deprecated or ''
if removed or replacement:
txt += "\n "
if removed:
txt += "and will be removed in version %s" % (removed,)
if replacement:
if removed:
txt += ", "
txt += "use %s instead" % replacement
txt += "."
if not wrapper.__doc__.strip(" ").endswith("\n"):
wrapper.__doc__ += "\n"
wrapper.__doc__ += "\n.. deprecated:: %s\n" % (txt,)
if is_classmethod:
wrapper = classmethod(wrapper)
return wrapper
return build
def deprecated_method(msg=None, deprecated=None, removed=None, updoc=True,
replacement=None):
"""decorator to deprecate a method.
:arg msg: optional msg, default chosen if omitted
:kwd deprecated: version when method was first deprecated
:kwd removed: version when method will be removed
:kwd replacement: alternate name / instructions for replacing this method.
:kwd updoc: add notice to docstring (default ``True``)
"""
return deprecated_function(msg, deprecated, removed, updoc, replacement,
_is_method=True)
#=============================================================================
# eof
#=============================================================================