Michael Merickel
2015-10-21 b7350ee78ad4101ea4112741ab26ce583b620ea7
remove ipython and bpython from core
5 files modified
275 ■■■■■ changed files
CHANGES.txt 13 ●●●●● patch | view | raw | blame | history
docs/narr/commandline.rst 43 ●●●●● patch | view | raw | blame | history
pyramid/scripts/pshell.py 78 ●●●● patch | view | raw | blame | history
pyramid/tests/test_scripts/test_pshell.py 135 ●●●● patch | view | raw | blame | history
setup.py 6 ●●●●● patch | view | raw | blame | history
CHANGES.txt
@@ -1,6 +1,19 @@
1.6 (2015-04-14)
================
Backward Incompatibilities
--------------------------
- IPython and BPython support have been removed from pshell in the core.
  To continue using them on Pyramid 1.6+ you must install the binding
  packages explicitly::
    $ pip install pyramid_ipython
    or
    $ pip install pyramid_bpython
Features
--------
docs/narr/commandline.rst
@@ -107,9 +107,7 @@
.. index::
   single: interactive shell
   single: IPython
   single: pshell
   single: bpython
.. _interactive_shell:
@@ -263,18 +261,13 @@
    >>> request.route_url('home')
    'https://www.example.com/'
.. index::
   single: IPython
   single: bpython
.. _ipython_or_bpython:
Alternative Shells
~~~~~~~~~~~~~~~~~~
If you have `IPython <http://en.wikipedia.org/wiki/IPython>`_ and/or `bpython
<http://bpython-interpreter.org/>`_ in the interpreter you use to invoke the
``pshell`` command, ``pshell`` will autodiscover and use the first one found.
However you could specifically invoke your choice with the ``-p choice`` or
The ``pshell`` command can be easily extended with alternate REPLs if the
default python REPL is not satisfactory. Assuming you have a binding
installed such as ``pyramid_ipython`` it will normally be auto-selected and
used. You may also specifically invoke your choice with the ``-p choice`` or
``--python-shell choice`` option.
.. code-block:: text
@@ -287,7 +280,7 @@
   $ $VENV/bin/pshell --list-shells
   Available shells:
     bpython  [not available]
     bpython
     ipython
     python
@@ -309,29 +302,19 @@
.. code-block:: python
    def ptpython_shell_factory():
        try:
            from ptpython.repl import embed
        except ImportError:
            # ptpython is not installed
            return None
    from ptpython.repl import embed
        def PTPShell(banner, **kwargs):
            print(banner)
            return embed(**kwargs)
        def shell(env, help):
            PTPShell(banner=help, locals=env)
        return shell
If the factory returns ``None`` then it is assumed that the shell is not
supported.
    def ptpython_shell_runner(env, help):
        print(help)
        return embed(locals=env)
.. versionchanged:: 1.6
   User-defined shells may be registered using entry points. Prior to this
   the only supported shells were ``ipython``, ``bpython`` and ``python``.
   ``ipython`` and ``bpython`` have been moved into their respective
   packages ``pyramid_ipython`` and ``pyramid_bpython``.
Setting a Default Shell
~~~~~~~~~~~~~~~~~~~~~~~
pyramid/scripts/pshell.py
@@ -21,6 +21,13 @@
    return command.run()
def python_shell_runner(env, help, interact=interact):
    cprt = 'Type "help" for more information.'
    banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt)
    banner += '\n\n' + help + '\n'
    interact(banner, local=env)
class PShellCommand(object):
    usage = '%prog config_uri'
    description = """\
@@ -61,6 +68,7 @@
                            "[pshell] ini section."))
    ConfigParser = configparser.ConfigParser # testing
    default_runner = python_shell_runner # testing
    loaded_objects = {}
    object_help = {}
@@ -186,23 +194,18 @@
    def show_shells(self):
        shells = self.find_all_shells()
        sorted_shells = sorted(shells.items(), key=lambda x: x[0].lower())
        max_name = max([len(s) for s in shells])
        sorted_names = sorted(shells.keys(), key=lambda x: x.lower())
        self.out('Available shells:')
        for name, factory in sorted_shells:
            shell = factory()
            if shell is not None:
                self.out('  %s' % (name,))
            else:
                self.out('  %s%s  [not available]' % (
                    name,
                    ' ' * (max_name - len(name))))
        for name in sorted_names:
            self.out('  %s' % (name,))
        return 0
    def find_all_shells(self):
        pkg_resources = self.pkg_resources
        shells = {}
        for ep in self.pkg_resources.iter_entry_points('pyramid.pshell'):
        for ep in pkg_resources.iter_entry_points('pyramid.pshell_runner'):
            name = ep.name
            shell_factory = ep.load()
            shells[name] = shell_factory
@@ -228,16 +231,15 @@
                except ValueError:
                    return 1
            sorted_shells = sorted(shells.items(), key=order)
            for name, factory in sorted_shells:
                shell = factory()
                if shell is not None:
                    break
            if len(sorted_shells) > 0:
                shell = sorted_shells[0][1]
        else:
            factory = shells.get(user_shell)
            runner = shells.get(user_shell)
            if factory is not None:
                shell = factory()
            if runner is not None:
                shell = runner
            if shell is None:
                raise ValueError(
@@ -245,45 +247,9 @@
                )
        if shell is None:
            shell = self.make_default_shell()
            # should never happen, but just incase entry points are borked
            shell = self.default_runner
        return shell
    @classmethod
    def make_python_shell(cls, interact=interact):
        def shell(env, help):
            cprt = 'Type "help" for more information.'
            banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt)
            banner += '\n\n' + help + '\n'
            interact(banner, local=env)
        return shell
    make_default_shell = make_python_shell
    @classmethod
    def make_bpython_shell(cls, BPShell=None):
        if BPShell is None: # pragma: no cover
            try:
                from bpython import embed
                BPShell = embed
            except ImportError:
                return None
        def shell(env, help):
            BPShell(locals_=env, banner=help + '\n')
        return shell
    @classmethod
    def make_ipython_shell(cls, IPShellFactory=None):
        if IPShellFactory is None: # pragma: no cover
            try:
                from IPython.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
pyramid/tests/test_scripts/test_pshell.py
@@ -37,44 +37,12 @@
    def _makeEntryPoints(self, command, shells):
        command.pkg_resources = dummy.DummyPkgResources(shells)
    def test_make_default_shell(self):
        command = self._makeOne()
        interact = dummy.DummyInteractor()
        shell = command.make_default_shell(interact)
        shell({'foo': 'bar'}, 'a help message')
        self.assertEqual(interact.local, {'foo': 'bar'})
        self.assertTrue('a help message' in interact.banner)
    def test_make_bpython_shell(self):
        command = self._makeOne()
        bpython = dummy.DummyBPythonShell()
        shell = command.make_bpython_shell(bpython)
        shell({'foo': 'bar'}, 'a help message')
        self.assertEqual(bpython.locals_, {'foo': 'bar'})
        self.assertTrue('a help message' in bpython.banner)
    def test_make_ipython_shell(self):
        command = self._makeOne()
        ipshell_factory = dummy.DummyIPShellFactory()
        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_command_loads_default_shell(self):
        command = self._makeOne()
        shell = dummy.DummyShell()
        self._makeEntryPoints(
            command,
            {
                'ipython': lambda: None,
                'bpython': lambda: None,
                'python': lambda: None,
            }
        )
        self._makeEntryPoints(command, {})
        command.make_default_shell = lambda: shell
        command.default_runner = shell
        command.run()
        self.assertTrue(self.config_factory.parser)
        self.assertEqual(self.config_factory.parser.filename,
@@ -99,17 +67,10 @@
        command.out = out
        shell = dummy.DummyShell()
        bad_shell = dummy.DummyShell()
        self._makeEntryPoints(
            command,
            {
                'ipython': lambda: bad_shell,
                'bpython': lambda: bad_shell,
            }
        )
        self._makeEntryPoints(command, {})
        command.make_default_shell = lambda: shell
        command.default_runner = shell
        command.options.python_shell = 'unknown_python_shell'
        result = command.run()
        self.assertEqual(result, 1)
@@ -129,8 +90,8 @@
        self._makeEntryPoints(
            command,
            {
                'ipython': lambda: shell,
                'bpython': lambda: bad_shell,
                'ipython': shell,
                'bpython': bad_shell,
            }
        )
@@ -150,33 +111,6 @@
        self.assertTrue(self.bootstrap.closer.called)
        self.assertTrue(shell.help)
    def test_command_loads_bpython_shell(self):
        command = self._makeOne()
        shell = dummy.DummyBPythonShell()
        self._makeEntryPoints(
            command,
            {
                'ipython': lambda: None,
                'bpython': lambda: shell,
            }
        )
        command.options.python_shell = 'bpython'
        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.locals_, {
            '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.banner)
    def test_shell_entry_points(self):
        command = self._makeOne()
        dshell = dummy.DummyShell()
@@ -184,12 +118,12 @@
        self._makeEntryPoints(
            command,
            {
                'ipython': lambda: dshell,
                'bpython': lambda: dshell,
                'ipython': dshell,
                'bpython': dshell,
            }
        )
        command.make_default_shell = lambda: None
        command.default_runner = None
        shell = command.make_shell()
        self.assertEqual(shell, dshell)
@@ -199,15 +133,9 @@
        bpshell = dummy.DummyShell()
        dshell = dummy.DummyShell()
        self._makeEntryPoints(
            command,
            {
                'ipython': lambda: None,
                'bpython': lambda: None,
            }
        )
        self._makeEntryPoints(command, {})
        command.make_default_shell = lambda: dshell
        command.default_runner = dshell
        shell = command.make_shell()
        self.assertEqual(shell, dshell)
@@ -215,15 +143,12 @@
        command.options.python_shell = 'ipython'
        self.assertRaises(ValueError, command.make_shell)
        command.options.python_shell = 'bpython'
        self.assertRaises(ValueError, command.make_shell)
        self._makeEntryPoints(
            command,
            {
                'ipython': lambda: ipshell,
                'bpython': lambda: bpshell,
                'python': lambda: dshell,
                'ipython': ipshell,
                'bpython': bpshell,
                'python': dshell,
            }
        )
@@ -248,13 +173,13 @@
        self._makeEntryPoints(
            command,
            {
                'ipython': lambda: ipshell,
                'bpython': lambda: bpshell,
                'python': lambda: dshell,
                'ipython': ipshell,
                'bpython': bpshell,
                'python': dshell,
            }
        )
        command.make_default_shell = lambda: dshell
        command.default_runner = dshell
        command.preferred_shells = ['ipython', 'bpython']
        shell = command.make_shell()
@@ -319,9 +244,8 @@
        self._makeEntryPoints(
            command,
            {
                'ipython': lambda: ipshell,
                'bpython': lambda: None,
                'python': lambda: dshell,
                'ipython': ipshell,
                'python': dshell,
            }
        )
        self.config_factory.items = [
@@ -434,9 +358,8 @@
        self._makeEntryPoints(
            command,
            {
                'ipython': lambda: dshell,
                'bpython': lambda: None,
                'python': lambda: dshell,
                'ipython': dshell,
                'python': dshell,
            }
        )
@@ -445,10 +368,21 @@
        self.assertEqual(result, 0)
        self.assertEqual(out_calls, [
            'Available shells:',
            '  bpython  [not available]',
            '  ipython',
            '  python',
        ])
class Test_python_shell_runner(unittest.TestCase):
    def _callFUT(self, env, help, interact):
        from pyramid.scripts.pshell import python_shell_runner
        return python_shell_runner(env, help, interact=interact)
    def test_it(self):
        interact = dummy.DummyInteractor()
        self._callFUT({'foo': 'bar'}, 'a help message', interact)
        self.assertEqual(interact.local, {'foo': 'bar'})
        self.assertTrue('a help message' in interact.banner)
class Test_main(unittest.TestCase):
    def _callFUT(self, argv):
@@ -458,4 +392,3 @@
    def test_it(self):
        result = self._callFUT(['pshell'])
        self.assertEqual(result, 2)
setup.py
@@ -111,10 +111,8 @@
        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
        python=pyramid.scripts.pshell:PShellCommand.make_python_shell
        [pyramid.pshell_runner]
        python=pyramid.scripts.pshell:python_shell_runner
        [console_scripts]
        pcreate = pyramid.scripts.pcreate:main
        pserve = pyramid.scripts.pserve:main