123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- import sys
- import unittest
- from unittest import mock
- from django import __version__
- from django.contrib.auth.models import Group, Permission, User
- from django.contrib.contenttypes.models import ContentType
- from django.core.management import CommandError, call_command
- from django.core.management.commands import shell
- from django.db import connection, models
- from django.test import SimpleTestCase
- from django.test.utils import (
- captured_stdin,
- captured_stdout,
- isolate_apps,
- override_settings,
- )
- from django.urls.base import resolve, reverse
- from .models import Marker, Phone
- class ShellCommandTestCase(SimpleTestCase):
- script_globals = 'print("__name__" in globals() and "Phone" in globals())'
- script_with_inline_function = (
- "import django\ndef f():\n print(django.__version__)\nf()"
- )
- def test_command_option(self):
- with self.assertLogs("test", "INFO") as cm:
- call_command(
- "shell",
- command=(
- "import django; from logging import getLogger; "
- 'getLogger("test").info(django.__version__)'
- ),
- )
- self.assertEqual(cm.records[0].getMessage(), __version__)
- def test_command_option_globals(self):
- with captured_stdout() as stdout:
- call_command("shell", command=self.script_globals)
- self.assertEqual(stdout.getvalue().strip(), "True")
- def test_command_option_inline_function_call(self):
- with captured_stdout() as stdout:
- call_command("shell", command=self.script_with_inline_function)
- self.assertEqual(stdout.getvalue().strip(), __version__)
- @unittest.skipIf(
- sys.platform == "win32", "Windows select() doesn't support file descriptors."
- )
- @mock.patch("django.core.management.commands.shell.select")
- def test_stdin_read(self, select):
- with captured_stdin() as stdin, captured_stdout() as stdout:
- stdin.write("print(100)\n")
- stdin.seek(0)
- call_command("shell")
- self.assertEqual(stdout.getvalue().strip(), "100")
- @unittest.skipIf(
- sys.platform == "win32",
- "Windows select() doesn't support file descriptors.",
- )
- @mock.patch("django.core.management.commands.shell.select") # [1]
- def test_stdin_read_globals(self, select):
- with captured_stdin() as stdin, captured_stdout() as stdout:
- stdin.write(self.script_globals)
- stdin.seek(0)
- call_command("shell")
- self.assertEqual(stdout.getvalue().strip(), "True")
- @unittest.skipIf(
- sys.platform == "win32",
- "Windows select() doesn't support file descriptors.",
- )
- @mock.patch("django.core.management.commands.shell.select") # [1]
- def test_stdin_read_inline_function_call(self, select):
- with captured_stdin() as stdin, captured_stdout() as stdout:
- stdin.write(self.script_with_inline_function)
- stdin.seek(0)
- call_command("shell")
- self.assertEqual(stdout.getvalue().strip(), __version__)
- def test_ipython(self):
- cmd = shell.Command()
- mock_ipython = mock.Mock(start_ipython=mock.MagicMock())
- options = {"verbosity": 0, "no_imports": False}
- with mock.patch.dict(sys.modules, {"IPython": mock_ipython}):
- cmd.ipython(options)
- self.assertEqual(
- mock_ipython.start_ipython.mock_calls,
- [mock.call(argv=[], user_ns=cmd.get_and_report_namespace(**options))],
- )
- @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")
- def test_bpython(self):
- cmd = shell.Command()
- mock_bpython = mock.Mock(embed=mock.MagicMock())
- options = {"verbosity": 0, "no_imports": False}
- with mock.patch.dict(sys.modules, {"bpython": mock_bpython}):
- cmd.bpython(options)
- self.assertEqual(
- mock_bpython.embed.mock_calls,
- [mock.call(cmd.get_and_report_namespace(**options))],
- )
- @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")
- def test_python(self):
- cmd = shell.Command()
- mock_code = mock.Mock(interact=mock.MagicMock())
- options = {"verbosity": 0, "no_startup": True, "no_imports": False}
- with mock.patch.dict(sys.modules, {"code": mock_code}):
- cmd.python(options)
- self.assertEqual(
- mock_code.interact.mock_calls,
- [mock.call(local=cmd.get_and_report_namespace(**options))],
- )
- # [1] Patch select to prevent tests failing 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() toward the
- # end of shell's handle() method.
- class ShellCommandAutoImportsTestCase(SimpleTestCase):
- @override_settings(
- INSTALLED_APPS=["shell", "django.contrib.auth", "django.contrib.contenttypes"]
- )
- def test_get_namespace(self):
- namespace = shell.Command().get_namespace()
- self.assertEqual(
- namespace,
- {
- "Marker": Marker,
- "Phone": Phone,
- "ContentType": ContentType,
- "Group": Group,
- "Permission": Permission,
- "User": User,
- },
- )
- @override_settings(INSTALLED_APPS=["basic", "shell"])
- @isolate_apps("basic", "shell", kwarg_name="apps")
- def test_get_namespace_precedence(self, apps):
- class Article(models.Model):
- class Meta:
- app_label = "basic"
- winner_article = Article
- class Article(models.Model):
- class Meta:
- app_label = "shell"
- with mock.patch("django.apps.apps.get_models", return_value=apps.get_models()):
- namespace = shell.Command().get_namespace()
- self.assertEqual(namespace, {"Article": winner_article})
- @override_settings(
- INSTALLED_APPS=["shell", "django.contrib.auth", "django.contrib.contenttypes"]
- )
- def test_get_namespace_overridden(self):
- class TestCommand(shell.Command):
- def get_namespace(self):
- from django.urls.base import resolve, reverse
- return {
- **super().get_namespace(),
- "resolve": resolve,
- "reverse": reverse,
- }
- namespace = TestCommand().get_namespace()
- self.assertEqual(
- namespace,
- {
- "resolve": resolve,
- "reverse": reverse,
- "Marker": Marker,
- "Phone": Phone,
- "ContentType": ContentType,
- "Group": Group,
- "Permission": Permission,
- "User": User,
- },
- )
- @override_settings(
- INSTALLED_APPS=["shell", "django.contrib.auth", "django.contrib.contenttypes"]
- )
- def test_no_imports_flag(self):
- for verbosity in (0, 1, 2, 3):
- with self.subTest(verbosity=verbosity), captured_stdout() as stdout:
- namespace = shell.Command().get_and_report_namespace(
- verbosity=verbosity, no_imports=True
- )
- self.assertEqual(namespace, {})
- self.assertEqual(stdout.getvalue().strip(), "")
- @override_settings(
- INSTALLED_APPS=["shell", "django.contrib.auth", "django.contrib.contenttypes"]
- )
- def test_verbosity_zero(self):
- with captured_stdout() as stdout:
- cmd = shell.Command()
- namespace = cmd.get_and_report_namespace(verbosity=0)
- self.assertEqual(namespace, cmd.get_namespace())
- self.assertEqual(stdout.getvalue().strip(), "")
- @override_settings(
- INSTALLED_APPS=["shell", "django.contrib.auth", "django.contrib.contenttypes"]
- )
- def test_verbosity_one(self):
- with captured_stdout() as stdout:
- cmd = shell.Command()
- namespace = cmd.get_and_report_namespace(verbosity=1)
- self.assertEqual(namespace, cmd.get_namespace())
- self.assertEqual(
- stdout.getvalue().strip(),
- "6 objects imported automatically (use -v 2 for details).",
- )
- @override_settings(INSTALLED_APPS=["shell", "django.contrib.contenttypes"])
- @mock.patch.dict(sys.modules, {"isort": None})
- def test_message_with_stdout_listing_objects_with_isort_not_installed(self):
- class TestCommand(shell.Command):
- def get_namespace(self):
- class MyClass:
- pass
- constant = "constant"
- return {
- **super().get_namespace(),
- "MyClass": MyClass,
- "constant": constant,
- }
- with captured_stdout() as stdout:
- TestCommand().get_and_report_namespace(verbosity=2)
- self.assertEqual(
- stdout.getvalue().strip(),
- "5 objects imported automatically, including:\n\n"
- " from django.contrib.contenttypes.models import ContentType\n"
- " from shell.models import Phone, Marker",
- )
- def test_message_with_stdout_one_object(self):
- class TestCommand(shell.Command):
- def get_namespace(self):
- return {"connection": connection}
- with captured_stdout() as stdout:
- TestCommand().get_and_report_namespace(verbosity=2)
- cases = {
- 0: "",
- 1: "1 object imported automatically (use -v 2 for details).",
- 2: (
- "1 object imported automatically, including:\n\n"
- " from django.utils.connection import connection"
- ),
- }
- for verbosity, expected in cases.items():
- with self.subTest(verbosity=verbosity):
- with captured_stdout() as stdout:
- TestCommand().get_and_report_namespace(verbosity=verbosity)
- self.assertEqual(stdout.getvalue().strip(), expected)
- def test_message_with_stdout_zero_objects(self):
- class TestCommand(shell.Command):
- def get_namespace(self):
- return {}
- cases = {
- 0: "",
- 1: "0 objects imported automatically.",
- 2: "0 objects imported automatically.",
- }
- for verbosity, expected in cases.items():
- with self.subTest(verbosity=verbosity):
- with captured_stdout() as stdout:
- TestCommand().get_and_report_namespace(verbosity=verbosity)
- self.assertEqual(stdout.getvalue().strip(), expected)
- @override_settings(INSTALLED_APPS=["shell", "django.contrib.contenttypes"])
- def test_message_with_stdout_listing_objects_with_isort(self):
- sorted_imports = (
- " from shell.models import Marker, Phone\n\n"
- " from django.contrib.contenttypes.models import ContentType"
- )
- mock_isort_code = mock.Mock(code=mock.MagicMock(return_value=sorted_imports))
- class TestCommand(shell.Command):
- def get_namespace(self):
- class MyClass:
- pass
- constant = "constant"
- return {
- **super().get_namespace(),
- "MyClass": MyClass,
- "constant": constant,
- }
- with (
- mock.patch.dict(sys.modules, {"isort": mock_isort_code}),
- captured_stdout() as stdout,
- ):
- TestCommand().get_and_report_namespace(verbosity=2)
- self.assertEqual(
- stdout.getvalue().strip(),
- "5 objects imported automatically, including:\n\n"
- " from shell.models import Marker, Phone\n\n"
- " from django.contrib.contenttypes.models import ContentType",
- )
|