coveragespace.plugins.BasePlugin.get_report()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 2
dl 0
loc 8
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
"""Plugins to extract coverage data from various formats."""
2
3 1
4 1
import os
5 1
import time
6 1
import webbrowser
7 1
from abc import ABC, abstractmethod
8
9 1
import coverage
10 1
import log
11
12 1
from .cache import Cache
13
14
15 1
cache = Cache()
16 1
17
18
class BasePlugin(ABC):  # pylint: disable=no-init
19
    """Base class for coverage plugins."""
20
21
    @abstractmethod
22
    def matches(self, cwd):
23
        """Determine if the current directory contains coverage data.
24
25
        :return bool: Indicates that the current directory should be processed.
26
27
        """
28
        raise NotImplementedError
29
30
    @abstractmethod
31
    def get_coverage(self, cwd):
32
        """Extract the coverage data from the current directory.
33
34
        :return float: Percentage of lines covered.
35
36
        """
37
        raise NotImplementedError
38
39
    @abstractmethod
40
    def get_report(self, cwd):
41
        """Get the path to the coverage report.
42
43
        :return str: Path to coverage report or `None` if not available.
44
45
        """
46
        raise NotImplementedError
47 1
48
49 1
def get_coverage(cwd=None):
50
    """Extract the current coverage data."""
51 1
    cwd = cwd or os.getcwd()
52 1
53
    plugin = _find_plugin(cwd)
54 1
    percentage = plugin.get_coverage(cwd)
55
56
    return round(percentage, 1)
57 1
58
59 1
def launch_report(cwd=None):
60
    """Open the generated coverage report in a web browser."""
61 1
    cwd = cwd or os.getcwd()
62
63 1
    plugin = _find_plugin(cwd, allow_missing=True)
64
65
    if plugin:
66
        path = plugin.get_report(cwd)
67
68
        if path and not _launched_recently(path):
69
            log.info("Launching report: %s", path)
70
            webbrowser.open("file://" + path, new=2, autoraise=True)
71 1
72
73 1
def _find_plugin(cwd, allow_missing=False):
74 1
    """Find an return a matching coverage plugin."""
75 1
    for cls in BasePlugin.__subclasses__():  # pylint: disable=no-member
76 1
        plugin = cls()  # type: ignore
77
        if plugin.matches(cwd):
78 1
            return plugin
79 1
80
    msg = "No coverage data found: {}".format(cwd)
81 1
    log.info(msg)
82 1
83
    if allow_missing:
84
        return None
85
86
    raise RuntimeError(msg)
87 1
88 1
89 1
def _launched_recently(path):
90 1
    now = time.time()
91 1
    then = cache.get(path, default=0)
92 1
    elapsed = now - then
93 1
    log.debug("Last launched %s seconds ago", elapsed)
94
    cache.set(path, now)
95
    return elapsed < 60 * 60  # 1 hour
96 1
97
98
class CoveragePy(BasePlugin):  # pylint: disable=no-init
99 1
    """Coverage extractor for the coverage.py format."""
100 1
101
    def matches(self, cwd):
102 1
        return any(('.coverage' in os.listdir(cwd), '.coveragerc' in os.listdir(cwd)))
103 1
104
    def get_coverage(self, cwd):
105 1
        os.chdir(cwd)
106 1
107
        cov = coverage.Coverage()
108 1
        cov.load()
109 1
110
        with open(os.devnull, 'w') as ignore:
111 1
            total = cov.report(file=ignore)
112
113 1
        return total
114
115
    def get_report(self, cwd):
116
        path = os.path.join(cwd, 'htmlcov', 'index.html')
117
118
        if os.path.isfile(path):
119
            log.info("Found coverage report: %s", path)
120
            return path
121
122
        log.info("No coverage report found: %s", cwd)
123
        return None
124