Skip to main content

A linting guardrail for LLM-generated Python code which raises structural doubts and a per-finding score before the code reaches a reviewer

Project description

humansays

humansays

CI PyPI Python License

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.

[!WARNING] Rule identifiers are unstable until 0.1.0. The current HS### identifiers may be renamed before then, so do not pin automation to them yet.

The current prerelease is v0.1.0a1. GitHub does not show it as the repository's latest release because it is marked as a prerelease.

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 self or 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:

  1. humansays.toml
  2. [tool.humansays] in pyproject.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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

humansays-0.1.0a2.tar.gz (29.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

humansays-0.1.0a2-py3-none-any.whl (38.8 kB view details)

Uploaded Python 3

File details

Details for the file humansays-0.1.0a2.tar.gz.

File metadata

  • Download URL: humansays-0.1.0a2.tar.gz
  • Upload date:
  • Size: 29.2 kB
  • Tags: Source
  • Uploaded using 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}

File hashes

Hashes for humansays-0.1.0a2.tar.gz
Algorithm Hash digest
SHA256 467131509f75d85b59089d76a7681e7d9874cb8472399dd9b923d2c4e15417f2
MD5 007659d898d95382e1dbc0e21ec1a88d
BLAKE2b-256 f473b1ab11da859d928d1ea60a98fb67f11209fa0ccc9ecc1d799f6984722357

See more details on using hashes here.

File details

Details for the file humansays-0.1.0a2-py3-none-any.whl.

File metadata

  • Download URL: humansays-0.1.0a2-py3-none-any.whl
  • Upload date:
  • Size: 38.8 kB
  • Tags: Python 3
  • Uploaded using 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}

File hashes

Hashes for humansays-0.1.0a2-py3-none-any.whl
Algorithm Hash digest
SHA256 2165ee4f32fa1be22430454b36fa26729f5eec3cfbd5a9a46a275372600c90e8
MD5 c618ba34cb74056b834085f6f03ccbd3
BLAKE2b-256 a6ff673f49a90ef7cc28f10b9745f8a555602561a620935ef8d32947847ca5c7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page