From 66cda3dbdf336e82b395a8b99e637509e23967bd Mon Sep 17 00:00:00 2001 From: cguardia Date: Sat, 16 Apr 2011 21:51:03 -0500 Subject: start playing with paster command to show matching views for url --- pyramid/config.py | 17 +++++++ pyramid/paster.py | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 1 + 3 files changed, 152 insertions(+) diff --git a/pyramid/config.py b/pyramid/config.py index 9fda75daa..ef520e053 100644 --- a/pyramid/config.py +++ b/pyramid/config.py @@ -2441,6 +2441,7 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, if xhr: def xhr_predicate(context, request): return request.is_xhr + xhr_predicate.__text__ = "XHR = True" weights.append(1 << 1) predicates.append(xhr_predicate) h.update('xhr:%r' % bool(xhr)) @@ -2448,6 +2449,8 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, if request_method is not None: def request_method_predicate(context, request): return request.method == request_method + msg = "Request method = %s" + request_method_predicate.__text__ = msg % request_method weights.append(1 << 2) predicates.append(request_method_predicate) h.update('request_method:%r' % request_method) @@ -2459,6 +2462,8 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, raise ConfigurationError(why[0]) def path_info_predicate(context, request): return path_info_val.match(request.path_info) is not None + msg = "path_info = %s" + path_info_predicate.__text__ = msg % path_info weights.append(1 << 3) predicates.append(path_info_predicate) h.update('path_info:%r' % path_info) @@ -2471,6 +2476,11 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, if request_param_val is None: return request_param in request.params return request.params.get(request_param) == request_param_val + if request_param_val is None: + msg = "request_param %s" % request_param + else: + msg = "request_param %s = %s" % (request_param, request_param_val) + request_param_predicate.__text__ = msg weights.append(1 << 4) predicates.append(request_param_predicate) h.update('request_param:%r=%r' % (request_param, request_param_val)) @@ -2491,6 +2501,11 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, if val is None: return False return header_val.match(val) is not None + if header_val is None: + msg = "header %s" % header_name + else: + msg = "header %s = %s" % (header_name, header_val) + header_predicate.__text__ = msg weights.append(1 << 5) predicates.append(header_predicate) h.update('header:%r=%r' % (header_name, header_val)) @@ -2654,6 +2669,7 @@ def preserve_view_attrs(view, wrapped_view): pass try: wrapped_view.__predicated__ = view.__predicated__ + wrapped_view.__predicates__ = view.__predicates__ except AttributeError: pass try: @@ -2791,6 +2807,7 @@ class ViewDeriver(object): return all((predicate(context, request) for predicate in predicates)) predicate_wrapper.__predicated__ = checker + predicate_wrapper.__predicates__ = predicates return predicate_wrapper @wraps_view diff --git a/pyramid/paster.py b/pyramid/paster.py index bc1573fb8..7ff77522e 100644 --- a/pyramid/paster.py +++ b/pyramid/paster.py @@ -195,3 +195,137 @@ class PRoutesCommand(PCommand): (IViewClassifier, request_iface, Interface), IView, name='', default=None) self.out(fmt % (route.name, route.pattern, view_callable)) + +class PViewsCommand(PCommand): + """Print, for a given URL, the views that might match. Underneath each + potentially matching route, list the predicates required. Underneath + each route+predicate set, print each view that might match and its + predicates. + + This command accepts three positional arguments: + + ``config_file`` -- specifies the PasteDeploy config file to use + for the interactive shell. + + ``section_name`` -- specifies the section name in the PasteDeploy + config file that represents the application. + + ``url`` -- specifies the URL that will be used to find matching views. + + Example:: + + $ paster proutes myapp.ini main url + + .. note:: You should use a ``section_name`` that refers to the + actual ``app`` section in the config file that points at + your Pyramid app without any middleware wrapping, or this + command will almost certainly fail. + """ + summary = "Print all views in an application that might match a URL" + min_args = 3 + max_args = 3 + stdout = sys.stdout + + parser = Command.standard_parser(simulate=True) + + def out(self, msg): # pragma: no cover + print msg + + def _find_view(self, url, registry): + """ + Accept ``url`` and ``registry``; create a :term:`request` and + find a :app:`Pyramid` view based on introspection of :term:`view + configuration` within the application registry; return the view. + """ + from zope.interface import providedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IRootFactory + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IRequestFactory + from pyramid.interfaces import IRoutesMapper + from pyramid.interfaces import ITraverser + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.request import Request + from pyramid.traversal import DefaultRootFactory + from pyramid.traversal import ResourceTreeTraverser + + q = registry.queryUtility + root_factory = q(IRootFactory, default=DefaultRootFactory) + routes_mapper = q(IRoutesMapper) + request_factory = q(IRequestFactory, default=Request) + + adapters = registry.adapters + request = None + + # create the request + environ = { + 'wsgi.url_scheme':'http', + 'SERVER_NAME':'localhost', + 'SERVER_PORT':'8080', + 'REQUEST_METHOD':'GET', + 'PATH_INFO':url, + } + request = request_factory(environ) + context = None + attrs = request.__dict__ + attrs['registry'] = registry + request_iface = IRequest + + # find the root object + root_factory = root_factory + if routes_mapper is not None: + info = routes_mapper(request) + match, route = info['match'], info['route'] + if route is not None: + attrs['matchdict'] = match + attrs['matched_route'] = route + + request_iface = registry.queryUtility( + IRouteRequest, + name=route.name, + default=IRequest) + root_factory = route.factory or root_factory + + root = root_factory(request) + attrs['root'] = root + + # find a context + traverser = adapters.queryAdapter(root, ITraverser) + if traverser is None: + traverser = ResourceTreeTraverser(root) + tdict = traverser(request) + context, view_name, subpath, traversed, vroot, vroot_path =( + tdict['context'], tdict['view_name'], tdict['subpath'], + tdict['traversed'], tdict['virtual_root'], + tdict['virtual_root_path']) + attrs.update(tdict) + + # find a view callable + context_iface = providedBy(context) + view_callable = adapters.lookup( + (IViewClassifier, request_iface, context_iface), + IView, name=view_name, default=None) + + return view_callable + + def command(self): + from pyramid.interfaces import IMultiView + + config_file, section_name, url = self.args + app = self.get_app(config_file, section_name, loadapp=self.loadapp[0]) + registry = app.registry + view = self._find_view(url, registry) + self.out('') + self.out(url) + if IMultiView.providedBy(view): + for dummy, view_wrapper, dummy in view.views: + self.out('') + for p in view_wrapper.__predicates__: + text = getattr(p, '__text__', p.__name__) + self.out(" %s" % text) + self.out(" %s" % str(view_wrapper.__original_view__)) + else: + self.out('') + self.out(view) + diff --git a/setup.py b/setup.py index 7c7c80040..4ab4cca82 100644 --- a/setup.py +++ b/setup.py @@ -85,6 +85,7 @@ setup(name='pyramid', [paste.paster_command] pshell=pyramid.paster:PShellCommand proutes=pyramid.paster:PRoutesCommand + pviews=pyramid.paster:PViewsCommand [console_scripts] bfg2pyramid = pyramid.fixers.fix_bfg_imports:main """ -- cgit v1.2.3