diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py index 9d1a4f0332..2e5e6b87fb 100644 --- a/django/contrib/admin/templatetags/admin_list.py +++ b/django/contrib/admin/templatetags/admin_list.py @@ -7,7 +7,7 @@ from django.utils import dateformat from django.utils.html import escape from django.utils.text import capfirst from django.utils.translation import get_date_formats, get_partial_date_formats, ugettext as _ -from django.utils.encoding import smart_unicode, smart_str +from django.utils.encoding import smart_unicode, smart_str, force_unicode from django.template import Library import datetime @@ -181,7 +181,7 @@ def items_for_result(cl, result): result_repr = dict(f.choices).get(field_val, EMPTY_CHANGELIST_VALUE) else: result_repr = escape(field_val) - if result_repr == '': + if force_unicode(result_repr) == '': result_repr = ' ' # If list_display_links not defined, add the link tag to the first field if (first and not cl.lookup_opts.admin.list_display_links) or field_name in cl.lookup_opts.admin.list_display_links: diff --git a/django/contrib/humanize/templatetags/humanize.py b/django/contrib/humanize/templatetags/humanize.py index f268042917..699d9300b8 100644 --- a/django/contrib/humanize/templatetags/humanize.py +++ b/django/contrib/humanize/templatetags/humanize.py @@ -1,5 +1,5 @@ -from django.utils.translation import ungettext, ugettext_lazy as _ -from django.utils.encoding import smart_unicode +from django.utils.translation import ungettext, ugettext as _ +from django.utils.encoding import force_unicode from django import template import re @@ -25,8 +25,8 @@ def intcomma(value): Converts an integer to a string containing commas every three digits. For example, 3000 becomes '3,000' and 45000 becomes '45,000'. """ - orig = smart_unicode(value) - new = re.sub("^(-?\d+)(\d{3})", '\g<1>,\g<2>', smart_unicode(value)) + orig = force_unicode(value) + new = re.sub("^(-?\d+)(\d{3})", '\g<1>,\g<2>', orig) if orig == new: return new else: diff --git a/django/core/validators.py b/django/core/validators.py index c737de22e8..55b71a2e41 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -12,6 +12,7 @@ import urllib2 from django.conf import settings from django.utils.translation import ugettext as _, ugettext_lazy, ungettext from django.utils.functional import Promise, lazy +from django.utils.encoding import force_unicode import re _datere = r'\d{4}-\d{1,2}-\d{1,2}' @@ -38,10 +39,11 @@ class ValidationError(Exception): def __init__(self, message): "ValidationError can be passed a string or a list." if isinstance(message, list): - self.messages = message + self.messages = [force_unicode(msg) for msg in message] else: assert isinstance(message, (basestring, Promise)), ("%s should be a string" % repr(message)) - self.messages = [message] + self.messages = [force_unicode(message)] + def __str__(self): # This is needed because, without a __str__(), printing an exception # instance would result in this: @@ -53,10 +55,11 @@ class CriticalValidationError(Exception): def __init__(self, message): "ValidationError can be passed a string or a list." if isinstance(message, list): - self.messages = message + self.messages = [force_unicode(msg) for msg in message] else: assert isinstance(message, (basestring, Promise)), ("'%s' should be a string" % message) - self.messages = [message] + self.messages = [force_unicode(message)] + def __str__(self): return str(self.messages) diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 20fe2e4813..4c3056881c 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -119,7 +119,7 @@ class Field(object): Subclasses should implement validate(), not validate_full(). """ if not self.blank and not field_data: - return [ugettext_lazy('This field is required.')] + return [_('This field is required.')] try: self.validate(field_data, all_data) except validators.ValidationError, e: diff --git a/django/db/models/options.py b/django/db/models/options.py index 04d4e665d7..21fe4a39d3 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -5,7 +5,7 @@ from django.db.models.fields import AutoField, FieldDoesNotExist from django.db.models.loading import get_models from django.db.models.query import orderlist2sql from django.db.models import Manager -from django.utils.translation import activate, deactivate_all, get_language +from django.utils.translation import activate, deactivate_all, get_language, string_concat from bisect import bisect import re @@ -60,12 +60,12 @@ class Options(object): setattr(self, attr_name, meta_attrs.pop(attr_name, getattr(self, attr_name))) # verbose_name_plural is a special case because it uses a 's' # by default. - setattr(self, 'verbose_name_plural', meta_attrs.pop('verbose_name_plural', self.verbose_name + 's')) + setattr(self, 'verbose_name_plural', meta_attrs.pop('verbose_name_plural', string_concat(self.verbose_name, 's'))) # Any leftover attributes must be invalid. if meta_attrs != {}: raise TypeError, "'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys()) else: - self.verbose_name_plural = self.verbose_name + 's' + self.verbose_name_plural = string_concat(self.verbose_name, 's') del self.meta def _prepare(self, model): diff --git a/django/newforms/forms.py b/django/newforms/forms.py index 6bf2c68d1e..7a522f4563 100644 --- a/django/newforms/forms.py +++ b/django/newforms/forms.py @@ -6,7 +6,7 @@ import copy from django.utils.datastructures import SortedDict from django.utils.html import escape -from django.utils.encoding import StrAndUnicode, smart_unicode +from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode from fields import Field from widgets import TextInput, Textarea @@ -125,7 +125,7 @@ class BaseForm(StrAndUnicode): if errors_on_separate_row and bf_errors: output.append(error_row % bf_errors) if bf.label: - label = escape(bf.label) + label = escape(force_unicode(bf.label)) # Only add a colon if the label does not end in punctuation. if label[-1] not in ':?.!': label += ':' diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index 6414724c64..27a0b07df0 100644 --- a/django/template/defaultfilters.py +++ b/django/template/defaultfilters.py @@ -3,7 +3,7 @@ from django.template import resolve_variable, Library from django.conf import settings from django.utils.translation import ugettext, ungettext -from django.utils.encoding import smart_unicode, smart_str, iri_to_uri +from django.utils.encoding import force_unicode, smart_str, iri_to_uri import re import random as random_module @@ -15,13 +15,14 @@ register = Library() def stringfilter(func): """ - Decorator for filters which should only receive unicode objects. The object passed - as the first positional argument will be converted to a unicode object. + Decorator for filters which should only receive unicode objects. The object + passed as the first positional argument will be converted to a unicode + object. """ def _dec(*args, **kwargs): if args: args = list(args) - args[0] = smart_unicode(args[0]) + args[0] = force_unicode(args[0]) return func(*args, **kwargs) # Include a reference to the real function (used to check original @@ -76,7 +77,7 @@ def floatformat(text, arg=-1): try: d = int(arg) except ValueError: - return smart_unicode(f) + return force_unicode(f) m = f - int(f) if not m and d < 0: return u'%d' % int(f) @@ -86,7 +87,7 @@ def floatformat(text, arg=-1): def iriencode(value): "Escapes an IRI value for use in a URL" - return smart_unicode(iri_to_uri(value)) + return force_unicode(iri_to_uri(value)) iriencode = stringfilter(iriencode) def linenumbers(value): @@ -175,7 +176,7 @@ upper = stringfilter(upper) def urlencode(value): "Escapes a value for use in a URL" import urllib - return smart_unicode(urllib.quote(value)) + return force_unicode(urllib.quote(value)) urlencode = stringfilter(urlencode) def urlize(value): @@ -309,7 +310,7 @@ def first(value): def join(value, arg): "Joins a list with a string, like Python's ``str.join(list)``" try: - return arg.join(map(smart_unicode, value)) + return arg.join(map(force_unicode, value)) except AttributeError: # fail silently but nicely return value @@ -369,10 +370,10 @@ def unordered_list(value): def _helper(value, tabs): indent = u'\t' * tabs if value[1]: - return u'%s
and
s"
- value = re.sub(r'\r\n|\r|\n', '\n', value) # normalize newlines
+ value = re.sub(r'\r\n|\r|\n', '\n', force_unicode(value)) # normalize newlines
paras = re.split('\n{2,}', value)
paras = [u'
%s
' % p.strip().replace('\n', '", but only if it's at the bottom of the text. text = trailing_empty_content_re.sub('', text) return text +clean_html = allow_lazy(clean_html, unicode) diff --git a/django/utils/text.py b/django/utils/text.py index a355744743..979775be77 100644 --- a/django/utils/text.py +++ b/django/utils/text.py @@ -1,16 +1,18 @@ import re from django.conf import settings -from django.utils.encoding import smart_unicode +from django.utils.encoding import force_unicode +from django.utils.functional import allow_lazy # Capitalizes the first letter of a string. -capfirst = lambda x: x and x[0].upper() + x[1:] +capfirst = lambda x: x and force_unicode(x)[0].upper() + force_unicode(x)[1:] +capfirst = allow_lazy(capfirst, unicode) 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 = smart_unicode(text) + text = force_unicode(text) def _generator(): it = iter(text.split(' ')) word = it.next() @@ -31,10 +33,11 @@ def wrap(text, width): pos = len(lines[-1]) yield word return u''.join(_generator()) +wrap = allow_lazy(wrap, unicode) def truncate_words(s, num): "Truncates a string after a certain number of words." - s = smart_unicode(s) + s = force_unicode(s) length = int(num) words = s.split() if len(words) > length: @@ -42,6 +45,7 @@ def truncate_words(s, num): if not words[-1].endswith('...'): words.append('...') return u' '.join(words) +truncate_words = allow_lazy(truncate_words, unicode) def truncate_html_words(s, num): """ @@ -49,7 +53,7 @@ def truncate_html_words(s, num): comments). Closes opened tags if they were correctly closed in the given html. """ - s = smart_unicode(s) + s = force_unicode(s) length = int(num) if length <= 0: return u'' @@ -104,6 +108,7 @@ def truncate_html_words(s, num): out += '%s>' % tag # Return string return out +truncate_html_words = allow_lazy(truncate_html_words, unicode) def get_valid_filename(s): """ @@ -114,8 +119,9 @@ def get_valid_filename(s): >>> get_valid_filename("john's portrait in 2004.jpg") 'johns_portrait_in_2004.jpg' """ - s = smart_unicode(s).strip().replace(' ', '_') + s = force_unicode(s).strip().replace(' ', '_') return re.sub(r'[^-A-Za-z0-9_.]', '', s) +get_valid_filename = allow_lazy(get_valid_filename, unicode) def get_text_list(list_, last_word=u'or'): """ @@ -131,18 +137,21 @@ def get_text_list(list_, last_word=u'or'): '' """ if len(list_) == 0: return u'' - if len(list_) == 1: return smart_unicode(list_[0]) - return u'%s %s %s' % (', '.join([smart_unicode(i) for i in list_][:-1]), smart_unicode(last_word), smart_unicode(list_[-1])) + if len(list_) == 1: return force_unicode(list_[0]) + return u'%s %s %s' % (', '.join([force_unicode(i) for i in list_][:-1]), force_unicode(last_word), force_unicode(list_[-1])) +get_text_list = allow_lazy(get_text_list, unicode) def normalize_newlines(text): - return smart_unicode(re.sub(r'\r\n|\r|\n', '\n', text)) + return force_unicode(re.sub(r'\r\n|\r|\n', '\n', text)) +normalize_newlines = allow_lazy(normalize_newlines, unicode) def recapitalize(text): "Recapitalizes text, placing caps after end-of-sentence punctuation." - text = smart_unicode(text).lower() + text = force_unicode(text).lower() capsRE = re.compile(r'(?:^|(?<=[\.\?\!] ))([a-z])') text = capsRE.sub(lambda x: x.group(1).upper(), text) return text +recapitalize = allow_lazy(recapitalize) def phone2numeric(phone): "Converts a phone number with letters into its numeric equivalent." @@ -153,6 +162,7 @@ def phone2numeric(phone): 's': '7', 'r': '7', 'u': '8', 't': '8', 'w': '9', 'v': '8', 'y': '9', 'x': '9'}.get(m.group(0).lower()) return letters.sub(char2number, phone) +phone2numeric = allow_lazy(phone2numeric) # From http://www.xhaus.com/alan/python/httpcomp.html#gzip # Used with permission. @@ -183,6 +193,7 @@ def javascript_quote(s, quote_double_quotes=False): if quote_double_quotes: s = s.replace('"', '"') return str(ustring_re.sub(fix, s)) +javascript_quote = allow_lazy(javascript_quote, unicode) smart_split_re = re.compile('("(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'|[^\\s]+)') def smart_split(text): @@ -195,7 +206,7 @@ def smart_split(text): >>> list(smart_split('This is "a person\'s" test.')) ['This', 'is', '"a person\'s"', 'test.'] """ - text = smart_unicode(text) + text = force_unicode(text) for bit in smart_split_re.finditer(text): bit = bit.group(0) if bit[0] == '"' and bit[-1] == '"': @@ -204,3 +215,5 @@ def smart_split(text): yield "'" + bit[1:-1].replace("\\'", "'").replace("\\\\", "\\") + "'" else: yield bit +smart_split = allow_lazy(smart_split, unicode) + diff --git a/django/utils/translation/trans_null.py b/django/utils/translation/trans_null.py index a09f5ce8df..c6fe16d37f 100644 --- a/django/utils/translation/trans_null.py +++ b/django/utils/translation/trans_null.py @@ -13,7 +13,7 @@ ngettext_lazy = ngettext def ungettext(singular, plural, number): return smart_unicode(ngettext(singular, plural, number)) -string_concat = lambda *strings: ''.join([str(el) for el in strings]) +string_concat = lambda *strings: u''.join([smart_unicode(el) for el in strings]) activate = lambda x: None deactivate = deactivate_all = install = lambda: None get_language = lambda: settings.LANGUAGE_CODE diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py index b8411366f9..765152afce 100644 --- a/django/utils/translation/trans_real.py +++ b/django/utils/translation/trans_real.py @@ -3,7 +3,7 @@ import os, re, sys import gettext as gettext_module from cStringIO import StringIO -from django.utils.encoding import smart_str, smart_unicode +from django.utils.encoding import force_unicode try: import threading @@ -516,8 +516,7 @@ def templatize(src): def string_concat(*strings): """" - lazy variant of string concatenation, needed for translations that are - constructed from multiple parts. Handles lazy strings and non-strings by - first turning all arguments to unicode, before joining them. + Lazy variant of string concatenation, needed for translations that are + constructed from multiple parts. """ - return u''.join([smart_unicode(el) for el in strings]) + return u''.join([force_unicode(s) for s in strings]) diff --git a/tests/regressiontests/forms/regressions.py b/tests/regressiontests/forms/regressions.py index c160ccbd01..723cad53cd 100644 --- a/tests/regressiontests/forms/regressions.py +++ b/tests/regressiontests/forms/regressions.py @@ -27,13 +27,13 @@ Translations are done at rendering time, so multi-lingual apps can define forms early and still send back the right translation. # XFAIL -# >>> activate('de') -# >>> print f.as_p() -#
-# >>> activate('pl') -# >>> f.as_p() -# u'
' -# >>> deactivate() +>>> activate('de') +>>> print f.as_p() +
+>>> activate('pl') +>>> f.as_p() +u'
' +>>> deactivate() Unicode decoding problems... >>> GENDERS = ((u'\xc5', u'En tied\xe4'), (u'\xf8', u'Mies'), (u'\xdf', u'Nainen')) diff --git a/tests/regressiontests/humanize/tests.py b/tests/regressiontests/humanize/tests.py index eca65f7575..f94d642973 100644 --- a/tests/regressiontests/humanize/tests.py +++ b/tests/regressiontests/humanize/tests.py @@ -15,7 +15,7 @@ class HumanizeTests(unittest.TestCase): self.assertEqual(rendered, result_list[index], msg="""%s test failed, produced %s, should've produced %s""" % (method, rendered, result_list[index])) - + def test_ordinal(self): test_list = ('1','2','3','4','11','12', '13','101','102','103','111', @@ -43,12 +43,12 @@ should've produced %s""" % (method, rendered, result_list[index])) self.humanize_tester(test_list, result_list, 'intword') def test_apnumber(self): - test_list = [str(x) for x in xrange(1,11)] - result_list = ('one', 'two', 'three', 'four', 'five', 'six', - 'seven', 'eight', 'nine', '10') + test_list = [str(x) for x in range(1, 11)] + result_list = (u'one', u'two', u'three', u'four', u'five', u'six', + u'seven', u'eight', u'nine', u'10') self.humanize_tester(test_list, result_list, 'apnumber') if __name__ == '__main__': unittest.main() - +