python-linters 0.1.3__tar.gz → 0.1.4__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.3 → python_linters-0.1.4}/PKG-INFO +5 -2
- {python_linters-0.1.3 → python_linters-0.1.4}/README.md +1 -1
- {python_linters-0.1.3 → python_linters-0.1.4}/pyproject.toml +3 -3
- {python_linters-0.1.3 → python_linters-0.1.4}/python_linters/.flake8 +2 -23
- python_linters-0.1.4/python_linters/add_noqa.py +30 -0
- python_linters-0.1.4/python_linters/config_files.py +11 -0
- python_linters-0.1.4/python_linters/extending_ruff_toml.py +37 -0
- {python_linters-0.1.3 → python_linters-0.1.4}/python_linters/fix_code.py +7 -7
- python_linters-0.1.4/python_linters/getting_to_be_linted_folders.py +51 -0
- {python_linters-0.1.3 → python_linters-0.1.4}/python_linters/parse_rules.py +2 -4
- python_linters-0.1.4/python_linters/pyrightconfig.json +21 -0
- {python_linters-0.1.3 → python_linters-0.1.4}/python_linters/ruff.toml +18 -5
- {python_linters-0.1.3 → python_linters-0.1.4}/python_linters/ruff_rule_categories.py +7 -2
- python_linters-0.1.4/python_linters/run_linters.py +42 -0
- python_linters-0.1.3/python_linters/add_noqa.py +0 -32
- python_linters-0.1.3/python_linters/run_linters.py +0 -105
- {python_linters-0.1.3 → python_linters-0.1.4}/LICENSE +0 -0
- {python_linters-0.1.3 → python_linters-0.1.4}/python_linters/__init__.py +0 -0
- {python_linters-0.1.3 → python_linters-0.1.4}/python_linters/ruff_rules.txt +0 -0
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-linters
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: bundling config files for linters like ruff and flake8
|
|
5
5
|
Home-page: https://github.com/dertilo/python-linters
|
|
6
|
+
License: MIT
|
|
6
7
|
Author: Tilo Himmelsbach
|
|
7
8
|
Author-email: dertilo@gmail.com
|
|
8
9
|
Requires-Python: >=3.10,<4.0
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
11
|
Classifier: Programming Language :: Python :: 3
|
|
10
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
11
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Dist: basedpyright (>=1.12.5,<2.0.0)
|
|
13
16
|
Requires-Dist: black (>=23.9.1,<24.0.0)
|
|
14
17
|
Requires-Dist: flake8 (>=6.1.0,<7.0.0)
|
|
15
18
|
Requires-Dist: flake8-newspaper-style (>=0.4.3,<0.5.0)
|
|
@@ -23,7 +26,7 @@ Description-Content-Type: text/markdown
|
|
|
23
26
|
[](https://opensource.org/licenses/MIT)
|
|
24
27
|
[](https://beartype.readthedocs.io)
|
|
25
28
|
# python-linters
|
|
26
|
-
- bundles flake8,
|
|
29
|
+
- bundles flake8, ruff and basedpyright into a single poetry-script
|
|
27
30
|
- project independent linter configuration (via `ruff.toml`,`.flake8`) -> one config to rule them all
|
|
28
31
|
### use-cases:
|
|
29
32
|
1. interactively learning python code quality standards via ruff+flake8, see [local development workflow](#2-local-development-workflow-before-pushing-to-ci-pipeline)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
[](https://opensource.org/licenses/MIT)
|
|
3
3
|
[](https://beartype.readthedocs.io)
|
|
4
4
|
# python-linters
|
|
5
|
-
- bundles flake8,
|
|
5
|
+
- bundles flake8, ruff and basedpyright into a single poetry-script
|
|
6
6
|
- project independent linter configuration (via `ruff.toml`,`.flake8`) -> one config to rule them all
|
|
7
7
|
### use-cases:
|
|
8
8
|
1. interactively learning python code quality standards via ruff+flake8, see [local development workflow](#2-local-development-workflow-before-pushing-to-ci-pipeline)
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-linters"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.4"
|
|
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'
|
|
@@ -22,6 +21,7 @@ wemake-python-styleguide = "^0.18.0"
|
|
|
22
21
|
ruff = ">=0.0.291"
|
|
23
22
|
black = "^23.9.1"
|
|
24
23
|
flake8-newspaper-style = "^0.4.3"
|
|
24
|
+
basedpyright = "^1.12.5"
|
|
25
25
|
|
|
26
26
|
[tool.poetry.group.dev.dependencies]
|
|
27
27
|
setuptools-scm = "^7.1.0"
|
|
@@ -2,16 +2,12 @@
|
|
|
2
2
|
per-file-ignores=
|
|
3
3
|
exclude=
|
|
4
4
|
ignore = B
|
|
5
|
-
select =
|
|
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.
|
|
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={
|
|
23
|
+
f"cd {dirr} && ruff check {folders} --config={create_extended_ruff_toml()} --fix",
|
|
24
24
|
)
|
|
25
|
-
run_cmd(f"cd {dirr} &&
|
|
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
|
-
|
|
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
|
-
#
|
|
144
|
-
|
|
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,42 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from python_linters.config_files import FLAKE8_CONFIG_FILE, 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
|
+
"flake8": lambda folders_tobelinted: f"poetry run flake8 --config={FLAKE8_CONFIG_FILE} {' '.join(folders_tobelinted)}",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def main() -> None:
|
|
28
|
+
folders_tobelinted = get_folders_to_be_linted("pyproject.toml")
|
|
29
|
+
print(f"linter-order: {'->'.join(NAME2LINTER.keys())}")
|
|
30
|
+
sys.stdout.flush()
|
|
31
|
+
|
|
32
|
+
for linter_name, linter_cmd_factory in NAME2LINTER.items():
|
|
33
|
+
print(f"running {linter_name}")
|
|
34
|
+
sys.stdout.flush()
|
|
35
|
+
|
|
36
|
+
if run_cmd(linter_cmd_factory(folders_tobelinted)) != 0:
|
|
37
|
+
raise LinterException(linter_name)
|
|
38
|
+
print(f"\npassed {linter_name} linter! ✨ 🍰 ✨\n")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
if __name__ == "__main__":
|
|
42
|
+
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
|
|
File without changes
|
|
File without changes
|