Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for omitting entire modules #221

Merged
merged 5 commits into from
Jun 2, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ plugins =
coverage_conditional_plugin

[coverage:coverage_conditional_plugin]
# Here we specify files to conditionally omit:
omit =
"sys_platform == 'win32'": "my_project/omit*.py"
# Here we specify our pragma rules:
rules =
"sys_version_info >= (3, 8)": py-gte-38
Expand All @@ -51,6 +54,10 @@ Or to your `pyproject.toml`:
# Here we specify plugins for coverage to be used:
plugins = ["coverage_conditional_plugin"]

[tool.coverage.coverage_conditional_plugin.omit]
# Here we specify files to conditionally omit:
"my_project/omit*.py" = "sys_platform == 'win32'"

[tool.coverage.coverage_conditional_plugin.rules]
# Here we specify our pragma rules:
py-gte-38 = "sys_version_info >= (3, 8)"
Expand Down Expand Up @@ -128,6 +135,27 @@ get_env_info()
```


## Writing omits

Omits allow entire files to be conditionally omitted from coverage measurement.

The INI format for omits is:

```ini
"pragma-condition": "file-name-pattern"
```

The TOML format for omits is:

```toml
[tool.coverage.coverage_conditional_plugin.omit]
"file-name-pattern" = "pragma-condition"
```

File name patterns should follow coverage.py's `[run] omit` syntax.
See [coverage.py](https://coverage.readthedocs.io/en/stable/source.html).


## License

[MIT](https://github.com/wemake.services/coverage-conditional-plugin/blob/master/LICENSE)
42 changes: 40 additions & 2 deletions coverage_conditional_plugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import sys
import traceback
from importlib import import_module
from typing import ClassVar, Dict, Iterable, Tuple, Union
from typing import ClassVar, Dict, Tuple, Union

from coverage import CoveragePlugin
from coverage.config import CoverageConfig
Expand Down Expand Up @@ -30,6 +30,8 @@ def get_env_info() -> Dict[str, object]:
class _ConditionalCovPlugin(CoveragePlugin):
_rules_opt_name: ClassVar[str] = 'coverage_conditional_plugin:rules'
_ignore_opt_name: ClassVar[str] = 'report:exclude_lines'
_omit_opt_name_plugin: ClassVar[str] = 'coverage_conditional_plugin:omit'
_omit_opt_name_coverage: ClassVar[str] = 'run:omit'

def configure(self, config: CoverageConfig) -> None:
"""
Expand All @@ -38,8 +40,23 @@ def configure(self, config: CoverageConfig) -> None:
Part of the ``coverage`` public API.
Called right after ``coverage_init`` function.
"""
rules: Iterable[str]
self._configure_omits(config)
self._configure_rules(config)

def _configure_omits(self, config: CoverageConfig) -> None:
try: # ini format
omits = filter(
bool,
config.get_option(self._omit_opt_name_plugin).splitlines(),
)
except AttributeError: # toml format
omits_dict = config.get_option(self._omit_opt_name_plugin)
omits = omits_dict.items() if omits_dict else ()

for omit in omits:
sobolevn marked this conversation as resolved.
Show resolved Hide resolved
self._process_omit(config, omit)

def _configure_rules(self, config: CoverageConfig) -> None:
try: # ini format
rules = filter(
bool,
Expand All @@ -51,6 +68,21 @@ def configure(self, config: CoverageConfig) -> None:
for rule in rules:
self._process_rule(config, rule)

def _process_omit(
self, config: CoverageConfig, omit: Union[str, Tuple[str, str]],
) -> None:
if isinstance(omit, str):
code, pattern = [part.strip() for part in omit.rsplit(':', 1)]
code = code[1:-1] # removes quotes
pattern = pattern[1:-1]
elif isinstance(omit, tuple):
pattern = omit[0]
code = omit[1]
else:
raise ValueError("Invalid type for 'omit'.")
if self._should_be_applied(code):
self._omit_pattern(config, pattern)

def _process_rule(
self, config: CoverageConfig, rule: Union[str, Tuple[str, str]],
) -> None:
Expand Down Expand Up @@ -108,6 +140,12 @@ def _ignore_marker(self, config: CoverageConfig, marker: str) -> None:
exclude_lines.append(marker)
config.set_option(self._ignore_opt_name, exclude_lines)

def _omit_pattern(self, config: CoverageConfig, pattern: str) -> None:
"""Adds a file name pattern to the omit list."""
omit_patterns = config.get_option(self._omit_opt_name_coverage)
omit_patterns.append(pattern)
config.set_option(self._omit_opt_name_coverage, omit_patterns)


def _is_installed(package: str) -> bool:
"""Helper function to detect if some package is installed."""
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ docstring-style = numpy
# Plugins:
max-complexity = 6
max-line-length = 80
max-methods = 8

# wemake-python-styleguide settings:
i-control-code = false
Expand Down
2 changes: 2 additions & 0 deletions test_project/.coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ plugins =
coverage_conditional_plugin

[coverage_conditional_plugin]
omit =
"sys_version_info >= (3, 6)": "test_project/omit*.py"
rules =
"sys_version_info >= (3, 6)": py-gte-36
"sys_version_info >= (3, 7)": py-gte-37
Expand Down
3 changes: 3 additions & 0 deletions test_project/omit1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def uncovered() -> str:
"""Test function that is uncovered."""
return 'uncovered'
3 changes: 3 additions & 0 deletions test_project/omit2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def uncovered() -> str:
"""Test function that is uncovered."""
return 'uncovered'
3 changes: 3 additions & 0 deletions test_project/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
[tool.coverage.run]
plugins = ["coverage_conditional_plugin"]

[tool.coverage.coverage_conditional_plugin.omit]
"test_project/omit*.py" = "sys_version_info >= (3, 6)"

[tool.coverage.coverage_conditional_plugin.rules]
py-gte-36 = "sys_version_info >= (3, 6)"
py-gte-37 = "sys_version_info >= (3, 7)"
Expand Down