From 1e0d648503fd992323737c7c702be204337e1e36 Mon Sep 17 00:00:00 2001 From: John Anderson Date: Sat, 7 Feb 2015 12:33:03 -0800 Subject: Raise error at configuration time --- pyramid/config/factories.py | 15 +++++++++--- pyramid/tests/test_config/test_factories.py | 21 +++++++--------- pyramid/tests/test_util.py | 38 +++++++++++++++++++++++++++++ pyramid/util.py | 27 +++++++++++++------- 4 files changed, 77 insertions(+), 24 deletions(-) diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py index 15cfb796f..4b2517ff1 100644 --- a/pyramid/config/factories.py +++ b/pyramid/config/factories.py @@ -15,8 +15,12 @@ from pyramid.traversal import DefaultRootFactory from pyramid.util import ( action_method, InstancePropertyMixin, + get_callable_name, ) +from pyramid.compat import native_ + + class FactoriesConfiguratorMixin(object): @action_method def set_root_factory(self, factory): @@ -33,9 +37,10 @@ class FactoriesConfiguratorMixin(object): factory = self.maybe_dotted(factory) if factory is None: factory = DefaultRootFactory + def register(): self.registry.registerUtility(factory, IRootFactory) - self.registry.registerUtility(factory, IDefaultRootFactory) # b/c + self.registry.registerUtility(factory, IDefaultRootFactory) # b/c intr = self.introspectable('root factories', None, @@ -44,7 +49,7 @@ class FactoriesConfiguratorMixin(object): intr['factory'] = factory self.action(IRootFactory, register, introspectables=(intr,)) - _set_root_factory = set_root_factory # bw compat + _set_root_factory = set_root_factory # bw compat @action_method def set_session_factory(self, factory): @@ -60,6 +65,7 @@ class FactoriesConfiguratorMixin(object): achieve the same purpose. """ factory = self.maybe_dotted(factory) + def register(): self.registry.registerUtility(factory, ISessionFactory) intr = self.introspectable('session factory', None, @@ -89,6 +95,7 @@ class FactoriesConfiguratorMixin(object): can be used to achieve the same purpose. """ factory = self.maybe_dotted(factory) + def register(): self.registry.registerUtility(factory, IRequestFactory) intr = self.introspectable('request factory', None, @@ -173,6 +180,8 @@ class FactoriesConfiguratorMixin(object): callable, name=name, reify=reify) elif name is None: name = callable.__name__ + else: + name = get_callable_name(name) def register(): exts = self.registry.queryUtility(IRequestExtensions) @@ -224,9 +233,9 @@ class FactoriesConfiguratorMixin(object): 'set_request_propery() is deprecated as of Pyramid 1.5; use ' 'add_request_method() with the property=True argument instead') + @implementer(IRequestExtensions) class _RequestExtensions(object): def __init__(self): self.descriptors = {} self.methods = {} - diff --git a/pyramid/tests/test_config/test_factories.py b/pyramid/tests/test_config/test_factories.py index 35677a91b..42bb5accc 100644 --- a/pyramid/tests/test_config/test_factories.py +++ b/pyramid/tests/test_config/test_factories.py @@ -128,24 +128,21 @@ class TestFactoriesMixin(unittest.TestCase): def test_add_request_method_with_text_type_name(self): from pyramid.interfaces import IRequestExtensions - from pyramid.compat import text_ - from pyramid.util import InstancePropertyMixin + from pyramid.compat import text_, PY3 + from pyramid.exceptions import ConfigurationError config = self._makeOne(autocommit=True) def boomshaka(r): pass - name = text_(b'La Pe\xc3\xb1a', 'utf-8') - config.add_request_method(boomshaka, name=name) - name2 = b'La Pe\xc3\xb1a' - config.add_request_method(boomshaka, name=name2) + def get_bad_name(): + if PY3: # pragma: nocover + name = b'La Pe\xc3\xb1a' + else: # pragma: nocover + name = text_(b'La Pe\xc3\xb1a', 'utf-8') - exts = config.registry.getUtility(IRequestExtensions) - inst = InstancePropertyMixin() - - def set_extensions(): - inst._set_extensions(exts) + config.add_request_method(boomshaka, name=name) - self.assertRaises(ValueError, set_extensions) + self.assertRaises(ConfigurationError, get_bad_name) class TestDeprecatedFactoriesMixinMethods(unittest.TestCase): def setUp(self): diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py index ac5ea0683..405fe927a 100644 --- a/pyramid/tests/test_util.py +++ b/pyramid/tests/test_util.py @@ -124,6 +124,21 @@ class Test_InstancePropertyMixin(unittest.TestCase): self.assertEqual(1, foo.x) self.assertEqual(2, foo.y) + def test__make_property_unicode(self): + from pyramid.compat import text_ + from pyramid.exceptions import ConfigurationError + + cls = self._getTargetClass() + if PY3: # pragma: nocover + name = b'La Pe\xc3\xb1a' + else: # pragma: nocover + name = text_(b'La Pe\xc3\xb1a', 'utf-8') + + def make_bad_name(): + cls._make_property(lambda x: 1, name=name, reify=True) + + self.assertRaises(ConfigurationError, make_bad_name) + def test__set_properties_with_dict(self): foo = self._makeOne() x_name, x_fn = foo._make_property(lambda _: 1, name='x', reify=True) @@ -619,7 +634,30 @@ class TestActionInfo(unittest.TestCase): "Line 0 of file filename:\n linerepr ") +class TestCallableName(unittest.TestCase): + def test_valid_ascii(self): + from pyramid.util import get_callable_name + name = u'hello world' + self.assertEquals(get_callable_name(name), name) + + def test_invalid_ascii(self): + from pyramid.util import get_callable_name + from pyramid.compat import text_, PY3 + from pyramid.exceptions import ConfigurationError + + def get_bad_name(): + if PY3: # pragma: nocover + name = b'La Pe\xc3\xb1a' + else: # pragma: nocover + name = text_(b'La Pe\xc3\xb1a', 'utf-8') + + get_callable_name(name) + + self.assertRaises(ConfigurationError, get_bad_name) + + def dummyfunc(): pass + class Dummy(object): pass diff --git a/pyramid/util.py b/pyramid/util.py index c036c1c2e..7e8535aaf 100644 --- a/pyramid/util.py +++ b/pyramid/util.py @@ -22,6 +22,7 @@ from pyramid.compat import ( string_types, text_, PY3, + native_ ) from pyramid.interfaces import IActionInfo @@ -55,7 +56,7 @@ class InstancePropertyMixin(object): raise ValueError('cannot reify a property') elif name is not None: fn = lambda this: callable(this) - fn.__name__ = name + fn.__name__ = get_callable_name(name) fn.__doc__ = callable.__doc__ else: name = callable.__name__ @@ -111,14 +112,7 @@ class InstancePropertyMixin(object): def _set_extensions(self, extensions): for name, fn in iteritems_(extensions.methods): method = fn.__get__(self, self.__class__) - try: - setattr(self, name, method) - except (UnicodeEncodeError, TypeError): - msg = ( - '`name="%s"` is invalid. `name` must be ascii because it is ' - 'used on __name__ of the method' - ) - raise ValueError(msg % name) + setattr(self, name, method) self._set_properties(extensions.descriptors) @@ -558,3 +552,18 @@ def action_method(wrapped): functools.update_wrapper(wrapper, wrapped) wrapper.__docobj__ = wrapped return wrapper + + +def get_callable_name(name): + """ + Verifies that the ``name`` is ascii and will raise a ``ConfigurationError`` + if it is not. + """ + try: + return native_(name, 'ascii') + except (UnicodeEncodeError, UnicodeDecodeError): + msg = ( + '`name="%s"` is invalid. `name` must be ascii because it is ' + 'used on __name__ of the method' + ) + raise ConfigurationError(msg % name) -- cgit v1.2.3