Переглянути джерело

Fixed #25968 -- Changed project/app templates to use a "py-tpl" suffix.

Debian packages unconditionally byte-compile .py files on installation and
do not silence errors by design. Therefore, we need a way of shipping these
invalid .py files without a .py extension but ensuring that when we
template them, they end up as .py.

We don't special-case .py files so that the all the TemplateCommand
command-line options (eg. extra_files and extensions) still work entirely
as expected and it may even be useful for other formats too.
Chris Lamb 9 роки тому
батько
коміт
abc0777b63

+ 0 - 0
django/conf/app_template/__init__.py → django/conf/app_template/__init__.py-tpl


+ 0 - 0
django/conf/app_template/admin.py → django/conf/app_template/admin.py-tpl


+ 0 - 0
django/conf/app_template/apps.py → django/conf/app_template/apps.py-tpl


+ 0 - 0
django/conf/app_template/migrations/__init__.py → django/conf/app_template/migrations/__init__.py-tpl


+ 0 - 0
django/conf/app_template/models.py → django/conf/app_template/models.py-tpl


+ 0 - 0
django/conf/app_template/tests.py → django/conf/app_template/tests.py-tpl


+ 0 - 0
django/conf/app_template/views.py → django/conf/app_template/views.py-tpl


+ 0 - 0
django/conf/project_template/manage.py → django/conf/project_template/manage.py-tpl


+ 0 - 0
django/conf/project_template/project_name/__init__.py → django/conf/project_template/project_name/__init__.py-tpl


+ 0 - 0
django/conf/project_template/project_name/settings.py → django/conf/project_template/project_name/settings.py-tpl


+ 0 - 0
django/conf/project_template/project_name/urls.py → django/conf/project_template/project_name/urls.py-tpl


+ 0 - 0
django/conf/project_template/project_name/wsgi.py → django/conf/project_template/project_name/wsgi.py-tpl


+ 11 - 1
django/core/management/templates.py

@@ -42,6 +42,11 @@ class TemplateCommand(BaseCommand):
     # Can't perform any active locale changes during this command, because
     # setting might not be available at all.
     leave_locale_alone = True
+    # Rewrite the following suffixes when determining the target filename.
+    rewrite_template_suffixes = (
+        # Allow shipping invalid .py files without byte-compilation.
+        ('.py-tpl', '.py'),
+    )
 
     def add_arguments(self, parser):
         parser.add_argument('name', help='Name of the application or project.')
@@ -139,6 +144,11 @@ class TemplateCommand(BaseCommand):
                 old_path = path.join(root, filename)
                 new_path = path.join(top_dir, relative_dir,
                                      filename.replace(base_name, name))
+                for old_suffix, new_suffix in self.rewrite_template_suffixes:
+                    if new_path.endswith(old_suffix):
+                        new_path = new_path[:-len(old_suffix)] + new_suffix
+                        break  # Only rewrite once
+
                 if path.exists(new_path):
                     raise CommandError("%s already exists, overlaying a "
                                        "project or app into an existing "
@@ -149,7 +159,7 @@ class TemplateCommand(BaseCommand):
                 # accidentally render Django templates files
                 with open(old_path, 'rb') as template_file:
                     content = template_file.read()
-                if filename.endswith(extensions) or filename in extra_files:
+                if new_path.endswith(extensions) or filename in extra_files:
                     content = content.decode('utf-8')
                     template = Engine().from_string(content)
                     content = template.render(context)

+ 9 - 0
docs/ref/django-admin.txt

@@ -1175,6 +1175,15 @@ files is:
     To work around this problem, you can use the :ttag:`templatetag`
     templatetag to "escape" the various parts of the template syntax.
 
+    In addition, to allow Python template files that contain Django template
+    language syntax while also preventing packaging systems from trying to
+    byte-compile invalid ``*.py`` files, template files ending with ``.py-tpl``
+    will be renamed to ``.py``.
+
+    .. versionchanged:: 1.9.2
+
+        Renaming of ``.py-tpl`` to ``.py`` was added.
+
 .. _source: https://github.com/django/django/tree/master/django/conf/app_template/
 
 startproject

+ 18 - 1
docs/releases/1.9.2.txt

@@ -4,7 +4,24 @@ Django 1.9.2 release notes
 
 *Under development*
 
-Django 1.9.2 fixes several bugs in 1.9.1.
+Django 1.9.2 fixes several bugs in 1.9.1 and makes a small backwards
+incompatible change that hopefully doesn't affect any users.
+
+Backwards incompatible change: ``.py-tpl`` files rewritten in project/app templates
+===================================================================================
+
+The addition of some Django template language syntax to the default app
+template in Django 1.9 means those files now have some invalid Python syntax.
+This causes difficulties for packaging systems that unconditionally
+byte-compile ``*.py`` files.
+
+To remedy this, a ``.py-tpl`` suffix is now used for the project and app
+template files included in Django. The ``.py-tpl`` suffix is replaced with
+``.py`` by the ``startproject`` and ``startapp`` commands. For example, a
+template with the filename ``manage.py-tpl`` will be created as ``manage.py``.
+
+Please file a ticket if you have a custom project template containing
+``.py-tpl`` files and find this behavior problematic.
 
 Bugfixes
 ========

+ 3 - 2
docs/releases/1.9.txt

@@ -1017,7 +1017,7 @@ a Django application with this structure::
 ``SyntaxError`` when installing Django setuptools 5.5.x
 -------------------------------------------------------
 
-When installing Django 1.9+ with setuptools 5.5.x, you'll see::
+When installing Django 1.9 or 1.9.1 with setuptools 5.5.x, you'll see::
 
     Compiling django/conf/app_template/apps.py ...
       File "django/conf/app_template/apps.py", line 4
@@ -1034,7 +1034,8 @@ When installing Django 1.9+ with setuptools 5.5.x, you'll see::
 It's safe to ignore these errors (Django will still install just fine), but you
 can avoid them by upgrading setuptools to a more recent version. If you're
 using pip, you can upgrade pip using ``pip install -U pip`` which will also
-upgrade setuptools.
+upgrade setuptools. This is resolved in later versions of Django as described
+in the :doc:`/releases/1.9.2`.
 
 Miscellaneous
 -------------

+ 1 - 2
docs/topics/install.txt

@@ -165,8 +165,7 @@ This is the recommended way to install Django.
 1. Install pip_. The easiest is to use the `standalone pip installer`_. If your
    distribution already has ``pip`` installed, you might need to update it if
    it's outdated. If it's outdated, you'll know because installation won't
-   work. If you're using an old version of setuptools, you might see some
-   :ref:`harmless SyntaxErrors <syntax-error-old-setuptools-django-19>` also.
+   work.
 
 2. Take a look at virtualenv_ and virtualenvwrapper_. These tools provide
    isolated Python environments, which are more practical than installing

+ 0 - 0
tests/admin_scripts/custom_templates/project_template/manage.py → tests/admin_scripts/custom_templates/project_template/manage.py-tpl


+ 1 - 1
tests/admin_scripts/tests.py

@@ -181,7 +181,7 @@ class AdminScriptTestCase(unittest.TestCase):
                 pass
 
         conf_dir = os.path.dirname(upath(conf.__file__))
-        template_manage_py = os.path.join(conf_dir, 'project_template', 'manage.py')
+        template_manage_py = os.path.join(conf_dir, 'project_template', 'manage.py-tpl')
 
         test_manage_py = os.path.join(self.test_dir, 'manage.py')
         shutil.copyfile(template_manage_py, test_manage_py)

+ 15 - 0
tests/project_template/test_settings.py

@@ -1,13 +1,28 @@
+import os
+import shutil
 import unittest
 
+from django import conf
 from django.test import TestCase
 from django.utils import six
+from django.utils._os import upath
 
 
 @unittest.skipIf(six.PY2,
     'Python 2 cannot import the project template because '
     'django/conf/project_template doesn\'t have an __init__.py file.')
 class TestStartProjectSettings(TestCase):
+    def setUp(self):
+        # Ensure settings.py exists
+        project_dir = os.path.join(
+            os.path.dirname(upath(conf.__file__)),
+            'project_template',
+            'project_name',
+        )
+        template_settings_py = os.path.join(project_dir, 'settings.py-tpl')
+        test_settings_py = os.path.join(project_dir, 'settings.py')
+        shutil.copyfile(template_settings_py, test_settings_py)
+        self.addCleanup(os.remove, test_settings_py)
 
     def test_middleware_classes_headers(self):
         """