assert-no-comments
Assert that nothing in a tree carries a comment or a docstring.
Why
Prose beside code is never checked. The compiler does not read it, the tests do not run it, and no linter asks whether it is still true, so it stops being true and nothing says when. The reader who believes it then works from a program that no longer exists, which is worse than having read nothing at all.
A rename is where the cost shows up first. Change one identifier and every sentence naming it is now wrong, in files the change never touched, and the diff gives no way to find them.
So this tool takes the other position: a name, a signature and the shape of a function are the whole of what a reader gets. When that is not enough to say what a thing holds or does, the thing is named or shaped wrong rather than under-explained. The reasoning still gets written down, in the commit message and the issue, each of which is dated and attached to a change instead of sitting beside a line claiming to describe it forever.
Installation
pip install assert-no-comments
Usage
# Every tree this repository publishes
assert-no-comments .github/workflows etc lib scripts src test
# The same, leaving out code somebody else wrote
assert-no-comments .github/workflows etc lib scripts src test \
--exclude 'src/www/spa/vendor/*'
Options
| Option | Effect |
|---|---|
--exclude PATTERNS |
Comma-separated globs to leave out. |
--annotate |
Print each finding as a GitHub Actions ::error annotation. |
--quiet |
Print nothing; report through the exit code. |
--count |
Print only how many findings there were. |
--verbose |
Print the files read, the findings and a summary. |
--fail-fast |
Stop at the first finding. |
--warn-only |
Always exit 0. |
Exit codes
| Code | Meaning |
|---|---|
| 0 | Nothing carries a comment |
| 1 | Something carries a comment |
| 2 | A tree was missing, unreadable, or would not parse |
What counts as a comment
| Read as | Suffixes | Reported |
|---|---|---|
| Python | .py |
#, and any module, class or function docstring |
| YAML | .yml, .yaml |
# |
| OpenTofu | .tf, .tfvars, .hcl |
#, //, and /* */ |
| JavaScript | .js, .mjs, .cjs, .jsx |
//, /* */, {/* */} |
| TypeScript | .ts, .mts, .cts |
//, and /* */ |
| TSX | .tsx |
//, /* */, {/* */} |
{/* */} is the third form because it is the only one JSX has among the children of an element: a bare /* */ there is text that renders.
.tsx is its own row rather than a fourth suffix on the TypeScript one because the two are different dialects: <string>y asserts a type in one and opens an element in the other, and a reader that guessed wrong would read past the rest of the line.
A file whose suffix names none of those is left alone, so a .md file is never read: prose is the content of a markdown file rather than a gloss on a line of code. .terraform.lock.hcl is left alone too, because OpenTofu writes its header and nobody can delete it.
A docstring counts as a comment. It is the same prose in the same place, and the fact that a language keeps it in __doc__ does not make it any more likely to still be true.
Every language is read by its own grammar, so nothing here decides what a character is by looking at the characters beside it: Python through tokenize and ast, YAML through the yaml scanner, and JavaScript, TypeScript, TSX and OpenTofu through their tree-sitter grammars. A # inside a Python string, a YAML quoted scalar or block scalar, or an HCL heredoc is content. A // inside a JavaScript string, template literal or regular expression is content, a / after a name is division, the / closing a JSX tag is neither, and text between JSX tags renders on the page rather than commenting on it. Each of those is a question about where in the file the character sits, which is what a parser knows and a marker table does not.
A Python or YAML file that will not parse is an error rather than a file with nothing in it. A JavaScript, TypeScript, TSX or OpenTofu file that will not parse is read as far as its grammar gets: the comments it can still recognise are reported, so a broken file gives findings rather than an exit code of 2.
What is walked
A directory argument is read recursively. Hidden directories are read, because .github/workflows is one of the trees most worth checking. .git, __pycache__ and node_modules are skipped, and everything else you want left out goes in --exclude.
The usual thing to exclude is code somebody else wrote:
assert-no-comments src --exclude 'src/www/spa/vendor/*'
Nobody here can answer for a vendored library, and a finding against one is answerable by nobody, so it is not worth reporting.
GitHub Actions
- name: Assert nothing this repository publishes carries a comment
uses: 10U-Labs/assert-no-comments@latest
with:
exclude: src/www/spa/vendor/*
trees: .github/workflows etc lib scripts src test
annotate defaults to true there, so each finding lands on the line it names in the diff.
License
Apache-2.0
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file assert_no_comments-20260829162625.tar.gz.
File metadata
- Download URL: assert_no_comments-20260829162625.tar.gz
- Upload date:
- Size: 32.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c98dd5bd99a0875d80c70b9e7347f5405acf22666c84b881649c0a4829bd687
|
|
| MD5 |
8932c9f3a80f963057aea73b251a5e27
|
|
| BLAKE2b-256 |
b6fc3a29233cdf27aa8e27f84b3f1b86a37d1255f3141e6f848f99560e21967f
|
Provenance
The following attestation bundles were made for assert_no_comments-20260829162625.tar.gz:
Publisher:
release.yml on 10U-Labs/assert-no-comments
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
assert_no_comments-20260829162625.tar.gz -
Subject digest:
4c98dd5bd99a0875d80c70b9e7347f5405acf22666c84b881649c0a4829bd687 - Sigstore transparency entry: 2640086824
- Sigstore integration time:
-
Permalink:
10U-Labs/assert-no-comments@09612c8fc670da7023b9948c45c690e20f7d3326 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/10U-Labs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@09612c8fc670da7023b9948c45c690e20f7d3326 -
Trigger Event:
push
-
Statement type:
File details
Details for the file assert_no_comments-20260829162625-py3-none-any.whl.
File metadata
- Download URL: assert_no_comments-20260829162625-py3-none-any.whl
- Upload date:
- Size: 12.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
33534ff99ab8a60470d3b02a7a212769ea97738f6c202061a0875869fbf91743
|
|
| MD5 |
1f59744a1aa92ac0244b79a6c3ae3fd7
|
|
| BLAKE2b-256 |
7b2383b7a800d764ba5ec8d5815bf92394b8084d8a4e7158e982c76c4ccc22cb
|
Provenance
The following attestation bundles were made for assert_no_comments-20260829162625-py3-none-any.whl:
Publisher:
release.yml on 10U-Labs/assert-no-comments
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
assert_no_comments-20260829162625-py3-none-any.whl -
Subject digest:
33534ff99ab8a60470d3b02a7a212769ea97738f6c202061a0875869fbf91743 - Sigstore transparency entry: 2640086840
- Sigstore integration time:
-
Permalink:
10U-Labs/assert-no-comments@09612c8fc670da7023b9948c45c690e20f7d3326 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/10U-Labs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@09612c8fc670da7023b9948c45c690e20f7d3326 -
Trigger Event:
push
-
Statement type: