codekingpro/portable-devtools
114k
1"""2passlib.utils.decor -- helper decorators & properties3"""4#=============================================================================5# imports6#=============================================================================7# core8from __future__ import absolute_import, division, print_function9import logging10log = logging.getLogger(__name__)11from functools import wraps, update_wrapper12import types13from warnings import warn14# site15# pkg16from passlib.utils.compat import PY317# local18__all__ = [19 "classproperty",20 "hybrid_method",21 22 "memoize_single_value",23 "memoized_property",24 25 "deprecated_function",26 "deprecated_method",27]28 29#=============================================================================30# class-level decorators31#=============================================================================32class classproperty(object):33 """Function decorator which acts like a combination of classmethod+property (limited to read-only properties)"""34 35 def __init__(self, func):36 self.im_func = func37 38 def __get__(self, obj, cls):39 return self.im_func(cls)40 41 @property42 def __func__(self):43 """py3 compatible alias"""44 return self.im_func45 46class hybrid_method(object):47 """48 decorator which invokes function with class if called as class method,49 and with object if called at instance level.50 """51 52 def __init__(self, func):53 self.func = func54 update_wrapper(self, func)55 56 def __get__(self, obj, cls):57 if obj is None:58 obj = cls59 if PY3:60 return types.MethodType(self.func, obj)61 else:62 return types.MethodType(self.func, obj, cls)63 64#=============================================================================65# memoization66#=============================================================================67 68def memoize_single_value(func):69 """70 decorator for function which takes no args,71 and memoizes result. exposes a ``.clear_cache`` method72 to clear the cached value.73 """74 cache = {}75 76 @wraps(func)77 def wrapper():78 try:79 return cache[True]80 except KeyError:81 pass82 value = cache[True] = func()83 return value84 85 def clear_cache():86 cache.pop(True, None)87 wrapper.clear_cache = clear_cache88 89 return wrapper90 91class memoized_property(object):92 """93 decorator which invokes method once, then replaces attr with result94 """95 def __init__(self, func):96 self.__func__ = func97 self.__name__ = func.__name__98 self.__doc__ = func.__doc__99 100 def __get__(self, obj, cls):101 if obj is None:102 return self103 value = self.__func__(obj)104 setattr(obj, self.__name__, value)105 return value106 107 if not PY3:108 109 @property110 def im_func(self):111 """py2 alias"""112 return self.__func__113 114 def clear_cache(self, obj):115 """116 class-level helper to clear stored value (if any).117 118 usage: :samp:`type(self).{attr}.clear_cache(self)`119 """120 obj.__dict__.pop(self.__name__, None)121 122 def peek_cache(self, obj, default=None):123 """124 class-level helper to peek at stored value125 126 usage: :samp:`value = type(self).{attr}.clear_cache(self)`127 """128 return obj.__dict__.get(self.__name__, default)129 130# works but not used131##class memoized_class_property(object):132## """function decorator which calls function as classmethod,133## and replaces itself with result for current and all future invocations.134## """135## def __init__(self, func):136## self.im_func = func137##138## def __get__(self, obj, cls):139## func = self.im_func140## value = func(cls)141## setattr(cls, func.__name__, value)142## return value143##144## @property145## def __func__(self):146## "py3 compatible alias"147 148#=============================================================================149# deprecation150#=============================================================================151def deprecated_function(msg=None, deprecated=None, removed=None, updoc=True,152 replacement=None, _is_method=False,153 func_module=None):154 """decorator to deprecate a function.155 156 :arg msg: optional msg, default chosen if omitted157 :kwd deprecated: version when function was first deprecated158 :kwd removed: version when function will be removed159 :kwd replacement: alternate name / instructions for replacing this function.160 :kwd updoc: add notice to docstring (default ``True``)161 """162 if msg is None:163 if _is_method:164 msg = "the method %(mod)s.%(klass)s.%(name)s() is deprecated"165 else:166 msg = "the function %(mod)s.%(name)s() is deprecated"167 if deprecated:168 msg += " as of Passlib %(deprecated)s"169 if removed:170 msg += ", and will be removed in Passlib %(removed)s"171 if replacement:172 msg += ", use %s instead" % replacement173 msg += "."174 def build(func):175 is_classmethod = _is_method and isinstance(func, classmethod)176 if is_classmethod:177 # NOTE: PY26 doesn't support "classmethod().__func__" directly...178 func = func.__get__(None, type).__func__179 opts = dict(180 mod=func_module or func.__module__,181 name=func.__name__,182 deprecated=deprecated,183 removed=removed,184 )185 if _is_method:186 def wrapper(*args, **kwds):187 tmp = opts.copy()188 klass = args[0] if is_classmethod else args[0].__class__189 tmp.update(klass=klass.__name__, mod=klass.__module__)190 warn(msg % tmp, DeprecationWarning, stacklevel=2)191 return func(*args, **kwds)192 else:193 text = msg % opts194 def wrapper(*args, **kwds):195 warn(text, DeprecationWarning, stacklevel=2)196 return func(*args, **kwds)197 update_wrapper(wrapper, func)198 if updoc and (deprecated or removed) and \199 wrapper.__doc__ and ".. deprecated::" not in wrapper.__doc__:200 txt = deprecated or ''201 if removed or replacement:202 txt += "\n "203 if removed:204 txt += "and will be removed in version %s" % (removed,)205 if replacement:206 if removed:207 txt += ", "208 txt += "use %s instead" % replacement209 txt += "."210 if not wrapper.__doc__.strip(" ").endswith("\n"):211 wrapper.__doc__ += "\n"212 wrapper.__doc__ += "\n.. deprecated:: %s\n" % (txt,)213 if is_classmethod:214 wrapper = classmethod(wrapper)215 return wrapper216 return build217 218def deprecated_method(msg=None, deprecated=None, removed=None, updoc=True,219 replacement=None):220 """decorator to deprecate a method.221 222 :arg msg: optional msg, default chosen if omitted223 :kwd deprecated: version when method was first deprecated224 :kwd removed: version when method will be removed225 :kwd replacement: alternate name / instructions for replacing this method.226 :kwd updoc: add notice to docstring (default ``True``)227 """228 return deprecated_function(msg, deprecated, removed, updoc, replacement,229 _is_method=True)230 231#=============================================================================232# eof233#=============================================================================234 