Skip to main content

Interrogate a codebase for docstring coverage.

Project description

Pink Sloth Logo

interrogate: explain yourself

Documentation Coverage Testing Coverage Documentation Status CI Status

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.5 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-badge ):

$ 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.2.0  # 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

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.

  -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 sloth logo is by JustineW purchased via the Noun Project (but also available under the Creative Commons License with attribution).

Release Information

1.3.0 (2020-08-23)

Added

  • Read configuration from pyproject.toml by default (#36).

  • Add -P / --ignore-property-decorators flag to ignore methods with property getter/setter decorators (#37).

  • Add support for read configuration from setup.cfg (#35).

Fixed

  • -e / --exclude doesn’t error if a non-existent file/directory is passed (#38 - thank you MarcoGorelli!).

Full changelog.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

interrogate-1.3.0.tar.gz (68.1 kB view hashes)

Uploaded Source

Built Distribution

interrogate-1.3.0-py3-none-any.whl (23.3 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page