|
1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 |
|
2 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt |
|
3 |
|
4 """Determine contexts for coverage.py""" |
|
5 |
|
6 |
|
7 def combine_context_switchers(context_switchers): |
|
8 """Create a single context switcher from multiple switchers. |
|
9 |
|
10 `context_switchers` is a list of functions that take a frame as an |
|
11 argument and return a string to use as the new context label. |
|
12 |
|
13 Returns a function that composites `context_switchers` functions, or None |
|
14 if `context_switchers` is an empty list. |
|
15 |
|
16 When invoked, the combined switcher calls `context_switchers` one-by-one |
|
17 until a string is returned. The combined switcher returns None if all |
|
18 `context_switchers` return None. |
|
19 """ |
|
20 if not context_switchers: |
|
21 return None |
|
22 |
|
23 if len(context_switchers) == 1: |
|
24 return context_switchers[0] |
|
25 |
|
26 def should_start_context(frame): |
|
27 """The combiner for multiple context switchers.""" |
|
28 for switcher in context_switchers: |
|
29 new_context = switcher(frame) |
|
30 if new_context is not None: |
|
31 return new_context |
|
32 return None |
|
33 |
|
34 return should_start_context |
|
35 |
|
36 |
|
37 def should_start_context_test_function(frame): |
|
38 """Is this frame calling a test_* function?""" |
|
39 co_name = frame.f_code.co_name |
|
40 if co_name.startswith("test") or co_name == "runTest": |
|
41 return qualname_from_frame(frame) |
|
42 return None |
|
43 |
|
44 |
|
45 def qualname_from_frame(frame): |
|
46 """Get a qualified name for the code running in `frame`.""" |
|
47 co = frame.f_code |
|
48 fname = co.co_name |
|
49 method = None |
|
50 if co.co_argcount and co.co_varnames[0] == "self": |
|
51 self = frame.f_locals["self"] |
|
52 method = getattr(self, fname, None) |
|
53 |
|
54 if method is None: |
|
55 func = frame.f_globals.get(fname) |
|
56 if func is None: |
|
57 return None |
|
58 return func.__module__ + '.' + fname |
|
59 |
|
60 func = getattr(method, '__func__', None) |
|
61 if func is None: |
|
62 cls = self.__class__ |
|
63 return cls.__module__ + '.' + cls.__name__ + "." + fname |
|
64 |
|
65 if hasattr(func, '__qualname__'): |
|
66 qname = func.__module__ + '.' + func.__qualname__ |
|
67 else: |
|
68 for cls in getattr(self.__class__, '__mro__', ()): |
|
69 f = cls.__dict__.get(fname, None) |
|
70 if f is None: |
|
71 continue |
|
72 if f is func: |
|
73 qname = cls.__module__ + '.' + cls.__name__ + "." + fname |
|
74 break |
|
75 else: |
|
76 # Support for old-style classes. |
|
77 def mro(bases): |
|
78 for base in bases: |
|
79 f = base.__dict__.get(fname, None) |
|
80 if f is func: |
|
81 return base.__module__ + '.' + base.__name__ + "." + fname |
|
82 for base in bases: |
|
83 qname = mro(base.__bases__) |
|
84 if qname is not None: |
|
85 return qname |
|
86 return None |
|
87 qname = mro([self.__class__]) |
|
88 if qname is None: |
|
89 qname = func.__module__ + '.' + fname |
|
90 |
|
91 return qname |