python-linters
- bundles ruff and
flake8basedpyright into a singlepoetryuv-script - even though I liked some flake8-rules (especially from wemake-python-styleguide) dropped it in favor of ruff for being too slow
- project independent linter configuration (via
ruff.toml,.flake8pyrightconfig.json) -> one config to rule them all- well one can still override the default config via
ruff_extensions.tomlandpyrightconfig.json
- well one can still override the default config via
use-cases:
- interactively learning python code quality standards via ruff+pyright, see local development workflow
- enforcing minimal quality-standards via a "lint-stage" in a ci-pipeline, see setup for ci-pipeline
0. motivation:
Python is a Bicycle!
- initial idea of python:
Bridge the gap between the shell and C.see
run-time "safety"
- type and data validation at runtime
- pytests run your code -> runtime
lint-time "safety"
- linting and static type checking
- flake8 already fell down!
more "safety" via shorter feed-back loops
1. install (via poetry)
- in
pyproject.tomladd to dependencies
[tool.poetry.group.dev.dependencies]
python-linters = { version = "<some-version-here>" }
- in your
pyproject.tomlspecify the directories that you want to be linted (currentlytests-dir is added by default) a. viapackagespackages = [ { include = "my_package_name" }, ]
b. or viatool.python-linters[tool.python-linters] folders_to_be_linted=["my_directory","another_dir/my_sub_package"]
2. local development workflow (before pushing to ci-pipeline)
enhanced "usability" via VSCODE-Extension
- git clone this repo
- install ruff-extension
- add the following to your
settings.json
"ruff.lint.args": [
"--config=<wherever-you-git-cloned-it>/python-linters/python_linters/ruff.toml"
],
three scripts/commands that you can run locally
uv run fixcodeto let black+isort+ruff "auto-fix" your code -> always have acleangit-repo before doing this (no uncommitted changes)uv run pythonlinter-> to lint your code- example black is not happy -> if you ran
fixcodeblack is guaranteed to be happy! - example ruff is not happy
- example black is not happy -> if you ran
uv run addnoqato let ruff insert rules-violation-comments to your code
flowchart TD
classDef style0 fill:#ffb3ba
classDef style1 fill:#ffdfba
classDef style2 fill:#ffffba
classDef style3 fill:#baffc9
classDef style4 fill:#bae1ff
op1["start: have a 'clean' repo"]:::style4 --> op2
op2[\"uv run fixcode\n refactor & git commit"/]:::style1 --> cond9{{"uv run pythonlinter"}}:::style0
cond9 --> | complains | cond47{{"ruff"}}:::style0
cond47 --> | complains | op51[\"uv run addnoqa"/]:::style1
op51 --> sub53[\"refactor your code \nOR\n keep/add noqa-comment"/]:::style1
sub53 --> op2
cond47 --> | is happy | cond58{{"pyright"}}:::style0
cond58 --> | complains | sub53
cond58 --> | is happy | sub66["git commit"]
sub66 --> sub80
cond9 --> | is happy | sub80["git push"]:::style3
- write-operations (changing your code):
uv run fixcode,poerty run addnoqa,refactor your code - read-only-operations:
uv run pythonlinter,git commit,git push
3. setup for ci-pipeline
- if you specified the packages in the
pyproject.tomland you run just runuv run pythonlinterwithout any arguments than thepyproject.tomlis getting parsed and specified packages are getting linted
stages:
- lint # lint-stage!
- build
- test
- deploy
...
linting:
stage: lint
extends: .poetry
variables:
DEBIAN_FRONTEND: noninteractive
script:
- poetry install --only dev
- poetry run pythonlinter package_a package_b tests # specify folders to be linted
## OR
- poetry run pythonlinter # to be linted folders are parsed out of the pyproject.toml packages-include section
example outputs
fixcode example
cd <somehwere>/whisper-streaming
uv run fixcode
Fixing <somehwere>/whisper-streaming/whisper_streaming/post_asr_preparations.py
whisper_streaming/faster_whisper_inference/faster_whisper_inferencer.py:231:30: ARG005 Unused lambda argument: `x`
whisper_streaming/faster_whisper_inference/faster_whisper_inferencer.py:234:5: RET503 Missing explicit `return` at the end of function able to return non-`None` value
whisper_streaming/post_asr_preparations.py:113:5: ANN201 Missing return type annotation for public function `remove_empty_segments`
whisper_streaming/post_asr_preparations.py:113:27: ANN001 Missing type annotation for function argument `whisper_output_segments`
Found 5 errors (1 fixed, 4 remaining).
No fixes available (2 hidden fixes can be enabled with the `--unsafe-fixes` option).
reformatted <somehwere>/whisper-streaming/whisper_streaming/post_asr_preparations.py
reformatted <somehwere>/whisper-streaming/whisper_streaming/faster_whisper_inference/faster_whisper_inferencer.py
All done! ✨ 🍰 ✨
2 files reformatted, 20 files left unchanged.
folders_to_be_linted=['whisper_streaming', 'tests']
pythonlinter ruff is not happy
- complains about
- an unused argument
- a missing return statement
- missing type annotations
uv run pythonlinter
linter-order: black->ruff->pyright
...
running ruff
whisper_streaming/faster_whisper_inference/faster_whisper_inferencer.py:232:29: ARG005 Unused lambda argument: `x`
whisper_streaming/faster_whisper_inference/faster_whisper_inferencer.py:235:5: RET503 Missing explicit `return` at the end of function able to return non-`None` value
whisper_streaming/post_asr_preparations.py:114:5: ANN201 Missing return type annotation for public function `remove_empty_segments`
whisper_streaming/post_asr_preparations.py:114:27: ANN001 Missing type annotation for function argument `whisper_output_segments`
Found 4 errors.
No fixes available (2 hidden fixes can be enabled with the `--unsafe-fixes` option).
python_linters.run_linters.LinterException: 💩 ruff is not happy! 💩
addnoqa example
- only shows how many noqas it added (
3) and how many files it left unchanged (22)
uv run addnoqa
Added 3 noqa directives.
All done! ✨ 🍰 ✨
22 files would be left unchanged.
folders_tobelinted=['whisper_streaming', 'tests']
addnoqa iteration: 0
- it adds noqas like this one:
def foobar(x): # noqa: ANN001, ANN202
return "whatever"
IDE integration
manual vscode integration
- in
.vscode/tasks.json
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "fixcode",
"type": "shell",
"command": "cd $(cd ${fileDirname} && git rev-parse --show-toplevel) && uv run fixcode"
},
{
"label": "addnoqa",
"type": "shell",
"command": "cd $(cd ${fileDirname} && git rev-parse --show-toplevel) && uv run addnoqa"
},
{
"label": "pythonlinter",
"type": "shell",
"command": "cd $(cd ${fileDirname} && git rev-parse --show-toplevel) && uv run pythonlinter"
}
]
}
manual pycharm integration
alternatives
Metadata
Release files for python-linters 0.1.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| python_linters-0.1.7.tar.gz | 39.3 kB | Details |
Release files / python_linters-0.1.7.tar.gz
| Download URL | python_linters-0.1.7.tar.gz |
|---|---|
| Size | 39.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
28b070a389cb841e56119872d9e036bf4934e135f95a882ab1747afb4c2e46c3
|
|
BLAKE2b-256 checksum How to use checksums |
e960775b6c78d03137a78b1a84e3346ae85e524957293dbc0591c0468a514256
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.12.9
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 25, 2025.
Transparency log