diff --git a/django/conf/__init__.py b/django/conf/__init__.py index 3337fd6f13..5731fe912c 100644 --- a/django/conf/__init__.py +++ b/django/conf/__init__.py @@ -9,23 +9,14 @@ for a list of all possible variables. import importlib import os import time -import traceback -import warnings from pathlib import Path -import django from django.conf import global_settings from django.core.exceptions import ImproperlyConfigured -from django.utils.deprecation import RemovedInDjango31Warning from django.utils.functional import LazyObject, empty ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE" -FILE_CHARSET_DEPRECATED_MSG = ( - 'The FILE_CHARSET setting is deprecated. Starting with Django 3.1, all ' - 'files read from disk must be UTF-8 encoded.' -) - class SettingsReference(str): """ @@ -114,20 +105,6 @@ class LazySettings(LazyObject): """Return True if the settings have already been configured.""" return self._wrapped is not empty - @property - def FILE_CHARSET(self): - stack = traceback.extract_stack() - # Show a warning if the setting is used outside of Django. - # Stack index: -1 this line, -2 the caller. - filename, _line_number, _function_name, _text = stack[-2] - if not filename.startswith(os.path.dirname(django.__file__)): - warnings.warn( - FILE_CHARSET_DEPRECATED_MSG, - RemovedInDjango31Warning, - stacklevel=2, - ) - return self.__getattr__('FILE_CHARSET') - class Settings: def __init__(self, settings_module): @@ -160,9 +137,6 @@ class Settings: if not self.SECRET_KEY: raise ImproperlyConfigured("The SECRET_KEY setting must not be empty.") - if self.is_overridden('FILE_CHARSET'): - warnings.warn(FILE_CHARSET_DEPRECATED_MSG, RemovedInDjango31Warning) - if hasattr(time, 'tzset') and self.TIME_ZONE: # When we can, attempt to validate the timezone. If we can't find # this file, no check happens and it's harmless. @@ -206,8 +180,6 @@ class UserSettingsHolder: def __setattr__(self, name, value): self._deleted.discard(name) - if name == 'FILE_CHARSET': - warnings.warn(FILE_CHARSET_DEPRECATED_MSG, RemovedInDjango31Warning) super().__setattr__(name, value) def __delattr__(self, name): diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 2274ea3f4f..398696530a 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -171,9 +171,6 @@ MANAGERS = ADMINS # manually specified. It's used to construct the Content-Type header. DEFAULT_CHARSET = 'utf-8' -# Encoding of files read from disk (template and initial SQL files). -FILE_CHARSET = 'utf-8' - # Email address that error messages come from. SERVER_EMAIL = 'root@localhost' diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py index 607c11b7d6..69af9ca297 100644 --- a/django/contrib/staticfiles/storage.py +++ b/django/contrib/staticfiles/storage.py @@ -285,7 +285,7 @@ class HashedFilesMixin: # ..to apply each replacement pattern to the content if name in adjustable_paths: old_hashed_name = hashed_name - content = original_file.read().decode(settings.FILE_CHARSET) + content = original_file.read().decode('utf-8') for extension, patterns in self._patterns.items(): if matches_patterns(path, (extension,)): for pattern, template in patterns: diff --git a/django/core/management/commands/makemessages.py b/django/core/management/commands/makemessages.py index 6c1e8248ce..69d7c41a2b 100644 --- a/django/core/management/commands/makemessages.py +++ b/django/core/management/commands/makemessages.py @@ -102,8 +102,7 @@ class BuildFile: if not self.is_templatized: return - encoding = settings.FILE_CHARSET if self.command.settings_available else 'utf-8' - with open(self.path, encoding=encoding) as fp: + with open(self.path, encoding='utf-8') as fp: src_data = fp.read() if self.domain == 'djangojs': diff --git a/django/template/backends/django.py b/django/template/backends/django.py index 91bfbcf0bb..80d11d3cdd 100644 --- a/django/template/backends/django.py +++ b/django/template/backends/django.py @@ -20,7 +20,7 @@ class DjangoTemplates(BaseEngine): options = params.pop('OPTIONS').copy() options.setdefault('autoescape', True) options.setdefault('debug', settings.DEBUG) - options.setdefault('file_charset', settings.FILE_CHARSET) + options.setdefault('file_charset', 'utf-8') libraries = options.get('libraries', {}) options['libraries'] = self.get_templatetag_libraries(libraries) super().__init__(params) diff --git a/django/template/backends/dummy.py b/django/template/backends/dummy.py index a59e10d178..6be05ca614 100644 --- a/django/template/backends/dummy.py +++ b/django/template/backends/dummy.py @@ -1,6 +1,5 @@ import string -from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.template import Origin, TemplateDoesNotExist from django.utils.html import conditional_escape @@ -28,7 +27,7 @@ class TemplateStrings(BaseEngine): tried = [] for template_file in self.iter_template_filenames(template_name): try: - with open(template_file, encoding=settings.FILE_CHARSET) as fp: + with open(template_file, encoding='utf-8') as fp: template_code = fp.read() except FileNotFoundError: tried.append(( diff --git a/django/test/signals.py b/django/test/signals.py index 31a5017602..68e6ad3fb6 100644 --- a/django/test/signals.py +++ b/django/test/signals.py @@ -86,7 +86,6 @@ def reset_template_engines(**kwargs): if kwargs['setting'] in { 'TEMPLATES', 'DEBUG', - 'FILE_CHARSET', 'INSTALLED_APPS', }: from django.template import engines diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 622baa8376..d0b66502ba 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -1414,21 +1414,6 @@ Default: ``None`` Specifies a timeout in seconds for blocking operations like the connection attempt. -.. setting:: FILE_CHARSET - -``FILE_CHARSET`` ----------------- - -Default: ``'utf-8'`` - -The character encoding used to decode any files read from disk. This includes -template files, static files, and translation catalogs. - -.. deprecated:: 2.2 - - This setting is deprecated. Starting with Django 3.1, files read from disk - must be UTF-8 encoded. - .. setting:: FILE_UPLOAD_HANDLERS ``FILE_UPLOAD_HANDLERS`` diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt index 8fb764115e..fdba88f035 100644 --- a/docs/releases/3.1.txt +++ b/docs/releases/3.1.txt @@ -232,3 +232,5 @@ to remove usage of these features. * ``django.contrib.postgres.fields.FloatRangeField`` and ``django.contrib.postgres.forms.FloatRangeField`` are removed. + +* The ``FILE_CHARSET`` setting is removed. diff --git a/tests/settings_tests/test_file_charset.py b/tests/settings_tests/test_file_charset.py deleted file mode 100644 index 1be96a26d2..0000000000 --- a/tests/settings_tests/test_file_charset.py +++ /dev/null @@ -1,40 +0,0 @@ -import sys -from types import ModuleType - -from django.conf import FILE_CHARSET_DEPRECATED_MSG, Settings, settings -from django.test import SimpleTestCase, ignore_warnings -from django.utils.deprecation import RemovedInDjango31Warning - - -class DeprecationTests(SimpleTestCase): - msg = FILE_CHARSET_DEPRECATED_MSG - - def test_override_settings_warning(self): - with self.assertRaisesMessage(RemovedInDjango31Warning, self.msg): - with self.settings(FILE_CHARSET='latin1'): - pass - - def test_settings_init_warning(self): - settings_module = ModuleType('fake_settings_module') - settings_module.FILE_CHARSET = 'latin1' - settings_module.SECRET_KEY = 'ABC' - sys.modules['fake_settings_module'] = settings_module - try: - with self.assertRaisesMessage(RemovedInDjango31Warning, self.msg): - Settings('fake_settings_module') - finally: - del sys.modules['fake_settings_module'] - - def test_access_warning(self): - with self.assertRaisesMessage(RemovedInDjango31Warning, self.msg): - settings.FILE_CHARSET - # Works a second time. - with self.assertRaisesMessage(RemovedInDjango31Warning, self.msg): - settings.FILE_CHARSET - - @ignore_warnings(category=RemovedInDjango31Warning) - def test_access(self): - with self.settings(FILE_CHARSET='latin1'): - self.assertEqual(settings.FILE_CHARSET, 'latin1') - # Works a second time. - self.assertEqual(settings.FILE_CHARSET, 'latin1')