python-linters 0.1.4__tar.gz → 0.1.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- python_linters-0.1.6/.gitignore +161 -0
- python_linters-0.1.4/README.md → python_linters-0.1.6/PKG-INFO +52 -57
- python_linters-0.1.4/PKG-INFO → python_linters-0.1.6/README.md +38 -82
- python_linters-0.1.6/pyproject.toml +40 -0
- {python_linters-0.1.4 → python_linters-0.1.6}/python_linters/add_noqa.py +8 -6
- {python_linters-0.1.4 → python_linters-0.1.6}/python_linters/config_files.py +0 -2
- {python_linters-0.1.4 → python_linters-0.1.6}/python_linters/extending_ruff_toml.py +3 -3
- python_linters-0.1.6/python_linters/fix_code.py +25 -0
- {python_linters-0.1.4 → python_linters-0.1.6}/python_linters/getting_to_be_linted_folders.py +2 -11
- {python_linters-0.1.4 → python_linters-0.1.6}/python_linters/pyrightconfig.json +5 -1
- {python_linters-0.1.4 → python_linters-0.1.6}/python_linters/ruff.toml +6 -6
- {python_linters-0.1.4 → python_linters-0.1.6}/python_linters/ruff_rule_categories.py +4 -2
- python_linters-0.1.6/python_linters/ruff_rules.txt +1182 -0
- python_linters-0.1.6/python_linters/run_linters.py +49 -0
- python_linters-0.1.6/python_linters/utils.py +72 -0
- python_linters-0.1.4/pyproject.toml +0 -34
- python_linters-0.1.4/python_linters/.flake8 +0 -32
- python_linters-0.1.4/python_linters/fix_code.py +0 -29
- python_linters-0.1.4/python_linters/ruff_rules.txt +0 -956
- python_linters-0.1.4/python_linters/run_linters.py +0 -42
- {python_linters-0.1.4 → python_linters-0.1.6}/LICENSE +0 -0
- {python_linters-0.1.4 → python_linters-0.1.6}/python_linters/__init__.py +0 -0
- {python_linters-0.1.4 → python_linters-0.1.6}/python_linters/parse_rules.py +0 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
_version.py
|
|
2
|
+
# Byte-compiled / optimized / DLL files
|
|
3
|
+
__pycache__/
|
|
4
|
+
*.py[cod]
|
|
5
|
+
*$py.class
|
|
6
|
+
|
|
7
|
+
# C extensions
|
|
8
|
+
*.so
|
|
9
|
+
|
|
10
|
+
# Distribution / packaging
|
|
11
|
+
.Python
|
|
12
|
+
build/
|
|
13
|
+
develop-eggs/
|
|
14
|
+
dist/
|
|
15
|
+
downloads/
|
|
16
|
+
eggs/
|
|
17
|
+
.eggs/
|
|
18
|
+
lib/
|
|
19
|
+
lib64/
|
|
20
|
+
parts/
|
|
21
|
+
sdist/
|
|
22
|
+
var/
|
|
23
|
+
wheels/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
# Usually these files are written by a python script from a template
|
|
32
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
33
|
+
*.manifest
|
|
34
|
+
*.spec
|
|
35
|
+
|
|
36
|
+
# Installer logs
|
|
37
|
+
pip-log.txt
|
|
38
|
+
pip-delete-this-directory.txt
|
|
39
|
+
|
|
40
|
+
# Unit test / coverage reports
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
.nox/
|
|
44
|
+
.coverage
|
|
45
|
+
.coverage.*
|
|
46
|
+
.cache
|
|
47
|
+
nosetests.xml
|
|
48
|
+
coverage.xml
|
|
49
|
+
*.cover
|
|
50
|
+
*.py,cover
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
cover/
|
|
54
|
+
|
|
55
|
+
# Translations
|
|
56
|
+
*.mo
|
|
57
|
+
*.pot
|
|
58
|
+
|
|
59
|
+
# Django stuff:
|
|
60
|
+
*.log
|
|
61
|
+
local_settings.py
|
|
62
|
+
db.sqlite3
|
|
63
|
+
db.sqlite3-journal
|
|
64
|
+
|
|
65
|
+
# Flask stuff:
|
|
66
|
+
instance/
|
|
67
|
+
.webassets-cache
|
|
68
|
+
|
|
69
|
+
# Scrapy stuff:
|
|
70
|
+
.scrapy
|
|
71
|
+
|
|
72
|
+
# Sphinx documentation
|
|
73
|
+
docs/_build/
|
|
74
|
+
|
|
75
|
+
# PyBuilder
|
|
76
|
+
.pybuilder/
|
|
77
|
+
target/
|
|
78
|
+
|
|
79
|
+
# Jupyter Notebook
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
|
|
82
|
+
# IPython
|
|
83
|
+
profile_default/
|
|
84
|
+
ipython_config.py
|
|
85
|
+
|
|
86
|
+
# pyenv
|
|
87
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
88
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
89
|
+
# .python-version
|
|
90
|
+
|
|
91
|
+
# pipenv
|
|
92
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
93
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
94
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
95
|
+
# install all needed dependencies.
|
|
96
|
+
#Pipfile.lock
|
|
97
|
+
|
|
98
|
+
# poetry
|
|
99
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
100
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
101
|
+
# commonly ignored for libraries.
|
|
102
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
103
|
+
#poetry.lock
|
|
104
|
+
|
|
105
|
+
# pdm
|
|
106
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
107
|
+
#pdm.lock
|
|
108
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
109
|
+
# in version control.
|
|
110
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
111
|
+
.pdm.toml
|
|
112
|
+
|
|
113
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
114
|
+
__pypackages__/
|
|
115
|
+
|
|
116
|
+
# Celery stuff
|
|
117
|
+
celerybeat-schedule
|
|
118
|
+
celerybeat.pid
|
|
119
|
+
|
|
120
|
+
# SageMath parsed files
|
|
121
|
+
*.sage.py
|
|
122
|
+
|
|
123
|
+
# Environments
|
|
124
|
+
.env
|
|
125
|
+
.venv
|
|
126
|
+
env/
|
|
127
|
+
venv/
|
|
128
|
+
ENV/
|
|
129
|
+
env.bak/
|
|
130
|
+
venv.bak/
|
|
131
|
+
|
|
132
|
+
# Spyder project settings
|
|
133
|
+
.spyderproject
|
|
134
|
+
.spyproject
|
|
135
|
+
|
|
136
|
+
# Rope project settings
|
|
137
|
+
.ropeproject
|
|
138
|
+
|
|
139
|
+
# mkdocs documentation
|
|
140
|
+
/site
|
|
141
|
+
|
|
142
|
+
# mypy
|
|
143
|
+
.mypy_cache/
|
|
144
|
+
.dmypy.json
|
|
145
|
+
dmypy.json
|
|
146
|
+
|
|
147
|
+
# Pyre type checker
|
|
148
|
+
.pyre/
|
|
149
|
+
|
|
150
|
+
# pytype static type analyzer
|
|
151
|
+
.pytype/
|
|
152
|
+
|
|
153
|
+
# Cython debug symbols
|
|
154
|
+
cython_debug/
|
|
155
|
+
|
|
156
|
+
# PyCharm
|
|
157
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
158
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
159
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
160
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
161
|
+
.idea/
|
|
@@ -1,12 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-linters
|
|
3
|
+
Version: 0.1.6
|
|
4
|
+
Summary: bundling config files for linters like ruff and pyright
|
|
5
|
+
Project-URL: repository, https://github.com/dertilo/python-linters
|
|
6
|
+
Author-email: Tilo Himmelsbach <dertilo@gmail.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Python: <4.0,>=3.10
|
|
10
|
+
Requires-Dist: basedpyright<2.0.0,>=1.12.5
|
|
11
|
+
Requires-Dist: json-with-comments<2.0.0,>=1.2.9
|
|
12
|
+
Requires-Dist: ruff>=0.0.291
|
|
13
|
+
Requires-Dist: toml<1.0.0,>=0.10.2
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
1
16
|

|
|
2
17
|
[](https://opensource.org/licenses/MIT)
|
|
3
18
|
[](https://beartype.readthedocs.io)
|
|
4
19
|
# python-linters
|
|
5
|
-
- bundles
|
|
6
|
-
-
|
|
20
|
+
- bundles ruff and ~~flake8~~ basedpyright into a single ~~poetry~~uv-script
|
|
21
|
+
- even though I liked some flake8-rules (especially from [wemake-python-styleguide](https://wemake-python-styleguide.readthedocs.io/en/latest/)) dropped it in favor of ruff for being too slow
|
|
22
|
+
- project independent linter configuration (via `ruff.toml`,~~`.flake8`~~`pyrightconfig.json`) -> one config to rule them all
|
|
23
|
+
- well one can still override the default config via `ruff_extensions.toml` and `pyrightconfig.json`
|
|
7
24
|
### use-cases:
|
|
8
|
-
1. interactively learning python code quality standards via ruff+
|
|
25
|
+
1. interactively learning python code quality standards via ruff+pyright, see [local development workflow](#2-local-development-workflow-before-pushing-to-ci-pipeline)
|
|
9
26
|
2. enforcing minimal quality-standards via a "lint-stage" in a ci-pipeline, see [setup for ci-pipeline](#3-setup-for-ci-pipeline)
|
|
27
|
+
## 0. motivation:
|
|
28
|
+
### Python is a Bicycle!
|
|
29
|
+
- initial idea of python: `Bridge the gap between the shell and C.`[see](https://www.artima.com/articles/pythons-design-goals)
|
|
30
|
+

|
|
31
|
+
### run-time "safety"
|
|
32
|
+
- type and data validation at runtime
|
|
33
|
+
- pytests run your code -> runtime
|
|
34
|
+

|
|
35
|
+
### lint-time "safety"
|
|
36
|
+
- linting and static type checking
|
|
37
|
+

|
|
38
|
+
- flake8 already fell down!
|
|
39
|
+
### more "safety" via shorter feed-back loops
|
|
40
|
+

|
|
41
|
+
|
|
10
42
|
|
|
11
43
|
## 1. install (via poetry)
|
|
12
44
|
1. in `pyproject.toml` add to dependencies
|
|
@@ -38,13 +70,12 @@ python-linters = { version = "<some-version-here>" }
|
|
|
38
70
|
],
|
|
39
71
|
```
|
|
40
72
|
### three scripts/commands that you can run locally
|
|
41
|
-
1. `
|
|
73
|
+
1. `uv run fixcode` to let black+isort+ruff "auto-fix" your code -> always have a `clean` git-repo before doing this (no uncommitted changes)
|
|
42
74
|
- [example output](#fixcode-example)
|
|
43
|
-
1. `
|
|
75
|
+
1. `uv run pythonlinter` -> to lint your code
|
|
44
76
|
- [example black is not happy](#pythonlinter-black-is-not-happy) -> if you ran `fixcode` black is guaranteed to be happy!
|
|
45
77
|
- [example ruff is not happy](#pythonlinter-ruff-is-not-happy)
|
|
46
|
-
|
|
47
|
-
1. `poetry run addnoqa` to let ruff insert rules-violation-comments to your code
|
|
78
|
+
1. `uv run addnoqa` to let ruff insert rules-violation-comments to your code
|
|
48
79
|
- [example](#addnoqa-example)
|
|
49
80
|
```mermaid
|
|
50
81
|
flowchart TD
|
|
@@ -54,22 +85,22 @@ classDef style2 fill:#ffffba
|
|
|
54
85
|
classDef style3 fill:#baffc9
|
|
55
86
|
classDef style4 fill:#bae1ff
|
|
56
87
|
op1["start: have a 'clean' repo"]:::style4 --> op2
|
|
57
|
-
op2[\"
|
|
88
|
+
op2[\"uv run fixcode\n refactor & git commit"/]:::style1 --> cond9{{"uv run pythonlinter"}}:::style0
|
|
58
89
|
cond9 --> | complains | cond47{{"ruff"}}:::style0
|
|
59
|
-
cond47 --> | complains | op51[\"
|
|
90
|
+
cond47 --> | complains | op51[\"uv run addnoqa"/]:::style1
|
|
60
91
|
op51 --> sub53[\"refactor your code \nOR\n keep/add noqa-comment"/]:::style1
|
|
61
92
|
sub53 --> op2
|
|
62
|
-
cond47 --> | is happy | cond58{{"
|
|
93
|
+
cond47 --> | is happy | cond58{{"pyright"}}:::style0
|
|
63
94
|
cond58 --> | complains | sub53
|
|
64
95
|
cond58 --> | is happy | sub66["git commit"]
|
|
65
96
|
sub66 --> sub80
|
|
66
97
|
cond9 --> | is happy | sub80["git push"]:::style3
|
|
67
98
|
```
|
|
68
|
-
- write-operations (changing your code): `
|
|
69
|
-
- read-only-operations: `
|
|
99
|
+
- write-operations (changing your code): `uv run fixcode`, `poerty run addnoqa`, `refactor your code`
|
|
100
|
+
- read-only-operations: `uv run pythonlinter`,`git commit`, `git push`
|
|
70
101
|
|
|
71
102
|
## 3. setup for ci-pipeline
|
|
72
|
-
* if you specified the packages in the `pyproject.toml` and you run just run `
|
|
103
|
+
* if you specified the packages in the `pyproject.toml` and you run just run `uv run pythonlinter` without any arguments than the `pyproject.toml` is getting parsed and specified packages are getting linted
|
|
73
104
|
```yaml
|
|
74
105
|
stages:
|
|
75
106
|
- lint # lint-stage!
|
|
@@ -97,7 +128,7 @@ linting:
|
|
|
97
128
|
### fixcode example
|
|
98
129
|
```commandline
|
|
99
130
|
cd <somehwere>/whisper-streaming
|
|
100
|
-
|
|
131
|
+
uv run fixcode
|
|
101
132
|
Fixing <somehwere>/whisper-streaming/whisper_streaming/post_asr_preparations.py
|
|
102
133
|
whisper_streaming/faster_whisper_inference/faster_whisper_inferencer.py:231:30: ARG005 Unused lambda argument: `x`
|
|
103
134
|
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
|
|
@@ -113,27 +144,14 @@ All done! ✨ 🍰 ✨
|
|
|
113
144
|
folders_to_be_linted=['whisper_streaming', 'tests']
|
|
114
145
|
```
|
|
115
146
|
|
|
116
|
-
### pythonlinter black is not happy
|
|
117
|
-
```commandline
|
|
118
|
-
poetry run pythonlinter
|
|
119
|
-
linter-order: black->ruff->flake8
|
|
120
|
-
folders_tobelinted=['whisper_streaming', 'tests']
|
|
121
|
-
running black
|
|
122
|
-
would reformat <somehwere>/whisper-streaming/whisper_streaming/post_asr_preparations.py
|
|
123
|
-
would reformat <somehwere>/whisper-streaming/whisper_streaming/faster_whisper_inference/faster_whisper_inferencer.py
|
|
124
|
-
|
|
125
|
-
Oh no! 💥 💔 💥
|
|
126
|
-
2 files would be reformatted, 20 files would be left unchanged.
|
|
127
|
-
python_linters.run_linters.LinterException: 💩 black is not happy! 💩
|
|
128
|
-
```
|
|
129
147
|
### pythonlinter ruff is not happy
|
|
130
148
|
- complains about
|
|
131
149
|
- an unused argument
|
|
132
150
|
- a missing return statement
|
|
133
151
|
- missing type annotations
|
|
134
152
|
```commandline
|
|
135
|
-
|
|
136
|
-
linter-order: black->ruff->
|
|
153
|
+
uv run pythonlinter
|
|
154
|
+
linter-order: black->ruff->pyright
|
|
137
155
|
|
|
138
156
|
...
|
|
139
157
|
|
|
@@ -146,31 +164,10 @@ Found 4 errors.
|
|
|
146
164
|
No fixes available (2 hidden fixes can be enabled with the `--unsafe-fixes` option).
|
|
147
165
|
python_linters.run_linters.LinterException: 💩 ruff is not happy! 💩
|
|
148
166
|
```
|
|
149
|
-
### pythonlinter flake8 is not happy
|
|
150
|
-
- complains about too many methods in a class and wrong position of a function in a module
|
|
151
|
-
```commandline
|
|
152
|
-
poetry run pythonlinter
|
|
153
|
-
linter-order: black->ruff->flake8
|
|
154
|
-
folders_tobelinted=['whisper_streaming', 'tests']
|
|
155
|
-
running black
|
|
156
|
-
All done! ✨ 🍰 ✨
|
|
157
|
-
22 files would be left unchanged.
|
|
158
|
-
|
|
159
|
-
passed black linter! ✨ 🍰 ✨
|
|
160
|
-
|
|
161
|
-
running ruff
|
|
162
|
-
|
|
163
|
-
passed ruff linter! ✨ 🍰 ✨
|
|
164
|
-
|
|
165
|
-
running flake8
|
|
166
|
-
whisper_streaming/faster_whisper_inference/faster_whisper_inferencer.py:111:1: WPS214 Found too many methods: 10 > 9
|
|
167
|
-
tests/conftest.py:35:8: NEW100 newspaper style: function is_in_docker_container defined in line 16 should be moved down
|
|
168
|
-
python_linters.run_linters.LinterException: 💩 flake8 is not happy! 💩
|
|
169
|
-
```
|
|
170
167
|
### addnoqa example
|
|
171
168
|
- only shows how many noqas it added (`3`) and how many files it left unchanged (`22`)
|
|
172
169
|
```commandline
|
|
173
|
-
|
|
170
|
+
uv run addnoqa
|
|
174
171
|
Added 3 noqa directives.
|
|
175
172
|
All done! ✨ 🍰 ✨
|
|
176
173
|
22 files would be left unchanged.
|
|
@@ -179,11 +176,9 @@ addnoqa iteration: 0
|
|
|
179
176
|
```
|
|
180
177
|
- it adds noqas like this one:
|
|
181
178
|
```python
|
|
182
|
-
def foobar(x):
|
|
179
|
+
def foobar(x): # noqa: ANN001, ANN202
|
|
183
180
|
return "whatever"
|
|
184
181
|
|
|
185
|
-
from faster_whisper.transcribe import Segment as FasterWhisperSegment # noqa: E402
|
|
186
|
-
|
|
187
182
|
```
|
|
188
183
|
# IDE integration
|
|
189
184
|
## manual vscode integration
|
|
@@ -197,17 +192,17 @@ from faster_whisper.transcribe import Segment as FasterWhisperSegment # noqa: E
|
|
|
197
192
|
{
|
|
198
193
|
"label": "fixcode",
|
|
199
194
|
"type": "shell",
|
|
200
|
-
"command": "cd $(cd ${fileDirname} && git rev-parse --show-toplevel) &&
|
|
195
|
+
"command": "cd $(cd ${fileDirname} && git rev-parse --show-toplevel) && uv run fixcode"
|
|
201
196
|
},
|
|
202
197
|
{
|
|
203
198
|
"label": "addnoqa",
|
|
204
199
|
"type": "shell",
|
|
205
|
-
"command": "cd $(cd ${fileDirname} && git rev-parse --show-toplevel) &&
|
|
200
|
+
"command": "cd $(cd ${fileDirname} && git rev-parse --show-toplevel) && uv run addnoqa"
|
|
206
201
|
},
|
|
207
202
|
{
|
|
208
203
|
"label": "pythonlinter",
|
|
209
204
|
"type": "shell",
|
|
210
|
-
"command": "cd $(cd ${fileDirname} && git rev-parse --show-toplevel) &&
|
|
205
|
+
"command": "cd $(cd ${fileDirname} && git rev-parse --show-toplevel) && uv run pythonlinter"
|
|
211
206
|
}
|
|
212
207
|
]
|
|
213
208
|
}
|
|
@@ -1,36 +1,29 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: python-linters
|
|
3
|
-
Version: 0.1.4
|
|
4
|
-
Summary: bundling config files for linters like ruff and flake8
|
|
5
|
-
Home-page: https://github.com/dertilo/python-linters
|
|
6
|
-
License: MIT
|
|
7
|
-
Author: Tilo Himmelsbach
|
|
8
|
-
Author-email: dertilo@gmail.com
|
|
9
|
-
Requires-Python: >=3.10,<4.0
|
|
10
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
-
Requires-Dist: basedpyright (>=1.12.5,<2.0.0)
|
|
16
|
-
Requires-Dist: black (>=23.9.1,<24.0.0)
|
|
17
|
-
Requires-Dist: flake8 (>=6.1.0,<7.0.0)
|
|
18
|
-
Requires-Dist: flake8-newspaper-style (>=0.4.3,<0.5.0)
|
|
19
|
-
Requires-Dist: ruff (>=0.0.291)
|
|
20
|
-
Requires-Dist: toml (>=0.10.2,<0.11.0)
|
|
21
|
-
Requires-Dist: wemake-python-styleguide (>=0.18.0,<0.19.0)
|
|
22
|
-
Project-URL: Repository, https://github.com/dertilo/python-linters
|
|
23
|
-
Description-Content-Type: text/markdown
|
|
24
|
-
|
|
25
1
|

|
|
26
2
|
[](https://opensource.org/licenses/MIT)
|
|
27
3
|
[](https://beartype.readthedocs.io)
|
|
28
4
|
# python-linters
|
|
29
|
-
- bundles
|
|
30
|
-
-
|
|
5
|
+
- bundles ruff and ~~flake8~~ basedpyright into a single ~~poetry~~uv-script
|
|
6
|
+
- even though I liked some flake8-rules (especially from [wemake-python-styleguide](https://wemake-python-styleguide.readthedocs.io/en/latest/)) dropped it in favor of ruff for being too slow
|
|
7
|
+
- project independent linter configuration (via `ruff.toml`,~~`.flake8`~~`pyrightconfig.json`) -> one config to rule them all
|
|
8
|
+
- well one can still override the default config via `ruff_extensions.toml` and `pyrightconfig.json`
|
|
31
9
|
### use-cases:
|
|
32
|
-
1. interactively learning python code quality standards via ruff+
|
|
10
|
+
1. interactively learning python code quality standards via ruff+pyright, see [local development workflow](#2-local-development-workflow-before-pushing-to-ci-pipeline)
|
|
33
11
|
2. enforcing minimal quality-standards via a "lint-stage" in a ci-pipeline, see [setup for ci-pipeline](#3-setup-for-ci-pipeline)
|
|
12
|
+
## 0. motivation:
|
|
13
|
+
### Python is a Bicycle!
|
|
14
|
+
- initial idea of python: `Bridge the gap between the shell and C.`[see](https://www.artima.com/articles/pythons-design-goals)
|
|
15
|
+

|
|
16
|
+
### run-time "safety"
|
|
17
|
+
- type and data validation at runtime
|
|
18
|
+
- pytests run your code -> runtime
|
|
19
|
+

|
|
20
|
+
### lint-time "safety"
|
|
21
|
+
- linting and static type checking
|
|
22
|
+

|
|
23
|
+
- flake8 already fell down!
|
|
24
|
+
### more "safety" via shorter feed-back loops
|
|
25
|
+

|
|
26
|
+
|
|
34
27
|
|
|
35
28
|
## 1. install (via poetry)
|
|
36
29
|
1. in `pyproject.toml` add to dependencies
|
|
@@ -62,13 +55,12 @@ python-linters = { version = "<some-version-here>" }
|
|
|
62
55
|
],
|
|
63
56
|
```
|
|
64
57
|
### three scripts/commands that you can run locally
|
|
65
|
-
1. `
|
|
58
|
+
1. `uv run fixcode` to let black+isort+ruff "auto-fix" your code -> always have a `clean` git-repo before doing this (no uncommitted changes)
|
|
66
59
|
- [example output](#fixcode-example)
|
|
67
|
-
1. `
|
|
60
|
+
1. `uv run pythonlinter` -> to lint your code
|
|
68
61
|
- [example black is not happy](#pythonlinter-black-is-not-happy) -> if you ran `fixcode` black is guaranteed to be happy!
|
|
69
62
|
- [example ruff is not happy](#pythonlinter-ruff-is-not-happy)
|
|
70
|
-
|
|
71
|
-
1. `poetry run addnoqa` to let ruff insert rules-violation-comments to your code
|
|
63
|
+
1. `uv run addnoqa` to let ruff insert rules-violation-comments to your code
|
|
72
64
|
- [example](#addnoqa-example)
|
|
73
65
|
```mermaid
|
|
74
66
|
flowchart TD
|
|
@@ -78,22 +70,22 @@ classDef style2 fill:#ffffba
|
|
|
78
70
|
classDef style3 fill:#baffc9
|
|
79
71
|
classDef style4 fill:#bae1ff
|
|
80
72
|
op1["start: have a 'clean' repo"]:::style4 --> op2
|
|
81
|
-
op2[\"
|
|
73
|
+
op2[\"uv run fixcode\n refactor & git commit"/]:::style1 --> cond9{{"uv run pythonlinter"}}:::style0
|
|
82
74
|
cond9 --> | complains | cond47{{"ruff"}}:::style0
|
|
83
|
-
cond47 --> | complains | op51[\"
|
|
75
|
+
cond47 --> | complains | op51[\"uv run addnoqa"/]:::style1
|
|
84
76
|
op51 --> sub53[\"refactor your code \nOR\n keep/add noqa-comment"/]:::style1
|
|
85
77
|
sub53 --> op2
|
|
86
|
-
cond47 --> | is happy | cond58{{"
|
|
78
|
+
cond47 --> | is happy | cond58{{"pyright"}}:::style0
|
|
87
79
|
cond58 --> | complains | sub53
|
|
88
80
|
cond58 --> | is happy | sub66["git commit"]
|
|
89
81
|
sub66 --> sub80
|
|
90
82
|
cond9 --> | is happy | sub80["git push"]:::style3
|
|
91
83
|
```
|
|
92
|
-
- write-operations (changing your code): `
|
|
93
|
-
- read-only-operations: `
|
|
84
|
+
- write-operations (changing your code): `uv run fixcode`, `poerty run addnoqa`, `refactor your code`
|
|
85
|
+
- read-only-operations: `uv run pythonlinter`,`git commit`, `git push`
|
|
94
86
|
|
|
95
87
|
## 3. setup for ci-pipeline
|
|
96
|
-
* if you specified the packages in the `pyproject.toml` and you run just run `
|
|
88
|
+
* if you specified the packages in the `pyproject.toml` and you run just run `uv run pythonlinter` without any arguments than the `pyproject.toml` is getting parsed and specified packages are getting linted
|
|
97
89
|
```yaml
|
|
98
90
|
stages:
|
|
99
91
|
- lint # lint-stage!
|
|
@@ -121,7 +113,7 @@ linting:
|
|
|
121
113
|
### fixcode example
|
|
122
114
|
```commandline
|
|
123
115
|
cd <somehwere>/whisper-streaming
|
|
124
|
-
|
|
116
|
+
uv run fixcode
|
|
125
117
|
Fixing <somehwere>/whisper-streaming/whisper_streaming/post_asr_preparations.py
|
|
126
118
|
whisper_streaming/faster_whisper_inference/faster_whisper_inferencer.py:231:30: ARG005 Unused lambda argument: `x`
|
|
127
119
|
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
|
|
@@ -137,27 +129,14 @@ All done! ✨ 🍰 ✨
|
|
|
137
129
|
folders_to_be_linted=['whisper_streaming', 'tests']
|
|
138
130
|
```
|
|
139
131
|
|
|
140
|
-
### pythonlinter black is not happy
|
|
141
|
-
```commandline
|
|
142
|
-
poetry run pythonlinter
|
|
143
|
-
linter-order: black->ruff->flake8
|
|
144
|
-
folders_tobelinted=['whisper_streaming', 'tests']
|
|
145
|
-
running black
|
|
146
|
-
would reformat <somehwere>/whisper-streaming/whisper_streaming/post_asr_preparations.py
|
|
147
|
-
would reformat <somehwere>/whisper-streaming/whisper_streaming/faster_whisper_inference/faster_whisper_inferencer.py
|
|
148
|
-
|
|
149
|
-
Oh no! 💥 💔 💥
|
|
150
|
-
2 files would be reformatted, 20 files would be left unchanged.
|
|
151
|
-
python_linters.run_linters.LinterException: 💩 black is not happy! 💩
|
|
152
|
-
```
|
|
153
132
|
### pythonlinter ruff is not happy
|
|
154
133
|
- complains about
|
|
155
134
|
- an unused argument
|
|
156
135
|
- a missing return statement
|
|
157
136
|
- missing type annotations
|
|
158
137
|
```commandline
|
|
159
|
-
|
|
160
|
-
linter-order: black->ruff->
|
|
138
|
+
uv run pythonlinter
|
|
139
|
+
linter-order: black->ruff->pyright
|
|
161
140
|
|
|
162
141
|
...
|
|
163
142
|
|
|
@@ -170,31 +149,10 @@ Found 4 errors.
|
|
|
170
149
|
No fixes available (2 hidden fixes can be enabled with the `--unsafe-fixes` option).
|
|
171
150
|
python_linters.run_linters.LinterException: 💩 ruff is not happy! 💩
|
|
172
151
|
```
|
|
173
|
-
### pythonlinter flake8 is not happy
|
|
174
|
-
- complains about too many methods in a class and wrong position of a function in a module
|
|
175
|
-
```commandline
|
|
176
|
-
poetry run pythonlinter
|
|
177
|
-
linter-order: black->ruff->flake8
|
|
178
|
-
folders_tobelinted=['whisper_streaming', 'tests']
|
|
179
|
-
running black
|
|
180
|
-
All done! ✨ 🍰 ✨
|
|
181
|
-
22 files would be left unchanged.
|
|
182
|
-
|
|
183
|
-
passed black linter! ✨ 🍰 ✨
|
|
184
|
-
|
|
185
|
-
running ruff
|
|
186
|
-
|
|
187
|
-
passed ruff linter! ✨ 🍰 ✨
|
|
188
|
-
|
|
189
|
-
running flake8
|
|
190
|
-
whisper_streaming/faster_whisper_inference/faster_whisper_inferencer.py:111:1: WPS214 Found too many methods: 10 > 9
|
|
191
|
-
tests/conftest.py:35:8: NEW100 newspaper style: function is_in_docker_container defined in line 16 should be moved down
|
|
192
|
-
python_linters.run_linters.LinterException: 💩 flake8 is not happy! 💩
|
|
193
|
-
```
|
|
194
152
|
### addnoqa example
|
|
195
153
|
- only shows how many noqas it added (`3`) and how many files it left unchanged (`22`)
|
|
196
154
|
```commandline
|
|
197
|
-
|
|
155
|
+
uv run addnoqa
|
|
198
156
|
Added 3 noqa directives.
|
|
199
157
|
All done! ✨ 🍰 ✨
|
|
200
158
|
22 files would be left unchanged.
|
|
@@ -203,11 +161,9 @@ addnoqa iteration: 0
|
|
|
203
161
|
```
|
|
204
162
|
- it adds noqas like this one:
|
|
205
163
|
```python
|
|
206
|
-
def foobar(x):
|
|
164
|
+
def foobar(x): # noqa: ANN001, ANN202
|
|
207
165
|
return "whatever"
|
|
208
166
|
|
|
209
|
-
from faster_whisper.transcribe import Segment as FasterWhisperSegment # noqa: E402
|
|
210
|
-
|
|
211
167
|
```
|
|
212
168
|
# IDE integration
|
|
213
169
|
## manual vscode integration
|
|
@@ -221,17 +177,17 @@ from faster_whisper.transcribe import Segment as FasterWhisperSegment # noqa: E
|
|
|
221
177
|
{
|
|
222
178
|
"label": "fixcode",
|
|
223
179
|
"type": "shell",
|
|
224
|
-
"command": "cd $(cd ${fileDirname} && git rev-parse --show-toplevel) &&
|
|
180
|
+
"command": "cd $(cd ${fileDirname} && git rev-parse --show-toplevel) && uv run fixcode"
|
|
225
181
|
},
|
|
226
182
|
{
|
|
227
183
|
"label": "addnoqa",
|
|
228
184
|
"type": "shell",
|
|
229
|
-
"command": "cd $(cd ${fileDirname} && git rev-parse --show-toplevel) &&
|
|
185
|
+
"command": "cd $(cd ${fileDirname} && git rev-parse --show-toplevel) && uv run addnoqa"
|
|
230
186
|
},
|
|
231
187
|
{
|
|
232
188
|
"label": "pythonlinter",
|
|
233
189
|
"type": "shell",
|
|
234
|
-
"command": "cd $(cd ${fileDirname} && git rev-parse --show-toplevel) &&
|
|
190
|
+
"command": "cd $(cd ${fileDirname} && git rev-parse --show-toplevel) && uv run pythonlinter"
|
|
235
191
|
}
|
|
236
192
|
]
|
|
237
193
|
}
|
|
@@ -240,4 +196,4 @@ from faster_whisper.transcribe import Segment as FasterWhisperSegment # noqa: E
|
|
|
240
196
|

|
|
241
197
|
## alternatives
|
|
242
198
|
- https://github.com/astral-sh/ruff-vscode
|
|
243
|
-
- https://plugins.jetbrains.com/plugin/20574-ruff
|
|
199
|
+
- https://plugins.jetbrains.com/plugin/20574-ruff
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "python-linters"
|
|
3
|
+
description = "bundling config files for linters like ruff and pyright"
|
|
4
|
+
authors = [ { name = "Tilo Himmelsbach", email = "dertilo@gmail.com" } ]
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = {text = "MIT"}
|
|
7
|
+
requires-python = "<4.0,>=3.10"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"toml<1.0.0,>=0.10.2",
|
|
10
|
+
"ruff>=0.0.291",
|
|
11
|
+
"basedpyright<2.0.0,>=1.12.5",
|
|
12
|
+
"json-with-comments<2.0.0,>=1.2.9",
|
|
13
|
+
]
|
|
14
|
+
dynamic = [
|
|
15
|
+
"version"
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.urls]
|
|
19
|
+
repository = "https://github.com/dertilo/python-linters"
|
|
20
|
+
|
|
21
|
+
[project.scripts]
|
|
22
|
+
pythonlinter = "python_linters.run_linters:main"
|
|
23
|
+
fixcode = "python_linters.fix_code:main"
|
|
24
|
+
addnoqa = "python_linters.add_noqa:main"
|
|
25
|
+
|
|
26
|
+
[build-system]
|
|
27
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
28
|
+
build-backend = "hatchling.build"
|
|
29
|
+
|
|
30
|
+
[tool.hatch.version]
|
|
31
|
+
source = "vcs"
|
|
32
|
+
|
|
33
|
+
[tool.hatch.build.hooks.vcs]
|
|
34
|
+
version-file = "_version.py"
|
|
35
|
+
|
|
36
|
+
[tool.hatch.build.targets.sdist]
|
|
37
|
+
packages = ["python_linters"] # packages option is semantically equivalent to only-include
|
|
38
|
+
|
|
39
|
+
[tool.hatch.build.targets.wheel]
|
|
40
|
+
packages = ["python_linters"]
|
|
@@ -2,28 +2,30 @@ import os
|
|
|
2
2
|
|
|
3
3
|
from python_linters.extending_ruff_toml import create_extended_ruff_toml
|
|
4
4
|
from python_linters.getting_to_be_linted_folders import get_folders_to_be_linted
|
|
5
|
-
from python_linters.
|
|
5
|
+
from python_linters.utils import run_cmd, get_pyproject_dir
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def main() -> None:
|
|
9
9
|
"""
|
|
10
10
|
expects to run from $ContentRoot$
|
|
11
11
|
"""
|
|
12
|
-
|
|
12
|
+
dirr = get_pyproject_dir()
|
|
13
|
+
folders_tobelinted = get_folders_to_be_linted(f"{dirr}/pyproject.toml")
|
|
14
|
+
|
|
13
15
|
BIG_NUMBER = 9
|
|
14
16
|
for k in range(
|
|
15
17
|
BIG_NUMBER,
|
|
16
18
|
): # theoretically could be necessary to run this loop as long as code changes!
|
|
17
19
|
print(f"addnoqa iteration: {k}")
|
|
18
20
|
run_cmd(
|
|
19
|
-
f"ruff check {' '.join(folders_tobelinted)} --config={create_extended_ruff_toml()} --add-noqa",
|
|
21
|
+
f"cd {dirr} && ruff check {' '.join(folders_tobelinted)} --config={create_extended_ruff_toml(str(dirr))} --add-noqa",
|
|
20
22
|
)
|
|
21
23
|
try:
|
|
22
|
-
run_cmd(f"ruff format --check {' '.join(folders_tobelinted)}")
|
|
24
|
+
run_cmd(f"cd {dirr} && ruff format --check {' '.join(folders_tobelinted)}")
|
|
23
25
|
break
|
|
24
26
|
except Exception:
|
|
25
|
-
run_cmd(f"ruff format {' '.join(folders_tobelinted)}")
|
|
26
|
-
run_cmd(f"fixcode {
|
|
27
|
+
run_cmd(f"cd {dirr} && ruff format {' '.join(folders_tobelinted)}")
|
|
28
|
+
run_cmd(f"fixcode {dirr}")
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
if __name__ == "__main__":
|
|
@@ -2,10 +2,8 @@ import pathlib
|
|
|
2
2
|
|
|
3
3
|
package_dir = str(pathlib.Path(__file__).parent.resolve())
|
|
4
4
|
|
|
5
|
-
FLAKE8_CONFIG_FILE = f"{package_dir}/.flake8"
|
|
6
5
|
RUFF_CONFIG_FILE = f"{package_dir}/ruff.toml"
|
|
7
6
|
PYRIGHT_CONFIG_FILE = f"{package_dir}/pyrightconfig.json"
|
|
8
7
|
|
|
9
|
-
assert pathlib.Path(FLAKE8_CONFIG_FILE).is_file()
|
|
10
8
|
assert pathlib.Path(RUFF_CONFIG_FILE).is_file()
|
|
11
9
|
assert pathlib.Path(PYRIGHT_CONFIG_FILE).is_file()
|
|
@@ -4,12 +4,12 @@ from pathlib import Path
|
|
|
4
4
|
from python_linters.config_files import RUFF_CONFIG_FILE
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def create_extended_ruff_toml() -> str:
|
|
8
|
-
ruff_extension_file = Path("ruff_extension.toml")
|
|
7
|
+
def create_extended_ruff_toml(package_root:str) -> str:
|
|
8
|
+
ruff_extension_file = Path(f"{package_root}/ruff_extension.toml")
|
|
9
9
|
|
|
10
10
|
if ruff_extension_file.is_file():
|
|
11
11
|
fingerprint = f"{ruff_extension_file.stat().st_size}-{ruff_extension_file.stat().st_mtime}"
|
|
12
|
-
extended_ruff_toml_file = f"extended_ruff-{fingerprint}.toml"
|
|
12
|
+
extended_ruff_toml_file = f"{package_root}/extended_ruff-{fingerprint}.toml"
|
|
13
13
|
if not Path(extended_ruff_toml_file).is_file():
|
|
14
14
|
print(f"extending {RUFF_CONFIG_FILE} with {ruff_extension_file=}")
|
|
15
15
|
with Path(extended_ruff_toml_file).open("w", encoding="locale") as f:
|