Skip to content

Commit 438034c

Browse files
authored
chore: move to Ruff and add rules (pybind#4483)
1 parent a19daea commit 438034c

35 files changed

+266
-279
lines changed

.pre-commit-config.yaml

+6-38
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,6 @@ repos:
3939
- id: requirements-txt-fixer
4040
- id: trailing-whitespace
4141

42-
# Upgrade old Python syntax
43-
- repo: https://github.com/asottile/pyupgrade
44-
rev: "v3.3.1"
45-
hooks:
46-
- id: pyupgrade
47-
args: [--py36-plus]
48-
49-
# Nicely sort includes
50-
- repo: https://github.com/PyCQA/isort
51-
rev: "5.12.0"
52-
hooks:
53-
- id: isort
54-
5542
# Black, the code formatter, natively supports pre-commit
5643
- repo: https://github.com/psf/black
5744
rev: "23.1.0" # Keep in sync with blacken-docs
@@ -72,47 +59,28 @@ repos:
7259
hooks:
7360
- id: remove-tabs
7461

62+
# Avoid directional quotes
7563
- repo: https://github.com/sirosen/texthooks
7664
rev: "0.5.0"
7765
hooks:
7866
- id: fix-ligatures
7967
- id: fix-smartquotes
8068

81-
# Autoremoves unused imports
82-
- repo: https://github.com/hadialqattan/pycln
83-
rev: "v2.1.3"
69+
# Ruff, the Python auto-correcting linter written in Rust
70+
- repo: https://github.com/charliermarsh/ruff-pre-commit
71+
rev: v0.0.251
8472
hooks:
85-
- id: pycln
86-
stages: [manual]
73+
- id: ruff
74+
args: ["--fix", "--show-fixes"]
8775

8876
# Checking for common mistakes
8977
- repo: https://github.com/pre-commit/pygrep-hooks
9078
rev: "v1.10.0"
9179
hooks:
92-
- id: python-check-blanket-noqa
93-
- id: python-check-blanket-type-ignore
94-
- id: python-no-log-warn
95-
- id: python-use-type-annotations
9680
- id: rst-backticks
9781
- id: rst-directive-colons
9882
- id: rst-inline-touching-normal
9983

100-
# Automatically remove noqa that are not used
101-
- repo: https://github.com/asottile/yesqa
102-
rev: "v1.4.0"
103-
hooks:
104-
- id: yesqa
105-
additional_dependencies: &flake8_dependencies
106-
- flake8-bugbear
107-
- pep8-naming
108-
109-
# Flake8 also supports pre-commit natively (same author)
110-
- repo: https://github.com/PyCQA/flake8
111-
rev: "6.0.0"
112-
hooks:
113-
- id: flake8
114-
exclude: ^(docs/.*|tools/.*)$
115-
additional_dependencies: *flake8_dependencies
11684

11785
# PyLint has native support - not always usable, but works for us
11886
- repo: https://github.com/PyCQA/pylint

docs/conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ def prepare(app):
353353
f.write(contents)
354354

355355

356-
def clean_up(app, exception):
356+
def clean_up(app, exception): # noqa: ARG001
357357
(DIR / "readme.rst").unlink()
358358

359359

pybind11/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import sys
22

3-
if sys.version_info < (3, 6):
3+
if sys.version_info < (3, 6): # noqa: UP036
44
msg = "pybind11 does not support Python < 3.6. 2.9 was the last release supporting Python 2.7 and 3.5."
55
raise ImportError(msg)
66

pybind11/commands.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
DIR = os.path.abspath(os.path.dirname(__file__))
44

55

6-
def get_include(user: bool = False) -> str: # pylint: disable=unused-argument
6+
def get_include(user: bool = False) -> str: # noqa: ARG001
77
"""
88
Return the path to the pybind11 include directory. The historical "user"
99
argument is unused, and may be removed.

pybind11/setup_helpers.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ def naive_recompile(obj: str, src: str) -> bool:
341341
return os.stat(obj).st_mtime < os.stat(src).st_mtime
342342

343343

344-
def no_recompile(obg: str, src: str) -> bool: # pylint: disable=unused-argument
344+
def no_recompile(obg: str, src: str) -> bool: # noqa: ARG001
345345
"""
346346
This is the safest but slowest choice (and is the default) - will always
347347
recompile sources.

pyproject.toml

+40-6
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,6 @@ ignore = [
1515
"noxfile.py",
1616
]
1717

18-
[tool.isort]
19-
# Needs the compiled .so modules and env.py from tests
20-
known_first_party = "env,pybind11_cross_module_tests,pybind11_tests,"
21-
# For black compatibility
22-
profile = "black"
23-
2418
[tool.mypy]
2519
files = ["pybind11"]
2620
python_version = "3.6"
@@ -58,4 +52,44 @@ messages_control.disable = [
5852
"invalid-name",
5953
"protected-access",
6054
"missing-module-docstring",
55+
"unused-argument", # covered by Ruff ARG
56+
]
57+
58+
[tool.ruff]
59+
select = [
60+
"E", "F", "W", # flake8
61+
"B", "B904", # flake8-bugbear
62+
"I", # isort
63+
"N", # pep8-naming
64+
"ARG", # flake8-unused-arguments
65+
"C4", # flake8-comprehensions
66+
"EM", # flake8-errmsg
67+
"ICN", # flake8-import-conventions
68+
"ISC", # flake8-implicit-str-concat
69+
"PGH", # pygrep-hooks
70+
"PIE", # flake8-pie
71+
"PL", # pylint
72+
"PT", # flake8-pytest-style
73+
"RET", # flake8-return
74+
"RUF100", # Ruff-specific
75+
"SIM", # flake8-simplify
76+
"UP", # pyupgrade
77+
"YTT", # flake8-2020
6178
]
79+
ignore = [
80+
"PLR", # Design related pylint
81+
"E501", # Line too long (Black is enough)
82+
"PT011", # Too broad with raises in pytest
83+
"PT004", # Fixture that doesn't return needs underscore (no, it is fine)
84+
"SIM118",# iter(x) is not always the same as iter(x.keys())
85+
]
86+
target-version = "py37"
87+
typing-modules = ["scikit_build_core._compat.typing"]
88+
src = ["src"]
89+
unfixable = ["T20"]
90+
exclude = []
91+
line-length = 120
92+
isort.known-first-party = ["env", "pybind11_cross_module_tests", "pybind11_tests"]
93+
94+
[tool.ruff.per-file-ignores]
95+
"tests/**" = ["EM", "N"]

setup.cfg

-8
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,3 @@ project_urls =
4040
[options]
4141
python_requires = >=3.6
4242
zip_safe = False
43-
44-
45-
[flake8]
46-
max-line-length = 120
47-
show_source = True
48-
exclude = .git, __pycache__, build, dist, docs, tools, venv
49-
extend-ignore = E203, E722
50-
extend-select = B902, B904

tests/conftest.py

+17-33
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,8 @@ def __eq__(self, other):
8383
b = _strip_and_dedent(other).splitlines()
8484
if a == b:
8585
return True
86-
else:
87-
self.explanation = _make_explanation(a, b)
88-
return False
86+
self.explanation = _make_explanation(a, b)
87+
return False
8988

9089

9190
class Unordered(Output):
@@ -96,9 +95,8 @@ def __eq__(self, other):
9695
b = _split_and_sort(other)
9796
if a == b:
9897
return True
99-
else:
100-
self.explanation = _make_explanation(a, b)
101-
return False
98+
self.explanation = _make_explanation(a, b)
99+
return False
102100

103101

104102
class Capture:
@@ -119,9 +117,8 @@ def __eq__(self, other):
119117
b = other
120118
if a == b:
121119
return True
122-
else:
123-
self.explanation = a.explanation
124-
return False
120+
self.explanation = a.explanation
121+
return False
125122

126123
def __str__(self):
127124
return self.out
@@ -138,7 +135,7 @@ def stderr(self):
138135
return Output(self.err)
139136

140137

141-
@pytest.fixture
138+
@pytest.fixture()
142139
def capture(capsys):
143140
"""Extended `capsys` with context manager and custom equality operators"""
144141
return Capture(capsys)
@@ -159,25 +156,22 @@ def __eq__(self, other):
159156
b = _strip_and_dedent(other)
160157
if a == b:
161158
return True
162-
else:
163-
self.explanation = _make_explanation(a.splitlines(), b.splitlines())
164-
return False
159+
self.explanation = _make_explanation(a.splitlines(), b.splitlines())
160+
return False
165161

166162

167163
def _sanitize_general(s):
168164
s = s.strip()
169165
s = s.replace("pybind11_tests.", "m.")
170-
s = _long_marker.sub(r"\1", s)
171-
return s
166+
return _long_marker.sub(r"\1", s)
172167

173168

174169
def _sanitize_docstring(thing):
175170
s = thing.__doc__
176-
s = _sanitize_general(s)
177-
return s
171+
return _sanitize_general(s)
178172

179173

180-
@pytest.fixture
174+
@pytest.fixture()
181175
def doc():
182176
"""Sanitize docstrings and add custom failure explanation"""
183177
return SanitizedString(_sanitize_docstring)
@@ -186,30 +180,20 @@ def doc():
186180
def _sanitize_message(thing):
187181
s = str(thing)
188182
s = _sanitize_general(s)
189-
s = _hexadecimal.sub("0", s)
190-
return s
183+
return _hexadecimal.sub("0", s)
191184

192185

193-
@pytest.fixture
186+
@pytest.fixture()
194187
def msg():
195188
"""Sanitize messages and add custom failure explanation"""
196189
return SanitizedString(_sanitize_message)
197190

198191

199-
# noinspection PyUnusedLocal
200-
def pytest_assertrepr_compare(op, left, right):
192+
def pytest_assertrepr_compare(op, left, right): # noqa: ARG001
201193
"""Hook to insert custom failure explanation"""
202194
if hasattr(left, "explanation"):
203195
return left.explanation
204-
205-
206-
@contextlib.contextmanager
207-
def suppress(exception):
208-
"""Suppress the desired exception"""
209-
try:
210-
yield
211-
except exception:
212-
pass
196+
return None
213197

214198

215199
def gc_collect():
@@ -220,7 +204,7 @@ def gc_collect():
220204

221205

222206
def pytest_configure():
223-
pytest.suppress = suppress
207+
pytest.suppress = contextlib.suppress
224208
pytest.gc_collect = gc_collect
225209

226210

tests/env.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,4 @@ def deprecated_call():
2424
pytest_major_minor = (int(pieces[0]), int(pieces[1]))
2525
if pytest_major_minor < (3, 9):
2626
return pytest.warns((DeprecationWarning, PendingDeprecationWarning))
27-
else:
28-
return pytest.deprecated_call()
27+
return pytest.deprecated_call()

tests/test_async.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
m = pytest.importorskip("pybind11_tests.async_module")
55

66

7-
@pytest.fixture
7+
@pytest.fixture()
88
def event_loop():
99
loop = asyncio.new_event_loop()
1010
yield loop
@@ -16,7 +16,7 @@ async def get_await_result(x):
1616

1717

1818
def test_await(event_loop):
19-
assert 5 == event_loop.run_until_complete(get_await_result(m.SupportsAsync()))
19+
assert event_loop.run_until_complete(get_await_result(m.SupportsAsync())) == 5
2020

2121

2222
def test_await_missing(event_loop):

tests/test_buffers.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ def test_to_python():
5454
mat2 = np.array(mat, copy=False)
5555
assert mat2.shape == (5, 4)
5656
assert abs(mat2).sum() == 11
57-
assert mat2[2, 3] == 4 and mat2[3, 2] == 7
57+
assert mat2[2, 3] == 4
58+
assert mat2[3, 2] == 7
5859
mat2[2, 3] = 5
5960
assert mat2[2, 3] == 5
6061

tests/test_builtin_casters.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ def test_bytes_to_string():
126126

127127
assert m.strlen(b"hi") == 2
128128
assert m.string_length(b"world") == 5
129-
assert m.string_length("a\x00b".encode()) == 3
130-
assert m.strlen("a\x00b".encode()) == 1 # C-string limitation
129+
assert m.string_length(b"a\x00b") == 3
130+
assert m.strlen(b"a\x00b") == 1 # C-string limitation
131131

132132
# passing in a utf8 encoded string should work
133133
assert m.string_length("💩".encode()) == 4
@@ -421,13 +421,15 @@ def test_reference_wrapper():
421421
a2 = m.refwrap_list(copy=True)
422422
assert [x.value for x in a1] == [2, 3]
423423
assert [x.value for x in a2] == [2, 3]
424-
assert not a1[0] is a2[0] and not a1[1] is a2[1]
424+
assert a1[0] is not a2[0]
425+
assert a1[1] is not a2[1]
425426

426427
b1 = m.refwrap_list(copy=False)
427428
b2 = m.refwrap_list(copy=False)
428429
assert [x.value for x in b1] == [1, 2]
429430
assert [x.value for x in b2] == [1, 2]
430-
assert b1[0] is b2[0] and b1[1] is b2[1]
431+
assert b1[0] is b2[0]
432+
assert b1[1] is b2[1]
431433

432434
assert m.refwrap_iiw(IncType(5)) == 5
433435
assert m.refwrap_call_iiw(IncType(10), m.refwrap_iiw) == [10, 10, 10, 10]

0 commit comments

Comments
 (0)