So, I've been working on a patch (PFA) . As being discussed, lists are 
easier for beginners to follow, and tuples provide a false sense of 
security. With that in mind, I've simply changed pretty much all the tuples 
in global_settings and template_settings to lists, and warn about the usage 
of tuples at startup time. I'm going through the documentation and changing 
it in the necessary places if this thing goes through. Suggestions ?

On Wednesday, January 21, 2015 at 4:47:28 AM UTC+5:30, Tom Christie wrote:
>
> Hi Andreas,
>
>   I'm completely in agreement with you that *in theory* using tuples would 
> be a (very marginal) improvement. I also happen think that the idea that 
> tuples are semantically different from simply being  immutable lists is a 
> nonsense - regardless of what a particular section of documentation may or 
> may not say, language features are defined solely in terms of their 
> behavior, they do not have dreams, hopes and grand philosophical outlooks 
> on how they should be used.
>
>   However, the simple case that tuples have a far poorer syntax in python 
> than lists, and are *very* easy for beginners to get wrong is a good enough 
> reason to prefer the usage of lists consistently.
>
>   ("a string")
>
>   ("a tuple",)
>
> Beginners will not be following your "I notate every (globally available) 
> constant sequence in the pattern" advice, and no amount of documentation is 
> sufficient to prevent it being a very simple and easy error, that might be 
> difficult for a beginner to track down. I think that's a bigger and easier 
> trap than intentional assignment.
>
> > accidental assignment to the settings object itself could be easily 
> prevented with a __setattr__ method on its class
>
> I'd suggest treating that as a separate issue - perhaps if you or someone 
> else came up with a pull request that enforced immutability of the settings 
> object that'd be considered on it's own merits. (Note that you could 
> perfectly well also deal with making translating list and dict objects into 
> immutable objects at *that* point of API, even if they're not in the 
> settings module.) I haven't given that any great thought, so expect it 
> would have it's own set of obstacles to overcome, but I think it's a 
> different issue to the topic at hand here, which is really just about 
> settling on an acceptable and consistent style.
>
> > maybe a compromise would be to explicitly note in the docs
>
> I'd be against that as unnecessary fluff - doing one thing in the code and 
> recommending another in the docs just introduces noise and uncertainty.
>
> The topic's been discussed more that it really deserves, but I understand 
> that it can be frustrating if it feels like your reasoned arguments are 
> being brickwalled. I wanted to at least contribute and note that I do agree 
> with you in theory, even if practically I'd say that lists throughout is 
> consistent, clear, and slightly less likely for developers to get wrong.
>
> Cheers,
>
>   Tom
>
>
> On Tuesday, 20 January 2015 17:52:51 UTC, Andreas Kahnert wrote:
>>
>> Just for completness: accidential assignment to the settings object 
>> itself could be easily prevented with a __setattr__ method on its class, 
>> since django yields on various other places about configuration problems it 
>> could not be wrong if the programmer gets noted about an illegal 
>> assignment. If everything works fine the method will only get called during 
>> startup, so there is no runtime overhead. Simplified example:
>> def __setattr__(self, key, val):
>>     if self.configured:
>>         raise Exception('settings can not be changed after server 
>> startup')
>>     super(LazySettings, self).__setattr__(key, val)
>>
>> @Carl Meyer: At the first hand you're right, a thing all programmers 
>> should know about (if they call themself so), but he assumed there existed 
>> some kind of copy-on-read semantic for the settings, because you get 
>> something different when imported from django.conf instead directly and 
>> because it's a "magical" lazy object.
>>
>>
>> But since you all seem to like lists that much, maybe a compromise would 
>> be to explicitly note in the docs that there is a danger in using lists 
>> which can be prevented by tuple usage.
>>
>

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/0d993ad8-ea41-4bfe-ac82-2f43f366f22c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
commit 88574b2e72b37fa3821289f860cf3018ae140e90
Author: darkryder <sambhav13...@iiitd.ac.in>
Date:   Wed Jan 21 13:39:29 2015 +0530

    Changing tuples to lists.

diff --git a/django/conf/__init__.py b/django/conf/__init__.py
index 7ca099b..34a04e6 100644
--- a/django/conf/__init__.py
+++ b/django/conf/__init__.py
@@ -91,21 +91,26 @@ class Settings(BaseSettings):
 
         mod = importlib.import_module(self.SETTINGS_MODULE)
 
-        tuple_settings = (
+        # How about raising a warning at server initialisation time ?
+        deprecated_tuple_settings = [
             "ALLOWED_INCLUDE_ROOTS",
             "INSTALLED_APPS",
             "TEMPLATE_DIRS",
             "LOCALE_PATHS",
-        )
+        ]
         self._explicit_settings = set()
         for setting in dir(mod):
             if setting.isupper():
                 setting_value = getattr(mod, setting)
 
-                if (setting in tuple_settings and
-                        isinstance(setting_value, six.string_types)):
-                    raise ImproperlyConfigured("The %s setting must be a tuple. "
-                            "Please fix your settings." % setting)
+                if (setting in deprecated_tuple_settings and
+                        not any([isinstance(setting_value, list), setting_value is None])):
+                    warnings.warn(
+                        "The %s setting should be a list. "
+                        "This will be enforced in Django 2.0. " # or whatever you prefer
+                        "Please change it to be a list and not a tuple.",
+                        RemovedInDjango20Warning,
+                    )
                 setattr(self, setting, setting_value)
                 self._explicit_settings.add(setting)
 
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
index d6263b2..7f5f710 100644
--- a/django/conf/global_settings.py
+++ b/django/conf/global_settings.py
@@ -22,12 +22,12 @@ USE_ETAGS = False
 
 # People who get code error notifications.
 # In the format (('Full Name', 'em...@example.com'), ('Full Name', 'anotherem...@example.com'))
-ADMINS = ()
+ADMINS = []
 
 # Tuple of IP addresses, as strings, that:
 #   * See debug comments, when DEBUG is true
 #   * Receive x-headers
-INTERNAL_IPS = ()
+INTERNAL_IPS = []
 
 # Hosts/domain names that are valid for this site.
 # "*" matches anything, ".example.com" matches example.com and all subdomains
@@ -47,7 +47,7 @@ USE_TZ = False
 LANGUAGE_CODE = 'en-us'
 
 # Languages we provide translations for, out of the box.
-LANGUAGES = (
+LANGUAGES = [
     ('af', gettext_noop('Afrikaans')),
     ('ar', gettext_noop('Arabic')),
     ('ast', gettext_noop('Asturian')),
@@ -134,15 +134,15 @@ LANGUAGES = (
     ('zh-hans', gettext_noop('Simplified Chinese')),
     ('zh-hant', gettext_noop('Traditional Chinese')),
     ('zh-tw', gettext_noop('Traditional Chinese')),
-)
+]
 
 # Languages using BiDi (right-to-left) layout
-LANGUAGES_BIDI = ("he", "ar", "fa", "ur")
+LANGUAGES_BIDI = ["he", "ar", "fa", "ur"]
 
 # If you set this to False, Django will make some optimizations so as not
 # to load the internationalization machinery.
 USE_I18N = True
-LOCALE_PATHS = ()
+LOCALE_PATHS = []
 
 # Settings for language cookie
 LANGUAGE_COOKIE_NAME = 'django_language'
@@ -199,24 +199,24 @@ EMAIL_SSL_KEYFILE = None
 EMAIL_TIMEOUT = None
 
 # List of strings representing installed apps.
-INSTALLED_APPS = ()
+INSTALLED_APPS = []
 
 # List of locations of the template source files, in search order.
-TEMPLATE_DIRS = ()
+TEMPLATE_DIRS = []
 
 # List of callables that know how to import templates from various sources.
 # See the comments in django/core/template/loader.py for interface
 # documentation.
-TEMPLATE_LOADERS = (
+TEMPLATE_LOADERS = [
     'django.template.loaders.filesystem.Loader',
     'django.template.loaders.app_directories.Loader',
     # 'django.template.loaders.eggs.Loader',
-)
+]
 
 # List of processors used by RequestContext to populate the context.
 # Each one should be a callable that takes the request object as its
 # only parameter and returns a dictionary to add to the context.
-TEMPLATE_CONTEXT_PROCESSORS = (
+TEMPLATE_CONTEXT_PROCESSORS = [
     'django.contrib.auth.context_processors.auth',
     'django.template.context_processors.debug',
     'django.template.context_processors.i18n',
@@ -225,7 +225,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
     'django.template.context_processors.tz',
     # 'django.template.context_processors.request',
     'django.contrib.messages.context_processors.messages',
-)
+]
 
 # Output to use in template system for invalid (e.g. misspelled) variables.
 TEMPLATE_STRING_IF_INVALID = ''
@@ -259,13 +259,13 @@ FORCE_SCRIPT_NAME = None
 #         re.compile(r'^SiteSucker.*'),
 #         re.compile(r'^sohu-search')
 #     )
-DISALLOWED_USER_AGENTS = ()
+DISALLOWED_USER_AGENTS = []
 
 ABSOLUTE_URL_OVERRIDES = {}
 
-# Tuple of strings representing allowed prefixes for the {% ssi %} tag.
+# List of strings representing allowed prefixes for the {% ssi %} tag.
 # Example: ('/home/html', '/var/www')
-ALLOWED_INCLUDE_ROOTS = ()
+ALLOWED_INCLUDE_ROOTS = []
 
 # List of compiled regular expression objects representing URLs that need not
 # be reported by BrokenLinkEmailsMiddleware. Here are a few examples:
@@ -277,7 +277,7 @@ ALLOWED_INCLUDE_ROOTS = ()
 #        re.compile(r'^/phpmyadmin/),
 #        re.compile(r'\.(cgi|php|pl)$'),
 #    )
-IGNORABLE_404_URLS = ()
+IGNORABLE_404_URLS = []
 
 # A secret key for this particular Django installation. Used in secret-key
 # hashing algorithms. Set this in your settings, or Django will complain
@@ -304,10 +304,10 @@ STATIC_ROOT = None
 STATIC_URL = None
 
 # List of upload handler classes to be applied in order.
-FILE_UPLOAD_HANDLERS = (
+FILE_UPLOAD_HANDLERS = [
     'django.core.files.uploadhandler.MemoryFileUploadHandler',
     'django.core.files.uploadhandler.TemporaryFileUploadHandler',
-)
+]
 
 # Maximum size, in bytes, of a request before it will be streamed to the
 # file system instead of into memory.
@@ -368,30 +368,30 @@ SHORT_DATETIME_FORMAT = 'm/d/Y P'
 # See all available format string here:
 # http://docs.python.org/library/datetime.html#strftime-behavior
 # * Note that these format strings are different from the ones to display dates
-DATE_INPUT_FORMATS = (
+DATE_INPUT_FORMATS = [
     '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y',  # '2006-10-25', '10/25/2006', '10/25/06'
     '%b %d %Y', '%b %d, %Y',             # 'Oct 25 2006', 'Oct 25, 2006'
     '%d %b %Y', '%d %b, %Y',             # '25 Oct 2006', '25 Oct, 2006'
     '%B %d %Y', '%B %d, %Y',             # 'October 25 2006', 'October 25, 2006'
     '%d %B %Y', '%d %B, %Y',             # '25 October 2006', '25 October, 2006'
-)
+]
 
 # Default formats to be used when parsing times from input boxes, in order
 # See all available format string here:
 # http://docs.python.org/library/datetime.html#strftime-behavior
 # * Note that these format strings are different from the ones to display dates
-TIME_INPUT_FORMATS = (
+TIME_INPUT_FORMATS = [
     '%H:%M:%S',     # '14:30:59'
     '%H:%M:%S.%f',  # '14:30:59.000200'
     '%H:%M',        # '14:30'
-)
+]
 
 # Default formats to be used when parsing dates and times from input boxes,
 # in order
 # See all available format string here:
 # http://docs.python.org/library/datetime.html#strftime-behavior
 # * Note that these format strings are different from the ones to display dates
-DATETIME_INPUT_FORMATS = (
+DATETIME_INPUT_FORMATS = [
     '%Y-%m-%d %H:%M:%S',     # '2006-10-25 14:30:59'
     '%Y-%m-%d %H:%M:%S.%f',  # '2006-10-25 14:30:59.000200'
     '%Y-%m-%d %H:%M',        # '2006-10-25 14:30'
@@ -404,7 +404,7 @@ DATETIME_INPUT_FORMATS = (
     '%m/%d/%y %H:%M:%S.%f',  # '10/25/06 14:30:59.000200'
     '%m/%d/%y %H:%M',        # '10/25/06 14:30'
     '%m/%d/%y',              # '10/25/06'
-)
+]
 
 # First day of week, to be used on calendars
 # 0 means Sunday, 1 means Monday...
@@ -455,10 +455,10 @@ SECURE_PROXY_SSL_HEADER = None
 # List of middleware classes to use.  Order is important; in the request phase,
 # this middleware classes will be applied in the order given, and in the
 # response phase the middleware will be applied in reverse order.
-MIDDLEWARE_CLASSES = (
+MIDDLEWARE_CLASSES = [
     'django.middleware.common.CommonMiddleware',
     'django.middleware.csrf.CsrfViewMiddleware',
-)
+]
 
 ############
 # SESSIONS #
@@ -510,7 +510,7 @@ CACHE_MIDDLEWARE_ALIAS = 'default'
 
 AUTH_USER_MODEL = 'auth.User'
 
-AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',)
+AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend']
 
 LOGIN_URL = '/accounts/login/'
 
@@ -524,7 +524,7 @@ PASSWORD_RESET_TIMEOUT_DAYS = 3
 # the first hasher in this list is the preferred algorithm.  any
 # password using different algorithms will be converted automatically
 # upon login
-PASSWORD_HASHERS = (
+PASSWORD_HASHERS = [
     'django.contrib.auth.hashers.PBKDF2PasswordHasher',
     'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
     'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
@@ -534,7 +534,7 @@ PASSWORD_HASHERS = (
     'django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher',
     'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher',
     'django.contrib.auth.hashers.CryptPasswordHasher',
-)
+]
 
 ###########
 # SIGNING #
@@ -598,25 +598,25 @@ TEST_NON_SERIALIZED_APPS = []
 ############
 
 # The list of directories to search for fixtures
-FIXTURE_DIRS = ()
+FIXTURE_DIRS = []
 
 ###############
 # STATICFILES #
 ###############
 
 # A list of locations of additional static files
-STATICFILES_DIRS = ()
+STATICFILES_DIRS = []
 
 # The default file storage backend used during the build process
 STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
 
 # List of finder classes that know how to find static files in
 # various locations.
-STATICFILES_FINDERS = (
+STATICFILES_FINDERS = [
     'django.contrib.staticfiles.finders.FileSystemFinder',
     'django.contrib.staticfiles.finders.AppDirectoriesFinder',
     # 'django.contrib.staticfiles.finders.DefaultStorageFinder',
-)
+]
 
 ##############
 # MIGRATIONS #
diff --git a/django/conf/project_template/project_name/settings.py b/django/conf/project_template/project_name/settings.py
index a0a3056..9d4b38d 100644
--- a/django/conf/project_template/project_name/settings.py
+++ b/django/conf/project_template/project_name/settings.py
@@ -31,16 +31,16 @@ ALLOWED_HOSTS = []
 
 # Application definition
 
-INSTALLED_APPS = (
+INSTALLED_APPS = [
     'django.contrib.admin',
     'django.contrib.auth',
     'django.contrib.contenttypes',
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
-)
+]
 
-MIDDLEWARE_CLASSES = (
+MIDDLEWARE_CLASSES = [
     'django.contrib.sessions.middleware.SessionMiddleware',
     'django.middleware.common.CommonMiddleware',
     'django.middleware.csrf.CsrfViewMiddleware',
@@ -49,7 +49,7 @@ MIDDLEWARE_CLASSES = (
     'django.contrib.messages.middleware.MessageMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
     'django.middleware.security.SecurityMiddleware',
-)
+]
 
 ROOT_URLCONF = '{{ project_name }}.urls'
 
diff --git a/docs/howto/auth-remote-user.txt b/docs/howto/auth-remote-user.txt
index 07389c1..0407a37 100644
--- a/docs/howto/auth-remote-user.txt
+++ b/docs/howto/auth-remote-user.txt
@@ -31,12 +31,12 @@ First, you must add the
 :setting:`MIDDLEWARE_CLASSES` setting **after** the
 :class:`django.contrib.auth.middleware.AuthenticationMiddleware`::
 
-    MIDDLEWARE_CLASSES = (
+    MIDDLEWARE_CLASSES = [
         '...',
         'django.contrib.auth.middleware.AuthenticationMiddleware',
         'django.contrib.auth.middleware.RemoteUserMiddleware',
         '...',
-    )
+    ]
 
 Next, you must replace the :class:`~django.contrib.auth.backends.ModelBackend`
 with :class:`~django.contrib.auth.backends.RemoteUserBackend` in the
diff --git a/docs/howto/static-files/index.txt b/docs/howto/static-files/index.txt
index d05ae6a..4ccae8d 100644
--- a/docs/howto/static-files/index.txt
+++ b/docs/howto/static-files/index.txt
@@ -55,10 +55,10 @@ particular app. In addition to using a ``static/`` directory inside your apps,
 you can define a list of directories (:setting:`STATICFILES_DIRS`) in your
 settings file where Django will also look for static files. For example::
 
-    STATICFILES_DIRS = (
+    STATICFILES_DIRS = [
         os.path.join(BASE_DIR, "static"),
         '/var/www/static/',
-    )
+    ]
 
 See the documentation for the :setting:`STATICFILES_FINDERS` setting for
 details on how ``staticfiles`` finds your files.

Reply via email to