Chris McDonough
2011-07-10 d1574c723bad0e6fc0e02627a65af47af3f548c2
Merge branch 'mmerickel-feature.pshell'
6 files modified
482 ■■■■ changed files
docs/api/paster.rst 7 ●●●● patch | view | raw | blame | history
docs/narr/project.rst 96 ●●●●● patch | view | raw | blame | history
docs/narr/urldispatch.rst 9 ●●●●● patch | view | raw | blame | history
docs/narr/viewconfig.rst 11 ●●●● patch | view | raw | blame | history
pyramid/paster.py 150 ●●●● patch | view | raw | blame | history
pyramid/tests/test_paster.py 209 ●●●● patch | view | raw | blame | history
docs/api/paster.rst
@@ -5,9 +5,12 @@
.. module:: pyramid.paster
.. function:: get_app(config_file, name)
.. function:: get_app(config_file, name=None)
    Return the WSGI application named ``name`` in the PasteDeploy
    config file ``config_file``.
    If the ``name`` is None, this will attempt to parse the name from
    the ``config_file`` string expecting the format ``ini_file#name``.
    If no name is found, the name will default to "main".
docs/narr/project.rst
@@ -258,8 +258,9 @@
:app:`Pyramid` project's :term:`resource` and :term:`view` objects from a
Python prompt.  To do so, use your virtualenv's ``paster pshell`` command.
The first argument to ``pshell`` is the path to your application's ``.ini``
file.  The second is the ``app`` section name inside the ``.ini`` file which
The argument to ``pshell`` follows the format ``config_file#section_name``
where ``config_file`` is the path to your application's ``.ini`` file and
``section_name`` is the ``app`` section name inside the ``.ini`` file which
points to *your application* as opposed to any other section within the
``.ini`` file.  For example, if your application ``.ini`` file might have a
``[app:MyProject]`` section that looks like so:
@@ -280,16 +281,21 @@
.. code-block:: text
   [chrism@vitaminf shellenv]$ ../bin/paster pshell development.ini MyProject
   [chrism@vitaminf shellenv]$ ../bin/paster pshell development.ini#MyProject
   Python 2.4.5 (#1, Aug 29 2008, 12:27:37)
   [GCC 4.0.1 (Apple Inc. build 5465)] on darwin
   Type "help" for more information. "root" is the Pyramid app root object,
   "registry" is the Pyramid registry object.
   Default Variables:
     app          The WSGI Application
     root         The root of the default resource tree.
     registry     The Pyramid registry object.
     settings     The Pyramid settings object.
   >>> root
   <myproject.resources.MyResource object at 0x445270>
   >>> registry
   <Registry myproject>
   >>> registry.settings['debug_notfound']
   >>> settings['debug_notfound']
   False
   >>> from myproject.views import my_view
   >>> from pyramid.request import Request
@@ -297,31 +303,16 @@
   >>> my_view(r)
   {'project': 'myproject'}
Two names are made available to the pshell user as globals: ``root`` and
``registry``.  ``root`` is the the object returned by the default :term:`root
factory` in your application.  ``registry`` is the :term:`application
registry` object associated with your project's application (often accessed
within view code as ``request.registry``).
The WSGI application that is loaded will be available in the shell as the
``app`` global. Also, if the application that is loaded is the
:app:`Pyramid` app with no surrounding middleware, the ``root`` object
returned by the default :term:`root factory`, ``registry``, and ``settings``
will be available.
If you have `IPython <http://en.wikipedia.org/wiki/IPython>`_ installed in
the interpreter you use to invoke the ``paster`` command, the ``pshell``
command will use an IPython interactive shell instead of a standard Python
interpreter shell.  If you don't want this to happen, even if you have
IPython installed, you can pass the ``--disable-ipython`` flag to the
``pshell`` command to use a standard Python interpreter shell
unconditionally.
.. code-block:: text
   [chrism@vitaminf shellenv]$ ../bin/paster pshell --disable-ipython \
                                development.ini MyProject
You should always use a section name argument that refers to the actual
``app`` section within the Paste configuration file that points at your
:app:`Pyramid` application *without any middleware wrapping*.  In particular,
a section name is inappropriate as the second argument to ``pshell`` if the
configuration section it names is a ``pipeline`` rather than an ``app``.  For
example, if you have the following ``.ini`` file content:
The interactive shell will not be able to load some of the globals like
``root``, ``registry`` and ``settings`` if the section name specified when
loading ``pshell`` is not referencing your :app:`Pyramid` application directly.
For example, if you have the following ``.ini`` file content:
.. code-block:: ini
   :linenos:
@@ -341,12 +332,51 @@
Use ``MyProject`` instead of ``main`` as the section name argument to
``pshell`` against the above ``.ini`` file (e.g. ``paster pshell
development.ini MyProject``).  If you use ``main`` instead, an error will
occur.  Use the most specific reference to your application within the
``.ini`` file possible as the section name argument.
development.ini#MyProject``).
Press ``Ctrl-D`` to exit the interactive shell (or ``Ctrl-Z`` on Windows).
Extending the Shell
~~~~~~~~~~~~~~~~~~~
It is sometimes convenient when using the interactive shell often to have
some variables significant to your application already loaded as globals
when you start the ``pshell``. To facilitate this, ``pshell`` will look
for a special ``[pshell]`` section in your INI file and expose the subsequent
key/value pairs to the shell.
For example, you want to expose your model to the shell, along with the
database session so that you can mutate the model on an actual database.
Here, we'll assume your model is stored in the ``myapp.models`` package.
.. code-block:: ini
   :linenos:
   [pshell]
   m = myapp.models
   session = myapp.models.DBSession
   t = transaction
When this INI file is loaded, the extra variables ``m``, ``session`` and
``t`` will be available for use immediately. This happens regardless of
whether the ``registry`` and other special variables are loaded.
IPython
~~~~~~~
If you have `IPython <http://en.wikipedia.org/wiki/IPython>`_ installed in
the interpreter you use to invoke the ``paster`` command, the ``pshell``
command will use an IPython interactive shell instead of a standard Python
interpreter shell.  If you don't want this to happen, even if you have
IPython installed, you can pass the ``--disable-ipython`` flag to the
``pshell`` command to use a standard Python interpreter shell
unconditionally.
.. code-block:: text
   [chrism@vitaminf shellenv]$ ../bin/paster pshell --disable-ipython \
                                development.ini#MyProject
.. index::
   single: running an application
   single: paster serve
docs/narr/urldispatch.rst
@@ -1084,16 +1084,17 @@
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.
command accepts one argument with the format ``config_file#section_name``.
The ``config_file`` is the path to your application's ``.ini`` file,
and ``section_name`` 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
   [chrism@thinko MyProject]$ ../bin/paster proutes development.ini#MyProject
   Name            Pattern                        View
   ----            -------                        ----                     
   home            /                              <function my_view>
docs/narr/viewconfig.rst
@@ -795,10 +795,11 @@
configuration details in your head, even if you defined all the views
yourself. You can use the ``paster pviews`` command in a terminal window to
print a summary of matching routes and views for a given URL in your
application. The ``paster pviews`` command accepts three arguments. The
first argument to ``pviews`` 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. The third is the URL to test for matching views.
application. The ``paster pviews`` command accepts two arguments. The
first argument to ``pviews`` is the path to your application's ``.ini`` file
and section name inside the ``.ini`` file which points to your application.
This should be of the format ``config_file#section_name``. The second argument
is the URL to test for matching views.
Here is an example for a simple view configuration using :term:`traversal`:
@@ -829,7 +830,7 @@
.. code-block:: text
   :linenos:
   $ ../bin/paster pviews development.ini shootout /about
   $ ../bin/paster pviews development.ini#shootout /about
   URL = /about
pyramid/paster.py
@@ -1,4 +1,6 @@
import ConfigParser
import os
import re
import sys
from code import interact
@@ -8,6 +10,7 @@
from paste.script.command import Command
from pyramid.scripting import get_root
from pyramid.util import DottedNameResolver
from pyramid.scaffolds import PyramidTemplate # bw compat
zope.deprecation.deprecated(
@@ -15,12 +18,22 @@
                        'pyramid.scaffolds.PyramidTemplate in Pyramid 1.1'),
)
def get_app(config_file, name, loadapp=loadapp):
def get_app(config_file, name=None, loadapp=loadapp):
    """ Return the WSGI application named ``name`` in the PasteDeploy
    config file ``config_file``"""
    config_name = 'config:%s' % config_file
    config file ``config_file``.
    If the ``name`` is None, this will attempt to parse the name from
    the ``config_file`` string expecting the format ``ini_file#name``.
    If no name is found, the name will default to "main"."""
    if '#' in config_file:
        path, section = config_file.split('#', 1)
    else:
        path, section = config_file, 'main'
    if name:
        section = name
    config_name = 'config:%s' % path
    here_dir = os.getcwd()
    app = loadapp(config_name, name=name, relative_to=here_dir)
    app = loadapp(config_name, name=section, relative_to=here_dir)
    return app
_marker = object()
@@ -42,17 +55,15 @@
class PShellCommand(PCommand):
    """Open an interactive shell with a :app:`Pyramid` app loaded.
    This command accepts two positional arguments:
    This command accepts one positional argument:
    ``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.
    ``config_file#section_name`` -- specifies the PasteDeploy config file
    to use for the interactive shell. If the section_name is left off,
    ``main`` will be assumed.
    Example::
        $ paster pshell myapp.ini main
        $ paster pshell myapp.ini#main
    .. note:: You should use a ``section_name`` that refers to the
              actual ``app`` section in the config file that points at
@@ -62,14 +73,30 @@
    """
    summary = "Open an interactive shell with a Pyramid application loaded"
    min_args = 2
    max_args = 2
    min_args = 1
    max_args = 1
    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")
    ConfigParser = ConfigParser.ConfigParser # testing
    def pshell_file_config(self, filename):
        resolver = DottedNameResolver(None)
        self.loaded_objects = {}
        self.object_help = {}
        config = self.ConfigParser()
        config.read(filename)
        try:
            items = config.items('pshell')
        except ConfigParser.NoSectionError:
            return
        for k, v in items:
            self.loaded_objects[k] = resolver.maybe_resolve(v)
            self.object_help[k] = v
    def command(self, IPShell=_marker):
        # IPShell passed to command method is for testing purposes
@@ -78,14 +105,59 @@
                from IPython.Shell import IPShell
            except ImportError:
                IPShell = None
        cprt =('Type "help" for more information. "root" is the Pyramid app '
               'root object, "registry" is the Pyramid registry object.')
        cprt =('Type "help" for more information.')
        banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt)
        config_file, section_name = self.args
        app_spec = self.args[0]
        config_file = app_spec.split('#', 1)[0]
        self.logging_file_config(config_file)
        app = self.get_app(config_file, section_name, loadapp=self.loadapp[0])
        root, closer = self.get_root(app)
        shell_globals = {'root':root, 'registry':app.registry}
        app = self.get_app(app_spec, loadapp=self.loadapp[0])
        # load default globals
        shell_globals = {
            'app': app,
        }
        default_variables = {'app': 'The WSGI Application'}
        if hasattr(app, 'registry'):
            root, closer = self.get_root(app)
            shell_globals.update({'root':root, 'registry':app.registry,
                                  'settings': app.registry.settings})
            default_variables.update({
                'root': 'The root of the default resource tree.',
                'registry': 'The Pyramid registry object.',
                'settings': 'The Pyramid settings object.',
            })
            warning = ''
        else:
            # warn the user that this isn't actually the Pyramid app
            warning = """\n
WARNING: You have loaded a generic WSGI application, therefore the
"root" and "registry" are not available. To correct this, run "pshell"
again and specify the INI section containing your Pyramid application."""
            closer = lambda: None
        # load the pshell section of the ini file
        self.pshell_file_config(config_file)
        shell_globals.update(self.loaded_objects)
        # eliminate duplicates from default_variables
        for k in self.loaded_objects:
            if k in default_variables:
                del default_variables[k]
        # append the loaded variables
        if default_variables:
            banner += '\n\nDefault Variables:'
            for var, txt in default_variables.iteritems():
                banner += '\n  %-12s %s' % (var, txt)
        if self.object_help:
            banner += '\n\nCustom Variables:'
            for var in sorted(self.object_help.keys()):
                banner += '\n  %-12s %s' % (var, self.object_help[var])
        # append the warning
        banner += warning
        banner += '\n'
        if (IPShell is None) or self.options.disable_ipython:
            try:
@@ -108,17 +180,15 @@
    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:
    This command accepts one positional argument:
    ``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.
    ``config_file#section_name`` -- specifies the PasteDeploy config file
    to use for the interactive shell. If the section_name is left off,
    ``main`` will be assumed.
    Example::
        $ paster proutes myapp.ini main
        $ 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
@@ -126,8 +196,8 @@
              command will almost certainly fail.
    """
    summary = "Print all URL dispatch routes related to a Pyramid application"
    min_args = 2
    max_args = 2
    min_args = 1
    max_args = 1
    stdout = sys.stdout
    parser = Command.standard_parser(simulate=True)
@@ -146,8 +216,8 @@
        from pyramid.interfaces import IViewClassifier
        from pyramid.interfaces import IView
        from zope.interface import Interface
        config_file, section_name = self.args
        app = self.get_app(config_file, section_name, loadapp=self.loadapp[0])
        app_spec = self.args[0]
        app = self.get_app(app_spec, loadapp=self.loadapp[0])
        registry = app.registry
        mapper = self._get_mapper(app)
        if mapper is not None:
@@ -179,19 +249,17 @@
    each route+predicate set, print each view that might match and its
    predicates.
    This command accepts three positional arguments:
    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.
    ``config_file#section_name`` -- specifies the PasteDeploy config file
    to use for the interactive shell. If the section_name is left off,
    ``main`` will be assumed.
    ``url`` -- specifies the URL that will be used to find matching views.
    Example::
        $ paster proutes myapp.ini main url
        $ 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
@@ -199,8 +267,8 @@
              command will almost certainly fail.
    """
    summary = "Print all views in an application that might match a URL"
    min_args = 3
    max_args = 3
    min_args = 2
    max_args = 2
    stdout = sys.stdout
    parser = Command.standard_parser(simulate=True)
@@ -395,10 +463,10 @@
                self.out("%sview predicates (%s)" % (indent, predicate_text))
    def command(self):
        config_file, section_name, url = self.args
        app_spec, url = self.args
        if not url.startswith('/'):
            url = '/%s' % url
        app = self.get_app(config_file, section_name, loadapp=self.loadapp[0])
        app = self.get_app(app_spec, loadapp=self.loadapp[0])
        registry = app.registry
        view = self._find_view(url, registry)
        self.out('')
pyramid/tests/test_paster.py
@@ -15,7 +15,8 @@
        loadapp = DummyLoadApp(app)
        command.interact = (interact,)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        command.ConfigParser = makeDummyConfigParser({})
        command.args = ('/foo/bar/myapp.ini#myapp',)
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython = False
@@ -27,8 +28,10 @@
        pushed = app.threadlocal_manager.pushed[0]
        self.assertEqual(pushed['registry'], dummy_registry)
        self.assertEqual(pushed['request'].registry, dummy_registry)
        self.assertEqual(interact.local, {'root':dummy_root,
                                          'registry':dummy_registry})
        self.assertEqual(interact.local, {'app':app,
                                          'root':dummy_root,
                                          'registry':dummy_registry,
                                          'settings':dummy_registry.settings})
        self.assertTrue(interact.banner)
        self.assertEqual(len(app.threadlocal_manager.popped), 1)
@@ -39,7 +42,8 @@
        loadapp = DummyLoadApp(app)
        command.interact = (interact,)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        command.ConfigParser = makeDummyConfigParser({})
        command.args = ('/foo/bar/myapp.ini#myapp',)
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython = True
@@ -51,8 +55,10 @@
        pushed = app.threadlocal_manager.pushed[0]
        self.assertEqual(pushed['registry'], dummy_registry)
        self.assertEqual(pushed['request'].registry, dummy_registry)
        self.assertEqual(interact.local, {'root':dummy_root,
                                          'registry':dummy_registry})
        self.assertEqual(interact.local, {'app':app,
                                          'root':dummy_root,
                                          'registry':dummy_registry,
                                          'settings':dummy_registry.settings})
        self.assertTrue(interact.banner)
        self.assertEqual(len(app.threadlocal_manager.popped), 1)
@@ -61,8 +67,9 @@
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.ConfigParser = makeDummyConfigParser({})
        dummy_shell_factory = DummyIPShellFactory()
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        command.args = ('/foo/bar/myapp.ini#myapp',)
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython = False
@@ -75,7 +82,9 @@
        self.assertEqual(pushed['registry'], dummy_registry)
        self.assertEqual(pushed['request'].registry, dummy_registry)
        self.assertEqual(dummy_shell_factory.shell.local_ns,
                         {'root':dummy_root, 'registry':dummy_registry})
                         {'app':app, 'root':dummy_root,
                          'registry':dummy_registry,
                          'settings':dummy_registry.settings})
        self.assertEqual(dummy_shell_factory.shell.global_ns, {})
        self.assertTrue('\n\n' in dummy_shell_factory.shell.IP.BANNER)
        self.assertEqual(len(app.threadlocal_manager.popped), 1)
@@ -92,7 +101,8 @@
        interact = DummyInteractor()
        app = DummyApp()
        command.interact = (interact,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        command.ConfigParser = makeDummyConfigParser({})
        command.args = ('/foo/bar/myapp.ini#myapp',)
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython =True
@@ -101,11 +111,13 @@
        pushed = app.threadlocal_manager.pushed[0]
        self.assertEqual(pushed['registry'], dummy_registry)
        self.assertEqual(pushed['request'].registry, dummy_registry)
        self.assertEqual(interact.local, {'root':dummy_root,
                                          'registry':dummy_registry})
        self.assertEqual(interact.local, {'app': app,
                                          'root':dummy_root,
                                          'registry':dummy_registry,
                                          'settings':dummy_registry.settings})
        self.assertTrue(interact.banner)
        self.assertEqual(len(app.threadlocal_manager.popped), 1)
        self.assertEqual(apped, [(('/foo/bar/myapp.ini', 'myapp'),
        self.assertEqual(apped, [(('/foo/bar/myapp.ini#myapp',),
                                  {'loadapp': loadapp})])
    def test_command_get_root_hookable(self):
@@ -115,13 +127,14 @@
        loadapp = DummyLoadApp(app)
        command.interact = (interact,)
        command.loadapp = (loadapp,)
        command.ConfigParser = makeDummyConfigParser({})
        root = Dummy()
        apps = []
        def get_root(app):
            apps.append(app)
            return root, lambda *arg: None
        command.get_root =get_root
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        command.args = ('/foo/bar/myapp.ini#myapp',)
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython =True
@@ -130,10 +143,107 @@
        self.assertEqual(loadapp.section_name, 'myapp')
        self.assertTrue(loadapp.relative_to)
        self.assertEqual(len(app.threadlocal_manager.pushed), 0)
        self.assertEqual(interact.local, {'root':root,
                                          'registry':dummy_registry})
        self.assertEqual(interact.local, {'app':app,
                                          'root':root,
                                          'registry':dummy_registry,
                                          'settings':dummy_registry.settings})
        self.assertTrue(interact.banner)
        self.assertEqual(apps, [app])
    def test_command_loads_custom_items(self):
        command = self._makeOne()
        interact = DummyInteractor()
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.interact = (interact,)
        command.loadapp = (loadapp,)
        model = Dummy()
        command.ConfigParser = makeDummyConfigParser([('m', model)])
        command.args = ('/foo/bar/myapp.ini#myapp',)
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython = False
        command.command(IPShell=None)
        self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
        self.assertEqual(loadapp.section_name, 'myapp')
        self.assertTrue(loadapp.relative_to)
        self.assertEqual(len(app.threadlocal_manager.pushed), 1)
        pushed = app.threadlocal_manager.pushed[0]
        self.assertEqual(pushed['registry'], dummy_registry)
        self.assertEqual(pushed['request'].registry, dummy_registry)
        self.assertEqual(interact.local, {'app':app,
                                          'root':dummy_root,
                                          'registry':dummy_registry,
                                          'settings':dummy_registry.settings,
                                          'm': model})
        self.assertTrue(interact.banner)
        self.assertEqual(len(app.threadlocal_manager.popped), 1)
    def test_command_no_custom_section(self):
        command = self._makeOne()
        interact = DummyInteractor()
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.interact = (interact,)
        command.loadapp = (loadapp,)
        command.ConfigParser = makeDummyConfigParser(None)
        command.args = ('/foo/bar/myapp.ini#myapp',)
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython = False
        command.command(IPShell=None)
        self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
        self.assertEqual(loadapp.section_name, 'myapp')
        self.assertTrue(loadapp.relative_to)
        self.assertEqual(len(app.threadlocal_manager.pushed), 1)
        pushed = app.threadlocal_manager.pushed[0]
        self.assertEqual(pushed['registry'], dummy_registry)
        self.assertEqual(pushed['request'].registry, dummy_registry)
        self.assertEqual(interact.local, {'app':app,
                                          'root':dummy_root,
                                          'registry':dummy_registry,
                                          'settings':dummy_registry.settings})
        self.assertTrue(interact.banner)
        self.assertEqual(len(app.threadlocal_manager.popped), 1)
    def test_command_custom_section_override(self):
        command = self._makeOne()
        interact = DummyInteractor()
        app = Dummy()
        loadapp = DummyLoadApp(app)
        command.interact = (interact,)
        command.loadapp = (loadapp,)
        model = Dummy()
        command.ConfigParser = makeDummyConfigParser([('app', model)])
        command.args = ('/foo/bar/myapp.ini#myapp',)
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython = False
        command.command(IPShell=None)
        self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
        self.assertEqual(loadapp.section_name, 'myapp')
        self.assertTrue(loadapp.relative_to)
        self.assertEqual(interact.local, {'app':model})
        self.assertTrue(interact.banner)
    def test_command_generic_wsgi_app(self):
        command = self._makeOne()
        interact = DummyInteractor()
        app = Dummy()
        loadapp = DummyLoadApp(app)
        command.interact = (interact,)
        command.loadapp = (loadapp,)
        command.ConfigParser = makeDummyConfigParser(None)
        command.args = ('/foo/bar/myapp.ini#myapp',)
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython = False
        command.command(IPShell=None)
        self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
        self.assertEqual(loadapp.section_name, 'myapp')
        self.assertTrue(loadapp.relative_to)
        self.assertEqual(interact.local, {'app':app})
        self.assertTrue(interact.banner)
class TestPRoutesCommand(unittest.TestCase):
    def _getTargetClass(self):
@@ -152,7 +262,7 @@
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        command.args = ('/foo/bar/myapp.ini#myapp',)
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L, [])
@@ -165,7 +275,7 @@
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        command.args = ('/foo/bar/myapp.ini#myapp',)
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L, [])
@@ -180,7 +290,7 @@
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        command.args = ('/foo/bar/myapp.ini#myapp',)
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(len(L), 3)
@@ -205,7 +315,7 @@
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        command.args = ('/foo/bar/myapp.ini#myapp',)
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(len(L), 3)
@@ -235,7 +345,7 @@
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        command.args = ('/foo/bar/myapp.ini#myapp',)
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(len(L), 3)
@@ -268,7 +378,7 @@
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        command.args = ('/foo/bar/myapp.ini#myapp',)
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(len(L), 3)
@@ -511,7 +621,7 @@
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -528,7 +638,7 @@
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', 'a')
        command.args = ('/foo/bar/myapp.ini#myapp', 'a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -546,7 +656,7 @@
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -567,7 +677,7 @@
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -588,7 +698,7 @@
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -612,7 +722,7 @@
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -635,7 +745,7 @@
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -665,7 +775,7 @@
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -690,7 +800,7 @@
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -719,7 +829,7 @@
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -743,7 +853,7 @@
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -770,7 +880,7 @@
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -796,6 +906,26 @@
        result = self._callFUT('/foo/bar/myapp.ini', 'myapp', loadapp)
        self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
        self.assertEqual(loadapp.section_name, 'myapp')
        self.assertEqual(loadapp.relative_to, os.getcwd())
        self.assertEqual(result, app)
    def test_it_with_hash(self):
        import os
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        result = self._callFUT('/foo/bar/myapp.ini#myapp', None, loadapp)
        self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
        self.assertEqual(loadapp.section_name, 'myapp')
        self.assertEqual(loadapp.relative_to, os.getcwd())
        self.assertEqual(result, app)
    def test_it_with_hash_and_name_override(self):
        import os
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        result = self._callFUT('/foo/bar/myapp.ini#myapp', 'yourapp', loadapp)
        self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
        self.assertEqual(loadapp.section_name, 'yourapp')
        self.assertEqual(loadapp.relative_to, os.getcwd())
        self.assertEqual(result, app)
        
@@ -824,6 +954,7 @@
dummy_root = Dummy()
class DummyRegistry(object):
    settings = {}
    def queryUtility(self, iface, default=None, name=''):
        return default
@@ -905,3 +1036,15 @@
        self.views = [(None, view, None) for view in views]
        self.__request_attrs__ = attrs
def makeDummyConfigParser(items):
    class DummyConfigParser(object):
        def read(self, filename):
            self.filename = filename
        def items(self, section):
            self.section = section
            if items is None:
                from ConfigParser import NoSectionError
                raise NoSectionError, section
            return items
    return DummyConfigParser