From 8b6f09d965a6e637b795a8268c310c81fcb43a10 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Fri, 25 Nov 2011 21:52:11 -0500 Subject: add rudimentary object description code --- TODO.txt | 14 ++++++++++ pyramid/config/__init__.py | 2 ++ pyramid/config/adapters.py | 4 +-- pyramid/config/factories.py | 9 ++++--- pyramid/config/routes.py | 3 ++- pyramid/config/views.py | 9 ++++++- pyramid/util.py | 66 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 100 insertions(+), 7 deletions(-) diff --git a/TODO.txt b/TODO.txt index f13cd5c6c..ed215138e 100644 --- a/TODO.txt +++ b/TODO.txt @@ -8,6 +8,20 @@ Must-Have - Fix SQLA tutorial to match alchemy scaffold. +- Introspection: + + * More specific filename/lineno info instead of opaque string (or a way to + parse the opaque string into filename/lineno info). + + * categorize() return value ordering not right yet. + + * implement ptweens and proutes based on introspection instead of current + state of affairs. + + * introspection hiding for directives? + + * usage docs. + Nice-to-Have ------------ diff --git a/pyramid/config/__init__.py b/pyramid/config/__init__.py index d7e95b7e9..e993d0700 100644 --- a/pyramid/config/__init__.py +++ b/pyramid/config/__init__.py @@ -34,6 +34,7 @@ from pyramid.settings import aslist from pyramid.threadlocal import manager from pyramid.util import DottedNameResolver from pyramid.util import WeakOrderedSet +from pyramid.util import object_description from pyramid.config.adapters import AdaptersConfiguratorMixin from pyramid.config.assets import AssetsConfiguratorMixin @@ -219,6 +220,7 @@ class Configurator( basepath = None includepath = () info = '' + object_description = staticmethod(object_description) def __init__(self, registry=None, diff --git a/pyramid/config/adapters.py b/pyramid/config/adapters.py index 620464ed3..04571bec3 100644 --- a/pyramid/config/adapters.py +++ b/pyramid/config/adapters.py @@ -29,7 +29,7 @@ class AdaptersConfiguratorMixin(object): self.registry.registerHandler(subscriber, iface) intr = self.introspectable('subscribers', id(subscriber), - repr(subscriber), + self.object_description(subscriber), 'subscriber') intr['subscriber'] = subscriber intr['interfaces'] = iface @@ -62,7 +62,7 @@ class AdaptersConfiguratorMixin(object): intr = self.introspectable( 'response adapters', discriminator, - repr(adapter), + self.object_description(adapter), 'response adapter') intr['adapter'] = adapter intr['type'] = type_or_iface diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py index 6cc90a80b..2ab9fc7db 100644 --- a/pyramid/config/factories.py +++ b/pyramid/config/factories.py @@ -27,7 +27,8 @@ class FactoriesConfiguratorMixin(object): self.registry.registerUtility(factory, IRootFactory) self.registry.registerUtility(factory, IDefaultRootFactory) # b/c - intr = self.introspectable('root factories', None, repr(factory), + intr = self.introspectable('root factories', None, + self.object_description(factory), 'root factory') intr['factory'] = factory self.action(IRootFactory, register, introspectables=(intr,)) @@ -50,7 +51,8 @@ class FactoriesConfiguratorMixin(object): factory = self.maybe_dotted(factory) def register(): self.registry.registerUtility(factory, ISessionFactory) - intr = self.introspectable('session factory', None, repr(factory), + intr = self.introspectable('session factory', None, + self.object_description(factory), 'session factory') intr['factory'] = factory self.action(ISessionFactory, register, introspectables=(intr,)) @@ -74,7 +76,8 @@ class FactoriesConfiguratorMixin(object): factory = self.maybe_dotted(factory) def register(): self.registry.registerUtility(factory, IRequestFactory) - intr = self.introspectable('request factory', None, repr(factory), + intr = self.introspectable('request factory', None, + self.object_description(factory), 'request factory') intr['factory'] = factory self.action(IRequestFactory, register, introspectables=(intr,)) diff --git a/pyramid/config/routes.py b/pyramid/config/routes.py index 46f69300d..0ab380ec1 100644 --- a/pyramid/config/routes.py +++ b/pyramid/config/routes.py @@ -365,7 +365,8 @@ class RoutesConfiguratorMixin(object): mapper = self.get_routes_mapper() - intr = self.introspectable('routes', name, name, 'route') + intr = self.introspectable('routes', name, + '%s (%s)' % (name, pattern), 'route') intr['name'] = name intr['pattern'] = pattern intr['factory'] = factory diff --git a/pyramid/config/views.py b/pyramid/config/views.py index f4e7bb1db..12193b478 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -931,7 +931,14 @@ 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('views', discriminator, repr(view), + if inspect.isclass(view) and attr: + view_desc = 'method %r of %s' % ( + attr, self.object_description(view)) + else: + view_desc = self.object_description(view) + view_intr = self.introspectable('views', + discriminator, + view_desc, 'view') view_intr.update( dict(name=name, diff --git a/pyramid/util.py b/pyramid/util.py index 3eb4b3fed..fd3bcd48d 100644 --- a/pyramid/util.py +++ b/pyramid/util.py @@ -1,5 +1,7 @@ +import inspect import pkg_resources import sys +import types import weakref from pyramid.compat import string_types @@ -228,3 +230,67 @@ 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: + + .. code-block:: python + + >>> object_description(None) + 'None' + >>> from xml.dom import minidom + >>> object_description(minidom) + 'module xml.dom.minidom' + >>> object_description(minidom.Attr) + 'class xml.dom.minidom.Attr' + >>> object_description(minidom.Attr.appendChild) + 'method appendChild of class xml.dom.minidom.Attr' + >>> + + If this method cannot identify the type of the object, a generic + description ala ``object `` will be returned. + + If the object passed is already a string, it is simply returned. If it + is a boolean, an integer, a list, a tuple, a set, or ``None``, a + (possibly shortened) string representation is returned. + """ + if isinstance(object, string_types): + return object + if isinstance(object, (bool, int, float, long, types.NoneType)): + return str(object) + if isinstance(object, (tuple, set)): + return shortrepr(object, ')') + if isinstance(object, list): + return shortrepr(object, ']') + if isinstance(object, dict): + return shortrepr(object, '}') + module = inspect.getmodule(object) + modulename = module.__name__ + if inspect.ismodule(object): + return 'module %s' % modulename + if inspect.ismethod(object): + oself = getattr(object, '__self__', None) + if oself is None: + oself = getattr(object, 'im_self', None) + oself.__class__ + return '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 + 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) + +def shortrepr(object, closer): + r = str(object) + if len(r) > 100: + r = r[:100] + '... %s' % closer + return r -- cgit v1.2.3