Skip to content

Commit

Permalink
PR feedback
Browse files Browse the repository at this point in the history
Signed-off-by: Bernat Gabor <bgabor8@bloomberg.net>
  • Loading branch information
gaborbernat committed Apr 2, 2020
1 parent 6f1c291 commit 100629e
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 92 deletions.
24 changes: 10 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,15 @@ branch = True
source = .
omit =
*/.tox/*
*/.nox/*
*/__main__.py
*/setup.py
*/venv*/*
*/.venv*/*
```

Note: if you enable installed packages the python environment folders
(``.tox`, ``.nox`, ``.venv*``, ``venv*``) might not be added statically, but
instead exploded for folders going into the python virtual environment
to avoid excluding the installed packages path.
Note: if you enable installed packages the python environment folders (`.tox`,
`venv*`) might not be added like here, but instead exploded for folders going
into the python virtual environment to avoid excluding the installed packages
path.

### `[coverage:report]`

Expand Down Expand Up @@ -158,20 +157,20 @@ defaults provided by `covdefaults`).
#### coverage for installed libraries

By default ``covdefaults`` assumes that you want to track coverage
in your local source tree, code that is version controlled. For libraries
it's better idea to detect coverage on your installed version, and map that
back to the source files. You can enable tracking installed libraries via:
in your local source tree, code that is version controlled. To detect
coverage on your installed version, and map that back to the source files.
You can enable tracking installed libraries via:

```ini
[coverage:covdefaults]
installed_libraries = tox:src virtualenv:.
installed_libraries = tox:src virtualenv
```

In this example we say we'll track the installed package/module tox that maps
back to the source folder present at ``src``, and ``virtualenv`` that maps back
to the source folder present at ``.``. Note the installed package/module must be
present in either the ``platlib`` or ``purelib`` path of the virtual environment
as specified by ``distutils`` ``install`` object.
as specified by ``sysconfig``.

```ini
[covdefaults]
Expand All @@ -189,9 +188,6 @@ exclude_lines =
^if MYPY:$
```

this will result in lines matching `^if MYPY:$` to additionally be excluded
from coverage in addition to the defaults provided by `covdefaults`.

#### `report:fail_under`

```ini
Expand Down
117 changes: 52 additions & 65 deletions covdefaults.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import os
import re
import sys
from distutils.dist import Distribution
import sysconfig
from typing import Dict
from typing import List
from typing import Optional
from typing import Set

from coverage import CoveragePlugin
from coverage.config import CoverageConfig
Expand Down Expand Up @@ -68,49 +66,54 @@ def __init__(
self._installed_package = self._parse_inst_pkg(installed_package)

@staticmethod
def _parse_inst_pkg(installed_package: str) -> Dict[str, Optional[str]]:
result: Dict[str, Optional[str]] = {}
for entry in installed_package.split(' '):
def _parse_inst_pkg(installed_package: str) -> Dict[str, str]:
result: Dict[str, str] = {}
for entry in installed_package.split():
if entry:
key, value = entry, None
key, value = entry, '.'
if entry.count(':') == 1:
key, value = entry.split(':')
result[key] = value
return result

def configure(self, config: CoverageConfig) -> None:
self._set_source(config)
for opt_k, opt_v in OPTIONS:
config.set_option(opt_k, opt_v)
for k, v in EXTEND:
before = set(config.get_option(k) or ())
before.update(v)
config.set_option(k, sorted(before))

self.fix_omit(config)

# remove DEFAULT_EXCLUDE, we add a more-strict casing
exclude = set(config.get_option('report:exclude_lines'))
exclude.difference_update(DEFAULT_EXCLUDE)
config.set_option('report:exclude_lines', sorted(exclude))

# fail_under: if they specify a value then honor it
if not config.get_option('report:fail_under'):
config.set_option('report:fail_under', 100)
def _set_source(self, config: CoverageConfig) -> None:
if not isinstance(config, CoverageConfig): # pragma: no cover
# https://github.com/nedbat/coveragepy/issues/967
config = config.config
source = []
if self._installed_package:
for path in {
sysconfig.get_paths()['platlib'],
sysconfig.get_paths()['purelib'],
}:
for pkg, dest in self._installed_package.items():
base = os.path.join(path, pkg)
for suffix in ('', '.py'):
at = f'{base}{suffix}'
if os.path.exists(at):
source.append(at)
if dest is not None:
if os.path.isfile(at):
src_path = dest
else:
src_path = os.path.join(dest, pkg)
config.paths[pkg] = [src_path, at]
# set paths to map to cwd
# https://coverage.readthedocs.io/en/latest/config.html#paths
source.append(os.getcwd())
config.set_option('run:source', source)

def fix_omit(self, config: CoverageConfig) -> None:
# subtract omit settings if requested
def _fix_omit(self, config: CoverageConfig) -> None:
omit = set(config.get_option('run:omit') or [])
omit.add('*/setup.py') # always ignore setup.py
omit.add('*/__main__.py') # always ignore __main__.py

# ignore common virtual environment folders, unless it's within source
source = config.get_option('run:source')
sep, suf = os.sep, ['.py', '.pyc', '.pyo']
sep = os.sep
for folder, is_prefix in (
('.tox', False),
('.nox', False),
('venv', True),
('.venv', True),
):
matcher = re.compile(f'.*{re.escape(f"{sep}{folder}{sep}")}.*')
for src in source:
Expand All @@ -122,13 +125,13 @@ def fix_omit(self, config: CoverageConfig) -> None:
level = sep.join(parts[:at])
child = parts[at]
for entry in os.listdir(level):
if entry == child: # dont' exclude children
if entry == child: # don't exclude children
continue
pattern = f'*/{level}/{entry}'
if os.path.isdir(os.path.join(level, entry)):
pattern += '/*'
else:
if not any(entry.endswith(e) for e in suf):
if not entry.endswith('.pyc'):
continue
omit.add(pattern)
break
Expand All @@ -138,40 +141,24 @@ def fix_omit(self, config: CoverageConfig) -> None:
omit.difference_update(self._subtract_omit)
config.set_option('run:omit', sorted(omit))

def _set_source(self, config: CoverageConfig) -> None:
if not isinstance(config, CoverageConfig): # pragma: no cover
# https://github.com/nedbat/coveragepy/issues/967
config = config.config
source = []
if self._installed_package:
for path in _get_install_paths():
for pkg, dest in self._installed_package.items():
base = os.path.join(path, pkg)
for suffix in ('', '.py'):
at = f'{base}{suffix}'
if os.path.exists(at):
source.append(at)
if dest is not None:
src_path = os.path.join(dest, pkg)
config.paths[pkg] = [src_path, at]
# set paths to map to cwd
# https://coverage.readthedocs.io/en/latest/config.html#paths
source.append(os.getcwd())
config.set_option('run:source', source)
def configure(self, config: CoverageConfig) -> None:
self._set_source(config)
for opt_k, opt_v in OPTIONS:
config.set_option(opt_k, opt_v)
for k, v in EXTEND:
before = set(config.get_option(k) or ())
before.update(v)
config.set_option(k, sorted(before))
self._fix_omit(config)

# remove DEFAULT_EXCLUDE, we add a more-strict casing
exclude = set(config.get_option('report:exclude_lines'))
exclude.difference_update(DEFAULT_EXCLUDE)
config.set_option('report:exclude_lines', sorted(exclude))

def _get_install_paths() -> Set[str]:
# follow what virtualenv uses
distribution = Distribution({'script_args': '--no-user-cfg'})
# disable macOS static paths for framework
if hasattr(sys, '_framework'): # pragma: no cover
sys._framework = None # type:ignore # pragma: no cover
install = distribution.get_command_obj('install', create=True)
if install is None: # pragma: no cover
return set() # pragma: no cover
install.prefix = sys.prefix # type:ignore
install.finalize_options()
return {install.install_platlib, install.install_purelib} # type:ignore
# fail_under: if they specify a value then honor it
if not config.get_option('report:fail_under'):
config.set_option('report:fail_under', 100)


def coverage_init(reg: Plugins, options: Dict[str, str]) -> None:
Expand Down
34 changes: 21 additions & 13 deletions tests/covdefaults_test.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import importlib
import os
import re
from pathlib import Path

import pytest
from coverage.config import CoverageConfig
Expand Down Expand Up @@ -46,9 +45,8 @@ def test_extends_existing_omit():
cfg.set_option('run:omit', ['pre_commit/resources/*'])
configure(cfg)
assert cfg.get_option('run:omit') == [
'*/.nox/*',
'*/.tox/*',
'*/.venv*/*',
'*/__main__.py',
'*/setup.py',
'*/venv*/*',
'pre_commit/resources/*',
Expand All @@ -59,8 +57,7 @@ def test_subtract_omit():
cfg = CoverageConfig()
covdefaults.CovDefaults(subtract_omit='*/.tox/*').configure(cfg)
assert cfg.get_option('run:omit') == [
'*/.nox/*',
'*/.venv*/*',
'*/__main__.py',
'*/setup.py',
'*/venv*/*',
]
Expand Down Expand Up @@ -127,21 +124,32 @@ def test_fix_coverage():


def test_installed_package():
import easy_install # module example
import coverage # package example
import easy_install # module example - map
import setuptools # pkg example - map
import coverage # package example - no map

cfg = CoverageConfig()
c = covdefaults.CovDefaults(installed_package='easy_install:. coverage')
value = 'easy_install:src setuptools:src coverage'
c = covdefaults.CovDefaults(installed_package=value)
c.configure(cfg)

assert dict(cfg.paths) == {
'easy_install': [
'./easy_install',
str(Path(easy_install.__file__).with_suffix('.py')),
'src',
easy_install.__file__,
],
'setuptools': [
'src/setuptools',
os.path.dirname(setuptools.__file__),
],
'coverage': [
'./coverage',
os.path.dirname(coverage.__file__),
],
}
assert cfg.source == [
str(Path(easy_install.__file__)),
str(Path(coverage.__file__).parent),
str(Path.cwd()),
easy_install.__file__,
os.path.dirname(setuptools.__file__),
os.path.dirname(coverage.__file__),
os.getcwd(),
]

0 comments on commit 100629e

Please sign in to comment.