diff options
| author | Chris McDonough <chrism@plope.com> | 2011-12-03 16:24:34 -0500 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2011-12-03 16:24:34 -0500 |
| commit | 5224059f71d0ad592a611c196a3af7cbd1dc828f (patch) | |
| tree | 636c2c2b0613d8350629e1a85a43f1b1ccf05e41 | |
| parent | 9d97b654057e621c4928fe597053d54aa5f63a8c (diff) | |
| download | pyramid-5224059f71d0ad592a611c196a3af7cbd1dc828f.tar.gz pyramid-5224059f71d0ad592a611c196a3af7cbd1dc828f.tar.bz2 pyramid-5224059f71d0ad592a611c196a3af7cbd1dc828f.zip | |
add more content to the introspectables narr chapter; adjust introspection registrations while doing so
| -rw-r--r-- | docs/narr/extconfig.rst | 13 | ||||
| -rw-r--r-- | docs/narr/introspector.rst | 425 | ||||
| -rw-r--r-- | pyramid/config/factories.py | 3 | ||||
| -rw-r--r-- | pyramid/config/i18n.py | 3 | ||||
| -rw-r--r-- | pyramid/config/rendering.py | 7 | ||||
| -rw-r--r-- | pyramid/config/routes.py | 21 | ||||
| -rw-r--r-- | pyramid/config/security.py | 6 | ||||
| -rw-r--r-- | pyramid/config/tweens.py | 9 | ||||
| -rw-r--r-- | pyramid/config/views.py | 17 | ||||
| -rw-r--r-- | pyramid/interfaces.py | 9 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_views.py | 14 |
11 files changed, 481 insertions, 46 deletions
diff --git a/docs/narr/extconfig.rst b/docs/narr/extconfig.rst index ac8b83baa..856654377 100644 --- a/docs/narr/extconfig.rst +++ b/docs/narr/extconfig.rst @@ -336,7 +336,7 @@ Two introspectables may have relationships between each other. config = Configurator() config.add_directive('add_jammyjam', add_jammyjam) -In the above example, the ``add_jammyjam`` directive registers *two* +In the above example, the ``add_jammyjam`` directive registers two introspectables. The first is related to the ``value`` passed to the directive; the second is related to the ``template`` passed to the directive. If you believe a concept within a directive is important enough to have its @@ -352,8 +352,15 @@ introspectable and the ``tmpl_intr`` introspectable; the arguments passed to ``relate`` are the category name and discriminator of the ``tmpl_intr`` introspectable. +Relationships need not be made between two introspectables created by the +same directive. Instead, a relationship can be formed between an +introspectable created in one directive and another introspectable created in +another by calling ``relate`` on either side with the other directive's +category name and discriminator. An error will be raised at configuration +commit time if you attempt to relate an introspectable with another +nonexistent introspectable, however. + Introspectable relationships will show up in frontend system renderings of introspection values. For example, if a view registration names a route name, the introspectable related to the view callable will show a reference -to the route it relates to and vice versa. - +to the route to which it relates to and vice versa. diff --git a/docs/narr/introspector.rst b/docs/narr/introspector.rst index 3cbafa010..8adfde7d1 100644 --- a/docs/narr/introspector.rst +++ b/docs/narr/introspector.rst @@ -9,16 +9,17 @@ Pyramid Configuration Introspection When Pyramid starts up, each call to a :term:`configuration directive` causes one or more :term:`introspectable` objects to be registered with an -:term:`introspector`. This introspector can be queried by application code -to obtain information about the configuration of the running application. -This feature is useful for debug toolbars, command-line scripts which show -some aspect of configuration, and for runtime reporting of startup-time +:term:`introspector`. The introspector can be queried by application code to +obtain information about the configuration of the running application. This +feature is useful for debug toolbars, command-line scripts which show some +aspect of configuration, and for runtime reporting of startup-time configuration settings. Using the Introspector ---------------------- -Here's an example of using Pyramid's introspector: +Here's an example of using Pyramid's introspector from within a view +callable: .. code-block:: python :linenos: @@ -26,64 +27,440 @@ Here's an example of using Pyramid's introspector: from pyramid.view import view_config from pyramid.response import Response - @view_config(route_name='foo') @view_config(route_name='bar') def route_accepts(request): introspector = request.registry.introspector route_name = request.matched_route.name route_intr = introspector.get('routes', route_name) - return Response(str(route_intr['accept'])) + return Response(str(route_intr['pattern'])) -This view will return a response that contains the "accept" argument provided -to the ``add_route`` method of the route which matched when the view was -called. It used the :meth:`pyramid.interfaces.IIntrospector.get` method to -return an introspectable in the category ``routes`` with a -:term:`discriminator` equal to the matched route name. It then used the -returned introspectable to obtain an "accept" value. +This view will return a response that contains the "pattern" argument +provided to the ``add_route`` method of the route which matched when the view +was called. It uses the :meth:`pyramid.interfaces.IIntrospector.get` method +to return an introspectable in the category ``routes`` with a +:term:`discriminator` equal to the matched route name. It then uses the +returned introspectable to obtain an "pattern" value. -The introspector has a number of other query-related methods: see -:class:`pyramid.interfaces.IIntrospector` for more information. The -introspectable returned by the query methods of the introspector has methods -and attributes described by :class:`pyramid.interfaces.IIntrospectable`. +The introspectable returned by the query methods of the introspector has +methods and attributes described by +:class:`pyramid.interfaces.IIntrospectable`. In particular, the +:meth:`~pyramid.interfaces.IIntrospector.get`, +:meth:`~pyramid.interfaces.IIntrospector.get_category`, +:meth:`~pyramid.interfaces.IIntrospector.categories`, +:meth:`~pyramid.interfaces.IIntrospector.categorized`, and +:meth:`~pyramid.interfaces.IIntrospector.related` methods of an introspector +can be used to query for introspectables. -Concrete Introspection Categories ---------------------------------- +Introspectable Objects +---------------------- + +Introspectable objects are returned from query methods of an introspector. +Each introspectable object implements the attributes and methods the +documented at :class:`pyramid.interfaces.IIntrospectable`. + +The important attributes shared by all introspectables are the following: + +``title`` + + A human-readable text title describing the introspectable + +``category_name`` + + A text category name describing the introspection category to which this + introspectable belongs. It is often a plural if there are expected to be + more than one introspectable registered within the category. + +``discriminator`` + + A hashable object representing the unique value of this introspectable + within its category. + +``discriminator_hash`` -This is a list of concrete introspection categories provided by Pyramid. + The integer hash of the discriminator (useful for using in HTML links). + +``type_name`` + + The text name of a subtype within this introspectable's category. If there + is only one type name in this introspectable's category, this value will + often be a singular version of the category name but it can be an arbitrary + value. + +Besides having the attributes described above, an introspectable is a +dictionary-like object. An introspectable can be queried for data values via +its ``__getitem__``, ``get``, ``keys``, ``values``, or ``items`` methods. +For example: + +.. code-block:: python + :linenos: + + route_intr = introspector.get('routes', 'edit_user') + pattern = route_intr['pattern'] + +Pyramid Introspection Categories +-------------------------------- + +The list of concrete introspection categories provided by built-in Pyramid +configuration directives follows. Add-on packages may supply other +introspectables in categories not described here. ``subscribers`` + Each introspectable in the ``subscribers`` category represents a call to + :meth:`pryamid.config.Configurator.add_subscriber` (or the decorator + equivalent); each will have the following data. + + ``subscriber`` + + The subscriber callable object (the resolution of the ``subscriber`` + argument passed to ``add_susbcriber``). + + ``interfaces`` + + A sequence of interfaces (or classes) that are subscribed to (the + resolution of the ``ifaces`` argument passed to ``add_subscriber``). + ``response adapters`` -``asset overrides`` + Each introspectable in the ``response adapters`` category represents a call + to :meth:`pyramid.config.Configurator.add_response_adapter` (or a decorator + equivalent); each will have the following data. + + ``adapter`` + + The adapter object (the resolved ``adapter`` argument to + ``add_response_adapter``). + + ``type`` + + The resolved ``type_or_iface`` argument passed to + ``add_response_adapter``. ``root factories`` + XXX ``default root factory`` category? + + Each introspectable in the ``root factories`` category represents a call to + :meth:`pyramid.config.Configurator.set_root_factory` (or the Configurator + constructor equivalent) *or* a ``factory`` argument passed to + :meth:`pyramid.config.Configurator.add_route`; each will have the following + data. + + ``factory`` + + The factory object (the resolved ``factory`` argument to + ``set_root_factory``). + + ``route_name`` + + The name of the route which will use this factory. If this is the + *default* root factory (if it's registered during a call to + ``set_root_factory``), this value will be ``None``. + ``session factory`` + Only one introspectable will exist in the ``session factory`` category. It + represents a call to :meth:`pyramid.config.Configurator.set_session_factory` + (or the Configurator constructor equivalent); it will have the following + data. + + ``factory`` + + The factory object (the resolved ``factory`` argument to + ``set_session_factory``). + ``request factory`` + Only one introspectable will exist in the ``request factory`` category. It + represents a call to :meth:`pyramid.config.Configurator.set_request_factory` + (or the Configurator constructor equivalent); it will have the following + data. + + ``factory`` + + The factory object (the resolved ``factory`` argument to + ``set_request_factory``). + ``locale negotiator`` -``translation directories`` + Only one introspectable will exist in the ``locale negotiator`` category. + It represents a call to + :meth:`pyramid.config.Configurator.set_locale_negotiator` (or the + Configurator constructor equivalent); it will have the following data. + + ``negotiator`` + + The factory object (the resolved ``negotiator`` argument to + ``set_locale_negotiator``). ``renderer factories`` + Each introspectable in the ``renderer factories`` category represents a + call to :meth:`pyramid.config.Configurator.add_renderer` (or the + Configurator constructor equivalent); each will have the following data. + + ``name`` + + The name of the renderer (the value of the ``name`` argument to + ``add_renderer``). + + ``factory`` + + The factory object (the resolved ``factory`` argument to + ``add_renderer``). + +``renderer globals factory`` + + There will be one and only one introspectable in the ``renderer globals + factory`` category. It represents a call to + :meth:`pyramid.config.Configurator.set_renderer_globals_factory`; it will + have the following data. + + ``factory`` + + The factory object (the resolved ``factory`` argument to + ``set_renderer_globals_factory``). + ``routes`` + Each introspectable in the ``routes`` category represents a call to + :meth:`pyramid.config.Configurator.add_route`; each will have the following + data. + + ``name`` + + The ``name`` argument passed to ``add_route``. + + ``pattern`` + + The ``pattern`` argument passed to ``add_route``. + + ``factory`` + + The (resolved) ``factory`` argument passed to ``add_route``. + + ``xhr`` + + The ``xhr`` argument passed to ``add_route``. + + ``request_method`` + + The ``request_method`` argument passed to ``add_route``. + + ``request_methods`` + + A sequence of request method names implied by the ``request_method`` + argument passed to ``add_route``. + + ``path_info`` + + The ``path_info`` argument passed to ``add_route``. + + ``request_param`` + + The ``request_param`` argument passed to ``add_route``. + + ``header`` + + The ``header`` argument passed to ``add_route``. + + ``accept`` + + The ``accept`` argument passed to ``add_route``. + + ``traverse`` + + The ``traverse`` argument passed to ``add_route``. + + ``custom_predicates`` + + The ``custom_predicates`` argument passed to ``add_route``. + + ``pregenerator`` + + The ``pregenerator`` argument passed to ``add_route``. + + ``pregenerator`` + + The ``static`` argument passed to ``add_route``. + + ``pregenerator`` + + The ``use_global_views`` argument passed to ``add_route``. + + ``object`` + + The :class:`pyramid.interfaces.IRoute` object that is used to perform + matching and generation for this route. + ``authentication policy`` + There will be one and only one introspectable in the ``authentication + policy`` category. It represents a call to the + :meth:`pyramid.config.Configurator.set_authentication_policy` method (or + its Configurator constructor equivalent); it will have the following data. + + ``policy`` + + The policy object (the resolved ``policy`` argument to + ``set_authentication_policy``). + ``authorization policy`` + There will be one and only one introspectable in the ``authorization + policy`` category. It represents a call to the + :meth:`pyramid.config.Configurator.set_authorization_policy` method (or its + Configurator constructor equivalent); it will have the following data. + + ``policy`` + + The policy object (the resolved ``policy`` argument to + ``set_authorization_policy``). + ``default permission`` -``tweens (implicit)`` + There will be one and only one introspectable in the ``default permission`` + category. It represents a call to the + :meth:`pyramid.config.Configurator.set_default_permission` method (or its + Configurator constructor equivalent); it will have the following data. + + ``value`` + + The permission name passed to ``set_default_permission``. ``views`` -``templates`` + Each introspectable in the ``views`` category represents a call to + :meth:`pyramid.config.Configurator.add_view`; each will have the following + data. + + ``name`` + + The ``name`` argument passed to ``add_view``. + + ``context`` + + The (resolved) ``context`` argument passed to ``add_view``. + + ``containment`` + + The (resolved) ``containment`` argument passed to ``add_view``. + + ``request_param`` + + The ``request_param`` argument passed to ``add_view``. + + ``request_methods`` + + A sequence of request method names implied by the ``request_method`` + argument passed to ``add_view``. + + ``route_name`` + + The ``route_name`` argument passed to ``add_view``. + + ``attr`` + + The ``attr`` argument passed to ``add_view``. + + ``xhr`` + + The ``xhr`` argument passed to ``add_view``. + + ``accept`` + + The ``accept`` argument passed to ``add_view``. + + ``header`` + + The ``header`` argument passed to ``add_view``. + + ``path_info`` + + The ``path_info`` argument passed to ``add_view``. + + ``match_param`` + + The ``match_param`` argument passed to ``add_view``. + + ``callable`` + + The (resolved) ``view`` argument passed to ``add_view``. Represents the + "raw" view callable. + + ``derived_callable`` + + The view callable derived from the ``view`` argument passed to + ``add_view``. Represents the view callable which Pyramid itself calls + (wrapped in security and other wrappers). + + ``mapper`` + + The (resolved) ``mapper`` argument passed to ``add_view``. + + ``decorator`` + + The (resolved) ``decorator`` argument passed to ``add_view``. ``permissions`` + Each introspectable in the ``permissions`` category represents a call to + :meth:`pyramid.config.Configurator.add_view` that has an explicit + ``permission`` argument to *or* a call to + :meth:`pyramid.config.Configurator.set_default_permission`; each will have + the following data. + + ``value`` + + The permission name passed to ``add_view`` or ``set_default_permission``. + +``templates`` + + Each introspectable in the ``templates`` category represents a call to + :meth:`pyramid.config.Configurator.add_view` that has a ``renderer`` + argument which points to a template; each will have the following data. + + ``name`` + + The renderer's name (a string). + + ``type`` + + The renderer's type (a string). + + ``renderer`` + + The :class:`pyramid.interfaces.IRendererInfo` object which represents + this template's renderer. + ``view mapper`` + XXX default view mapper category? + + Each introspectable in the ``permissions`` category represents a call to + :meth:`pyramid.config.Configurator.add_view` that has an explicit + ``mapper`` argument to *or* a call to + :meth:`pyramid.config.Configurator.set_view_mapper`; each will have + the following data. + + ``mapper`` + + The (resolved) ``mapper`` argument passed to ``add_view`` or + ``set_view_mapper``. + +``asset overrides`` + + XXX + +``translation directories`` + + XXX + +``tweens (implicit)`` + + XXX + +``tweens (explicit)`` + + XXX + diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py index cfa91d6d9..530b6cc28 100644 --- a/pyramid/config/factories.py +++ b/pyramid/config/factories.py @@ -29,7 +29,8 @@ class FactoriesConfiguratorMixin(object): self.registry.registerUtility(factory, IRootFactory) self.registry.registerUtility(factory, IDefaultRootFactory) # b/c - intr = self.introspectable('root factories', None, + intr = self.introspectable('root factories', + None, self.object_description(factory), 'root factory') intr['factory'] = factory diff --git a/pyramid/config/i18n.py b/pyramid/config/i18n.py index 2c149923c..5770f84c8 100644 --- a/pyramid/config/i18n.py +++ b/pyramid/config/i18n.py @@ -40,7 +40,8 @@ class I18NConfiguratorMixin(object): """ def register(): self._set_locale_negotiator(negotiator) - intr = self.introspectable('locale negotiator', None, repr(negotiator), + intr = self.introspectable('locale negotiator', None, + self.object_description(negotiator), 'locale negotiator') intr['negotiator'] = negotiator self.action(ILocaleNegotiator, register, introspectables=(intr,)) diff --git a/pyramid/config/rendering.py b/pyramid/config/rendering.py index 0d37e201f..926511b7b 100644 --- a/pyramid/config/rendering.py +++ b/pyramid/config/rendering.py @@ -48,7 +48,8 @@ class RenderingConfiguratorMixin(object): name = '' def register(): self.registry.registerUtility(factory, IRendererFactory, name=name) - intr = self.introspectable('renderer factories', name, + intr = self.introspectable('renderer factories', + name, self.object_description(factory), 'renderer factory') intr['factory'] = factory @@ -74,7 +75,9 @@ class RenderingConfiguratorMixin(object): .. warning:: - This method is deprecated as of Pyramid 1.1. + This method is deprecated as of Pyramid 1.1. Use a BeforeRender + event subscriber as documented in the :ref:`hooks_chapter` chapter + instead. .. note:: diff --git a/pyramid/config/routes.py b/pyramid/config/routes.py index 35ad0f8c4..2628f9cac 100644 --- a/pyramid/config/routes.py +++ b/pyramid/config/routes.py @@ -14,6 +14,7 @@ from pyramid.urldispatch import RoutesMapper from pyramid.config.util import ( action_method, make_predicates, + as_sorted_tuple, ) class RoutesConfiguratorMixin(object): @@ -369,14 +370,17 @@ class RoutesConfiguratorMixin(object): mapper = self.get_routes_mapper() - intr = self.introspectable('routes', name, + introspectables = [] + + intr = self.introspectable('routes', + name, '%s (pattern: %r)' % (name, pattern), 'route') intr['name'] = name intr['pattern'] = pattern intr['factory'] = factory intr['xhr'] = xhr - intr['request_method'] = request_method + intr['request_methods'] = as_sorted_tuple(request_method) intr['path_info'] = path_info intr['request_param'] = request_param intr['header'] = header @@ -386,6 +390,17 @@ class RoutesConfiguratorMixin(object): intr['pregenerator'] = pregenerator intr['static'] = static intr['use_global_views'] = use_global_views + introspectables.append(intr) + + if factory: + factory_intr = self.introspectable('root factories', + name, + self.object_description(factory), + 'root factory') + factory_intr['factory'] = factory + factory_intr['route_name'] = name + factory_intr.relate('routes', name) + introspectables.append(factory_intr) def register_route_request_iface(): request_iface = self.registry.queryUtility(IRouteRequest, name=name) @@ -414,7 +429,7 @@ class RoutesConfiguratorMixin(object): # But IRouteRequest interfaces must be registered before we begin to # process view registrations (in phase 3) self.action(('route', name), register_route_request_iface, - order=PHASE2_CONFIG, introspectables=(intr,)) + order=PHASE2_CONFIG, introspectables=introspectables) # deprecated adding views from add_route; must come after # route registration for purposes of autocommit ordering diff --git a/pyramid/config/security.py b/pyramid/config/security.py index 1830fb900..a0ea173ba 100644 --- a/pyramid/config/security.py +++ b/pyramid/config/security.py @@ -126,8 +126,10 @@ class SecurityConfiguratorMixin(object): permission, 'default permission') intr['value'] = permission - perm_intr = self.introspectable('permissions', permission, - permission, 'permission') + perm_intr = self.introspectable('permissions', + permission, + permission, + 'permission') perm_intr['value'] = permission # default permission used during view registration (phase 3) self.action(IDefaultPermission, register, order=PHASE1_CONFIG, diff --git a/pyramid/config/tweens.py b/pyramid/config/tweens.py index 22ea21a57..e36e9e84e 100644 --- a/pyramid/config/tweens.py +++ b/pyramid/config/tweens.py @@ -146,7 +146,8 @@ class TweensConfiguratorMixin(object): registry.registerUtility(tweens, ITweens) ex_intr = self.introspectable('tweens (implicit)', ('tween', EXCVIEW, False), - EXCVIEW, 'implicit tween') + EXCVIEW, + 'implicit tween') ex_intr['factory'] = excview_tween_factory ex_intr['type'] = 'implicit' ex_intr['under'] = None @@ -163,8 +164,10 @@ class TweensConfiguratorMixin(object): discriminator = ('tween', name, explicit) tween_type = explicit and 'explicit' or 'implicit' - intr = self.introspectable('tweens (%s)' % tween_type, discriminator, - name, '%s tween' % tween_type) + intr = self.introspectable('tweens (%s)' % tween_type, + discriminator, + name, + '%s tween' % tween_type) intr['factory'] = tween_factory intr['type'] = tween_type intr['under'] = under diff --git a/pyramid/config/views.py b/pyramid/config/views.py index 5c4470834..02dcbf2ee 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -962,7 +962,7 @@ class ViewsConfiguratorMixin(object): context=context, containment=containment, request_param=request_param, - request_method=request_method, + request_methods=request_method, route_name=route_name, attr=attr, xhr=xhr, @@ -1116,9 +1116,18 @@ class ViewsConfiguratorMixin(object): (IExceptionViewClassifier, request_iface, context), IMultiView, name=name) + if mapper: + mapper_intr = self.introspectable('view mappers', + discriminator, + view_desc, + 'view mapper') + mapper_intr['mapper'] = mapper + mapper_intr.relate('views', discriminator) + introspectables.append(mapper_intr) if route_name: view_intr.relate('routes', route_name) # see add_route if renderer is not None and renderer.name and '.' in renderer.name: + # it's a template tmpl_intr = self.introspectable('templates', discriminator, renderer.name, 'template') tmpl_intr.relate('views', discriminator) @@ -1360,9 +1369,11 @@ class ViewsConfiguratorMixin(object): self.registry.registerUtility(mapper, IViewMapperFactory) # IViewMapperFactory is looked up as the result of view config # in phase 3 - intr = self.introspectable('view mapper', IViewMapperFactory, + intr = self.introspectable('view mappers', + IViewMapperFactory, self.object_description(mapper), - 'view mapper') + 'default view mapper') + intr['mapper'] = mapper self.action(IViewMapperFactory, register, order=PHASE1_CONFIG, introspectables=(intr,)) diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index 05881571e..a8a9cc55a 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -946,14 +946,15 @@ class IIntrospectable(Interface): title = Attribute('Text title describing this introspectable') type_name = Attribute('Text type name describing this introspectable') order = Attribute('integer order in which registered with introspector ' - '(managed by introspector, usually') + '(managed by introspector, usually)') category_name = Attribute('introspection category name') discriminator = Attribute('introspectable discriminator (within category) ' '(must be hashable)') discriminator_hash = Attribute('an integer hash of the discriminator') - action_info = Attribute('An object representing the caller that invoked ' - 'the creation of this introspectable (usually ' - 'a sentinel until updated during self.register)') + action_info = Attribute('An IActionInfo object representing the caller ' + 'that invoked the creation of this introspectable ' + '(usually a sentinel until updated during ' + 'self.register)') def relate(category_name, discriminator): """ Indicate an intent to relate this IIntrospectable with another diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py index a9a4d5836..d80a6bb64 100644 --- a/pyramid/tests/test_config/test_views.py +++ b/pyramid/tests/test_config/test_views.py @@ -1362,6 +1362,20 @@ class TestViewsConfigurationMixin(unittest.TestCase): request = self._makeRequest(config) self.assertEqual(view(None, request), 'OK') + def test_add_view_with_mapper(self): + from pyramid.renderers import null_renderer + class Mapper(object): + def __init__(self, **kw): + self.__class__.kw = kw + def __call__(self, view): + return view + config = self._makeOne(autocommit=True) + def view(context, request): return 'OK' + config.add_view(view=view, mapper=Mapper, renderer=null_renderer) + view = self._getViewCallable(config) + self.assertEqual(view(None, None), 'OK') + self.assertEqual(Mapper.kw['mapper'], Mapper) + def test_derive_view_function(self): from pyramid.renderers import null_renderer def view(request): |
