summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2011-11-28 19:06:07 -0500
committerChris McDonough <chrism@plope.com>2011-11-28 19:06:07 -0500
commite496386fd5e9e98c0479f39d67c092d61720c29c (patch)
treef7eb78289c206b0ba8ad6236914964fa2f08b6d5
parent8a4c36cdca2c512e9f0be8a862774c89367cf323 (diff)
downloadpyramid-e496386fd5e9e98c0479f39d67c092d61720c29c.tar.gz
pyramid-e496386fd5e9e98c0479f39d67c092d61720c29c.tar.bz2
pyramid-e496386fd5e9e98c0479f39d67c092d61720c29c.zip
rejigger how registry is assigned an introspector; add tests for object_description
-rw-r--r--pyramid/config/__init__.py79
-rw-r--r--pyramid/config/introspection.py152
-rw-r--r--pyramid/registry.py146
-rw-r--r--pyramid/tests/test_events.py5
-rw-r--r--pyramid/tests/test_util.py70
-rw-r--r--pyramid/util.py44
6 files changed, 279 insertions, 217 deletions
diff --git a/pyramid/config/__init__.py b/pyramid/config/__init__.py
index b0c86f0cd..3039abedd 100644
--- a/pyramid/config/__init__.py
+++ b/pyramid/config/__init__.py
@@ -12,7 +12,6 @@ from webob.exc import WSGIHTTPException as WebobWSGIHTTPException
from pyramid.interfaces import (
IDebugLogger,
IExceptionResponse,
- IIntrospector,
)
from pyramid.asset import resolve_asset_spec
@@ -41,7 +40,11 @@ from pyramid.path import (
package_of,
)
-from pyramid.registry import Registry
+from pyramid.registry import (
+ Introspectable,
+ Introspector,
+ Registry,
+ )
from pyramid.router import Router
@@ -69,9 +72,9 @@ from pyramid.config.tweens import TweensConfiguratorMixin
from pyramid.config.util import action_method
from pyramid.config.views import ViewsConfiguratorMixin
from pyramid.config.zca import ZCAConfiguratorMixin
-from pyramid.config.introspection import IntrospectionConfiguratorMixin
empty = text_('')
+_marker = object()
ConfigurationError = ConfigurationError # pyflakes
@@ -88,7 +91,6 @@ class Configurator(
SettingsConfiguratorMixin,
FactoriesConfiguratorMixin,
AdaptersConfiguratorMixin,
- IntrospectionConfiguratorMixin,
):
"""
A Configurator is used to configure a :app:`Pyramid`
@@ -231,8 +233,13 @@ class Configurator(
If ``route_prefix`` is passed, all routes added with
:meth:`pyramid.config.Configurator.add_route` will have the specified path
- prepended to their pattern. This parameter is new in Pyramid 1.2."""
+ prepended to their pattern. This parameter is new in Pyramid 1.2.
+ If ``introspector`` is passed, it must be an instance implementing the
+ :class:`pyramid.interfaces.IIntrospector` interface. If no
+ ``introspector`` is passed, the default IIntrospector implementation will
+ be used.
+ """
manager = manager # for testing injection
venusian = venusian # for testing injection
_ainfo = None
@@ -240,6 +247,7 @@ class Configurator(
includepath = ()
info = ''
object_description = staticmethod(object_description)
+ introspectable = Introspectable
def __init__(self,
registry=None,
@@ -259,6 +267,7 @@ class Configurator(
autocommit=False,
exceptionresponse_view=default_exceptionresponse_view,
route_prefix=None,
+ introspector=None,
):
if package is None:
package = caller_package()
@@ -286,15 +295,24 @@ class Configurator(
session_factory=session_factory,
default_view_mapper=default_view_mapper,
exceptionresponse_view=exceptionresponse_view,
+ introspector=introspector,
)
- def setup_registry(self, settings=None, root_factory=None,
- authentication_policy=None, authorization_policy=None,
- renderers=None, debug_logger=None,
- locale_negotiator=None, request_factory=None,
- renderer_globals_factory=None, default_permission=None,
- session_factory=None, default_view_mapper=None,
- exceptionresponse_view=default_exceptionresponse_view):
+ def setup_registry(self,
+ settings=None,
+ root_factory=None,
+ authentication_policy=None,
+ authorization_policy=None,
+ renderers=None,
+ debug_logger=None,
+ locale_negotiator=None,
+ request_factory=None,
+ renderer_globals_factory=None,
+ default_permission=None,
+ session_factory=None,
+ default_view_mapper=None,
+ exceptionresponse_view=default_exceptionresponse_view,
+ introspector=None):
""" When you pass a non-``None`` ``registry`` argument to the
:term:`Configurator` constructor, no initial setup is performed
against the registry. This is because the registry you pass in may
@@ -314,6 +332,10 @@ class Configurator(
registry = self.registry
self._fix_registry()
+
+ if introspector is not None:
+ self.introspector = introspector
+
self._set_settings(settings)
self._register_response_adapters()
@@ -442,23 +464,24 @@ class Configurator(
info=info, event=event)
_registry.registerSelfAdapter = registerSelfAdapter
- if not hasattr(_registry, 'introspector'):
- def _get_introspector(reg):
- return reg.queryUtility(IIntrospector)
-
- def _set_introspector(reg, introspector):
- reg.registerUtility(introspector, IIntrospector)
-
- def _del_introspector(reg):
- reg.unregisterUtility(IIntrospector)
-
- introspector = property(_get_introspector, _set_introspector,
- _del_introspector)
-
- _registry.__class__.introspector = introspector
-
-
# API
+
+ def _get_introspector(self):
+ introspector = getattr(self.registry, 'introspector', _marker)
+ if introspector is _marker:
+ introspector = Introspector()
+ self._set_introspector(introspector)
+ return introspector
+
+ def _set_introspector(self, introspector):
+ self.registry.introspector = introspector
+
+ def _del_introspector(self):
+ del self.registry.introspector
+
+ introspector = property(_get_introspector,
+ _set_introspector,
+ _del_introspector)
@property
def action_info(self):
diff --git a/pyramid/config/introspection.py b/pyramid/config/introspection.py
deleted file mode 100644
index 666728fc5..000000000
--- a/pyramid/config/introspection.py
+++ /dev/null
@@ -1,152 +0,0 @@
-import operator
-
-from zope.interface import implementer
-
-from pyramid.interfaces import (
- IIntrospectable,
- IIntrospector
- )
-
-@implementer(IIntrospector)
-class Introspector(object):
- def __init__(self):
- self._refs = {}
- self._categories = {}
- self._counter = 0
-
- def add(self, intr):
- category = self._categories.setdefault(intr.category_name, {})
- category[intr.discriminator] = intr
- category[intr.discriminator_hash] = intr
- intr.order = self._counter
- self._counter += 1
-
- def get(self, category_name, discriminator, default=None):
- category = self._categories.setdefault(category_name, {})
- intr = category.get(discriminator, default)
- return intr
-
- def get_category(self, category_name, sort_fn=None):
- if sort_fn is None:
- sort_fn = operator.attrgetter('order')
- category = self._categories[category_name]
- values = category.values()
- values = sorted(set(values), key=sort_fn)
- return [{'introspectable':intr, 'related':self.related(intr)} for
- intr in values]
-
- def categories(self):
- return sorted(self._categories.keys())
-
- def categorized(self, sort_fn=None):
- L = []
- for category_name in self.categories():
- L.append((category_name, self.get_category(category_name, sort_fn)))
- return L
-
- def remove(self, category_name, discriminator):
- intr = self.get(category_name, discriminator)
- if intr is None:
- return
- L = self._refs.pop((category_name, discriminator), [])
- for d in L:
- L2 = self._refs[d]
- L2.remove((category_name, discriminator))
- category = self._categories[intr.category_name]
- del category[intr.discriminator]
- del category[intr.discriminator_hash]
-
- def _get_intrs_by_pairs(self, pairs):
- introspectables = []
- for pair in pairs:
- category_name, discriminator = pair
- intr = self._categories.get(category_name, {}).get(discriminator)
- if intr is None:
- raise KeyError((category_name, discriminator))
- introspectables.append(intr)
- return introspectables
-
- def relate(self, *pairs):
- introspectables = self._get_intrs_by_pairs(pairs)
- relatable = ((x,y) for x in introspectables for y in introspectables)
- for x, y in relatable:
- L = self._refs.setdefault(x, [])
- if x is not y and y not in L:
- L.append(y)
-
- def unrelate(self, *pairs):
- introspectables = self._get_intrs_by_pairs(pairs)
- relatable = ((x,y) for x in introspectables for y in introspectables)
- for x, y in relatable:
- L = self._refs.get(x, [])
- if y in L:
- L.remove(y)
-
- def related(self, intr):
- category_name, discriminator = intr.category_name, intr.discriminator
- intr = self._categories.get(category_name, {}).get(discriminator)
- if intr is None:
- raise KeyError((category_name, discriminator))
- return self._refs.get(intr, [])
-
- def register(self, introspectable, action_info=''):
- introspectable.action_info = action_info
- self.add(introspectable)
- for category_name, discriminator in introspectable.relations:
- self.relate((
- introspectable.category_name, introspectable.discriminator),
- (category_name, discriminator))
-
- for category_name, discriminator in introspectable.unrelations:
- self.unrelate((
- introspectable.category_name, introspectable.discriminator),
- (category_name, discriminator))
-
-@implementer(IIntrospectable)
-class Introspectable(dict):
-
- order = 0 # mutated by introspector.add/introspector.add_intr
- action_info = '' # mutated by introspector.register
-
- def __init__(self, category_name, discriminator, title, type_name):
- self.category_name = category_name
- self.discriminator = discriminator
- self.title = title
- self.type_name = type_name
- self.relations = []
- self.unrelations = []
-
- def relate(self, category_name, discriminator):
- self.relations.append((category_name, discriminator))
-
- def unrelate(self, category_name, discriminator):
- self.unrelations.append((category_name, discriminator))
-
- @property
- def discriminator_hash(self):
- return hash(self.discriminator)
-
- def __hash__(self):
- return hash((self.category_name,) + (self.discriminator,))
-
- def __repr__(self):
- return '<%s category %r, discriminator %r>' % (self.__class__.__name__,
- self.category_name,
- self.discriminator)
-
- def __nonzero__(self):
- return True
-
- __bool__ = __nonzero__ # py3
-
-class IntrospectionConfiguratorMixin(object):
- introspectable = Introspectable
-
- @property
- def introspector(self):
- introspector = getattr(self.registry, 'introspector', None)
- if introspector is None:
- introspector = Introspector()
- self.registry.introspector = introspector
- return introspector
-
diff --git a/pyramid/registry.py b/pyramid/registry.py
index 99f5ee843..947643252 100644
--- a/pyramid/registry.py
+++ b/pyramid/registry.py
@@ -1,9 +1,15 @@
+import operator
+
+from zope.interface import implementer
+
from zope.interface.registry import Components
from pyramid.compat import text_
+
from pyramid.interfaces import (
ISettings,
- IIntrospector
+ IIntrospector,
+ IIntrospectable,
)
empty = text_('')
@@ -78,16 +84,136 @@ class Registry(Components, dict):
settings = property(_get_settings, _set_settings)
- def _get_introspector(self):
- return self.queryUtility(IIntrospector)
+@implementer(IIntrospector)
+class Introspector(object):
+ def __init__(self):
+ self._refs = {}
+ self._categories = {}
+ self._counter = 0
+
+ def add(self, intr):
+ category = self._categories.setdefault(intr.category_name, {})
+ category[intr.discriminator] = intr
+ category[intr.discriminator_hash] = intr
+ intr.order = self._counter
+ self._counter += 1
+
+ def get(self, category_name, discriminator, default=None):
+ category = self._categories.setdefault(category_name, {})
+ intr = category.get(discriminator, default)
+ return intr
+
+ def get_category(self, category_name, sort_fn=None):
+ if sort_fn is None:
+ sort_fn = operator.attrgetter('order')
+ category = self._categories[category_name]
+ values = category.values()
+ values = sorted(set(values), key=sort_fn)
+ return [{'introspectable':intr, 'related':self.related(intr)} for
+ intr in values]
+
+ def categories(self):
+ return sorted(self._categories.keys())
+
+ def categorized(self, sort_fn=None):
+ L = []
+ for category_name in self.categories():
+ L.append((category_name, self.get_category(category_name, sort_fn)))
+ return L
+
+ def remove(self, category_name, discriminator):
+ intr = self.get(category_name, discriminator)
+ if intr is None:
+ return
+ L = self._refs.pop((category_name, discriminator), [])
+ for d in L:
+ L2 = self._refs[d]
+ L2.remove((category_name, discriminator))
+ category = self._categories[intr.category_name]
+ del category[intr.discriminator]
+ del category[intr.discriminator_hash]
+
+ def _get_intrs_by_pairs(self, pairs):
+ introspectables = []
+ for pair in pairs:
+ category_name, discriminator = pair
+ intr = self._categories.get(category_name, {}).get(discriminator)
+ if intr is None:
+ raise KeyError((category_name, discriminator))
+ introspectables.append(intr)
+ return introspectables
+
+ def relate(self, *pairs):
+ introspectables = self._get_intrs_by_pairs(pairs)
+ relatable = ((x,y) for x in introspectables for y in introspectables)
+ for x, y in relatable:
+ L = self._refs.setdefault(x, [])
+ if x is not y and y not in L:
+ L.append(y)
+
+ def unrelate(self, *pairs):
+ introspectables = self._get_intrs_by_pairs(pairs)
+ relatable = ((x,y) for x in introspectables for y in introspectables)
+ for x, y in relatable:
+ L = self._refs.get(x, [])
+ if y in L:
+ L.remove(y)
+
+ def related(self, intr):
+ category_name, discriminator = intr.category_name, intr.discriminator
+ intr = self._categories.get(category_name, {}).get(discriminator)
+ if intr is None:
+ raise KeyError((category_name, discriminator))
+ return self._refs.get(intr, [])
+
+ def register(self, introspectable, action_info=''):
+ introspectable.action_info = action_info
+ self.add(introspectable)
+ for category_name, discriminator in introspectable.relations:
+ self.relate((
+ introspectable.category_name, introspectable.discriminator),
+ (category_name, discriminator))
+
+ for category_name, discriminator in introspectable.unrelations:
+ self.unrelate((
+ introspectable.category_name, introspectable.discriminator),
+ (category_name, discriminator))
+
+@implementer(IIntrospectable)
+class Introspectable(dict):
+
+ order = 0 # mutated by introspector.add/introspector.add_intr
+ action_info = '' # mutated by introspector.register
+
+ def __init__(self, category_name, discriminator, title, type_name):
+ self.category_name = category_name
+ self.discriminator = discriminator
+ self.title = title
+ self.type_name = type_name
+ self.relations = []
+ self.unrelations = []
+
+ def relate(self, category_name, discriminator):
+ self.relations.append((category_name, discriminator))
+
+ def unrelate(self, category_name, discriminator):
+ self.unrelations.append((category_name, discriminator))
+
+ @property
+ def discriminator_hash(self):
+ return hash(self.discriminator)
+
+ def __hash__(self):
+ return hash((self.category_name,) + (self.discriminator,))
+
+ def __repr__(self):
+ return '<%s category %r, discriminator %r>' % (self.__class__.__name__,
+ self.category_name,
+ self.discriminator)
- def _set_introspector(self, introspector):
- self.registerUtility(introspector, IIntrospector)
-
- def _del_introspector(self):
- self.unregisterUtility(IIntrospector)
+ def __nonzero__(self):
+ return True
- introspector = property(_get_introspector, _set_introspector,
- _del_introspector)
+ __bool__ = __nonzero__ # py3
global_registry = Registry('global')
diff --git a/pyramid/tests/test_events.py b/pyramid/tests/test_events.py
index 108a5d2d9..4b58a129c 100644
--- a/pyramid/tests/test_events.py
+++ b/pyramid/tests/test_events.py
@@ -122,11 +122,10 @@ class ContextFoundEventTests(unittest.TestCase):
class TestSubscriber(unittest.TestCase):
def setUp(self):
- registry = DummyRegistry()
- self.config = testing.setUp(registry=registry)
+ self.config = testing.setUp()
def tearDown(self):
- self.config.end()
+ testing.tearDown()
def _makeOne(self, *ifaces):
from pyramid.events import subscriber
diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py
index 57bcd08d7..f45c75535 100644
--- a/pyramid/tests/test_util.py
+++ b/pyramid/tests/test_util.py
@@ -255,5 +255,75 @@ class Test_WeakOrderedSet(unittest.TestCase):
self.assertEqual(list(wos), [])
self.assertEqual(wos.last, None)
+class Test_object_description(unittest.TestCase):
+ def _callFUT(self, object):
+ from pyramid.util import object_description
+ return object_description(object)
+
+ def test_string(self):
+ self.assertEqual(self._callFUT('abc'), 'abc')
+
+ def test_int(self):
+ self.assertEqual(self._callFUT(1), '1')
+
+ def test_bool(self):
+ self.assertEqual(self._callFUT(True), 'True')
+
+ def test_None(self):
+ self.assertEqual(self._callFUT(None), 'None')
+
+ def test_float(self):
+ self.assertEqual(self._callFUT(1.2), '1.2')
+
+ def test_tuple(self):
+ self.assertEqual(self._callFUT(('a', 'b')), "('a', 'b')")
+
+ def test_set(self):
+ self.assertEqual(self._callFUT(set(['a'])), "set(['a'])")
+
+ def test_list(self):
+ self.assertEqual(self._callFUT(['a']), "['a']")
+
+ def test_dict(self):
+ self.assertEqual(self._callFUT({'a':1}), "{'a': 1}")
+
+ def test_nomodule(self):
+ o = object()
+ self.assertEqual(self._callFUT(o), 'object %s' % str(o))
+
+ def test_module(self):
+ import pyramid
+ self.assertEqual(self._callFUT(pyramid), 'module pyramid')
+
+ def test_method(self):
+ self.assertEqual(
+ self._callFUT(self.test_method),
+ 'method test_method of class pyramid.tests.test_util.'
+ 'Test_object_description')
+
+ def test_class(self):
+ self.assertEqual(
+ self._callFUT(self.__class__),
+ 'class pyramid.tests.test_util.Test_object_description')
+
+ def test_function(self):
+ self.assertEqual(
+ self._callFUT(dummyfunc),
+ 'function pyramid.tests.test_util.dummyfunc')
+
+ def test_instance(self):
+ inst = Dummy()
+ self.assertEqual(
+ self._callFUT(inst),
+ "object %s" % str(inst))
+
+ def test_shortened_repr(self):
+ inst = ['1'] * 1000
+ self.assertEqual(
+ self._callFUT(inst),
+ str(inst)[:100] + ' ... ]')
+
+def dummyfunc(): pass
+
class Dummy(object):
pass
diff --git a/pyramid/util.py b/pyramid/util.py
index c439456b6..1fd612d09 100644
--- a/pyramid/util.py
+++ b/pyramid/util.py
@@ -1,12 +1,12 @@
import inspect
import pkg_resources
import sys
-import types
import weakref
from pyramid.compat import (
integer_types,
string_types,
+ text_,
)
from pyramid.exceptions import ConfigurationError
@@ -235,20 +235,20 @@ def strings_differ(string1, string2):
return invalid_bits != 0
def object_description(object):
- """ Produce a human-consumable string description of ``object``, usually
- involving a Python dotted name. For example:
+ """ Produce a human-consumable text description of ``object``,
+ usually involving a Python dotted name. For example:
.. code-block:: python
>>> object_description(None)
- 'None'
+ u'None'
>>> from xml.dom import minidom
>>> object_description(minidom)
- 'module xml.dom.minidom'
+ u'module xml.dom.minidom'
>>> object_description(minidom.Attr)
- 'class xml.dom.minidom.Attr'
+ u'class xml.dom.minidom.Attr'
>>> object_description(minidom.Attr.appendChild)
- 'method appendChild of class xml.dom.minidom.Attr'
+ u'method appendChild of class xml.dom.minidom.Attr'
>>>
If this method cannot identify the type of the object, a generic
@@ -259,11 +259,11 @@ def object_description(object):
(possibly shortened) string representation is returned.
"""
if isinstance(object, string_types):
- return object
+ return text_(object)
if isinstance(object, integer_types):
- return object
+ return text_(str(object))
if isinstance(object, (bool, float, type(None))):
- return str(object)
+ return text_(str(object))
if isinstance(object, (tuple, set)):
return shortrepr(object, ')')
if isinstance(object, list):
@@ -272,32 +272,28 @@ def object_description(object):
return shortrepr(object, '}')
module = inspect.getmodule(object)
if module is None:
- return 'object %s' % str(object)
+ return text_('object %s' % str(object))
modulename = module.__name__
if inspect.ismodule(object):
- return 'module %s' % modulename
+ return text_('module %s' % modulename)
if inspect.ismethod(object):
oself = getattr(object, '__self__', None)
- if oself is None:
+ if oself is None: # pragma: no cover
oself = getattr(object, 'im_self', None)
- return 'method %s of class %s.%s' % (object.__name__, modulename,
- oself.__class__.__name__)
+ return text_('method %s of class %s.%s' %
+ (object.__name__, modulename,
+ oself.__class__.__name__))
if inspect.isclass(object):
dottedname = '%s.%s' % (modulename, object.__name__)
- return 'class %s' % dottedname
+ return text_('class %s' % dottedname)
if inspect.isfunction(object):
dottedname = '%s.%s' % (modulename, object.__name__)
- return 'function %s' % dottedname
- if inspect.isbuiltin(object):
- dottedname = '%s.%s' % (modulename, object.__name__)
- return 'builtin %s' % dottedname
- if hasattr(object, '__name__'):
- return 'object %s' % object.__name__
- return 'object %s' % str(object)
+ return text_('function %s' % dottedname)
+ return text_('object %s' % str(object))
def shortrepr(object, closer):
r = str(object)
if len(r) > 100:
- r = r[:100] + '... %s' % closer
+ r = r[:100] + ' ... %s' % closer
return r