~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/services/utils.py

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-06-25 08:55:37 UTC
  • mfrom: (13287.1.8 bug-800652)
  • Revision ID: launchpad@pqm.canonical.com-20110625085537-moikyoo2pe98zs7r
[r=jcsackett, julian-edwards][bug=800634,
        800652] Enable and display overrides on sync package uploads.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
    'file_exists',
19
19
    'iter_list_chunks',
20
20
    'iter_split',
21
 
    'load_bz2_pickle',
22
 
    'obfuscate_email',
23
 
    'obfuscate_structure',
24
 
    're_email_address',
 
21
    'RegisteredSubclass',
25
22
    'run_capturing_output',
26
 
    'save_bz2_pickle',
27
23
    'synchronize',
28
24
    'text_delta',
29
25
    'traceback_info',
30
 
    'utc_now',
31
26
    'value_string',
32
27
    ]
33
28
 
34
 
import bz2
35
 
import cPickle as pickle
36
 
from datetime import datetime
37
29
from itertools import tee
38
30
import os
39
 
import re
 
31
from StringIO import StringIO
40
32
import string
41
 
from StringIO import StringIO
42
33
import sys
43
34
from textwrap import dedent
44
35
from types import FunctionType
48
39
    MonkeyPatch,
49
40
    )
50
41
from lazr.enum import BaseItem
51
 
import pytz
52
42
from twisted.python.util import mergeFunctionMetadata
53
43
from zope.security.proxy import isinstance as zope_isinstance
54
44
 
298
288
    sys._getframe(1).f_locals["__traceback_info__"] = info
299
289
 
300
290
 
301
 
def utc_now():
302
 
    """Return a timezone-aware timestamp for the current time."""
303
 
    return datetime.now(tz=pytz.UTC)
304
 
 
305
 
 
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
311
 
 
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
316
 
 
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
324
 
 
325
 
 
326
 
def obfuscate_email(text_to_obfuscate, replacement=None):
327
 
    """Obfuscate an email address.
328
 
 
329
 
    The email address is obfuscated as <email address hidden> by default,
330
 
    or with the given replacement.
331
 
 
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.
336
 
    """
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.
342
 
    text = text.replace(
343
 
        "<<email address hidden>>", "<email address hidden>")
344
 
    return text
345
 
 
346
 
 
347
 
def save_bz2_pickle(obj, filename):
348
 
    """Save a bz2 compressed pickle of `obj` to `filename`."""
349
 
    fout = bz2.BZ2File(filename, "w")
350
 
    try:
351
 
        pickle.dump(obj, fout, pickle.HIGHEST_PROTOCOL)
352
 
    finally:
353
 
        fout.close()
354
 
 
355
 
 
356
 
def load_bz2_pickle(filename):
357
 
    """Load and return a bz2 compressed pickle from `filename`."""
358
 
    fin = bz2.BZ2File(filename, "r")
359
 
    try:
360
 
        return pickle.load(fin)
361
 
    finally:
362
 
        fin.close()
363
 
 
364
 
 
365
 
def obfuscate_structure(o):
366
 
    """Obfuscate the strings of a json-serializable structure.
367
 
 
368
 
    Note: tuples are converted to lists because json encoders do not
369
 
    distinguish between lists and tuples.
370
 
 
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
374
 
        recursively.
375
 
    """
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)):
381
 
        return dict(
382
 
            (obfuscate_structure(key), obfuscate_structure(value))
383
 
            for key, value in o.iteritems())
384
 
    else:
385
 
        return o
 
291
class RegisteredSubclass(type):
 
292
    """Metaclass for when subclasses should be registered."""
 
293
 
 
294
    def __init__(cls, name, bases, dict_):
 
295
        # _register_subclass must be a static method to permit upcalls.
 
296
        #
 
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
 
299
        # metaclass.)
 
300
        #
 
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)