Source code for dcolumn.common.decorators
# -*- coding: utf-8 -*-
#
# dcolumn/common/decorators.py
#
"""
Dynamic Column decorators.
"""
__docformat__ = "restructuredtext en"
import inspect
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import login_required
from dcolumn.dcolumns.manager import dcolumn_manager
[docs]def dcolumn_login_required(function=None,
redirect_field_name=REDIRECT_FIELD_NAME,
login_url=None):
"""
Decorator for views that checks that the user is logged in, redirecting
to the log-in page if necessary. Authentication on the API can be turned
on or off depending on the ``settings.DYNAMIC_COLUMNS[
'INACTIVATE_API_AUTH' ]`` state.
:param function: The function or method to decorate.
:type function: Function or method
:param redirect_field_name: See `Django Docs <https://docs.djangoproject.com/en/1.9/topics/auth/default/#django.contrib.auth.decorators.login_required>`_.
:type redirect_field_name: str
:param login_url: Optional URL to login through.
:type login_url: str
:rtype: The results from ``login_required``'
"""
if dcolumn_manager.api_auth_state:
return function
return login_required(function, redirect_field_name=redirect_field_name,
login_url=login_url)
#
# InspectChoice
#
[docs]class InspectChoice(object):
"""
This class does introspection on a non-model ``CHOICE`` object and
supplies a decorator for the choice manager mixin. It should not be
necessary to use this class outside of DColumns itself.
"""
def __init__(self):
self._path, self._caller_name = self._caller_info(skip=4)
def _caller_info(self, skip=2):
"""
Get a name of a caller in the format module.class.method.
:param skip: Specifies how many levels of stack to skip while getting
caller name. skip=1 means 'who calls me', skip=2 'who
calls my caller' etc.
:type skip: int
:rtype: A list or empty string
An empty string is returned if skipped levels exceed stack height.
See: https://gist.github.com/techtonik/2151727
"""
stack = inspect.stack()
start = 0 + skip
if len(stack) < start + 1: # pragma: no cover
return ''
parentframe = stack[start][0]
name_list = []
module = inspect.getmodule(parentframe)
# `modname` can be None when frame is executed directly in console
# TODO(techtonik): consider using __main__
if module:
name_list.append(module.__name__)
# detect classname
if 'self' in parentframe.f_locals: # pragma: no cover
# I don't know any way to detect a call from the object method
# XXX: there seems to be no way to detect a static method call--it
# will be just a function call.
name_list.append(parentframe.f_locals['self'].__class__.__name__)
codename = parentframe.f_code.co_name
if codename != '<module>': # top level usually
name_list.append( codename ) # function or a method
del parentframe
#log.debug("name_list: %s", name_list)
return name_list
[docs] @classmethod
def set_model(self, method):
"""
A decorator to set the choices model object of the calling class in
the manager.
:param method: The method name to be decorated.
:type method: str
:rtype: The enclosed function embedded in this method.
"""
def wrapper(this, *args, **kwargs):
modules = __import__(
this._path, globals=globals(), locals=locals(),
fromlist=(this._caller_name,), level=0)
this.model = getattr(modules, this._caller_name)
return method(this, *args, **kwargs)
# Make the wrapper look like the decorated method.
wrapper.__name__ = method.__name__
wrapper.__dict__ = method.__dict__
wrapper.__doc__ = method.__doc__
return wrapper