python-linters 0.1.2__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.
@@ -1,15 +1,18 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-linters
3
- Version: 0.1.2
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
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
24
27
  [![bear-ified](https://raw.githubusercontent.com/beartype/beartype-assets/main/badge/bear-ified.svg)](https://beartype.readthedocs.io)
25
28
  # python-linters
26
- - bundles flake8, black, isort, ruff into a single poetry-script
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
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
3
3
  [![bear-ified](https://raw.githubusercontent.com/beartype/beartype-assets/main/badge/bear-ified.svg)](https://beartype.readthedocs.io)
4
4
  # python-linters
5
- - bundles flake8, black, isort, ruff into a single poetry-script
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.2"
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 = 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,8 +1,14 @@
1
1
  target-version = "py310"
2
- [lint.isort]
3
- profile = "black"
4
2
  # Exclude a variety of commonly ignored directories. see https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml
5
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
+
6
12
  exclude = [
7
13
  ".bzr",
8
14
  ".direnv",
@@ -38,6 +44,7 @@ select = [
38
44
  "YTT", # flake8-2020
39
45
  "ANN", # flake8-annotations
40
46
  "ASYNC", # flake8-async
47
+ "TRIO", # flake8-trio
41
48
  "S", # flake8-bandit
42
49
  "BLE", # flake8-blind-except
43
50
  # excluded "FBT", # flake8-boolean-trap -> tilo: this one might be too strict/hard to refactor
@@ -54,6 +61,7 @@ select = [
54
61
  "FA", # flake8-future-annotations
55
62
  "ISC", # flake8-implicit-str-concat
56
63
  "ICN", # flake8-import-conventions
64
+ "LOG", # flake8-logging
57
65
  "G", # flake8-logging-format
58
66
  "INP", # flake8-no-pep420
59
67
  "PIE", # flake8-pie
@@ -77,13 +85,16 @@ select = [
77
85
  "PD", # pandas-vet
78
86
  "PGH", # pygrep-hooks
79
87
  "PL", # Pylint
88
+ "C", # Convention
89
+ "E", # Error
90
+ "R", # Refactor
91
+ "W", # Warning
80
92
  "TRY", # tryceratops
81
93
  "FLY", # flynt
82
94
  "NPY", # NumPy-specific rules
83
95
  # excluded "AIR", # Airflow -> we are not using airflow!
84
96
  "PERF", # Perflint
85
- # excluded "FURB", # refurb -> not yet stable?
86
- # excluded "LOG", # flake8-logging -> not yet stabel?
97
+ "FURB", # refurb
87
98
  "RUF", # Ruff-specific rules
88
99
  ]
89
100
  # --------------- IGNORING specific rules --------------------------------------------------------
@@ -114,6 +125,7 @@ ignore = [
114
125
  "UP008", # see: https://stackoverflow.com/questions/73268995/typeerror-when-calling-super-in-dataclassslots-true-subclass
115
126
  "RET506", # looks like a false-positive
116
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
117
129
  ]
118
130
 
119
131
  [lint.mccabe]
@@ -142,6 +154,5 @@ max-returns = 3
142
154
  # PLR0915 default is 50! -> can you ever reach this limit? max-complexity would catch those code-blocks first!
143
155
  max-statements = 25
144
156
  # --------------- PLR0904 too-many-public-methods --------------------------------------------------------
145
- # TODO: seems to have no effect, not working at all! ruff bug?
146
- # 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?
147
- 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