pytest-ditto 0.0.1__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.
- pytest_ditto-0.0.1/.gitignore +12 -0
- pytest_ditto-0.0.1/PKG-INFO +31 -0
- pytest_ditto-0.0.1/README.md +1 -0
- pytest_ditto-0.0.1/ditto/__init__.py +6 -0
- pytest_ditto-0.0.1/ditto/_unittest.py +29 -0
- pytest_ditto-0.0.1/ditto/_version.py +1 -0
- pytest_ditto-0.0.1/ditto/io/__init__.py +37 -0
- pytest_ditto-0.0.1/ditto/io/_json.py +18 -0
- pytest_ditto-0.0.1/ditto/io/_pandas_parquet.py +18 -0
- pytest_ditto-0.0.1/ditto/io/_pickle.py +20 -0
- pytest_ditto-0.0.1/ditto/io/_yaml.py +18 -0
- pytest_ditto-0.0.1/ditto/io/protocol.py +13 -0
- pytest_ditto-0.0.1/ditto/plugin.py +71 -0
- pytest_ditto-0.0.1/ditto/snapshot.py +73 -0
- pytest_ditto-0.0.1/pyproject.toml +166 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: pytest-ditto
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Author-email: Lachlan Taylor <lachlanbtaylor@proton.me>
|
|
5
|
+
Maintainer-email: Lachlan Taylor <lachlanbtaylor@proton.me>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: pytest
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Framework :: Pytest
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Testing
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: pandas
|
|
22
|
+
Requires-Dist: pyarrow
|
|
23
|
+
Requires-Dist: pytest>=3.5.0
|
|
24
|
+
Requires-Dist: pyyaml
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: hatch-vcs>=0.4.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: hatch>=1.9.4; extra == 'dev'
|
|
28
|
+
Requires-Dist: pre-commit; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# pytest-ditto
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# pytest-ditto
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import inspect
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from ditto.snapshot import Snapshot
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _calling_test_path() -> Path:
|
|
10
|
+
frame = inspect.currentframe()
|
|
11
|
+
outer_frames = inspect.getouterframes(frame)
|
|
12
|
+
# 2 calls back up the stack, index 1 of the frame has the calling filepath
|
|
13
|
+
return Path(outer_frames[2][1]).parent
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DittoTestCase(unittest.TestCase):
|
|
17
|
+
|
|
18
|
+
record: ClassVar[bool] = True
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def snapshot(self) -> Snapshot:
|
|
22
|
+
path = _calling_test_path() / ".ditto"
|
|
23
|
+
path.mkdir(exist_ok=True)
|
|
24
|
+
|
|
25
|
+
return Snapshot(
|
|
26
|
+
path=path,
|
|
27
|
+
name=".".join(self.id().split(".")[-3:]),
|
|
28
|
+
record=self.record,
|
|
29
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.0.1'
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from ditto.io.protocol import SnapshotIO
|
|
2
|
+
from ditto.io._yaml import YamlIO
|
|
3
|
+
from ditto.io._json import JsonIO
|
|
4
|
+
from ditto.io._pickle import PickleIO
|
|
5
|
+
from ditto.io._pandas_parquet import PandasParquetIO
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"SnapshotIO",
|
|
10
|
+
"YamlIO",
|
|
11
|
+
"JsonIO",
|
|
12
|
+
"PickleIO",
|
|
13
|
+
"PandasParquetIO",
|
|
14
|
+
"register",
|
|
15
|
+
"get",
|
|
16
|
+
"default",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
_NAME_IO_MAP: dict[str, type[SnapshotIO]] = {
|
|
21
|
+
"pkl": PickleIO,
|
|
22
|
+
"json": JsonIO,
|
|
23
|
+
"yaml": YamlIO,
|
|
24
|
+
"pandas_parquet": PandasParquetIO,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def register(name: str, io: type[SnapshotIO]) -> None:
|
|
29
|
+
_NAME_IO_MAP[name] = io
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get(name: str, default: SnapshotIO = PickleIO) -> SnapshotIO:
|
|
33
|
+
return _NAME_IO_MAP.get(name, default)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def default() -> type[SnapshotIO]:
|
|
37
|
+
return PickleIO
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import ClassVar, Any
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class JsonIO:
|
|
8
|
+
extension: ClassVar[str] = "json"
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def save(data: Any, filepath: Path) -> None:
|
|
12
|
+
with open(filepath, "w") as f:
|
|
13
|
+
json.dump(data, f)
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def load(filepath: Path) -> Any:
|
|
17
|
+
with open(filepath, "r") as f:
|
|
18
|
+
return json.load(f)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import ClassVar
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
from ditto.io.protocol import SnapshotIO
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PandasParquetIO(SnapshotIO):
|
|
10
|
+
extension: ClassVar[str] = "parquet"
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def save(data: pd.DataFrame, filepath: Path) -> None:
|
|
14
|
+
data.to_parquet(filepath)
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def load(filepath: Path) -> pd.DataFrame:
|
|
18
|
+
return pd.read_parquet(filepath)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import ClassVar, Any
|
|
3
|
+
|
|
4
|
+
import pickle
|
|
5
|
+
|
|
6
|
+
from ditto.io import SnapshotIO
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PickleIO(SnapshotIO):
|
|
10
|
+
extension: ClassVar[str] = "pkl"
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def save(data: Any, filepath: Path) -> None:
|
|
14
|
+
with open(filepath, "wb") as f:
|
|
15
|
+
pickle.dump(data, f)
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def load(filepath: Path) -> Any:
|
|
19
|
+
with open(filepath, "rb") as f:
|
|
20
|
+
return pickle.load(f)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import ClassVar, Any
|
|
3
|
+
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class YamlIO:
|
|
8
|
+
extension: ClassVar[str] = "yaml"
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def save(data: Any, filepath: Path) -> None:
|
|
12
|
+
with open(filepath, "w") as f:
|
|
13
|
+
yaml.dump(data, f, Dumper=yaml.SafeDumper)
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def load(filepath: Path) -> Any:
|
|
17
|
+
with open(filepath, "r") as f:
|
|
18
|
+
return yaml.load(f, Loader=yaml.SafeLoader)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import ClassVar, Any, Protocol
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# TODO: maybe use abstract base class instead
|
|
6
|
+
class SnapshotIO(Protocol):
|
|
7
|
+
extension: ClassVar[str]
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def save(data: Any, filepath: Path) -> None: ...
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def load(filepath: Path) -> Any: ...
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from ditto.snapshot import Snapshot
|
|
6
|
+
from ditto import io
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture(scope="function")
|
|
10
|
+
def snapshot(request) -> Snapshot:
|
|
11
|
+
|
|
12
|
+
io_name = None
|
|
13
|
+
parameters = {}
|
|
14
|
+
for mark in request.node.iter_markers(name="record"):
|
|
15
|
+
if mark.args:
|
|
16
|
+
if io_name is not None:
|
|
17
|
+
pytest.fail("Only one 'record' mark is allowed.")
|
|
18
|
+
io_name = mark.args[0]
|
|
19
|
+
|
|
20
|
+
if mark.kwargs:
|
|
21
|
+
parameters.update(mark.kwargs)
|
|
22
|
+
|
|
23
|
+
io_name = io_name if io_name is not None else "pkl"
|
|
24
|
+
|
|
25
|
+
# TODO: do i like this?
|
|
26
|
+
# if io_name is None:
|
|
27
|
+
# pytest.fail("'record' is a required mark when using the 'snapshot' fixture.")
|
|
28
|
+
|
|
29
|
+
path = request.path.parent / ".ditto"
|
|
30
|
+
path.mkdir(exist_ok=True)
|
|
31
|
+
|
|
32
|
+
# Get the snapshot identifier from the 'record' mark parameters (via kwargs) if it
|
|
33
|
+
# exists; otherwise, use the test function name.
|
|
34
|
+
identifier = parameters.get("identifier", request.node.name)
|
|
35
|
+
|
|
36
|
+
return Snapshot(
|
|
37
|
+
path=path,
|
|
38
|
+
name=identifier,
|
|
39
|
+
# record=True,
|
|
40
|
+
io=io.get(io_name, default=io.PickleIO),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def pytest_configure(config):
|
|
45
|
+
# register an additional marker
|
|
46
|
+
config.addinivalue_line("markers", "record(io): snapshot values")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# def pytest_runtest_setup(item):
|
|
50
|
+
# # envnames = [mark.args[0] for mark in item.iter_markers(name="env")]
|
|
51
|
+
# # if envnames:
|
|
52
|
+
# # if item.config.getoption("-E") not in envnames:
|
|
53
|
+
# # pytest.skip("test requires env in {!r}".format(envnames))
|
|
54
|
+
# #
|
|
55
|
+
# for mark in item.iter_markers(name="record"):
|
|
56
|
+
# msg =f"recording: args={mark.args}; kwargs={mark.kwargs}"
|
|
57
|
+
#
|
|
58
|
+
# path = item.path.parent / ".ditto"
|
|
59
|
+
# name = mark.kwargs.get("identifier", mark.name)
|
|
60
|
+
# identifier = mark.kwargs.get("identifier")
|
|
61
|
+
# io_name = next(iter(mark.args))
|
|
62
|
+
#
|
|
63
|
+
# snapshot = Snapshot(
|
|
64
|
+
# path=path,
|
|
65
|
+
# name=name,
|
|
66
|
+
# record=False,
|
|
67
|
+
# io=io.get(io_name, default=io.PickleIO),
|
|
68
|
+
# identifier=identifier,
|
|
69
|
+
# )
|
|
70
|
+
# if not snapshot.filepath(identifier=identifier).exists():
|
|
71
|
+
# pytest.skip(msg)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from ditto import io
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Snapshot:
|
|
9
|
+
|
|
10
|
+
data: Any | None
|
|
11
|
+
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
path: Path,
|
|
15
|
+
name: str,
|
|
16
|
+
record: bool = False,
|
|
17
|
+
io: io.SnapshotIO = io.PickleIO,
|
|
18
|
+
identifier: str | None = None,
|
|
19
|
+
) -> None:
|
|
20
|
+
self.path = path
|
|
21
|
+
self.name = name
|
|
22
|
+
self.record = record
|
|
23
|
+
self.io = io if io is not None else io.default()
|
|
24
|
+
self.identifier = identifier
|
|
25
|
+
self.data = None
|
|
26
|
+
|
|
27
|
+
def filepath(self, identifier: str | None = None) -> Path:
|
|
28
|
+
identifier = identifier if identifier is not None else ""
|
|
29
|
+
identifier = f"{self.name}@{identifier}" if identifier else self.name
|
|
30
|
+
return self.path / f"{identifier}.{self.io.extension}"
|
|
31
|
+
|
|
32
|
+
def _save(self, data: Any, identifier: str | None = None) -> None:
|
|
33
|
+
identifier = identifier if identifier is not None else ""
|
|
34
|
+
self.io.save(data, self.filepath(identifier))
|
|
35
|
+
|
|
36
|
+
def _load(self, identifier: str | None = None) -> Any:
|
|
37
|
+
identifier = identifier if identifier is not None else ""
|
|
38
|
+
return self.io.load(self.filepath(identifier))
|
|
39
|
+
|
|
40
|
+
def __call__(self, data: Any, identifier: str | None = None) -> Any:
|
|
41
|
+
# If the snapshot data exists, and we are not recording, load the data from the
|
|
42
|
+
# snapshot file; otherwise, save the data to the snapshot file.
|
|
43
|
+
|
|
44
|
+
# TODO: At the moment there is no way to re-record snapshots. The approach is to
|
|
45
|
+
# manually delete the snapshot files and re-run the tests. Using another mark,
|
|
46
|
+
# e.g., 'record' might be a good way to do this?
|
|
47
|
+
if self.filepath(identifier).exists():
|
|
48
|
+
self.data = self._load(identifier)
|
|
49
|
+
|
|
50
|
+
else:
|
|
51
|
+
self._save(data, identifier)
|
|
52
|
+
self.data = data
|
|
53
|
+
|
|
54
|
+
_msg = (
|
|
55
|
+
f"\nNo snapshot found: {identifier=}"
|
|
56
|
+
f"\nRecoding new snapshot to {self.filepath(identifier)!r}. "
|
|
57
|
+
"\nRun again to test with recorded snapshot."
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# FIXME: For tests that contain multiple snapshots, when initially recording
|
|
61
|
+
# the snapshot files, this call to pytest.skip results in the test exiting
|
|
62
|
+
# early and the remaining snapshots to remain unsaved. This means we need
|
|
63
|
+
# to run the test N times to get all snapshot files saved, where N is the
|
|
64
|
+
# number of snapshot calls in the test.
|
|
65
|
+
pytest.skip(_msg)
|
|
66
|
+
|
|
67
|
+
return self.data
|
|
68
|
+
|
|
69
|
+
def __repr__(self) -> str:
|
|
70
|
+
return f"{self.__class__.__name__.lower()}({self.data.__repr__()})"
|
|
71
|
+
|
|
72
|
+
def __str__(self) -> str:
|
|
73
|
+
return f"{self.__class__.__name__.lower()}({self.data.__str__()})"
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pytest-ditto"
|
|
3
|
+
dynamic = [
|
|
4
|
+
"version",
|
|
5
|
+
]
|
|
6
|
+
description = ""
|
|
7
|
+
keywords = ["pytest"]
|
|
8
|
+
authors = [{ name = "Lachlan Taylor", email = "lachlanbtaylor@proton.me" }]
|
|
9
|
+
maintainers = [{ name = "Lachlan Taylor", email = "lachlanbtaylor@proton.me" }]
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Framework :: Pytest",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Topic :: Software Development :: Testing",
|
|
19
|
+
"Programming Language :: Python",
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Operating System :: OS Independent",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"pytest>=3.5.0",
|
|
29
|
+
"pyyaml",
|
|
30
|
+
"pandas",
|
|
31
|
+
"pyarrow",
|
|
32
|
+
]
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
dev = [
|
|
35
|
+
"pre-commit",
|
|
36
|
+
"hatch>=1.9.4",
|
|
37
|
+
"hatch-vcs>=0.4.0",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[project.entry-points.pytest11]
|
|
41
|
+
recording = "ditto.plugin"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
[build-system]
|
|
45
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
46
|
+
build-backend = "hatchling.build"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
[tool.hatch.build]
|
|
50
|
+
hooks.vcs.version-file = "ditto/_version.py"
|
|
51
|
+
hooks.vcs.template = "__version__ = {version!r}"
|
|
52
|
+
|
|
53
|
+
[tool.hatch.build.targets.wheel]
|
|
54
|
+
packages = ["ditto"]
|
|
55
|
+
strict-naming = false
|
|
56
|
+
|
|
57
|
+
[tool.hatch.build.targets.sdist]
|
|
58
|
+
packages = ["ditto"]
|
|
59
|
+
strict-naming = false
|
|
60
|
+
|
|
61
|
+
[tool.hatch.version]
|
|
62
|
+
source = "vcs"
|
|
63
|
+
|
|
64
|
+
[tool.hatch.envs.default]
|
|
65
|
+
python = "3.10"
|
|
66
|
+
dependencies = [
|
|
67
|
+
"pytest",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
[tool.hatch.envs.default.scripts]
|
|
71
|
+
test = "pytest {args:tests}"
|
|
72
|
+
|
|
73
|
+
[[tool.hatch.envs.all.matrix]]
|
|
74
|
+
python = ["3.10", "3.11", "3.12"]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
[tool.pytest.ini_options]
|
|
78
|
+
testpaths = "tests"
|
|
79
|
+
filterwarnings = [
|
|
80
|
+
# "error",
|
|
81
|
+
"ignore::UserWarning",
|
|
82
|
+
# note the use of single quote below to denote "raw" strings in TOML
|
|
83
|
+
'ignore::DeprecationWarning',
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
[tool.black]
|
|
88
|
+
line-length = 88
|
|
89
|
+
target-version = ['py38', 'py39', 'py310', 'py311', 'py312']
|
|
90
|
+
exclude = '''
|
|
91
|
+
# A regex preceded with ^/ will apply only to files and directories
|
|
92
|
+
# in the root of the project.
|
|
93
|
+
^/(
|
|
94
|
+
(
|
|
95
|
+
\.eggs
|
|
96
|
+
| \.git
|
|
97
|
+
| \.pytest_cache
|
|
98
|
+
| \.vscode
|
|
99
|
+
| \.mypy_cache
|
|
100
|
+
| __pycache__
|
|
101
|
+
| _cache
|
|
102
|
+
| app_data
|
|
103
|
+
| logs
|
|
104
|
+
| venv
|
|
105
|
+
| build
|
|
106
|
+
| dist
|
|
107
|
+
)/
|
|
108
|
+
)
|
|
109
|
+
'''
|
|
110
|
+
|
|
111
|
+
[tool.coverage.run]
|
|
112
|
+
branch = true
|
|
113
|
+
source = ["ditto"]
|
|
114
|
+
|
|
115
|
+
[tool.coverage.report]
|
|
116
|
+
show_missing = true
|
|
117
|
+
fail_under = 80
|
|
118
|
+
|
|
119
|
+
[tool.ruff]
|
|
120
|
+
line-length = 88 # same as Black.
|
|
121
|
+
target-version = "py310"
|
|
122
|
+
|
|
123
|
+
[tool.ruff.lint]
|
|
124
|
+
# Enable the following rule sets:
|
|
125
|
+
# pycodestyle (`E`) https://docs.astral.sh/ruff/rules/#pyflakes-f
|
|
126
|
+
# Pyflakes (`F`) https://docs.astral.sh/ruff/rules/#pyflakes-f
|
|
127
|
+
# flake8-bugbear (B) https://docs.astral.sh/ruff/rules/#flake8-bugbear-b
|
|
128
|
+
# flake8-simplify (SIM) https://docs.astral.sh/ruff/rules/#flake8-simplify-sim
|
|
129
|
+
# flake8-quotes (Q) https://docs.astral.sh/ruff/rules/#flake8-quotes-q
|
|
130
|
+
select = ["E", "F", "C90", "B", "SIM"]
|
|
131
|
+
extend-select = ["Q"]
|
|
132
|
+
ignore = []
|
|
133
|
+
# F401 - imported but unused
|
|
134
|
+
# F841 - local variable assigned but never used
|
|
135
|
+
# SIM300 - Yoda conditions are discouraged
|
|
136
|
+
extend-ignore = ["F401", "F841", "SIM300"]
|
|
137
|
+
|
|
138
|
+
# Allow autofix for all enabled rules (when `--fix`) is provided.
|
|
139
|
+
fixable = ["A", "B", "C", "D", "E", "F"]
|
|
140
|
+
unfixable = []
|
|
141
|
+
|
|
142
|
+
# Exclude a variety of commonly ignored directories.
|
|
143
|
+
exclude = [
|
|
144
|
+
".bzr",
|
|
145
|
+
".direnv",
|
|
146
|
+
".eggs",
|
|
147
|
+
".git",
|
|
148
|
+
".hg",
|
|
149
|
+
".mypy_cache",
|
|
150
|
+
".nox",
|
|
151
|
+
".pants.d",
|
|
152
|
+
".pytype",
|
|
153
|
+
".ruff_cache",
|
|
154
|
+
".svn",
|
|
155
|
+
".tox",
|
|
156
|
+
".venv",
|
|
157
|
+
"__pypackages__",
|
|
158
|
+
"_build",
|
|
159
|
+
"buck-out",
|
|
160
|
+
"build",
|
|
161
|
+
"dist",
|
|
162
|
+
"node_modules",
|
|
163
|
+
"venv",
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
mccabe.max-complexity = 5
|