[Python-modules-commits] [python-django] 01/07: Import python-django_1.9.4.orig.tar.gz

Luke Faraone lfaraone at moszumanska.debian.org
Sat Mar 5 21:05:22 UTC 2016


This is an automated email from the git hooks/post-receive script.

lfaraone pushed a commit to branch debian/master
in repository python-django.

commit b849285b12d9ba5635c7c84b03652d81ae147dac
Author: Luke Faraone <lfaraone at debian.org>
Date:   Sat Mar 5 20:10:07 2016 +0000

    Import python-django_1.9.4.orig.tar.gz
---
 Django.egg-info/PKG-INFO                           |   2 +-
 Django.egg-info/SOURCES.txt                        |   5 +
 PKG-INFO                                           |   2 +-
 django/__init__.py                                 |   2 +-
 django/contrib/admin/templates/admin/login.html    |   2 +-
 django/contrib/auth/base_user.py                   |   2 +-
 django/contrib/auth/hashers.py                     |  77 +++++++++----
 django/contrib/contenttypes/models.py              |  20 ++--
 django/contrib/postgres/fields/array.py            |   7 +-
 django/contrib/postgres/fields/ranges.py           |   8 +-
 django/core/checks/urls.py                         |  10 +-
 django/core/validators.py                          |   9 +-
 django/db/backends/base/operations.py              |   2 +-
 django/db/backends/postgresql/base.py              |   2 +-
 django/db/migrations/state.py                      |   2 +-
 django/db/models/expressions.py                    |   2 +-
 django/db/models/fields/__init__.py                |   2 +-
 django/db/models/fields/related.py                 |  50 ++++----
 django/db/models/fields/related_descriptors.py     |   2 +-
 django/db/models/query.py                          |   8 +-
 django/db/models/query_utils.py                    |   2 +-
 django/db/models/sql/query.py                      |   2 +-
 django/forms/boundfield.py                         |   2 +-
 django/middleware/common.py                        |   2 +-
 django/template/loaders/cached.py                  |   2 +-
 django/template/response.py                        |   6 +-
 django/test/signals.py                             |   2 +-
 django/utils/http.py                               |  13 ++-
 django/utils/translation/__init__.py               |   7 ++
 docs/conf.py                                       |   9 ++
 docs/faq/general.txt                               |   2 +-
 docs/howto/custom-model-fields.txt                 |   3 +-
 docs/howto/deployment/checklist.txt                |   5 +-
 docs/howto/deployment/wsgi/apache-auth.txt         |   4 +-
 docs/howto/deployment/wsgi/modwsgi.txt             |   4 +-
 docs/howto/outputting-pdf.txt                      |   6 +-
 docs/internals/deprecation.txt                     |   3 +
 docs/internals/release-process.txt                 |   5 +-
 docs/intro/reusable-apps.txt                       |   6 +-
 docs/intro/tutorial02.txt                          |  13 ++-
 docs/intro/tutorial04.txt                          |   2 +-
 docs/ref/applications.txt                          |  12 +-
 docs/ref/class-based-views/generic-date-based.txt  |  32 ++++--
 docs/ref/contrib/admin/admindocs.txt               |   9 --
 docs/ref/contrib/admin/index.txt                   |  16 ++-
 docs/ref/contrib/gis/feeds.txt                     |   4 +-
 docs/ref/contrib/gis/geos.txt                      |   2 +-
 docs/ref/contrib/gis/testing.txt                   |   4 +-
 docs/ref/contrib/gis/tutorial.txt                  |   8 +-
 docs/ref/csrf.txt                                  |  61 ++++++++--
 docs/ref/models/expressions.txt                    |  37 +++++-
 docs/ref/models/fields.txt                         |  29 +++++
 docs/ref/models/meta.txt                           |   4 +-
 docs/ref/settings.txt                              |  75 +++++++-----
 docs/ref/templates/builtins.txt                    |   5 +-
 docs/ref/validators.txt                            |   6 +-
 docs/releases/1.6.txt                              |   4 +-
 docs/releases/1.8.10.txt                           |  80 +++++++++++++
 docs/releases/1.8.11.txt                           |   8 ++
 docs/releases/1.8.txt                              |   7 +-
 docs/releases/1.9.1.txt                            |   2 +-
 docs/releases/1.9.3.txt                            | 107 +++++++++++++++++
 docs/releases/1.9.4.txt                            |   8 ++
 docs/releases/index.txt                            |   4 +
 docs/releases/security.txt                         |  38 ++++++
 docs/spelling_wordlist                             |   2 +
 docs/topics/auth/customizing.txt                   | 128 ++++++---------------
 docs/topics/auth/default.txt                       |   2 +-
 docs/topics/auth/passwords.txt                     | 114 ++++++++++++++++++
 docs/topics/db/models.txt                          |  11 +-
 docs/topics/db/multi-db.txt                        |   4 +-
 docs/topics/email.txt                              |   2 +
 docs/topics/i18n/translation.txt                   |  24 +---
 docs/topics/security.txt                           |   3 +-
 docs/topics/settings.txt                           |  11 ++
 docs/topics/testing/tools.txt                      |  96 +++++++---------
 setup.cfg                                          |   2 +-
 tests/admin_filters/tests.py                       |  19 ++-
 tests/auth_tests/test_forms.py                     |  34 ++++++
 tests/auth_tests/test_handlers.py                  |   7 +-
 tests/auth_tests/test_hashers.py                   |  58 +++++++++-
 tests/backends/tests.py                            |   5 +
 tests/check_framework/test_urls.py                 |   7 ++
 tests/contenttypes_tests/test_models.py            |  14 ++-
 tests/foreign_object/models/article.py             |  13 +++
 tests/foreign_object/tests.py                      |  13 +++
 tests/forms_tests/tests/test_fields.py             |   3 +
 tests/forms_tests/tests/test_forms.py              |  14 +++
 tests/i18n/tests.py                                |   8 ++
 tests/invalid_models_tests/test_relative_fields.py |  36 +++++-
 tests/model_fields/tests.py                        |  66 ++++++-----
 tests/postgres_tests/test_array.py                 |   6 +-
 tests/postgres_tests/test_hstore.py                |   9 +-
 tests/postgres_tests/test_json.py                  |   6 +-
 tests/postgres_tests/test_ranges.py                |  11 ++
 tests/queries/tests.py                             |  12 ++
 tests/raw_query/models.py                          |   1 +
 tests/raw_query/tests.py                           |   6 +
 tests/requests/tests.py                            |   8 +-
 tests/template_tests/test_loaders.py               |  12 ++
 tests/template_tests/test_response_deprecations.py |  42 +++++++
 tests/utils_tests/test_http.py                     |  24 ++++
 tests/validators/invalid_urls.txt                  |   4 +-
 tests/validators/valid_urls.txt                    |   4 +
 104 files changed, 1277 insertions(+), 442 deletions(-)

diff --git a/Django.egg-info/PKG-INFO b/Django.egg-info/PKG-INFO
index 72647a0..265a382 100644
--- a/Django.egg-info/PKG-INFO
+++ b/Django.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: Django
-Version: 1.9.2
+Version: 1.9.4
 Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design.
 Home-page: http://www.djangoproject.com/
 Author: Django Software Foundation
diff --git a/Django.egg-info/SOURCES.txt b/Django.egg-info/SOURCES.txt
index 9fd152c..cedc787 100644
--- a/Django.egg-info/SOURCES.txt
+++ b/Django.egg-info/SOURCES.txt
@@ -3487,6 +3487,8 @@ docs/releases/1.7.8.txt
 docs/releases/1.7.9.txt
 docs/releases/1.7.txt
 docs/releases/1.8.1.txt
+docs/releases/1.8.10.txt
+docs/releases/1.8.11.txt
 docs/releases/1.8.2.txt
 docs/releases/1.8.3.txt
 docs/releases/1.8.4.txt
@@ -3498,6 +3500,8 @@ docs/releases/1.8.9.txt
 docs/releases/1.8.txt
 docs/releases/1.9.1.txt
 docs/releases/1.9.2.txt
+docs/releases/1.9.3.txt
+docs/releases/1.9.4.txt
 docs/releases/1.9.txt
 docs/releases/index.txt
 docs/releases/security.txt
@@ -4980,6 +4984,7 @@ tests/template_tests/test_logging.py
 tests/template_tests/test_nodelist.py
 tests/template_tests/test_parser.py
 tests/template_tests/test_response.py
+tests/template_tests/test_response_deprecations.py
 tests/template_tests/test_smartif.py
 tests/template_tests/test_unicode.py
 tests/template_tests/tests.py
diff --git a/PKG-INFO b/PKG-INFO
index 72647a0..265a382 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: Django
-Version: 1.9.2
+Version: 1.9.4
 Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design.
 Home-page: http://www.djangoproject.com/
 Author: Django Software Foundation
diff --git a/django/__init__.py b/django/__init__.py
index 3dbe18f..e0c468f 100644
--- a/django/__init__.py
+++ b/django/__init__.py
@@ -1,6 +1,6 @@
 from django.utils.version import get_version
 
-VERSION = (1, 9, 2, 'final', 0)
+VERSION = (1, 9, 4, 'final', 0)
 
 __version__ = get_version(VERSION)
 
diff --git a/django/contrib/admin/templates/admin/login.html b/django/contrib/admin/templates/admin/login.html
index 7a0d650..adb58d6 100644
--- a/django/contrib/admin/templates/admin/login.html
+++ b/django/contrib/admin/templates/admin/login.html
@@ -34,7 +34,7 @@
 
 {% if user.is_authenticated %}
 <p class="errornote">
-{% blocktrans with username=request.user.username trimmed %}
+{% blocktrans with username=request.user.get_username trimmed %}
     You are authenticated as {{ username }}, but are not authorized to
     access this page. Would you like to login to a different account?
 {% endblocktrans %}
diff --git a/django/contrib/auth/base_user.py b/django/contrib/auth/base_user.py
index 31fec4f..48ab950 100644
--- a/django/contrib/auth/base_user.py
+++ b/django/contrib/auth/base_user.py
@@ -19,7 +19,7 @@ class BaseUserManager(models.Manager):
     @classmethod
     def normalize_email(cls, email):
         """
-        Normalize the email address by lowercasing the domain part of the it.
+        Normalize the email address by lowercasing the domain part of it.
         """
         email = email or ''
         try:
diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py
index 93d4598..05ac849 100644
--- a/django/contrib/auth/hashers.py
+++ b/django/contrib/auth/hashers.py
@@ -4,6 +4,7 @@ import base64
 import binascii
 import hashlib
 import importlib
+import warnings
 from collections import OrderedDict
 
 from django.conf import settings
@@ -46,10 +47,17 @@ def check_password(password, encoded, setter=None, preferred='default'):
     preferred = get_hasher(preferred)
     hasher = identify_hasher(encoded)
 
-    must_update = hasher.algorithm != preferred.algorithm
-    if not must_update:
-        must_update = preferred.must_update(encoded)
+    hasher_changed = hasher.algorithm != preferred.algorithm
+    must_update = hasher_changed or preferred.must_update(encoded)
     is_correct = hasher.verify(password, encoded)
+
+    # If the hasher didn't change (we don't protect against enumeration if it
+    # does) and the password should get updated, try to close the timing gap
+    # between the work factor of the current encoded password and the default
+    # work factor.
+    if not is_correct and not hasher_changed and must_update:
+        hasher.harden_runtime(password, encoded)
+
     if setter and is_correct and must_update:
         setter(password)
     return is_correct
@@ -216,6 +224,19 @@ class BasePasswordHasher(object):
     def must_update(self, encoded):
         return False
 
+    def harden_runtime(self, password, encoded):
+        """
+        Bridge the runtime gap between the work factor supplied in `encoded`
+        and the work factor suggested by this hasher.
+
+        Taking PBKDF2 as an example, if `encoded` contains 20000 iterations and
+        `self.iterations` is 30000, this method should run password through
+        another 10000 iterations of PBKDF2. Similar approaches should exist
+        for any hasher that has a work factor. If not, this method should be
+        defined as a no-op to silence the warning.
+        """
+        warnings.warn('subclasses of BasePasswordHasher should provide a harden_runtime() method')
+
 
 class PBKDF2PasswordHasher(BasePasswordHasher):
     """
@@ -258,6 +279,12 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
         algorithm, iterations, salt, hash = encoded.split('$', 3)
         return int(iterations) != self.iterations
 
+    def harden_runtime(self, password, encoded):
+        algorithm, iterations, salt, hash = encoded.split('$', 3)
+        extra_iterations = self.iterations - int(iterations)
+        if extra_iterations > 0:
+            self.encode(password, salt, extra_iterations)
+
 
 class PBKDF2SHA1PasswordHasher(PBKDF2PasswordHasher):
     """
@@ -308,23 +335,8 @@ class BCryptSHA256PasswordHasher(BasePasswordHasher):
     def verify(self, password, encoded):
         algorithm, data = encoded.split('$', 1)
         assert algorithm == self.algorithm
-        bcrypt = self._load_library()
-
-        # Hash the password prior to using bcrypt to prevent password truncation
-        #   See: https://code.djangoproject.com/ticket/20138
-        if self.digest is not None:
-            # We use binascii.hexlify here because Python3 decided that a hex encoded
-            #   bytestring is somehow a unicode.
-            password = binascii.hexlify(self.digest(force_bytes(password)).digest())
-        else:
-            password = force_bytes(password)
-
-        # Ensure that our data is a bytestring
-        data = force_bytes(data)
-        # force_bytes() necessary for py-bcrypt compatibility
-        hashpw = force_bytes(bcrypt.hashpw(password, data))
-
-        return constant_time_compare(data, hashpw)
+        encoded_2 = self.encode(password, force_bytes(data))
+        return constant_time_compare(encoded, encoded_2)
 
     def safe_summary(self, encoded):
         algorithm, empty, algostr, work_factor, data = encoded.split('$', 4)
@@ -341,6 +353,16 @@ class BCryptSHA256PasswordHasher(BasePasswordHasher):
         algorithm, empty, algostr, rounds, data = encoded.split('$', 4)
         return int(rounds) != self.rounds
 
+    def harden_runtime(self, password, encoded):
+        _, data = encoded.split('$', 1)
+        salt = data[:29]  # Length of the salt in bcrypt.
+        rounds = data.split('$')[2]
+        # work factor is logarithmic, adding one doubles the load.
+        diff = 2**(self.rounds - int(rounds)) - 1
+        while diff > 0:
+            self.encode(password, force_bytes(salt))
+            diff -= 1
+
 
 class BCryptPasswordHasher(BCryptSHA256PasswordHasher):
     """
@@ -388,6 +410,9 @@ class SHA1PasswordHasher(BasePasswordHasher):
             (_('hash'), mask_hash(hash)),
         ])
 
+    def harden_runtime(self, password, encoded):
+        pass
+
 
 class MD5PasswordHasher(BasePasswordHasher):
     """
@@ -416,6 +441,9 @@ class MD5PasswordHasher(BasePasswordHasher):
             (_('hash'), mask_hash(hash)),
         ])
 
+    def harden_runtime(self, password, encoded):
+        pass
+
 
 class UnsaltedSHA1PasswordHasher(BasePasswordHasher):
     """
@@ -448,6 +476,9 @@ class UnsaltedSHA1PasswordHasher(BasePasswordHasher):
             (_('hash'), mask_hash(hash)),
         ])
 
+    def harden_runtime(self, password, encoded):
+        pass
+
 
 class UnsaltedMD5PasswordHasher(BasePasswordHasher):
     """
@@ -481,6 +512,9 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher):
             (_('hash'), mask_hash(encoded, show=3)),
         ])
 
+    def harden_runtime(self, password, encoded):
+        pass
+
 
 class CryptPasswordHasher(BasePasswordHasher):
     """
@@ -515,3 +549,6 @@ class CryptPasswordHasher(BasePasswordHasher):
             (_('salt'), salt),
             (_('hash'), mask_hash(data, show=3)),
         ])
+
+    def harden_runtime(self, password, encoded):
+        pass
diff --git a/django/contrib/contenttypes/models.py b/django/contrib/contenttypes/models.py
index 27a5388..8d459f6 100644
--- a/django/contrib/contenttypes/models.py
+++ b/django/contrib/contenttypes/models.py
@@ -13,13 +13,15 @@ from django.utils.translation import ugettext_lazy as _
 class ContentTypeManager(models.Manager):
     use_in_migrations = True
 
-    # Cache to avoid re-looking up ContentType objects all over the place.
-    # This cache is shared by all the get_for_* methods.
-    _cache = {}
+    def __init__(self, *args, **kwargs):
+        super(ContentTypeManager, self).__init__(*args, **kwargs)
+        # Cache shared by all the get_for_* methods to speed up
+        # ContentType retrieval.
+        self._cache = {}
 
     def get_by_natural_key(self, app_label, model):
         try:
-            ct = self.__class__._cache[self.db][(app_label, model)]
+            ct = self._cache[self.db][(app_label, model)]
         except KeyError:
             ct = self.get(app_label=app_label, model=model)
             self._add_to_cache(self.db, ct)
@@ -34,7 +36,7 @@ class ContentTypeManager(models.Manager):
 
     def _get_from_cache(self, opts):
         key = (opts.app_label, opts.model_name)
-        return self.__class__._cache[self.db][key]
+        return self._cache[self.db][key]
 
     def create(self, **kwargs):
         if 'name' in kwargs:
@@ -129,7 +131,7 @@ class ContentTypeManager(models.Manager):
         (though ContentTypes are obviously not created on-the-fly by get_by_id).
         """
         try:
-            ct = self.__class__._cache[self.db][id]
+            ct = self._cache[self.db][id]
         except KeyError:
             # This could raise a DoesNotExist; that's correct behavior and will
             # make sure that only correct ctypes get stored in the cache dict.
@@ -144,15 +146,15 @@ class ContentTypeManager(models.Manager):
         django.contrib.contenttypes.management.update_contenttypes for where
         this gets called).
         """
-        self.__class__._cache.clear()
+        self._cache.clear()
 
     def _add_to_cache(self, using, ct):
         """Insert a ContentType into the cache."""
         # Note it's possible for ContentType objects to be stale; model_class() will return None.
         # Hence, there is no reliance on model._meta.app_label here, just using the model fields instead.
         key = (ct.app_label, ct.model)
-        self.__class__._cache.setdefault(using, {})[key] = ct
-        self.__class__._cache.setdefault(using, {})[ct.id] = ct
+        self._cache.setdefault(using, {})[key] = ct
+        self._cache.setdefault(using, {})[ct.id] = ct
 
 
 @python_2_unicode_compatible
diff --git a/django/contrib/postgres/fields/array.py b/django/contrib/postgres/fields/array.py
index 65f2867..28e592c 100644
--- a/django/contrib/postgres/fields/array.py
+++ b/django/contrib/postgres/fields/array.py
@@ -107,8 +107,11 @@ class ArrayField(Field):
         base_field = self.base_field
 
         for val in vals:
-            obj = AttributeSetter(base_field.attname, val)
-            values.append(base_field.value_to_string(obj))
+            if val is None:
+                values.append(None)
+            else:
+                obj = AttributeSetter(base_field.attname, val)
+                values.append(base_field.value_to_string(obj))
         return json.dumps(values)
 
     def get_transform(self, name):
diff --git a/django/contrib/postgres/fields/ranges.py b/django/contrib/postgres/fields/ranges.py
index bf3f8cb..5bce074 100644
--- a/django/contrib/postgres/fields/ranges.py
+++ b/django/contrib/postgres/fields/ranges.py
@@ -51,8 +51,12 @@ class RangeField(models.Field):
         base_field = self.base_field
         result = {"bounds": value._bounds}
         for end in ('lower', 'upper'):
-            obj = AttributeSetter(base_field.attname, getattr(value, end))
-            result[end] = base_field.value_to_string(obj)
+            val = getattr(value, end)
+            if val is None:
+                result[end] = None
+            else:
+                obj = AttributeSetter(base_field.attname, val)
+                result[end] = base_field.value_to_string(obj)
         return json.dumps(result)
 
     def formfield(self, **kwargs):
diff --git a/django/core/checks/urls.py b/django/core/checks/urls.py
index d2aac61..387d2b4 100644
--- a/django/core/checks/urls.py
+++ b/django/core/checks/urls.py
@@ -1,13 +1,17 @@
 from __future__ import unicode_literals
 
+from django.conf import settings
+
 from . import Tags, Warning, register
 
 
 @register(Tags.urls)
 def check_url_config(app_configs, **kwargs):
-    from django.core.urlresolvers import get_resolver
-    resolver = get_resolver()
-    return check_resolver(resolver)
+    if getattr(settings, 'ROOT_URLCONF', None):
+        from django.core.urlresolvers import get_resolver
+        resolver = get_resolver()
+        return check_resolver(resolver)
+    return []
 
 
 def check_resolver(resolver):
diff --git a/django/core/validators.py b/django/core/validators.py
index 69cc76f..852ff49 100644
--- a/django/core/validators.py
+++ b/django/core/validators.py
@@ -85,7 +85,14 @@ class URLValidator(RegexValidator):
     # Host patterns
     hostname_re = r'[a-z' + ul + r'0-9](?:[a-z' + ul + r'0-9-]*[a-z' + ul + r'0-9])?'
     domain_re = r'(?:\.(?!-)[a-z' + ul + r'0-9-]+(?<!-))*'
-    tld_re = r'\.(?:[a-z' + ul + r']{2,}|xn--[a-z0-9]+)\.?'
+    tld_re = (
+        '\.'                                # dot
+        '(?!-)'                             # can't start with a dash
+        '(?:[a-z' + ul + '-]{2,}'           # domain label
+        '|xn--[a-z0-9]+)'                   # or punycode label
+        '(?<!-)'                            # can't end with a dash
+        '\.?'                               # may have a trailing dot
+    )
     host_re = '(' + hostname_re + domain_re + tld_re + '|localhost)'
 
     regex = _lazy_re_compile(
diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py
index 6e5aff1..acca127 100644
--- a/django/db/backends/base/operations.py
+++ b/django/db/backends/base/operations.py
@@ -485,7 +485,7 @@ class BaseDatabaseOperations(object):
             raise ValueError("Django does not support timezone-aware times.")
         return six.text_type(value)
 
-    def adapt_decimalfield_value(self, value, max_digits, decimal_places):
+    def adapt_decimalfield_value(self, value, max_digits=None, decimal_places=None):
         """
         Transforms a decimal.Decimal value to an object compatible with what is
         expected by the backend driver for decimal (numeric) columns.
diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py
index 383601e..6a53648 100644
--- a/django/db/backends/postgresql/base.py
+++ b/django/db/backends/postgresql/base.py
@@ -196,7 +196,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
 
         conn_timezone_name = self.connection.get_parameter_status('TimeZone')
 
-        if conn_timezone_name != self.timezone_name:
+        if self.timezone_name and conn_timezone_name != self.timezone_name:
             cursor = self.connection.cursor()
             try:
                 cursor.execute(self.ops.set_time_zone_sql(), [self.timezone_name])
diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py
index 2365e39..117f320 100644
--- a/django/db/migrations/state.py
+++ b/django/db/migrations/state.py
@@ -245,7 +245,7 @@ class StateApps(Apps):
         """
         def extract_field(operation):
             # operation is annotated with the field in
-            # apps.register.Apps.lazy_model_operation().
+            # apps.registry.Apps.lazy_model_operation().
             return getattr(operation, 'field', None)
 
         def extract_field_names(operations):
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py
index f03a273..b558668 100644
--- a/django/db/models/expressions.py
+++ b/django/db/models/expressions.py
@@ -210,7 +210,7 @@ class BaseExpression(object):
         ])
         return c
 
-    def _prepare(self):
+    def _prepare(self, field):
         """
         Hook used by Field.get_prep_lookup() to do custom preparation.
         """
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 9449904..df2e3b4 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -732,7 +732,7 @@ class Field(RegisterLookupMixin):
         Perform preliminary non-db specific lookup checks and conversions
         """
         if hasattr(value, '_prepare'):
-            return value._prepare()
+            return value._prepare(self)
 
         if lookup_type in {
             'iexact', 'contains', 'icontains',
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index ff820ae..5d9b881 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -8,6 +8,7 @@ from django.apps import apps
 from django.core import checks, exceptions
 from django.db import connection, router
 from django.db.backends import utils
+from django.db.models import Q
 from django.db.models.deletion import CASCADE, SET_DEFAULT, SET_NULL
 from django.db.models.query_utils import PathInfo
 from django.db.models.utils import make_model_tuple
@@ -39,7 +40,7 @@ from .reverse_related import (
 RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
 
 
-def resolve_relation(scope_model, relation, resolve_recursive_relationship=True):
+def resolve_relation(scope_model, relation):
     """
     Transform relation into a model or fully-qualified model string of the form
     "app_label.ModelName", relative to scope_model.
@@ -54,11 +55,12 @@ def resolve_relation(scope_model, relation, resolve_recursive_relationship=True)
     """
     # Check for recursive relations
     if relation == RECURSIVE_RELATIONSHIP_CONSTANT:
-        if resolve_recursive_relationship:
-            relation = scope_model
+        relation = scope_model
+
     # Look for an "app.Model" relation
-    elif isinstance(relation, six.string_types) and '.' not in relation:
-        relation = "%s.%s" % (scope_model._meta.app_label, relation)
+    if isinstance(relation, six.string_types):
+        if "." not in relation:
+            relation = "%s.%s" % (scope_model._meta.app_label, relation)
 
     return relation
 
@@ -196,12 +198,6 @@ class RelatedField(Field):
         if not isinstance(self.remote_field.model, ModelBase):
             return []
 
-        # If the field doesn't install backward relation on the target model (so
-        # `is_hidden` returns True), then there are no clashes to check and we
-        # can skip these fields.
-        if self.remote_field.is_hidden():
-            return []
-
         # Consider that we are checking field `Model.foreign` and the models
         # are:
         #
@@ -213,12 +209,15 @@ class RelatedField(Field):
         #         foreign = models.ForeignKey(Target)
         #         m2m = models.ManyToManyField(Target)
 
-        rel_opts = self.remote_field.model._meta
         # rel_opts.object_name == "Target"
+        rel_opts = self.remote_field.model._meta
+        # If the field doesn't install a backward relation on the target model
+        # (so `is_hidden` returns True), then there are no clashes to check
+        # and we can skip these fields.
+        rel_is_hidden = self.remote_field.is_hidden()
         rel_name = self.remote_field.get_accessor_name()  # i. e. "model_set"
         rel_query_name = self.related_query_name()  # i. e. "model"
-        field_name = "%s.%s" % (opts.object_name,
-            self.name)  # i. e. "Model.field"
+        field_name = "%s.%s" % (opts.object_name, self.name)  # i. e. "Model.field"
 
         # Check clashes between accessor or reverse query name of `field`
         # and any other field name -- i.e. accessor for Model.foreign is
@@ -227,7 +226,7 @@ class RelatedField(Field):
         for clash_field in potential_clashes:
             clash_name = "%s.%s" % (rel_opts.object_name,
                 clash_field.name)  # i. e. "Target.model_set"
-            if clash_field.name == rel_name:
+            if not rel_is_hidden and clash_field.name == rel_name:
                 errors.append(
                     checks.Error(
                         "Reverse accessor for '%s' clashes with field name '%s'." % (field_name, clash_name),
@@ -257,7 +256,7 @@ class RelatedField(Field):
             clash_name = "%s.%s" % (  # i. e. "Model.m2m"
                 clash_field.related_model._meta.object_name,
                 clash_field.field.name)
-            if clash_field.get_accessor_name() == rel_name:
+            if not rel_is_hidden and clash_field.get_accessor_name() == rel_name:
                 errors.append(
                     checks.Error(
                         "Reverse accessor for '%s' clashes with reverse accessor for '%s'." % (field_name, clash_name),
@@ -305,11 +304,6 @@ class RelatedField(Field):
                 field.remote_field.model = related
                 field.do_related_class(related, model)
             lazy_related_operation(resolve_related_class, cls, self.remote_field.model, field=self)
-        else:
-            # Bind a lazy reference to the app in which the model is defined.
-            self.remote_field.model = resolve_relation(
-                cls, self.remote_field.model, resolve_recursive_relationship=False
-            )
 
     def get_forward_related_filter(self, obj):
         """
@@ -335,8 +329,13 @@ class RelatedField(Field):
             rh_field.attname: getattr(obj, lh_field.attname)
             for lh_field, rh_field in self.related_fields
         }
-        base_filter.update(self.get_extra_descriptor_filter(obj) or {})
-        return base_filter
+        descriptor_filter = self.get_extra_descriptor_filter(obj)
+        base_q = Q(**base_filter)
+        if isinstance(descriptor_filter, dict):
+            return base_q & Q(**descriptor_filter)
+        elif descriptor_filter:
+            return base_q & descriptor_filter
+        return base_q
 
     @property
     def swappable_setting(self):
@@ -1581,11 +1580,6 @@ class ManyToManyField(RelatedField):
                 lazy_related_operation(resolve_through_model, cls, self.remote_field.through, field=self)
             elif not cls._meta.swapped:
                 self.remote_field.through = create_many_to_many_intermediary_model(self, cls)
-        else:
-            # Bind a lazy reference to the app in which the model is defined.
-            self.remote_field.through = resolve_relation(
-                cls, self.remote_field.through, resolve_recursive_relationship=False
-            )
 
         # Add the descriptor for the m2m relation.
         setattr(cls, self.name, ManyToManyDescriptor(self.remote_field, reverse=False))
diff --git a/django/db/models/fields/related_descriptors.py b/django/db/models/fields/related_descriptors.py
index e9caa26..abfa728 100644
--- a/django/db/models/fields/related_descriptors.py
+++ b/django/db/models/fields/related_descriptors.py
@@ -164,7 +164,7 @@ class ForwardManyToOneDescriptor(object):
                 rel_obj = None
             else:
                 qs = self.get_queryset(instance=instance)
-                qs = qs.filter(**self.field.get_reverse_related_filter(instance))
+                qs = qs.filter(self.field.get_reverse_related_filter(instance))
                 # Assuming the database enforces foreign keys, this won't fail.
                 rel_obj = qs.get()
                 # If this is a one-to-one relation, set the reverse accessor
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 852a5ba..cb02085 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -1107,12 +1107,18 @@ class QuerySet(object):
         for field, objects in other._known_related_objects.items():
             self._known_related_objects.setdefault(field, {}).update(objects)
 
-    def _prepare(self):
+    def _prepare(self, field):
         if self._fields is not None:
             # values() queryset can only be used as nested queries
             # if they are set up to select only a single field.
             if len(self._fields or self.model._meta.concrete_fields) > 1:
                 raise TypeError('Cannot use multi-field values as a filter value.')
+        else:
+            # If the query is used as a subquery for a ForeignKey with non-pk
+            # target field, make sure to select the target field in the subquery.
+            foreign_fields = getattr(field, 'foreign_related_fields', ())
+            if len(foreign_fields) == 1 and not foreign_fields[0].primary_key:
+                return self.values(foreign_fields[0].name)
         return self
 
     def _as_sql(self, connection):
diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
index b25b408..195e8f4 100644
--- a/django/db/models/query_utils.py
+++ b/django/db/models/query_utils.py
@@ -45,7 +45,7 @@ class QueryWrapper(object):
 class Q(tree.Node):
     """
     Encapsulates filters as objects that can then be combined logically (using
-    & and |).
+    `&` and `|`).
     """
     # Connection types
     AND = 'AND'
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 2b446b9..29fe885 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -244,7 +244,7 @@ class Query(object):
         memo[id(self)] = result
         return result
 
-    def _prepare(self):
+    def _prepare(self, field):
         return self
 
     def get_compiler(self, using=None, connection=None):
diff --git a/django/forms/boundfield.py b/django/forms/boundfield.py
index bcfe2e2..6567c4f 100644
--- a/django/forms/boundfield.py
+++ b/django/forms/boundfield.py
@@ -60,7 +60,7 @@ class BoundField(object):
     def __getitem__(self, idx):
         # Prevent unnecessary reevaluation when accessing BoundField's attrs
         # from templates.
-        if not isinstance(idx, six.integer_types):
+        if not isinstance(idx, six.integer_types + (slice,)):
             raise TypeError
         return list(self.__iter__())[idx]
 
diff --git a/django/middleware/common.py b/django/middleware/common.py
index a8958a8..50acf32 100644
--- a/django/middleware/common.py
+++ b/django/middleware/common.py
@@ -85,7 +85,7 @@ class CommonMiddleware(object):
         Return the full path of the request with a trailing slash appended.
 
         Raise a RuntimeError if settings.DEBUG is True and request.method is
-        GET, PUT, or PATCH.
+        POST, PUT, or PATCH.
         """
         new_path = request.get_full_path(force_append_slash=True)
         if settings.DEBUG and request.method in ('POST', 'PUT', 'PATCH'):
diff --git a/django/template/loaders/cached.py b/django/template/loaders/cached.py
index c13346b..5bf5c10 100644
--- a/django/template/loaders/cached.py
+++ b/django/template/loaders/cached.py
@@ -131,7 +131,7 @@ class Loader(BaseLoader):
         template_tuple = self.template_cache.get(key)
         # A cached previous failure:
         if template_tuple is TemplateDoesNotExist:
-            raise TemplateDoesNotExist
+            raise TemplateDoesNotExist(template_name)
         elif template_tuple is None:
             template, origin = self.find_template(template_name, template_dirs)
             if not hasattr(template, 'render'):
diff --git a/django/template/response.py b/django/template/response.py
index 17b6066..a30d982 100644
--- a/django/template/response.py
+++ b/django/template/response.py
@@ -4,7 +4,7 @@ from django.http import HttpResponse
 from django.utils import six
 from django.utils.deprecation import RemovedInDjango110Warning
 
-from .backends.django import Template as BackendTemplate
+from .backends.django import DjangoTemplates, Template as BackendTemplate
 from .base import Template
 from .context import Context, RequestContext, _current_app_undefined
 from .loader import get_template, select_template
@@ -25,7 +25,7 @@ class SimpleTemplateResponse(HttpResponse):
                 "anymore. It may be a backend-specific template like those "
                 "created by get_template().".format(self.__class__.__name__),
                 RemovedInDjango110Warning, stacklevel=2)
-            template = BackendTemplate(template)
+            template = BackendTemplate(template, DjangoTemplates)
 
         # It would seem obvious to call these next two members 'template' and
         # 'context', but those names are reserved as part of the test Client
@@ -95,7 +95,7 @@ class SimpleTemplateResponse(HttpResponse):
                 "{}.".format(
                     self.__class__.__name__, new_template.__class__.__name__),
                 RemovedInDjango110Warning, stacklevel=2)
-            new_template = BackendTemplate(new_template)
+            new_template = BackendTemplate(new_template, DjangoTemplates)
         return new_template
 
     def resolve_context(self, context):
diff --git a/django/test/signals.py b/django/test/signals.py
index f783663..bb04af6 100644
--- a/django/test/signals.py
+++ b/django/test/signals.py
@@ -69,7 +69,7 @@ def update_connections_time_zone(**kwargs):
             except AttributeError:
                 pass
             tz_sql = conn.ops.set_time_zone_sql()
-            if tz_sql:
+            if tz_sql and conn.timezone_name:
                 with conn.cursor() as cursor:
                     cursor.execute(tz_sql, [conn.timezone_name])
 
diff --git a/django/utils/http.py b/django/utils/http.py
index 70bcbd9..62e854d 100644
--- a/django/utils/http.py
+++ b/django/utils/http.py
@@ -290,8 +290,17 @@ def is_safe_url(url, host=None):
         url = url.strip()
     if not url:
         return False
-    # Chrome treats \ completely as /
-    url = url.replace('\\', '/')
+    if six.PY2:
+        try:
+            url = force_text(url)
+        except UnicodeDecodeError:
+            return False
+    # Chrome treats \ completely as / in paths but it could be part of some
+    # basic auth credentials so we need to check both URLs.
+    return _is_safe_url(url, host) and _is_safe_url(url.replace('\\', '/'), host)
+
+
+def _is_safe_url(url, host):
     # Chrome considers any URL with more than two slashes to be absolute, but
     # urlparse is not so flexible. Treat any url with three slashes as unsafe.
     if url.startswith('///'):
diff --git a/django/utils/translation/__init__.py b/django/utils/translation/__init__.py
index 7311a61..9b66493 100644
--- a/django/utils/translation/__init__.py
+++ b/django/utils/translation/__init__.py
@@ -106,6 +106,8 @@ def lazy_number(func, resultclass, number=None, **kwargs):
         kwargs['number'] = number
         proxy = lazy(func, resultclass)(**kwargs)
     else:
+        original_kwargs = kwargs.copy()
+
         class NumberAwareString(resultclass):
             def __mod__(self, rhs):
                 if isinstance(rhs, dict) and number:
@@ -128,9 +130,14 @@ def lazy_number(func, resultclass, number=None, **kwargs):
                 return translated
 
         proxy = lazy(lambda **kwargs: NumberAwareString(), NumberAwareString)(**kwargs)
+        proxy.__reduce__ = lambda: (_lazy_number_unpickle, (func, resultclass, number, original_kwargs))
     return proxy
 
 
+def _lazy_number_unpickle(func, resultclass, number, kwargs):
+    return lazy_number(func, resultclass, number=number, **kwargs)
+
+
 def ngettext_lazy(singular, plural, number=None):
     return lazy_number(ngettext, str, singular=singular, plural=plural, number=number)
 
diff --git a/docs/conf.py b/docs/conf.py
index b23f62d..b146392 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -16,6 +16,15 @@ from __future__ import unicode_literals
 import sys
 from os.path import abspath, dirname, join
 
+# Workaround for sphinx-build recursion limit overflow:
+# pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL)
+#  RuntimeError: maximum recursion depth exceeded while pickling an object
+#
+# Python's default allowed recursion depth is 1000 but this isn't enough for
+# building docs/ref/settings.txt sometimes.
+# https://groups.google.com/d/topic/sphinx-dev/MtRf64eGtv4/discussion
+sys.setrecursionlimit(2000)
+
 # Make sure we get the version of this copy of Django
 sys.path.insert(1, dirname(dirname(abspath(__file__))))
 
diff --git a/docs/faq/general.txt b/docs/faq/general.txt
index cb31e8b..22d37fe 100644
--- a/docs/faq/general.txt
+++ b/docs/faq/general.txt
@@ -32,7 +32,7 @@ thrilled to be able to give something back to the open-source community.
 What does "Django" mean, and how do you pronounce it?
 =====================================================
 
-Django is named after `Django Reinhardt`_, a gypsy jazz guitarist from the 1930s
+Django is named after `Django Reinhardt`_, a jazz manouche guitarist from the 1930s
 to early 1950s. To this day, he's considered one of the best guitarists of all time.
 
 Listen to his music. You'll like it.
diff --git a/docs/howto/custom-model-fields.txt b/docs/howto/custom-model-fields.txt
index 375b4c1..3485c8a 100644
--- a/docs/howto/custom-model-fields.txt
+++ b/docs/howto/custom-model-fields.txt
@@ -464,6 +464,7 @@ instances::
 
     from django.core.exceptions import ValidationError
     from django.db import models
+    from django.utils.translation import ugettext_lazy as _
 
     def parse_hand(hand_string):
         """Takes a string of cards and splits into a full hand."""
@@ -471,7 +472,7 @@ instances::
         p2 = re.compile('..')
         args = [p2.findall(x) for x in p1.findall(hand_string)]
         if len(args) != 4:
-            raise ValidationError("Invalid input for a Hand instance")
+            raise ValidationError(_("Invalid input for a Hand instance"))
         return Hand(*args)
 
     class HandField(models.Field):
diff --git a/docs/howto/deployment/checklist.txt b/docs/howto/deployment/checklist.txt
index 5971719..5effdb7 100644
--- a/docs/howto/deployment/checklist.txt
+++ b/docs/howto/deployment/checklist.txt
@@ -254,8 +254,9 @@ Python Options
 ==============
 
 It's strongly recommended that you invoke the Python process running your
-Django application using the `-R`_ option or with the
-:envvar:`PYTHONHASHSEED` environment variable set to ``random``.
+Django application using the `-R`_ option or with the :envvar:`PYTHONHASHSEED`
+environment variable set to ``random``. This option is enabled by default
+starting with Python 3.3.
 
 These options help protect your site from denial-of-service (DoS)
 attacks triggered by carefully crafted inputs. Such an attack can
diff --git a/docs/howto/deployment/wsgi/apache-auth.txt b/docs/howto/deployment/wsgi/apache-auth.txt
index 9246c08..e6fea98 100644
--- a/docs/howto/deployment/wsgi/apache-auth.txt
+++ b/docs/howto/deployment/wsgi/apache-auth.txt
@@ -97,8 +97,8 @@ Requests beginning with ``/secret/`` will now require a user to authenticate.
 The mod_wsgi `access control mechanisms documentation`_ provides additional
 details and information about alternative methods of authentication.
 
-.. _Defining Application Groups: https://code.google.com/p/modwsgi/wiki/ConfigurationGuidelines#Defining_Application_Groups
-.. _access control mechanisms documentation: https://code.google.com/p/modwsgi/wiki/AccessControlMechanisms
+.. _Defining Application Groups: https://modwsgi.readthedocs.org/en/develop/user-guides/configuration-guidelines.html#defining-application-groups
+.. _access control mechanisms documentation: https://modwsgi.readthedocs.org/en/develop/user-guides/access-control-mechanisms.html
 
 Authorization with ``mod_wsgi`` and Django groups
 -------------------------------------------------
diff --git a/docs/howto/deployment/wsgi/modwsgi.txt b/docs/howto/deployment/wsgi/modwsgi.txt
index a3d5e8a..988ff8a 100644
--- a/docs/howto/deployment/wsgi/modwsgi.txt
+++ b/docs/howto/deployment/wsgi/modwsgi.txt
@@ -140,7 +140,7 @@ to the configuration above:
 See the official mod_wsgi documentation for `details on setting up daemon
 mode`_.
 
-.. _details on setting up daemon mode: https://code.google.com/p/modwsgi/wiki/QuickConfigurationGuide#Delegation_To_Daemon_Process
+.. _details on setting up daemon mode: https://modwsgi.readthedocs.org/en/develop/user-guides/quick-configuration-guide.html#delegation-to-daemon-process
 
 .. _serving-files:
 
@@ -198,7 +198,7 @@ If you are using a version of Apache older than 2.4, replace
 .. More details on configuring a mod_wsgi site to serve static files can be found
 .. in the mod_wsgi documentation on `hosting static files`_.
 
-.. _hosting static files: https://code.google.com/p/modwsgi/wiki/ConfigurationGuidelines#Hosting_Of_Static_Files
+.. _hosting static files: https://modwsgi.readthedocs.org/en/develop/user-guides/configuration-guidelines.html#hosting-of-static-files
 
 .. _serving-the-admin-files:
... 2701 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-django.git



More information about the Python-modules-commits mailing list