This release is a pre-release and may not be stable for production use.
humansays
A python linter which evaluates how difficult AI generated code is to read, understand, test, maintain, and safely modify. It gives coding agents actionable and easy to reason about feedback on design problems that traditional linters, type checkers, and tests cannot detect.
Full documentation: https://rhawk117.github.io/humansays/
humansays parses Python with the standard-library AST and points reviewers toward code that deserves a second look. It reports structural signals such as long parameter lists, deep nesting, mutable module state, lazy imports, oversized functions and classes, and methods that use neither self nor the class.
It does not claim that a finding is a bug. Each finding is a review lead: a location, a score, and a question worth asking before the code reaches production.
Why this exists
Linters are good at detecting known mistakes. Type checkers are good at proving whether values satisfy declared interfaces. Neither is responsible for asking whether a function has accumulated too many jobs, whether a class is carrying unrelated state, or whether generated code is technically valid but unpleasant to maintain.
That is the gap humansays targets.
It gives a human reviewer, or a tool acting on behalf of one, a smaller set of places to inspect. The output is intentionally framed as questions rather than verdicts because syntax alone cannot prove intent.
Install
humansays requires Python 3.11 or newer.
pip install humansays
There are no runtime dependencies and no extras. Text output is written with
plain ANSI escapes, which NO_COLOR, FORCE_COLOR, and TERM=dumb control.
Quick start
Scan a directory:
humansays src/
Scan specific files:
humansays app.py tests/test_app.py
Read tracked Python paths from standard input:
git ls-files "*.py" | humansays
Produce machine-readable output:
humansays --format json src/
Fail CI when warnings are present:
humansays --fail-on warning src/
Fail CI when the project score drops below a threshold:
humansays --min-score 70 src/
Inspect one symbol:
humansays --symbol build_report src/
What it reviews
humansays looks for structural signals that often justify manual review, including:
- functions with many arguments, branches, nested blocks, or source lines
- classes with many attributes or base classes
- modules that have grown beyond a configured size
- mutable state defined at module scope
- imports performed inside functions or methods
- methods that do not use
selfor the class
A finding means "inspect this," not "rewrite this." Large functions and mutable state can be reasonable. The tool is useful because those choices should usually be deliberate.
Command-line options
| Option | Default | Effect |
|---|---|---|
--format text|json |
text |
Select the report format |
--limit N |
200 |
Show at most N targets; 0 shows all |
--symbol NAME |
none | Restrict output to one symbol |
--exclude PATTERN |
none | Skip matching paths; repeatable |
--fail-on never|warning|any |
never |
Exit nonzero when findings reach the selected level |
--min-score N |
none | Exit nonzero when the score is below N |
--config PATH |
auto | Load an explicit configuration file |
Configuration
humansays reads configuration from either:
humansays.toml[tool.humansays]inpyproject.toml
The first discovered configuration source is used. Command-line arguments override file settings.
The block below is an example of loosened thresholds, not the defaults. Every key and its real default is documented at Configuration.
[tool.humansays.report]
format = "text"
limit = 50
fail_on = "warning"
min_score = 70.0
[tool.humansays.selection]
exclude = ["migrations", "generated"]
[tool.humansays.thresholds.functions]
max_arguments = 5
max_nesting = 4
max_branches = 12
max_lines = 60
[tool.humansays.thresholds.classes]
max_attributes = 10
max_base_classes = 2
[tool.humansays.thresholds.modules]
max_lines = 500
The same settings can live in a standalone humansays.toml file without the tool.humansays prefix:
[report]
format = "text"
limit = 50
fail_on = "warning"
min_score = 70.0
[selection]
exclude = ["migrations", "generated"]
[thresholds.functions]
max_arguments = 5
max_nesting = 4
max_branches = 12
max_lines = 60
[thresholds.classes]
max_attributes = 10
max_base_classes = 2
[thresholds.modules]
max_lines = 500
CI usage
humansays does not fail a command merely because it found something. The default --fail-on never keeps the report advisory.
Use --fail-on or --min-score when you want enforcement:
humansays --format text --fail-on warning --min-score 70 src/
For tools that consume structured results:
humansays --format json src/ > humansays-report.json
Rule identifiers may change before 0.1.0, so prerelease integrations should consume the report as a whole rather than treating the current identifiers as a stable API.
Exit codes
| Code | Meaning |
|---|---|
0 |
The command completed without crossing a configured failure threshold |
1 |
Findings crossed --fail-on, or the score fell below --min-score |
2 |
The requested --symbol was not found |
3 |
No paths were given, or no Python files were found |
4 |
The configuration could not be loaded |
5 |
One or more files could not be analyzed |
70 |
Internal error — this is a humansays bug |
A file that cannot be parsed, read, or decoded is reported and makes the run
exit 5. Findings take precedence: a run with both findings and unanalyzed
files exits 1. The score and grade cover only the files that were analyzed,
and the text report names the gap. Enforced by
tests/cli/test_exit_contract.py.
Scope and limitations
humansays is not a replacement for Ruff, Pylint, a type checker, tests, or a security scanner.
It does not:
- prove runtime behavior from syntax
- determine whether code was written by a person or a model
- decide that every reported structure is incorrect
- compete with general-purpose linters on speed or rule coverage
It reports syntax-level patterns that commonly benefit from a second reader. The reviewer still decides whether the code is clear, justified, and worth changing.
Development status
humansays is currently alpha software. Configuration, output details, scoring behavior, and rule identifiers may change before the first stable release.
Bug reports and focused examples are useful, especially cases where a finding is consistently noisy or where an obvious structural problem is missed.
License
MIT
Metadata
Release files for humansays 0.1.0a2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| humansays-0.1.0a2.tar.gz | 29.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| humansays-0.1.0a2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 68.0 kB
Release files / humansays-0.1.0a2.tar.gz
| Download URL | humansays-0.1.0a2.tar.gz |
|---|---|
| Size | 29.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
467131509f75d85b59089d76a7681e7d9874cb8472399dd9b923d2c4e15417f2
|
|
BLAKE2b-256 checksum How to use checksums |
f473b1ab11da859d928d1ea60a98fb67f11209fa0ccc9ecc1d799f6984722357
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / humansays-0.1.0a2-py3-none-any.whl
| Download URL | humansays-0.1.0a2-py3-none-any.whl |
|---|---|
| Size | 38.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2165ee4f32fa1be22430454b36fa26729f5eec3cfbd5a9a46a275372600c90e8
|
|
BLAKE2b-256 checksum How to use checksums |
a6ff673f49a90ef7cc28f10b9745f8a555602561a620935ef8d32947847ca5c7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|