summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2011-07-16 20:03:08 -0400
committerChris McDonough <chrism@plope.com>2011-07-16 20:03:08 -0400
commitc3ceaf474c68a0b12f126b4cb57448b26792b697 (patch)
tree733577de0bda50a101153262da91a907ccd36166
parentd50edc5807539da66e266a7ed7810f770cee969a (diff)
parent69452f63ab2efa39c9273646959341287ba5ee15 (diff)
downloadpyramid-c3ceaf474c68a0b12f126b4cb57448b26792b697.tar.gz
pyramid-c3ceaf474c68a0b12f126b4cb57448b26792b697.tar.bz2
pyramid-c3ceaf474c68a0b12f126b4cb57448b26792b697.zip
Merge branch 'master' of github.com:Pylons/pyramid
-rw-r--r--docs/api/scripting.rst2
-rw-r--r--docs/narr/commandline.rst76
-rw-r--r--pyramid/paster.py104
-rw-r--r--pyramid/scripting.py68
-rw-r--r--pyramid/tests/test_paster.py377
-rw-r--r--pyramid/tests/test_scripting.py10
-rw-r--r--pyramid/util.py13
7 files changed, 283 insertions, 367 deletions
diff --git a/docs/api/scripting.rst b/docs/api/scripting.rst
index 79136a98b..51bd3c7a0 100644
--- a/docs/api/scripting.rst
+++ b/docs/api/scripting.rst
@@ -9,5 +9,3 @@
.. autofunction:: prepare
- .. autofunction:: make_request
-
diff --git a/docs/narr/commandline.rst b/docs/narr/commandline.rst
index bc210904f..30e678f07 100644
--- a/docs/narr/commandline.rst
+++ b/docs/narr/commandline.rst
@@ -294,7 +294,7 @@ Writing a Script
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 @@ representing Pyramid your application configuration as a single argument:
.. 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 @@ to load instead of ``main``:
.. 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?
+
+Assuming that you have a route configured in your application like so:
+
+.. code-block:: python
-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.
+ config.add_route('verify', '/verify/{code}')
-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:
+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.request import Request
- request = Request.blank('/another/url')
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 @@ call the ``closer`` callback:
.. 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']()
diff --git a/pyramid/paster.py b/pyramid/paster.py
index 048f747e8..444608f92 100644
--- a/pyramid/paster.py
+++ b/pyramid/paster.py
@@ -42,11 +42,12 @@ def bootstrap(config_uri, request=None):
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 @@ def bootstrap(config_uri, request=None):
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 @@ def bootstrap(config_uri, request=None):
``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 @@ class PShellCommand(PCommand):
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 @@ class PShellCommand(PCommand):
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()
diff --git a/pyramid/scripting.py b/pyramid/scripting.py
index c04915d3a..47178c22e 100644
--- a/pyramid/scripting.py
+++ b/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 @@ def get_root(app, request=None):
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 @@ def get_root(app, request=None):
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 @@ def prepare(request=None, registry=None):
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.
diff --git a/pyramid/tests/test_paster.py b/pyramid/tests/test_paster.py
index 7785b006e..4a099bbdb 100644
--- a/pyramid/tests/test_paster.py
+++ b/pyramid/tests/test_paster.py
@@ -5,245 +5,117 @@ class TestPShellCommand(unittest.TestCase):
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
- 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
+ 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})
- 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
- 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
+ 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':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 DummyLoadApp:
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 @@ 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
+class DummyConfigParser(object):
+ def __init__(self, result):
+ self.result = result
+
+ 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,
+ }
diff --git a/pyramid/tests/test_scripting.py b/pyramid/tests/test_scripting.py
index ccc6656df..f01e744b8 100644
--- a/pyramid/tests/test_scripting.py
+++ b/pyramid/tests/test_scripting.py
@@ -48,6 +48,10 @@ class Test_prepare(unittest.TestCase):
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 @@ class Test_prepare(unittest.TestCase):
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)
diff --git a/pyramid/util.py b/pyramid/util.py
index a4b69ed96..7fd1b0dc6 100644
--- a/pyramid/util.py
+++ b/pyramid/util.py
@@ -151,6 +151,17 @@ class WeakOrderedSet(object):
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 @@ class WeakOrderedSet(object):
""" 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