Completed
Push — develop ( f586da...fbd670 )
by Jace
02:29
created

launch_report()   A

Complexity

Conditions 4

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
c 1
b 0
f 0
dl 0
loc 12
ccs 4
cts 8
cp 0.5
crap 6
rs 9.2
1
"""Plugins to extract coverage data from various formats."""
2
3 1
import os
4 1
from abc import ABCMeta, abstractmethod
5 1
import time
6 1
import webbrowser
7 1
import logging
8
9 1
import coverage
10 1
from six import with_metaclass
11
12 1
from .cache import Cache
13
14
15 1
log = logging.getLogger(__name__)
16 1
cache = Cache()
17
18
19
class BasePlugin(with_metaclass(ABCMeta)):  # pragma: no cover (abstract class)
0 ignored issues
show
Complexity introduced by
This abstract class seems to be used only once.

Abstract classes which are used only once can usually be inlined into the class which already uses this abstract class.

Loading history...
20
    """Base class for coverage plugins."""
21
22
    @abstractmethod
23
    def matches(self, cwd):
24
        """Determine if the current directory contains coverage data.
25
26
        :return bool: Indicates that the current directory should be processed.
27
28
        """
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
38
    @abstractmethod
39
    def get_report(self, cwd):
40
        """Get the path to the coverage report.
41
42
        :return str: Path to coverage report or `None` if not available.
43
44
        """
45
46
47 1
def get_coverage(cwd=None):
48
    """Extract the current coverage data."""
49 1
    cwd = cwd or os.getcwd()
50
51 1
    plugin = _find_plugin(cwd)
52 1
    percentage = plugin.get_coverage(cwd)
53
54 1
    return round(percentage, 1)
55
56
57 1
def launch_report(cwd=None):
58
    """Open the generated coverage report in a web browser."""
59 1
    cwd = cwd or os.getcwd()
60
61 1
    plugin = _find_plugin(cwd, allow_missing=True)
62
63 1
    if plugin:
64
        path = plugin.get_report(cwd)
65
66
        if path and not _launched_recently(path):
67
            log.info("Launching report: %s", path)
68
            webbrowser.open("file://" + path, new=2, autoraise=True)
69
70
71 1
def _find_plugin(cwd, allow_missing=False):
72
    """Find an return a matching coverage plugin."""
73 1
    for cls in BasePlugin.__subclasses__():  # pylint: disable=no-member
74 1
        plugin = cls()
75 1
        if plugin.matches(cwd):
76 1
            return plugin
77
78 1
    msg = "No coverage data found: {}".format(cwd)
79 1
    log.info(msg)
80
81 1
    if allow_missing:
82 1
        return None
83
84
    raise RuntimeError(msg + '.')
85
86
87 1
def _launched_recently(path):
88 1
    now = time.time()
89 1
    then = cache.get(path, default=0)
90 1
    elapsed = now - then
91 1
    log.debug("Last launched %s seconds ago", elapsed)
92 1
    cache.set(path, now)
93 1
    return elapsed < 60 * 60  # 1 hour
94
95
96 1
class CoveragePy(BasePlugin):
97
    """Coverage extractor for the coverage.py format."""
98
99 1
    def matches(self, cwd):
100 1
        return '.coverage' in os.listdir(cwd)
101
102 1
    def get_coverage(self, cwd):
103 1
        os.chdir(cwd)
104
105 1
        cov = coverage.Coverage()
0 ignored issues
show
Bug introduced by
The Module coverage does not seem to have a member named Coverage.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
106 1
        cov.load()
107
108 1
        with open(os.devnull, 'w') as ignore:
109 1
            total = cov.report(file=ignore)
110
111 1
        return total
112
113 1
    def get_report(self, cwd):
114
        path = os.path.join(cwd, 'htmlcov', 'index.html')
115
116
        if os.path.isfile(path):
117
            log.info("Found coverage report: %s", path)
118
            return path
119
120
        log.info("No coverage report found: %s", cwd)
121
        return None
122