Skip to content

one-function-per-file ​

A source file holds at most one module-scope function whose body runs longer than the configured threshold. Functions at or under the threshold are trivial — an expression with a name — and share a file freely. This page is the complete record of the check.

Why this check exists ​

colocated-test pairs widget.py with widget_test.py, and that pairing is worth as much as the file is coherent. A file holding ten functions still has one colocated test, so the 1:1 mapping describes the file, not the code in it. One function per file closes that gap: the file name is the subject, so a coverage or mutation result reported against a file is a result about one function.

It also gives an agentic edit a structural stopping point. "Add the helper here" is the cheapest move available, and it is how a 40-line module becomes a 400-line one with a single sprawling test beside it.

What it flags ​

Every module-scope function past the first whose body runs longer than max_lines, reported as path:line: one-function-per-file — <name> runs N lines, and <holder> already holds this file. The first over-threshold function in a file holds it; each later one is a violation naming both.

A function's length is the count of code lines in its body — blank lines, comments, and a Python docstring don't count, and neither does the signature.

What counts as module-scope, per language:

LanguageSubjects
Pythondef and async def at module level
TypeScripttop-level function declarations, and a top-level const / let / var bound to an arrow function or function expression, export or not
Rustfn items at the top level of the file

Methods, nested functions, and callbacks belong to their owner and are never counted on their own. The scan reads the same source tree colocated-test does, so test files, Rust's inline #[cfg(test)] modules, and the suite tiers under <package root>/tests/ are all outside it.

What a passing run reports ​

A passing run prints one line to stderr, alongside the version banner:

one-function-per-file: scanned 8 file(s), 0 violations

The count is the number of source files the scan actually inspected, so a wrong path, an empty directory, or a language filter that matches nothing reads as scanned 0 file(s) — visibly different from a real pass, rather than the version banner alone either way.

When it runs ​

Always, as a step of the Static checks (<language>) job, for Python, TypeScript, and Rust alike. It scans the source files under source. The gates input names it one-function-per-file.

Rust is off until you opt in. Python and TypeScript run at the default threshold; a Rust run with no [rust].one_function_per_file table reports that the check is not enabled and passes:

unit one-function-per-file: not enabled for rust — set `[rust].one_function_per_file` to opt in

In Python and TypeScript a file is a bag of definitions, so "one subject per file" is a choice the author makes. In Rust a file is a module, and grouping a type, its impl blocks, and the free functions around it inside one is how Rust is written. The capability is identical in all three languages — a Rust tree that wants the check names a threshold and gets exactly what Python and TypeScript get. Only the default differs. See One function per file.

Configuration ​

The check takes one key per language table, one_function_per_file, whose max_lines sets the threshold:

LanguageDefault
Pythonmax_lines = 1
TypeScriptmax_lines = 1
Rustoff until the table names a threshold
toml
[python]
one_function_per_file = { max_lines = 5 }

[rust]
one_function_per_file = { max_lines = 8 }

A tree adopting the check onto existing source starts at a threshold that passes and walks it down; raising the number never turns the check off, it only moves the line between "trivial enough to share" and "substantial enough to own the file".

A file whose functions genuinely belong together takes a one-function-per-file[[<language>.exempt]] entry with a required reason:

CheckLanguageLifts
one-function-per-filePython, TypeScript, Rustthe check for one file (a generated module, a table of small constructors, a public surface re-exported as one API)

Learn more ​