Giter VIP home page Giter VIP logo

interrogate's Introduction

Pink Sloth Logo

interrogate: explain yourself

Documentation Coverage Testing Coverage Documentation Status CI Status pre-commit.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.8 and above.

Installation

interrogate is available on PyPI and GitHub. The recommended installation method is pip-installing into a virtualenv:

$ pip install interrogate

Extras

interrogate provides a way to generate a shields.io-like coverage badge as an SVG file. To generate a PNG file instead, install interrogate with the extras [png]:

$ pip install interrogate[png]

NOTICE: Additional system libraries/tools may be required in order to generate a PNG file of the coverage badge:

  • on Windows, install Visual C++ compiler for Cairo;
  • on macOS, install cairo and libffi (with Homebrew for example - see note below);
  • on Linux, install the cairo, python3-dev and libffi-dev packages (names may vary depending on distribution).

Refer to the cairosvg documentation for more information.

MacOS and Cairo

If you get an error when trying to generate a badge like so:

OSError: no library called "cairo-2" was found
no library called "cairo" was found
no library called "libcairo-2" was found

Then first try:

export DYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib

And rerun the command.

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          |       6 |      0 |       6 |     100% |
| src/interrogate/cli.py                |       2 |      0 |       2 |     100% |
| src/interrogate/config.py             |       8 |      0 |       8 |     100% |
| src/interrogate/coverage.py           |      27 |      0 |      27 |     100% |
| src/interrogate/utils.py              |      10 |      0 |      10 |     100% |
| src/interrogate/visit.py              |      18 |      0 |      18 |     100% |
| tests/functional/__init__.py          |       1 |      0 |       1 |     100% |
| tests/functional/test_cli.py          |       8 |      0 |       8 |     100% |
| tests/functional/test_coverage.py     |      10 |      0 |      10 |     100% |
| tests/unit/__init__.py                |       1 |      0 |       1 |     100% |
| tests/unit/test_badge_gen.py          |       8 |      0 |       8 |     100% |
| tests/unit/test_config.py             |      10 |      0 |      10 |     100% |
| tests/unit/test_utils.py              |      13 |      0 |      13 |     100% |
|---------------------------------------|---------|--------|---------|----------|
| TOTAL                                 |     124 |      0 |     124 |   100.0% |
---------------- RESULT: PASSED (minimum: 80.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 (L42)                                                  | COVERED |
|   get_badge (L87)                                                   | COVERED |
|   should_generate_badge (L103)                                      | COVERED |
|   get_color (L160)                                                  | COVERED |
|   create (L173)                                                     | COVERED |
|---------------------------------------------------------------------|---------|
| src/interrogate/cli.py (module)                                     | COVERED |
|   main (L258)                                                       | COVERED |
|---------------------------------------------------------------------|---------|
| src/interrogate/config.py (module)                                  | COVERED |
|   InterrogateConfig (L19)                                           | COVERED |
|   find_project_root (L61)                                           | COVERED |
|   find_project_config (L89)                                         | COVERED |
|   parse_pyproject_toml (L100)                                       | COVERED |
|   sanitize_list_values (L116)                                       | COVERED |
|   parse_setup_cfg (L139)                                            | COVERED |
|   read_config_file (L173)                                           | COVERED |
|---------------------------------------------------------------------|---------|
| src/interrogate/coverage.py (module)                                | COVERED |
|   BaseInterrogateResult (L23)                                       | COVERED |
|     BaseInterrogateResult.perc_covered (L37)                        | COVERED |
|   InterrogateFileResult (L54)                                       | COVERED |
|     InterrogateFileResult.combine (L67)                             | COVERED |
|   InterrogateResults (L81)                                          | COVERED |
|     InterrogateResults.combine (L93)                                | COVERED |
|   InterrogateCoverage (L101)                                        | COVERED |
|     InterrogateCoverage._add_common_exclude (L121)                  | COVERED |
|     InterrogateCoverage._filter_files (L128)                        | COVERED |
|     InterrogateCoverage.get_filenames_from_paths (L141)             | COVERED |
|     InterrogateCoverage._filter_nodes (L168)                        | COVERED |
|     InterrogateCoverage._filter_inner_nested (L194)                 | COVERED |
|     InterrogateCoverage._get_file_coverage (L203)                   | COVERED |
|     InterrogateCoverage._get_coverage (L231)                        | COVERED |
|     InterrogateCoverage.get_coverage (L248)                         | COVERED |
|     InterrogateCoverage._get_filename (L253)                        | COVERED |
|     InterrogateCoverage._get_detailed_row (L264)                    | COVERED |
|     InterrogateCoverage._create_detailed_table (L281)               | COVERED |
|       InterrogateCoverage._create_detailed_table._sort_nodes (L288) | COVERED |
|     InterrogateCoverage._print_detailed_table (L315)                | COVERED |
|     InterrogateCoverage._create_summary_table (L338)                | COVERED |
|     InterrogateCoverage._print_summary_table (L381)                 | COVERED |
|     InterrogateCoverage._sort_results (L399)                        | COVERED |
|     InterrogateCoverage._get_header_base (L429)                     | COVERED |
|     InterrogateCoverage._print_omitted_file_count (L438)            | COVERED |
|     InterrogateCoverage.print_results (L469)                        | COVERED |
|---------------------------------------------------------------------|---------|
| src/interrogate/utils.py (module)                                   | COVERED |
|   parse_regex (L21)                                                 | COVERED |
|   smart_open (L40)                                                  | COVERED |
|   get_common_base (L60)                                             | COVERED |
|   OutputFormatter (L80)                                             | COVERED |
|     OutputFormatter.should_markup (L90)                             | COVERED |
|     OutputFormatter.set_detailed_markup (L105)                      | COVERED |
|     OutputFormatter.set_summary_markup (L129)                       | COVERED |
|     OutputFormatter._interrogate_line_formatter (L158)              | COVERED |
|     OutputFormatter.get_table_formatter (L226)                      | COVERED |
|---------------------------------------------------------------------|---------|
| src/interrogate/visit.py (module)                                   | COVERED |
|   CovNode (L15)                                                     | COVERED |
|   CoverageVisitor (L44)                                             | COVERED |
|     CoverageVisitor._has_doc (L58)                                  | COVERED |
|     CoverageVisitor._visit_helper (L65)                             | COVERED |
|     CoverageVisitor._is_nested_func (L112)                          | COVERED |
|     CoverageVisitor._is_nested_cls (L121)                           | COVERED |
|     CoverageVisitor._is_private (L133)                              | COVERED |
|     CoverageVisitor._is_semiprivate (L141)                          | COVERED |
|     CoverageVisitor._is_ignored_common (L151)                       | COVERED |
|     CoverageVisitor._has_property_decorators (L168)                 | COVERED |
|     CoverageVisitor._has_setters (L182)                             | COVERED |
|     CoverageVisitor._is_func_ignored (L193)                         | COVERED |
|     CoverageVisitor._is_class_ignored (L217)                        | COVERED |
|     CoverageVisitor.visit_Module (L221)                             | COVERED |
|     CoverageVisitor.visit_ClassDef (L228)                           | COVERED |
|     CoverageVisitor.visit_FunctionDef (L237)                        | COVERED |
|     CoverageVisitor.visit_AsyncFunctionDef (L246)                   | 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 (L77)                                         | COVERED |
|   test_run_longflags (L106)                                         | COVERED |
|   test_run_multiple_flags (L124)                                    | COVERED |
|   test_generate_badge (L135)                                        | COVERED |
|   test_incompatible_options (L170)                                  | COVERED |
|---------------------------------------------------------------------|---------|
| tests/functional/test_coverage.py (module)                          | COVERED |
|   test_coverage_simple (L60)                                        | COVERED |
|   test_coverage_errors (L73)                                        | COVERED |
|   test_print_results (L101)                                         | COVERED |
|   test_print_results_omit_covered (L130)                            | COVERED |
|   test_print_results_omit_none (L156)                               | COVERED |
|   test_print_results_omit_all_summary (L174)                        | COVERED |
|   test_print_results_omit_all_detailed (L198)                       | COVERED |
|   test_print_results_ignore_module (L226)                           | COVERED |
|   test_print_results_single_file (L253)                             | COVERED |
|---------------------------------------------------------------------|---------|
| tests/unit/__init__.py (module)                                     | COVERED |
|---------------------------------------------------------------------|---------|
| tests/unit/test_badge_gen.py (module)                               | COVERED |
|   test_save_badge (L26)                                             | COVERED |
|   test_save_badge_windows (L50)                                     | COVERED |
|   test_save_badge_no_cairo (L62)                                    | COVERED |
|   test_get_badge (L73)                                              | COVERED |
|   test_should_generate (L96)                                        | COVERED |
|   test_get_color (L115)                                             | COVERED |
|   test_create (L136)                                                | COVERED |
|---------------------------------------------------------------------|---------|
| tests/unit/test_config.py (module)                                  | COVERED |
|   test_find_project_root (L29)                                      | COVERED |
|   test_find_project_config (L48)                                    | COVERED |
|   test_parse_pyproject_toml (L57)                                   | COVERED |
|   test_sanitize_list_values (L93)                                   | COVERED |
|   test_parse_setup_cfg (L98)                                        | COVERED |
|   test_parse_setup_cfg_raises (L123)                                | COVERED |
|   test_read_config_file_none (L134)                                 | COVERED |
|   test_read_config_file (L193)                                      | COVERED |
|   test_read_config_file_raises (L207)                               | 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          |       6 |      0 |       6 |     100% |
| src/interrogate/cli.py                |       2 |      0 |       2 |     100% |
| src/interrogate/config.py             |       8 |      0 |       8 |     100% |
| src/interrogate/coverage.py           |      27 |      0 |      27 |     100% |
| src/interrogate/utils.py              |      10 |      0 |      10 |     100% |
| src/interrogate/visit.py              |      18 |      0 |      18 |     100% |
| tests/functional/__init__.py          |       1 |      0 |       1 |     100% |
| tests/functional/test_cli.py          |       8 |      0 |       8 |     100% |
| tests/functional/test_coverage.py     |      10 |      0 |      10 |     100% |
| tests/unit/__init__.py                |       1 |      0 |       1 |     100% |
| tests/unit/test_badge_gen.py          |       8 |      0 |       8 |     100% |
| tests/unit/test_config.py             |      10 |      0 |      10 |     100% |
| tests/unit/test_utils.py              |      13 |      0 |      13 |     100% |
|---------------------------------------|---------|--------|---------|----------|
| TOTAL                                 |     124 |      0 |     124 |   100.0% |
---------------- RESULT: PASSED (minimum: 80.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

See below for more badge configuration.

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.7.0  # or master if you're bold
    hooks:
      - id: interrogate
        args: [--quiet, --fail-under=95]
        pass_filenames: false  # needed if excluding files with pyproject.toml or setup.cfg

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!).

Or use interrogate in VSCode with the interrogate extension written & maintained by Kenneth Love (thank you, Kenneth!).

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 = false
ignore-init-module = false
ignore-magic = false
ignore-semiprivate = false
ignore-private = false
ignore-property-decorators = false
ignore-module = false
ignore-nested-functions = false
ignore-nested-classes = false
ignore-setters = false
ignore-overloaded-functions = false
fail-under = 80
# example values
exclude = ["setup.py", "docs", "build"]
# example regex
ignore-regex = ["^get$", "^mock_.*", ".*BaseClass.*"]
ext = []
# possible values: sphinx (default), google
style = "sphinx"
# possible values: 0 (minimal output), 1 (-v), 2 (-vv)
verbose = 0
quiet = false
whitelist-regex = []
color = true
omit-covered-files = false
# output file logation
generate-badge = "."
badge-format = "svg"

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 = false
ignore-init-module = false
ignore-magic = false
ignore-semiprivate = false
ignore-private = false
ignore-property-decorators = false
ignore-module = false
ignore-nested-functions = false
ignore-nested-classes = false
ignore-setters = false
ignore-overloaded-functions = false
fail-under = 80
; example values
exclude = setup.py,docs,build
; example regex
ignore-regex = ^get$,^mock_.*,.*BaseClass.*
ext = []
; possible values: sphinx (default), google
style = sphinx
; possible values: 0 (minimal output), 1 (-v), 2 (-vv)
verbose = 0
quiet = false
whitelist-regex =
color = true
omit-covered-files = false
; output file logation
generate-badge = .
badge-format = svg

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.

Badge Options

Badge Format

The default file format is svg. Use the --badge-format flag to create a png file instead. Note: interrogate must be installed with interrogate[png] in order to generate png files (see above).

$ interrogate --generate-badge PATH --badge-format png
RESULT: PASSED (minimum: 80.0%, actual: 100.0%)
Generated badge to /Users/lynn/dev/interrogate/docs/_static/interrogate_badge.png

Badge Style

The following badge styles are available via the --badge-style flag:

option example
flat flat-example
flat-square flat-square-example
flat-square-modified (default) interrogate-badge
for-the-badge for-the-badge-example
plastic plastic-example
social social-example

Command Line Options

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.

                                  NOTE: When configuring verbosity in
                                  pyproject.toml or setup.cfg, `verbose=1`
                                  maps to `-v`, and `verbose=2` maps to `-vv`.
                                  `verbose=0` is the equivalent of no verbose
                                  flags used, producing minimal output.
  -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]

  -C, --ignore-nested-classes     Ignore nested classes.  [default: False]

  -O, --ignore-overloaded-functions
                                  Ignore `@typing.overload`-decorated functions.
                                  [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/deleter
                                  decorators.  [default: False]

  -S, --ignore-setters            Ignore methods with property setter
                                  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.

  --ext                           Include Python-like files with the given
                                  extension (supported: ``pyi``). Multiple
                                  `--ext` invocations supported.

  -w, --whitelist-regex STR       Regex identifying class, method, and
                                  function names to include. Multiple
                                  `-w/--whitelist-regex` invocations
                                  supported.

  --style [sphinx|google]         Style of docstrings to honor. Using `google`
                                  will consider a class and its `__init__`
                                  method both covered if there is either a
                                  class-level docstring, or an `__init__`
                                  method docstring, instead of enforcing both.
                                  Mutually exclusive with `-i`/`--ignore-init`
                                  flag.  [default: sphinx]

  -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]

  --omit-covered-files            Omit reporting files that have 100%
                                  documentation coverage. This option is
                                  ignored if verbosity is not set.  [default:
                                  False]

  -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.

  --badge-format [svg|png]        File format for the generated badge. Used
                                  with the `-g/--generate-badge` flag.
                                  [default: svg]

                                  NOTE: To generate a PNG file, interrogate
                                  must be installed with `interrogate[png]`,
                                  i.e. `pip install interrogate[png]`.

  --badge-style [flat|flat-square|flat-square-modified|for-the-badge|plastic|social]
                                  Desired style of shields.io badge. Used with
                                  the `-g/--generate-badge` flag. [default:
                                  flat-square-modified]

  -h, --help                      Show this message and exit.
  -c, --config FILE               Read configuration from `pyproject.toml` or
                                  `setup.cfg`.

Users of Interrogate

Interrogate in the Wild

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).

interrogate's People

Contributors

bildzeitung avatar bluetech avatar dependabot[bot] avatar econchick avatar erwinjunge avatar hynek avatar jimrybarski avatar marcogorelli avatar mmtj avatar oriash93 avatar pre-commit-ci[bot] avatar psobot avatar q0w avatar s-weigand avatar szabopeter avatar zackyancey avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

interrogate's Issues

[BUG] Exclude paths not os independent

Environment

  • interrogate version(s) (interrogate --version: 1.3.1
  • Operating System(s): Windows10
  • Python version(s): 3.7.8

Description of the bug

In one of my projects I exclude a subdir with autogenerated files, form being tested with inrerrogate.
In order for this to work crossplatform I need to define the exclusion twice.
Once with windows path separators \\ and once with Posix separators /.
So I end up with the following config:

[tool.interrogate]
exclude = ["setup.py", "docs", "tests", ".eggs", "djangocms_equation\\migrations", "djangocms_equation/migrations"]

What you expected to happen

Just using "djangocms_equation/migrations" should be sufficient.

How to reproduce (as minimally and precisely as possible)

Just exclude a file with posix path noatation on windows, and it won't be excluded.

Anthing else we need to know?

This can be fixed by using os.path.normpath in the following line.

maybe_excluded_dir = any(
[f.startswith(exc) for exc in self.excluded]
)
maybe_excluded_file = f in self.excluded
if any([maybe_excluded_dir, maybe_excluded_file]):
continue

            maybe_excluded_dir = any(
                [f.startswith(os.path.normpath(exc)) for exc in self.excluded]
            )

Also maybe_excluded_file seams to be redundant since some_str.startswith(some_str) == some_str in (some_str,)

In [1]: file_path = "/home/user/foo"

In [2]: exclude = (file_path, )

In [3]: any(file_path.startswith(exc) for exc in exclude)
Out[3]: True

In [4]: file_path in exclude
Out[4]: True

Add flag to check class attributes for documentation

Hi there,

One thing currently not tested by the tool is class variables, i.e. class members or attributes. This is important when generating documentation from code.

For example, both code snippets will have the same documentation coverage currently:

class Controller:
    """
    The Starlite Controller class. Subclass this class to create 'view' like components and utilize OOP.
    """

    after_request: Optional["AfterRequestHandler"]
    after_response: Optional["AfterResponseHandler"]
    before_request: Optional["BeforeRequestHandler"]
    dependencies: Optional[Dict[str, "Provide"]]
    exception_handlers: Optional[Dict[Union[int, "Type[Exception]"], "ExceptionHandler"]]
    guards: Optional[List["Guard"]]
    middleware: Optional[List["Middleware"]]
    owner: "Router"
    parameters: Optional[Dict[str, "FieldInfo"]]
    path: str
    response_class: Optional["Type[Response]"]
    response_cookies: Optional[List["Cookie"]]
    response_headers: Optional[Dict[str, "ResponseHeader"]]
    tags: Optional[List[str]]

    ...
class Controller:
    """
    The Starlite Controller class. Subclass this class to create 'view' like components and utilize OOP.
    """

    __slots__ = (
        "after_request",
        "after_response",
        "before_request",
        "dependencies",
        "exception_handlers",
        "guards",
        "middleware",
        "owner",
        "parameters",
        "path",
        "response_class",
        "response_cookies",
        "response_headers",
        "tags",
    )

    after_request: Optional["AfterRequestHandler"]
    """
        A sync or async function executed before a [Request][starlite.connection.Request] is passed to any route handler.
        If this function returns a value, the request will not reach the route handler, and instead this value will be used.
    """
    after_response: Optional["AfterResponseHandler"]
    """
        A sync or async function called after the response has been awaited.
        It receives the [Request][starlite.connection.Request] instance and should not return any values.
    """
    before_request: Optional["BeforeRequestHandler"]
    """
        A sync or async function called immediately before calling the route handler.
        It receives the [Request][starlite.connection.Request] instance and any
        non-`None` return value is used for the response, bypassing the route handler.
    """
    dependencies: Optional[Dict[str, "Provide"]]
    """
        A string/[Provider][starlite.provide.Provide] dictionary that maps dependency providers.
    """
    exception_handlers: Optional[Dict[Union[int, "Type[Exception]"], "ExceptionHandler"]]
    """
        A dictionary that maps handler functions to status codes and/or exception types.
    """
    guards: Optional[List["Guard"]]
    """
        A list of [Guard][starlite.types.Guard] callables.
    """
    middleware: Optional[List["Middleware"]]
    """
        A list of [Middleware][starlite.types.Middleware].
    """
    owner: "Router"
    """
        The [Router][starlite.router.Router] or [Starlite][starlite.app.Starlite] app that owns the controller.
        This value is set internally by Starlite and it should not be set when subclassing the controller.
    """
    parameters: Optional[Dict[str, "FieldInfo"]]
    """
        A mapping of [Parameter][starlite.params.Parameter] definitions available to all application paths.
    """
    path: str
    """
        A path fragment for the controller.
        All route handlers under the controller will have the fragment appended to them.
        If not set it defaults to '/'.
    """
    response_class: Optional["Type[Response]"]
    """
        A custom subclass of [starlite.response.Response] to be used as the default response
        for all route handlers under the controller.
    """
    response_cookies: Optional[List["Cookie"]]
    """
        A list of [Cookie](starlite.datastructures.Cookie] instances.
    """
    response_headers: Optional[Dict[str, "ResponseHeader"]]
    """
        A string keyed dictionary mapping [ResponseHeader][starlite.datastructures.ResponseHeader] instances.
    """
    tags: Optional[List[str]]
    """
        A list of string tags that will be appended to the schema of all route handlers under the controller.
    """
    ...

Error: Option '--verbose' does not take a value.

Environment

  • interrogate version(s) (interrogate --version: 1.5.0
  • Operating System(s): Debian Bullseye
  • Python version(s): 3.9
  • Pre-commit version: 3.0.3

Description of the bug

Having set --verbose=2 as one of the options in my .pre-commit-config.yaml (full config below), and having run pre-commit during a git commit action, I get the error "Error: Option '--verbose' does not take a value"

What you expected to happen

The extra-verbose output from interrogate during pre-commit, as with the -vv flag (which does work)

How to reproduce (as minimally and precisely as possible)

Use the config below in your pre-commit-config.yaml in any repo, run pre-commit install, commit something and behold.

Anthing else we need to know?

  1. -vv works!
  2. --verbose also works, but is not verbose enough
  3. pre-commit-config.yaml:
# Interrogate for docstring coverage
- repo: https://github.com/econchick/interrogate
  rev: 1.5.0
  hooks:
    - id: interrogate
      name: check docstring coverage of functions
      args:
        - --ignore-regex=^Base.*$
        - --ignore-regex=^.*Exception$
        - --ignore-regex=^.*Error$
        - --ignore-init-module
        - --ignore-init-method
        - --ignore-module
        - --ignore-magic
        - --ignore-property-decorators
        - --fail-under=90
        - --verbose=2

Allow read configuration from 'pyproject.toml' by default

Describe the feature you'd like

Currently, we have to pass 'pyproject.toml' file as a mandatory to '--config' argument to read custom configuration. Please add ability to read 'pyproject.toml' config file by default without pointing it explicitly.

Your Environment

  • Operating System(s): Mac OS, Linux
  • Python version(s): python >=3.7

re.error: nothing to repeat at position 0

Environment

  • interrogate version(s) (interrogate --version: interrogate, version 1.5.0
  • Operating System(s): Ubuntu 22.04 LTS
  • Python version(s): 3.10

Description of the bug

Interrogate throws an exception (re.error) on every command.

Traceback (most recent call last): File "/home/id986507/.local/bin/interrogate", line 8, in <module> sys.exit(main()) File "/home/id986507/.local/lib/python3.10/site-packages/click/core.py", line 829, in __call__ return self.main(*args, **kwargs) File "/home/id986507/.local/lib/python3.10/site-packages/click/core.py", line 781, in main with self.make_context(prog_name, args, **extra) as ctx: File "/home/id986507/.local/lib/python3.10/site-packages/click/core.py", line 700, in make_context self.parse_args(ctx, args) File "/home/id986507/.local/lib/python3.10/site-packages/click/core.py", line 1048, in parse_args value, args = param.handle_parse_result(ctx, opts, args) File "/home/id986507/.local/lib/python3.10/site-packages/click/core.py", line 1630, in handle_parse_result value = invoke_param_callback(self.callback, ctx, self, value) File "/home/id986507/.local/lib/python3.10/site-packages/click/core.py", line 123, in invoke_param_callback return callback(ctx, param, value) File "/home/id986507/.local/lib/python3.10/site-packages/interrogate/utils.py", line 36, in parse_regex return [re.compile(v) for v in values] File "/home/id986507/.local/lib/python3.10/site-packages/interrogate/utils.py", line 36, in <listcomp> return [re.compile(v) for v in values] File "/usr/lib/python3.10/re.py", line 251, in compile return _compile(pattern, flags) File "/usr/lib/python3.10/re.py", line 303, in _compile p = sre_compile.compile(pattern, flags) File "/usr/lib/python3.10/sre_compile.py", line 764, in compile p = sre_parse.parse(p, flags) File "/usr/lib/python3.10/sre_parse.py", line 950, in parse p = _parse_sub(source, state, flags & SRE_FLAG_VERBOSE, 0) File "/usr/lib/python3.10/sre_parse.py", line 443, in _parse_sub itemsappend(_parse(source, state, verbose, nested + 1, File "/usr/lib/python3.10/sre_parse.py", line 668, in _parse raise source.error("nothing to repeat", re.error: nothing to repeat at position 0

What you expected to happen

Check for documentation in files, the normal behavior

How to reproduce (as minimally and precisely as possible)

id986507@BHLCWG3:~/saf$ interrogate --generate-badge saf/badge.svg Traceback (most recent call last): File "/home/id986507/.local/bin/interrogate", line 8, in <module>

Anthing else we need to know?

Add --skip-covered flag to disable reporting covered files

Describe the feature you'd like

Interrogate's output can get very verbose which is very useful, however sometimes it would also be useful to ignore any files that are 100% covered to focus on the files that need docstrings added, I suggest adding a --skip-covered flag to disable reporting of such files.

Is your feature request related to a problem?

No

Your Environment

  • interrogate version(s) (interrogate --version: version 1.4.0
  • Operating System(s): MacOS
  • Python version(s): 3.9

Additional context

Coverage.py supports skipping covered files

`ignore-nested-functions` should work with coroutines

Describe the feature you'd like

I'd like ignore-nested-functions to work with coroutines (i.e. async def). However if either function is actually a coroutine, it does not work

Is your feature request related to a problem?

I'm frustrated when I have to add docstrings to unimportant functions and I can't avoid it just because of how they are defined i.e. there is nothing materially different between a function and coroutine when it comes to docstrings and their necessity (or lack thereof)

Your Environment

  • interrogate version(s) (interrogate --version: 1.5.0
  • Operating System(s): MacOS Big Sur
  • Python version(s): 3.9.7

Allow read configuration from 'setup.cfg' file

Describe the feature you'd like

Currently, there is the only ability to read configuration from 'pyproject.toml' file. Please add this for 'setup.cfg' too.

Your Environment

  • Operating System(s): Mac OS, Linux
  • Python version(s): python >=3.7

Feature Request: ability to ignore property methods decorated with *@property*

Describe the feature you'd like

I would like to be able to ignore class property methods that are defined with @property decorators. Many times these can be self-descriptive and do not follow the get_ regex pattern.

Is your feature request related to a problem?

Your Environment

  • interrogate version(s) (interrogate --version:
  • Operating System(s):
  • Python version(s):

Additional context

I don't believe the existing ignore-regex option covers this use case but I could be wrong.

Python Match|Case no support?

Environment

  • interrogate version(s) (interrogate --version: 1.5.0
  • Operating System(s): Linux
  • Python version(s): 3.10

Description of the bug

Interrogate fails with new match syntax
image

What you expected to happen

Pass

How to reproduce (as minimally and precisely as possible)

Get the coverage of your code when match is used in it.

Anthing else we need to know?

One posible solution

# https://github.com/Instagram/LibCST/issues/285#issuecomment-1011427731
if sys.version_info.major >= 3 and sys.version_info.minor >= 10:
    os.environ["LIBCST_PARSER_TYPE"] = "native"

-I doesn't work with pre-commit

Environment

  • interrogate, version 1.5.0
  • OS: Windows 10
  • Python version: 3.8.10

Description of the bug

I'm trying to use interrogate through pre-commit with this hook

  - repo: https://github.com/econchick/interrogate
    rev: 1.5.0
    hooks:
      - id: interrogate
        language: python
        types: [python]
        args:
          [
            "-vv",
            "-i",
            "-I",
            "-M",
            "--fail-under=60",
         ]

If I run pre-commit run --all-files
I obtain

| TOTAL                                         |   517 |  378 |   139 |  26.9% |
--------------- RESULT: FAILED (minimum: 60.0%, actual: 26.9%) ----------------

If instead I run interrogate -vv -i -I -M --fail-under=60 .
I obtain

| TOTAL                                                      |             491 |            364 |             127 |            25.9% | 
------------------------------------------- RESULT: FAILED (minimum: 60.0%, actual: 25.9%) ------------------------------------------- 

That is because in the first case __init__.py are not ignored, while in the second case they are correctly ignored as by -I documentation.

I've done the test also on -M and -i but these two parameter works properly resulting in the same output on both executions.

What you expected to happen

Output of both run should be exactly the same

How to reproduce (as minimally and precisely as possible)

Run interrogate with -I parameter using pre-commit and using cli on an __init__.py file that lacks documentation, the one from cli should pass, the one from interrogate should wrongly fail.

Anthing else we need to know?

I guess it's pretty much the same problem of #60 but here there's no workaround!

`interrogate` is unable to run when installed with `png` extra

Environment

  • interrogate version(s) (interrogate --version: 1.5.0
  • Operating System(s): macOS 10.15, Windows 10
  • Python version(s): 3.10.1, 3.10.2

Description of the bug

I have installed using pipx install 'interrogate[png]' and on a virtual environment on both macOS and Windows, and even interrogate --version fails with the following error:

macOS

$ pipx install 'interrogate[png]'
  installed package interrogate 1.5.0, installed using Python 3.10.1
  These apps are now globally available
    - interrogate
done! ✨ 🌟 ✨
$ interrogate --version
Traceback (most recent call last):
  File "/Users/thecesrom/.local/bin/interrogate", line 5, in <module>
    from interrogate.cli import main
  File "/Users/thecesrom/.local/pipx/venvs/interrogate/lib/python3.10/site-packages/interrogate/cli.py", line 11, in <module>
    from interrogate import badge_gen
  File "/Users/thecesrom/.local/pipx/venvs/interrogate/lib/python3.10/site-packages/interrogate/badge_gen.py", line 15, in <module>
    import cairosvg
  File "/Users/thecesrom/.local/pipx/venvs/interrogate/lib/python3.10/site-packages/cairosvg/__init__.py", line 26, in <module>
    from . import surface  # noqa isort:skip
  File "/Users/thecesrom/.local/pipx/venvs/interrogate/lib/python3.10/site-packages/cairosvg/surface.py", line 9, in <module>
    import cairocffi as cairo
  File "/Users/thecesrom/.local/pipx/venvs/interrogate/lib/python3.10/site-packages/cairocffi/__init__.py", line 48, in <module>
    cairo = dlopen(
  File "/Users/thecesrom/.local/pipx/venvs/interrogate/lib/python3.10/site-packages/cairocffi/__init__.py", line 45, in dlopen
    raise OSError(error_message)  # pragma: no cover
OSError: no library called "cairo-2" was found
no library called "cairo" was found
no library called "libcairo-2" was found
cannot load library 'libcairo.so.2': dlopen(libcairo.so.2, 2): image not found
cannot load library 'libcairo.2.dylib': dlopen(libcairo.2.dylib, 2): image not found
cannot load library 'libcairo-2.dll': dlopen(libcairo-2.dll, 2): image not found

Windows

> interrogate --version
Traceback (most recent call last):
  File "C:\Python\Python310\lib\runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Python\Python310\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "C:\Users\cesrom\.local\bin\interrogate.exe\__main__.py", line 4, in <module>
  File "C:\Users\cesrom\.local\pipx\venvs\interrogate\lib\site-packages\interrogate\cli.py", line 11, in <module>
    from interrogate import badge_gen
  File "C:\Users\cesrom\.local\pipx\venvs\interrogate\lib\site-packages\interrogate\badge_gen.py", line 15, in <module>
    import cairosvg
  File "C:\Users\cesrom\.local\pipx\venvs\interrogate\lib\site-packages\cairosvg\__init__.py", line 26, in <module>
    from . import surface  # noqa isort:skip
  File "C:\Users\cesrom\.local\pipx\venvs\interrogate\lib\site-packages\cairosvg\surface.py", line 9, in <module>
    import cairocffi as cairo
  File "C:\Users\cesrom\.local\pipx\venvs\interrogate\lib\site-packages\cairocffi\__init__.py", line 48, in <module>
    cairo = dlopen(
  File "C:\Users\cesrom\.local\pipx\venvs\interrogate\lib\site-packages\cairocffi\__init__.py", line 45, in dlopen
    raise OSError(error_message)  # pragma: no cover
OSError: no library called "cairo-2" was found
no library called "cairo" was found
no library called "libcairo-2" was found
cannot load library 'libcairo.so.2': error 0x7e
cannot load library 'libcairo.2.dylib': error 0x7e
cannot load library 'libcairo-2.dll': error 0x7e

What you expected to happen

interrogate should be able to run without errors.

How to reproduce (as minimally and precisely as possible)

  1. python -m pip install 'interrogate[png]' or pipx install 'interrogate[png]'
  2. Execute interrogate --version or interrogate --verbose <dir>
  3. Error

Anything else we need to know?

interrogate without the [png] extra works fine.

$ pipx install interrogate
  installed package interrogate 1.5.0, installed using Python 3.10.1
  These apps are now globally available
    - interrogate
done! ✨ 🌟 ✨
$ interrogate --version
interrogate, version 1.5.0

[Feature Request] Allow glob pattern for exclude paths

Describe the feature you'd like

Atm it is not possible to exclude files by a pattern, i.e. *tests*.
Which means that each path needs to be added explicitly to the exclude list.
While this might be ok for very small projects, it gets quite nasty for bigger projects.

Is your feature request related to a problem?

If you have a project where the tests are part of the package and its subpackages, i.e.:

project/
    tests/
     sub-package1/
         tests/
     sub-package2/
         tests/

You would need to add project/tests, project/ sub-package1/tests and project/ sub-package2/tests to be excluded, if you want to exclude all tests.

Additional context

The desired behaviour could be quite easily achieved by checking the path to exclude with fnmatch, this would at the same time solve #51 and could be included in #56 .

In [1]: from fnmatch import fnmatch

In [2]: test_paths = ["/home/project/tests", "/home/project/tests/test_file", r"C:\project\tests", r"C:\project\tests\test_file"]

In [3]: [fnmatch(test_path, "*tests") for test_path in test_paths]
Out[3]: [True, False, True, False]

In [4]: [fnmatch(test_path, "*tests*") for test_path in test_paths]
Out[4]: [True, True, True, True]

In [5]: [fnmatch(test_path, "*/tests*") for test_path in test_paths]
Out[5]: [True, True, True, True]

In [6]: [fnmatch(test_path, "/home/project/tests*") for test_path in test_paths]
Out[6]: [True, True, False, False]

In [7]: fnmatch(r"project\tests\test_file.py", "project/tests")
Out[7]: False

In [8]: fnmatch(r"project\tests\test_file.py", "project/tests*")
Out[8]: True

In [9]: fnmatch(r"project\tests", "project/tests")
Out[9]: True

Bug: `common_base` not found on Windows

First of all, I really like the intent of this package! 👍

Is this a BUG REPORT or FEATURE REQUEST?:

  • bug
  • feature

Description of Bug or Feature

Environment

  • interrogate version: 1.14
  • Operating System(s): Windows 10
  • Python version(s): 3.8

What happened

utils.get_common_base() does not find the common base for files on Windows.

This happens because path levels are split by \\ in Python on Windows. As a result, the common_base is an empty string (""). This has a downstream effect when generating the summary tables.

Specifically, in _get_filename(), since the common_base is an empty string, a filepath:

C:\path\to\project_root\src\file.py

becomes
:\path\to\project_root\src\file.py

instead of:
\src\file.py

Suggestion

Since this is a python 3.5+ project, I think that this could be fixed in a flexible way by handling filepaths with pathlib instead.
For example this in utils.py:

level_slices = zip(*[f.split("/") for f in files])

could be replaced with:

from pathlib import Path
level_slices = zip(*[Path(f).parts for f in files])

(if file names had not already been converted to Path objects)

Using pathlib here would help make this feature work across systems, but I think it could also replace just about all path manipulation interrogate uses os for.

If you'd like I could work contributing to any of these proposed changes.

Support for multiple styles for the interrogate badge

Is it possible for the interrogate badge to support all the styles supported by Shields.io, namely: plastic, flat, flat-square and for-the-badge? I wish to use the interrogate badge in my READMEs, but since I prefer the for-the-badge style, it ends up looking like the odd one out 😅 . I am using interrogate v1.3.2, with Python v3.8 and Ubuntu 20.04.

Inconsistency for default values in documentation

Description of the bug

It seems that documentation contains inconsistency about ignore-init-method and ignore-nested-classes in configuration section and command line options. Command line options seems to match to default values here (i.e. set on False) while the configuration section indicates that they are set to True by default.
I guess if this error is correct, it is required to modify the README.rst file?
Thanks

Don't require `toml` on Python 3.11

Describe the feature you'd like

Hi!

I'm currently upgrading the Python version on of my projects to Python 3.11 (which added tomllib to the standard library) and was delighted to see that almost all packages I use dropped the requirement for tomli:

Screenshot 2023-03-20 at 16 57 32

The only dependencies that don't use the the built in tomllib on Python 3.11 is django-stubs (typeddjango/django-stubs#1408) and interrogate.

I'm not sure if there's a need to switch from toml to tomli, but ideally neither should be required on Python 3.11 and the built in tomllib should be used.

As far as I see, the replacement should be pretty straightforward and I'd be happy to provide a PR.

Lastly, an example of how black dealt with this:

https://github.com/psf/black/blob/ef6e079901d53a42dfae4ab10b081ce7a73a47b5/pyproject.toml#L71

https://github.com/psf/black/blob/ef6e079901d53a42dfae4ab10b081ce7a73a47b5/src/black/files.py#L26-L34

Your Environment

  • interrogate version(s) (interrogate --version: 1.5.0
  • Operating System(s): macOS 12.6.3
  • Python version(s): 3.11

-o, --output flag doesn't create output directory for log file if it doesn't exist already

Environment

  • interrogate version(s): 1.5.0
  • Python version(s): 3.6-3.9

Description of the bug

When using the -o, --output flag, interrogate doesn't create the parent(s) of the file if they don't already exist:

$interrogate -vv -o build/coverage/docstring_coverage.log dir/
<traceback>
FileNotFoundError: [Errno 2] No such file or directory: 'build/coverage/docstring_coverage.log'

What you expected to happen

I would expect that interrogate would create the parent directories that do not exist (e.g. in this case build/coverage/.

Feature Request: Add sloth icon to badge

Describe the feature you'd like

Since icons are a great way to quickly navigate to what you are looking for and this project already has its happy sloth branding, IMHO it would be cool to also integrate it into the badge.

Is your feature request related to a problem?

Nope.

Additional context

This is my first draft on how the new badge could look like.
Disclaimers:

  • The width changed from 118 to 127, but most badges don't have a uniform width.
  • It's so pixelated since I had to convert it to png before uploading (github doesn't support svg in issues yet).
  • Looking at it now in direct comparison, the text looks a bit cramped (I'm far from a designer).

Original badges

badge_0
badge_40
badge_60
badge_75
badge_90
badge_95

Slothified badges

new_badge_0
new_badge_40
new_badge_60
new_badge_75
new_badge_90
new_badge_95

Ignore nested classes (and subnested functions)

Describe the feature you'd like

In the same vein as --ignore-nested-functions, It would be nice if we could have an --ignore-nested-classes (or additionally capture nested classes with --ignore-nested-functions).

Is your feature request related to a problem?

For instance, a lot of my classes in one of my projects have a nested Schema class which follows an almost identical structure and really doesn't need docstrings (or so I think) so it would be great if we could ignore them.

Your Environment

  • interrogate version(s) (interrogate --version: 1.3.2
  • Operating System(s): Ubuntu 18.04
  • Python version(s): 3.7.9

Additional context

I know this can be done with regex in this particular case since they all have the same name but I think it could be a handy feature nonetheless.

__init__.py files included despite pyproject.toml negating them

Environment

  • interrogate version(s) (interrogate --version: version 1.5.0
  • Operating System(s): Mac OS Ventura 13.2.1
  • Python version(s): 3.10.9

Description of the bug

pyproject.toml option ignore-init-module = true gets honoured when interrogate is launched from command line, but is NOT honoured when invoked from pre-commit

What you expected to happen

I would expect my init.py to be ignored when interrogate is invoked either way (command line or pre-commit)

How to reproduce (as minimally and precisely as possible)

.pre-commit-config.yaml contains the following:

repos:
  - repo: https://github.com/econchick/interrogate
    rev: 1.5.0
    hooks:
      - id: interrogate

pyproject.toml

[tool.interrogate]
ignore-init-method = true
ignore-init-module = true
ignore-magic = false
ignore-semiprivate = false
ignore-private = false
ignore-property-decorators = false
ignore-module = false
ignore-nested-functions = false
ignore-nested-classes = true
ignore-setters = false
fail-under = 95
exclude = ["docs", "rja_dev"]
ignore-regex = ["^get$", "^mock_.*", ".*BaseClass.*"]
# possible values: 0 (minimal output), 1 (-v), 2 (-vv)
verbose = 1
quiet = false
whitelist-regex = []
color = true
omit-covered-files = false
generate-badge = "."
badge-format = "svg"

Run from pre-commit:

(memaback-py3.10) (base) bob@Roberts-Mac-mini memaback % pre-commit run --all-files
interrogate..............................................................Failed
- hook id: interrogate
- exit code: 1

= Coverage for /Users/bob/Documents/work/code/memaback/ =
- Summary -
| Name                                         | Total | Miss | Cover | Cover% |
|----------------------------------------------|-------|------|-------|--------|
| src/memaback/__init__.py                     |     1 |    1 |     0 |     0% |
| src/memaback/mema_fetch_article_url.py       |     8 |    0 |     8 |   100% |
| src/memaback/mema_nlp_ensemble_experiment.py |    16 |    0 |    16 |   100% |
| tests/__init__.py                            |     1 |    1 |     0 |     0% |
| tests/test_mongoconn.py                      |     2 |    0 |     2 |   100% |
|----------------------------------------------|-------|------|-------|--------|
| TOTAL                                        |    28 |    2 |    26 |  92.9% |
- RESULT: FAILED (minimum: 95.0%, actual: 92.9%) -
Generated badge to /Users/bob/Documents/work/code/memaback/interrogate_badge.svg

Anthing else we need to know?

Feature Request: Flag to check pydocstyle

Is this a BUG REPORT or FEATURE REQUEST?:

  • bug
  • feature

Description of Bug or Feature

I regularly use flake8 and recently came across the flake8-docstrings extension which utilizes pydocstyle to check for various docstring content issues. It feels to me like pydocstyle is the kind of optional dependency/flag that could be incorporated here somehow. Possibly a third level of verbosity could look something like this?

$ interrogate -vvv [PATH]

==================== Coverage for /Users/lynn/dev/interrogate/ ======================
--------------------------------- Detailed Coverage ---------------------------------
| Name                                                                  |    Status |
|-----------------------------------------------------------------------|-----------|
| tests/unit/__init__.py (module)                                       |   COVERED |
|-----------------------------------------------------------------------|-----------|
| tests/unit/test_badge_gen.py (module)                                 |   COVERED |
|   test_save_badge (L14)                                               |   COVERED |
|     D201: No blank lines allowed before function docstring (found 1)  |   ------- |
|     D300: Use """triple double quotes""" (found '''-quotes)           |   ------- |
|   test_get_badge (L35)                                                |    MISSED |
|     D101: Docstring missing                                           |   ------- |
|   test_get_color (L44)                                                |   COVERED |
|   test_create (L61)                                                   |   COVERED |
|-----------------------------------------------------------------------|-----------|

Feel free to ignore/delete this issue if you aren't interested in going this direction. Great tool by the way though!

Missing .pre-commit-hooks.yaml in 1.1.3

Is this a BUG REPORT or FEATURE REQUEST?:

  • bug
  • feature

Description of Bug or Feature

The docs currently contain the following config for using interrogate with pre-commit:

repos:
  - repo: https://github.com/econchick/interrogate
    rev: 1.1.3
    hooks:
      - id: interrogate
        args: [--quiet, --fail-under=95]

However, that fails for me with Python 3.7 and pre-commit 2.3.0 with the error message

/home/my-user/.cache/pre-commit/repo59ixepcf/.pre-commit-hooks.yaml does not exist

And indeed, the file seems not be in the repo for the 1.1.3 tag. It is in master, so setting the rev to master is a temporary workaround. Obviously it would be desirable to pin a fixed version in the long run.

Add `ignore-typing-overload` option

Describe the feature you'd like

Consider the following example:

from typing import overload, Literal

class MyClass:
    @overload
    def something(self, raw=Literal[True]) -> bytes:
        ...

    @overload
    def something(self, raw=Literal[False]) -> str:
        ...

    def something(self, raw: bool = False) -> bytes | str:
        """My method that does something"""
        pass

It should be totally valid to have only final method documented and the first two methods to be totally blank on documentation.

Is your feature request related to a problem?

Incorrect documented/undocumented ratio.

Your Environment

  • interrogate version(s) (interrogate --version: 1.5.0
  • Operating System(s): Linux
  • Python version(s): 3.10

Additional context

So, just having something like this (default - False), would do the job:

[tool.interrogate]
# ...
ignore-typing-overload = true
# ...

Refuses to scan executable python scripts that don't end in .py

Description of the bug

In the code there's an automatic error if if not path.endswith(".py") but python executables usually lack the file extension.

What you expected to happen

Scan the python file.

How to reproduce (as minimally and precisely as possible)

Make a python file with a #! line, but no .py in the filename.

Anthing else we need to know?

As an example, we're trying to use interrogate with a project that has a couple of python scripts within the repo in a bin directory, and none of them end in .py.

❯ interrogate bin/postgres-ready
E: Invalid file '[...]/bin/postgres-ready'. Unable interrogate non-Python files.

❯ identify-cli bin/postgres-ready  # from pre-commit
["executable", "file", "python", "text"]

❯ file bin/postgres-ready
bin/postgres-ready: Python script text executable, ASCII text

❯ head -1 bin/postgres-ready
#!/usr/bin/env python

-e/--exclude doesn't work with pre-commit

Environment

  • interrogate version: 1.3.1
  • Operating System(s): Windows
  • Python version(s): 3.7.7

Description of the bug

Using the -e/--exclude argument works perfectly fine when using interrogate directly on the command line. However, when I pass it in my .pre-commit-config.yaml file like so:

- repo: https://github.com/econchick/interrogate
      rev: 1.3.1 # or master if you're bold
      hooks:
          - id: interrogate
            args: [--exclude=tests, --fail-under=100]

The argument doesn't seem to take effect. So far, I have resulted to using --ignore-regex which does work and achieves the same goal. Still, strange that --exclude doesn't seem to work. Maybe I am doing something wrong?

What you expected to happen

I expected --exclude to work using pre-commit.

Interrogate has a lower bound on the Click version it can use

Environment

  • interrogate version(s) (interrogate --version: 1.4.0
  • Operating System(s): Linux
  • Python version(s): 3.6.8

Description of the bug

I have a project that uses a particularly old version of Click (v6.7). In version 1.3.0 of interrogate, this is fine, but version 1.4.0 uses the case_sensitive option in click.Choice(), which isn't present in that version.

This isn't a problem with interrogate, as it's perfectly reasonable behaviour, but I think it's reasonable to declare that Click needs to be version 7.x and up.

What you expected to happen

I expected that, at install time, pip would indicate that interrogate requires at least Click v7 and up and that this wasn't being met currently.

How to reproduce (as minimally and precisely as possible)

In a virtual env, install Click v6.7, then install interrogate. Invoking interrogate will cause a stack trace.

Anthing else we need to know?

Not a functional problem with interrogate as such; this is a packaging nicety.

No output during failure when -o, --output specified

Environment

  • interrogate version(s): 1.5.0
  • Python version(s): 3.6-3.9

Description of the bug

When using the -o, --output flag, interrogate doesn't provide any output when the minimum docstring coverage is not met.

$interrogate -vv -o log.log dir/
$

What you expected to happen

I expect that at the very least interrogate would report why coverage failed. I think an improvement would be to change the -o, --output to not suppress stdout as I viewed that flag as a way to save a log of the results in CI. There is already a quiet flag which is why I was surprised when I saw coverage fail but no information as to why.

I think a better reporting structure would be something similar to darglint, flake8, black, etc. I find those useful because I can navigate directly to the file and line number from the terminal. When something fails, it always gives me exactly what I need in the terminal but verbosity allows for digging into deeper details when needed. When -o is specified, I think it should always write tables to the file with the highest verbosity. Then -v would increase the verbosity in stdout:

$ interrogate
FAILED (minimum: 100.0%, actual: 95.5%)
dir/file.py:125 some_fn MISSED
...

$ interrogate -v
FAILED (minimum: 100.0%, actual: 95.5%)
dir/file.py:125 some_fn MISSED
dir/file.py:130 some_cls COVERED
...

$ interrogate -vv
FAILED (minimum: 100.0%, actual: 95.5%)
<any internal logs>
dir/file.py:125 some_fn MISSED
dir/file.py:130 some_cls COVERED
dir/file.py:133 skip_me SKIPPED
dir/__init__.py SKIPPED
...

Then the passed case would be similar although I wouldn't expect it to spit anything out when interrogate passes unless I provide -v/-vv

Make Badge newline configurable or only update if content changed

Describe the feature you'd like

First of all thanks for the awesome tool 😄
I would love to use the badge creation feature as a pre-push hook in my projects, so the badge is always up to date.
But since each major OS has its own newline (Linux \n, MacOs '\r' and Windows \r\n), creating a new badge on push can lead to a diff, which basically is empty, blocking the push.
Since my projects (as most others I guess) are setup to have \n as newline, it would be nice to have badge_newline as a configurable option (this can be done with a kwarg in open).

Alternative solution

Another alternative would be to check if the text in interrogate_badge.svg did change and only write changes if that is the case.
That way the new line cofiguration would be left to the user (i.e. via .gitattributes), an not clutter the CLI and config of interrogate.

Is your feature request related to a problem?

Badge creation blocks usage with pre-commit.

Your Environment

  • interrogate version(s) (interrogate --version: 1.2.0
  • Operating System(s): Win10
  • Python version(s): 3.7.6

Allow -e to specify directory that doesn't exist

Describe the feature you'd like

E.g. when running interrogate locally I'd like to ignore build, but I don't need to ignore that during CI. But if I pass interrogate . -e build then I get

Error: Invalid value for '-e' / '--exclude': Path 'build' does not exist.

Your Environment

  • interrogate version(s) (interrogate --version: 1.2.0
  • Operating System(s): Linux
  • Python version(s): 3.7

`--fail-under` not working with float

Environment

  • interrogate version(s) (interrogate --version: 1.5.0
  • Operating System(s): Linux
  • Python version(s): 3.8

Description of the bug

Passing a float to --fail-under flag results in fail despite the coverage is exactly the same.

---------------------------------------------------------------- RESULT: FAILED (minimum: 50.8%, actual: 50.8%) ----------------------------------------------------------------

What you expected to happen

Expected PASSED.

How to reproduce (as minimally and precisely as possible)

Get the coverage of your code, then run again with --fail-under and pass the exact number.

Anthing else we need to know?

Support skip comment markup like # noqa

Awesome, extremely useful project, thank you!! (Just noticed I'm signed in using a GH account I created strictly for teaching purposes .. doh! My normal handle is @GavinHuttley).

Describe the feature you'd like

I have cases where we use decorators to extend docstrings from other methods / classes, etc.. Rather than try to catch such a special case, perhaps adding support for a comment annotation (like # noqa, as used by linters) that indicates a method / function to be ignored?

Is your feature request related to a problem?

Only in that methods documented in this way are not recognised as such.

Your Environment

  • interrogate version(s) (interrogate --version): 1.2.0
  • Operating System(s): All
  • Python version(s): 3.6+

Additional context

None.

Bug: Terminal width on Windows command is off by 1

Is this a BUG REPORT or FEATURE REQUEST?:

  • bug
  • feature

Description of Bug or Feature

When generating the report, the last "|"-char for the table is on the next line, causing the output to look really weird. Expanding the windows command (terminal) width by 1 makes everything look nice again.

Environment

  • interrogate version(s): 1.1.4
  • Operating System(s): Windows 10
  • Python version(s): 3.7.6

I have not been able to check if it works with other versions of interrogate, OS or Python.

What happened

Last "|" char in the table goes to the next line.

What you expected to happen

Expect every output line to not be wrapped like this to the next line.

How to reproduce it (as minimally and precisely as possible)

Open Windows Command and run interrogate to produce results.

Anything else we need to know?

I've checked that this is not due to the output requiring this due to long outputs. There is plenty of available padding in each cell of the output.

Thank you for this awesome tool! :)

PyPI classifiers including licence information not used in setup.py

Environment

  • interrogate version(s) (interrogate --version: 1.3.1
  • Operating System(s): Windows
  • Python version(s): Python 3.6

Description of the bug

On PyPI there is no licence meta information. Some systems automatically scan for this information to "approve of" certain packages.

I believe this is due to a bug in setup.py where CLASSIFIERS is not passed as an argument to setuptools.setup.

What you expected to happen

Should be simple fix which could be released as a patch version.

How to reproduce (as minimally and precisely as possible)

On https://pypi.org/project/interrogate left-hand column under "Meta", there should be something with "MIT" in there.

Anthing else we need to know?

No.

When Total=0 and Miss=0 I would expect the result to be SUCCESS rather than FAILED

Environment

python 3.7
interrogate 1.3.1

Description of the bug

When nothing is missed but the coverage is 0 because there was nothing to document, I would expect the result to be SUCCESS.

  • Summary -
    | Name | Total | Miss | Cover | Cover% |
    |-------|-------|------|-------|--------|
    |-------|-------|------|-------|--------|
    | TOTAL | 0 | 0 | 0 | 0.0% |
  • RESULT: FAILED (minimum: 60.0%, actual: 0.0%) -

What you expected to happen

I would expect the result to be successful

How to reproduce (as minimally and precisely as possible)

Create a module that do not contain any methods or properties. (In my case it was a module only defining type aliases.
Use this configuration:
[tool.interrogate]
ignore-init-method = true
ignore-init-module = true
ignore-magic = true
ignore-semiprivate = true
ignore-private = true
ignore-property-decorators = true
ignore-module = true
ignore-nested-functions = true
fail-under = 60
exclude = ["setup.py", "docs", "build", "test"]
verbose = 1
quiet = false
whitelist-regex = []
color = true

Split out the `--ignore-property-decorators` to allow ignore setter only

Describe the feature you'd like

Currently the -P, --ignore-property-decorators option ignores methods with property setter/getter decorators.
I (and potentially others) often just document the getter as IDEs like PyCharm will use the getter documentation for the setter too. This prevents the need to duplicate docstrings across the getter and setter.

It would be great if there was an option that allowed to just ignore the the setter only on a property.

Flag for color coded output

Is this a BUG REPORT or FEATURE REQUEST?:

  • bug
  • feature

Description of Bug or Feature

It would be really nice to get the output color-coded. Especially when -vv is flagged. In a reasonably large package in combination with a wide terminal output, correlating those "COVERED" and "MISSED" labels with the corresponding item is often difficult.

Having the option to print lines as green for covered items and red for missed items would be really great to quickly identify the items with no docstring.

Property deleter not being ignored

Environment

  • interrogate version(s) (interrogate --version): 1.5.0
  • Operating System(s): macOS
  • Python version(s): 3.9.10

Description of the bug

With ignore-property-decorators set to true, a property deleter is not being ignored.

What you expected to happen

A property deleter should be ignored the same way a property getter and setter is.

How to reproduce (as minimally and precisely as possible)

class C:
    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

Both class and __init__ documentation are required when they shouldnt be

Hiya,

interrogate assumes I need to document both the class and its __init__ method. Yet this is not required by tooling usually and is actually considered an anti-pattern - the class constructor is equivalent to documenting the class and vice versa.

That is, the tool requires something like this:

class MyClass:
    """
    This is MyClass and it has a valid docstring.
    """
    def __init__(self, some_arg: int):
        """        
        Args:
            some_arg: an integer
        """
        ...

Whereas the two examples below are in fact correct documentation that raises errors currently:

class MyClass:
    def __init__(self, some_arg: int):
        """ 
            This is MyClass and it has a valid docstring.
            
            Args:
                some_arg: an integer
        """
        ...

OR:

class MyClass:
    """
    This is MyClass and it has a valid docstring.

    Args:
        some_arg: an integer
    """
    def __init__(self, some_arg: int):
        ...

There is a good exposition on this https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html, which states:

    The __init__ method may be documented in either the class level
    docstring, or as a docstring on the __init__ method itself.

    Either form is acceptable, but the two should not be mixed. Choose one
    convention to document the __init__ method and be consistent with it.

Feature request: Option to accept inherited docstrings

Is this a BUG REPORT or FEATURE REQUEST?:

  • bug
  • feature

Description of Bug or Feature

Many tools (e.g. inspect.getdoc, Sphinx) allow methods in subclasses to inherit the docstring of the parent method:

class Foo:
    def do_it(self):
        """
        Do the thing!
        """
        pass

class SpecialFoo(Foo):
    def do_it(self):
        print('So special!')

In these cases, I usually only add a separate docstring to SpecialFoo.do_it if its behavior differs significantly from what one would expected after reading the docstring of Foo.do_it. In most cases, the docstring of Foo.do_it is just fine.

Hence I'd like an option to not count these cases as missing.

Feature Request: Flag for alternative formatting of line number output

Is this a BUG REPORT or FEATURE REQUEST?:

  • bug
  • feature

Description of Bug or Feature

I'm using interrogate a lot right now to go through projects and add missing docstrings (- vv flag mostly). VS Code (and other IDEs?) can convert the path to a file in the output as a link, so I can click to a file missing docstrings (technically with output from interrogate, I believe it's actually searching for the file. In my case, if the paths included another level or two, it would be a be proper link). If the path to a file is followed by :line_number, clicking the link jumps to the line of code.

It would be nice to have a flag which would include file_path:line_number in the output for each function found, so a user could jump to the function even faster.

So instead of something like this:

==================== Coverage for /Users/lynn/dev/interrogate/ ======================
--------------------------------- Detailed Coverage ---------------------------------
| Name                                                                  |    Status |
|-----------------------------------------------------------------------|-----------|
| tests/unit/__init__.py (module)                                       |   COVERED |
|-----------------------------------------------------------------------|-----------|
| tests/unit/test_badge_gen.py (module)                                 |   COVERED |
|   test_save_badge (L14)                                               |   COVERED |
|   test_get_badge (L35)                                                |   COVERED |
|   test_get_color (L44)                                                |   COVERED |
|   test_create (L61)                                                   |   COVERED |
|-----------------------------------------------------------------------|-----------|

The user could ask for this type of output

==================== Coverage for /Users/lynn/dev/interrogate/ ======================
--------------------------------- Detailed Coverage ---------------------------------
| Name                                                                  |    Status |
|-----------------------------------------------------------------------|-----------|
| tests/unit/__init__.py (module)                                       |   COVERED |
|-----------------------------------------------------------------------|-----------|
| tests/unit/test_badge_gen.py (module)                                 |   COVERED |
|   test_save_badge                                                     |   COVERED |
|       tests/unit/test_badge_gen.py:14                                 |           |
|   test_get_badge                                                      |   COVERED |
|       tests/unit/test_badge_gen.py:35                                 |           |
|   test_get_color                                                      |   COVERED |
|       tests/unit/test_badge_gen.py:44                                 |           |
|   test_create                                                         |   COVERED |
|       tests/unit/test_badge_gen.py:61                                 |           |
|-----------------------------------------------------------------------|-----------|

I'm not sure the best way to format it all, and it would be "messier", but would make it really easy to jump through these functions. I mostly use VS Code, but I'm not sure if other IDEs have similar linking.

Feature Request: Option to ignore nested functions

Is this a BUG REPORT or FEATURE REQUEST?:

  • bug
  • feature

Description of Bug or Feature

Nested functions (i.e. function definitions within function definitions) are usually small helpers that need no docstring and are not part of the public API. It should therefore be possible to optionally ignore them.

`ignore-semiprivate` does not ignore semiprivate modules

Describe the feature you'd like

In sigstore-python, we mark modules as internal / semiprivate by prefixing them with _. I'd like interrogate to consider members of these modules as implicitly semiprivate, just like how interrogate already ignores public members of semiprivate classes.

Here's a minimal example:

_test.py
# semiprivate class
class _Foo():

    # public member
    def bar():
        pass

# public class
class Foo():
    """Foo class."""

    # public member
    def bar():
        pass

When running python -m interrogate --ignore-semiprivate -vv _test.py, we get the following coverage report:

======================= Coverage for /Users/tnytown/Documents/sw/sigstore-python/test/ ========================
---------------------------------------------- Detailed Coverage ----------------------------------------------
| Name                                                      |                                          Status |
|-----------------------------------------------------------|-------------------------------------------------|
| _test.py (module)                                         |                                          MISSED |
|   Foo (L9)                                                |                                         COVERED |
|     Foo.bar (L13)                                         |                                          MISSED |
|-----------------------------------------------------------|-------------------------------------------------|

--------------------------------------------------- Summary ---------------------------------------------------
| Name                    |              Total |              Miss |              Cover |              Cover% |
|-------------------------|--------------------|-------------------|--------------------|---------------------|
| _test.py                |                  3 |                 2 |                  1 |                 33% |
|-------------------------|--------------------|-------------------|--------------------|---------------------|
| TOTAL                   |                  3 |                 2 |                  1 |               33.3% |
------------------------------- RESULT: FAILED (minimum: 100.0%, actual: 33.3%) -------------------------------

interrogate skips the semiprivate class and its member, lints the undocumented member of the public class, and also lints the missing module-level docstring. l However, in this situation, I would like interrogate to skip the whole module, as it is semiprivate.

Alternatively, if this isn't something you're comfortable with supporting, maybe the documentation can make it more clear that semiprivateness doesn't affect modules?

I'd be happy to submit a patch for either the behavioral change or the documentation :)

Your Environment

  • interrogate version(s) (interrogate --version: 1.5.0
  • Python version(s): Python 3.7.13

Add the ability to generate a badge in PNG format

Describe the feature you'd like

Add the ability to generate badge in the PNG format. Currently we run interrogate --generate-badge . to generate the badge (in SVG). If it was possible to do interrogate --generate-badge --filetype=png . or something like that would be cool.

Is your feature request related to a problem?

Yes. I'm having a problem where the Azure Devops README is not able to render SVG files for some reason.

Option to ignore overloaded functions

Describe the feature you'd like

When decorating functions with @typing.overload, the stubs are marked as not covered when they don't have their own docstring. My suggestion is to add an option to ignore those stubs.

Is your feature request related to a problem?

We have overloaded functions in our codebase that we want to start using this package in and we only document the actual implementation and not the stubs.

Your Environment

  • interrogate version(s) (interrogate --version: 1.5.0
  • Operating System(s): linux
  • Python version(s): 3.6-3.10

Additional context

PR incoming.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.