Browse Source

Fixed #23433 -- Deprecated django-admin.py entry point in favor of django-admin.

Unify on the entry point created by setuptools entry_points feature.
Jon Dufresne 5 years ago
parent
commit
5708327c37

+ 16 - 0
django/bin/django-admin.py

@@ -1,5 +1,21 @@
 #!/usr/bin/env python
+# When the django-admin.py deprecation ends, remove this script.
+import warnings
+
 from django.core import management
 
+try:
+    from django.utils.deprecation import RemovedInDjango40Warning
+except ImportError:
+    raise ImportError(
+        'django-admin.py was deprecated in Django 3.1 and removed in Django '
+        '4.0. Please manually remove this script from your virtual environment '
+        'and use django-admin instead.'
+    )
+
 if __name__ == "__main__":
+    warnings.warn(
+        'django-admin.py is deprecated in favor of django-admin.',
+        RemovedInDjango40Warning,
+    )
     management.execute_from_command_line()

+ 3 - 9
docs/faq/troubleshooting.txt

@@ -14,15 +14,9 @@ Problems running ``django-admin``
 -----------------------------------
 
 :doc:`django-admin </ref/django-admin>` should be on your system path if you
-installed Django via ``pip``. If it's not on your path, you can find it in
-``site-packages/django/bin``, where ``site-packages`` is a directory within
-your Python installation. Consider symlinking to :doc:`django-admin
-</ref/django-admin>` from some place on your path, such as
-:file:`/usr/local/bin`.
-
-If ``django-admin`` doesn't work but ``django-admin.py`` does, you're probably
-using a version of Django that doesn't match the version of this documentation.
-``django-admin`` is new in Django 1.7.
+installed Django via ``pip``. If it's not in your path, ensure you have your
+virtual environment activated and you can try running the equivalent command
+``python -m django``.
 
 macOS permissions
 -----------------

+ 1 - 1
docs/internals/contributing/writing-documentation.txt

@@ -287,7 +287,7 @@ define some extra description units:
         :ticket:`12345`
 
 Django's documentation uses a custom ``console`` directive for documenting
-command-line examples involving ``django-admin.py``, ``manage.py``, ``python``,
+command-line examples involving ``django-admin``, ``manage.py``, ``python``,
 etc.). In the HTML documentation, it renders a two-tab UI, with one tab showing
 a Unix-style command prompt and a second tab showing a Windows prompt.
 

+ 2 - 0
docs/internals/deprecation.txt

@@ -42,6 +42,8 @@ details on these changes.
 * The ``django.db.models.query_utils.InvalidQuery`` exception class will be
   removed.
 
+* The ``django-admin.py`` entry point will be removed.
+
 See the :ref:`Django 3.1 release notes <deprecated-features-3.1>` for more
 details on these changes.
 

+ 2 - 8
docs/ref/django-admin.txt

@@ -11,14 +11,8 @@ does the same thing as ``django-admin`` but also sets the
 project's ``settings.py`` file.
 
 The ``django-admin`` script should be on your system path if you installed
-Django via ``pip``. If it's not on your path, you can find it in
-``site-packages/django/bin`` within your Python installation. Consider
-symlinking it from some place on your path, such as ``/usr/local/bin``.
-
-For Windows users, who do not have symlinking functionality available, you can
-copy ``django-admin.exe`` to a location on your existing path or edit the
-``PATH`` settings (under ``Settings - Control Panel - System - Advanced -
-Environment...``) to point to its installed location.
+Django via ``pip``. If it's not in your path, ensure you have your virtual
+environment activated.
 
 Generally, when working on a single Django project, it's easier to use
 ``manage.py`` than ``django-admin``. If you need to switch between multiple

+ 3 - 0
docs/releases/3.1.txt

@@ -384,6 +384,9 @@ Miscellaneous
   :class:`~django.core.exceptions.FieldDoesNotExist` and
   :class:`~django.core.exceptions.FieldError`.
 
+* The ``django-admin.py`` entry point is deprecated in favor of
+  ``django-admin``.
+
 .. _removed-features-3.1:
 
 Features removed in 3.1

+ 3 - 3
extras/django_bash_completion

@@ -1,6 +1,5 @@
-# #########################################################################
-# This bash script adds tab-completion feature to django-admin.py and
-# manage.py.
+# #############################################################################
+# This bash script adds tab-completion feature to django-admin and manage.py.
 #
 # Testing it out without installing
 # =================================
@@ -37,6 +36,7 @@ _django_completion()
                    COMP_CWORD=$COMP_CWORD \
                    DJANGO_AUTO_COMPLETE=1 $1 ) )
 }
+# When the django-admin.py deprecation ends, remove django-admin.py.
 complete -F _django_completion -o default django-admin.py manage.py django-admin
 
 _python_django_completion()

+ 1 - 0
setup.cfg

@@ -34,6 +34,7 @@ project_urls =
 [options]
 python_requires = >=3.6
 packages = find:
+# When the django-admin.py deprecation ends, remove "scripts".
 scripts = django/bin/django-admin.py
 include_package_data = true
 zip_safe = false

+ 37 - 0
tests/admin_scripts/test_django_admin_py.py

@@ -0,0 +1,37 @@
+import subprocess
+import sys
+from pathlib import Path
+
+import django
+from django.test import SimpleTestCase
+
+
+class DeprecationTests(SimpleTestCase):
+    DEPRECATION_MESSAGE = (
+        b'RemovedInDjango40Warning: django-admin.py is deprecated in favor of '
+        b'django-admin.'
+    )
+
+    def _run_test(self, args):
+        p = subprocess.run(
+            [sys.executable, *args],
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            check=True,
+        )
+        return p.stdout, p.stderr
+
+    def test_django_admin_py_deprecated(self):
+        django_admin_py = Path(django.__file__).parent / 'bin' / 'django-admin.py'
+        _, err = self._run_test(['-Wd', django_admin_py, '--version'])
+        self.assertIn(self.DEPRECATION_MESSAGE, err)
+
+    def test_main_not_deprecated(self):
+        _, err = self._run_test(['-Wd', '-m', 'django', '--version'])
+        self.assertNotIn(self.DEPRECATION_MESSAGE, err)
+
+    def test_django_admin_py_equivalent_main(self):
+        django_admin_py = Path(django.__file__).parent / 'bin' / 'django-admin.py'
+        django_admin_py_out, _ = self._run_test([django_admin_py, '--version'])
+        django_out, _ = self._run_test(['-m', 'django', '--version'])
+        self.assertEqual(django_admin_py_out, django_out)

+ 11 - 11
tests/admin_scripts/tests.py

@@ -14,7 +14,6 @@ import unittest
 from io import StringIO
 from unittest import mock
 
-import django
 from django import conf, get_version
 from django.conf import settings
 from django.core.management import (
@@ -125,8 +124,7 @@ class AdminScriptTestCase(SimpleTestCase):
         return p.stdout, p.stderr
 
     def run_django_admin(self, args, settings_file=None):
-        script_dir = os.path.abspath(os.path.join(os.path.dirname(django.__file__), 'bin'))
-        return self.run_test([os.path.join(script_dir, 'django-admin.py'), *args], settings_file)
+        return self.run_test(['-m', 'django', *args], settings_file)
 
     def run_manage(self, args, settings_file=None, manage_py=None):
         template_manage_py = (
@@ -1898,7 +1896,12 @@ class StartProject(LiveServerTestCase, AdminScriptTestCase):
         # running again..
         out, err = self.run_django_admin(args)
         self.assertNoOutput(out)
-        self.assertOutput(err, "already exists")
+        self.assertOutput(
+            err,
+            "CommandError: 'testproject' conflicts with the name of an "
+            "existing Python module and cannot be used as a project name. "
+            "Please try another name.",
+        )
 
     def test_invalid_project_name(self):
         "Make sure the startproject management command validates a project name"
@@ -2160,8 +2163,10 @@ class StartApp(AdminScriptTestCase):
         )
 
     def test_overlaying_app(self):
-        self.run_django_admin(['startapp', 'app1'])
-        out, err = self.run_django_admin(['startapp', 'app2', 'app1'])
+        # Use a subdirectory so it is outside the PYTHONPATH.
+        os.makedirs(os.path.join(self.test_dir, 'apps/app1'))
+        self.run_django_admin(['startapp', 'app1', 'apps/app1'])
+        out, err = self.run_django_admin(['startapp', 'app2', 'apps/app1'])
         self.assertOutput(
             err,
             "already exists. Overlaying an app into an existing directory "
@@ -2261,11 +2266,6 @@ class Dumpdata(AdminScriptTestCase):
 class MainModule(AdminScriptTestCase):
     """python -m django works like django-admin."""
 
-    def test_runs_django_admin(self):
-        cmd_out, _ = self.run_django_admin(['--version'])
-        mod_out, _ = self.run_test(['-m', 'django', '--version'])
-        self.assertEqual(mod_out, cmd_out)
-
     def test_program_name_in_help(self):
         out, err = self.run_test(['-m', 'django', 'help'])
         self.assertOutput(out, "Type 'python -m django help <subcommand>' for help on a specific subcommand.")