python-linters 0.1.3__tar.gz → 0.1.5__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.
@@ -1,12 +1,47 @@
1
+ Metadata-Version: 2.1
2
+ Name: python-linters
3
+ Version: 0.1.5
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: ruff (>=0.0.291)
17
+ Requires-Dist: toml (>=0.10.2,<0.11.0)
18
+ Project-URL: Repository, https://github.com/dertilo/python-linters
19
+ Description-Content-Type: text/markdown
20
+
1
21
  ![pypi](https://github.com/dertilo/python-linters/actions/workflows/release.yml/badge.svg)
2
22
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
3
23
  [![bear-ified](https://raw.githubusercontent.com/beartype/beartype-assets/main/badge/bear-ified.svg)](https://beartype.readthedocs.io)
4
24
  # python-linters
5
- - bundles flake8, black, isort, ruff into a single poetry-script
25
+ - bundles ruff and basedpyright into a single poetry-script
6
26
  - project independent linter configuration (via `ruff.toml`,`.flake8`) -> one config to rule them all
7
27
  ### use-cases:
8
28
  1. interactively learning python code quality standards via ruff+flake8, see [local development workflow](#2-local-development-workflow-before-pushing-to-ci-pipeline)
9
29
  2. enforcing minimal quality-standards via a "lint-stage" in a ci-pipeline, see [setup for ci-pipeline](#3-setup-for-ci-pipeline)
30
+ ## 0. motivation:
31
+ ### Python is a Bicycle!
32
+ - initial idea of python: `Bridge the gap between the shell and C.`[see](https://www.artima.com/articles/pythons-design-goals)
33
+ ![img.png](images/python_is_a_bicycle.png)
34
+ ### run-time "safety"
35
+ - type and data validation at runtime
36
+ - pytests run your code -> runtime
37
+ ![img.png](images/runtime_safety.png)
38
+ ### lint-time "safety"
39
+ - linting and static type checking
40
+ ![img.png](images/lint_time_safety.png)
41
+ - flake8 already fell down!
42
+ ### more "safety" via shorter feed-back loops
43
+ ![img_3.png](images/feedback_loops.png)
44
+
10
45
 
11
46
  ## 1. install (via poetry)
12
47
  1. in `pyproject.toml` add to dependencies
@@ -59,7 +94,7 @@ cond9 --> | complains | cond47{{"ruff"}}:::style0
59
94
  cond47 --> | complains | op51[\"poetry run addnoqa"/]:::style1
60
95
  op51 --> sub53[\"refactor your code \nOR\n keep/add noqa-comment"/]:::style1
61
96
  sub53 --> op2
62
- cond47 --> | is happy | cond58{{"flake8"}}:::style0
97
+ cond47 --> | is happy | cond58{{"pyright"}}:::style0
63
98
  cond58 --> | complains | sub53
64
99
  cond58 --> | is happy | sub66["git commit"]
65
100
  sub66 --> sub80
@@ -113,19 +148,6 @@ All done! ✨ 🍰 ✨
113
148
  folders_to_be_linted=['whisper_streaming', 'tests']
114
149
  ```
115
150
 
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
151
  ### pythonlinter ruff is not happy
130
152
  - complains about
131
153
  - an unused argument
@@ -146,27 +168,6 @@ Found 4 errors.
146
168
  No fixes available (2 hidden fixes can be enabled with the `--unsafe-fixes` option).
147
169
  python_linters.run_linters.LinterException: 💩 ruff is not happy! 💩
148
170
  ```
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
171
  ### addnoqa example
171
172
  - only shows how many noqas it added (`3`) and how many files it left unchanged (`22`)
172
173
  ```commandline
@@ -179,11 +180,9 @@ addnoqa iteration: 0
179
180
  ```
180
181
  - it adds noqas like this one:
181
182
  ```python
182
- def foobar(x):
183
+ def foobar(x): # noqa: ANN001, ANN202
183
184
  return "whatever"
184
185
 
185
- from faster_whisper.transcribe import Segment as FasterWhisperSegment # noqa: E402
186
-
187
186
  ```
188
187
  # IDE integration
189
188
  ## manual vscode integration
@@ -216,4 +215,4 @@ from faster_whisper.transcribe import Segment as FasterWhisperSegment # noqa: E
216
215
  ![pycharm_settings.png](pycharm_settings.png)
217
216
  ## alternatives
218
217
  - https://github.com/astral-sh/ruff-vscode
219
- - https://plugins.jetbrains.com/plugin/20574-ruff
218
+ - https://plugins.jetbrains.com/plugin/20574-ruff
@@ -1,33 +1,27 @@
1
- Metadata-Version: 2.1
2
- Name: python-linters
3
- Version: 0.1.3
4
- Summary: bundling config files for linters like ruff and flake8
5
- Home-page: https://github.com/dertilo/python-linters
6
- Author: Tilo Himmelsbach
7
- Author-email: dertilo@gmail.com
8
- Requires-Python: >=3.10,<4.0
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: Programming Language :: Python :: 3.10
11
- Classifier: Programming Language :: Python :: 3.11
12
- Classifier: Programming Language :: Python :: 3.12
13
- Requires-Dist: black (>=23.9.1,<24.0.0)
14
- Requires-Dist: flake8 (>=6.1.0,<7.0.0)
15
- Requires-Dist: flake8-newspaper-style (>=0.4.3,<0.5.0)
16
- Requires-Dist: ruff (>=0.0.291)
17
- Requires-Dist: toml (>=0.10.2,<0.11.0)
18
- Requires-Dist: wemake-python-styleguide (>=0.18.0,<0.19.0)
19
- Project-URL: Repository, https://github.com/dertilo/python-linters
20
- Description-Content-Type: text/markdown
21
-
22
1
  ![pypi](https://github.com/dertilo/python-linters/actions/workflows/release.yml/badge.svg)
23
2
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
24
3
  [![bear-ified](https://raw.githubusercontent.com/beartype/beartype-assets/main/badge/bear-ified.svg)](https://beartype.readthedocs.io)
25
4
  # python-linters
26
- - bundles flake8, black, isort, ruff into a single poetry-script
5
+ - bundles ruff and basedpyright into a single poetry-script
27
6
  - project independent linter configuration (via `ruff.toml`,`.flake8`) -> one config to rule them all
28
7
  ### use-cases:
29
8
  1. interactively learning python code quality standards via ruff+flake8, see [local development workflow](#2-local-development-workflow-before-pushing-to-ci-pipeline)
30
9
  2. enforcing minimal quality-standards via a "lint-stage" in a ci-pipeline, see [setup for ci-pipeline](#3-setup-for-ci-pipeline)
10
+ ## 0. motivation:
11
+ ### Python is a Bicycle!
12
+ - initial idea of python: `Bridge the gap between the shell and C.`[see](https://www.artima.com/articles/pythons-design-goals)
13
+ ![img.png](images/python_is_a_bicycle.png)
14
+ ### run-time "safety"
15
+ - type and data validation at runtime
16
+ - pytests run your code -> runtime
17
+ ![img.png](images/runtime_safety.png)
18
+ ### lint-time "safety"
19
+ - linting and static type checking
20
+ ![img.png](images/lint_time_safety.png)
21
+ - flake8 already fell down!
22
+ ### more "safety" via shorter feed-back loops
23
+ ![img_3.png](images/feedback_loops.png)
24
+
31
25
 
32
26
  ## 1. install (via poetry)
33
27
  1. in `pyproject.toml` add to dependencies
@@ -80,7 +74,7 @@ cond9 --> | complains | cond47{{"ruff"}}:::style0
80
74
  cond47 --> | complains | op51[\"poetry run addnoqa"/]:::style1
81
75
  op51 --> sub53[\"refactor your code \nOR\n keep/add noqa-comment"/]:::style1
82
76
  sub53 --> op2
83
- cond47 --> | is happy | cond58{{"flake8"}}:::style0
77
+ cond47 --> | is happy | cond58{{"pyright"}}:::style0
84
78
  cond58 --> | complains | sub53
85
79
  cond58 --> | is happy | sub66["git commit"]
86
80
  sub66 --> sub80
@@ -134,19 +128,6 @@ All done! ✨ 🍰 ✨
134
128
  folders_to_be_linted=['whisper_streaming', 'tests']
135
129
  ```
136
130
 
137
- ### pythonlinter black is not happy
138
- ```commandline
139
- poetry run pythonlinter
140
- linter-order: black->ruff->flake8
141
- folders_tobelinted=['whisper_streaming', 'tests']
142
- running black
143
- would reformat <somehwere>/whisper-streaming/whisper_streaming/post_asr_preparations.py
144
- would reformat <somehwere>/whisper-streaming/whisper_streaming/faster_whisper_inference/faster_whisper_inferencer.py
145
-
146
- Oh no! 💥 💔 💥
147
- 2 files would be reformatted, 20 files would be left unchanged.
148
- python_linters.run_linters.LinterException: 💩 black is not happy! 💩
149
- ```
150
131
  ### pythonlinter ruff is not happy
151
132
  - complains about
152
133
  - an unused argument
@@ -167,27 +148,6 @@ Found 4 errors.
167
148
  No fixes available (2 hidden fixes can be enabled with the `--unsafe-fixes` option).
168
149
  python_linters.run_linters.LinterException: 💩 ruff is not happy! 💩
169
150
  ```
170
- ### pythonlinter flake8 is not happy
171
- - complains about too many methods in a class and wrong position of a function in a module
172
- ```commandline
173
- poetry run pythonlinter
174
- linter-order: black->ruff->flake8
175
- folders_tobelinted=['whisper_streaming', 'tests']
176
- running black
177
- All done! ✨ 🍰 ✨
178
- 22 files would be left unchanged.
179
-
180
- passed black linter! ✨ 🍰 ✨
181
-
182
- running ruff
183
-
184
- passed ruff linter! ✨ 🍰 ✨
185
-
186
- running flake8
187
- whisper_streaming/faster_whisper_inference/faster_whisper_inferencer.py:111:1: WPS214 Found too many methods: 10 > 9
188
- tests/conftest.py:35:8: NEW100 newspaper style: function is_in_docker_container defined in line 16 should be moved down
189
- python_linters.run_linters.LinterException: 💩 flake8 is not happy! 💩
190
- ```
191
151
  ### addnoqa example
192
152
  - only shows how many noqas it added (`3`) and how many files it left unchanged (`22`)
193
153
  ```commandline
@@ -200,11 +160,9 @@ addnoqa iteration: 0
200
160
  ```
201
161
  - it adds noqas like this one:
202
162
  ```python
203
- def foobar(x):
163
+ def foobar(x): # noqa: ANN001, ANN202
204
164
  return "whatever"
205
165
 
206
- from faster_whisper.transcribe import Segment as FasterWhisperSegment # noqa: E402
207
-
208
166
  ```
209
167
  # IDE integration
210
168
  ## manual vscode integration
@@ -237,4 +195,4 @@ from faster_whisper.transcribe import Segment as FasterWhisperSegment # noqa: E
237
195
  ![pycharm_settings.png](pycharm_settings.png)
238
196
  ## alternatives
239
197
  - https://github.com/astral-sh/ruff-vscode
240
- - https://plugins.jetbrains.com/plugin/20574-ruff
198
+ - https://plugins.jetbrains.com/plugin/20574-ruff
@@ -1,13 +1,12 @@
1
1
  [tool.poetry]
2
2
  name = "python-linters"
3
- version = "0.1.3"
3
+ version = "0.1.5"
4
4
  description = "bundling config files for linters like ruff and flake8"
5
5
  authors = ["Tilo Himmelsbach <dertilo@gmail.com>"]
6
6
  readme = "README.md"
7
7
  repository = "https://github.com/dertilo/python-linters"
8
8
  packages = [{ include = "python_linters" }]
9
-
10
- include=["python_linters/.flake8","python_linters/ruff.toml"]
9
+ license = "MIT"
11
10
 
12
11
  [tool.poetry.scripts]
13
12
  pythonlinter = 'python_linters.run_linters:main'
@@ -17,11 +16,8 @@ addnoqa = 'python_linters.add_noqa:main'
17
16
  [tool.poetry.dependencies]
18
17
  python = "^3.10"
19
18
  toml = "^0.10.2"
20
- flake8 = "^6.1.0"
21
- wemake-python-styleguide = "^0.18.0"
22
19
  ruff = ">=0.0.291"
23
- black = "^23.9.1"
24
- flake8-newspaper-style = "^0.4.3"
20
+ basedpyright = "^1.12.5"
25
21
 
26
22
  [tool.poetry.group.dev.dependencies]
27
23
  setuptools-scm = "^7.1.0"
@@ -2,16 +2,12 @@
2
2
  per-file-ignores=
3
3
  exclude=
4
4
  ignore = B
5
- select = WPS200,WPS202,WPS204,WPS214,WPS219,WPS227,WPS228,WPS229,WPS230,WPS231,WPS232,WPS233,WPS236,WPS425,WPS449, NEW
5
+ select = WPS204,WPS236, NEW
6
6
 
7
7
  # --- WPS: Complexity Violations by "wemake-python-styleguide" ---
8
8
  # see https://wemake-python-styleguide.readthedocs.io/en/latest/pages/usage/violations/complexity.html#complexity
9
9
 
10
- # WPS200: JonesScoreViolation -> this is a median of "LineComplexity", default is 12
11
- max-jones-score= 12
12
10
  # EXCLUDED WPS201: TooManyImportsViolation, default is 12, make no sense for heavy type-annotation usage, many types need to be imported
13
- # WPS202: TooManyModuleMembersViolation default is 7
14
- max-module-members=9
15
11
  # EXCLUDED WPS203: TooManyImportedNamesViolation, excluded because its not super necessary!
16
12
  # WPS204: OverusedExpressionViolation
17
13
  # EXCLUDED WPS210: TooManyLocalsViolation, has default of 5 which is too low, it encourages complex lines and is bad for readability!
@@ -19,30 +15,13 @@ max-module-members=9
19
15
  # EXCLUDED WPS211: TooManyArgumentsViolation -> this one is already covered by "PLR0913"
20
16
  # EXCLUDED WPS212: TooManyReturnsViolation -> this one is already covered by "PLR0911"
21
17
  ### WPS213: TooManyExpressionsViolation -> this one is already covered by "PLR0915"
22
- # WPS214: TooManyMethodsViolation, "wemake-default" is 7!
23
- max-methods=9
24
- # WPS219: TooDeepAccessViolation, Default: 4, tilo: even 3 is a lot!
25
- max-access-level=3
26
18
  # EXCLUDED WPS221: LineComplexityViolation, "wemake-default" is 14 -> this is not properly working for annotations!
27
19
  ##max-line-complexity=14
28
20
  # EXCLUDED WPS226: OverusedStringViolation -> sure that this isn't already in ruff? -> eigenverantwortung here?
29
- # WPS227: TooLongOutputTupleViolation
30
- # WPS228: TooLongCompareViolation -> already covered by WPS221?
31
- # WPS229: TooLongTryBodyViolation: default is at 1!!
32
- max-try-body-length=3
33
- # WPS230: TooManyPublicAttributesViolation, default is 6!
34
- max-attributes=6
35
- # WPS231: CognitiveComplexityViolation, wemake-default is 12, melevir's is 7
36
- max-cognitive-score=12
37
- # WPS232: CognitiveModuleComplexityViolation, default is 8
38
- max-cognitive-average=8
39
- # WPS233: TooLongCallChainViolation
40
21
  # WPS236: TooLongTupleUnpackViolation: "If you have more than 2 values in a tuple, consider using typing.NamedTuple or a dataclass instead." rather than unpacking third-party returns you should directly "pipe" into a dataclass
41
22
  max-tuple-unpack-length=4
42
- # WPS425 — Forbid booleans as non-keyword parameters.
43
- # WPS449 — Forbid float keys.
44
23
  # NEW https://github.com/mobility-university/flake8-newspaper-style
45
- # TMN002: TODO(tilo): maybe incorporate some of this packages logic into the linter?: essentially prohibiting tuples, see: flake8-too-many
24
+ # TMN002: TODO(tilo): maybe incorporate some of this packages logic into the linter?: essentially prohibiting tuples, see: https://gitlab.cc-asp.fraunhofer.de/iais.nm.audio/sandbox/nmaudio-flake8-too-many
46
25
  # max-function-return-values=3
47
26
 
48
27
 
@@ -0,0 +1,30 @@
1
+ import os
2
+
3
+ from python_linters.extending_ruff_toml import create_extended_ruff_toml
4
+ from python_linters.getting_to_be_linted_folders import get_folders_to_be_linted
5
+ from python_linters.run_linters import run_cmd
6
+
7
+
8
+ def main() -> None:
9
+ """
10
+ expects to run from $ContentRoot$
11
+ """
12
+ folders_tobelinted = get_folders_to_be_linted("pyproject.toml")
13
+ BIG_NUMBER = 9
14
+ for k in range(
15
+ BIG_NUMBER,
16
+ ): # theoretically could be necessary to run this loop as long as code changes!
17
+ print(f"addnoqa iteration: {k}")
18
+ run_cmd(
19
+ f"ruff check {' '.join(folders_tobelinted)} --config={create_extended_ruff_toml()} --add-noqa",
20
+ )
21
+ try:
22
+ run_cmd(f"ruff format --check {' '.join(folders_tobelinted)}")
23
+ break
24
+ except Exception:
25
+ run_cmd(f"ruff format {' '.join(folders_tobelinted)}")
26
+ run_cmd(f"fixcode {os.getcwd()}")
27
+
28
+
29
+ if __name__ == "__main__":
30
+ main()
@@ -0,0 +1,11 @@
1
+ import pathlib
2
+
3
+ package_dir = str(pathlib.Path(__file__).parent.resolve())
4
+
5
+ FLAKE8_CONFIG_FILE = f"{package_dir}/.flake8"
6
+ RUFF_CONFIG_FILE = f"{package_dir}/ruff.toml"
7
+ PYRIGHT_CONFIG_FILE = f"{package_dir}/pyrightconfig.json"
8
+
9
+ assert pathlib.Path(FLAKE8_CONFIG_FILE).is_file()
10
+ assert pathlib.Path(RUFF_CONFIG_FILE).is_file()
11
+ assert pathlib.Path(PYRIGHT_CONFIG_FILE).is_file()
@@ -0,0 +1,37 @@
1
+ from collections.abc import Iterator
2
+ from pathlib import Path
3
+
4
+ from python_linters.config_files import RUFF_CONFIG_FILE
5
+
6
+
7
+ def create_extended_ruff_toml() -> str:
8
+ ruff_extension_file = Path("ruff_extension.toml")
9
+
10
+ if ruff_extension_file.is_file():
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"
13
+ if not Path(extended_ruff_toml_file).is_file():
14
+ print(f"extending {RUFF_CONFIG_FILE} with {ruff_extension_file=}")
15
+ with Path(extended_ruff_toml_file).open("w", encoding="locale") as f:
16
+ lines = [
17
+ f'extend = "{RUFF_CONFIG_FILE}"',
18
+ *list(_read_lines(ruff_extension_file)),
19
+ ]
20
+ for l in lines:
21
+ f.write(f"{l}\n")
22
+ else:
23
+ print(f"using {extended_ruff_toml_file}")
24
+ else:
25
+ extended_ruff_toml_file = RUFF_CONFIG_FILE
26
+ return extended_ruff_toml_file
27
+
28
+
29
+ def _read_lines(
30
+ file: Path,
31
+ encoding: str = "utf-8",
32
+ ) -> Iterator[str]:
33
+ with file.open(mode="rb") as f:
34
+ for raw_line in f:
35
+ line = raw_line.decode(encoding)
36
+ line = line.replace("\n", "").replace("\r", "")
37
+ yield line
@@ -2,27 +2,27 @@ import subprocess
2
2
  import sys
3
3
  from pathlib import Path
4
4
 
5
- from python_linters.run_linters import RUFF_CONFIG_FILE, get_folders_to_be_linted
5
+ from python_linters.extending_ruff_toml import create_extended_ruff_toml
6
+ from python_linters.getting_to_be_linted_folders import get_folders_to_be_linted
6
7
 
7
8
 
8
- def run_cmd(cmd):
9
+ def run_cmd(cmd) -> None:
9
10
  subprocess.check_call(cmd, stdout=sys.stdout, stderr=sys.stdout, shell=True)
10
11
 
11
12
 
12
- def run_cmd_ignore_errors(cmd):
13
+ def run_cmd_ignore_errors(cmd) -> None:
13
14
  subprocess.call(cmd, stdout=sys.stdout, stderr=sys.stdout, shell=True)
14
15
 
15
16
 
16
- def main():
17
+ def main() -> None:
17
18
  dirr = Path.cwd()
18
19
  folders_to_be_linted = get_folders_to_be_linted("pyproject.toml")
19
20
  folders = " ".join(folders_to_be_linted)
20
21
 
21
- run_cmd(f"cd {dirr} && isort --profile black {folders}")
22
22
  run_cmd_ignore_errors(
23
- f"cd {dirr} && ruff check {folders} --config={RUFF_CONFIG_FILE} --fix",
23
+ f"cd {dirr} && ruff check {folders} --config={create_extended_ruff_toml()} --fix",
24
24
  )
25
- run_cmd(f"cd {dirr} && black {folders}")
25
+ run_cmd(f"cd {dirr} && ruff format {folders}")
26
26
 
27
27
 
28
28
  if __name__ == "__main__":
@@ -0,0 +1,51 @@
1
+ import pathlib
2
+ import sys
3
+
4
+ import toml
5
+
6
+
7
+ class PackagesOrFoldersToBeLintedAreNotProperlyDefined(Exception): # noqa: N818
8
+ def __init__(self) -> None:
9
+ sys.tracebacklimit = -1
10
+ msg = """in your pyproject.toml specify the directories that you want to be linted
11
+ a. via packages
12
+
13
+ packages = [
14
+ { include = \"my_package_name\" },
15
+ ]
16
+
17
+ b. or via tool.python-linters
18
+
19
+ [tool.python-linters]
20
+ folders_to_be_linted=["my_directory","another_dir/my_sub_package"]"""
21
+ super().__init__(msg)
22
+
23
+
24
+ def get_folders_to_be_linted(pyproject_toml: str) -> list[str]:
25
+ if not pathlib.Path(pyproject_toml).is_file():
26
+ msg = f"pyproject.toml not found in {pathlib.Path.cwd()}\nplease run this script from the root of your project"
27
+ raise FileNotFoundError(
28
+ msg,
29
+ )
30
+
31
+ with open(pyproject_toml, encoding="locale") as f:
32
+ t = toml.load(f)
33
+ folders = (
34
+ t.get("tool", {})
35
+ .get("python-linters", {})
36
+ .get("folders_to_be_linted", None)
37
+ )
38
+ if (
39
+ folders is None
40
+ and (packages := t.get("tool", {}).get("poetry", {}).get("packages", None))
41
+ is not None
42
+ ):
43
+ folders = [p["include"] for p in packages]
44
+ if pathlib.Path(f"{pathlib.Path(pyproject_toml).parent}/tests").is_dir():
45
+ folders += ["tests"]
46
+
47
+ if folders is None:
48
+ raise PackagesOrFoldersToBeLintedAreNotProperlyDefined
49
+ assert len(folders) > 0
50
+ print(f"found following {folders=}")
51
+ return folders
@@ -10,9 +10,7 @@ if __name__ == "__main__":
10
10
  )
11
11
  return code, f"{short_expl}: {long_expl}"
12
12
 
13
- with open("python_linter_configs/ruff_rules.txt") as f:
14
- code_desc = (
15
- parse_line(l) for l in f.readlines() if pattern.match(l) is not None
16
- )
13
+ with open("python_linter_configs/ruff_rules.txt", encoding="locale") as f:
14
+ code_desc = (parse_line(l) for l in f if pattern.match(l) is not None)
17
15
  code2desc = dict(code_desc)
18
16
  print(code2desc)
@@ -0,0 +1,21 @@
1
+ {
2
+ "typeCheckingMode": "basic", // basic to ease transition, this entire config is (automatically copypasted and) to be adapted to each project
3
+ "reportAny": false,
4
+ "reportImplicitOverride": false,
5
+ // cause typing.override leads to false positives
6
+ "reportMissingSuperCall": false,
7
+ "reportMissingParameterType": false, // already done by ruff
8
+ "reportUnusedCallResult": false, // gibt schlimmeres!
9
+ "reportPrivateUsage": false, // underscore convention is enough!
10
+ "reportIncompatibleMethodOverride": false,
11
+ "reportMissingTypeStubs": false,
12
+ "reportUnknownLambdaType": false, // seems to affect all lambdas
13
+
14
+
15
+ "reportUnknownVariableType": "warning",
16
+ "reportUnknownMemberType": "warning",
17
+ "reportUnknownArgumentType": "warning",
18
+ "reportMissingTypeArgument": "warning",
19
+ "reportUnknownParameterType": "warning",
20
+ "reportUnusedVariable": "warning"
21
+ }
@@ -1,6 +1,14 @@
1
1
  target-version = "py310"
2
2
  # Exclude a variety of commonly ignored directories. see https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml
3
3
  [lint]
4
+ preview = true
5
+ extend-safe-fixes = ["ALL"] # yolo
6
+ extend-unsafe-fixes=[
7
+ "T20", # don't remove prints
8
+ "FURB140" # starmap slower than comprehension in 3.12
9
+ ]
10
+
11
+
4
12
  exclude = [
5
13
  ".bzr",
6
14
  ".direnv",
@@ -36,6 +44,7 @@ select = [
36
44
  "YTT", # flake8-2020
37
45
  "ANN", # flake8-annotations
38
46
  "ASYNC", # flake8-async
47
+ "TRIO", # flake8-trio
39
48
  "S", # flake8-bandit
40
49
  "BLE", # flake8-blind-except
41
50
  # excluded "FBT", # flake8-boolean-trap -> tilo: this one might be too strict/hard to refactor
@@ -52,6 +61,7 @@ select = [
52
61
  "FA", # flake8-future-annotations
53
62
  "ISC", # flake8-implicit-str-concat
54
63
  "ICN", # flake8-import-conventions
64
+ "LOG", # flake8-logging
55
65
  "G", # flake8-logging-format
56
66
  "INP", # flake8-no-pep420
57
67
  "PIE", # flake8-pie
@@ -75,13 +85,16 @@ select = [
75
85
  "PD", # pandas-vet
76
86
  "PGH", # pygrep-hooks
77
87
  "PL", # Pylint
88
+ "C", # Convention
89
+ "E", # Error
90
+ "R", # Refactor
91
+ "W", # Warning
78
92
  "TRY", # tryceratops
79
93
  "FLY", # flynt
80
94
  "NPY", # NumPy-specific rules
81
95
  # excluded "AIR", # Airflow -> we are not using airflow!
82
96
  "PERF", # Perflint
83
- # excluded "FURB", # refurb -> not yet stable?
84
- # excluded "LOG", # flake8-logging -> not yet stabel?
97
+ "FURB", # refurb
85
98
  "RUF", # Ruff-specific rules
86
99
  ]
87
100
  # --------------- IGNORING specific rules --------------------------------------------------------
@@ -112,6 +125,7 @@ ignore = [
112
125
  "UP008", # see: https://stackoverflow.com/questions/73268995/typeerror-when-calling-super-in-dataclassslots-true-subclass
113
126
  "RET506", # looks like a false-positive
114
127
  "SLF001", # everybody knows about the _-conventin, if one still accesses "protected" attributes one has their reasons!
128
+ "FURB140", # starmap slower than comprehension in 3.12
115
129
  ]
116
130
 
117
131
  [lint.mccabe]
@@ -140,6 +154,5 @@ max-returns = 3
140
154
  # PLR0915 default is 50! -> can you ever reach this limit? max-complexity would catch those code-blocks first!
141
155
  max-statements = 25
142
156
  # --------------- PLR0904 too-many-public-methods --------------------------------------------------------
143
- # TODO: seems to have no effect, not working at all! ruff bug?
144
- # Default value: 20, TODO: this rule is "shadowed" by wemake's TooManyMethodsViolation (WPS214) which has default of 7, I set it to 9 and is counting all methods, also @properties?
145
- max-public-methods = 0
157
+ # Default value: 20, wemake's TooManyMethodsViolation (WPS214) has default of 7
158
+ max-public-methods = 20 # is not only counting public but all methods!
@@ -11,6 +11,7 @@ pyupgrade (UP)
11
11
  flake8-2020 (YTT)
12
12
  flake8-annotations (ANN)
13
13
  flake8-async (ASYNC)
14
+ flake8-trio (TRIO)
14
15
  flake8-bandit (S)
15
16
  flake8-blind-except (BLE)
16
17
  flake8-boolean-trap (FBT)
@@ -27,6 +28,7 @@ flake8-executable (EXE)
27
28
  flake8-future-annotations (FA)
28
29
  flake8-implicit-str-concat (ISC)
29
30
  flake8-import-conventions (ICN)
31
+ flake8-logging (LOG)
30
32
  flake8-logging-format (G)
31
33
  flake8-no-pep420 (INP)
32
34
  flake8-pie (PIE)
@@ -50,18 +52,21 @@ eradicate (ERA)
50
52
  pandas-vet (PD)
51
53
  pygrep-hooks (PGH)
52
54
  Pylint (PL)
55
+ Convention (C)
56
+ Error (E)
57
+ Refactor (R)
58
+ Warning (W)
53
59
  tryceratops (TRY)
54
60
  flynt (FLY)
55
61
  NumPy-specific rules (NPY)
56
62
  Airflow (AIR)
57
63
  Perflint (PERF)
58
64
  refurb (FURB)
59
- flake8-logging (LOG)
60
65
  Ruff-specific rules (RUF)
61
66
  """
62
67
 
63
68
 
64
- def copypaste_this_into_ruff_tomls_select_list():
69
+ def copypaste_this_into_ruff_tomls_select_list() -> None:
65
70
  names_code = [l.replace(")", "").split("(") for l in rule_categories.split("\n")]
66
71
  code_names = [(cn[1], cn[0]) for cn in names_code if len(cn) == 2]
67
72
  print(code_names)
@@ -0,0 +1,44 @@
1
+ import subprocess
2
+ import sys
3
+
4
+ from python_linters.config_files import PYRIGHT_CONFIG_FILE
5
+ from python_linters.extending_ruff_toml import create_extended_ruff_toml
6
+ from python_linters.getting_to_be_linted_folders import get_folders_to_be_linted
7
+
8
+
9
+ def run_cmd(cmd: str) -> int:
10
+ return subprocess.call(cmd, stdout=sys.stdout, stderr=sys.stdout, shell=True)
11
+
12
+
13
+ class LinterException(Exception):
14
+ def __init__(self, linter_name: str):
15
+ sys.tracebacklimit = -1 # to disable traceback
16
+ super().__init__(f"💩 {linter_name} is not happy! 💩")
17
+
18
+
19
+ NAME2LINTER = {
20
+ "ruff-format": lambda folders_tobelinted: f"ruff format --check {' '.join(folders_tobelinted)}",
21
+ "ruff": lambda folders_tobelinted: f"poetry run ruff check {' '.join(folders_tobelinted)} --config={create_extended_ruff_toml()}",
22
+ "basedpyright": lambda folders_tobelinted: f"cp -n {PYRIGHT_CONFIG_FILE} ./ && poetry run basedpyright {' '.join(folders_tobelinted)} --gitlabcodequality report.json --level $(cat pyrightlevel.txt 2>/dev/null || echo 'warning')",
23
+ # most flake8 linters are already included in ruff
24
+ # the news-paper-style function ordering rule is currently only enforced by a flake8 plugin but its not that important
25
+ # "flake8": lambda folders_tobelinted: f"poetry run flake8 --config={FLAKE8_CONFIG_FILE} {' '.join(folders_tobelinted)}",
26
+ }
27
+
28
+
29
+ def main() -> None:
30
+ folders_tobelinted = get_folders_to_be_linted("pyproject.toml")
31
+ print(f"linter-order: {'->'.join(NAME2LINTER.keys())}")
32
+ sys.stdout.flush()
33
+
34
+ for linter_name, linter_cmd_factory in NAME2LINTER.items():
35
+ print(f"running {linter_name}")
36
+ sys.stdout.flush()
37
+
38
+ if run_cmd(linter_cmd_factory(folders_tobelinted)) != 0:
39
+ raise LinterException(linter_name)
40
+ print(f"\npassed {linter_name} linter! ✨ 🍰 ✨\n")
41
+
42
+
43
+ if __name__ == "__main__":
44
+ main()
@@ -1,32 +0,0 @@
1
- import os
2
-
3
- from python_linters.run_linters import (
4
- RUFF_CONFIG_FILE,
5
- get_folders_to_be_linted,
6
- run_cmd,
7
- )
8
-
9
-
10
- def main():
11
- """
12
- expects to run from $ContentRoot$
13
- """
14
- folders_tobelinted = get_folders_to_be_linted("pyproject.toml")
15
- BIG_NUMBER = 9
16
- for k in range(
17
- BIG_NUMBER,
18
- ): # theoretically could be necessary to run this loop as long as code changes!
19
- print(f"addnoqa iteration: {k}")
20
- run_cmd(
21
- f"poetry run ruff check {' '.join(folders_tobelinted)} --config={RUFF_CONFIG_FILE} --add-noqa",
22
- )
23
- try:
24
- run_cmd(f"black --check {' '.join(folders_tobelinted)}")
25
- break
26
- except Exception:
27
- run_cmd(f"black {' '.join(folders_tobelinted)}")
28
- run_cmd(f"poetry run fixcode {os.getcwd()}")
29
-
30
-
31
- if __name__ == "__main__":
32
- main()
@@ -1,105 +0,0 @@
1
- import pathlib
2
- import subprocess
3
- import sys
4
-
5
- import toml
6
-
7
-
8
- # TODO: one could brute-force search all files and replace codes, but is there a more elegant way?
9
- # def replace_noqa_code():
10
- # >> > import re
11
- # >> > l = {'NORTH': 'N', 'SOUTH': 'S', 'EAST': 'E', 'WEST': 'W'}
12
- # >> > pattern = '|'.join(sorted(re.escape(k) for k in l))
13
- # >> > address = "123 north anywhere street"
14
- # >> > re.sub(pattern, lambda m: l.get(m.group(0).upper()), address,
15
- # flags=re.IGNORECASE)
16
- # '123 N anywhere street'
17
- # >> >
18
- #
19
- def run_cmd(cmd: str) -> int:
20
- return subprocess.call(cmd, stdout=sys.stdout, stderr=sys.stdout, shell=True)
21
-
22
-
23
- package_dir = str(pathlib.Path(__file__).parent.resolve())
24
-
25
- FLAKE8_CONFIG_FILE = f"{package_dir}/.flake8"
26
- RUFF_CONFIG_FILE = f"{package_dir}/ruff.toml"
27
- assert pathlib.Path(FLAKE8_CONFIG_FILE).is_file()
28
- assert pathlib.Path(RUFF_CONFIG_FILE).is_file()
29
-
30
- RUFF_ARGS = ["--fix", "--add-noqa"]
31
-
32
-
33
- class PackagesOrFoldersToBeLintedAreNotProperlyDefined(Exception): # noqa: N818
34
- def __init__(self) -> None:
35
- sys.tracebacklimit = -1
36
- msg = """in your pyproject.toml specify the directories that you want to be linted
37
- a. via packages
38
-
39
- packages = [
40
- { include = \"my_package_name\" },
41
- ]
42
-
43
- b. or via tool.python-linters
44
-
45
- [tool.python-linters]
46
- folders_to_be_linted=["my_directory","another_dir/my_sub_package"]"""
47
- super().__init__(msg)
48
-
49
-
50
- def get_folders_to_be_linted(pyproject_toml: str) -> list[str]:
51
- if not pathlib.Path(pyproject_toml).is_file():
52
- raise FileNotFoundError(
53
- f"pyproject.toml not found in {pathlib.Path.cwd()}\nplease run this script from the root of your project",
54
- )
55
-
56
- with open(pyproject_toml) as f:
57
- t = toml.load(f)
58
- folders = (
59
- t.get("tool", {}).get("python-linters", {}).get("folders_to_be_linted", None)
60
- )
61
- if (
62
- folders is None
63
- and (packages := t.get("tool", {}).get("poetry", {}).get("packages", None))
64
- is not None
65
- ):
66
- folders = [p["include"] for p in packages]
67
- if pathlib.Path(f"{pathlib.Path(pyproject_toml).parent}/tests").is_dir():
68
- folders += ["tests"]
69
-
70
- if folders is None:
71
- raise PackagesOrFoldersToBeLintedAreNotProperlyDefined
72
- assert len(folders) > 0
73
- print(f"found following {folders=}")
74
- return folders
75
-
76
-
77
- class LinterException(Exception):
78
- def __init__(self, linter_name: str):
79
- sys.tracebacklimit = -1 # to disable traceback
80
- super().__init__(f"💩 {linter_name} is not happy! 💩")
81
-
82
-
83
- NAME2LINTER = {
84
- "black": lambda folders_tobelinted: f"black --check {' '.join(folders_tobelinted)}",
85
- "ruff": lambda folders_tobelinted: f"poetry run ruff check {' '.join(folders_tobelinted)} --config={RUFF_CONFIG_FILE}",
86
- "flake8": lambda folders_tobelinted: f"poetry run flake8 --config={FLAKE8_CONFIG_FILE} {' '.join(folders_tobelinted)}",
87
- }
88
-
89
-
90
- def main():
91
- folders_tobelinted = get_folders_to_be_linted("pyproject.toml")
92
- print(f"linter-order: {'->'.join(NAME2LINTER.keys())}")
93
- sys.stdout.flush()
94
-
95
- for linter_name, linter_cmd_factory in NAME2LINTER.items():
96
- print(f"running {linter_name}")
97
- sys.stdout.flush()
98
-
99
- if run_cmd(linter_cmd_factory(folders_tobelinted)) != 0:
100
- raise LinterException(linter_name)
101
- print(f"\npassed {linter_name} linter! ✨ 🍰 ✨\n")
102
-
103
-
104
- if __name__ == "__main__":
105
- main()
File without changes