diff options
| author | Chris McDonough <chrism@plope.com> | 2011-07-10 00:16:42 -0400 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2011-07-10 00:16:42 -0400 |
| commit | d1574c723bad0e6fc0e02627a65af47af3f548c2 (patch) | |
| tree | 3070ee582ae360fd5da363f0404acac4b212746d | |
| parent | 8cad6080cda9a72902333a70c20b58c5ea02226c (diff) | |
| parent | b45a60617e1f7e89289acf8d1df921696fa3e0a0 (diff) | |
| download | pyramid-d1574c723bad0e6fc0e02627a65af47af3f548c2.tar.gz pyramid-d1574c723bad0e6fc0e02627a65af47af3f548c2.tar.bz2 pyramid-d1574c723bad0e6fc0e02627a65af47af3f548c2.zip | |
Merge branch 'mmerickel-feature.pshell'
| -rw-r--r-- | docs/api/paster.rst | 7 | ||||
| -rw-r--r-- | docs/narr/project.rst | 96 | ||||
| -rw-r--r-- | docs/narr/urldispatch.rst | 9 | ||||
| -rw-r--r-- | docs/narr/viewconfig.rst | 11 | ||||
| -rw-r--r-- | pyramid/paster.py | 150 | ||||
| -rw-r--r-- | pyramid/tests/test_paster.py | 209 |
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 |
