Skip to content

Commit

Permalink
- Add paster proute command which displays a summary of the routing
Browse files Browse the repository at this point in the history
  table.  See the narrative documentation section within the "URL Dispatch"
  chapter entitled "Displaying All Application Routes".

- Added narrative documentation section within the "URL Dispatch" chapter
  entitled "Displaying All Application Routes" (for ``paster proutes``
  command).
  • Loading branch information
mcdonc committed Dec 27, 2010
1 parent e8db031 commit 90a327b
Show file tree
Hide file tree
Showing 7 changed files with 281 additions and 19 deletions.
8 changes: 8 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ Features
arguments to add_route work by raising an exception during configuration if
view-related arguments exist but no ``view`` argument is passed.

- Add ``paster proute`` command which displays a summary of the routing
table. See the narrative documentation section within the "URL Dispatch"
chapter entitled "Displaying All Application Routes".

Paster Templates
----------------

Expand Down Expand Up @@ -81,6 +85,10 @@ Documentation

- Merge "Static Assets" chapter into the "Assets" chapter.

- Added narrative documentation section within the "URL Dispatch" chapter
entitled "Displaying All Application Routes" (for ``paster proutes``
command).

1.0a7 (2010-12-20)
==================

Expand Down
2 changes: 2 additions & 0 deletions docs/narr/project.rst
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ create`` -generated project. Within a project generated by the
single: IPython
single: paster pshell

.. _interactive_shell:

The Interactive Shell
---------------------

Expand Down
38 changes: 38 additions & 0 deletions docs/narr/urldispatch.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,44 @@ which you started the application from. For example:
See :ref:`environment_chapter` for more information about how, and where to
set these values.

.. index::
pair: routes; printing
single: paster proutes

Displaying All Application Routes
---------------------------------

You can use the ``paster proutes`` command in a terminal window to print a
summary of routes related to your application. Much like the ``paster
pshell`` command (see :ref:`interactive shell`), the ``paster proutes``
command accepts two arguments. The first argument to ``proutes`` is the path
to your application's ``.ini`` file. The second is the ``app`` section name
inside the ``.ini`` file which points to your application.

For example:

.. code-block:: text
:linenos:
[chrism@thinko MyProject]$ ../bin/paster proutes development.ini MyProject
Name Pattern View
---- ------- ----
home / <function my_view>
home2 / <function my_view>
another /another None
static/ static/*subpath <static_view object>
catchall /*subpath <function static_view>
``paster proutes`` generates a table. The table has three columns: a Name
name column, a Pattern column, and a View column. The items listed in the
Name column are route names, the items listen in the Pattern column are route
patterns, and the items listed in the View column are representations of the
view callable that will be invoked when a request matches the associated
route pattern. The view column may show ``None`` if no associated view
callable could be found. If no routes are configured within your
application, nothing will be printed to the console when ``paster proutes``
is executed.

References
----------

Expand Down
109 changes: 95 additions & 14 deletions pyramid/paster.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,22 @@ def get_app(config_file, name, loadapp=loadapp):
return app

_marker = object()
class PShellCommand(Command):

class PCommand(Command):
get_app = staticmethod(get_app) # hook point
get_root = staticmethod(get_root) # hook point
group_name = 'pyramid'
interact = (interact,) # for testing
loadapp = (loadapp,) # for testing
verbose = 3

def __init__(self, *arg, **kw):
# needs to be in constructor to support Jython (used to be at class
# scope as ``usage = '\n' + __doc__``.
self.usage = '\n' + self.__doc__
Command.__init__(self, *arg, **kw)

class PShellCommand(PCommand):
"""Open an interactive shell with a :app:`Pyramid` app loaded.
This command accepts two positional arguments:
Expand All @@ -88,26 +103,13 @@ class PShellCommand(Command):

min_args = 2
max_args = 2
group_name = 'pyramid'

parser = Command.standard_parser(simulate=True)
parser.add_option('-d', '--disable-ipython',
action='store_true',
dest='disable_ipython',
help="Don't use IPython even if it is available")

interact = (interact,) # for testing
loadapp = (loadapp,) # for testing
get_app = staticmethod(get_app) # hook point
get_root = staticmethod(get_root) # hook point
verbose = 3

def __init__(self, *arg, **kw):
# needs to be in constructor to support Jython (used to be at class
# scope as ``usage = '\n' + __doc__``.
self.usage = '\n' + self.__doc__
Command.__init__(self, *arg, **kw)

def command(self, IPShell=_marker):
if IPShell is _marker:
try: #pragma no cover
Expand Down Expand Up @@ -136,3 +138,82 @@ def command(self, IPShell=_marker):
closer()

BFGShellCommand = PShellCommand # b/w compat forever

class PRoutesCommand(PCommand):
"""Print all URL dispatch routes used by a Pyramid application in the
order in which they are evaluated. Each route includes the name of the
route, the pattern of the route, and the view callable which will be
invoked when the route is matched.
This command accepts two 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.
Example::
$ paster proutes myapp.ini main
.. 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 URL dispatch routes related to a Pyramid application"
min_args = 2
max_args = 2
stdout = sys.stdout

parser = Command.standard_parser(simulate=True)

def _get_mapper(self, app):
from pyramid.config import Configurator
registry = app.registry
config = Configurator(registry = registry)
return config.get_routes_mapper()

def out(self, msg): # pragma: no cover
print msg

def command(self):
from pyramid.request import Request
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IView
from zope.interface import Interface
from zope.interface import providedBy
config_file, section_name = self.args
app = self.get_app(config_file, section_name, loadapp=self.loadapp[0])
registry = app.registry
mapper = self._get_mapper(app)
if mapper is not None:
routes = mapper.get_routes()
fmt = '%-15s %-30s %-25s'
if not routes:
return
self.out(fmt % ('Name', 'Pattern', 'View'))
self.out(
fmt % ('-'*len('Name'), '-'*len('Pattern'), '-'*len('View')))
for route in routes:
request_iface = registry.queryUtility(IRouteRequest,
name=route.name)
view_callable = None
if request_iface is not None:
if route.factory is None:
context_iface = Interface
else:
request = Request.blank('/')
inst = route.factory(request)
context_iface = providedBy(inst)
# try with factory instance as context; views registered for
# a more general interface will be found if the context
# iface is very specific
view_callable = registry.adapters.lookup(
(IViewClassifier, request_iface, context_iface),
IView, name='', default=None)
self.out(fmt % (route.name, route.pattern, view_callable))


8 changes: 4 additions & 4 deletions pyramid/tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2338,7 +2338,7 @@ def test_add_route_no_view_with_view_attr(self):
config.add_route('name', '/pattern', view_attr='abc')
except ConfigurationError:
pass
else:
else: # pragma: no cover
raise AssertionError

def test_add_route_no_view_with_view_context(self):
Expand All @@ -2348,7 +2348,7 @@ def test_add_route_no_view_with_view_context(self):
config.add_route('name', '/pattern', view_context=DummyContext)
except ConfigurationError:
pass
else:
else: # pragma: no cover
raise AssertionError

def test_add_route_no_view_with_view_permission(self):
Expand All @@ -2358,7 +2358,7 @@ def test_add_route_no_view_with_view_permission(self):
config.add_route('name', '/pattern', view_permission='edit')
except ConfigurationError:
pass
else:
else: # pragma: no cover
raise AssertionError

def test_add_route_no_view_with_view_renderer(self):
Expand All @@ -2368,7 +2368,7 @@ def test_add_route_no_view_with_view_renderer(self):
config.add_route('name', '/pattern', view_renderer='json')
except ConfigurationError:
pass
else:
else: # pragma: no cover
raise AssertionError

def test__override_not_yet_registered(self):
Expand Down
Loading

0 comments on commit 90a327b

Please sign in to comment.