summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt6
-rw-r--r--docs/narr/project.rst18
-rw-r--r--repoze/bfg/paster.py31
-rw-r--r--repoze/bfg/tests/test_paster.py47
4 files changed, 91 insertions, 11 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 48921432f..9f8a850e7 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,6 +1,12 @@
Next release
============
+Features
+--------
+
+- ``paster bfgshell`` now supports IPython if it's available for
+ import. Thanks to Daniel Holth for the initial patch.
+
Documentation
-------------
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index dc3385fcd..cb69fdc0e 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -200,7 +200,7 @@ the name ``main`` as a section name:
.. code-block:: bash
:linenos:
- [chrism@vitaminf bfgshellenv]$ ../bin/paster bfgshell MyProject.ini main
+ [chrism@vitaminf bfgshellenv]$ ../bin/paster bfgshell --plugin=repoze.bfg MyProject.ini main
Python 2.4.5 (#1, Aug 29 2008, 12:27:37)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
@@ -208,10 +208,18 @@ the name ``main`` as a section name:
>>> root
<foo.models.MyModel object at 0x445270>
-If that command fails because ``paster`` claims it knows nothing about
-the "bfgshell" command (this happens under certain conditions that are
-not yet well-understood) try passing the flag ``--plugin=repoze.bfg``
-before the filename:
+... note:: You *might* get away without passing
+ ``--plugin=repoze.bfg`` to the bfgshell command; the
+ ``--plugin=repoze.bfg`` option is required under conditions
+ that are not yet well-understood.
+
+If you have `IPython <http://en.wikipedia.org/wiki/IPython>`_
+installed in the interpreter you use to invoke the ``paster`` command,
+the ``bfgshell`` 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 ``bfgshell`` command to use a
+standard Python interpreter shell unconditionally.
.. code-block:: bash
:linenos:
diff --git a/repoze/bfg/paster.py b/repoze/bfg/paster.py
index c60d0b437..de5dc1f79 100644
--- a/repoze/bfg/paster.py
+++ b/repoze/bfg/paster.py
@@ -9,6 +9,12 @@ from paste.util.template import paste_script_template_renderer
from repoze.bfg.scripting import get_root
+try:
+ from IPython.Shell import IPShellEmbed
+except ImportError:
+ IPShellEmbed = None
+
+
class StarterProjectTemplate(Template):
_template_dir = 'paster_templates/starter'
summary = 'repoze.bfg starter project'
@@ -66,8 +72,14 @@ class BFGShellCommand(Command):
group_name = 'bfg'
parser = Command.standard_parser(simulate=True)
+ parser.add_option('-d', '--disable-ipython',
+ action='store_true',
+ dest='disable_ipython',
+ help="Don't use IPython even if it is available")
+
interact = (interact,) # for testing
loadapp = (loadapp,) # for testing
+ IPShellEmbed = IPShellEmbed # for testing
verbose = 3
def command(self):
@@ -77,8 +89,17 @@ class BFGShellCommand(Command):
config_file, section_name = self.args
app = get_app(config_file, section_name, loadapp=self.loadapp[0])
root, closer = get_root(app)
- try:
- self.interact[0](banner, local={'root':root})
- finally:
- closer()
-
+ if self.IPShellEmbed is not None and not self.options.disable_ipython:
+ shell = self.IPShellEmbed(argv=self.args)
+ shell.set_banner(shell.IP.BANNER + '\n\n' + banner)
+ try:
+ shell(local_ns={'root':root}, global_ns={})
+ finally:
+ closer()
+ else:
+ try:
+ self.interact[0](banner, local={'root':root})
+ finally:
+ closer()
+
+
diff --git a/repoze/bfg/tests/test_paster.py b/repoze/bfg/tests/test_paster.py
index 0119e3313..4650f04cd 100644
--- a/repoze/bfg/tests/test_paster.py
+++ b/repoze/bfg/tests/test_paster.py
@@ -8,14 +8,18 @@ class TestBFGShellCommand(unittest.TestCase):
def _makeOne(self):
return self._getTargetClass()('bfgshell')
- def test_command(self):
+ def test_command_ipython_disabled(self):
command = self._makeOne()
interact = DummyInteractor()
app = DummyApp()
loadapp = DummyLoadApp(app)
command.interact = (interact,)
command.loadapp = (loadapp,)
+ command.IPShellEmbed = True # fake out
command.args = ('/foo/bar/myapp.ini', 'myapp')
+ class Options(object): pass
+ command.options = Options()
+ command.options.disable_ipython =True
command.command()
self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
self.assertEqual(loadapp.section_name, 'myapp')
@@ -28,6 +32,31 @@ class TestBFGShellCommand(unittest.TestCase):
self.failUnless(interact.banner)
self.assertEqual(len(app.threadlocal_manager.popped), 1)
+ def test_command_ipython_enabled(self):
+ command = self._makeOne()
+ interact = DummyInteractor()
+ app = DummyApp()
+ loadapp = DummyLoadApp(app)
+ command.loadapp = (loadapp,)
+ dummy_shell_factory = DummyIPShellFactory()
+ command.IPShellEmbed = dummy_shell_factory
+ command.args = ('/foo/bar/myapp.ini', 'myapp')
+ class Options(object): pass
+ command.options = Options()
+ command.options.disable_ipython = False
+ 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(dummy_shell_factory.shell.local_ns,{'root':dummy_root})
+ self.assertEqual(dummy_shell_factory.shell.global_ns, {})
+ self.failUnless(dummy_shell_factory.shell.banner)
+ self.assertEqual(len(app.threadlocal_manager.popped), 1)
+
class TestGetApp(unittest.TestCase):
def _callFUT(self, config_file, section_name, loadapp):
from repoze.bfg.paster import get_app
@@ -46,6 +75,22 @@ class TestGetApp(unittest.TestCase):
class Dummy:
pass
+class DummyIPShellFactory(object):
+ def __call__(self, argv):
+ shell = DummyIPShell()
+ self.shell = shell
+ return shell
+
+class DummyIPShell(object):
+ IP = Dummy()
+ IP.BANNER = 'foo'
+ def set_banner(self, banner):
+ self.banner = banner
+
+ def __call__(self, local_ns, global_ns):
+ self.local_ns = local_ns
+ self.global_ns = global_ns
+
dummy_root = Dummy()
dummy_registry = Dummy()