Skip to main content

Well, actually — an opinionated Python linter and formatter: bans else and elif, allows only flat ternaries, and enforces blank lines around return

Project description

actually

Well, actually, your code should read like this.

actually is a highly opinionated Python linter and formatter built on ast-grep. It enforces a guard-clause style through rules with ruff-style stable codes, grouped by language construct (ADR 1). Every rule is checkable; the auto-fix column marks what format can rewrite. Each rule links to its documentation page with rationale and a banned/wanted example pair — generated from rules.toml and validated by the linter itself (ADR 2):

actually-conditionals

Code Rule Status Auto-fix What it enforces
ACTC001 no-else stable partial else on if, and the completion clauses on for, while, try
ACTC002 no-elif stable no elif — use guard clauses or a dispatch table
ACTC003 ternary-not-nested stable no a ternary inside another ternary's arm (elif in expression form)
ACTC004 ternary-not-empty unstable no a degenerate ternary arm (None, "", empty container) — conditional inclusion in disguise

actually-literals

Code Rule Status Auto-fix What it enforces
ACTL001 trailing-comma unstable yes a dict/list/set literal whose last element lacks a trailing comma
ACTL002 one-element-per-line unstable partial a dict/list/set literal with elements sharing a line with a bracket or each other

actually-returns

Code Rule Status Auto-fix What it enforces
ACTR001 blank-before-return stable yes a return stacked directly under other statements in its block
ACTR002 blank-after-return stable yes code directly under a return line

Usage

uvx well-actually@latest check .
uvx well-actually@latest format .

The @latest matters: a bare uvx well-actually reuses a cached tool environment and can silently run an outdated version; @latest re-resolves against the index every time.

Installed (uv tool install well-actually), the short command is available too:

actually check .
actually format .

check reports violations and exits non-zero when it finds any. Both commands lint .py files only; directory scans skip environment, cache, and VCS directories (.venv, venv, .git, __pycache__, node_modules, and friends) and respect .gitignore files — nested ones and negations included, matched via pathspec (black's approach), so no git installation is required. When a .git directory is found above the scanned path, .gitignore files up to that repo root apply as well. Global excludes (core.excludesFile, .git/info/exclude) are not consulted. A .py file passed explicitly is always linted.

format rewrites files in place, then reports what it could not fix:

  • inserts the missing blank lines around return
  • dedents a try/except/else completion clause into straight-line code when every except body already exits (return, raise, continue, break) — when one falls through, the rewrite would change behaviour, so it is reported for human refactoring instead
  • rewrites dict/list/set literals to one element per line with a trailing comma — literals carrying comments or multiline elements are reported for human formatting instead
  • --only-autofixable makes it best effort: every available fix is applied, the remaining violations are still reported, and the exit code stays 0

Example

def describe_config(path):
    try:
        config = parse_json_file(path)
    except ParseError:
        return "invalid config"
    else:
        return describe(config)

actually format rewrites this to:

def describe_config(path):
    try:
        config = parse_json_file(path)
    except ParseError:
        return "invalid config"

    return describe(config)

Development

uv sync
mise install
hk install
uv run pytest

README.md and rules/*.md are generated from README.template.md and rules.toml by scripts/generate_docs.py; an hk pre-commit hook regenerates and stages them. Edit the sources, never the outputs.

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

well_actually-0.4.0.tar.gz (10.4 kB view details)

Uploaded Source

Built Distribution

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

well_actually-0.4.0-py3-none-any.whl (14.3 kB view details)

Uploaded Python 3

File details

Details for the file well_actually-0.4.0.tar.gz.

File metadata

  • Download URL: well_actually-0.4.0.tar.gz
  • Upload date:
  • Size: 10.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","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 well_actually-0.4.0.tar.gz
Algorithm Hash digest
SHA256 36d6e211cbff5ada4c38cc6514792b1c530f657ac1c711092ecf1aa5283e2eaa
MD5 32d2e64a36ad6b5ef470f5afe2baf201
BLAKE2b-256 45dfc17e84eb5e07cc499f0ab847eb0eff4f0f5d2f086bfc064f1f4ea73af18e

See more details on using hashes here.

File details

Details for the file well_actually-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: well_actually-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 14.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","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 well_actually-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bbb89db54ddfd6362e6fbf8f33dc3bc20cfcb3b7f23a7e800247e0f042b07708
MD5 a67f1e0df6bda79b6e61e7fb67e38b01
BLAKE2b-256 24f4e1cefef4f22dc91e9b2cf2ce3a9530099fd4ffbd51c8a752cbc4c6a5f19b

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