From d8d3a919f6a2a17db7ef20cea9a867bc0ec714a5 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 12 Nov 2012 00:16:42 -0500 Subject: Move pyramid.config.views.viewdefaults -> pyramid.util.viewdefaults Move pyramid.config.util.ActionInfo -> pyramid.util.ActionInfo Move pyramid.config.util.action_method -> pyramid.util.action_method This is in support of not requiring substance d and other like systems to import from within pyramid.config (even though these functions and classes are still not officially documented APIs). Backwards compatibility imports have been left in place. --- pyramid/config/__init__.py | 11 +++--- pyramid/config/assets.py | 2 +- pyramid/config/factories.py | 8 ++-- pyramid/config/i18n.py | 3 +- pyramid/config/rendering.py | 2 +- pyramid/config/security.py | 2 +- pyramid/config/testing.py | 2 +- pyramid/config/util.py | 61 +++++-------------------------- pyramid/config/views.py | 19 +--------- pyramid/tests/test_config/test_init.py | 2 +- pyramid/tests/test_config/test_util.py | 30 --------------- pyramid/tests/test_util.py | 31 ++++++++++++++++ pyramid/util.py | 67 ++++++++++++++++++++++++++++++++++ 13 files changed, 126 insertions(+), 114 deletions(-) diff --git a/pyramid/config/__init__.py b/pyramid/config/__init__.py index 1dc438597..40edaa324 100644 --- a/pyramid/config/__init__.py +++ b/pyramid/config/__init__.py @@ -70,16 +70,17 @@ from pyramid.config.security import SecurityConfiguratorMixin from pyramid.config.settings import SettingsConfiguratorMixin from pyramid.config.testing import TestingConfiguratorMixin from pyramid.config.tweens import TweensConfiguratorMixin -from pyramid.config.util import ( - action_method, - ActionInfo, - PredicateList, - ) +from pyramid.config.util import PredicateList from pyramid.config.views import ViewsConfiguratorMixin from pyramid.config.zca import ZCAConfiguratorMixin from pyramid.path import DottedNameResolver +from pyramid.util import ( + action_method, + ActionInfo, + ) + empty = text_('') _marker = object() diff --git a/pyramid/config/assets.py b/pyramid/config/assets.py index c93431987..5d4682349 100644 --- a/pyramid/config/assets.py +++ b/pyramid/config/assets.py @@ -8,7 +8,7 @@ from pyramid.interfaces import IPackageOverrides from pyramid.exceptions import ConfigurationError from pyramid.threadlocal import get_current_registry -from pyramid.config.util import action_method +from pyramid.util import action_method class OverrideProvider(pkg_resources.DefaultProvider): def __init__(self, module): diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py index 01b1fb22e..ef7975d92 100644 --- a/pyramid/config/factories.py +++ b/pyramid/config/factories.py @@ -1,7 +1,5 @@ from zope.interface import implementer -from pyramid.config.util import action_method - from pyramid.interfaces import ( IDefaultRootFactory, IRequestFactory, @@ -11,7 +9,11 @@ from pyramid.interfaces import ( ) from pyramid.traversal import DefaultRootFactory -from pyramid.util import InstancePropertyMixin + +from pyramid.util import ( + action_method, + InstancePropertyMixin, + ) class FactoriesConfiguratorMixin(object): @action_method diff --git a/pyramid/config/i18n.py b/pyramid/config/i18n.py index 67a7e2018..9eb59e1c7 100644 --- a/pyramid/config/i18n.py +++ b/pyramid/config/i18n.py @@ -13,8 +13,7 @@ from pyramid.exceptions import ConfigurationError from pyramid.i18n import get_localizer from pyramid.path import package_path from pyramid.threadlocal import get_current_request - -from pyramid.config.util import action_method +from pyramid.util import action_method class I18NConfiguratorMixin(object): @action_method diff --git a/pyramid/config/rendering.py b/pyramid/config/rendering.py index 926511b7b..4f33b23d9 100644 --- a/pyramid/config/rendering.py +++ b/pyramid/config/rendering.py @@ -6,7 +6,7 @@ from pyramid.interfaces import ( PHASE1_CONFIG, ) -from pyramid.config.util import action_method +from pyramid.util import action_method from pyramid import ( renderers, diff --git a/pyramid/config/security.py b/pyramid/config/security.py index 567999cc4..6a1257b6a 100644 --- a/pyramid/config/security.py +++ b/pyramid/config/security.py @@ -7,7 +7,7 @@ from pyramid.interfaces import ( ) from pyramid.exceptions import ConfigurationError -from pyramid.config.util import action_method +from pyramid.util import action_method class SecurityConfiguratorMixin(object): @action_method diff --git a/pyramid/config/testing.py b/pyramid/config/testing.py index abbbffc10..7141a5049 100644 --- a/pyramid/config/testing.py +++ b/pyramid/config/testing.py @@ -14,7 +14,7 @@ from pyramid.traversal import ( split_path_info, ) -from pyramid.config.util import action_method +from pyramid.util import action_method class TestingConfiguratorMixin(object): # testing API diff --git a/pyramid/config/util.py b/pyramid/config/util.py index 1c6e1ca15..c16755a75 100644 --- a/pyramid/config/util.py +++ b/pyramid/config/util.py @@ -1,10 +1,4 @@ -import traceback - -from functools import update_wrapper - -from zope.interface import implementer - -from pyramid.interfaces import IActionInfo +from hashlib import md5 from pyramid.compat import ( bytes_, @@ -13,56 +7,19 @@ from pyramid.compat import ( from pyramid.exceptions import ConfigurationError from pyramid.registry import predvalseq -from pyramid.util import TopologicalSorter -from hashlib import md5 +from pyramid.util import ( + TopologicalSorter, + action_method, + ActionInfo, + ) + +action_method = action_method # support bw compat imports +ActionInfo = ActionInfo # support bw compat imports MAX_ORDER = 1 << 30 DEFAULT_PHASH = md5().hexdigest() -@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""" - 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', 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=3) - info = ActionInfo(*f[-backframes]) - except: # 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__'): - update_wrapper(wrapper, wrapped) - wrapper.__docobj__ = wrapped - return wrapper - def as_sorted_tuple(val): if not is_nonstr_iter(val): val = (val,) diff --git a/pyramid/config/views.py b/pyramid/config/views.py index 8a4db149e..745b6f810 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -1,7 +1,6 @@ import inspect import operator import os -from functools import wraps from zope.interface import ( Interface, @@ -71,6 +70,8 @@ from pyramid.view import ( from pyramid.util import ( object_description, + viewdefaults, + action_method, ) import pyramid.config.predicates @@ -78,7 +79,6 @@ import pyramid.config.predicates from pyramid.config.util import ( DEFAULT_PHASH, MAX_ORDER, - action_method, ) urljoin = urlparse.urljoin @@ -621,21 +621,6 @@ class MultiView(object): continue raise PredicateMismatch(self.name) -def viewdefaults(wrapped): - def wrapper(self, *arg, **kw): - defaults = {} - if arg: - view = arg[0] - else: - view = kw.get('view') - view = self.maybe_dotted(view) - if inspect.isclass(view): - defaults = getattr(view, '__view_defaults__', {}).copy() - defaults.update(kw) - defaults['_backframes'] = 3 # for action_method - return wrapped(self, *arg, **defaults) - return wraps(wrapped)(wrapper) - class ViewsConfiguratorMixin(object): @viewdefaults @action_method diff --git a/pyramid/tests/test_config/test_init.py b/pyramid/tests/test_config/test_init.py index 2cf9a269a..7c2880a18 100644 --- a/pyramid/tests/test_config/test_init.py +++ b/pyramid/tests/test_config/test_init.py @@ -772,7 +772,7 @@ pyramid.tests.test_config.dummy_include2""", self.assertEqual(config.action('discrim', kw={'a':1}), None) def test_action_autocommit_with_introspectables(self): - from pyramid.config.util import ActionInfo + from pyramid.util import ActionInfo config = self._makeOne(autocommit=True) intr = DummyIntrospectable() config.action('discrim', introspectables=(intr,)) diff --git a/pyramid/tests/test_config/test_util.py b/pyramid/tests/test_config/test_util.py index 8c3cd7455..b32f9c6ef 100644 --- a/pyramid/tests/test_config/test_util.py +++ b/pyramid/tests/test_config/test_util.py @@ -366,36 +366,6 @@ class TestPredicateList(unittest.TestCase): self.assertRaises(ConfigurationError, self._callFUT, unknown=1) -class TestActionInfo(unittest.TestCase): - def _getTargetClass(self): - from pyramid.config.util import ActionInfo - return ActionInfo - - def _makeOne(self, filename, lineno, function, linerepr): - return self._getTargetClass()(filename, lineno, function, linerepr) - - def test_class_conforms(self): - from zope.interface.verify import verifyClass - from pyramid.interfaces import IActionInfo - verifyClass(IActionInfo, self._getTargetClass()) - - def test_instance_conforms(self): - from zope.interface.verify import verifyObject - from pyramid.interfaces import IActionInfo - verifyObject(IActionInfo, self._makeOne('f', 0, 'f', 'f')) - - def test_ctor(self): - inst = self._makeOne('filename', 10, 'function', 'src') - self.assertEqual(inst.file, 'filename') - self.assertEqual(inst.line, 10) - self.assertEqual(inst.function, 'function') - self.assertEqual(inst.src, 'src') - - def test___str__(self): - inst = self._makeOne('filename', 0, 'function', ' linerepr ') - self.assertEqual(str(inst), - "Line 0 of file filename:\n linerepr ") - class DummyCustomPredicate(object): def __init__(self): self.__text__ = 'custom predicate' diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py index 785950230..2ca4c4a66 100644 --- a/pyramid/tests/test_util.py +++ b/pyramid/tests/test_util.py @@ -545,6 +545,37 @@ class TestSentinel(unittest.TestCase): r = repr(Sentinel('ABC')) self.assertEqual(r, 'ABC') +class TestActionInfo(unittest.TestCase): + def _getTargetClass(self): + from pyramid.util import ActionInfo + return ActionInfo + + def _makeOne(self, filename, lineno, function, linerepr): + return self._getTargetClass()(filename, lineno, function, linerepr) + + def test_class_conforms(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import IActionInfo + verifyClass(IActionInfo, self._getTargetClass()) + + def test_instance_conforms(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IActionInfo + verifyObject(IActionInfo, self._makeOne('f', 0, 'f', 'f')) + + def test_ctor(self): + inst = self._makeOne('filename', 10, 'function', 'src') + self.assertEqual(inst.file, 'filename') + self.assertEqual(inst.line, 10) + self.assertEqual(inst.function, 'function') + self.assertEqual(inst.src, 'src') + + def test___str__(self): + inst = self._makeOne('filename', 0, 'function', ' linerepr ') + self.assertEqual(str(inst), + "Line 0 of file filename:\n linerepr ") + + def dummyfunc(): pass class Dummy(object): diff --git a/pyramid/util.py b/pyramid/util.py index d83837322..ca7f5951c 100644 --- a/pyramid/util.py +++ b/pyramid/util.py @@ -1,6 +1,10 @@ +import functools import inspect +import traceback import weakref +from zope.interface import implementer + from pyramid.exceptions import ( ConfigurationError, CyclicDependencyError, @@ -15,6 +19,7 @@ from pyramid.compat import ( PY3, ) +from pyramid.interfaces import IActionInfo from pyramid.path import DottedNameResolver as _DottedNameResolver class DottedNameResolver(_DottedNameResolver): @@ -453,3 +458,65 @@ class TopologicalSorter(object): return result +def viewdefaults(wrapped): + """ Decorator for add_view-like methods which takes into account + __view_defaults__ attached to view it is passed. Not a documented API but + used by some external systems.""" + def wrapper(self, *arg, **kw): + defaults = {} + if arg: + view = arg[0] + else: + view = kw.get('view') + view = self.maybe_dotted(view) + if inspect.isclass(view): + defaults = getattr(view, '__view_defaults__', {}).copy() + defaults.update(kw) + defaults['_backframes'] = 3 # for action_method + return wrapped(self, *arg, **defaults) + return functools.wraps(wrapped)(wrapper) + +@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', 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=3) + info = ActionInfo(*f[-backframes]) + except: # 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 + -- cgit v1.2.3