Rigorous linting and code quality setup for Python projects with pre-commit hooks
Project description
Hard-Lint for Python 🐍
Rigorous linting and code quality setup for Python projects with automatic pre-commit hooks.
Features
- Automated Pre-commit Hooks: Validates code before every commit
pre-commit: Runsruff,black, andisortcommit-msg: Validates messages follow Conventional Commits format
- Strict Linting: Ruff checks for errors, complexity, and code quality
- Synchronized Formatters: Ruff, Black, and isort configured with same profile (no conflicts)
- Zero Configuration: Works out-of-the-box with sensible defaults
- Multiplattform: Works on Windows, macOS, and Linux
- Auto-setup: Single command to enable all checks
Installation
pip install hard-lint-py
# or with poetry
poetry add -D hard-lint-py
Quick Start
# 1. Install and setup hooks
hard-lint-py
# 2. Make your first commit
git add .
git commit -m "feat: initial setup"
That's it! Your project now has:
- Ruff for fast Python linting
- Black for code formatting
- isort for import sorting
- Commitlint for commit message validation
What Gets Installed
Git Hooks
Hooks are created in .hardlint/_/:
-
pre-commit: Runs before commits to validate code
- Fixes issues with
ruff check --fix - Formats with
black - Sorts imports with
isort - Validates no comments exist
- Fixes issues with
-
commit-msg: Validates commit messages
- Must follow Conventional Commits format
- Examples:
feat:,fix:,chore:,docs:, etc.
Linting Rules
Ruff Rules Enforced
| Rule | Category | Description |
|---|---|---|
| E | pycodestyle errors | PEP 8 compliance |
| F | Pyflakes | Unused imports, undefined names |
| W | pycodestyle warnings | Code warnings |
| I | isort | Import sorting |
| N | pep8-naming | Naming conventions |
| C | mccabe | Code complexity |
| B | flake8-bugbear | Common bugs and design problems |
| RUF | Ruff-specific | Additional quality checks |
| UP | pyupgrade | Modernize Python syntax |
Synchronized Formatter Configuration
All three formatters (Ruff, Black, isort) use the same profile to prevent conflicts:
| Aspect | Configuration | Notes |
|---|---|---|
| Line Length | 100 characters | Consistent across all tools |
| Python Version | 3.10+ | Targets modern Python |
| Imports Profile | isort: black |
Compatible with Black |
| Trailing Commas | Enabled | Multi-line consistency |
Configuration
Auto-configured in pyproject.toml:
[tool.black]
line-length = 100
target-version = ["py310", "py311", "py312"]
[tool.isort]
profile = "black" # Compatible with Black
line_length = 100
known_first_party = ["hard_lint_py"]
[tool.ruff]
line-length = 100
target-version = "py310"
select = ["E", "F", "W", "I", "N", "C", "B", "RUF", "UP"]
Usage
Normal workflow
# Make changes
cat > new_file.py << 'EOF'
def greet(name):
# Greet a person by name
return f"Hello, {name}!"
EOF
# Stage changes
git add new_file.py
# Commit (hooks will run automatically)
git commit -m "feat: add greeting function"
Pre-commit Hook Behavior
When you commit, the hook automatically:
- Ruff: Checks for linting issues, imports, and code quality
- Black: Formats code to 100-character lines
- isort: Sorts and organizes imports
- Blocks commit if any issues can't be auto-fixed
If pre-commit fails:
# Fix will be auto-applied, just re-add and commit
git add .
git commit -m "feat: your message"
If commit-msg fails
Invalid message example:
git commit -m "added this feature" # ❌ Missing type prefix
Valid message example:
git commit -m "feat: added new feature" # ✅ Has type prefix
Skip hooks (use with caution)
git commit -m "..." --no-verify
Configuration
All tools read from pyproject.toml. Customize as needed:
[tool.ruff]
line-length = 120
select = ["E", "F", "W"]
[tool.black]
line-length = 120
[tool.isort]
profile = "black"
line_length = 120
Troubleshooting
Hooks not running?
# Re-run installation
hard-lint-py
Commit-msg validation failing?
- Check that your message starts with a type:
feat:,fix:,chore:, etc.
Formatters conflicting with each other?
- They shouldn't! Ruff, Black, and isort are configured with the same profile
- If you see conflicts, run all three:
ruff check --fix,black .,isort .
Line length conflicts?
- All tools configured to 100 characters
- Black and isort won't fight over formatting
- Ruff respects Black's decisions (E501 ignored)
Supported Python Versions
- Python 3.10+
- Python 3.11
- Python 3.12
Development
When developing with hard-lint-py:
Pre-commit Validation
Your code will be validated on every commit:
git commit -m "feat: your feature"
# Runs: ruff check --fix → black → isort → commitlint
Manual Validation
Run validation checks manually:
# All checks
make lint
make format
make test
make test-cov
# Individual tools
poetry run ruff check src/ tests/
poetry run black --check src/ tests/
poetry run isort --check-only src/ tests/
Quality Standards
- ✅ 99% code coverage
- ✅ No unused imports or variables
- ✅ Consistent formatting (100 char lines)
- ✅ Code complexity within limits
- ✅ Modern Python syntax
- ✅ No common bugs or design problems
License
MIT
Author
Naylson Ferreira
Project details
Release history Release notifications | RSS feed
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 hard_lint_py-0.0.1.tar.gz.
File metadata
- Download URL: hard_lint_py-0.0.1.tar.gz
- Upload date:
- Size: 5.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
45aea94e878916f10a1b9fb11aec05a7e6591fa6075641f3f4acaa4e4979a248
|
|
| MD5 |
d29c5f29802c9335a536a2688a02def8
|
|
| BLAKE2b-256 |
9ddfdfd6c18f9eae66750fca72538082b91694f871116fcc2a38530342f6e6cf
|
Provenance
The following attestation bundles were made for hard_lint_py-0.0.1.tar.gz:
Publisher:
publish.yml on naylsonferreira/hard-lint-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hard_lint_py-0.0.1.tar.gz -
Subject digest:
45aea94e878916f10a1b9fb11aec05a7e6591fa6075641f3f4acaa4e4979a248 - Sigstore transparency entry: 1062713640
- Sigstore integration time:
-
Permalink:
naylsonferreira/hard-lint-py@c7c5ad550ac6af9fb4a0525f86a1c0023e61df34 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/naylsonferreira
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c7c5ad550ac6af9fb4a0525f86a1c0023e61df34 -
Trigger Event:
push
-
Statement type:
File details
Details for the file hard_lint_py-0.0.1-py3-none-any.whl.
File metadata
- Download URL: hard_lint_py-0.0.1-py3-none-any.whl
- Upload date:
- Size: 6.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e828456099a05c8b7ebed846bea81577e7da1a08513e4c5a8163f9b9c0d0d218
|
|
| MD5 |
3dab92cff9fd5baa64b90f305d4f475a
|
|
| BLAKE2b-256 |
aea68f3215d6ede085117c012158881f06a26472b21128af03c5549ee3c162c2
|
Provenance
The following attestation bundles were made for hard_lint_py-0.0.1-py3-none-any.whl:
Publisher:
publish.yml on naylsonferreira/hard-lint-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hard_lint_py-0.0.1-py3-none-any.whl -
Subject digest:
e828456099a05c8b7ebed846bea81577e7da1a08513e4c5a8163f9b9c0d0d218 - Sigstore transparency entry: 1062713896
- Sigstore integration time:
-
Permalink:
naylsonferreira/hard-lint-py@c7c5ad550ac6af9fb4a0525f86a1c0023e61df34 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/naylsonferreira
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c7c5ad550ac6af9fb4a0525f86a1c0023e61df34 -
Trigger Event:
push
-
Statement type: