interrogate
: explain yourself
Interrogate a codebase for docstring coverage.
Why Do I Need This?
interrogate
checks your code base for missing docstrings.
Documentation should be as important as code itself. And it should live within code. Python standardized docstrings, allowing for developers to navigate libraries as simply as calling help()
on objects, and with powerful tools like Sphinx, pydoc, and Docutils to automatically generate HTML, LaTeX, PDFs, etc.
Enter: interrogate
.
interrogate
will tell you which methods, functions, classes, and modules have docstrings, and which do not. Use interrogate
to:
- Get an understanding of how well your code is documented;
- Add it to CI/CD checks to enforce documentation on newly-added code;
- Assess a new code base for (one aspect of) code quality and maintainability.
Let's get started.
Requirements
interrogate
supports Python 3.6 and above.
Installation
interrogate
is available on PyPI and GitHub. The recommended installation method is pip-installing into a virtualenv:
$ pip install interrogate
Usage
Try it out on a Python project:
$ interrogate [PATH]
RESULT: PASSED (minimum: 80.0%, actual: 100.0%)
Add verbosity to see a summary:
$ interrogate -v [PATH]
================== Coverage for /Users/lynn/dev/interrogate/ ====================
------------------------------------ Summary ------------------------------------
| Name | Total | Miss | Cover | Cover% |
|---------------------------------------|---------|--------|---------|----------|
| src/interrogate/__init__.py | 1 | 0 | 1 | 100% |
| src/interrogate/__main__.py | 1 | 0 | 1 | 100% |
| src/interrogate/badge_gen.py | 5 | 0 | 5 | 100% |
| src/interrogate/cli.py | 2 | 0 | 2 | 100% |
| src/interrogate/config.py | 8 | 0 | 8 | 100% |
| src/interrogate/coverage.py | 25 | 0 | 25 | 100% |
| src/interrogate/utils.py | 10 | 0 | 10 | 100% |
| src/interrogate/visit.py | 16 | 0 | 16 | 100% |
| tests/functional/__init__.py | 1 | 0 | 1 | 100% |
| tests/functional/test_cli.py | 7 | 0 | 7 | 100% |
| tests/functional/test_coverage.py | 6 | 0 | 6 | 100% |
| tests/unit/__init__.py | 1 | 0 | 1 | 100% |
| tests/unit/test_badge_gen.py | 6 | 0 | 6 | 100% |
| tests/unit/test_config.py | 10 | 0 | 10 | 100% |
| tests/unit/test_utils.py | 13 | 0 | 13 | 100% |
|---------------------------------------|---------|--------|---------|----------|
| TOTAL | 112 | 0 | 112 | 100.0% |
---------------- RESULT: PASSED (minimum: 95.0%, actual: 100.0%) ----------------
Add even more verbosity:
$ interrogate -vv [PATH]
================== Coverage for /Users/lynn/dev/interrogate/ ====================
------------------------------- Detailed Coverage -------------------------------
| Name | Status |
|---------------------------------------------------------------------|---------|
| src/interrogate/__init__.py (module) | COVERED |
|---------------------------------------------------------------------|---------|
| src/interrogate/__main__.py (module) | COVERED |
|---------------------------------------------------------------------|---------|
| src/interrogate/badge_gen.py (module) | COVERED |
| save_badge (L33) | COVERED |
| get_badge (L50) | COVERED |
| get_color (L66) | COVERED |
| create (L79) | COVERED |
|---------------------------------------------------------------------|---------|
| src/interrogate/cli.py (module) | COVERED |
| main (L218) | COVERED |
|---------------------------------------------------------------------|---------|
| src/interrogate/config.py (module) | COVERED |
| InterrogateConfig (L17) | COVERED |
| find_project_root (L52) | COVERED |
| find_project_config (L80) | COVERED |
| parse_pyproject_toml (L91) | COVERED |
| sanitize_list_values (L107) | COVERED |
| parse_setup_cfg (L130) | COVERED |
| read_config_file (L164) | COVERED |
|---------------------------------------------------------------------|---------|
| src/interrogate/coverage.py (module) | COVERED |
| BaseInterrogateResult (L21) | COVERED |
| BaseInterrogateResult.perc_covered (L35) | COVERED |
| InterrogateFileResult (L49) | COVERED |
| InterrogateFileResult.combine (L62) | COVERED |
| InterrogateResults (L76) | COVERED |
| InterrogateResults.combine (L88) | COVERED |
| InterrogateCoverage (L96) | COVERED |
| InterrogateCoverage._add_common_exclude (L115) | COVERED |
| InterrogateCoverage._filter_files (L122) | COVERED |
| InterrogateCoverage.get_filenames_from_paths (L139) | COVERED |
| InterrogateCoverage._filter_nodes (L166) | COVERED |
| InterrogateCoverage._get_file_coverage (L192) | COVERED |
| InterrogateCoverage._get_coverage (L218) | COVERED |
| InterrogateCoverage.get_coverage (L235) | COVERED |
| InterrogateCoverage._get_filename (L240) | COVERED |
| InterrogateCoverage._get_detailed_row (L251) | COVERED |
| InterrogateCoverage._create_detailed_table (L268) | COVERED |
| InterrogateCoverage._create_detailed_table._sort_nodes (L275) | COVERED |
| InterrogateCoverage._print_detailed_table (L297) | COVERED |
| InterrogateCoverage._create_summary_table (L315) | COVERED |
| InterrogateCoverage._print_summary_table (L349) | COVERED |
| InterrogateCoverage._sort_results (L367) | COVERED |
| InterrogateCoverage._get_header_base (L397) | COVERED |
| InterrogateCoverage.print_results (L406) | COVERED |
|---------------------------------------------------------------------|---------|
| src/interrogate/utils.py (module) | COVERED |
| parse_regex (L22) | COVERED |
| smart_open (L41) | COVERED |
| get_common_base (L61) | COVERED |
| OutputFormatter (L81) | COVERED |
| OutputFormatter.should_markup (L91) | COVERED |
| OutputFormatter.set_detailed_markup (L106) | COVERED |
| OutputFormatter.set_summary_markup (L130) | COVERED |
| OutputFormatter._interrogate_line_formatter (L159) | COVERED |
| OutputFormatter.get_table_formatter (L222) | COVERED |
|---------------------------------------------------------------------|---------|
| src/interrogate/visit.py (module) | COVERED |
| CovNode (L15) | COVERED |
| CoverageVisitor (L42) | COVERED |
| CoverageVisitor._has_doc (L56) | COVERED |
| CoverageVisitor._visit_helper (L63) | COVERED |
| CoverageVisitor._is_nested (L109) | COVERED |
| CoverageVisitor._is_private (L118) | COVERED |
| CoverageVisitor._is_semiprivate (L126) | COVERED |
| CoverageVisitor._is_ignored_common (L136) | COVERED |
| CoverageVisitor._has_property_decorators (L153) | COVERED |
| CoverageVisitor._is_func_ignored (L167) | COVERED |
| CoverageVisitor._is_class_ignored (L188) | COVERED |
| CoverageVisitor.visit_Module (L192) | COVERED |
| CoverageVisitor.visit_ClassDef (L199) | COVERED |
| CoverageVisitor.visit_FunctionDef (L208) | COVERED |
| CoverageVisitor.visit_AsyncFunctionDef (L217) | COVERED |
|---------------------------------------------------------------------|---------|
| tests/functional/__init__.py (module) | COVERED |
|---------------------------------------------------------------------|---------|
| tests/functional/test_cli.py (module) | COVERED |
| runner (L22) | COVERED |
| test_run_no_paths (L30) | COVERED |
| test_run_shortflags (L71) | COVERED |
| test_run_longflags (L97) | COVERED |
| test_run_multiple_flags (L115) | COVERED |
| test_generate_badge (L126) | COVERED |
|---------------------------------------------------------------------|---------|
| tests/functional/test_coverage.py (module) | COVERED |
| test_coverage_simple (L42) | COVERED |
| test_coverage_errors (L55) | COVERED |
| test_print_results (L83) | COVERED |
| test_print_results_ignore_module (L117) | COVERED |
| test_print_results_single_file (L144) | COVERED |
|---------------------------------------------------------------------|---------|
| tests/unit/__init__.py (module) | COVERED |
|---------------------------------------------------------------------|---------|
| tests/unit/test_badge_gen.py (module) | COVERED |
| test_save_badge (L25) | COVERED |
| test_save_badge_windows (L47) | COVERED |
| test_get_badge (L61) | COVERED |
| test_get_color (L85) | COVERED |
| test_create (L102) | COVERED |
|---------------------------------------------------------------------|---------|
| tests/unit/test_config.py (module) | COVERED |
| test_find_project_root (L29) | COVERED |
| test_find_project_config (L45) | COVERED |
| test_parse_pyproject_toml (L54) | COVERED |
| test_sanitize_list_values (L84) | COVERED |
| test_parse_setup_cfg (L89) | COVERED |
| test_parse_setup_cfg_raises (L114) | COVERED |
| test_read_config_file_none (L125) | COVERED |
| test_read_config_file (L184) | COVERED |
| test_read_config_file_raises (L198) | COVERED |
|---------------------------------------------------------------------|---------|
| tests/unit/test_utils.py (module) | COVERED |
| test_parse_regex (L32) | COVERED |
| test_smart_open (L39) | COVERED |
| test_get_common_base (L69) | COVERED |
| test_get_common_base_windows (L100) | COVERED |
| test_output_formatter_should_markup (L132) | COVERED |
| test_output_formatter_set_detailed_markup (L163) | COVERED |
| test_output_formatter_set_summary_markup (L206) | COVERED |
| test_output_formatter_interrogate_line_formatter (L258) | COVERED |
| test_output_formatter_interrogate_line_formatter_windows (L319) | COVERED |
| test_output_formatter_get_table_formatter (L343) | COVERED |
| test_output_formatter_get_table_formatter_py38 (L381) | COVERED |
| test_output_formatter_get_table_formatter_raises (L395) | COVERED |
|---------------------------------------------------------------------|---------|
------------------------------------ Summary ------------------------------------
| Name | Total | Miss | Cover | Cover% |
|---------------------------------------|---------|--------|---------|----------|
| src/interrogate/__init__.py | 1 | 0 | 1 | 100% |
| src/interrogate/__main__.py | 1 | 0 | 1 | 100% |
| src/interrogate/badge_gen.py | 5 | 0 | 5 | 100% |
| src/interrogate/cli.py | 2 | 0 | 2 | 100% |
| src/interrogate/config.py | 8 | 0 | 8 | 100% |
| src/interrogate/coverage.py | 25 | 0 | 25 | 100% |
| src/interrogate/utils.py | 10 | 0 | 10 | 100% |
| src/interrogate/visit.py | 16 | 0 | 16 | 100% |
| tests/functional/__init__.py | 1 | 0 | 1 | 100% |
| tests/functional/test_cli.py | 7 | 0 | 7 | 100% |
| tests/functional/test_coverage.py | 6 | 0 | 6 | 100% |
| tests/unit/__init__.py | 1 | 0 | 1 | 100% |
| tests/unit/test_badge_gen.py | 6 | 0 | 6 | 100% |
| tests/unit/test_config.py | 10 | 0 | 10 | 100% |
| tests/unit/test_utils.py | 13 | 0 | 13 | 100% |
|---------------------------------------|---------|--------|---------|----------|
| TOTAL | 112 | 0 | 112 | 100.0% |
---------------- RESULT: PASSED (minimum: 95.0%, actual: 100.0%) ----------------
Other Usage
Generate a shields.io badge (like this one! ):
$ interrogate --generate-badge PATH
RESULT: PASSED (minimum: 80.0%, actual: 100.0%)
Generated badge to /Users/lynn/dev/interrogate/docs/_static/interrogate_badge.svg
Add it to your tox.ini
file to enforce a level of coverage:
[testenv:doc]
deps = interrogate
skip_install = true
commands =
interrogate --quiet --fail-under 95 src tests
Or use it with pre-commit:
repos:
- repo: https://github.com/econchick/interrogate
rev: 1.3.2 # or master if you're bold
hooks:
- id: interrogate
args: [--quiet, --fail-under=95]
Use it within your code directly:
>>> from interrogate import coverage
>>> cov = coverage.InterrogateCoverage(paths=["src"])
>>> results = cov.get_coverage()
>>> results
InterrogateResults(total=68, covered=65, missing=3)
Use interrogate
with GitHub Actions. Check out the action written & maintained by Jack McKew (thank you, Jack!).
Configuration
Configure within your pyproject.toml
(interrogate
will automatically detect a pyproject.toml
file and pick up default values for the command line options):
$ interrogate -c pyproject.toml [OPTIONS] [PATHS]...
[tool.interrogate]
ignore-init-method = true
ignore-init-module = false
ignore-magic = false
ignore-semiprivate = false
ignore-private = false
ignore-property-decorators = false
ignore-module = false
fail-under = 95
exclude = ["setup.py", "docs", "build"]
ignore-regex = ["^get$", "^mock_.*", ".*BaseClass.*"]
verbose = 0
quiet = false
whitelist-regex = []
color = true
Or configure within your setup.cfg
(interrogate
will automatically detect a setup.cfg
file and pick up default values for the command line options):
$ interrogate -c setup.cfg [OPTIONS] [PATHS]...
[tool:interrogate]
ignore-init-method = true
ignore-init-module = false
ignore-magic = false
ignore-semiprivate = false
ignore-private = false
ignore-property-decorators = false
ignore-module = false
fail-under = 95
exclude = setup.py,docs,build
ignore-regex = ^get$,^mock_.*,.*BaseClass.*
verbose = 0
quiet = false
whitelist-regex =
color = true
Warning
The use of setup.cfg
is not recommended unless for very simple use cases. .cfg
files use a different parser than pyproject.toml
which might cause hard to track down problems. When possible, it is recommended to use pyproject.toml
to define your interrogate configuration.
To view all options available, run interrogate --help
:
interrogate -h
Usage: interrogate [OPTIONS] [PATHS]...
Measure and report on documentation coverage in Python modules.
Options:
--version Show the version and exit.
-v, --verbose Level of verbosity [default: 0]
-q, --quiet Do not print output [default: False]
-f, --fail-under INT | FLOAT Fail when coverage % is less than a given
amount. [default: 80.0]
-e, --exclude PATH Exclude PATHs of files and/or directories.
Multiple `-e/--exclude` invocations
supported.
-i, --ignore-init-method Ignore `__init__` method of classes.
[default: False]
-I, --ignore-init-module Ignore `__init__.py` modules. [default:
False]
-m, --ignore-magic Ignore all magic methods of classes.
[default: False]
NOTE: This does not include the `__init__`
method. To ignore `__init__` methods, use
`--ignore-init-method`.
-M, --ignore-module Ignore module-level docstrings. [default:
False]
-n, --ignore-nested-functions Ignore nested functions and methods.
[default: False]
-p, --ignore-private Ignore private classes, methods, and
functions starting with two underscores.
[default: False]
NOTE: This does not include magic methods;
use `--ignore-magic` and/or `--ignore-init-
method` instead.
-P, --ignore-property-decorators
Ignore methods with property setter/getter
decorators. [default: False]
-s, --ignore-semiprivate Ignore semiprivate classes, methods, and
functions starting with a single underscore.
[default: False]
-r, --ignore-regex STR Regex identifying class, method, and
function names to ignore. Multiple
`-r/--ignore-regex` invocations supported.
-w, --whitelist-regex STR Regex identifying class, method, and
function names to include. Multiple
`-w/--whitelist-regex` invocations
supported.
-o, --output FILE Write output to a given FILE. [default:
stdout]
--color / --no-color Toggle color output on/off when printing to
stdout. [default: True]
-g, --generate-badge PATH Generate a 'shields.io' status badge (an SVG
image) in at a given file or directory. Will
not generate a badge if results did not
change from an existing badge of the same
path.
-h, --help Show this message and exit.
-c, --config FILE Read configuration from `pyproject.toml` or
`setup.cfg`.
Credits
interrogate
was inspired by docstr-coverage
, which was forked from Alexey "DataGreed" Strelkov's docstring-coverage
, which was inspired by a 2004 recipe from James Harlow (turtles...).
The cute logo is by JustineW purchased via the Noun Project (but also available under the Creative Commons License with attribution).