-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: move to Ruff and add rules #4483
Conversation
7948ee8
to
f98562b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a review, really ... Looks like a big step forward in almost automatic source code cleanup!
Did you already look at the many CI failures? Is that something we should tackle as a team, divvying up the work?
else: | ||
self.explanation = _make_explanation(a.splitlines(), b.splitlines()) | ||
return False | ||
self.explanation = _make_explanation(a.splitlines(), b.splitlines()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
@@ -54,7 +54,8 @@ def test_to_python(): | |||
mat2 = np.array(mat, copy=False) | |||
assert mat2.shape == (5, 4) | |||
assert abs(mat2).sum() == 11 | |||
assert mat2[2, 3] == 4 and mat2[3, 2] == 7 | |||
assert mat2[2, 3] == 4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing!
6438372
to
e660fb2
Compare
5621b34
to
54e491b
Compare
@@ -72,13 +72,13 @@ def test_iterator_referencing(): | |||
vec = m.VectorNonCopyableIntPair() | |||
vec.append([3, 4]) | |||
vec.append([5, 7]) | |||
assert [int(x) for x in vec.keys()] == [3, 5] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is kind of often wrong. I've had it trigger incorrectly in another package too. Not all objects play nice and have iter(x) == iter(x.keys())
. I wish it was type-aware.
54e491b
to
d36b7d2
Compare
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
d36b7d2
to
be9a604
Compare
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
be9a604
to
2248f65
Compare
Passing now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Passing now.
AWESOME!
I could only glance through right now. I need to find a block of time to comb through more carefully. Asap.
"unused-argument", # covered by Ruff ARG | ||
] | ||
|
||
[tool.ruff] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a ruff doc we could link to from here?
To jump-start finding the answers to: what does this do and why do we need it?
(Only if there is an easy direct link and it makes sense to you.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't think we need to link to everything everywhere, and that URL might change in the future. I added a note in .pre-commit-config.yaml. Ruff is easy to find via Googling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked more carefully now. Overall I'm still amazed, but I'm a worried by the changes ruff made to test_enum.py. Are there more like that elsewhere that I overlooked? I'll try to make another pass through later. I'll also try to get a coverage analysis before and after these changes.
def msg(): | ||
"""Sanitize messages and add custom failure explanation""" | ||
return SanitizedString(_sanitize_message) | ||
|
||
|
||
# noinspection PyUnusedLocal | ||
def pytest_assertrepr_compare(op, left, right): | ||
def pytest_assertrepr_compare(op, left, right): # noqa: ARG001 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tend to use
del op
del right
as a way to document what is (obviously) intentionally unused, rather than using suppressions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer the suppression. It's in the definition, rather than in the implementation, which IMO is better. I shouldn't have to look in the body of the function to be warned that some arguments are ignored. It's also possible these are never called by name in the hook, in which case _op
, _right
would be better.
tests/test_enum.py
Outdated
@@ -66,20 +66,20 @@ def test_unscoped_enum(): | |||
# Unscoped enums will accept ==/!= int comparisons | |||
y = m.UnscopedEnum.ETwo | |||
assert y == 2 | |||
assert 2 == y | |||
assert y == 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All, or maybe almost all, changes in this file undermine the original intent in a critical way.
Is there a way to tell ruff not to touch any of the operator tests in this file?
Generally: the requirements for production code are subtly and critically different from requirements for unit test code. Does ruff have a concept of the two classes (that we could apply here)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you can disable checks per file. This is yoda-conditions from SIM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be useful if I jump in and try? — Asking to make sure we're not both working on it at the same time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can do it, since I'm looking at it now. It's just a simple series of commands:
$ git checkout master -- tests/test_enum.py
$ pre-commit run -a
...
Fixed 19 errors:
- tests/test_enum.py:
10 × SIM201 (negate-equal-op)
7 × SIM300 (yoda-conditions)
2 × SIM202 (negate-not-equal-op)
$ git restore .
Then I add those codes to pyproject.toml.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation!
In pyproject.toml:
"tests/test_enum.py" = ["SIM201", "SIM300", "SIM202"]
For someone externally this will be very difficult to discover. Preferred alternative to make this more obvious (tested):
test_embed.py (e.g. top):
# It is critical that ruff does not rewrite the tests for overloaded operators:
# ruff: noqa SIM201 SIM202 SIM300
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A) that was just added a release or two ago :)
B) I'm not sure that's the right syntax. Did you verify that this is not a global ignore of all codes + some extra comment letters? The docs say ruff: noqa: SIM201 SIM202 SIM300
(note the second colon).
Sure, happy to do it that way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Answer to B: yes, that's exactly what it is. If the colon is missing, that's just a comment after a total noqa
. Hmm, wait a minute, even with the colon, it still seems to be ignoring too much. Will investigate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Answer to B: yes, that's exactly what it is.
Oh ... sorry, I picked up the syntax from some documentation page and apparently was too happy too quickly. Thanks for looking into this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does show a warning, but you need a Ruff new enough to support it, which I didn't have. It's (as of writing) the latest version now.
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
"RET", # flake8-return | ||
"RUF100", # Ruff-specific | ||
"SIM", # flake8-simplify | ||
"UP", # pyupgrade |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we enable keep runtime typing for Pyupgrade?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can disable the pyupgrade code for those per file pattern, which is about the same (better in some ways, worse in others). Won't make a difference until py37 is the minimum, I think.
.pre-commit-config.yaml
Outdated
rev: "v2.1.3" | ||
# Ruff, the Python auto-correcting linter written in Rust | ||
- repo: https://github.com/charliermarsh/ruff-pre-commit | ||
rev: v0.0.247 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This version is already out of date please autoupdate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You also need another arg now to provide a non-zero exit code when fixits are applied.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think pre-commit cares - it can detect changed files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If not, this flag needs to be added to the pre-commit hook under entry
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ran C++ coverage analysis 1. for master as-is, 2. with this PR as-is.
The results are exactly identical (line coverage & branch coverage).
I did that twice.
I also removed a couple tests, to convince myself that there are differences in that case.
I think we're good!
It would be great if we could have the test_enum.py suppressions in the file itself, but if that's not easily possible, I'm fine with this PR as-is.
] | ||
|
||
[tool.ruff] | ||
select = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Newest version of ruff has PEP8-naming rules implemented. We should enable those too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pybind11/setup_helpers.py:274:7: N801 Class name `build_ext` should use CapWords convention
tests/test_exceptions.py:292:7: N818 Exception name `FlakyException` should be named with an Error suffix
tests/test_pytypes.py:18:5: N802 Function name `test_handle_from_move_only_type_with_operator_PyObject` should be lowercase
tests/test_sequences_and_iterators.py:157:11: N818 Exception name `BadLen` should be named with an Error suffix
Found 4 errors.
Not sure it really matters in tests, and the one in the project is wrong. Guess I can just use --add-noqa
and inject these four skips, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, skipped naming rules for tests, and noqa'd the only non-test one.
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
We do have them in-file now. |
Description
Moves the linters over to a single tool - Ruff. This combines a lot of other tools, is 10-100x faster (Rust rewrite), has auto-fix support for some rules, is a single no-dependency binary, and is configured in pyproject.toml. Build, scikit-build-core, Pandas, SciPy, cibuildwheel, FastAPI, and others have switched now too.
Adding a lot more rules since plugins are "free", basically, they don't complicate the environment setup, they are all compiled in.
Ruff technically only supports Python 3.7+, but I think it's fine and we are about to drop 3.6 support too anyway. (Flake8 only supports running on 3.8+ now anyway, too)
Suggested changelog entry: