2 utils needed for django's feed generator
6 Utilities for XML generation/parsing.
7 from django.utils.xmlutils import SimplerXMLGenerator
10 from xml.sax.saxutils import XMLGenerator
12 class SimplerXMLGenerator(XMLGenerator):
13 def addQuickElement(self, name, contents=None, attrs=None):
14 "Convenience method for adding an element with no children"
15 if attrs is None: attrs = {}
16 self.startElement(name, attrs)
17 if contents is not None:
18 self.characters(contents)
22 from django.utils.encoding import force_unicode, iri_to_uri
29 from decimal import Decimal
31 class DjangoUnicodeDecodeError(UnicodeDecodeError):
32 def __init__(self, obj, *args):
34 UnicodeDecodeError.__init__(self, *args)
37 original = UnicodeDecodeError.__str__(self)
38 return '%s. You passed in %r (%s)' % (original, self.obj,
41 class StrAndUnicode(object):
43 A class whose __str__ returns its __unicode__ as a UTF-8 bytestring.
48 return self.__unicode__().encode('utf-8')
50 def is_protected_type(obj):
51 """Determine if the object instance is of a protected type.
53 Objects of protected types are preserved as-is when passed to
54 force_unicode(strings_only=True).
56 return isinstance(obj, (
59 datetime.datetime, datetime.date, datetime.time,
63 def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
65 Similar to smart_unicode, except that lazy instances are resolved to
66 strings, rather than kept as lazy objects.
68 If strings_only is True, don't convert (some) non-string-like objects.
70 if strings_only and is_protected_type(s):
73 if not isinstance(s, basestring,):
74 if hasattr(s, '__unicode__'):
78 s = unicode(str(s), encoding, errors)
79 except UnicodeEncodeError:
80 if not isinstance(s, Exception):
82 # If we get to here, the caller has passed in an Exception
83 # subclass populated with non-ASCII data without special
84 # handling to display as a string. We need to handle this
85 # without raising a further exception. We do an
86 # approximation to what the Exception's standard str()
88 s = ' '.join([force_unicode(arg, encoding, strings_only,
89 errors) for arg in s])
90 elif not isinstance(s, unicode):
91 # Note: We use .decode() here, instead of unicode(s, encoding,
92 # errors), so that if s is a SafeString, it ends up being a
93 # SafeUnicode at the end.
94 s = s.decode(encoding, errors)
95 except UnicodeDecodeError, e:
96 if not isinstance(s, Exception):
97 raise DjangoUnicodeDecodeError(s, *e.args)
99 # If we get to here, the caller has passed in an Exception
100 # subclass populated with non-ASCII bytestring data without a
101 # working unicode method. Try to handle this without raising a
102 # further exception by individually forcing the exception args
104 s = ' '.join([force_unicode(arg, encoding, strings_only,
105 errors) for arg in s])
108 def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):
110 Returns a bytestring version of 's', encoded as specified in 'encoding'.
112 If strings_only is True, don't convert (some) non-string-like objects.
114 if strings_only and isinstance(s, (types.NoneType, int)):
116 elif not isinstance(s, basestring):
119 except UnicodeEncodeError:
120 if isinstance(s, Exception):
121 # An Exception subclass containing non-ASCII data that doesn't
122 # know how to print itself properly. We shouldn't raise a
124 return ' '.join([smart_str(arg, encoding, strings_only,
125 errors) for arg in s])
126 return unicode(s).encode(encoding, errors)
127 elif isinstance(s, unicode):
128 return s.encode(encoding, errors)
129 elif s and encoding != 'utf-8':
130 return s.decode('utf-8', errors).encode(encoding, errors)
136 Convert an Internationalized Resource Identifier (IRI) portion to a URI
137 portion that is suitable for inclusion in a URL.
139 This is the algorithm from section 3.1 of RFC 3987. However, since we are
140 assuming input is either UTF-8 or unicode already, we can simplify things a
141 little from the full method.
143 Returns an ASCII string containing the encoded result.
145 # The list of safe characters here is constructed from the "reserved" and
146 # "unreserved" characters specified in sections 2.2 and 2.3 of RFC 3986:
147 # reserved = gen-delims / sub-delims
148 # gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
149 # sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
150 # / "*" / "+" / "," / ";" / "="
151 # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
152 # Of the unreserved characters, urllib.quote already considers all but
154 # The % character is also added to the list of safe characters here, as the
155 # end of section 3.1 of RFC 3987 specifically mentions that % must not be
159 return urllib.quote(smart_str(iri), safe="/#%[]=:;$&()+,!?*@'~")
162 # The encoding of the default system locale but falls back to the
163 # given fallback encoding if the encoding is unsupported by python or could
164 # not be determined. See tickets #10335 and #5846
166 DEFAULT_LOCALE_ENCODING = locale.getdefaultlocale()[1] or 'ascii'
167 codecs.lookup(DEFAULT_LOCALE_ENCODING)
169 DEFAULT_LOCALE_ENCODING = 'ascii'