Skip to content

Commit

Permalink
Support coveragepy 5 (alpha) (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
blueyed authored Oct 23, 2018
1 parent 0fc6e53 commit bb84cc9
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 25 deletions.
7 changes: 7 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ common: &common
set +x
fi
jobs:
py37-coveragepy5:
<<: *common
docker:
- image: circleci/python:3.7
environment:
TOXENV=py37-coveragepy5-coverage
py37:
<<: *common
docker:
Expand Down Expand Up @@ -80,6 +86,7 @@ workflows:
version: 2
test:
jobs:
- py37-coveragepy5
- py37
- py37-click6
- py36
Expand Down
15 changes: 13 additions & 2 deletions covimerage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,14 +186,25 @@ def get_coveragepy_data(self):

# TODO: move to CoverageWrapper
def write_coveragepy_data(self, data_file='.coverage'):
import coverage

cov_data = self.get_coveragepy_data()
if not cov_data.line_counts():
try:
line_counts = cov_data.line_counts()
except AttributeError:
line_counts = coverage.data.line_counts(cov_data)
if not line_counts:
logger.warning('Not writing coverage file: no data to report!')
return False

if isinstance(data_file, string_types):
logger.info('Writing coverage file %s.', data_file)
cov_data.write_file(data_file)
try:
write_file = cov_data.write_file
except AttributeError:
# coveragepy 5
write_file = cov_data._write_file
write_file(data_file)
else:
try:
filename = data_file.name
Expand Down
42 changes: 32 additions & 10 deletions covimerage/coveragepy.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,42 @@
r'"\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(cover|COVER)')


try:
from coverage.data import CoverageJsonData as CoveragePyData
except ImportError:
from coverage.data import CoverageData as CoveragePyData


@attr.s(frozen=True)
class CoverageData(object):
cov_data = attr.ib(default=None)
data_file = attr.ib(default=None)

def __attrs_post_init__(self):
if self.cov_data and self.data_file:
raise TypeError('data and data_file are mutually exclusive.')
if self.cov_data:
if not isinstance(self.cov_data, coverage.data.CoverageData):
raise TypeError(
'data needs to be of type coverage.data.CoverageData')
if self.cov_data is not None:
if not isinstance(self.cov_data, CoveragePyData):
raise TypeError('data needs to be of type %s.%s' % (
CoveragePyData.__module__,
CoveragePyData.__name__,))
if self.data_file is not None:
raise TypeError('data and data_file are mutually exclusive.')
return
cov_data = coverage.data.CoverageData()
cov_data = CoveragePyData()
if self.data_file:
fname, fobj, fstr = get_fname_and_fobj_and_str(self.data_file)
try:
if fobj:
cov_data.read_fileobj(fobj)
try:
read_fileobj = cov_data.read_fileobj
except AttributeError: # made private in coveragepy 5
read_fileobj = cov_data._read_fileobj
read_fileobj(fobj)
else:
cov_data.read_file(fname)
try:
read_file = cov_data.read_file
except AttributeError: # made private in coveragepy 5
read_file = cov_data._read_file
read_file(fname)
except coverage.CoverageException as exc:
raise CoverageWrapperException(
'Coverage could not read data_file: %s' % fstr,
Expand Down Expand Up @@ -73,6 +88,8 @@ def __attrs_post_init__(self):
if not isinstance(self.data, CoverageData):
data = CoverageData(cov_data=self.data, data_file=self.data_file)
object.__setattr__(self, 'data', data)
# (confusing to have it twice)
# object.__setattr__(self, 'data_file', None)
elif self.data_file:
raise TypeError('data and data_file are mutually exclusive.')

Expand All @@ -96,7 +113,12 @@ def _get_file_reporter(self, morf):
config_file=True if self.config_file is None else self.config_file,
)
cov_coverage._init()
cov_coverage.data = self.data.cov_data
if hasattr(cov_coverage, '_data'):
# coveragepy 5
# TODO: get rid of intermediate handling of CoverageData?
cov_coverage._data = self.data.cov_data
else:
cov_coverage.data = self.data.cov_data
return cov_coverage

@handle_coverage_exceptions
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def run(self):
install_requires=[
'attrs',
'click',
'coverage<=5',
'coverage',
],
extras_require={
'testing': DEPS_TESTING,
Expand Down
1 change: 1 addition & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ def test_coverage_plugin_for_annotate_merged_conditionals(runner, capfd,
f.write('[run]\nplugins = covimerage')

exit_code = call(['env', 'COVERAGE_FILE=%s' % tmpfile,
'COVERAGE_STORAGE=json', # for coveragepy 5
'coverage', 'annotate', '--rcfile', coveragerc,
'--directory', str(tmpdir)])
out, err = capfd.readouterr()
Expand Down
52 changes: 42 additions & 10 deletions tests/test_coveragepy.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,20 +60,32 @@ def coverage_fileobj():

def test_coveragedata(coverage_fileobj):
import coverage
from covimerage.coveragepy import CoverageData, CoverageWrapperException
from covimerage.coveragepy import (
CoverageData, CoveragePyData, CoverageWrapperException)

with pytest.raises(TypeError) as excinfo:
CoverageData(data_file='foo', cov_data='bar')
CoverageData(data_file='foo', cov_data=CoveragePyData())
assert excinfo.value.args == (
'data and data_file are mutually exclusive.',)

data = CoverageData()
assert isinstance(data.cov_data, coverage.data.CoverageData)
try:
from coverage.data import CoverageJsonData
except ImportError:
assert isinstance(data.cov_data, coverage.data.CoverageData)
else:
assert isinstance(data.cov_data, CoverageJsonData)

with pytest.raises(TypeError) as excinfo:
CoverageData(cov_data='foo')
assert excinfo.value.args == (
'data needs to be of type coverage.data.CoverageData',)
try:
from coverage.data import CoverageJsonData
except ImportError:
assert excinfo.value.args == (
'data needs to be of type coverage.data.CoverageData',)
else:
assert excinfo.value.args == (
'data needs to be of type coverage.data.CoverageJsonData',)

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

f = StringIO()
data = CoverageData()
data.cov_data.write_fileobj(f)
try:
write_fileobj = data.cov_data.write_fileobj
except AttributeError:
# coveragepy 5
write_fileobj = data.cov_data._write_fileobj
write_fileobj(f)
f.seek(0)
assert f.read() == covdata_empty


def test_coveragewrapper(coverage_fileobj, devnull):
import coverage
from covimerage.coveragepy import (
CoverageData, CoverageWrapper, CoverageWrapperException)
CoverageData, CoveragePyData, CoverageWrapper, CoverageWrapperException)

cov_data = CoverageWrapper()
assert cov_data.lines == {}
assert isinstance(cov_data.data, CoverageData)

cov_data = CoverageWrapper(data=coverage.data.CoverageData())
cov_data = CoverageWrapper(data=CoveragePyData())
assert cov_data.lines == {}
assert isinstance(cov_data.data, CoverageData)

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

with pytest.raises(TypeError):
CoverageWrapper(data_file='foo', data=CoverageData())
CoverageWrapper(data_file='foo', data=CoveragePyData())

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

assert isinstance(cov._cov_obj, coverage.control.Coverage)
assert cov._cov_obj.data is cov.data.cov_data
if hasattr(cov._cov_obj, '_data'):
# coveragepy 5
assert cov._cov_obj._data is cov.data.cov_data
else:
assert cov._cov_obj.data is cov.data.cov_data

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


def test_coveragewrapper_requires_jsondata():
pytest.importorskip('coverage.sqldata')
from covimerage.coveragepy import CoverageWrapper

with pytest.raises(TypeError) as excinfo:
CoverageWrapper(data=coverage.sqldata.CoverageSqliteData())

assert excinfo.value.args[0] == (
'data needs to be of type coverage.data.CoverageJsonData')


def test_coveragewrapper_uses_config_file(tmpdir, capfd):
from covimerage.coveragepy import CoverageWrapper, CoverageWrapperException

Expand Down
8 changes: 6 additions & 2 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,12 @@ def test_merged_profiles_get_coveragepy_data():

m = MergedProfiles([])
cov_data = m.get_coveragepy_data()
assert isinstance(cov_data, coverage.CoverageData)
assert repr(cov_data) == '<CoverageData lines={0} arcs=None tracers={0} runs=[0]>'
try:
from coverage.data import CoverageJsonData
except ImportError:
assert isinstance(cov_data, coverage.CoverageData)
else:
assert isinstance(cov_data, CoverageJsonData)


def test_merged_profiles_write_coveragepy_data_handles_fname_and_fobj(
Expand Down
2 changes: 2 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ changedir =
integration: {envtmpdir}
deps =
click6: click<7
coveragepy4: coverage<5
coveragepy5: coverage>=5<6

[testenv:checkqa]
extras = qa
Expand Down

0 comments on commit bb84cc9

Please sign in to comment.