1
2
3 __doc__ = """Utility functions.
4
5 Synopsis
6 --------
7
8 Just a list of utility functions that help you for common tasks.
9
10 """
11
12 __all__ = ['ENCODING', 'generate_uuid', 'generate_uri', 'generate_uuid_uri',
13 'get_isodate', 'parse_isodate', 'decode_slug', 'encode_slug',
14 'compute_etag_from_entry', 'compute_etag_from_feed', 'extract_media_type',
15 'handle_if_match', 'strip_list', 'extract_uuid_from_urn', 'extract_url_path_info',
16 'safe_quote', 'safe_unquote', 'safe_url_join', 'extract_url_trail', 'reencode',
17 'parse_multiform_data', 'parse_query_string', 'safe_path_join', 'qname']
18
19 import os
20 import os.path
21 import md5
22 import uuid
23 import datetime
24 import tempfile
25 from urllib import quote, unquote
26 from urlparse import urljoin, urlparse
27
28 import amara
29 import isodate
30
31 from amplee.error import ResourceOperationException
32
33 ATOM10_PREFIX = u'atom'
34 ATOMPUB_PREFIX = u'app'
35 THR_PREFIX = u'thr'
36
37 ATOM10_NS = u'http://www.w3.org/2005/Atom'
38 ATOMPUB_NS = u'http://www.w3.org/2007/app'
39 THR_NS = u'http://purl.org/syndication/thread/1.0'
40
41
42 XML_NS = u'http://www.w3.org/XML/1998/namespace'
43 XML_PREFIX = u'xml'
44
45 XMLNS_NS = u'http://www.w3.org/2000/xmlns/'
46 XMLNS_PREFIX = u'xmlns'
47
48 XHTML1_NS = u'http://www.w3.org/1999/xhtml'
49 XHTML1_PREFIX = u'xhtml'
50
51 ENCODING = 'utf-8'
52
54 """Returns a uuid value as specified in RFC 4122.
55
56 The ``seed``, if provided, allows this function to be idempotent.
57 When not provided a random value is returned each time.
58 """
59 if not seed:
60 return unicode(uuid.uuid4())
61 else:
62 return unicode(uuid.uuid5(uuid.NAMESPACE_URL, seed))
63
65 """Builds an URI as an unicode object
66
67 The ``scheme`` is URI scheme (e.g. http)
68 The ``authority`` is the hostname
69 The ``trail`` is the URI path
70 """
71 uri = u"%s:%s:%s" % (scheme, authority, trail)
72 return unicode(uri)
73
75 """Returns an URN from uuid
76
77 The ``seed``, if provided, allows this function to be idempotent.
78 When not provided a random value is returned each time.
79 """
80 return generate_uri('urn', 'uuid', generate_uuid(seed))
81
83 """Returns the UUID value from a URN"""
84 return urn[9:]
85
87 """ Returns a MD5 hash value of seed
88 """
89 return '"%s"' % md5.new(seed).hexdigest()
90
92 """Returns a MD5 hash value of the atom:id+app:edited content value"""
93 if isinstance(entry, amara.bindery.root_base):
94 entry = entry.entry
95 etag = md5.new(str(entry.id))
96 edited = entry.xml_child_elements.get('edited')
97 if edited:
98 etag.update(str(edited))
99 else:
100 etag.update(str(entry.updated))
101
102 return '"%s"' % etag.hexdigest()
103
105 """Returns a MD5 hash value of the atom:id+atom:updated content value"""
106 if isinstance(feed, amara.bindery.root_base):
107 feed = feed.feed
108 etag = md5.new(str(feed.id))
109 etag.update(str(feed.updated))
110
111 return '"%s"' % etag.hexdigest()
112
114 """Raises a ResourceOperationException error if the If-Match condition is not met"""
115 conditions = [str(x) for x in conditions]
116 if conditions and not (conditions == ["*"] or etag in conditions):
117 raise ResourceOperationException("If-Match failed: ETag %r did " \
118 "not match %r" % (etag, conditions), 412)
119
121 """Returns a date respecting the ISO 8601 format
122
123 If ``dt`` is not provided the current UTC time is used.
124 """
125 if not dt:
126 dt = datetime.datetime.utcnow()
127 return unicode(dt.isoformat() + 'Z')
128
130 """Attempts to parse value as an ISO date and if succeeding returns
131 a datetime object, ``None`` otherwise.
132
133 The ``value`` is a date in a string format.
134 """
135 if not value:
136 return None
137
138 dt = None
139 try:
140 dt = isodate.parse(value)
141 except ValueError:
142 value = value.replace(' ', 'T')
143 if value[-1] != 'Z':
144 value = value + 'Z'
145 try:
146 dt = isodate.parse(value)
147 except ValueError:
148 pass
149 if dt:
150 return datetime.datetime.utcfromtimestamp(dt)
151
152 return None
153
155 """Decodes and returns the provided slug value as a byte string.
156
157 For example:
158 'The Beach at S%C3%A8te' will return 'The Beach at S\xc3\xa8te'
159
160 The ``slug`` is a percent-encoded utf-8 Unicode string
161 that does not contain CR or LF.
162 """
163 if slug == None:
164 return
165 return unquote(slug)
166
168 """Returns a valid slug header as a byte string.
169
170 The ``text`` is a unicode string or a bytes string of
171 the text to encode.
172 The ``encoding`` is encoding used on the ``text`` object
173 """
174 if text == None:
175 return
176
177 return safe_quote(text, encoding)
178
180 """Returns a list where empty elements are skipped
181 """
182 return [l.strip() for l in list_in if l.strip()]
183
185 if isinstance(text, unicode):
186 return quote(text.encode(encoding), safe=' /')
187 return quote(text, safe=' /')
188
190 if isinstance(text, unicode):
191 return unquote(text.encode(encoding))
192 return unquote(text)
193
195 """
196 Joins the different tokens. The safety comes from the fact that
197 if one token is a unicode string it will be first encoded.
198
199 ``tokens`` is a list of values to join.
200
201 The returned value is a unicode string
202 """
203 _tokens = []
204 for token in tokens:
205 if isinstance(token, unicode):
206 token = token.encode(encoding)
207 _tokens.append(token)
208 return os.path.join(*_tokens).decode(encoding)
209
211 _tokens = []
212 for token in args:
213 if token == None:
214 continue
215
216 if isinstance(token, unicode):
217 token = token.encode('utf-8')
218
219 _tokens.append(token)
220
221 return os.path.join(*_tokens)
222
224 """Returns the last part of an URL"""
225 return os.path.split(urlparse(url)[2])[-1]
226
228 """Returns the full path info of a given URL"""
229 return urlparse(url)[2]
230
232 """Translate CGI-environ header names to their
233 HTTP lower cased equivalent"""
234 headers = {}
235 for cgiName in environ:
236 translatedHeader = cgiName.replace("_", "-").lower()
237 headers[translatedHeader] = environ[cgiName]
238
239 return headers
240
241
242 from cgi import parse_qs
243 try:
244 from cherrypy._cpcgifs import FieldStorage
245 from cherrypy.lib.http import params_from_CGI_form
246 except ImportError:
247 from cgi import FieldStorage
248
249
269
290
291
293 qsparams = parse_qs(querystring)
294 params = {}
295 for key in qsparams:
296 if isinstance(qsparams[key], list) and len(qsparams[key]) > 1:
297 params[key] = qsparams[key][:]
298 elif isinstance(qsparams[key], list) and len(qsparams[key]) == 1:
299 params[key] = qsparams[key][0]
300 elif isinstance(qsparams[key], list) and len(qsparams[key]) == 0:
301 params[key] = ''
302 else:
303 params[key] = qsparams[key]
304
305 return params
306
307 -def qname(local_name, prefix=None):
308 """Builds and returns a qualified name"""
309 if not prefix:
310 return local_name
311
312 return "%s:%s" % (prefix, local_name)
313
325
327 """Encodes value in the given encoding."""
328 if isinstance(value, unicode):
329 value = value.encode(encoding).decode(encoding)
330 elif isinstance(value, str):
331 value = value.decode(encoding)
332 elif isinstance(value, list) or isinstance(value, tuple):
333 encoded_value = []
334 for item in value:
335 if isinstance(item, unicode):
336 encoded_value.append(item.encode(encoding).decode(encoding))
337 elif isinstance(item, str):
338 encoded_value.append(item.decode(encoding))
339
340 value = encoded_value
341
342 return value
343