From ec6d2531c59466924b645f314ac33f54470d7ac3 Mon Sep 17 00:00:00 2001
From: William Schwartz <wkschwartz@gmail.com>
Date: Mon, 4 Jan 2021 08:50:49 -0600
Subject: [PATCH] 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.
---
 django/utils/autoreload.py                | 10 +++++-----
 tests/utils_tests/test_autoreload.py      | 14 +++++++++++++-
 tests/utils_tests/test_module/__main__.py |  0
 3 files changed, 18 insertions(+), 6 deletions(-)
 create mode 100644 tests/utils_tests/test_module/__main__.py

diff --git a/django/utils/autoreload.py b/django/utils/autoreload.py
index b8efb9f881..faa3252c71 100644
--- a/django/utils/autoreload.py
+++ b/django/utils/autoreload.py
@@ -216,14 +216,14 @@ def get_child_arguments():
     executable is reported to not have the .exe extension which can cause bugs
     on reloading.
     """
-    import django.__main__
-    django_main_path = Path(django.__main__.__file__)
+    import __main__
     py_script = Path(sys.argv[0])
 
     args = [sys.executable] + ['-W%s' % o for o in sys.warnoptions]
-    if py_script == django_main_path:
-        # The server was started with `python -m django runserver`.
-        args += ['-m', 'django']
+    # __spec__ is set when the server was started with the `-m` option,
+    # see https://docs.python.org/3/reference/import.html#main-spec
+    if __main__.__spec__ is not None and __main__.__spec__.parent:
+        args += ['-m', __main__.__spec__.parent]
         args += sys.argv[1:]
     elif not py_script.exists():
         # sys.argv[0] may not exist for several reasons on Windows.
diff --git a/tests/utils_tests/test_autoreload.py b/tests/utils_tests/test_autoreload.py
index b9f2db7276..a43d7c2fde 100644
--- a/tests/utils_tests/test_autoreload.py
+++ b/tests/utils_tests/test_autoreload.py
@@ -23,6 +23,7 @@ from django.test.utils import extend_sys_path
 from django.utils import autoreload
 from django.utils.autoreload import WatchmanUnavailable
 
+from .test_module import __main__ as test_main
 from .utils import on_macos_with_hfs
 
 
@@ -157,6 +158,7 @@ class TestIterModulesAndFiles(SimpleTestCase):
 
 
 class TestChildArguments(SimpleTestCase):
+    @mock.patch.dict(sys.modules, {'__main__': django.__main__})
     @mock.patch('sys.argv', [django.__main__.__file__, 'runserver'])
     @mock.patch('sys.warnoptions', [])
     def test_run_as_module(self):
@@ -165,6 +167,15 @@ class TestChildArguments(SimpleTestCase):
             [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.warnoptions', ['error'])
     def test_warnoptions(self):
@@ -447,7 +458,8 @@ class RestartWithReloaderTests(SimpleTestCase):
         argv = [main, 'runserver']
         mock_call = self.patch_autoreload(argv)
         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_args[0][0], [self.executable, '-Wall', '-m', 'django'] + argv[1:])
 
diff --git a/tests/utils_tests/test_module/__main__.py b/tests/utils_tests/test_module/__main__.py
new file mode 100644
index 0000000000..e69de29bb2