Fixed #32314 -- Fixed detection when started non-django modules with "python -m" in autoreloader.

django.utils.autoreload.get_child_arguments() detected when Python was
started with the `-m` option only for `django` module. This commit
changes the logic to check __spec__, see
https://docs.python.org/3/reference/import.html#main-spec

Now packages can implement their own __main__ with the runserver
command.
This commit is contained in:
William Schwartz 2021-01-04 08:50:49 -06:00 committed by Mariusz Felisiak
parent 415f50298f
commit ec6d2531c5
3 changed files with 18 additions and 6 deletions

View File

@ -216,14 +216,14 @@ def get_child_arguments():
executable is reported to not have the .exe extension which can cause bugs executable is reported to not have the .exe extension which can cause bugs
on reloading. on reloading.
""" """
import django.__main__ import __main__
django_main_path = Path(django.__main__.__file__)
py_script = Path(sys.argv[0]) py_script = Path(sys.argv[0])
args = [sys.executable] + ['-W%s' % o for o in sys.warnoptions] args = [sys.executable] + ['-W%s' % o for o in sys.warnoptions]
if py_script == django_main_path: # __spec__ is set when the server was started with the `-m` option,
# The server was started with `python -m django runserver`. # see https://docs.python.org/3/reference/import.html#main-spec
args += ['-m', 'django'] if __main__.__spec__ is not None and __main__.__spec__.parent:
args += ['-m', __main__.__spec__.parent]
args += sys.argv[1:] args += sys.argv[1:]
elif not py_script.exists(): elif not py_script.exists():
# sys.argv[0] may not exist for several reasons on Windows. # sys.argv[0] may not exist for several reasons on Windows.

View File

@ -23,6 +23,7 @@ from django.test.utils import extend_sys_path
from django.utils import autoreload from django.utils import autoreload
from django.utils.autoreload import WatchmanUnavailable from django.utils.autoreload import WatchmanUnavailable
from .test_module import __main__ as test_main
from .utils import on_macos_with_hfs from .utils import on_macos_with_hfs
@ -157,6 +158,7 @@ class TestIterModulesAndFiles(SimpleTestCase):
class TestChildArguments(SimpleTestCase): class TestChildArguments(SimpleTestCase):
@mock.patch.dict(sys.modules, {'__main__': django.__main__})
@mock.patch('sys.argv', [django.__main__.__file__, 'runserver']) @mock.patch('sys.argv', [django.__main__.__file__, 'runserver'])
@mock.patch('sys.warnoptions', []) @mock.patch('sys.warnoptions', [])
def test_run_as_module(self): def test_run_as_module(self):
@ -165,6 +167,15 @@ class TestChildArguments(SimpleTestCase):
[sys.executable, '-m', 'django', 'runserver'] [sys.executable, '-m', 'django', 'runserver']
) )
@mock.patch.dict(sys.modules, {'__main__': test_main})
@mock.patch('sys.argv', [test_main.__file__, 'runserver'])
@mock.patch('sys.warnoptions', [])
def test_run_as_non_django_module(self):
self.assertEqual(
autoreload.get_child_arguments(),
[sys.executable, '-m', 'utils_tests.test_module', 'runserver'],
)
@mock.patch('sys.argv', [__file__, 'runserver']) @mock.patch('sys.argv', [__file__, 'runserver'])
@mock.patch('sys.warnoptions', ['error']) @mock.patch('sys.warnoptions', ['error'])
def test_warnoptions(self): def test_warnoptions(self):
@ -447,7 +458,8 @@ class RestartWithReloaderTests(SimpleTestCase):
argv = [main, 'runserver'] argv = [main, 'runserver']
mock_call = self.patch_autoreload(argv) mock_call = self.patch_autoreload(argv)
with mock.patch('django.__main__.__file__', main): with mock.patch('django.__main__.__file__', main):
autoreload.restart_with_reloader() with mock.patch.dict(sys.modules, {'__main__': django.__main__}):
autoreload.restart_with_reloader()
self.assertEqual(mock_call.call_count, 1) self.assertEqual(mock_call.call_count, 1)
self.assertEqual(mock_call.call_args[0][0], [self.executable, '-Wall', '-m', 'django'] + argv[1:]) self.assertEqual(mock_call.call_args[0][0], [self.executable, '-Wall', '-m', 'django'] + argv[1:])