summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Anderson <sontek@gmail.com>2015-09-06 15:26:35 -0700
committerJohn Anderson <sontek@gmail.com>2015-09-06 15:26:35 -0700
commitcb92023e9d0ddb56d20dcc445a5e028fec581342 (patch)
treef101714a995a4c8d1d373cd4d4b6e188f8929f1f
parentf17b973ea66b7ffb2afb03da2c559e55c22fe3ee (diff)
downloadpyramid-cb92023e9d0ddb56d20dcc445a5e028fec581342.tar.gz
pyramid-cb92023e9d0ddb56d20dcc445a5e028fec581342.tar.bz2
pyramid-cb92023e9d0ddb56d20dcc445a5e028fec581342.zip
Use entry points for pshell
-rw-r--r--docs/narr/commandline.rst34
-rw-r--r--pyramid/scripts/pshell.py73
-rw-r--r--pyramid/tests/test_scripts/dummy.py23
-rw-r--r--pyramid/tests/test_scripts/test_pshell.py166
-rw-r--r--setup.py3
5 files changed, 155 insertions, 144 deletions
diff --git a/docs/narr/commandline.rst b/docs/narr/commandline.rst
index 1fe2d9278..89df13ce4 100644
--- a/docs/narr/commandline.rst
+++ b/docs/narr/commandline.rst
@@ -288,6 +288,40 @@ specifically invoke one of your choice with the ``-p choice`` or
$ $VENV/bin/pshell -p ipython | bpython | python development.ini#MyProject
+Alternative Shells
+~~~~~~~~~~~~~~~~~~
+If you want to use a shell that isn't supported out of the box you can introduce
+a new shell by registering an entrypoint in your setup.py:
+
+.. code-block:: python
+
+ setup(
+ entry_points = """\
+ [pyramid.pshell]
+ myshell=my_app.ptpython_shell_factory
+ """
+ )
+
+and then your shell factory should return a function that accepts two arguments,
+``env`` and ``help``, this would look like this:
+
+.. code-block:: python
+
+ def ptpython_shell_factory():
+ try:
+ from ptpython.repl import embed
+ def PTPShell(banner, **kwargs):
+ print(banner)
+ return embed(**kwargs)
+ except ImportError:
+ return None
+
+ def shell(env, help):
+ PTPShell(banner=help, locals=env)
+
+ return shell
+
+
.. index::
pair: routes; printing
single: proutes
diff --git a/pyramid/scripts/pshell.py b/pyramid/scripts/pshell.py
index 1168ba78a..563418302 100644
--- a/pyramid/scripts/pshell.py
+++ b/pyramid/scripts/pshell.py
@@ -3,6 +3,7 @@ import optparse
import os
import sys
import textwrap
+import pkg_resources
from pyramid.compat import configparser
from pyramid.compat import exec_
@@ -17,6 +18,7 @@ def main(argv=sys.argv, quiet=False):
command = PShellCommand(argv, quiet)
return command.run()
+
class PShellCommand(object):
usage = '%prog config_uri'
description = """\
@@ -32,7 +34,8 @@ class PShellCommand(object):
than one Pyramid application within it, the loader will use the
last one.
"""
- bootstrap = (bootstrap,) # for testing
+ bootstrap = (bootstrap,) # for testing
+ pkg_resources = pkg_resources # for testing
parser = optparse.OptionParser(
usage,
@@ -159,18 +162,33 @@ class PShellCommand(object):
closer()
def make_shell(self):
+ shells = {}
+
+ priority_order = ['ipython', 'bpython']
+
+ for ep in self.pkg_resources.iter_entry_points('pyramid.pshell'):
+ name = ep.name
+ shell_module = ep.load()
+ shells[name] = shell_module
+
+ sorted_shells = sorted(
+ shells.items(), key=lambda x: priority_order.index(x[0])
+ )
+
shell = None
user_shell = self.options.python_shell.lower()
+
if not user_shell:
- shell = self.make_ipython_shell()
- if shell is None:
- shell = self.make_bpython_shell()
+ for name, factory in sorted_shells:
+ shell = factory()
- elif user_shell == 'ipython':
- shell = self.make_ipython_shell()
+ if shell is not None:
+ break
+ else:
+ factory = shells.get(user_shell)
- elif user_shell == 'bpython':
- shell = self.make_bpython_shell()
+ if factory is not None:
+ shell = factory()
if shell is None:
shell = self.make_default_shell()
@@ -185,7 +203,8 @@ class PShellCommand(object):
interact(banner, local=env)
return shell
- def make_bpython_shell(self, BPShell=None):
+ @classmethod
+ def make_bpython_shell(cls, BPShell=None):
if BPShell is None: # pragma: no cover
try:
from bpython import embed
@@ -196,15 +215,8 @@ class PShellCommand(object):
BPShell(locals_=env, banner=help + '\n')
return shell
- def make_ipython_shell(self):
- shell = self.make_ipython_v1_1_shell()
- if shell is None:
- shell = self.make_ipython_v0_11_shell()
- if shell is None:
- shell = self.make_ipython_v0_10_shell()
- return shell
-
- def make_ipython_v1_1_shell(self, IPShellFactory=None):
+ @classmethod
+ def make_ipython_shell(cls, IPShellFactory=None):
if IPShellFactory is None: # pragma: no cover
try:
from IPython.terminal.embed import (
@@ -217,31 +229,6 @@ class PShellCommand(object):
IPShell()
return shell
- def make_ipython_v0_11_shell(self, IPShellFactory=None):
- if IPShellFactory is None: # pragma: no cover
- try:
- from IPython.frontend.terminal.embed import (
- InteractiveShellEmbed)
- IPShellFactory = InteractiveShellEmbed
- except ImportError:
- return None
- def shell(env, help):
- IPShell = IPShellFactory(banner2=help + '\n', user_ns=env)
- IPShell()
- return shell
-
- def make_ipython_v0_10_shell(self, IPShellFactory=None):
- if IPShellFactory is None: # pragma: no cover
- try:
- from IPython.Shell import IPShellEmbed
- IPShellFactory = IPShellEmbed
- except ImportError:
- return None
- def shell(env, help):
- IPShell = IPShellFactory(argv=[], user_ns=env)
- IPShell.set_banner(IPShell.IP.BANNER + '\n' + help + '\n')
- IPShell()
- return shell
if __name__ == '__main__': # pragma: no cover
sys.exit(main() or 0)
diff --git a/pyramid/tests/test_scripts/dummy.py b/pyramid/tests/test_scripts/dummy.py
index 930b9ed64..2788c0b32 100644
--- a/pyramid/tests/test_scripts/dummy.py
+++ b/pyramid/tests/test_scripts/dummy.py
@@ -40,9 +40,6 @@ class DummyIPShell(object):
IP = Dummy()
IP.BANNER = 'foo'
- def set_banner(self, banner):
- self.banner = banner
-
def __call__(self):
self.called = True
@@ -157,3 +154,23 @@ class DummyBootstrap(object):
'root_factory': self.root_factory,
'closer': self.closer,
}
+
+
+class DummyEntryPoint(object):
+ def __init__(self, name, module):
+ self.name = name
+ self.module = module
+
+ def load(self):
+ return self.module
+
+
+class DummyPkgResources(object):
+ def __init__(self, entry_point_values):
+ self.entry_points = []
+
+ for name, module in entry_point_values.items():
+ self.entry_points.append(DummyEntryPoint(name, module))
+
+ def iter_entry_points(self, name):
+ return self.entry_points
diff --git a/pyramid/tests/test_scripts/test_pshell.py b/pyramid/tests/test_scripts/test_pshell.py
index dab32fecd..b5c2775d6 100644
--- a/pyramid/tests/test_scripts/test_pshell.py
+++ b/pyramid/tests/test_scripts/test_pshell.py
@@ -2,6 +2,7 @@ import os
import unittest
from pyramid.tests.test_scripts import dummy
+
class TestPShellCommand(unittest.TestCase):
def _getTargetClass(self):
from pyramid.scripts.pshell import PShellCommand
@@ -10,6 +11,7 @@ class TestPShellCommand(unittest.TestCase):
def _makeOne(self, patch_bootstrap=True, patch_config=True,
patch_args=True, patch_options=True):
cmd = self._getTargetClass()([])
+
if patch_bootstrap:
self.bootstrap = dummy.DummyBootstrap()
cmd.bootstrap = (self.bootstrap,)
@@ -25,11 +27,15 @@ class TestPShellCommand(unittest.TestCase):
self.options.python_shell = ''
self.options.setup = None
cmd.options = self.options
+
# default to None to prevent side-effects from running tests in
# unknown environments
cmd.pystartup = None
return cmd
+ def _makeEntryPoints(self, command, shells):
+ command.pkg_resources = dummy.DummyPkgResources(shells)
+
def test_make_default_shell(self):
command = self._makeOne()
interact = dummy.DummyInteractor()
@@ -49,36 +55,23 @@ class TestPShellCommand(unittest.TestCase):
def test_make_ipython_v1_1_shell(self):
command = self._makeOne()
ipshell_factory = dummy.DummyIPShellFactory()
- shell = command.make_ipython_v1_1_shell(ipshell_factory)
+ shell = command.make_ipython_shell(ipshell_factory)
shell({'foo': 'bar'}, 'a help message')
self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'})
self.assertTrue('a help message' in ipshell_factory.kw['banner2'])
self.assertTrue(ipshell_factory.shell.called)
- def test_make_ipython_v0_11_shell(self):
- command = self._makeOne()
- ipshell_factory = dummy.DummyIPShellFactory()
- shell = command.make_ipython_v0_11_shell(ipshell_factory)
- shell({'foo': 'bar'}, 'a help message')
- self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'})
- self.assertTrue('a help message' in ipshell_factory.kw['banner2'])
- self.assertTrue(ipshell_factory.shell.called)
-
- def test_make_ipython_v0_10_shell(self):
- command = self._makeOne()
- ipshell_factory = dummy.DummyIPShellFactory()
- shell = command.make_ipython_v0_10_shell(ipshell_factory)
- shell({'foo': 'bar'}, 'a help message')
- self.assertEqual(ipshell_factory.kw['argv'], [])
- self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'})
- self.assertTrue('a help message' in ipshell_factory.shell.banner)
- self.assertTrue(ipshell_factory.shell.called)
-
def test_command_loads_default_shell(self):
command = self._makeOne()
shell = dummy.DummyShell()
- command.make_ipython_shell = lambda: None
- command.make_bpython_shell = lambda: None
+ self._makeEntryPoints(
+ command,
+ {
+ 'ipython': lambda: None,
+ 'bpython': lambda: None,
+ }
+ )
+
command.make_default_shell = lambda: shell
command.run()
self.assertTrue(self.config_factory.parser)
@@ -98,8 +91,15 @@ class TestPShellCommand(unittest.TestCase):
command = self._makeOne()
shell = dummy.DummyShell()
bad_shell = dummy.DummyShell()
- command.make_ipython_shell = lambda: bad_shell
- command.make_bpython_shell = lambda: bad_shell
+
+ self._makeEntryPoints(
+ command,
+ {
+ 'ipython': lambda: bad_shell,
+ 'bpython': lambda: bad_shell,
+ }
+ )
+
command.make_default_shell = lambda: shell
command.options.python_shell = 'unknow_python_shell'
command.run()
@@ -120,58 +120,16 @@ class TestPShellCommand(unittest.TestCase):
def test_command_loads_ipython_v1_1(self):
command = self._makeOne()
shell = dummy.DummyShell()
- command.make_ipython_v1_1_shell = lambda: shell
- command.make_ipython_v0_11_shell = lambda: None
- command.make_ipython_v0_10_shell = lambda: None
- command.make_bpython_shell = lambda: None
- command.make_default_shell = lambda: None
- command.options.python_shell = 'ipython'
- command.run()
- 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(shell.env, {
- '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(shell.help)
+ self._makeEntryPoints(
+ command,
+ {
+ 'ipython': lambda: shell,
+ 'bpython': lambda: bad_shell,
+ }
+ )
- def test_command_loads_ipython_v0_11(self):
- command = self._makeOne()
- shell = dummy.DummyShell()
- command.make_ipython_v1_1_shell = lambda: None
- command.make_ipython_v0_11_shell = lambda: shell
- command.make_ipython_v0_10_shell = lambda: None
- command.make_bpython_shell = lambda: None
- command.make_default_shell = lambda: None
command.options.python_shell = 'ipython'
- command.run()
- 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(shell.env, {
- '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(shell.help)
- def test_command_loads_ipython_v0_10(self):
- command = self._makeOne()
- shell = dummy.DummyShell()
- command.make_ipython_v1_1_shell = lambda: None
- command.make_ipython_v0_11_shell = lambda: None
- command.make_ipython_v0_10_shell = lambda: shell
- command.make_bpython_shell = lambda: None
- command.make_default_shell = lambda: None
- command.options.python_shell = 'ipython'
command.run()
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
@@ -189,8 +147,15 @@ class TestPShellCommand(unittest.TestCase):
def test_command_loads_bpython_shell(self):
command = self._makeOne()
shell = dummy.DummyBPythonShell()
- command.make_ipython_shell = lambda: None
- command.make_bpython_shell = lambda: shell
+
+ self._makeEntryPoints(
+ command,
+ {
+ 'ipython': lambda: None,
+ 'bpython': lambda: shell,
+ }
+ )
+
command.options.python_shell = 'bpython'
command.run()
self.assertTrue(self.config_factory.parser)
@@ -206,37 +171,36 @@ class TestPShellCommand(unittest.TestCase):
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.banner)
- def test_shell_ipython_ordering(self):
+ def test_shell_entry_points(self):
command = self._makeOne()
- shell1_1 = dummy.DummyShell()
- shell0_11 = dummy.DummyShell()
- shell0_10 = dummy.DummyShell()
- command.make_ipython_v1_1_shell = lambda: shell1_1
- shell = command.make_shell()
- self.assertEqual(shell, shell1_1)
-
- command.make_ipython_v1_1_shell = lambda: None
- command.make_ipython_v0_11_shell = lambda: shell0_11
- shell = command.make_shell()
- self.assertEqual(shell, shell0_11)
+ dshell = dummy.DummyShell()
- command.make_ipython_v0_11_shell = lambda: None
- command.make_ipython_v0_10_shell = lambda: shell0_10
- shell = command.make_shell()
- self.assertEqual(shell, shell0_10)
+ self._makeEntryPoints(
+ command,
+ {
+ 'ipython': lambda: dshell,
+ 'bpython': lambda: dhell,
+ }
+ )
- command.options.python_shell = 'ipython'
- command.make_ipython_v1_1_shell = lambda: shell1_1
+ command.make_default_shell = lambda: None
shell = command.make_shell()
- self.assertEqual(shell, shell1_1)
+ self.assertEqual(shell, dshell)
def test_shell_ordering(self):
command = self._makeOne()
ipshell = dummy.DummyShell()
bpshell = dummy.DummyShell()
dshell = dummy.DummyShell()
- command.make_ipython_shell = lambda: None
- command.make_bpython_shell = lambda: None
+
+ self._makeEntryPoints(
+ command,
+ {
+ 'ipython': lambda: None,
+ 'bpython': lambda: None,
+ }
+ )
+
command.make_default_shell = lambda: dshell
shell = command.make_shell()
@@ -250,8 +214,14 @@ class TestPShellCommand(unittest.TestCase):
shell = command.make_shell()
self.assertEqual(shell, dshell)
- command.make_ipython_shell = lambda: ipshell
- command.make_bpython_shell = lambda: bpshell
+ self._makeEntryPoints(
+ command,
+ {
+ 'ipython': lambda: ipshell,
+ 'bpython': lambda: bpshell,
+ }
+ )
+
command.options.python_shell = 'ipython'
shell = command.make_shell()
self.assertEqual(shell, ipshell)
diff --git a/setup.py b/setup.py
index ffe70cd00..bb71880b5 100644
--- a/setup.py
+++ b/setup.py
@@ -110,6 +110,9 @@ setup(name='pyramid',
starter=pyramid.scaffolds:StarterProjectTemplate
zodb=pyramid.scaffolds:ZODBProjectTemplate
alchemy=pyramid.scaffolds:AlchemyProjectTemplate
+ [pyramid.pshell]
+ ipython=pyramid.scripts.pshell:PShellCommand.make_ipython_shell
+ bpython=pyramid.scripts.pshell:PShellCommand.make_bpython_shell
[console_scripts]
pcreate = pyramid.scripts.pcreate:main
pserve = pyramid.scripts.pserve:main