From abc0777b63057e2ff97eee2ff184356051e14c47 Mon Sep 17 00:00:00 2001 From: Chris Lamb Date: Sun, 24 Jan 2016 10:06:01 +0100 Subject: [PATCH] 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. --- .../{__init__.py => __init__.py-tpl} | 0 .../app_template/{admin.py => admin.py-tpl} | 0 .../app_template/{apps.py => apps.py-tpl} | 0 .../{__init__.py => __init__.py-tpl} | 0 .../app_template/{models.py => models.py-tpl} | 0 .../app_template/{tests.py => tests.py-tpl} | 0 .../app_template/{views.py => views.py-tpl} | 0 .../{manage.py => manage.py-tpl} | 0 .../{__init__.py => __init__.py-tpl} | 0 .../{settings.py => settings.py-tpl} | 0 .../project_name/{urls.py => urls.py-tpl} | 0 .../project_name/{wsgi.py => wsgi.py-tpl} | 0 django/core/management/templates.py | 12 +++++++++++- docs/ref/django-admin.txt | 9 +++++++++ docs/releases/1.9.2.txt | 19 ++++++++++++++++++- docs/releases/1.9.txt | 5 +++-- docs/topics/install.txt | 3 +-- .../{manage.py => manage.py-tpl} | 0 tests/admin_scripts/tests.py | 2 +- tests/project_template/test_settings.py | 15 +++++++++++++++ 20 files changed, 58 insertions(+), 7 deletions(-) rename django/conf/app_template/{__init__.py => __init__.py-tpl} (100%) rename django/conf/app_template/{admin.py => admin.py-tpl} (100%) rename django/conf/app_template/{apps.py => apps.py-tpl} (100%) rename django/conf/app_template/migrations/{__init__.py => __init__.py-tpl} (100%) rename django/conf/app_template/{models.py => models.py-tpl} (100%) rename django/conf/app_template/{tests.py => tests.py-tpl} (100%) rename django/conf/app_template/{views.py => views.py-tpl} (100%) rename django/conf/project_template/{manage.py => manage.py-tpl} (100%) rename django/conf/project_template/project_name/{__init__.py => __init__.py-tpl} (100%) rename django/conf/project_template/project_name/{settings.py => settings.py-tpl} (100%) rename django/conf/project_template/project_name/{urls.py => urls.py-tpl} (100%) rename django/conf/project_template/project_name/{wsgi.py => wsgi.py-tpl} (100%) rename tests/admin_scripts/custom_templates/project_template/{manage.py => manage.py-tpl} (100%) diff --git a/django/conf/app_template/__init__.py b/django/conf/app_template/__init__.py-tpl similarity index 100% rename from django/conf/app_template/__init__.py rename to django/conf/app_template/__init__.py-tpl diff --git a/django/conf/app_template/admin.py b/django/conf/app_template/admin.py-tpl similarity index 100% rename from django/conf/app_template/admin.py rename to django/conf/app_template/admin.py-tpl diff --git a/django/conf/app_template/apps.py b/django/conf/app_template/apps.py-tpl similarity index 100% rename from django/conf/app_template/apps.py rename to django/conf/app_template/apps.py-tpl diff --git a/django/conf/app_template/migrations/__init__.py b/django/conf/app_template/migrations/__init__.py-tpl similarity index 100% rename from django/conf/app_template/migrations/__init__.py rename to django/conf/app_template/migrations/__init__.py-tpl diff --git a/django/conf/app_template/models.py b/django/conf/app_template/models.py-tpl similarity index 100% rename from django/conf/app_template/models.py rename to django/conf/app_template/models.py-tpl diff --git a/django/conf/app_template/tests.py b/django/conf/app_template/tests.py-tpl similarity index 100% rename from django/conf/app_template/tests.py rename to django/conf/app_template/tests.py-tpl diff --git a/django/conf/app_template/views.py b/django/conf/app_template/views.py-tpl similarity index 100% rename from django/conf/app_template/views.py rename to django/conf/app_template/views.py-tpl diff --git a/django/conf/project_template/manage.py b/django/conf/project_template/manage.py-tpl similarity index 100% rename from django/conf/project_template/manage.py rename to django/conf/project_template/manage.py-tpl diff --git a/django/conf/project_template/project_name/__init__.py b/django/conf/project_template/project_name/__init__.py-tpl similarity index 100% rename from django/conf/project_template/project_name/__init__.py rename to django/conf/project_template/project_name/__init__.py-tpl diff --git a/django/conf/project_template/project_name/settings.py b/django/conf/project_template/project_name/settings.py-tpl similarity index 100% rename from django/conf/project_template/project_name/settings.py rename to django/conf/project_template/project_name/settings.py-tpl diff --git a/django/conf/project_template/project_name/urls.py b/django/conf/project_template/project_name/urls.py-tpl similarity index 100% rename from django/conf/project_template/project_name/urls.py rename to django/conf/project_template/project_name/urls.py-tpl diff --git a/django/conf/project_template/project_name/wsgi.py b/django/conf/project_template/project_name/wsgi.py-tpl similarity index 100% rename from django/conf/project_template/project_name/wsgi.py rename to django/conf/project_template/project_name/wsgi.py-tpl diff --git a/django/core/management/templates.py b/django/core/management/templates.py index 7c147a5231..1cd06d101a 100644 --- a/django/core/management/templates.py +++ b/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) diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index a0d9e54296..5bc31ea112 100644 --- a/docs/ref/django-admin.txt +++ b/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 diff --git a/docs/releases/1.9.2.txt b/docs/releases/1.9.2.txt index f977b02ac3..e64c62e3da 100644 --- a/docs/releases/1.9.2.txt +++ b/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 ======== diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt index dc43dce873..0c67558d69 100644 --- a/docs/releases/1.9.txt +++ b/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 ------------- diff --git a/docs/topics/install.txt b/docs/topics/install.txt index f0fbc47881..b5195bea57 100644 --- a/docs/topics/install.txt +++ b/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 ` also. + work. 2. Take a look at virtualenv_ and virtualenvwrapper_. These tools provide isolated Python environments, which are more practical than installing diff --git a/tests/admin_scripts/custom_templates/project_template/manage.py b/tests/admin_scripts/custom_templates/project_template/manage.py-tpl similarity index 100% rename from tests/admin_scripts/custom_templates/project_template/manage.py rename to tests/admin_scripts/custom_templates/project_template/manage.py-tpl diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index 805be8b144..60c3a68fd2 100644 --- a/tests/admin_scripts/tests.py +++ b/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) diff --git a/tests/project_template/test_settings.py b/tests/project_template/test_settings.py index ac115f7dc2..25a95717a9 100644 --- a/tests/project_template/test_settings.py +++ b/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): """