summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-05-27 15:47:11 +0000
committerChris McDonough <chrism@agendaless.com>2009-05-27 15:47:11 +0000
commit156375861f191f51f4e97ce25cd4d39f8025f90b (patch)
treeead4a91f8781cc26b6754e496d5e52399568edbe
parent711b60c05b9573f688994233ec1baac3f89bc45a (diff)
downloadpyramid-156375861f191f51f4e97ce25cd4d39f8025f90b.tar.gz
pyramid-156375861f191f51f4e97ce25cd4d39f8025f90b.tar.bz2
pyramid-156375861f191f51f4e97ce25cd4d39f8025f90b.zip
- A paster command has been added named "bfgshell". This command can
be used to get an interactive prompt with your BFG root object in the global namespace. E.g.:: bin/paster bfgshell /path/to/myapp.ini myapp See the ``Project`` chapter in the BFG documentation for more information.
-rw-r--r--CHANGES.txt19
-rw-r--r--docs/narr/project.rst27
-rw-r--r--repoze/bfg/paster.py68
-rw-r--r--repoze/bfg/registry.py10
-rw-r--r--repoze/bfg/scripting.py18
-rw-r--r--repoze/bfg/tests/test_paster.py71
-rw-r--r--repoze/bfg/tests/test_scripting.py31
-rw-r--r--setup.py2
8 files changed, 180 insertions, 66 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index cb2b7d7ae..bb9ada6db 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -4,17 +4,14 @@
Features
--------
-- A new API function has been added for scripts which need to obtain
- the traversal root: ``repoze.bfg.scripting.get_root``. Given a
- ``repoze.bfg`` Router application instance as its ``router``
- argument, this callable returns the traversal root of graph as
- defined by the application's root factory. It also has the effect
- of pushing a new registry and request on to the internal thread
- local stack managed by BFG so that registry lookups work properly.
-
- .. warning:: This function should never be called from *within* a
- BFG model or view, only from top-level scripts which wish to
- get the root of a graph to do offline processing.
+- A paster command has been added named "bfgshell". This command can
+ be used to get an interactive prompt with your BFG root object in
+ the global namespace. E.g.::
+
+ bin/paster bfgshell /path/to/myapp.ini myapp
+
+ See the ``Project`` chapter in the BFG documentation for more
+ information.
Deprecations
------------
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index 2788887e5..8e17a618f 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -648,3 +648,30 @@ build your application. You are not required to write tests to use
:mod:`repoze.bfg`, this file is simply provided as convenience and
example.
+The Interactive Shell
+---------------------
+
+You can use an interactive shell to examine your BFG application from
+a Python prompt. To do so, use the ``bfgshell`` paster command:
+
+.. code-block:: python
+ :linenos:
+
+ [chrism@vitaminf bfgshellenv]$ bin/paster bfgshell foo/foo.ini main
+
+ 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 BFG app root object.
+ >>> root
+ <foo.models.MyModel object at 0x445270>
+
+The first argument is the path to your application's ``.ini`` file.
+The second is the section name inside the ``.ini`` file which points
+to your application.
+
+.. note:: You should use a ``section name`` argument that refers to
+ the actual ``app`` section within the config file that
+ points at your BFG app *without any middleware wrapping*, or
+ this command will almost certainly fail. The section name
+ ``main`` is often inappropriate if the configuration does
+ any middleware wrapping.
diff --git a/repoze/bfg/paster.py b/repoze/bfg/paster.py
index df8bfecb4..bc339fd4c 100644
--- a/repoze/bfg/paster.py
+++ b/repoze/bfg/paster.py
@@ -1,4 +1,14 @@
+import os
+import sys
+
+from code import interact
+
+from paste.deploy import loadapp
+
from paste.script.templates import Template
+from paste.script.command import Command
+from paste.script.command import BadCommand
+
from paste.util.template import paste_script_template_renderer
class StarterProjectTemplate(Template):
@@ -20,3 +30,61 @@ class AlchemyProjectTemplate(Template):
_template_dir = 'paster_templates/alchemy'
summary = 'repoze.bfg SQLAlchemy project using traversal'
template_renderer = staticmethod(paste_script_template_renderer)
+
+class BFGShellCommand(Command):
+ """Open an interactive shell with a repoze.bfg app loaded.
+
+ This command accepts two positional arguments:
+
+ ``config_file`` -- specifies the PasteDeploy config file to use
+ for the interactive shell.
+
+ ``section_name`` -- specifies the section name in the PasteDeploy
+ config file that represents the application.
+
+ Example::
+
+ $ paster bfgshell myapp.ini main
+
+ .. note:: You should use a ``section_name`` that refers to the
+ actual ``app`` section in the config file that points at
+ your BFG app without any middleware wrapping, or this
+ command will almost certainly fail.
+
+ """
+ summary = "Open an interactive shell with a repoze.bfg app loaded"
+ usage = '\n' + __doc__
+
+ min_args = 2
+ max_args = 2
+ group_name = 'bfg'
+
+ parser = Command.standard_parser(simulate=True)
+ environ = {}
+ interact = (interact,) # for testing
+ loadapp = (loadapp,) # for testing
+ verbose = 3
+
+ def __init__(self, name):
+ Command.__init__(self, name)
+
+ def command(self):
+ cprt =('Type "help" for more information. "root" is the BFG app '
+ 'root object.')
+ banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt)
+
+ config_file, section_name = self.args
+ config_name = 'config:%s' % config_file
+ here_dir = os.getcwd()
+
+ app = self.loadapp[0](config_name,
+ name=section_name, relative_to=here_dir)
+ registry = app.registry
+ threadlocals = {'registry':registry, 'request':None}
+ try:
+ app.threadlocal_manager.push(threadlocals)
+ root = app.root_factory(self.environ)
+ self.interact[0](banner, local={'root':root})
+ finally:
+ app.threadlocal_manager.pop()
+
diff --git a/repoze/bfg/registry.py b/repoze/bfg/registry.py
index b9397e7da..46db57c9e 100644
--- a/repoze/bfg/registry.py
+++ b/repoze/bfg/registry.py
@@ -112,9 +112,7 @@ registry_manager = FakeRegistryManager()
deprecated('registry_manager',
'As of repoze.bfg 0.9, any import of registry_manager from'
'``repoze.bfg.registry`` is '
- 'deprecated. Instead, if you are trying to push a BFG '
- 'application registry into a registry_manager within a "debug" '
- 'script, call ``app.get_root(environ)``, which has the side '
- 'effect of pushing the current registry into a thread local '
- 'stack. ``registry_manager`` will disappear in a later '
- 'release of repoze.bfg')
+ 'deprecated. If you are trying to use the registry manager '
+ 'within a "debug" script of your own making, use the ``bfgshell`` '
+ 'paster command instead ``registry_manager`` will disappear in '
+ 'a later release of repoze.bfg')
diff --git a/repoze/bfg/scripting.py b/repoze/bfg/scripting.py
deleted file mode 100644
index 9e83e2fe8..000000000
--- a/repoze/bfg/scripting.py
+++ /dev/null
@@ -1,18 +0,0 @@
-_GET_ROOT_ENVIRON = {}
-
-def get_root(router):
- """ Given a :mod:`repoze.bfg` Router application instance as its
- ``router`` argument, this callable returns the traversal root of
- graph as defined by the application's root factory. It also has
- the effect of pushing a new registry and request on to the
- internal thread local stack managed by BFG so that registry
- lookups work properly.
-
- .. warning:: This function should never be called from *within* a
- BFG model or view, only from top-level scripts which wish to
- get the root of a graph to do offline processing."""
- registry = router.registry
- threadlocals = {'registry':registry, 'request':None}
- router.threadlocal_manager.push(threadlocals)
- return router.root_factory(_GET_ROOT_ENVIRON)
-
diff --git a/repoze/bfg/tests/test_paster.py b/repoze/bfg/tests/test_paster.py
new file mode 100644
index 000000000..3339fe498
--- /dev/null
+++ b/repoze/bfg/tests/test_paster.py
@@ -0,0 +1,71 @@
+import unittest
+
+class TestBFGShellCommand(unittest.TestCase):
+ def _getTargetClass(self):
+ from repoze.bfg.paster import BFGShellCommand
+ return BFGShellCommand
+
+ def _makeOne(self):
+ return self._getTargetClass()('bfgshell')
+
+ def test_command(self):
+ command = self._makeOne()
+ interact = DummyInteractor()
+ app = DummyApp()
+ loadapp = DummyLoadApp(app)
+ command.interact = (interact,)
+ command.loadapp = (loadapp,)
+ command.args = ('/foo/bar/myapp.ini', 'myapp')
+ command.command()
+ self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
+ self.assertEqual(loadapp.section_name, 'myapp')
+ self.failUnless(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'], None)
+ self.assertEqual(interact.local, {'root':dummy_root})
+ self.failUnless(interact.banner)
+ self.assertEqual(len(app.threadlocal_manager.popped), 1)
+
+class Dummy:
+ pass
+
+dummy_root = Dummy()
+
+dummy_registry = Dummy()
+
+class DummyInteractor:
+ def __call__(self, banner, local):
+ self.banner = banner
+ self.local = local
+
+class DummyLoadApp:
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, config_name, name=None, relative_to=None):
+ self.config_name = config_name
+ self.section_name = name
+ self.relative_to = relative_to
+ return self.app
+
+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)
+
diff --git a/repoze/bfg/tests/test_scripting.py b/repoze/bfg/tests/test_scripting.py
deleted file mode 100644
index a54b4b7d9..000000000
--- a/repoze/bfg/tests/test_scripting.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import unittest
-
-class TestGetRoot(unittest.TestCase):
- def _callFUT(self, router):
- from repoze.bfg.scripting import get_root
- return get_root(router)
-
- def test_it(self):
- router = DummyRouter()
- result = self._callFUT(router)
- self.assertEqual(result, router)
- self.assertEqual(len(router.threadlocal_manager.pushed), 1)
- self.assertEqual(router.threadlocal_manager.pushed[0],
- {'registry':None, 'request':None})
-
-
-class DummyThreadLocalManager:
- def __init__(self):
- self.pushed = []
-
- def push(self, val):
- self.pushed.append(val)
-
-class DummyRouter:
- def __init__(self):
- self.registry = None
- self.threadlocal_manager = DummyThreadLocalManager()
-
- def root_factory(self, environ):
- return self
-
diff --git a/setup.py b/setup.py
index f1e320f6d..d6aa204b8 100644
--- a/setup.py
+++ b/setup.py
@@ -71,6 +71,8 @@ setup(name='repoze.bfg',
bfg_starter=repoze.bfg.paster:StarterProjectTemplate
bfg_zodb=repoze.bfg.paster:ZODBProjectTemplate
bfg_routesalchemy=repoze.bfg.paster:RoutesAlchemyProjectTemplate
+ [paste.paster_command]
+ bfgshell=repoze.bfg.paster:BFGShellCommand
"""
)