Fixed #7496 -- It's now possible to pickle SortedDicts with pickle protocol 2
[django.git] / django / utils / encoding.py
blobc54e67610fd9f18cd48ef0a161e1855c146c465b
1 import types
2 import urllib
3 import datetime
4 from django.utils.functional import Promise
6 class DjangoUnicodeDecodeError(UnicodeDecodeError):
7 def __init__(self, obj, *args):
8 self.obj = obj
9 UnicodeDecodeError.__init__(self, *args)
11 def __str__(self):
12 original = UnicodeDecodeError.__str__(self)
13 return '%s. You passed in %r (%s)' % (original, self.obj,
14 type(self.obj))
16 class StrAndUnicode(object):
17 """
18 A class whose __str__ returns its __unicode__ as a UTF-8 bytestring.
20 Useful as a mix-in.
21 """
22 def __str__(self):
23 return self.__unicode__().encode('utf-8')
25 def smart_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
26 """
27 Returns a unicode object representing 's'. Treats bytestrings using the
28 'encoding' codec.
30 If strings_only is True, don't convert (some) non-string-like objects.
31 """
32 if isinstance(s, Promise):
33 # The input is the result of a gettext_lazy() call.
34 return s
35 return force_unicode(s, encoding, strings_only, errors)
37 def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
38 """
39 Similar to smart_unicode, except that lazy instances are resolved to
40 strings, rather than kept as lazy objects.
42 If strings_only is True, don't convert (some) non-string-like objects.
43 """
44 if strings_only and isinstance(s, (types.NoneType, int, long, datetime.datetime, datetime.date, datetime.time, float)):
45 return s
46 try:
47 if not isinstance(s, basestring,):
48 if hasattr(s, '__unicode__'):
49 s = unicode(s)
50 else:
51 s = unicode(str(s), encoding, errors)
52 elif not isinstance(s, unicode):
53 # Note: We use .decode() here, instead of unicode(s, encoding,
54 # errors), so that if s is a SafeString, it ends up being a
55 # SafeUnicode at the end.
56 s = s.decode(encoding, errors)
57 except UnicodeDecodeError, e:
58 raise DjangoUnicodeDecodeError(s, *e.args)
59 return s
61 def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):
62 """
63 Returns a bytestring version of 's', encoded as specified in 'encoding'.
65 If strings_only is True, don't convert (some) non-string-like objects.
66 """
67 if strings_only and isinstance(s, (types.NoneType, int)):
68 return s
69 if isinstance(s, Promise):
70 return unicode(s).encode(encoding, errors)
71 elif not isinstance(s, basestring):
72 try:
73 return str(s)
74 except UnicodeEncodeError:
75 return unicode(s).encode(encoding, errors)
76 elif isinstance(s, unicode):
77 return s.encode(encoding, errors)
78 elif s and encoding != 'utf-8':
79 return s.decode('utf-8', errors).encode(encoding, errors)
80 else:
81 return s
83 def iri_to_uri(iri):
84 """
85 Convert an Internationalized Resource Identifier (IRI) portion to a URI
86 portion that is suitable for inclusion in a URL.
88 This is the algorithm from section 3.1 of RFC 3987. However, since we are
89 assuming input is either UTF-8 or unicode already, we can simplify things a
90 little from the full method.
92 Returns an ASCII string containing the encoded result.
93 """
94 # The list of safe characters here is constructed from the printable ASCII
95 # characters that are not explicitly excluded by the list at the end of
96 # section 3.1 of RFC 3987.
97 if iri is None:
98 return iri
99 return urllib.quote(smart_str(iri), safe='/#%[]=:;$&()+,!?*')