summaryrefslogtreecommitdiff
path: root/django/utils
diff options
context:
space:
mode:
authorAndrew Godwin <andrew@aeracode.org>2012-08-10 12:40:37 +0100
committerAndrew Godwin <andrew@aeracode.org>2012-08-10 12:40:37 +0100
commit184cf9ab798d5b25d855649ddb2ca580949778df (patch)
tree8512633ec04a6979b0953e32e73c9a43d09e5805 /django/utils
parentc4b2a3262cc79383d6562cfc7e9af20135c8e0bf (diff)
parent7275576235ae2e87f3de7b0facb3f9b0a2368f28 (diff)
Merge branch 'master' into schema-alteration
Diffstat (limited to 'django/utils')
-rw-r--r--django/utils/_os.py20
-rw-r--r--django/utils/archive.py7
-rw-r--r--django/utils/cache.py4
-rw-r--r--django/utils/crypto.py17
-rw-r--r--django/utils/datastructures.py2
-rw-r--r--django/utils/dateformat.py6
-rw-r--r--django/utils/dateparse.py7
-rw-r--r--django/utils/dictconfig.py2
-rw-r--r--django/utils/encoding.py68
-rw-r--r--django/utils/feedgenerator.py37
-rw-r--r--django/utils/functional.py64
-rw-r--r--django/utils/html.py26
-rw-r--r--django/utils/http.py24
-rw-r--r--django/utils/regex_helper.py11
-rw-r--r--django/utils/safestring.py4
-rw-r--r--django/utils/six.py1
-rw-r--r--django/utils/termcolors.py4
-rw-r--r--django/utils/text.py24
-rw-r--r--django/utils/translation/__init__.py4
-rw-r--r--django/utils/translation/trans_null.py6
-rw-r--r--django/utils/translation/trans_real.py41
-rw-r--r--django/utils/tree.py3
-rw-r--r--django/utils/tzinfo.py4
23 files changed, 222 insertions, 164 deletions
diff --git a/django/utils/_os.py b/django/utils/_os.py
index 884689fefc..9eb5e5e8ea 100644
--- a/django/utils/_os.py
+++ b/django/utils/_os.py
@@ -1,7 +1,8 @@
import os
import stat
from os.path import join, normcase, normpath, abspath, isabs, sep
-from django.utils.encoding import force_unicode
+from django.utils.encoding import force_text
+from django.utils import six
try:
WindowsError = WindowsError
@@ -10,13 +11,12 @@ except NameError:
pass
-# Define our own abspath function that can handle joining
-# unicode paths to a current working directory that has non-ASCII
-# characters in it. This isn't necessary on Windows since the
-# Windows version of abspath handles this correctly. The Windows
-# abspath also handles drive letters differently than the pure
-# Python implementation, so it's best not to replace it.
-if os.name == 'nt':
+# Under Python 2, define our own abspath function that can handle joining
+# unicode paths to a current working directory that has non-ASCII characters
+# in it. This isn't necessary on Windows since the Windows version of abspath
+# handles this correctly. It also handles drive letters differently than the
+# pure Python implementation, so it's best not to replace it.
+if six.PY3 or os.name == 'nt':
abspathu = abspath
else:
def abspathu(path):
@@ -37,8 +37,8 @@ def safe_join(base, *paths):
The final path must be located inside of the base path component (otherwise
a ValueError is raised).
"""
- base = force_unicode(base)
- paths = [force_unicode(p) for p in paths]
+ base = force_text(base)
+ paths = [force_text(p) for p in paths]
final_path = abspathu(join(base, *paths))
base_path = abspathu(base)
base_path_len = len(base_path)
diff --git a/django/utils/archive.py b/django/utils/archive.py
index 6b5d73290f..829b55dd28 100644
--- a/django/utils/archive.py
+++ b/django/utils/archive.py
@@ -23,7 +23,6 @@ THE SOFTWARE.
"""
import os
import shutil
-import sys
import tarfile
import zipfile
@@ -147,11 +146,11 @@ class TarArchive(BaseArchive):
else:
try:
extracted = self._archive.extractfile(member)
- except (KeyError, AttributeError):
+ except (KeyError, AttributeError) as exc:
# Some corrupt tar files seem to produce this
# (specifically bad symlinks)
- print ("In the tar file %s the member %s is invalid: %s" %
- (name, member.name, sys.exc_info()[1]))
+ print("In the tar file %s the member %s is invalid: %s" %
+ (name, member.name, exc))
else:
dirname = os.path.dirname(filename)
if dirname and not os.path.exists(dirname):
diff --git a/django/utils/cache.py b/django/utils/cache.py
index 8b81a2ffa2..42b0de4ce6 100644
--- a/django/utils/cache.py
+++ b/django/utils/cache.py
@@ -23,7 +23,7 @@ import time
from django.conf import settings
from django.core.cache import get_cache
-from django.utils.encoding import iri_to_uri, force_unicode
+from django.utils.encoding import iri_to_uri, force_text
from django.utils.http import http_date
from django.utils.timezone import get_current_timezone_name
from django.utils.translation import get_language
@@ -169,7 +169,7 @@ def _i18n_cache_key_suffix(request, cache_key):
# Windows is known to use non-standard, locale-dependant names.
# User-defined tzinfo classes may return absolutely anything.
# Hence this paranoid conversion to create a valid cache key.
- tz_name = force_unicode(get_current_timezone_name(), errors='ignore')
+ tz_name = force_text(get_current_timezone_name(), errors='ignore')
cache_key += '.%s' % tz_name.encode('ascii', 'ignore').replace(' ', '_')
return cache_key
diff --git a/django/utils/crypto.py b/django/utils/crypto.py
index 9d46bdd793..70a07e7fde 100644
--- a/django/utils/crypto.py
+++ b/django/utils/crypto.py
@@ -23,7 +23,7 @@ except NotImplementedError:
using_sysrandom = False
from django.conf import settings
-from django.utils.encoding import smart_str
+from django.utils.encoding import smart_bytes
from django.utils.six.moves import xrange
@@ -50,7 +50,7 @@ def salted_hmac(key_salt, value, secret=None):
# line is redundant and could be replaced by key = key_salt + secret, since
# the hmac module does the same thing for keys longer than the block size.
# However, we need to ensure that we *always* do this.
- return hmac.new(key, msg=value, digestmod=hashlib.sha1)
+ return hmac.new(key, msg=smart_bytes(value), digestmod=hashlib.sha1)
def get_random_string(length=12,
@@ -99,7 +99,7 @@ def _bin_to_long(x):
This is a clever optimization for fast xor vector math
"""
- return int(x.encode('hex'), 16)
+ return int(binascii.hexlify(x), 16)
def _long_to_bin(x, hex_format_string):
@@ -112,13 +112,14 @@ def _long_to_bin(x, hex_format_string):
def _fast_hmac(key, msg, digest):
"""
- A trimmed down version of Python's HMAC implementation
+ A trimmed down version of Python's HMAC implementation.
+
+ This function operates on bytes.
"""
dig1, dig2 = digest(), digest()
- key = smart_str(key)
if len(key) > dig1.block_size:
key = digest(key).digest()
- key += chr(0) * (dig1.block_size - len(key))
+ key += b'\x00' * (dig1.block_size - len(key))
dig1.update(key.translate(_trans_36))
dig1.update(msg)
dig2.update(key.translate(_trans_5c))
@@ -141,8 +142,8 @@ def pbkdf2(password, salt, iterations, dklen=0, digest=None):
assert iterations > 0
if not digest:
digest = hashlib.sha256
- password = smart_str(password)
- salt = smart_str(salt)
+ password = smart_bytes(password)
+ salt = smart_bytes(salt)
hlen = digest().digest_size
if not dklen:
dklen = hlen
diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py
index bbd31ad36c..ad17573104 100644
--- a/django/utils/datastructures.py
+++ b/django/utils/datastructures.py
@@ -129,7 +129,7 @@ class SortedDict(dict):
data = list(data)
super(SortedDict, self).__init__(data)
if isinstance(data, dict):
- self.keyOrder = list(six.iterkeys(data))
+ self.keyOrder = list(data)
else:
self.keyOrder = []
seen = set()
diff --git a/django/utils/dateformat.py b/django/utils/dateformat.py
index c9a6138aed..6a91a370e5 100644
--- a/django/utils/dateformat.py
+++ b/django/utils/dateformat.py
@@ -20,7 +20,7 @@ import datetime
from django.utils.dates import MONTHS, MONTHS_3, MONTHS_ALT, MONTHS_AP, WEEKDAYS, WEEKDAYS_ABBR
from django.utils.tzinfo import LocalTimezone
from django.utils.translation import ugettext as _
-from django.utils.encoding import force_unicode
+from django.utils.encoding import force_text
from django.utils import six
from django.utils.timezone import is_aware, is_naive
@@ -30,9 +30,9 @@ re_escaped = re.compile(r'\\(.)')
class Formatter(object):
def format(self, formatstr):
pieces = []
- for i, piece in enumerate(re_formatchars.split(force_unicode(formatstr))):
+ for i, piece in enumerate(re_formatchars.split(force_text(formatstr))):
if i % 2:
- pieces.append(force_unicode(getattr(self, piece)()))
+ pieces.append(force_text(getattr(self, piece)()))
elif piece:
pieces.append(re_escaped.sub(r'\1', piece))
return ''.join(pieces)
diff --git a/django/utils/dateparse.py b/django/utils/dateparse.py
index 532bb259c3..032eb493b6 100644
--- a/django/utils/dateparse.py
+++ b/django/utils/dateparse.py
@@ -7,6 +7,7 @@
import datetime
import re
+from django.utils import six
from django.utils.timezone import utc
from django.utils.tzinfo import FixedOffset
@@ -34,7 +35,7 @@ def parse_date(value):
"""
match = date_re.match(value)
if match:
- kw = dict((k, int(v)) for k, v in match.groupdict().iteritems())
+ kw = dict((k, int(v)) for k, v in six.iteritems(match.groupdict()))
return datetime.date(**kw)
def parse_time(value):
@@ -53,7 +54,7 @@ def parse_time(value):
kw = match.groupdict()
if kw['microsecond']:
kw['microsecond'] = kw['microsecond'].ljust(6, '0')
- kw = dict((k, int(v)) for k, v in kw.iteritems() if v is not None)
+ kw = dict((k, int(v)) for k, v in six.iteritems(kw) if v is not None)
return datetime.time(**kw)
def parse_datetime(value):
@@ -80,6 +81,6 @@ def parse_datetime(value):
if tzinfo[0] == '-':
offset = -offset
tzinfo = FixedOffset(offset)
- kw = dict((k, int(v)) for k, v in kw.iteritems() if v is not None)
+ kw = dict((k, int(v)) for k, v in six.iteritems(kw) if v is not None)
kw['tzinfo'] = tzinfo
return datetime.datetime(**kw)
diff --git a/django/utils/dictconfig.py b/django/utils/dictconfig.py
index b4d6d66b3c..c7b39819b5 100644
--- a/django/utils/dictconfig.py
+++ b/django/utils/dictconfig.py
@@ -363,7 +363,7 @@ class DictConfigurator(BaseConfigurator):
#which were in the previous configuration but
#which are not in the new configuration.
root = logging.root
- existing = root.manager.loggerDict.keys()
+ existing = list(root.manager.loggerDict)
#The list needs to be sorted so that we can
#avoid disabling child loggers of explicitly
#named loggers. With a sorted list it is easier
diff --git a/django/utils/encoding.py b/django/utils/encoding.py
index f2295444bf..eb60cfde8b 100644
--- a/django/utils/encoding.py
+++ b/django/utils/encoding.py
@@ -24,9 +24,13 @@ class DjangoUnicodeDecodeError(UnicodeDecodeError):
class StrAndUnicode(object):
"""
- A class whose __str__ returns its __unicode__ as a UTF-8 bytestring.
+ A class that derives __str__ from __unicode__.
- Useful as a mix-in.
+ On Python 2, __str__ returns the output of __unicode__ encoded as a UTF-8
+ bytestring. On Python 3, __str__ returns the output of __unicode__.
+
+ Useful as a mix-in. If you support Python 2 and 3 with a single code base,
+ you can inherit this mix-in and just define __unicode__.
"""
if six.PY3:
def __str__(self):
@@ -35,37 +39,36 @@ class StrAndUnicode(object):
def __str__(self):
return self.__unicode__().encode('utf-8')
-def smart_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
+def smart_text(s, encoding='utf-8', strings_only=False, errors='strict'):
"""
- Returns a unicode object representing 's'. Treats bytestrings using the
- 'encoding' codec.
+ Returns a text object representing 's' -- unicode on Python 2 and str on
+ Python 3. Treats bytestrings using the 'encoding' codec.
If strings_only is True, don't convert (some) non-string-like objects.
"""
if isinstance(s, Promise):
# The input is the result of a gettext_lazy() call.
return s
- return force_unicode(s, encoding, strings_only, errors)
+ return force_text(s, encoding, strings_only, errors)
def is_protected_type(obj):
"""Determine if the object instance is of a protected type.
Objects of protected types are preserved as-is when passed to
- force_unicode(strings_only=True).
+ force_text(strings_only=True).
"""
return isinstance(obj, six.integer_types + (type(None), float, Decimal,
datetime.datetime, datetime.date, datetime.time))
-def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
+def force_text(s, encoding='utf-8', strings_only=False, errors='strict'):
"""
- Similar to smart_unicode, except that lazy instances are resolved to
+ Similar to smart_text, except that lazy instances are resolved to
strings, rather than kept as lazy objects.
If strings_only is True, don't convert (some) non-string-like objects.
"""
- # Handle the common case first, saves 30-40% in performance when s
- # is an instance of unicode. This function gets called often in that
- # setting.
+ # Handle the common case first, saves 30-40% when s is an instance of
+ # six.text_type. This function gets called often in that setting.
if isinstance(s, six.text_type):
return s
if strings_only and is_protected_type(s):
@@ -92,9 +95,9 @@ def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
# without raising a further exception. We do an
# approximation to what the Exception's standard str()
# output should be.
- s = ' '.join([force_unicode(arg, encoding, strings_only,
+ s = ' '.join([force_text(arg, encoding, strings_only,
errors) for arg in s])
- elif not isinstance(s, six.text_type):
+ else:
# Note: We use .decode() here, instead of six.text_type(s, encoding,
# errors), so that if s is a SafeString, it ends up being a
# SafeUnicode at the end.
@@ -108,21 +111,26 @@ def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
# working unicode method. Try to handle this without raising a
# further exception by individually forcing the exception args
# to unicode.
- s = ' '.join([force_unicode(arg, encoding, strings_only,
+ s = ' '.join([force_text(arg, encoding, strings_only,
errors) for arg in s])
return s
-def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):
+def smart_bytes(s, encoding='utf-8', strings_only=False, errors='strict'):
"""
Returns a bytestring version of 's', encoded as specified in 'encoding'.
If strings_only is True, don't convert (some) non-string-like objects.
"""
+ if isinstance(s, bytes):
+ if encoding == 'utf-8':
+ return s
+ else:
+ return s.decode('utf-8', errors).encode(encoding, errors)
if strings_only and (s is None or isinstance(s, int)):
return s
if isinstance(s, Promise):
return six.text_type(s).encode(encoding, errors)
- elif not isinstance(s, six.string_types):
+ if not isinstance(s, six.string_types):
try:
if six.PY3:
return six.text_type(s).encode(encoding)
@@ -133,15 +141,25 @@ def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):
# An Exception subclass containing non-ASCII data that doesn't
# know how to print itself properly. We shouldn't raise a
# further exception.
- return ' '.join([smart_str(arg, encoding, strings_only,
+ return ' '.join([smart_bytes(arg, encoding, strings_only,
errors) for arg in s])
return six.text_type(s).encode(encoding, errors)
- elif isinstance(s, six.text_type):
- return s.encode(encoding, errors)
- elif s and encoding != 'utf-8':
- return s.decode('utf-8', errors).encode(encoding, errors)
else:
- return s
+ return s.encode(encoding, errors)
+
+if six.PY3:
+ smart_str = smart_text
+else:
+ smart_str = smart_bytes
+ # backwards compatibility for Python 2
+ smart_unicode = smart_text
+ force_unicode = force_text
+
+smart_str.__doc__ = """\
+Apply smart_text in Python 3 and smart_bytes in Python 2.
+
+This is suitable for writing to sys.stdout (for instance).
+"""
def iri_to_uri(iri):
"""
@@ -168,7 +186,7 @@ def iri_to_uri(iri):
# converted.
if iri is None:
return iri
- return quote(smart_str(iri), safe=b"/#%[]=:;$&()+,!?*@'~")
+ return quote(smart_bytes(iri), safe=b"/#%[]=:;$&()+,!?*@'~")
def filepath_to_uri(path):
"""Convert an file system path to a URI portion that is suitable for
@@ -187,7 +205,7 @@ def filepath_to_uri(path):
return path
# I know about `os.sep` and `os.altsep` but I want to leave
# some flexibility for hardcoding separators.
- return quote(smart_str(path).replace("\\", "/"), safe=b"/~!*()'")
+ return quote(smart_bytes(path).replace("\\", "/"), 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
diff --git a/django/utils/feedgenerator.py b/django/utils/feedgenerator.py
index 6498aaf57c..f9126a6782 100644
--- a/django/utils/feedgenerator.py
+++ b/django/utils/feedgenerator.py
@@ -29,8 +29,10 @@ try:
except ImportError: # Python 2
from urlparse import urlparse
from django.utils.xmlutils import SimplerXMLGenerator
-from django.utils.encoding import force_unicode, iri_to_uri
+from django.utils.encoding import force_text, iri_to_uri
from django.utils import datetime_safe
+from django.utils import six
+from django.utils.six import StringIO
from django.utils.timezone import is_aware
def rfc2822_date(date):
@@ -44,25 +46,29 @@ def rfc2822_date(date):
dow = days[date.weekday()]
month = months[date.month - 1]
time_str = date.strftime('%s, %%d %s %%Y %%H:%%M:%%S ' % (dow, month))
+ if not six.PY3: # strftime returns a byte string in Python 2
+ time_str = time_str.decode('utf-8')
if is_aware(date):
offset = date.tzinfo.utcoffset(date)
timezone = (offset.days * 24 * 60) + (offset.seconds // 60)
hour, minute = divmod(timezone, 60)
- return time_str + "%+03d%02d" % (hour, minute)
+ return time_str + '%+03d%02d' % (hour, minute)
else:
return time_str + '-0000'
def rfc3339_date(date):
# Support datetime objects older than 1900
date = datetime_safe.new_datetime(date)
+ time_str = date.strftime('%Y-%m-%dT%H:%M:%S')
+ if not six.PY3: # strftime returns a byte string in Python 2
+ time_str = time_str.decode('utf-8')
if is_aware(date):
- time_str = date.strftime('%Y-%m-%dT%H:%M:%S')
offset = date.tzinfo.utcoffset(date)
timezone = (offset.days * 24 * 60) + (offset.seconds // 60)
hour, minute = divmod(timezone, 60)
- return time_str + "%+03d:%02d" % (hour, minute)
+ return time_str + '%+03d:%02d' % (hour, minute)
else:
- return date.strftime('%Y-%m-%dT%H:%M:%SZ')
+ return time_str + 'Z'
def get_tag_uri(url, date):
"""
@@ -81,12 +87,12 @@ class SyndicationFeed(object):
def __init__(self, title, link, description, language=None, author_email=None,
author_name=None, author_link=None, subtitle=None, categories=None,
feed_url=None, feed_copyright=None, feed_guid=None, ttl=None, **kwargs):
- to_unicode = lambda s: force_unicode(s, strings_only=True)
+ to_unicode = lambda s: force_text(s, strings_only=True)
if categories:
- categories = [force_unicode(c) for c in categories]
+ categories = [force_text(c) for c in categories]
if ttl is not None:
# Force ints to unicode
- ttl = force_unicode(ttl)
+ ttl = force_text(ttl)
self.feed = {
'title': to_unicode(title),
'link': iri_to_uri(link),
@@ -114,12 +120,12 @@ class SyndicationFeed(object):
objects except pubdate, which is a datetime.datetime object, and
enclosure, which is an instance of the Enclosure class.
"""
- to_unicode = lambda s: force_unicode(s, strings_only=True)
+ to_unicode = lambda s: force_text(s, strings_only=True)
if categories:
categories = [to_unicode(c) for c in categories]
if ttl is not None:
# Force ints to unicode
- ttl = force_unicode(ttl)
+ ttl = force_text(ttl)
item = {
'title': to_unicode(title),
'link': iri_to_uri(link),
@@ -178,8 +184,7 @@ class SyndicationFeed(object):
"""
Returns the feed in the given encoding as a string.
"""
- from io import BytesIO
- s = BytesIO()
+ s = StringIO()
self.write(s, encoding)
return s.getvalue()
@@ -237,7 +242,7 @@ class RssFeed(SyndicationFeed):
handler.addQuickElement("category", cat)
if self.feed['feed_copyright'] is not None:
handler.addQuickElement("copyright", self.feed['feed_copyright'])
- handler.addQuickElement("lastBuildDate", rfc2822_date(self.latest_post_date()).decode('utf-8'))
+ handler.addQuickElement("lastBuildDate", rfc2822_date(self.latest_post_date()))
if self.feed['ttl'] is not None:
handler.addQuickElement("ttl", self.feed['ttl'])
@@ -271,7 +276,7 @@ class Rss201rev2Feed(RssFeed):
handler.addQuickElement("dc:creator", item["author_name"], {"xmlns:dc": "http://purl.org/dc/elements/1.1/"})
if item['pubdate'] is not None:
- handler.addQuickElement("pubDate", rfc2822_date(item['pubdate']).decode('utf-8'))
+ handler.addQuickElement("pubDate", rfc2822_date(item['pubdate']))
if item['comments'] is not None:
handler.addQuickElement("comments", item['comments'])
if item['unique_id'] is not None:
@@ -314,7 +319,7 @@ class Atom1Feed(SyndicationFeed):
if self.feed['feed_url'] is not None:
handler.addQuickElement("link", "", {"rel": "self", "href": self.feed['feed_url']})
handler.addQuickElement("id", self.feed['id'])
- handler.addQuickElement("updated", rfc3339_date(self.latest_post_date()).decode('utf-8'))
+ handler.addQuickElement("updated", rfc3339_date(self.latest_post_date()))
if self.feed['author_name'] is not None:
handler.startElement("author", {})
handler.addQuickElement("name", self.feed['author_name'])
@@ -340,7 +345,7 @@ class Atom1Feed(SyndicationFeed):
handler.addQuickElement("title", item['title'])
handler.addQuickElement("link", "", {"href": item['link'], "rel": "alternate"})
if item['pubdate'] is not None:
- handler.addQuickElement("updated", rfc3339_date(item['pubdate']).decode('utf-8'))
+ handler.addQuickElement("updated", rfc3339_date(item['pubdate']))
# Author information.
if item['author_name'] is not None:
diff --git a/django/utils/functional.py b/django/utils/functional.py
index 2dca182b99..085ec40b63 100644
--- a/django/utils/functional.py
+++ b/django/utils/functional.py
@@ -93,13 +93,19 @@ def lazy(func, *resultclasses):
if hasattr(cls, k):
continue
setattr(cls, k, meth)
- cls._delegate_str = bytes in resultclasses
- cls._delegate_unicode = six.text_type in resultclasses
- assert not (cls._delegate_str and cls._delegate_unicode), "Cannot call lazy() with both str and unicode return types."
- if cls._delegate_unicode:
- cls.__unicode__ = cls.__unicode_cast
- elif cls._delegate_str:
- cls.__str__ = cls.__str_cast
+ cls._delegate_bytes = bytes in resultclasses
+ cls._delegate_text = six.text_type in resultclasses
+ assert not (cls._delegate_bytes and cls._delegate_text), "Cannot call lazy() with both bytes and text return types."
+ if cls._delegate_text:
+ if six.PY3:
+ cls.__str__ = cls.__text_cast
+ else:
+ cls.__unicode__ = cls.__text_cast
+ elif cls._delegate_bytes:
+ if six.PY3:
+ cls.__bytes__ = cls.__bytes_cast
+ else:
+ cls.__str__ = cls.__bytes_cast
__prepare_class__ = classmethod(__prepare_class__)
def __promise__(cls, klass, funcname, method):
@@ -120,17 +126,17 @@ def lazy(func, *resultclasses):
return __wrapper__
__promise__ = classmethod(__promise__)
- def __unicode_cast(self):
+ def __text_cast(self):
return func(*self.__args, **self.__kw)
- def __str_cast(self):
- return str(func(*self.__args, **self.__kw))
+ def __bytes_cast(self):
+ return bytes(func(*self.__args, **self.__kw))
def __cast(self):
- if self._delegate_str:
- return self.__str_cast()
- elif self._delegate_unicode:
- return self.__unicode_cast()
+ if self._delegate_bytes:
+ return self.__bytes_cast()
+ elif self._delegate_text:
+ return self.__text_cast()
else:
return func(*self.__args, **self.__kw)
@@ -144,10 +150,12 @@ def lazy(func, *resultclasses):
other = other.__cast()
return self.__cast() < other
+ __hash__ = object.__hash__
+
def __mod__(self, rhs):
- if self._delegate_str:
- return str(self) % rhs
- elif self._delegate_unicode:
+ if self._delegate_bytes and not six.PY3:
+ return bytes(self) % rhs
+ elif self._delegate_text:
return six.text_type(self) % rhs
else:
raise AssertionError('__mod__ not supported for non-string types')
@@ -178,7 +186,7 @@ def allow_lazy(func, *resultclasses):
"""
@wraps(func)
def wrapper(*args, **kwargs):
- for arg in list(args) + kwargs.values():
+ for arg in list(args) + list(six.itervalues(kwargs)):
if isinstance(arg, Promise):
break
else:
@@ -234,6 +242,9 @@ class LazyObject(object):
__dir__ = new_method_proxy(dir)
+# Workaround for http://bugs.python.org/issue12370
+_super = super
+
class SimpleLazyObject(LazyObject):
"""
A lazy object initialised from any function.
@@ -251,13 +262,17 @@ class SimpleLazyObject(LazyObject):
value.
"""
self.__dict__['_setupfunc'] = func
- super(SimpleLazyObject, self).__init__()
+ _super(SimpleLazyObject, self).__init__()
def _setup(self):
self._wrapped = self._setupfunc()
- __str__ = new_method_proxy(bytes)
- __unicode__ = new_method_proxy(six.text_type)
+ if six.PY3:
+ __bytes__ = new_method_proxy(bytes)
+ __str__ = new_method_proxy(str)
+ else:
+ __str__ = new_method_proxy(str)
+ __unicode__ = new_method_proxy(unicode)
def __deepcopy__(self, memo):
if self._wrapped is empty:
@@ -284,7 +299,8 @@ class SimpleLazyObject(LazyObject):
__class__ = property(new_method_proxy(operator.attrgetter("__class__")))
__eq__ = new_method_proxy(operator.eq)
__hash__ = new_method_proxy(hash)
- __nonzero__ = new_method_proxy(bool)
+ __bool__ = new_method_proxy(bool) # Python 3
+ __nonzero__ = __bool__ # Python 2
class lazy_property(property):
@@ -312,8 +328,8 @@ def partition(predicate, values):
Splits the values into two sets, based on the return value of the function
(True/False). e.g.:
- >>> partition(lambda: x > 3, range(5))
- [1, 2, 3], [4]
+ >>> partition(lambda x: x > 3, range(5))
+ [0, 1, 2, 3], [4]
"""
results = ([], [])
for item in values:
diff --git a/django/utils/html.py b/django/utils/html.py
index ca3340ccb1..7dd53a1353 100644
--- a/django/utils/html.py
+++ b/django/utils/html.py
@@ -11,7 +11,7 @@ except ImportError: # Python 2
from urlparse import urlsplit, urlunsplit
from django.utils.safestring import SafeData, mark_safe
-from django.utils.encoding import smart_str, force_unicode
+from django.utils.encoding import smart_bytes, force_text
from django.utils.functional import allow_lazy
from django.utils import six
from django.utils.text import normalize_newlines
@@ -33,13 +33,13 @@ link_target_attribute_re = re.compile(r'(<a [^>]*?)target=[^\s>]+')
html_gunk_re = re.compile(r'(?:<br clear="all">|<i><\/i>|<b><\/b>|<em><\/em>|<strong><\/strong>|<\/?smallcaps>|<\/?uppercase>)', re.IGNORECASE)
hard_coded_bullets_re = re.compile(r'((?:<p>(?:%s).*?[a-zA-Z].*?</p>\s*)+)' % '|'.join([re.escape(x) for x in DOTS]), re.DOTALL)
trailing_empty_content_re = re.compile(r'(?:<p>(?:&nbsp;|\s|<br \/>)*?</p>\s*)+\Z')
-del x # Temporary variable
+
def escape(text):
"""
Returns the given text with ampersands, quotes and angle brackets encoded for use in HTML.
"""
- return mark_safe(force_unicode(text).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;'))
+ return mark_safe(force_text(text).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;'))
escape = allow_lazy(escape, six.text_type)
_base_js_escapes = (
@@ -63,7 +63,7 @@ _js_escapes = (_base_js_escapes +
def escapejs(value):
"""Hex encodes characters for use in JavaScript strings."""
for bad, good in _js_escapes:
- value = mark_safe(force_unicode(value).replace(bad, good))
+ value = mark_safe(force_text(value).replace(bad, good))
return value
escapejs = allow_lazy(escapejs, six.text_type)
@@ -84,7 +84,7 @@ def format_html(format_string, *args, **kwargs):
"""
args_safe = map(conditional_escape, args)
kwargs_safe = dict([(k, conditional_escape(v)) for (k, v) in
- kwargs.iteritems()])
+ six.iteritems(kwargs)])
return mark_safe(format_string.format(*args_safe, **kwargs_safe))
def format_html_join(sep, format_string, args_generator):
@@ -120,22 +120,22 @@ linebreaks = allow_lazy(linebreaks, six.text_type)
def strip_tags(value):
"""Returns the given HTML with all tags stripped."""
- return re.sub(r'<[^>]*?>', '', force_unicode(value))
+ return re.sub(r'<[^>]*?>', '', force_text(value))
strip_tags = allow_lazy(strip_tags)
def strip_spaces_between_tags(value):
"""Returns the given HTML with spaces between tags removed."""
- return re.sub(r'>\s+<', '><', force_unicode(value))
+ return re.sub(r'>\s+<', '><', force_text(value))
strip_spaces_between_tags = allow_lazy(strip_spaces_between_tags, six.text_type)
def strip_entities(value):
"""Returns the given HTML with all entities (&something;) stripped."""
- return re.sub(r'&(?:\w+|#\d+);', '', force_unicode(value))
+ return re.sub(r'&(?:\w+|#\d+);', '', force_text(value))
strip_entities = allow_lazy(strip_entities, six.text_type)
def fix_ampersands(value):
"""Returns the given HTML with all unencoded ampersands encoded correctly."""
- return unencoded_ampersands_re.sub('&amp;', force_unicode(value))
+ return unencoded_ampersands_re.sub('&amp;', force_text(value))
fix_ampersands = allow_lazy(fix_ampersands, six.text_type)
def smart_urlquote(url):
@@ -153,9 +153,9 @@ def smart_urlquote(url):
# contains a % not followed by two hexadecimal digits. See #9655.
if '%' not in url or unquoted_percents_re.search(url):
# See http://bugs.python.org/issue2637
- url = quote(smart_str(url), safe=b'!*\'();:@&=+$,/?#[]~')
+ url = quote(smart_bytes(url), safe=b'!*\'();:@&=+$,/?#[]~')
- return force_unicode(url)
+ return force_text(url)
def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False):
"""
@@ -176,7 +176,7 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False):
"""
trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x
safe_input = isinstance(text, SafeData)
- words = word_split_re.split(force_unicode(text))
+ words = word_split_re.split(force_text(text))
for i, word in enumerate(words):
match = None
if '.' in word or '@' in word or ':' in word:
@@ -245,7 +245,7 @@ def clean_html(text):
bottom of the text.
"""
from django.utils.text import normalize_newlines
- text = normalize_newlines(force_unicode(text))
+ text = normalize_newlines(force_text(text))
text = re.sub(r'<(/?)\s*b\s*>', '<\\1strong>', text)
text = re.sub(r'<(/?)\s*i\s*>', '<\\1em>', text)
text = fix_ampersands(text)
diff --git a/django/utils/http.py b/django/utils/http.py
index f3a3dce58c..22e81a33d7 100644
--- a/django/utils/http.py
+++ b/django/utils/http.py
@@ -13,7 +13,7 @@ except ImportError: # Python 2
from email.utils import formatdate
from django.utils.datastructures import MultiValueDict
-from django.utils.encoding import smart_str, force_unicode
+from django.utils.encoding import force_text, smart_str
from django.utils.functional import allow_lazy
from django.utils import six
@@ -37,7 +37,7 @@ def urlquote(url, safe='/'):
can safely be used as part of an argument to a subsequent iri_to_uri() call
without double-quoting occurring.
"""
- return force_unicode(urllib_parse.quote(smart_str(url), smart_str(safe)))
+ return force_text(urllib_parse.quote(smart_str(url), smart_str(safe)))
urlquote = allow_lazy(urlquote, six.text_type)
def urlquote_plus(url, safe=''):
@@ -47,7 +47,7 @@ def urlquote_plus(url, safe=''):
returned string can safely be used as part of an argument to a subsequent
iri_to_uri() call without double-quoting occurring.
"""
- return force_unicode(urllib_parse.quote_plus(smart_str(url), smart_str(safe)))
+ return force_text(urllib_parse.quote_plus(smart_str(url), smart_str(safe)))
urlquote_plus = allow_lazy(urlquote_plus, six.text_type)
def urlunquote(quoted_url):
@@ -55,7 +55,7 @@ def urlunquote(quoted_url):
A wrapper for Python's urllib.unquote() function that can operate on
the result of django.utils.http.urlquote().
"""
- return force_unicode(urllib_parse.unquote(smart_str(quoted_url)))
+ return force_text(urllib_parse.unquote(smart_str(quoted_url)))
urlunquote = allow_lazy(urlunquote, six.text_type)
def urlunquote_plus(quoted_url):
@@ -63,7 +63,7 @@ def urlunquote_plus(quoted_url):
A wrapper for Python's urllib.unquote_plus() function that can operate on
the result of django.utils.http.urlquote_plus().
"""
- return force_unicode(urllib_parse.unquote_plus(smart_str(quoted_url)))
+ return force_text(urllib_parse.unquote_plus(smart_str(quoted_url)))
urlunquote_plus = allow_lazy(urlunquote_plus, six.text_type)
def urlencode(query, doseq=0):
@@ -167,8 +167,9 @@ def base36_to_int(s):
if len(s) > 13:
raise ValueError("Base36 input too large")
value = int(s, 36)
- # ... then do a final check that the value will fit into an int.
- if value > sys.maxint:
+ # ... then do a final check that the value will fit into an int to avoid
+ # returning a long (#15067). The long type was removed in Python 3.
+ if not six.PY3 and value > sys.maxint:
raise ValueError("Base36 input too large")
return value
@@ -178,8 +179,13 @@ def int_to_base36(i):
"""
digits = "0123456789abcdefghijklmnopqrstuvwxyz"
factor = 0
- if not 0 <= i <= sys.maxint:
- raise ValueError("Base36 conversion input too large or incorrect type.")
+ if i < 0:
+ raise ValueError("Negative base36 conversion input.")
+ if not six.PY3:
+ if not isinstance(i, six.integer_types):
+ raise TypeError("Non-integer base36 conversion input.")
+ if i > sys.maxint:
+ raise ValueError("Base36 conversion input too large.")
# Find starting factor
while True:
factor += 1
diff --git a/django/utils/regex_helper.py b/django/utils/regex_helper.py
index 8953a21e95..7b40d141de 100644
--- a/django/utils/regex_helper.py
+++ b/django/utils/regex_helper.py
@@ -8,6 +8,7 @@ should be good enough for a large class of URLS, however.
from __future__ import unicode_literals
from django.utils import six
+from django.utils.six.moves import zip
# Mapping of an escape character to a representative of that class. So, e.g.,
# "\w" is replaced by "x" in a reverse URL. A value of None means to ignore
@@ -44,8 +45,8 @@ class NonCapture(list):
def normalize(pattern):
"""
- Given a reg-exp pattern, normalizes it to a list of forms that suffice for
- reverse matching. This does the following:
+ Given a reg-exp pattern, normalizes it to an iterable of forms that
+ suffice for reverse matching. This does the following:
(1) For any repeating sections, keeps the minimum number of occurrences
permitted (this means zero for optional groups).
@@ -80,7 +81,7 @@ def normalize(pattern):
try:
ch, escaped = next(pattern_iter)
except StopIteration:
- return zip([''], [[]])
+ return [('', [])]
try:
while True:
@@ -193,9 +194,9 @@ def normalize(pattern):
pass
except NotImplementedError:
# A case of using the disjunctive form. No results for you!
- return zip([''], [[]])
+ return [('', [])]
- return zip(*flatten_result(result))
+ return list(zip(*flatten_result(result)))
def next_char(input_iter):
"""
diff --git a/django/utils/safestring.py b/django/utils/safestring.py
index 1599fc2a66..bfaefd07ee 100644
--- a/django/utils/safestring.py
+++ b/django/utils/safestring.py
@@ -96,7 +96,7 @@ def mark_safe(s):
"""
if isinstance(s, SafeData):
return s
- if isinstance(s, bytes) or (isinstance(s, Promise) and s._delegate_str):
+ if isinstance(s, bytes) or (isinstance(s, Promise) and s._delegate_bytes):
return SafeString(s)
if isinstance(s, (six.text_type, Promise)):
return SafeUnicode(s)
@@ -112,7 +112,7 @@ def mark_for_escaping(s):
"""
if isinstance(s, (SafeData, EscapeData)):
return s
- if isinstance(s, bytes) or (isinstance(s, Promise) and s._delegate_str):
+ if isinstance(s, bytes) or (isinstance(s, Promise) and s._delegate_bytes):
return EscapeString(s)
if isinstance(s, (six.text_type, Promise)):
return EscapeUnicode(s)
diff --git a/django/utils/six.py b/django/utils/six.py
index e226bba09e..ceb5d70e58 100644
--- a/django/utils/six.py
+++ b/django/utils/six.py
@@ -113,6 +113,7 @@ class _MovedItems(types.ModuleType):
_moved_attributes = [
MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
+ MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
MovedAttribute("reduce", "__builtin__", "functools"),
diff --git a/django/utils/termcolors.py b/django/utils/termcolors.py
index 1eebaa2316..4f74b564a5 100644
--- a/django/utils/termcolors.py
+++ b/django/utils/termcolors.py
@@ -2,6 +2,8 @@
termcolors.py
"""
+from django.utils import six
+
color_names = ('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white')
foreground = dict([(color_names[x], '3%s' % x) for x in range(8)])
background = dict([(color_names[x], '4%s' % x) for x in range(8)])
@@ -41,7 +43,7 @@ def colorize(text='', opts=(), **kwargs):
code_list = []
if text == '' and len(opts) == 1 and opts[0] == 'reset':
return '\x1b[%sm' % RESET
- for k, v in kwargs.iteritems():
+ for k, v in six.iteritems(kwargs):
if k == 'fg':
code_list.append(foreground[v])
elif k == 'bg':
diff --git a/django/utils/text.py b/django/utils/text.py
index 43056aa634..0838d79c65 100644
--- a/django/utils/text.py
+++ b/django/utils/text.py
@@ -7,13 +7,13 @@ from gzip import GzipFile
from django.utils.six.moves import html_entities
from io import BytesIO
-from django.utils.encoding import force_unicode
+from django.utils.encoding import force_text
from django.utils.functional import allow_lazy, SimpleLazyObject
from django.utils import six
from django.utils.translation import ugettext_lazy, ugettext as _, pgettext
# Capitalizes the first letter of a string.
-capfirst = lambda x: x and force_unicode(x)[0].upper() + force_unicode(x)[1:]
+capfirst = lambda x: x and force_text(x)[0].upper() + force_text(x)[1:]
capfirst = allow_lazy(capfirst, six.text_type)
# Set up regular expressions
@@ -26,7 +26,7 @@ def wrap(text, width):
A word-wrap function that preserves existing line breaks and most spaces in
the text. Expects that existing line breaks are posix newlines.
"""
- text = force_unicode(text)
+ text = force_text(text)
def _generator():
it = iter(text.split(' '))
word = next(it)
@@ -55,14 +55,14 @@ class Truncator(SimpleLazyObject):
An object used to truncate text, either by characters or words.
"""
def __init__(self, text):
- super(Truncator, self).__init__(lambda: force_unicode(text))
+ super(Truncator, self).__init__(lambda: force_text(text))
def add_truncation_text(self, text, truncate=None):
if truncate is None:
truncate = pgettext(
'String to return when truncating text',
'%(truncated_text)s...')
- truncate = force_unicode(truncate)
+ truncate = force_text(truncate)
if '%(truncated_text)s' in truncate:
return truncate % {'truncated_text': text}
# The truncation text didn't contain the %(truncated_text)s string
@@ -226,7 +226,7 @@ def get_valid_filename(s):
>>> get_valid_filename("john's portrait in 2004.jpg")
'johns_portrait_in_2004.jpg'
"""
- s = force_unicode(s).strip().replace(' ', '_')
+ s = force_text(s).strip().replace(' ', '_')
return re.sub(r'(?u)[^-\w.]', '', s)
get_valid_filename = allow_lazy(get_valid_filename, six.text_type)
@@ -244,20 +244,20 @@ def get_text_list(list_, last_word=ugettext_lazy('or')):
''
"""
if len(list_) == 0: return ''
- if len(list_) == 1: return force_unicode(list_[0])
+ if len(list_) == 1: return force_text(list_[0])
return '%s %s %s' % (
# Translators: This string is used as a separator between list elements
- _(', ').join([force_unicode(i) for i in list_][:-1]),
- force_unicode(last_word), force_unicode(list_[-1]))
+ _(', ').join([force_text(i) for i in list_][:-1]),
+ force_text(last_word), force_text(list_[-1]))
get_text_list = allow_lazy(get_text_list, six.text_type)
def normalize_newlines(text):
- return force_unicode(re.sub(r'\r\n|\r|\n', '\n', text))
+ return force_text(re.sub(r'\r\n|\r|\n', '\n', text))
normalize_newlines = allow_lazy(normalize_newlines, six.text_type)
def recapitalize(text):
"Recapitalizes text, placing caps after end-of-sentence punctuation."
- text = force_unicode(text).lower()
+ text = force_text(text).lower()
capsRE = re.compile(r'(?:^|(?<=[\.\?\!] ))([a-z])')
text = capsRE.sub(lambda x: x.group(1).upper(), text)
return text
@@ -330,7 +330,7 @@ def smart_split(text):
>>> list(smart_split(r'A "\"funky\" style" test.'))
['A', '"\\"funky\\" style"', 'test.']
"""
- text = force_unicode(text)
+ text = force_text(text)
for bit in smart_split_re.finditer(text):
yield bit.group(0)
smart_split = allow_lazy(smart_split, six.text_type)
diff --git a/django/utils/translation/__init__.py b/django/utils/translation/__init__.py
index d31a7aebf1..febde404a5 100644
--- a/django/utils/translation/__init__.py
+++ b/django/utils/translation/__init__.py
@@ -3,7 +3,7 @@ Internationalization support.
"""
from __future__ import unicode_literals
-from django.utils.encoding import force_unicode
+from django.utils.encoding import force_text
from django.utils.functional import lazy
from django.utils import six
@@ -139,7 +139,7 @@ def _string_concat(*strings):
Lazy variant of string concatenation, needed for translations that are
constructed from multiple parts.
"""
- return ''.join([force_unicode(s) for s in strings])
+ return ''.join([force_text(s) for s in strings])
string_concat = lazy(_string_concat, six.text_type)
def get_language_info(lang_code):
diff --git a/django/utils/translation/trans_null.py b/django/utils/translation/trans_null.py
index 0fabc707ce..5c51468204 100644
--- a/django/utils/translation/trans_null.py
+++ b/django/utils/translation/trans_null.py
@@ -3,7 +3,7 @@
# settings.USE_I18N = False can use this module rather than trans_real.py.
from django.conf import settings
-from django.utils.encoding import force_unicode
+from django.utils.encoding import force_text
from django.utils.safestring import mark_safe, SafeData
def ngettext(singular, plural, number):
@@ -12,7 +12,7 @@ def ngettext(singular, plural, number):
ngettext_lazy = ngettext
def ungettext(singular, plural, number):
- return force_unicode(ngettext(singular, plural, number))
+ return force_text(ngettext(singular, plural, number))
def pgettext(context, message):
return ugettext(message)
@@ -44,7 +44,7 @@ def gettext(message):
return result
def ugettext(message):
- return force_unicode(gettext(message))
+ return force_text(gettext(message))
gettext_noop = gettext_lazy = _ = gettext
diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py
index 9ebcbf5441..9e6eadcd48 100644
--- a/django/utils/translation/trans_real.py
+++ b/django/utils/translation/trans_real.py
@@ -9,7 +9,9 @@ import gettext as gettext_module
from threading import local
from django.utils.importlib import import_module
+from django.utils.encoding import smart_str, smart_text
from django.utils.safestring import mark_safe, SafeData
+from django.utils import six
from django.utils.six import StringIO
@@ -259,12 +261,14 @@ def do_translate(message, translation_function):
def gettext(message):
return do_translate(message, 'gettext')
-def ugettext(message):
- return do_translate(message, 'ugettext')
+if six.PY3:
+ ugettext = gettext
+else:
+ def ugettext(message):
+ return do_translate(message, 'ugettext')
def pgettext(context, message):
- result = do_translate(
- "%s%s%s" % (context, CONTEXT_SEPARATOR, message), 'ugettext')
+ result = ugettext("%s%s%s" % (context, CONTEXT_SEPARATOR, message))
if CONTEXT_SEPARATOR in result:
# Translation not found
result = message
@@ -297,20 +301,23 @@ def ngettext(singular, plural, number):
"""
return do_ntranslate(singular, plural, number, 'ngettext')
-def ungettext(singular, plural, number):
- """
- Returns a unicode strings of the translation of either the singular or
- plural, based on the number.
- """
- return do_ntranslate(singular, plural, number, 'ungettext')
+if six.PY3:
+ ungettext = ngettext
+else:
+ def ungettext(singular, plural, number):
+ """
+ Returns a unicode strings of the translation of either the singular or
+ plural, based on the number.
+ """
+ return do_ntranslate(singular, plural, number, 'ungettext')
def npgettext(context, singular, plural, number):
- result = do_ntranslate("%s%s%s" % (context, CONTEXT_SEPARATOR, singular),
- "%s%s%s" % (context, CONTEXT_SEPARATOR, plural),
- number, 'ungettext')
+ result = ungettext("%s%s%s" % (context, CONTEXT_SEPARATOR, singular),
+ "%s%s%s" % (context, CONTEXT_SEPARATOR, plural),
+ number)
if CONTEXT_SEPARATOR in result:
# Translation not found
- result = do_ntranslate(singular, plural, number, 'ungettext')
+ result = ungettext(singular, plural, number)
return result
def all_locale_paths():
@@ -440,7 +447,7 @@ def templatize(src, origin=None):
from django.conf import settings
from django.template import (Lexer, TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK,
TOKEN_COMMENT, TRANSLATOR_COMMENT_MARK)
- src = src.decode(settings.FILE_CHARSET)
+ src = smart_text(src, settings.FILE_CHARSET)
out = StringIO()
message_context = None
intrans = False
@@ -455,7 +462,7 @@ def templatize(src, origin=None):
content = ''.join(comment)
translators_comment_start = None
for lineno, line in enumerate(content.splitlines(True)):
- if line.lstrip().startswith(TRANSLATOR_COMMENT_MARK):
+ if line.lstrip().startswith(smart_text(TRANSLATOR_COMMENT_MARK)):
translators_comment_start = lineno
for lineno, line in enumerate(content.splitlines(True)):
if translators_comment_start is not None and lineno >= translators_comment_start:
@@ -570,7 +577,7 @@ def templatize(src, origin=None):
out.write(' # %s' % t.contents)
else:
out.write(blankout(t.contents, 'X'))
- return out.getvalue().encode('utf-8')
+ return smart_str(out.getvalue())
def parse_accept_lang_header(lang_string):
"""
diff --git a/django/utils/tree.py b/django/utils/tree.py
index 36b5977942..717181d2b9 100644
--- a/django/utils/tree.py
+++ b/django/utils/tree.py
@@ -68,11 +68,12 @@ class Node(object):
"""
return len(self.children)
- def __nonzero__(self):
+ def __bool__(self):
"""
For truth value testing.
"""
return bool(self.children)
+ __nonzero__ = __bool__ # Python 2
def __contains__(self, other):
"""
diff --git a/django/utils/tzinfo.py b/django/utils/tzinfo.py
index 05f4aa6d2f..208b7e7191 100644
--- a/django/utils/tzinfo.py
+++ b/django/utils/tzinfo.py
@@ -5,7 +5,7 @@ from __future__ import unicode_literals
import time
from datetime import timedelta, tzinfo
-from django.utils.encoding import smart_unicode, smart_str, DEFAULT_LOCALE_ENCODING
+from django.utils.encoding import smart_text, smart_str, DEFAULT_LOCALE_ENCODING
# Python's doc say: "A tzinfo subclass must have an __init__() method that can
# be called with no arguments". FixedOffset and LocalTimezone don't honor this
@@ -72,7 +72,7 @@ class LocalTimezone(tzinfo):
def tzname(self, dt):
try:
- return smart_unicode(time.tzname[self._isdst(dt)],
+ return smart_text(time.tzname[self._isdst(dt)],
DEFAULT_LOCALE_ENCODING)
except UnicodeDecodeError:
return None