Chris McDonough
2011-07-17 c3ceaf474c68a0b12f126b4cb57448b26792b697
Merge branch 'master' of github.com:Pylons/pyramid
7 files modified
650 ■■■■■ changed files
docs/api/scripting.rst 2 ●●●●● patch | view | raw | blame | history
docs/narr/commandline.rst 78 ●●●●● patch | view | raw | blame | history
pyramid/paster.py 104 ●●●● patch | view | raw | blame | history
pyramid/scripting.py 68 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_paster.py 375 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_scripting.py 10 ●●●● patch | view | raw | blame | history
pyramid/util.py 13 ●●●●● patch | view | raw | blame | history
docs/api/scripting.rst
@@ -9,5 +9,3 @@
  .. autofunction:: prepare
  .. autofunction:: make_request
docs/narr/commandline.rst
@@ -294,7 +294,7 @@
All web applications are, at their hearts, systems which accept a request and
return a response.  When a request is accepted by a :app:`Pyramid`
application, the system receives state from the request which is later relied
on your application code.  For example, one :term:`view callable` may assume
on by your application code.  For example, one :term:`view callable` may assume
it's working against a request that has a ``request.matchdict`` of a
particular composition, while another assumes a different composition of the
matchdict.
@@ -324,14 +324,14 @@
.. code-block:: python
   from pyramid.paster import bootstrap
   info = bootstrap('/path/to/my/development.ini')
   print info['request'].route_url('home')
   env = bootstrap('/path/to/my/development.ini')
   print env['request'].route_url('home')
:func:`pyramid.paster.bootstrap` returns a dictionary containing
framework-related information.  This dictionary will always contain a
:term:`request` object as its ``request`` key.
The following keys are available in the ``info`` dictionary returned by
The following keys are available in the ``env`` dictionary returned by
:func:`pyramid.paster.bootstrap`:
request
@@ -386,43 +386,54 @@
.. code-block:: python
   from pyramid.paster import bootstrap
   info = bootstrap('/path/to/my/development.ini#another')
   print info['request'].route_url('home')
   env = bootstrap('/path/to/my/development.ini#another')
   print env['request'].route_url('home')
The above example specifies the ``another`` ``app``, ``pipeline``, or
``composite`` section of your PasteDeploy configuration file.  In the case
that we're using a configuration file that looks like this:
``composite`` section of your PasteDeploy configuration file. The ``app``
object present in the ``env`` dictionary returned by
:func:`pyramid.paster.bootstrap` will be a :app:`Pyramid` :term:`router`.
.. code-block:: ini
Changing the Request
~~~~~~~~~~~~~~~~~~~~
   [pipeline:main]
   pipeline = egg:WebError#evalerror
              another
By default, Pyramid will generate a request object in the ``env`` dictionary
for the URL ``http://localhost:80/``. This means that any URLs generated
by Pyramid during the execution of your script will be anchored here. This
is generally not what you want.
   [app:another]
   use = egg:MyProject
So how do we make Pyramid generate the correct URLs?
It will mean that the ``/path/to/my/development.ini#another`` argument passed
to bootstrap will imply the ``[app:another]`` section in our configuration
file.  Therefore, it will not wrap the WSGI application present in the info
dictionary as ``app`` using WebError's ``evalerror`` middleware.  The ``app``
object present in the info dictionary returned by
:func:`pyramid.paster.bootstrap` will be a :app:`Pyramid` :term:`router`
instead.
By default, Pyramid will general a request object in the ``info`` dictionary
anchored at the root path (``/``).  You can alternately supply your own
:class:`pyramid.request.Request` instance to the
:func:`pyramid.paster.bootstrap` function, to set up request parameters
beforehand:
Assuming that you have a route configured in your application like so:
.. code-block:: python
   from pyramid.request import Request
   request = Request.blank('/another/url')
   config.add_route('verify', '/verify/{code}')
You need to inform the Pyramid environment that the WSGI application is
handling requests from a certain base. For example, we want to mount our
application at `example.com/prefix` and the generated URLs should use HTTPS.
This can be done by mutating the request object:
.. code-block:: python
   from pyramid.paster import bootstrap
   info = bootstrap('/path/to/my/development.ini#another', request=request)
   print info['request'].path_info # will print '/another/url'
   env = bootstrap('/path/to/my/development.ini#another')
   env['request'].host = 'example.com'
   env['request'].scheme = 'https'
   env['request'].script_name = '/prefix'
   print env['request'].application_url
   # will print 'https://example.com/prefix/another/url'
Now you can readily use Pyramid's APIs for generating URLs:
.. code-block:: python
   route_url('verify', env['request'], code='1337')
   # will return 'https://example.com/prefix/verify/1337'
Cleanup
~~~~~~~
When your scripting logic finishes, it's good manners (but not required) to
call the ``closer`` callback:
@@ -430,10 +441,9 @@
.. code-block:: python
   from pyramid.paster import bootstrap
   info = bootstrap('/path/to/my/development.ini')
   env = bootstrap('/path/to/my/development.ini')
   # .. do stuff ...
   info['closer']()
   env['closer']()
pyramid/paster.py
@@ -42,11 +42,12 @@
    currently serving ``request``, leaving a natural environment in place
    to write scripts that can generate URLs and utilize renderers.
    This function returns a dictionary with ``app``, ``root`` and ``closer``
    keys.  ``app`` is the WSGI app loaded (based on the ``config_uri``),
    ``root`` is the traversal root resource of the Pyramid application, and
    ``closer`` is a parameterless callback that may be called when your
    script is complete (it pops a threadlocal stack).
    This function returns a dictionary with ``app``, ``root``, ``closer``,
    ``request``, and ``registry`` keys.  ``app`` is the WSGI app loaded
    (based on the ``config_uri``), ``root`` is the traversal root resource
    of the Pyramid application, and ``closer`` is a parameterless callback
    that may be called when your script is complete (it pops a threadlocal
    stack).
    .. note:: Most operations within :app:`Pyramid` expect to be invoked
              within the context of a WSGI request, thus it's important when
@@ -59,7 +60,7 @@
              the context of the last-loaded :app:`Pyramid` application. You
              may load a specific application yourself by using the
              lower-level functions :meth:`pyramid.paster.get_app` and
              :meth:`pyramid.scripting.get_root2` in conjunction with
              :meth:`pyramid.scripting.prepare` in conjunction with
              :attr:`pyramid.config.global_registries`.
    ``config_uri`` -- specifies the PasteDeploy config file to use for the
@@ -69,15 +70,17 @@
    ``request`` -- specified to anchor the script to a given set of WSGI
    parameters. For example, most people would want to specify the host,
    scheme and port such that their script will generate URLs in relation
    to those parameters.
    to those parameters. A request with default parameters is constructed
    for you if none is provided. You can mutate the request's ``environ``
    later to setup a specific host/port/scheme/etc.
    See :ref:`writing_a_script` for more information about how to use this
    function.
    """
    app = get_app(config_uri)
    info = prepare(request)
    info['app'] = app
    return info
    env = prepare(request)
    env['app'] = app
    return env
_marker = object()
@@ -126,6 +129,7 @@
                      dest='disable_ipython',
                      help="Don't use IPython even if it is available")
    bootstrap = (bootstrap,) # testing
    ConfigParser = ConfigParser.ConfigParser # testing
    def pshell_file_config(self, filename):
@@ -149,72 +153,60 @@
                from IPython.Shell import IPShell
            except ImportError:
                IPShell = None
        cprt = 'Type "help" for more information.'
        banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt)
        config_uri = self.args[0]
        config_file = config_uri.split('#', 1)[0]
        self.logging_file_config(config_file)
        app = self.get_app(config_uri, loadapp=self.loadapp[0])
        self.pshell_file_config(config_file)
        # 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",
"registry", and "settings" global variables are not available. To correct
this, run "pshell" again and specify the INI section containing your Pyramid
application.  For example, if your app is in the '[app:myapp]' config file
section, use 'development.ini#myapp' instead of 'development.ini' or
'development.ini#main'."""
            closer = lambda: None
        # bootstrap the environ
        env = self.bootstrap[0](config_uri)
        # remove the closer from the env
        closer = env.pop('closer')
        # setup help text for default environment
        env_help = dict(env)
        env_help['app'] = 'The WSGI application.'
        env_help['root'] = 'Root of the default resource tree.'
        env_help['registry'] = 'Active Pyramid registry.'
        env_help['request'] = 'Active request object.'
        env_help['root_factory'] = (
            'Default root factory used to create `root`.')
        # load the pshell section of the ini file
        self.pshell_file_config(config_file)
        shell_globals.update(self.loaded_objects)
        env.update(self.loaded_objects)
        # eliminate duplicates from default_variables
        # eliminate duplicates from env, allowing custom vars to override
        for k in self.loaded_objects:
            if k in default_variables:
                del default_variables[k]
            if k in env_help:
                del env_help[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)
        # generate help text
        help = '\n'
        if env_help:
            help += 'Environment:'
            for var in sorted(env_help.keys()):
                help += '\n  %-12s %s' % (var, env_help[var])
        if self.object_help:
            banner += '\n\nCustom Variables:'
            help += '\n\nCustom Variables:'
            for var in sorted(self.object_help.keys()):
                banner += '\n  %-12s %s' % (var, self.object_help[var])
                help += '\n  %-12s %s' % (var, self.object_help[var])
        # append the warning
        banner += warning
        banner += '\n'
        help += '\n'
        if (IPShell is None) or self.options.disable_ipython:
            cprt = 'Type "help" for more information.'
            banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt)
            banner += '\n' + help
            try:
                self.interact[0](banner, local=shell_globals)
                self.interact[0](banner, local=env)
            finally:
                closer()
        else:
            try:
                shell = IPShell(argv=[], user_ns=shell_globals)
                shell.IP.BANNER = shell.IP.BANNER + '\n\n' + banner
                shell = IPShell(argv=[], user_ns=env)
                shell.IP.BANNER = shell.IP.BANNER + help
                shell.mainloop()
            finally:
                closer()
pyramid/scripting.py
@@ -1,4 +1,5 @@
from pyramid.config import global_registries
from pyramid.exceptions import ConfigurationError
from pyramid.request import Request
from pyramid.interfaces import IRequestFactory
from pyramid.interfaces import IRootFactory
@@ -12,13 +13,14 @@
    is a callable (accepting no arguments) that should be called when
    your scripting application is finished using the root.
    If ``request`` is not None, it is used as the request passed to the
    :app:`Pyramid` application root factory. A request is constructed
    using :meth:`pyramid.scripting.make_request` and passed to the root
    factory if ``request`` is None."""
    ``request`` is passed to the :app:`Pyramid` application root
    factory to compute the root. If ``request`` is None, a default
    will be constructed using the registry's :term:`Request Factory`
    via the :meth:`pyramid.interfaces.IRequestFactory.blank` method.
    """
    registry = app.registry
    if request is None:
        request = make_request('/', registry)
        request = _make_request('/', registry)
    threadlocals = {'registry':registry, 'request':request}
    app.threadlocal_manager.push(threadlocals)
    def closer(request=request): # keep request alive via this function default
@@ -27,37 +29,43 @@
    return root, closer
def prepare(request=None, registry=None):
    """ This function pushes data onto the Pyramid threadlocal stack (request
    and registry), making those objects 'current'.  It returns a dictionary
    useful for bootstrapping a Pyramid application in a scripting
    environment.
    """ This function pushes data onto the Pyramid threadlocal stack
    (request and registry), making those objects 'current'.  It
    returns a dictionary useful for bootstrapping a Pyramid
    application in a scripting environment.
    If ``request`` is None, a default request is constructed using
    :meth:`pyramid.scripting.make_request`. The request passed to the
    :app:`Pyramid` application root factory to compute the root.
    ``request`` is passed to the :app:`Pyramid` application root
    factory to compute the root. If ``request`` is None, a default
    will be constructed using the registry's :term:`Request Factory`
    via the :meth:`pyramid.interfaces.IRequestFactory.blank` method.
    If ``registry`` is not supplied, the last registry loaded from
    :attr:`pyramid.config.global_registries` will be used. If you have
    loaded more than one :app:`Pyramid` application in the current
    process, you may not want to use the last registry loaded, thus
    you can search the ``global_registries`` and supply the appropriate
    one based on your own criteria.
    :attr:`pyramid.config.global_registries` will be used. If you
    have loaded more than one :app:`Pyramid` application in the
    current process, you may not want to use the last registry
    loaded, thus you can search the ``global_registries`` and supply
    the appropriate one based on your own criteria.
    The function returns a dictionary composed of ``root``, ``closer``,
    ``registry``, ``request`` and ``root_factory``.  The ``root`` returned is
    the application's root resource object.  The ``closer`` returned is a
    callable (accepting no arguments) that should be called when your
    scripting application is finished using the root.  ``registry`` is the
    registry object passed or the last registry loaded into
    The function returns a dictionary composed of ``root``,
    ``closer``, ``registry``, ``request`` and ``root_factory``.  The
    ``root`` returned is the application's root resource object.  The
    ``closer`` returned is a callable (accepting no arguments) that
    should be called when your scripting application is finished
    using the root.  ``registry`` is the registry object passed or
    the last registry loaded into
    :attr:`pyramid.config.global_registries` if no registry is passed.
    ``request`` is the request object passed or the constructed request if no
    request is passed.  ``root_factory`` is the root factory used to
    construct the root.
    ``request`` is the request object passed or the constructed request
    if no request is passed.  ``root_factory`` is the root factory used
    to construct the root.
    """
    if registry is None:
        registry = getattr(request, 'registry', global_registries.last)
    if registry is None:
        raise ConfigurationError('No valid Pyramid applications could be '
                                 'found, make sure one has been created '
                                 'before trying to activate it.')
    if request is None:
        request = make_request('/', registry)
        request = _make_request('/', registry)
    request.registry = registry
    threadlocals = {'registry':registry, 'request':request}
    threadlocal_manager.push(threadlocals)
@@ -69,14 +77,14 @@
    return {'root':root, 'closer':closer, 'registry':registry,
            'request':request, 'root_factory':root_factory}
def make_request(path, registry=None):
def _make_request(path, registry=None):
    """ Return a :meth:`pyramid.request.Request` object anchored at a
    given path. The object returned will be generated from the supplied
    registry's :term:`Request Factory` using the
    :meth:`pyramid.interfaces.IRequestFactory.blank` method.
    This request object can be passed to
    :meth:`pyramid.scripting.get_root` to initialize an application in
    This request object can be passed to :meth:`pyramid.scripting.get_root`
    or :meth:`pyramid.scripting.prepare` to initialize an application in
    preparation for executing a script with a proper environment setup.
    URLs can then be generated with the object, as well as rendering
    templates.
pyramid/tests/test_paster.py
@@ -5,245 +5,117 @@
        from pyramid.paster import PShellCommand
        return PShellCommand
    def _makeOne(self):
        return self._getTargetClass()('pshell')
    def _makeOne(self, patch_interact=True, patch_bootstrap=True,
                 patch_config=True, patch_args=True, patch_options=True):
        cmd = self._getTargetClass()('pshell')
        if patch_interact:
            self.interact = DummyInteractor()
            cmd.interact = (self.interact,)
        if patch_bootstrap:
            self.bootstrap = DummyBootstrap()
            cmd.bootstrap = (self.bootstrap,)
        if patch_config:
            self.config_factory = DummyConfigParserFactory()
            cmd.ConfigParser = self.config_factory
        if patch_args:
            self.args = ('/foo/bar/myapp.ini#myapp',)
            cmd.args = self.args
        if patch_options:
            class Options(object): pass
            self.options = Options()
            self.options.disable_ipython = True
            cmd.options = self.options
        return cmd
    def test_command_ipshell_is_None_ipython_enabled(self):
        command = self._makeOne()
        interact = DummyInteractor()
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.interact = (interact,)
        command.loadapp = (loadapp,)
        command.ConfigParser = makeDummyConfigParser({})
        command.args = ('/foo/bar/myapp.ini#myapp',)
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython = False
        command.options.disable_ipython = True
        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)
        self.assertTrue(self.config_factory.parser)
        self.assertEqual(self.config_factory.parser.filename,
                         '/foo/bar/myapp.ini')
        self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
        self.assertEqual(self.interact.local, {
            'app':self.bootstrap.app, 'root':self.bootstrap.root,
            'registry':self.bootstrap.registry,
            'request':self.bootstrap.request,
            'root_factory':self.bootstrap.root_factory,
        })
        self.assertTrue(self.bootstrap.closer.called)
        self.assertTrue(self.interact.banner)
    def test_command_ipshell_is_not_None_ipython_disabled(self):
        command = self._makeOne()
        interact = DummyInteractor()
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.interact = (interact,)
        command.loadapp = (loadapp,)
        command.ConfigParser = makeDummyConfigParser({})
        command.args = ('/foo/bar/myapp.ini#myapp',)
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython = True
        command.command(IPShell='notnone')
        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)
        self.assertTrue(self.config_factory.parser)
        self.assertEqual(self.config_factory.parser.filename,
                         '/foo/bar/myapp.ini')
        self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
        self.assertEqual(self.interact.local, {
            'app':self.bootstrap.app, 'root':self.bootstrap.root,
            'registry':self.bootstrap.registry,
            'request':self.bootstrap.request,
            'root_factory':self.bootstrap.root_factory,
        })
        self.assertTrue(self.bootstrap.closer.called)
        self.assertTrue(self.interact.banner)
    def test_command_ipython_enabled(self):
        command = self._makeOne()
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.ConfigParser = makeDummyConfigParser({})
        dummy_shell_factory = DummyIPShellFactory()
        command.args = ('/foo/bar/myapp.ini#myapp',)
        class Options(object): pass
        command.options = Options()
        command = self._makeOne(patch_interact=False)
        command.options.disable_ipython = False
        dummy_shell_factory = DummyIPShellFactory()
        command.command(IPShell=dummy_shell_factory)
        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(dummy_shell_factory.shell.local_ns,
                         {'app':app, 'root':dummy_root,
                          'registry':dummy_registry,
                          'settings':dummy_registry.settings})
        self.assertTrue(self.config_factory.parser)
        self.assertEqual(self.config_factory.parser.filename,
                         '/foo/bar/myapp.ini')
        self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
        self.assertEqual(dummy_shell_factory.shell.local_ns, {
            'app':self.bootstrap.app, 'root':self.bootstrap.root,
            'registry':self.bootstrap.registry,
            'request':self.bootstrap.request,
            'root_factory':self.bootstrap.root_factory,
        })
        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)
    def test_command_get_app_hookable(self):
        from paste.deploy import loadapp
        command = self._makeOne()
        app = DummyApp()
        apped = []
        def get_app(*arg, **kw):
            apped.append((arg, kw))
            return app
        command.get_app = get_app
        interact = DummyInteractor()
        app = DummyApp()
        command.interact = (interact,)
        command.ConfigParser = makeDummyConfigParser({})
        command.args = ('/foo/bar/myapp.ini#myapp',)
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython =True
        command.command(IPShell=None)
        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)
        self.assertEqual(apped, [(('/foo/bar/myapp.ini#myapp',),
                                  {'loadapp': loadapp})])
    def test_command_get_root_hookable(self):
        command = self._makeOne()
        interact = DummyInteractor()
        app = DummyApp()
        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',)
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython =True
        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), 0)
        self.assertEqual(interact.local, {'app':app,
                                          'root':root,
                                          'registry':dummy_registry,
                                          'settings':dummy_registry.settings})
        self.assertTrue(interact.banner)
        self.assertEqual(apps, [app])
        self.assertTrue(self.bootstrap.closer.called)
    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
        self.config_factory.items = [('m', model)]
        command.options.disable_ipython = True
        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)
        self.assertTrue(self.config_factory.parser)
        self.assertEqual(self.config_factory.parser.filename,
                         '/foo/bar/myapp.ini')
        self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
        self.assertEqual(self.interact.local, {
            'app':self.bootstrap.app, 'root':self.bootstrap.root,
            'registry':self.bootstrap.registry,
            'request':self.bootstrap.request,
            'root_factory':self.bootstrap.root_factory,
            'm':model,
        })
        self.assertTrue(self.bootstrap.closer.called)
        self.assertTrue(self.interact.banner)
    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
        dummy = Dummy()
        self.config_factory.items = [('app', dummy), ('root', dummy),
                                     ('registry', dummy), ('request', dummy)]
        command.options.disable_ipython = True
        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)
        self.assertTrue(self.config_factory.parser)
        self.assertEqual(self.config_factory.parser.filename,
                         '/foo/bar/myapp.ini')
        self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
        self.assertEqual(self.interact.local, {
            'app':dummy, 'root':dummy, 'registry':dummy, 'request':dummy,
            'root_factory':self.bootstrap.root_factory,
        })
        self.assertTrue(self.bootstrap.closer.called)
        self.assertTrue(self.interact.banner)
class TestPRoutesCommand(unittest.TestCase):
    def _getTargetClass(self):
@@ -1015,22 +887,7 @@
class DummyApp:
    def __init__(self):
        self.registry = dummy_registry
        self.threadlocal_manager = DummyThreadLocalManager()
    def root_factory(self, environ):
        return dummy_root
class DummyThreadLocalManager:
    def __init__(self):
        self.pushed = []
        self.popped = []
    def push(self, item):
        self.pushed.append(item)
    def pop(self):
        self.popped.append(True)
class DummyMapper(object):
    def __init__(self, *routes):
        self.routes = routes
@@ -1073,15 +930,49 @@
        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
class DummyConfigParser(object):
    def __init__(self, result):
        self.result = result
        def items(self, section):
            self.section = section
            if items is None:
                from ConfigParser import NoSectionError
                raise NoSectionError, section
            return items
    return DummyConfigParser
    def read(self, filename):
        self.filename = filename
    def items(self, section):
        self.section = section
        if self.result is None:
            from ConfigParser import NoSectionError
            raise NoSectionError, section
        return self.result
class DummyConfigParserFactory(object):
    items = None
    def __call__(self):
        self.parser = DummyConfigParser(self.items)
        return self.parser
class DummyCloser(object):
    def __call__(self):
        self.called = True
class DummyBootstrap(object):
    def __init__(self, app=None, registry=None, request=None, root=None,
                 root_factory=None, closer=None):
        self.app = app or DummyApp()
        self.registry = registry or dummy_registry
        self.request = request or DummyRequest({})
        self.root = root or dummy_root
        self.root_factory = root_factory or Dummy()
        self.closer = closer or DummyCloser()
    def __call__(self, *a, **kw):
        self.a = a
        self.kw = kw
        return {
            'app': self.app,
            'registry': self.registry,
            'request': self.request,
            'root': self.root,
            'root_factory': self.root_factory,
            'closer': self.closer,
        }
pyramid/tests/test_scripting.py
@@ -48,6 +48,10 @@
        self.manager = manager
        self.default = manager.get()
    def test_it_no_valid_apps(self):
        from pyramid.exceptions import ConfigurationError
        self.assertRaises(ConfigurationError, self._callFUT)
    def test_it_norequest(self):
        registry = self._makeRegistry()
        info = self._callFUT(registry=registry)
@@ -85,10 +89,10 @@
        closer()
        self.assertEqual(self.default, self.manager.get())
class TestMakeRequest(unittest.TestCase):
class Test__make_request(unittest.TestCase):
    def _callFUT(self, path='/', registry=None):
        from pyramid.scripting import make_request
        return make_request(path, registry)
        from pyramid.scripting import _make_request
        return _make_request(path, registry)
    def test_it_with_registry(self):
        request = self._callFUT('/', dummy_registry)
pyramid/util.py
@@ -151,6 +151,17 @@
    The values may be iterated over or the last item added may be
    accessed via the ``last`` property.
    If items are added more than once, the most recent addition will
    be remembered in the order:
        order = WeakOrderedSet()
        order.add('1')
        order.add('2')
        order.add('1')
        list(order) == ['2', '1']
        order.last == '1'
    """
    def __init__(self):
@@ -161,6 +172,8 @@
        """ Add an item to the set."""
        oid = id(item)
        if oid in self._items:
            self._order.remove(oid)
            self._order.append(oid)
            return
        ref = weakref.ref(item, lambda x: self.remove(item))
        self._items[oid] = ref