From 5e92f34f019e2e6e06ff4c0b5c019349591f9a43 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 24 Nov 2011 17:37:19 -0500 Subject: add type name to iintrospectable constructor; rejigger responsibilities between iintrospectable and iintrospector --- pyramid/config/__init__.py | 9 ++- pyramid/config/adapters.py | 12 +++- pyramid/config/assets.py | 4 +- pyramid/config/factories.py | 19 +++--- pyramid/config/i18n.py | 5 +- pyramid/config/introspection.py | 61 ++++++++------------ pyramid/config/routes.py | 2 +- pyramid/config/views.py | 17 +++--- pyramid/interfaces.py | 125 +++++++++++++++++++++++++++------------- 9 files changed, 152 insertions(+), 102 deletions(-) diff --git a/pyramid/config/__init__.py b/pyramid/config/__init__.py index d78d46cb8..d7e95b7e9 100644 --- a/pyramid/config/__init__.py +++ b/pyramid/config/__init__.py @@ -477,12 +477,14 @@ class Configurator( autocommit = self.autocommit action_info = self.action_info + introspector = self.introspector if autocommit: if callable is not None: callable(*args, **kw) for introspectable in introspectables: - introspectable(self.introspector, action_info) + if introspector is not None: + introspector.register(introspectable, action_info) else: self.action_state.action( @@ -960,8 +962,9 @@ class ActionState(object): tb) finally: del t, v, tb - for introspectable in introspectables: - introspectable(introspector, info) + if introspector is not None: + for introspectable in introspectables: + introspector.register(introspectable, info) finally: if clear: diff --git a/pyramid/config/adapters.py b/pyramid/config/adapters.py index df6b64fa8..620464ed3 100644 --- a/pyramid/config/adapters.py +++ b/pyramid/config/adapters.py @@ -27,7 +27,10 @@ class AdaptersConfiguratorMixin(object): iface = (iface,) def register(): self.registry.registerHandler(subscriber, iface) - intr = self.introspectable('subscriber', id(subscriber), 'subscriber') + intr = self.introspectable('subscribers', + id(subscriber), + repr(subscriber), + 'subscriber') intr['subscriber'] = subscriber intr['interfaces'] = iface self.action(None, register, introspectables=(intr,)) @@ -56,8 +59,11 @@ class AdaptersConfiguratorMixin(object): else: reg.registerAdapter(adapter, (type_or_iface,), IResponse) discriminator = (IResponse, type_or_iface) - intr = self.introspectable('response adapter', discriminator, - 'response adapter') + intr = self.introspectable( + 'response adapters', + discriminator, + repr(adapter), + 'response adapter') intr['adapter'] = adapter intr['type'] = type_or_iface self.action(discriminator, register, introspectables=(intr,)) diff --git a/pyramid/config/assets.py b/pyramid/config/assets.py index b37f11865..7080e5e7c 100644 --- a/pyramid/config/assets.py +++ b/pyramid/config/assets.py @@ -237,8 +237,10 @@ class AssetsConfiguratorMixin(object): override(from_package, path, to_package, override_prefix) intr = self.introspectable( - 'asset override', + 'asset overrides', (package, override_package, path, override_prefix), + '%s/%s -> %s/%s' % (package, path, override_package, + override_prefix), 'asset override', ) intr['package'] = package diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py index c557ff72e..6cc90a80b 100644 --- a/pyramid/config/factories.py +++ b/pyramid/config/factories.py @@ -27,17 +27,18 @@ class FactoriesConfiguratorMixin(object): self.registry.registerUtility(factory, IRootFactory) self.registry.registerUtility(factory, IDefaultRootFactory) # b/c - intr = self.introspectable('root factory', None, 'root factory') + intr = self.introspectable('root factories', None, repr(factory), + 'root factory') intr['factory'] = factory self.action(IRootFactory, register, introspectables=(intr,)) _set_root_factory = set_root_factory # bw compat @action_method - def set_session_factory(self, session_factory): + def set_session_factory(self, factory): """ Configure the application with a :term:`session factory`. If this - method is called, the ``session_factory`` argument must be a session + method is called, the ``factory`` argument must be a session factory callable or a :term:`dotted Python name` to that factory. .. note:: @@ -46,11 +47,12 @@ class FactoriesConfiguratorMixin(object): :class:`pyramid.config.Configurator` constructor can be used to achieve the same purpose. """ - session_factory = self.maybe_dotted(session_factory) + factory = self.maybe_dotted(factory) def register(): - self.registry.registerUtility(session_factory, ISessionFactory) - intr = self.introspectable('session factory', None, 'session factory') - intr['factory'] = session_factory + self.registry.registerUtility(factory, ISessionFactory) + intr = self.introspectable('session factory', None, repr(factory), + 'session factory') + intr['factory'] = factory self.action(ISessionFactory, register, introspectables=(intr,)) @action_method @@ -72,7 +74,8 @@ class FactoriesConfiguratorMixin(object): factory = self.maybe_dotted(factory) def register(): self.registry.registerUtility(factory, IRequestFactory) - intr = self.introspectable('request factory', None, 'request factory') + intr = self.introspectable('request factory', None, repr(factory), + 'request factory') intr['factory'] = factory self.action(IRequestFactory, register, introspectables=(intr,)) diff --git a/pyramid/config/i18n.py b/pyramid/config/i18n.py index 5b60f1d99..b405f3683 100644 --- a/pyramid/config/i18n.py +++ b/pyramid/config/i18n.py @@ -38,7 +38,7 @@ class I18NConfiguratorMixin(object): """ def register(): self._set_locale_negotiator(negotiator) - intr = self.introspectable('locale negotiator', None, + intr = self.introspectable('locale negotiator', None, repr(negotiator), 'locale negotiator') intr['negotiator'] = negotiator self.action(ILocaleNegotiator, register, introspectables=(intr,)) @@ -87,7 +87,8 @@ class I18NConfiguratorMixin(object): if not os.path.isdir(os.path.realpath(directory)): raise ConfigurationError('"%s" is not a directory' % directory) - intr = self.introspectable('translation directory', directory, spec) + intr = self.introspectable('translation directories', directory, + spec, 'translation directory') intr['directory'] = directory introspectables.append(intr) directories.append(directory) diff --git a/pyramid/config/introspection.py b/pyramid/config/introspection.py index 1d766fb5d..666728fc5 100644 --- a/pyramid/config/introspection.py +++ b/pyramid/config/introspection.py @@ -3,32 +3,18 @@ import operator from zope.interface import implementer from pyramid.interfaces import ( - IIntrospector, - IIntrospectable + IIntrospectable, + IIntrospector ) @implementer(IIntrospector) class Introspector(object): - action_info = None def __init__(self): self._refs = {} self._categories = {} self._counter = 0 - def add(self, category_name, discriminator): - category = self._categories.setdefault(category_name, {}) - intr = category.get(discriminator) - if intr is None: - intr = Introspectable(category_name, discriminator) - category[intr.discriminator] = intr - category[intr.discriminator_hash] = intr - intr.order = self._counter - self._counter += 1 - return intr - - # for adding custom introspectables (instead of using .add) - - def add_intr(self, intr): + def add(self, intr): category = self._categories.setdefault(intr.category_name, {}) category[intr.discriminator] = intr category[intr.discriminator_hash] = intr @@ -49,9 +35,12 @@ class Introspector(object): 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 sorted(self._categories.keys()): + for category_name in self.categories(): L.append((category_name, self.get_category(category_name, sort_fn))) return L @@ -73,7 +62,6 @@ class Introspector(object): category_name, discriminator = pair intr = self._categories.get(category_name, {}).get(discriminator) if intr is None: - import pdb; pdb.set_trace() raise KeyError((category_name, discriminator)) introspectables.append(intr) return introspectables @@ -101,16 +89,30 @@ class Introspector(object): 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 .add/.add_intr - action_info = '' + order = 0 # mutated by introspector.add/introspector.add_intr + action_info = '' # mutated by introspector.register - def __init__(self, category_name, discriminator, title): + 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 = [] @@ -120,25 +122,10 @@ class Introspectable(dict): def unrelate(self, category_name, discriminator): self.unrelations.append((category_name, discriminator)) - def __call__(self, introspector, action_info): - self.action_info = action_info - introspector.add_intr(self) - for category_name, discriminator in self.relations: - introspector.relate((self.category_name, self.discriminator), - (category_name, discriminator)) - - for category_name, discriminator in self.unrelations: - introspector.unrelate((self.category_name, self.discriminator), - (category_name, discriminator)) - @property def discriminator_hash(self): return hash(self.discriminator) - @property - def related(self, introspector): - return introspector.related(self) - def __hash__(self): return hash((self.category_name,) + (self.discriminator,)) diff --git a/pyramid/config/routes.py b/pyramid/config/routes.py index d4e8a94ab..46f69300d 100644 --- a/pyramid/config/routes.py +++ b/pyramid/config/routes.py @@ -365,7 +365,7 @@ class RoutesConfiguratorMixin(object): mapper = self.get_routes_mapper() - intr = self.introspectable('route', name, name) + intr = self.introspectable('routes', name, name, 'route') intr['name'] = name intr['pattern'] = pattern intr['factory'] = factory diff --git a/pyramid/config/views.py b/pyramid/config/views.py index df0a64231..f4e7bb1db 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -931,7 +931,8 @@ class ViewsConfiguratorMixin(object): xhr, accept, header, path_info, match_param] discriminator.extend(sorted([hash(x) for x in custom_predicates])) discriminator = tuple(discriminator) - view_intr = self.introspectable('view', discriminator, 'view') + view_intr = self.introspectable('views', discriminator, repr(view), + 'view') view_intr.update( dict(name=name, context=context, @@ -1092,20 +1093,20 @@ class ViewsConfiguratorMixin(object): IMultiView, name=name) if route_name: - view_intr.relate('route', route_name) # see add_route + view_intr.relate('routes', route_name) # see add_route if renderer is not None and renderer.name and '.' in renderer.name: - tmpl_intr = self.introspectable('template', discriminator, - renderer.name) - tmpl_intr.relate('view', discriminator) + tmpl_intr = self.introspectable('templates', discriminator, + renderer.name, 'template') + tmpl_intr.relate('views', discriminator) tmpl_intr['name'] = renderer.name tmpl_intr['type'] = renderer.type tmpl_intr['renderer'] = renderer introspectables.append(tmpl_intr) if permission is not None: - perm_intr = self.introspectable('permission', permission, - permission) + perm_intr = self.introspectable('permissions', permission, + permission, 'permission') perm_intr['value'] = permission - perm_intr.relate('view', discriminator) + perm_intr.relate('views', discriminator) introspectables.append(perm_intr) self.action(discriminator, register, introspectables=introspectables) diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index fd66ebca5..81ff6df1e 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -858,16 +858,6 @@ class IRendererInfo(Interface): class IIntrospector(Interface): - def add(category_name, discriminator): - """ If the introspectable of category_name with ``discriminator`` - already exists, return it; otherwise create an IIntrospectable object - and return it""" - - def add_intr(intr): - """ Add the IIntrospectable ``intr`` (use - instead of :meth:`pyramid.interfaces.IIntrospector.add` when you have - a custom IIntrospectable). """ - def get(category_name, discriminator, default=None): """ Get the IIntrospectable related to the category_name and the discriminator (or discriminator hash) ``discriminator``. If it does @@ -884,6 +874,10 @@ class IIntrospector(Interface): value from it (ala the ``key`` function of Python's ``sorted`` callable).""" + def categories(): + """ Return a sorted sequence of category names known by + this introspector """ + def categorized(sort_fn=None): """ Get a sequence of tuples in the form ``[(category_name, [{'introspectable':IIntrospectable, 'related':[sequence of related @@ -896,8 +890,57 @@ class IIntrospector(Interface): def remove(category_name, discriminator): """ Remove the IIntrospectable related to ``category_name`` and - ``discriminator`` from the introspector. This method will not raise - an error if the intrpsectable does not exist. """ + ``discriminator`` from the introspector, and fix up any relations + that the introspectable participates in. This method will not raise + an error if an introspectable related to the category name and + discriminator does not exist.""" + + def related(category_name, discriminator): + """ Return a sequence of IIntrospectables related to the + IIntrospectable associated with (``category_name``, + ``discriminator``). Return the empty sequence if no relations for + exist.""" + + def register(introspectable, action_info=''): + """ Register an IIntrospectable with this introspector. This method + is invoked during action execution. Adds the introspectable and its + relations to the introspector. ``introspectable`` should be an + object implementing IIntrospectable. ``action_info`` should be a + string representing the call that registered this introspectable + (e.g. with line numbers, etc). Pseudocode for an implementation of + this method: + + .. code-block:: python + + def register(self, introspectable, action_info=''): + i = introspectable + i.action_info = action_info + self.add(introspectable) + for category_name, discriminator in i.relations: + self.relate(( + i.category_name, i.discriminator), + (category_name, discriminator)) + + for category_name, discriminator in i.unrelations: + self.unrelate(( + i.category_name, i.discriminator), + (category_name, discriminator)) + + The introspectable you wished to be related to or unrelated from must + have already been added via + :meth:`pyramid.interfaces.IIntrospector.add` (or by this method, + which implies an add) before this method is called; a :exc:`KeyError` + will result if this is not true. + """ + + def add(intr): + """ Add the IIntrospectable ``intr`` (use instead of + :meth:`pyramid.interfaces.IIntrospector.add` when you have a custom + IIntrospectable). Replaces any existing introspectable registered + using the same category/discriminator. + + This method is not typically called directly, instead it's called + indirectly by :meth:`pyramid.interfaces.IIntrospector.register`""" def relate(*pairs): """ Given any number of of ``(category_name, discriminator)`` pairs @@ -905,7 +948,11 @@ class IIntrospector(Interface): to each other. The introspectable related to each pair must have already been added via ``.add`` or ``.add_intr``; a :exc:`KeyError` will result if this is not true. An error will not be raised if any - pair has already been associated with another.""" + pair has already been associated with another. + + This method is not typically called directly, instead it's called + indirectly by :meth:`pyramid.interfaces.IIntrospector.register` + """ def unrelate(*pairs): """ Given any number of of ``(category_name, discriminator)`` pairs @@ -913,13 +960,11 @@ class IIntrospector(Interface): from each other. The introspectable related to each pair must have already been added via ``.add`` or ``.add_intr``; a :exc:`KeyError` will result if this is not true. An error will not be raised if any - pair is not already related to another.""" + pair is not already related to another. - def related(category_name, discriminator): - """ Return a sequence of IIntrospectables related to the - IIntrospectable associated with (``category_name``, - ``discriminator``). Return the empty sequence if no relations for - exist.""" + This method is not typically called directly, instead it's called + indirectly by :meth:`pyramid.interfaces.IIntrospector.register` + """ class IIntrospectable(Interface): @@ -928,35 +973,37 @@ class IIntrospectable(Interface): must also implement all the methods of Python's ``collections.MutableMapping`` (the "dictionary interface").""" - order = Attribute('integer order in which registered with introspector') + 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') category_name = Attribute('introspection category name') discriminator = Attribute('introspectable discriminator (within category) ' '(must be hashable)') discriminator_hash = Attribute('an integer hash of the discriminator') + relations = Attribute('A sequence of ``(category_name, discriminator)`` ' + 'pairs indicating the relations that this ' + 'introspectable wants to establish when registered ' + 'with the introspector') + unrelations = Attribute('A sequence of ``(category_name, discriminator)`` ' + 'pairs indicating the relations that this ' + 'introspectable wants to break when registered ' + 'with the introspector') + action_info = Attribute('A string representing the caller that invoked ' + 'the creation of this introspectable (usually ' + 'managed by IIntrospector during registration)') def relate(category_name, discriminator): - """ Relate this IIntrospectable with another IIntrospectable (the one - associated with the ``category_name`` and ``discriminator``). The - introspectable you wish to relate to must have already been added via - :meth:`pyramid.interfaces.IIntrospector.add` or - :meth:`pyramid.interfaces.IIntrospector.add_intr`; a :exc:`KeyError` - will result if this is not true. + """ Indicate an intent to relate this IIntrospectable with another + IIntrospectable (the one associated with the ``category_name`` and + ``discriminator``) during action execution. """ def unrelate(category_name, discriminator): - """ Break any relationship between this IIntrospectable and another - IIntrospectable (the one associated with the ``category_name`` and - ``discriminator``). The introspectable you wish to unrelate from must - have already been added via - :meth:`pyramid.interfaces.IIntrospector.add` or - :meth:`pyramid.interfaces.IIntrospector.add_intr`; a :exc:`KeyError` - will result if this is not true. """ - - def related(introspector): - """ Return a sequence of related IIntrospectables """ - - def __call__(introspector, action_info): - """ Register this IIntrospectable with the introspector """ + """ Indicate an intent to break the relationship between this + IIntrospectable with another IIntrospectable (the one associated with + the ``category_name`` and ``discriminator``) during action execution. + """ # configuration phases: a lower phase number means the actions associated # with this phase will be executed earlier than those with later phase -- cgit v1.2.3