Commits

Anonymous committed 284eb9b

Merge from bitbucket.org/loewis/django-3k, 34d9d1d4a2ba:
Fix Python 3 compatibility issues.

  • Participants
  • Parent commits 437967f
  • Branches features/py3k

Comments (0)

Files changed (17)

File django/contrib/admin/sites.py

 from django.utils.safestring import mark_safe
 from django.utils.text import capfirst
 from django.utils.translation import ugettext as _
+from django.utils.py3 import dictvalues
 from django.views.decorators.cache import never_cache
 from django.conf import settings
 
                         }
 
         # Sort the apps alphabetically.
-        app_list = app_dict.values()
+        app_list = dictvalues(app_dict)
         app_list.sort(key=lambda x: x['name'])
 
         # Sort the models alphabetically within each app.

File django/contrib/admin/templatetags/log.py

         if tokens[4] != 'for_user':
             raise template.TemplateSyntaxError(
                 "Fourth argument to 'get_admin_log' must be 'for_user'")
-    return AdminLogNode(limit=tokens[1], varname=tokens[3], user=(len(tokens) > 5 and tokens[5] or None))
+    return AdminLogNode(limit=int(tokens[1]), varname=tokens[3], user=(len(tokens) > 5 and tokens[5] or None))

File django/contrib/sessions/backends/base.py

         encoded_data = base64.decodestring(session_data.encode('ascii'))
         try:
             # could produce ValueError if there is no ':'
-            hash, pickled = encoded_data.split(':', 1)
+            hash, pickled = encoded_data.split(b(':'), 1)
             expected_hash = self._hash(pickled)
             if not constant_time_compare(hash, expected_hash):
                 raise SuspiciousOperation("Session data corrupted")

File django/contrib/staticfiles/management/commands/collectstatic.py

                 'unmodified': (self.unmodified_files and ', %s unmodified'
                                % unmodified_count or ''),
             }
-            self.stdout.write(smart_str(summary))
+            if sys.version_info < (3,0):
+                summary = smart_str(summary)
+            self.stdout.write(summary)
 
     def log(self, msg, level=2):
         """
         Small log helper
         """
-        msg = smart_str(msg)
+        if sys.version_info < (3,0):
+            msg = smart_str(msg)
         if not msg.endswith("\n"):
             msg += "\n"
         if self.verbosity >= level:

File django/core/handlers/wsgi.py

 from django.utils import datastructures
 from django.utils.encoding import force_unicode, iri_to_uri
 from django.utils.log import getLogger
+from django.utils.py3 import b
 
 logger = getLogger('django.request')
 
     def __init__(self, stream, limit, buf_size=64 * 1024 * 1024):
         self.stream = stream
         self.remaining = limit
-        self.buffer = ''
+        self.buffer = b('')
         self.buf_size = buf_size
 
     def _read_limited(self, size=None):
         if size is None or size > self.remaining:
             size = self.remaining
         if size == 0:
-            return ''
+            return b('')
         result = self.stream.read(size)
         self.remaining -= len(result)
         return result
     def read(self, size=None):
         if size is None:
             result = self.buffer + self._read_limited()
-            self.buffer = ''
+            self.buffer = b('')
         elif size < len(self.buffer):
             result = self.buffer[:size]
             self.buffer = self.buffer[size:]
         else: # size >= len(self.buffer)
             result = self.buffer + self._read_limited(size - len(self.buffer))
-            self.buffer = ''
+            self.buffer = b('')
         return result
 
     def readline(self, size=None):

File django/core/management/sql.py

+import sys
 import os
 import re
 
                  os.path.join(app_dir, "%s.sql" % opts.object_name.lower())]
     for sql_file in sql_files:
         if os.path.exists(sql_file):
-            fp = open(sql_file, 'rbU')
-            for statement in statements.split(fp.read().decode(settings.FILE_CHARSET)):
+            if sys.version_info < (3,):
+                fp = open(sql_file, 'U')
+                sql_data = fp.read().decode(settings.FILE_CHARSET)
+            else:
+                fp = open(sql_file, 'r', encoding=settings.FILE_CHARSET)
+                sql_data = fp.read()
+            fp.close()
+            for statement in statements.split(sql_data):
                 # Remove any comments from the file
                 statement = re.sub(ur"--.*([\n\Z]|$)", "", statement)
                 if statement.strip():
                     output.append(statement + u";")
-            fp.close()
 
     return output
 

File django/core/urlresolvers.py

 from django.http import Http404
 from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
 from django.utils.datastructures import MultiValueDict
-from django.utils.encoding import iri_to_uri, force_unicode, smart_text, smart_str
+from django.utils.encoding import iri_to_uri, force_unicode, smart_text
 from django.utils.functional import memoize, lazy
 from django.utils.importlib import import_module
 from django.utils.module_loading import module_has_submodule
         self.default_args = default_args or {}
         self.name = name
 
-    if sys.version_info < (3,):
-        def __repr__(self):
-            return smart_str(u'<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern))
-    else:
-        def __repr__(self):
-            return u'<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern)
+    def __repr__(self):
+        return smart_text(u'<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern))
 
     def add_prefix(self, prefix):
         """
         self._app_dict = {}
 
     def __repr__(self):
-        return smart_str(u'<%s %s (%s:%s) %s>' % (self.__class__.__name__, self.urlconf_name, self.app_name, self.namespace, self.regex.pattern))
+        return smart_text(u'<%s %s (%s:%s) %s>' % (self.__class__.__name__, self.urlconf_name, self.app_name, self.namespace, self.regex.pattern))
 
     def _populate(self):
         lookups = MultiValueDict()

File django/forms/formsets.py

             total_forms = initial_forms + self.extra
             # Allow all existing related objects/inlines to be displayed,
             # but don't allow extra beyond max_num.
-            if initial_forms > self.max_num >= 0:
-                total_forms = initial_forms
-            elif total_forms > self.max_num >= 0:
-                total_forms = self.max_num
+            if self.max_num is not None:
+                if initial_forms > self.max_num >= 0:
+                    total_forms = initial_forms
+                elif total_forms > self.max_num >= 0:
+                    total_forms = self.max_num
         return total_forms
 
     def initial_form_count(self):
         else:
             # Use the length of the inital data if it's there, 0 otherwise.
             initial_forms = self.initial and len(self.initial) or 0
-            if initial_forms > self.max_num >= 0:
+            if self.max_num is not None and initial_forms > self.max_num >= 0:
                 initial_forms = self.max_num
         return initial_forms
 

File django/http/__init__.py

 from urllib import urlencode, quote
 from urlparse import urljoin
 try:
-    from cStringIO import StringIO
+    from io import BytesIO as StringIO
 except ImportError:
-    from StringIO import StringIO
+    try:
+        from cStringIO import StringIO
+    except ImportError:
+        from StringIO import StringIO
 try:
     # The mod_python version is more efficient, so try importing it first.
     from mod_python.util import parse_qsl
     except ImportError:
         # Python 2.5.  Works on Python 2.6 but raises PendingDeprecationWarning
         from cgi import parse_qsl
+from django.utils.py3 import b
 
 import Cookie
 # httponly support exists in Python 2.6's Cookie library,
                 except UnicodeError, e:
                     e.reason += ', HTTP response headers must be in US-ASCII format'
                     raise
+            elif sys.version_info >= (3,0) and isinstance(value, bytes):
+                # str(<bytes>) would result in "b'...'"
+                value = value.decode()
             else:
                 value = str(value)
             if '\n' in value or '\r' in value:
 
     def _get_content(self):
         if self.has_header('Content-Encoding'):
-            return ''.join([str(e) for e in self._container])
-        return ''.join([smart_str(e, self._charset) for e in self._container])
+            return b('').join([bytes(e) for e in self._container])
+        return b('').join([smart_str(e, self._charset) for e in self._container])
 
     def _set_content(self, value):
-        if hasattr(value, '__iter__'):
+        # in 3.x, bytes and unicode has __iter__, but they shouldn't be considered
+        # collections here
+        if hasattr(value, '__iter__') and not isinstance(value, (bytes, unicode)):
             self._container = value
             self._base_content_is_iter = True
         else:

File django/test/client.py

             'SERVER_PROTOCOL':   'HTTP/1.1',
             'wsgi.version':      (1,0),
             'wsgi.url_scheme':   'http',
-            'wsgi.input':        FakePayload(''),
+            'wsgi.input':        FakePayload(b('')),
             'wsgi.errors':       self.errors,
             'wsgi.multiprocess': True,
             'wsgi.multithread':  False,

File django/utils/cache.py

     # max-age, use the minimum of the two ages. In practice this happens when
     # a decorator and a piece of middleware both operate on a given view.
     if 'max-age' in cc and 'max_age' in kwargs:
-        kwargs['max_age'] = min(cc['max-age'], kwargs['max_age'])
+        kwargs['max_age'] = min(int(cc['max-age']), kwargs['max_age'])
 
     # Allow overriding private caching and vice versa
     if 'private' in cc and 'public' in kwargs:

File django/utils/crypto.py

 Django's standard crypto functions and utilities.
 """
 
+import sys
 import hashlib
 import hmac
 from django.conf import settings
     if len(val1) != len(val2):
         return False
     result = 0
-    for x, y in zip(val1, val2):
-        result |= ord(x) ^ ord(y)
+    if (sys.version_info >= (3,0)) and (type(val1) is type(val2) is bytes):
+        for x, y in zip(val1, val2):
+            result |= x ^ y
+    else:
+        for x, y in zip(val1, val2):
+            result |= ord(x) ^ ord(y)
     return result == 0

File django/utils/dictconfig.py

     def configure_custom(self, config):
         """Configure an object with a user-supplied factory."""
         c = config.pop('()')
-        if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
+        if not hasattr(c, '__call__') and (not hasattr(types, 'ClassType') or type(c) != types.ClassType):
             c = self.resolve(c)
         props = config.pop('.', None)
         # Check for valid identifiers
         filters = config.pop('filters', None)
         if '()' in config:
             c = config.pop('()')
-            if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
+            if not hasattr(c, '__call__') and (not hasattr(types, 'ClassType') or type(c) != types.ClassType):
                 c = self.resolve(c)
             factory = c
         else:

File django/utils/encoding.py

         return path
     # I know about `os.sep` and `os.altsep` but I want to leave
     # some flexibility for hardcoding separators.
-    return urllib.quote(smart_str(path).replace("\\", "/"), safe="/~!*()'")
+    return urllib.quote(smart_str(path).replace(b("\\"), b("/")),
+                        safe=b("/~!*()'"))
 
 # The encoding of the default system locale but falls back to the
 # given fallback encoding if the encoding is unsupported by python or could

File django/utils/text.py

         ellipsis (...).
         """
         length = int(num)
-        text = unicodedata.normalize('NFC', self._wrapped)
+        text = unicodedata.normalize('NFC', unicode(self))
 
         # Calculate the length to truncate to (max length - end_text length)
         truncate_len = length
 
         Newlines in the string will be stripped.
         """
-        words = self._wrapped.split()
+        words = self.split()
         if len(words) > length:
             words = words[:length]
             return self.add_truncation_text(u' '.join(words), truncate)
         words = 0
         open_tags = []
         while words <= length:
-            m = re_words.search(self._wrapped, pos)
+            m = re_words.search(unicode(self), pos)
             if not m:
                 # Checked through whole string
                 break
                 open_tags.insert(0, tagname)
         if words <= length:
             # Don't try to close tags if we don't need to truncate
-            return self._wrapped
-        out = self._wrapped[:end_text_pos]
+            return unicode(self)
+        out = unicode(self)[:end_text_pos]
         truncate_text = self.add_truncation_text('', truncate)
         if truncate_text:
             out += truncate_text

File django/utils/translation/trans_real.py

     ugettext = gettext
 
 def pgettext(context, message):
-    result = do_translate(
-        u"%s%s%s" % (context, CONTEXT_SEPARATOR, message), 'ugettext')
+    result = ugettext(
+        u"%s%s%s" % (context, CONTEXT_SEPARATOR, message))
     if CONTEXT_SEPARATOR in result:
         # Translation not found
         result = message

File django/utils/unittest/main.py

             installHandler()
         if self.testRunner is None:
             self.testRunner = runner.TextTestRunner
-        if isinstance(self.testRunner, (type, types.ClassType)):
+        if isinstance(self.testRunner, type) or hasattr(types, 'ClassType') and isinstance(self.testRunner, types.ClassType):
             try:
                 testRunner = self.testRunner(verbosity=self.verbosity,
                                              failfast=self.failfast,