Michael Merickel
2018-10-19 d579f2104de139e0b0fc5d6c81aabb2f826e5e54
src/pyramid/config/actions.py
@@ -1,18 +1,19 @@
import functools
import itertools
import operator
import sys
import traceback
from zope.interface import implementer
from pyramid.compat import reraise
from pyramid.config.util import ActionInfo
from pyramid.exceptions import (
    ConfigurationConflictError,
    ConfigurationError,
    ConfigurationExecutionError,
)
from pyramid.interfaces import IActionInfo
from pyramid.registry import undefer
from pyramid.util import is_nonstr_iter
class ActionConfiguratorMixin(object):
@@ -152,6 +153,7 @@
        finally:
            self.end()
        self.action_state = ActionState()  # old actions have been processed
# this class is licensed under the ZPL (stolen from Zope)
class ActionState(object):
@@ -523,3 +525,57 @@
        order=order,
        introspectables=introspectables,
    )
@implementer(IActionInfo)
class ActionInfo(object):
    def __init__(self, file, line, function, src):
        self.file = file
        self.line = line
        self.function = function
        self.src = src
    def __str__(self):
        srclines = self.src.split('\n')
        src = '\n'.join('    %s' % x for x in srclines)
        return 'Line %s of file %s:\n%s' % (self.line, self.file, src)
def action_method(wrapped):
    """ Wrapper to provide the right conflict info report data when a method
    that calls Configurator.action calls another that does the same.  Not a
    documented API but used by some external systems."""
    def wrapper(self, *arg, **kw):
        if self._ainfo is None:
            self._ainfo = []
        info = kw.pop('_info', None)
        # backframes for outer decorators to actionmethods
        backframes = kw.pop('_backframes', 0) + 2
        if is_nonstr_iter(info) and len(info) == 4:
            # _info permitted as extract_stack tuple
            info = ActionInfo(*info)
        if info is None:
            try:
                f = traceback.extract_stack(limit=4)
                # Work around a Python 3.5 issue whereby it would insert an
                # extra stack frame. This should no longer be necessary in
                # Python 3.5.1
                last_frame = ActionInfo(*f[-1])
                if last_frame.function == 'extract_stack':  # pragma: no cover
                    f.pop()
                info = ActionInfo(*f[-backframes])
            except Exception:  # pragma: no cover
                info = ActionInfo(None, 0, '', '')
        self._ainfo.append(info)
        try:
            result = wrapped(self, *arg, **kw)
        finally:
            self._ainfo.pop()
        return result
    if hasattr(wrapped, '__name__'):
        functools.update_wrapper(wrapper, wrapped)
    wrapper.__docobj__ = wrapped
    return wrapper