diff options
| author | John Anderson <sontek@gmail.com> | 2015-09-06 15:26:35 -0700 |
|---|---|---|
| committer | John Anderson <sontek@gmail.com> | 2015-09-06 15:26:35 -0700 |
| commit | cb92023e9d0ddb56d20dcc445a5e028fec581342 (patch) | |
| tree | f101714a995a4c8d1d373cd4d4b6e188f8929f1f | |
| parent | f17b973ea66b7ffb2afb03da2c559e55c22fe3ee (diff) | |
| download | pyramid-cb92023e9d0ddb56d20dcc445a5e028fec581342.tar.gz pyramid-cb92023e9d0ddb56d20dcc445a5e028fec581342.tar.bz2 pyramid-cb92023e9d0ddb56d20dcc445a5e028fec581342.zip | |
Use entry points for pshell
| -rw-r--r-- | docs/narr/commandline.rst | 34 | ||||
| -rw-r--r-- | pyramid/scripts/pshell.py | 73 | ||||
| -rw-r--r-- | pyramid/tests/test_scripts/dummy.py | 23 | ||||
| -rw-r--r-- | pyramid/tests/test_scripts/test_pshell.py | 166 | ||||
| -rw-r--r-- | setup.py | 3 |
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) @@ -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 |
