Skip to content

Configuration

codaviz reads an optional [tool.codaviz] table from the analyzed project's pyproject.toml: the one in the directory you point it at.

[tool.codaviz]
exclude = ["generated/*.py", "vendor/**"]  # extra glob patterns to skip
max-complexity = 15                        # cyclomatic threshold for hints
max-cognitive = 15                         # cognitive threshold for hints
treemap-depth = 2                          # initial treemap depth (0 = all levels)
include-tests = false                      # set true to analyze test files too

Everything is optional; the values above are the defaults. A project with no pyproject.toml, or none of this table, gets them.

Settings

exclude

Extra glob patterns to skip, on top of the built-in exclusions.

exclude = ["generated/*.py", "vendor/**", "**/_pb2.py"]

Patterns are matched against each file's source-root-relative path with Python's Path.match. In a src/ layout the source root is src/, so a pattern targets mypkg/generated/*.py even though the file on disk is src/mypkg/generated/foo.py.

Note that Path.match is not fnmatch: a relative pattern matches from the right, so "*.py" matches every Python file at any depth, while "vendor/**" anchors on a vendor directory.

max-complexity

Cyclomatic complexity above which a function is flagged in the report, with a "consider extracting" hint. Also drives the Functions over CC stat card. Default 15.

Matching it to your Ruff C901 limit keeps the two tools telling the same story: codaviz's Python cyclomatic numbers come from mccabe, the same library Ruff uses.

max-cognitive

Cognitive complexity above which a function is flagged, with a "hard to follow" hint, and counted in the Functions over Cog stat card. Default 15.

Cognitive complexity is Python-only, so this has no effect on other languages.

treemap-depth

How many levels of the treemap render at once when the report opens. Default 2. Use 0 for all levels.

This is a starting value only. The report has a depth dropdown (1 / 2 / 3 / All) that overrides it at view time.

Deep trees need a low number. A flat project can afford 0.

include-tests

Set true to analyze test files too. Default false.

Test code has its own complexity profile: long parametrised fixtures, deliberate repetition. Mixing it into the hotspot ranking usually buries the production code you were looking for. Turn it on when the test suite itself is what you want to look at.

Which config applies where

Analyzing several roots at once means several pyproject.toml files. They are not merged:

Setting Read from
exclude each root's own pyproject.toml
include-tests each root's own pyproject.toml
max-complexity the first root only
max-cognitive the first root only
treemap-depth the first root only

Discovery settings are per-project, because each project knows what its own generated and vendored code is. The report-wide knobs come from the first root, because there is one report.

In a workspace, put the thresholds in the root you list first. Putting them in every one costs nothing and survives reordering.

What is excluded

Even with an empty config, codaviz skips a good deal.

Inside a git repository

File discovery goes through git:

git ls-files -z --cached --others --exclude-standard

which returns tracked and uncommitted files while honouring .gitignore. A fresh checkout works, and ignored trees never show up. Outside a repository, codaviz prints a notice on stderr and walks the filesystem instead.

Always-excluded directories

A file is dropped if any component of its path is one of:

.git .hg .svn .venv venv .env env __pycache__ .mypy_cache .ruff_cache .pytest_cache .tox .nox .eggs build dist node_modules site-packages migrations

Test files

Unless include-tests = true, a file is treated as a test if:

  • any path component is test or tests, or
  • its name is conftest.py, starts with test_, or ends with _test.py.

This heuristic is Python-shaped

It does not catch other ecosystems' conventions: foo.test.ts, foo.spec.js, and __tests__/ are all analyzed as production code. Exclude them by hand; see Languages.

Source-root detection

A src/ directory containing real Python (a .py file outside the excluded directories above) is treated as the import root. A src/mypkg layout therefore surfaces mypkg as a top-level package and does not nest everything under a meaningless src node.

This matters for correctness as well: module names for circular-import detection are computed relative to the source root, so a src/ layout resolves its own imports properly.

The "real Python" check mirrors the discovery filters on purpose. A src/ holding only, say, migrations/ is not selected as the source root, since otherwise it would be chosen and then filtered down to nothing, silently dropping the project.