summaryrefslogtreecommitdiff
path: root/coverage/context.py
blob: 9ef680a3b87f9bda081ebedb0cfbdabe9499f045 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt

"""Determine contexts for coverage.py"""


def combine_context_switchers(context_switchers):
    """Create a single context switcher from multiple switchers.

    `context_switchers` is a list of functions that take a frame as an
    argument and return a string to use as the new context label.

    Returns a function that composites `context_switchers` functions, or None
    if `context_switchers` is an empty list.

    When invoked, the combined switcher calls `context_switchers` one-by-one
    until a string is returned.  The combined switcher returns None if all
    `context_switchers` return None.
    """
    if not context_switchers:
        return None

    if len(context_switchers) == 1:
        return context_switchers[0]

    def should_start_context(frame):
        """The combiner for multiple context switchers."""
        for switcher in context_switchers:
            new_context = switcher(frame)
            if new_context is not None:
                return new_context
        return None

    return should_start_context


def should_start_context_test_function(frame):
    """Is this frame calling a test_* function?"""
    if frame.f_code.co_name.startswith("test"):
        return qualname_from_frame(frame)
    return None


def qualname_from_frame(frame):
    """Get a qualified name for the code running in `frame`."""
    co = frame.f_code
    fname = co.co_name
    if not co.co_varnames:
        func = frame.f_globals[fname]
        return func.__module__ + '.' + fname

    first_arg = co.co_varnames[0]
    if co.co_argcount and first_arg == "self":
        self = frame.f_locals["self"]
    else:
        func = frame.f_globals[fname]
        return func.__module__ + '.' + fname

    method = getattr(self, fname, None)
    if method is None:
        func = frame.f_globals[fname]
        return func.__module__ + '.' + fname

    func = getattr(method, '__func__', None)
    if func is None:
        cls = self.__class__
        return cls.__module__ + '.' + cls.__name__ + "." + fname

    if hasattr(func, '__qualname__'):
        qname = func.__module__ + '.' + func.__qualname__
    else:
        for cls in getattr(self.__class__, '__mro__', ()):
            f = cls.__dict__.get(fname, None)
            if f is None:
                continue
            if f is func:
                qname = cls.__module__ + '.' + cls.__name__ + "." + fname
                break
        else:
            qname = func.__module__ + '.' + fname
    return qname