"""
Decorator utilities for Tet applications.
This module provides useful decorators:
- :func:`deprecated` - Mark functions as deprecated
- :class:`reify_attr` - Cached property with configurable attribute name
Example
-------
Marking a function as deprecated::
from tet.decorators import deprecated
@deprecated
def old_function():
pass
Using reify_attr for cached properties::
from tet.decorators import reify_attr
class MyClass:
@reify_attr
def expensive_computation(self):
# This will only be called once per instance
return compute_something()
"""
import warnings
from functools import update_wrapper
[docs]
def deprecated(func):
"""This is a decorator which can be used to mark functions
as deprecated. It will result in a warning being emitted
when the function is used."""
def new_func(*args, **kwargs):
warnings.warn(
"Call to deprecated function {}."
.format(func.__qualname__, category=DeprecationWarning, stacklevel=2))
return func(*args, **kwargs)
new_func.__name__ = func.__name__
new_func.__doc__ = func.__doc__
new_func.__dict__.update(func.__dict__)
return new_func
[docs]
class reify_attr(object):
"""
A cached property descriptor that uses the actual attribute name.
Unlike Pyramid's ``reify`` which gets the attribute name from the decorated
method, ``reify_attr`` uses the name of the actual attribute it's assigned to.
This is determined via ``__set_name__``, falling back to finding the attribute
on the class if ``__set_name__`` is not called (e.g., when the descriptor is
assigned dynamically).
This pattern is useful as a building block for descriptors like ``autowired``
in pyramid_di, where the descriptor needs to know its attribute name to cache
the resolved value on the instance.
"""
def __init__(self, wrapped):
self.wrapped = wrapped
update_wrapper(self, wrapped)
self.names = None
def __get__(self, inst, objtype=None):
if inst is None:
return self
val = self.wrapped(inst)
if self.names is None:
names = []
for name, value in list(objtype.__dict__.items()):
if value is self:
names.append(name)
self.names = names
for name in self.names:
setattr(inst, name, val)
return val
def __set_name__(self, owner, name):
if self.names is None:
self.names = [name]
else:
self.names.append(name)