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.
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:
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
testortests, or - its name is
conftest.py, starts withtest_, 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.