298
288
sys._getframe(1).f_locals["__traceback_info__"] = info
302
"""Return a timezone-aware timestamp for the current time."""
303
return datetime.now(tz=pytz.UTC)
306
# This is a regular expression that matches email address embedded in
307
# text. It is not RFC 2821 compliant, nor does it need to be. This
308
# expression strives to identify probable email addresses so that they
309
# can be obfuscated when viewed by unauthenticated users. See
310
# http://www.email-unlimited.com/stuff/email_address_validator.htm
312
# localnames do not have [&?%!@<>,;:`|{}()#*^~ ] in practice
313
# (regardless of RFC 2821) because they conflict with other systems.
314
# See https://lists.ubuntu.com
315
# /mailman/private/launchpad-reviews/2007-June/006081.html
317
# This verson of the re is more than 5x faster that the orginal
318
# version used in ftest/test_tales.testObfuscateEmail.
319
re_email_address = re.compile(r"""
320
\b[a-zA-Z0-9._/="'+-]{1,64}@ # The localname.
321
[a-zA-Z][a-zA-Z0-9-]{1,63} # The hostname.
322
\.[a-zA-Z0-9.-]{1,251}\b # Dot starts one or more domains.
323
""", re.VERBOSE) # ' <- font-lock turd
326
def obfuscate_email(text_to_obfuscate, replacement=None):
327
"""Obfuscate an email address.
329
The email address is obfuscated as <email address hidden> by default,
330
or with the given replacement.
332
The pattern used to identify an email address is not 2822. It strives
333
to match any possible email address embedded in the text. For example,
334
mailto:person@domain.dom and http://person:password@domain.dom both
335
match, though the http match is in fact not an email address.
337
if replacement is None:
338
replacement = '<email address hidden>'
339
text = re_email_address.sub(
340
replacement, text_to_obfuscate)
341
# Avoid doubled angle brackets.
343
"<<email address hidden>>", "<email address hidden>")
347
def save_bz2_pickle(obj, filename):
348
"""Save a bz2 compressed pickle of `obj` to `filename`."""
349
fout = bz2.BZ2File(filename, "w")
351
pickle.dump(obj, fout, pickle.HIGHEST_PROTOCOL)
356
def load_bz2_pickle(filename):
357
"""Load and return a bz2 compressed pickle from `filename`."""
358
fin = bz2.BZ2File(filename, "r")
360
return pickle.load(fin)
365
def obfuscate_structure(o):
366
"""Obfuscate the strings of a json-serializable structure.
368
Note: tuples are converted to lists because json encoders do not
369
distinguish between lists and tuples.
371
:param o: Any json-serializable object.
372
:return: a possibly-new structure in which all strings, list and tuple
373
elements, and dict keys and values have undergone obfuscate_email
376
if isinstance(o, basestring):
377
return obfuscate_email(o)
378
elif isinstance(o, (list, tuple)):
379
return [obfuscate_structure(value) for value in o]
380
elif isinstance(o, (dict)):
382
(obfuscate_structure(key), obfuscate_structure(value))
383
for key, value in o.iteritems())
291
class RegisteredSubclass(type):
292
"""Metaclass for when subclasses should be registered."""
294
def __init__(cls, name, bases, dict_):
295
# _register_subclass must be a static method to permit upcalls.
297
# We cannot use super(Class, cls) to do the upcalls, because Class
298
# isn't fully defined yet. (Remember, we're calling this from a
301
# Without using super, a classmethod that overrides another
302
# classmethod has no reasonable way to call the overridden version AND
303
# provide its class as first parameter (i.e. "cls"). Therefore, we
304
# must use a static method.
305
cls._register_subclass(cls)