Completed
Push — develop ( 395b20...45cb19 )
by Jace
08:12
created

CoveragePy.get_report()   A

Complexity

Conditions 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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