Skip to content

Commit bb84cc9

Browse files
authored
Support coveragepy 5 (alpha) (#54)
1 parent 0fc6e53 commit bb84cc9

File tree

8 files changed

+104
-25
lines changed

8 files changed

+104
-25
lines changed

.circleci/config.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ common: &common
3333
set +x
3434
fi
3535
jobs:
36+
py37-coveragepy5:
37+
<<: *common
38+
docker:
39+
- image: circleci/python:3.7
40+
environment:
41+
TOXENV=py37-coveragepy5-coverage
3642
py37:
3743
<<: *common
3844
docker:
@@ -80,6 +86,7 @@ workflows:
8086
version: 2
8187
test:
8288
jobs:
89+
- py37-coveragepy5
8390
- py37
8491
- py37-click6
8592
- py36

covimerage/__init__.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,14 +186,25 @@ def get_coveragepy_data(self):
186186

187187
# TODO: move to CoverageWrapper
188188
def write_coveragepy_data(self, data_file='.coverage'):
189+
import coverage
190+
189191
cov_data = self.get_coveragepy_data()
190-
if not cov_data.line_counts():
192+
try:
193+
line_counts = cov_data.line_counts()
194+
except AttributeError:
195+
line_counts = coverage.data.line_counts(cov_data)
196+
if not line_counts:
191197
logger.warning('Not writing coverage file: no data to report!')
192198
return False
193199

194200
if isinstance(data_file, string_types):
195201
logger.info('Writing coverage file %s.', data_file)
196-
cov_data.write_file(data_file)
202+
try:
203+
write_file = cov_data.write_file
204+
except AttributeError:
205+
# coveragepy 5
206+
write_file = cov_data._write_file
207+
write_file(data_file)
197208
else:
198209
try:
199210
filename = data_file.name

covimerage/coveragepy.py

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,42 @@
1313
r'"\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(cover|COVER)')
1414

1515

16+
try:
17+
from coverage.data import CoverageJsonData as CoveragePyData
18+
except ImportError:
19+
from coverage.data import CoverageData as CoveragePyData
20+
21+
1622
@attr.s(frozen=True)
1723
class CoverageData(object):
1824
cov_data = attr.ib(default=None)
1925
data_file = attr.ib(default=None)
2026

2127
def __attrs_post_init__(self):
22-
if self.cov_data and self.data_file:
23-
raise TypeError('data and data_file are mutually exclusive.')
24-
if self.cov_data:
25-
if not isinstance(self.cov_data, coverage.data.CoverageData):
26-
raise TypeError(
27-
'data needs to be of type coverage.data.CoverageData')
28+
if self.cov_data is not None:
29+
if not isinstance(self.cov_data, CoveragePyData):
30+
raise TypeError('data needs to be of type %s.%s' % (
31+
CoveragePyData.__module__,
32+
CoveragePyData.__name__,))
33+
if self.data_file is not None:
34+
raise TypeError('data and data_file are mutually exclusive.')
2835
return
29-
cov_data = coverage.data.CoverageData()
36+
cov_data = CoveragePyData()
3037
if self.data_file:
3138
fname, fobj, fstr = get_fname_and_fobj_and_str(self.data_file)
3239
try:
3340
if fobj:
34-
cov_data.read_fileobj(fobj)
41+
try:
42+
read_fileobj = cov_data.read_fileobj
43+
except AttributeError: # made private in coveragepy 5
44+
read_fileobj = cov_data._read_fileobj
45+
read_fileobj(fobj)
3546
else:
36-
cov_data.read_file(fname)
47+
try:
48+
read_file = cov_data.read_file
49+
except AttributeError: # made private in coveragepy 5
50+
read_file = cov_data._read_file
51+
read_file(fname)
3752
except coverage.CoverageException as exc:
3853
raise CoverageWrapperException(
3954
'Coverage could not read data_file: %s' % fstr,
@@ -73,6 +88,8 @@ def __attrs_post_init__(self):
7388
if not isinstance(self.data, CoverageData):
7489
data = CoverageData(cov_data=self.data, data_file=self.data_file)
7590
object.__setattr__(self, 'data', data)
91+
# (confusing to have it twice)
92+
# object.__setattr__(self, 'data_file', None)
7693
elif self.data_file:
7794
raise TypeError('data and data_file are mutually exclusive.')
7895

@@ -96,7 +113,12 @@ def _get_file_reporter(self, morf):
96113
config_file=True if self.config_file is None else self.config_file,
97114
)
98115
cov_coverage._init()
99-
cov_coverage.data = self.data.cov_data
116+
if hasattr(cov_coverage, '_data'):
117+
# coveragepy 5
118+
# TODO: get rid of intermediate handling of CoverageData?
119+
cov_coverage._data = self.data.cov_data
120+
else:
121+
cov_coverage.data = self.data.cov_data
100122
return cov_coverage
101123

102124
@handle_coverage_exceptions

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def run(self):
8484
install_requires=[
8585
'attrs',
8686
'click',
87-
'coverage<=5',
87+
'coverage',
8888
],
8989
extras_require={
9090
'testing': DEPS_TESTING,

tests/test_cli.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ def test_coverage_plugin_for_annotate_merged_conditionals(runner, capfd,
410410
f.write('[run]\nplugins = covimerage')
411411

412412
exit_code = call(['env', 'COVERAGE_FILE=%s' % tmpfile,
413+
'COVERAGE_STORAGE=json', # for coveragepy 5
413414
'coverage', 'annotate', '--rcfile', coveragerc,
414415
'--directory', str(tmpdir)])
415416
out, err = capfd.readouterr()

tests/test_coveragepy.py

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,20 +60,32 @@ def coverage_fileobj():
6060

6161
def test_coveragedata(coverage_fileobj):
6262
import coverage
63-
from covimerage.coveragepy import CoverageData, CoverageWrapperException
63+
from covimerage.coveragepy import (
64+
CoverageData, CoveragePyData, CoverageWrapperException)
6465

6566
with pytest.raises(TypeError) as excinfo:
66-
CoverageData(data_file='foo', cov_data='bar')
67+
CoverageData(data_file='foo', cov_data=CoveragePyData())
6768
assert excinfo.value.args == (
6869
'data and data_file are mutually exclusive.',)
6970

7071
data = CoverageData()
71-
assert isinstance(data.cov_data, coverage.data.CoverageData)
72+
try:
73+
from coverage.data import CoverageJsonData
74+
except ImportError:
75+
assert isinstance(data.cov_data, coverage.data.CoverageData)
76+
else:
77+
assert isinstance(data.cov_data, CoverageJsonData)
7278

7379
with pytest.raises(TypeError) as excinfo:
7480
CoverageData(cov_data='foo')
75-
assert excinfo.value.args == (
76-
'data needs to be of type coverage.data.CoverageData',)
81+
try:
82+
from coverage.data import CoverageJsonData
83+
except ImportError:
84+
assert excinfo.value.args == (
85+
'data needs to be of type coverage.data.CoverageData',)
86+
else:
87+
assert excinfo.value.args == (
88+
'data needs to be of type coverage.data.CoverageJsonData',)
7789

7890
with pytest.raises(CoverageWrapperException) as excinfo:
7991
CoverageData(data_file='/does/not/exist')
@@ -106,29 +118,34 @@ def test_coveragedata_empty(covdata_empty):
106118

107119
f = StringIO()
108120
data = CoverageData()
109-
data.cov_data.write_fileobj(f)
121+
try:
122+
write_fileobj = data.cov_data.write_fileobj
123+
except AttributeError:
124+
# coveragepy 5
125+
write_fileobj = data.cov_data._write_fileobj
126+
write_fileobj(f)
110127
f.seek(0)
111128
assert f.read() == covdata_empty
112129

113130

114131
def test_coveragewrapper(coverage_fileobj, devnull):
115132
import coverage
116133
from covimerage.coveragepy import (
117-
CoverageData, CoverageWrapper, CoverageWrapperException)
134+
CoverageData, CoveragePyData, CoverageWrapper, CoverageWrapperException)
118135

119136
cov_data = CoverageWrapper()
120137
assert cov_data.lines == {}
121138
assert isinstance(cov_data.data, CoverageData)
122139

123-
cov_data = CoverageWrapper(data=coverage.data.CoverageData())
140+
cov_data = CoverageWrapper(data=CoveragePyData())
124141
assert cov_data.lines == {}
125142
assert isinstance(cov_data.data, CoverageData)
126143

127144
with pytest.raises(TypeError):
128145
CoverageWrapper(data_file='foo', data='bar')
129146

130147
with pytest.raises(TypeError):
131-
CoverageWrapper(data_file='foo', data=CoverageData())
148+
CoverageWrapper(data_file='foo', data=CoveragePyData())
132149

133150
cov = CoverageWrapper(data_file=coverage_fileobj)
134151
with pytest.raises(attr.exceptions.FrozenInstanceError):
@@ -139,7 +156,11 @@ def test_coveragewrapper(coverage_fileobj, devnull):
139156
3, 8, 9, 11, 13, 14, 15, 17, 23]}
140157

141158
assert isinstance(cov._cov_obj, coverage.control.Coverage)
142-
assert cov._cov_obj.data is cov.data.cov_data
159+
if hasattr(cov._cov_obj, '_data'):
160+
# coveragepy 5
161+
assert cov._cov_obj._data is cov.data.cov_data
162+
else:
163+
assert cov._cov_obj.data is cov.data.cov_data
143164

144165
with pytest.raises(CoverageWrapperException) as excinfo:
145166
CoverageWrapper(data_file=devnull.name)
@@ -158,6 +179,17 @@ def test_coveragewrapper(coverage_fileobj, devnull):
158179
e.message, e.orig_exc)
159180

160181

182+
def test_coveragewrapper_requires_jsondata():
183+
pytest.importorskip('coverage.sqldata')
184+
from covimerage.coveragepy import CoverageWrapper
185+
186+
with pytest.raises(TypeError) as excinfo:
187+
CoverageWrapper(data=coverage.sqldata.CoverageSqliteData())
188+
189+
assert excinfo.value.args[0] == (
190+
'data needs to be of type coverage.data.CoverageJsonData')
191+
192+
161193
def test_coveragewrapper_uses_config_file(tmpdir, capfd):
162194
from covimerage.coveragepy import CoverageWrapper, CoverageWrapperException
163195

tests/test_main.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,8 +380,12 @@ def test_merged_profiles_get_coveragepy_data():
380380

381381
m = MergedProfiles([])
382382
cov_data = m.get_coveragepy_data()
383-
assert isinstance(cov_data, coverage.CoverageData)
384-
assert repr(cov_data) == '<CoverageData lines={0} arcs=None tracers={0} runs=[0]>'
383+
try:
384+
from coverage.data import CoverageJsonData
385+
except ImportError:
386+
assert isinstance(cov_data, coverage.CoverageData)
387+
else:
388+
assert isinstance(cov_data, CoverageJsonData)
385389

386390

387391
def test_merged_profiles_write_coveragepy_data_handles_fname_and_fobj(

tox.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ changedir =
1313
integration: {envtmpdir}
1414
deps =
1515
click6: click<7
16+
coveragepy4: coverage<5
17+
coveragepy5: coverage>=5<6
1618

1719
[testenv:checkqa]
1820
extras = qa

0 commit comments

Comments
 (0)