1import sys, re, traceback
2
3# these statuses are ordered such that a status earlier in the list will
4# override a status later in a list (e.g. ERROR during a test will override
5# prior GOOD results, but WARN will not override a FAIL)
6job_statuses = ["TEST_NA", "ABORT", "ERROR", "FAIL", "WARN", "GOOD", "ALERT",
7                "RUNNING", "NOSTATUS"]
8
9def is_valid_status(status):
10    if not re.match(r'(START|INFO|(END )?(' + '|'.join(job_statuses) + '))$',
11                    status):
12        return False
13    else:
14        return True
15
16
17def is_failure(status):
18    if not is_valid_status(status):
19        return False
20    if status in ('START', 'INFO'):
21        return False
22    if status.startswith('END '):
23        status = status[len('END '):]
24    return job_statuses.index(status) <= job_statuses.index("FAIL")
25
26
27def record(fn):
28    """
29    Generic method decorator for logging calls under the
30    assumption that return=GOOD, exception=FAIL. The method
31    determines parameters as:
32            subdir = self.subdir if it exists, or None
33            operation = "class name"."method name"
34            status = None on GOOD, str(exception) on FAIL
35    The object using this method must have a job attribute
36    for the logging to actually occur, otherwise the logging
37    will silently fail.
38
39    Logging can explicitly be disabled for a call by passing
40    a logged=False parameter
41    """
42    def recorded_func(self, *args, **dargs):
43        logged = dargs.pop('logged', True)
44        job = getattr(self, 'job', None)
45        # if logging is disabled/unavailable, just
46        # call the method
47        if not logged or job is None:
48            return fn(self, *args, **dargs)
49        # logging is available, so wrap the method call
50        # in success/failure logging
51        subdir = getattr(self, 'subdir', None)
52        operation = '%s.%s' % (self.__class__.__name__,
53                               fn.__name__)
54        try:
55            result = fn(self, *args, **dargs)
56            job.record('GOOD', subdir, operation)
57        except Exception, detail:
58            job.record('FAIL', subdir, operation, str(detail))
59            raise
60        return result
61    return recorded_func
62
63
64def log_and_ignore_errors(msg):
65    """ A decorator for wrapping functions in a 'log exception and ignore'
66    try-except block. """
67    def decorator(fn):
68        def decorated_func(*args, **dargs):
69            try:
70                fn(*args, **dargs)
71            except Exception:
72                print >> sys.stderr, msg
73                traceback.print_exc(file=sys.stderr)
74        return decorated_func
75    return decorator
76