diff --git a/django/core/management/commands/shell.py b/django/core/management/commands/shell.py index fbf79711f6..de8f33ed84 100644 --- a/django/core/management/commands/shell.py +++ b/django/core/management/commands/shell.py @@ -3,7 +3,7 @@ import select import sys import warnings -from django.core.management.base import BaseCommand +from django.core.management import BaseCommand, CommandError from django.utils.datastructures import OrderedSet from django.utils.deprecation import RemovedInDjango20Warning @@ -108,4 +108,4 @@ class Command(BaseCommand): return getattr(self, shell)(options) except ImportError: pass - raise ImportError("Couldn't load any of the specified interfaces.") + raise CommandError("Couldn't import {} interface.".format(shell)) diff --git a/tests/shell/tests.py b/tests/shell/tests.py index 11f1826d30..f2d1d0e392 100644 --- a/tests/shell/tests.py +++ b/tests/shell/tests.py @@ -2,7 +2,7 @@ import sys import unittest from django import __version__ -from django.core.management import call_command +from django.core.management import CommandError, call_command from django.test import SimpleTestCase, mock from django.test.utils import captured_stdin, captured_stdout, patch_logger @@ -29,3 +29,24 @@ class ShellCommandTestCase(SimpleTestCase): stdin.seek(0) call_command('shell') self.assertEqual(stdout.getvalue().strip(), '100') + + @mock.patch('django.core.management.commands.shell.select.select') # [1] + @mock.patch.dict('sys.modules', {'IPython': None}) + def test_shell_with_ipython_not_installed(self, select): + select.return_value = ([], [], []) + with self.assertRaisesMessage(CommandError, "Couldn't import ipython interface."): + call_command('shell', interface='ipython') + + @mock.patch('django.core.management.commands.shell.select.select') # [1] + @mock.patch.dict('sys.modules', {'bpython': None}) + def test_shell_with_bpython_not_installed(self, select): + select.return_value = ([], [], []) + with self.assertRaisesMessage(CommandError, "Couldn't import bpython interface."): + call_command('shell', interface='bpython') + + # [1] Patch select to prevent tests failing when when the test suite is run + # in parallel mode. The tests are run in a subprocess and the subprocess's + # stdin is closed and replaced by /dev/null. Reading from /dev/null always + # returns EOF and so select always shows that sys.stdin is ready to read. + # This causes problems because of the call to select.select() towards the + # end of shell's handle() method.