Package amplee :: Package utils
[hide private]
[frames] | no frames]

Source Code for Package amplee.utils

  1  # -*- coding: utf-8 -*- 
  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   
53 -def generate_uuid(seed=None):
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
64 -def generate_uri(scheme, authority, trail):
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
74 -def generate_uuid_uri(seed=None):
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
82 -def extract_uuid_from_urn(urn):
83 """Returns the UUID value from a URN""" 84 return urn[9:]
85
86 -def compute_etag(seed):
87 """ Returns a MD5 hash value of seed 88 """ 89 return '"%s"' % md5.new(seed).hexdigest()
90
91 -def compute_etag_from_entry(entry):
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
104 -def compute_etag_from_feed(feed):
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
113 -def handle_if_match(etag, conditions):
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
120 -def get_isodate(dt=None):
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
129 -def parse_isodate(value):
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
154 -def decode_slug(slug):
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
167 -def encode_slug(text, encoding='utf-8'):
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
179 -def strip_list(list_in):
180 """Returns a list where empty elements are skipped 181 """ 182 return [l.strip() for l in list_in if l.strip()]
183
184 -def safe_quote(text, encoding='utf-8'):
185 if isinstance(text, unicode): 186 return quote(text.encode(encoding), safe=' /') 187 return quote(text, safe=' /')
188
189 -def safe_unquote(text, encoding='utf-8'):
190 if isinstance(text, unicode): 191 return unquote(text.encode(encoding)) 192 return unquote(text)
193
194 -def safe_url_join(tokens=None, encoding='utf-8'):
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
210 -def safe_path_join(*args):
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
223 -def extract_url_trail(url):
224 """Returns the last part of an URL""" 225 return os.path.split(urlparse(url)[2])[-1]
226
227 -def extract_url_path_info(url):
228 """Returns the full path info of a given URL""" 229 return urlparse(url)[2]
230
231 -def translate_environ(environ):
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 # Copied from CherryPy
250 - def params_from_CGI_form(form):
251 params = {} 252 for key in form.keys(): 253 value_list = form[key] 254 if isinstance(value_list, list): 255 params[key] = [] 256 for item in value_list: 257 if item.filename is not None: 258 value = item # It's a file upload 259 else: 260 value = item.value # It's a regular field 261 params[key].append(value) 262 else: 263 if value_list.filename is not None: 264 value = value_list # It's a file upload 265 else: 266 value = value_list.value # It's a regular field 267 params[key] = value 268 return params
269
270 -def parse_multiform_data(fileobj, headers):
271 """ 272 Parses the content read from the ``fileobj`` as 273 a multipart/form-data type. 274 275 Returns a dictionnary of the keys and values of the 276 form. 277 278 Do not use this function on a different media-type. 279 """ 280 if not headers.get("content-length", ""): 281 return 282 283 methenv = {'REQUEST_METHOD': 'POST'} 284 forms = FieldStorage(fp=fileobj, 285 headers=headers, 286 environ=methenv, 287 keep_blank_values=1) 288 289 return params_from_CGI_form(forms)
290 291
292 -def parse_query_string(querystring):
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
314 -def extract_media_type(entry):
315 if isinstance(entry, amara.bindery.root_base): 316 entry = entry.entry 317 318 links = entry.xml_xpath('atom:link[@rel="edit-media"]') 319 if links: 320 return links[0].type 321 322 content = entry.xml_xpath('atom:content') 323 if content: 324 return content[0].type
325
326 -def reencode(value, encoding='utf-8'):
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