summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2011-07-10 00:09:29 -0400
committerChris McDonough <chrism@plope.com>2011-07-10 00:09:29 -0400
commitb45a60617e1f7e89289acf8d1df921696fa3e0a0 (patch)
tree3070ee582ae360fd5da363f0404acac4b212746d
parent8cad6080cda9a72902333a70c20b58c5ea02226c (diff)
parent9e7162eb7f584e8afdbc2f04846d0d7e1fcf676c (diff)
downloadpyramid-b45a60617e1f7e89289acf8d1df921696fa3e0a0.tar.gz
pyramid-b45a60617e1f7e89289acf8d1df921696fa3e0a0.tar.bz2
pyramid-b45a60617e1f7e89289acf8d1df921696fa3e0a0.zip
Merge branch 'feature.pshell' of https://github.com/mmerickel/pyramid into mmerickel-feature.pshell
-rw-r--r--docs/api/paster.rst7
-rw-r--r--docs/narr/project.rst96
-rw-r--r--docs/narr/urldispatch.rst9
-rw-r--r--docs/narr/viewconfig.rst11
-rw-r--r--pyramid/paster.py150
-rw-r--r--pyramid/tests/test_paster.py209
6 files changed, 364 insertions, 118 deletions
diff --git a/docs/api/paster.rst b/docs/api/paster.rst
index 9ecfa3d9c..6668f3c77 100644
--- a/docs/api/paster.rst
+++ b/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".
+
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index 631412f42..be673c370 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -258,8 +258,9 @@ develop``, you can use an interactive Python shell to examine your
: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 @@ name ``MyProject`` as a section name:
.. 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 @@ name ``MyProject`` as a section name:
>>> 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``).
-
-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
+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.
-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 @@ example, if you have the following ``.ini`` file content:
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
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index f94ed3ba8..51a840b8d 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -1084,16 +1084,17 @@ 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.
+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>
diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst
index ec42446ff..67ac39259 100644
--- a/docs/narr/viewconfig.rst
+++ b/docs/narr/viewconfig.rst
@@ -795,10 +795,11 @@ For a big application with several views, it can be hard to keep the view
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 @@ A more complex configuration might generate something like this:
.. code-block:: text
:linenos:
- $ ../bin/paster pviews development.ini shootout /about
+ $ ../bin/paster pviews development.ini#shootout /about
URL = /about
diff --git a/pyramid/paster.py b/pyramid/paster.py
index b1f0a6c8b..eabd12a1b 100644
--- a/pyramid/paster.py
+++ b/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.deploy import loadapp
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 @@ zope.deprecation.deprecated(
'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 PCommand(Command):
class PShellCommand(PCommand):
"""Open an interactive shell with a :app:`Pyramid` app loaded.
- This command accepts two positional arguments:
-
- ``config_file`` -- specifies the PasteDeploy config file to use
- for the interactive shell.
+ This command accepts one positional argument:
- ``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,8 +73,8 @@ class PShellCommand(PCommand):
"""
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',
@@ -71,6 +82,22 @@ class PShellCommand(PCommand):
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
if IPShell is _marker: # pragma: no cover
@@ -78,14 +105,59 @@ class PShellCommand(PCommand):
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 @@ class PRoutesCommand(PCommand):
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.
+ This command accepts one positional argument:
- ``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 @@ class PRoutesCommand(PCommand):
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 @@ class PRoutesCommand(PCommand):
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 @@ class PViewsCommand(PCommand):
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.
+ This command accepts two positional arguments:
- ``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 @@ class PViewsCommand(PCommand):
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 @@ class PViewsCommand(PCommand):
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('')
diff --git a/pyramid/tests/test_paster.py b/pyramid/tests/test_paster.py
index cf0b38a80..e7a3b7507 100644
--- a/pyramid/tests/test_paster.py
+++ b/pyramid/tests/test_paster.py
@@ -15,7 +15,8 @@ class TestPShellCommand(unittest.TestCase):
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 @@ class TestPShellCommand(unittest.TestCase):
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 @@ class TestPShellCommand(unittest.TestCase):
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 @@ class TestPShellCommand(unittest.TestCase):
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 @@ class TestPShellCommand(unittest.TestCase):
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 @@ class TestPShellCommand(unittest.TestCase):
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 @@ class TestPShellCommand(unittest.TestCase):
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 @@ class TestPShellCommand(unittest.TestCase):
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 @@ class TestPShellCommand(unittest.TestCase):
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,11 +143,108 @@ class TestPShellCommand(unittest.TestCase):
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):
from pyramid.paster import PRoutesCommand
@@ -152,7 +262,7 @@ class TestPRoutesCommand(unittest.TestCase):
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 @@ class TestPRoutesCommand(unittest.TestCase):
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 @@ class TestPRoutesCommand(unittest.TestCase):
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 @@ class TestPRoutesCommand(unittest.TestCase):
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 @@ class TestPRoutesCommand(unittest.TestCase):
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 @@ class TestPRoutesCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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')
@@ -798,6 +908,26 @@ class TestGetApp(unittest.TestCase):
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 @@ class DummyIPShell(object):
dummy_root = Dummy()
class DummyRegistry(object):
+ settings = {}
def queryUtility(self, iface, default=None, name=''):
return default
@@ -905,3 +1036,15 @@ class DummyMultiView(object):
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