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