docs/narr/commandline.rst | ●●●●● patch | view | raw | blame | history | |
pyramid/scripts/pshell.py | ●●●●● patch | view | raw | blame | history | |
pyramid/tests/test_scripts/dummy.py | ●●●●● patch | view | raw | blame | history | |
pyramid/tests/test_scripts/test_pshell.py | ●●●●● patch | view | raw | blame | history | |
setup.py | ●●●●● patch | view | raw | blame | history |
docs/narr/commandline.rst
@@ -269,32 +269,39 @@ .. _ipython_or_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, in this order: IPython, bpython, standard Python interpreter. However you could specifically invoke your choice with the ``-p choice`` or ``--python-shell choice`` option. ``pshell`` command, ``pshell`` will autodiscover and use the first one found. However you could specifically invoke your choice with the ``-p choice`` or ``--python-shell choice`` option. .. code-block:: text $ $VENV/bin/pshell -p ipython | bpython | python development.ini#MyProject $ $VENV/bin/pshell -p ipython development.ini#MyProject Alternative Shells ~~~~~~~~~~~~~~~~~~ You may use the ``--list-shells`` option to see the available shells. .. code-block:: text $ $VENV/bin/pshell --list-shells Available shells: bpython [not available] ipython python If you want to use a shell that isn't supported out of the box, you can introduce a new shell by registering an entry point in your setup.py: .. code-block:: python setup( entry_points = """\ [pyramid.pshell] myshell=my_app:ptpython_shell_factory """ entry_points={ 'pyramid.pshell': [ 'myshell=my_app:ptpython_shell_factory', ], }, ) And then your shell factory should return a function that accepts two @@ -303,7 +310,12 @@ .. code-block:: python def ptpython_shell_factory(): from ptpython.repl import embed try: from ptpython.repl import embed except ImportError: # ptpython is not installed return None def PTPShell(banner, **kwargs): print(banner) return embed(**kwargs) @@ -313,6 +325,25 @@ return shell If the factory returns ``None`` then it is assumed that the shell is not supported. .. versionchanged:: 1.6 User-defined shells may be registered using entry points. Prior to this the only supported shells were ``ipython``, ``bpython`` and ``python``. Setting a Default Shell ~~~~~~~~~~~~~~~~~~~~~~~ You may use the ``default_shell`` option in your ``[pshell]`` ini section to specify a list of preferred shells. .. code-block:: ini :linenos: [pshell] default_shell = ptpython ipython bpython .. versionadded:: 1.6 .. index:: pyramid/scripts/pshell.py
@@ -12,6 +12,8 @@ from pyramid.paster import setup_logging from pyramid.settings import aslist from pyramid.scripts.common import parse_vars def main(argv=sys.argv, quiet=False): @@ -43,7 +45,14 @@ ) parser.add_option('-p', '--python-shell', action='store', type='string', dest='python_shell', default='', help='ipython | bpython | python') default='', help=('Select the shell to use. A list of possible ' 'shells is available using the --list-shells ' 'option.')) parser.add_option('-l', '--list-shells', dest='list', action='store_true', help='List all available shells.') parser.add_option('--setup', dest='setup', help=("A callable that will be passed the environment " @@ -55,6 +64,7 @@ loaded_objects = {} object_help = {} preferred_shells = [] setup = None pystartup = os.environ.get('PYTHONSTARTUP') @@ -64,6 +74,7 @@ def pshell_file_config(self, filename): config = self.ConfigParser() config.optionxform = str config.read(filename) try: items = config.items('pshell') @@ -77,6 +88,8 @@ for k, v in items: if k == 'setup': self.setup = v elif k == 'default_shell': self.preferred_shells = [x.lower() for x in aslist(v)] else: self.loaded_objects[k] = resolver.maybe_resolve(v) self.object_help[k] = v @@ -86,6 +99,8 @@ print(msg) def run(self, shell=None): if self.options.list: return self.show_shells() if not self.args: self.out('Requires a config file argument') return 2 @@ -169,19 +184,50 @@ finally: self.closer() def make_shell(self): shells = {} 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]) 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)))) return 0 def find_all_shells(self): shells = {} for ep in self.pkg_resources.iter_entry_points('pyramid.pshell'): name = ep.name shell_module = ep.load() shells[name] = shell_module shell_factory = ep.load() shells[name] = shell_factory return shells def make_shell(self): shells = self.find_all_shells() shell = None user_shell = self.options.python_shell.lower() if not user_shell: sorted_shells = sorted(shells.items(), key=lambda x: x[0]) preferred_shells = self.preferred_shells if not preferred_shells: # by default prioritize all shells above python preferred_shells = [k for k in shells.keys() if k != 'python'] max_weight = len(preferred_shells) def order(x): # invert weight to reverse sort the list # (closer to the front is higher priority) try: return preferred_shells.index(x[0].lower()) - max_weight except ValueError: return 1 sorted_shells = sorted(shells.items(), key=order) for name, factory in sorted_shells: shell = factory() @@ -192,7 +238,8 @@ if factory is not None: shell = factory() else: if shell is None: raise ValueError( 'could not find a shell named "%s"' % user_shell ) @@ -202,7 +249,8 @@ return shell def make_default_shell(self, interact=interact): @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) @@ -210,6 +258,8 @@ 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 pyramid/tests/test_scripts/dummy.py
@@ -21,10 +21,12 @@ class DummyShell(object): env = {} help = '' called = False def __call__(self, env, help): self.env = env self.help = help self.called = True class DummyInteractor: def __call__(self, banner, local): @@ -35,6 +37,7 @@ def __call__(self, locals_, banner): self.locals_ = locals_ self.banner = banner self.called = True class DummyIPShell(object): IP = Dummy() pyramid/tests/test_scripts/test_pshell.py
@@ -26,6 +26,7 @@ self.options = Options() self.options.python_shell = '' self.options.setup = None self.options.list = None cmd.options = self.options # default to None to prevent side-effects from running tests in @@ -52,7 +53,7 @@ self.assertEqual(bpython.locals_, {'foo': 'bar'}) self.assertTrue('a help message' in bpython.banner) def test_make_ipython_v1_1_shell(self): def test_make_ipython_shell(self): command = self._makeOne() ipshell_factory = dummy.DummyIPShellFactory() shell = command.make_ipython_shell(ipshell_factory) @@ -69,6 +70,7 @@ { 'ipython': lambda: None, 'bpython': lambda: None, 'python': lambda: None, } ) @@ -87,7 +89,7 @@ self.assertTrue(self.bootstrap.closer.called) self.assertTrue(shell.help) def test_command_loads_default_shell_with_unknown_shell(self): def test_command_errors_with_unknown_shell(self): command = self._makeOne() out_calls = [] @@ -120,9 +122,10 @@ self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') self.assertTrue(self.bootstrap.closer.called) def test_command_loads_ipython_v1_1(self): def test_command_loads_ipython(self): command = self._makeOne() shell = dummy.DummyShell() bad_shell = dummy.DummyShell() self._makeEntryPoints( command, { @@ -190,7 +193,7 @@ shell = command.make_shell() self.assertEqual(shell, dshell) def test_shell_ordering(self): def test_shell_override(self): command = self._makeOne() ipshell = dummy.DummyShell() bpshell = dummy.DummyShell() @@ -210,12 +213,10 @@ self.assertEqual(shell, dshell) command.options.python_shell = 'ipython' shell = command.make_shell() self.assertEqual(shell, dshell) self.assertRaises(ValueError, command.make_shell) command.options.python_shell = 'bpython' shell = command.make_shell() self.assertEqual(shell, dshell) self.assertRaises(ValueError, command.make_shell) self._makeEntryPoints( command, @@ -235,6 +236,35 @@ self.assertEqual(shell, bpshell) command.options.python_shell = 'python' shell = command.make_shell() self.assertEqual(shell, dshell) def test_shell_ordering(self): command = self._makeOne() ipshell = dummy.DummyShell() bpshell = dummy.DummyShell() dshell = dummy.DummyShell() self._makeEntryPoints( command, { 'ipython': lambda: ipshell, 'bpython': lambda: bpshell, 'python': lambda: dshell, } ) command.make_default_shell = lambda: dshell command.preferred_shells = ['ipython', 'bpython'] shell = command.make_shell() self.assertEqual(shell, ipshell) command.preferred_shells = ['bpython', 'python'] shell = command.make_shell() self.assertEqual(shell, bpshell) command.preferred_shells = ['python', 'ipython'] shell = command.make_shell() self.assertEqual(shell, dshell) @@ -281,6 +311,27 @@ }) self.assertTrue(self.bootstrap.closer.called) self.assertTrue(shell.help) def test_command_default_shell_option(self): command = self._makeOne() ipshell = dummy.DummyShell() dshell = dummy.DummyShell() self._makeEntryPoints( command, { 'ipython': lambda: ipshell, 'bpython': lambda: None, 'python': lambda: dshell, } ) self.config_factory.items = [ ('default_shell', 'bpython python\nipython')] 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.assertTrue(dshell.called) def test_command_loads_check_variable_override_order(self): command = self._makeOne() @@ -369,6 +420,36 @@ self.assertTrue(self.bootstrap.closer.called) self.assertTrue(shell.help) def test_list_shells(self): command = self._makeOne() dshell = dummy.DummyShell() out_calls = [] def out(msg): out_calls.append(msg) command.out = out self._makeEntryPoints( command, { 'ipython': lambda: dshell, 'bpython': lambda: None, 'python': lambda: dshell, } ) command.options.list = True result = command.run() self.assertEqual(result, 0) self.assertEqual(out_calls, [ 'Available shells:', ' bpython [not available]', ' ipython', ' python', ]) class Test_main(unittest.TestCase): def _callFUT(self, argv): from pyramid.scripts.pshell import main setup.py
@@ -114,6 +114,7 @@ [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 [console_scripts] pcreate = pyramid.scripts.pcreate:main pserve = pyramid.scripts.pserve:main