Skip to content

Python lint: Use ruff instead of flake8. NFC #23085

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

Merged
merged 1 commit into from
Dec 9, 2024

Conversation

cclauss
Copy link
Contributor

@cclauss cclauss commented Dec 5, 2024

https://docs.astral.sh/ruff is an extremely fast Python linter and code formatter, written in Rust. With over 800 linting rules, ruff can be used to replace Flake8 (plus dozens of plugins), Black, isort, pydocstyle, pyupgrade, autoflake, and more, all while executing tens or hundreds of times faster than any individual tool.

Set some upper limits on code complexity using ruff rules for McCabe cyclomatic complexity and Pylint.

% git grep flake8
% flake8-to-ruff .flake8 > pyproject.toml
% validate-pyproject pyproject.toml
% pyproject-fmt pyproject.toml
% ruff check


The Required ci/circleci: flake8 job was renamed to ci/circleci: ruff so the Required label should be removed from the old job and added to the new one.


% ruff rule C901

complex-structure (C901)

Derived from the mccabe linter.

What it does

Checks for functions with a high McCabe complexity.

Why is this bad?

The McCabe complexity of a function is a measure of the complexity of
the control flow graph of the function. It is calculated by adding
one to the number of decision points in the function. A decision
point is a place in the code where the program has a choice of two
or more paths to follow.

Functions with a high complexity are hard to understand and maintain.

Example

def foo(a, b, c):
    if a:
        if b:
            if c:
                return 1
            else:
                return 2
        else:
            return 3
    else:
        return 4

Use instead:

def foo(a, b, c):
    if not a:
        return 4
    if not b:
        return 3
    if not c:
        return 2
    return 1

Options

  • lint.mccabe.max-complexity

Copy link
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like ruff is indeed an improvement over flake8!

Is there any stuff that flake8 does that ruff does now catch?

test/jsrun.py Outdated
@@ -106,7 +106,8 @@ def run_js(filename, engine, args=None,
stderr=stderr,
cwd=cwd,
timeout=timeout,
universal_newlines=True)
universal_newlines=True,
check=skip_check)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like maybe a semantic change.. did ruff make this?

Copy link
Contributor Author

@cclauss cclauss Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ruff rule PLW1510 will set check=False which ignores failures as recently discussed at https://github.com/pyodide/pyodide-build/pull/66/files#r1861265823 We can set check=False if we always want to ignore errors.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we simply revert this?

@@ -368,13 +368,13 @@ def emscript(in_wasm, out_wasm, outfile_js, js_syms, finalize=True, base_metadat
# check_call doesn't support the `input` argument.
if asm_consts:
validate = '\n'.join([f'var tmp = {f};' for _, f in asm_consts])
proc = subprocess.run(config.NODE_JS + ['--check', '-'], input=validate.encode('utf-8'))
proc = subprocess.run(config.NODE_JS + ['--check', '-'], input=validate.encode('utf-8'), check=False)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this? check is false by default isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default is to ignore errors.

ruff rule PLW1510

Why is this bad?

By default, subprocess.run does not check the return code of the process
it runs. This can lead to silent failures.

Instead, consider using check=True to raise an exception if the process
fails, or set check=False explicitly to mark the behavior as intentional.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just leaving it out is fine/clear.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we revert revert this hunk.

@sbc100
Copy link
Collaborator

sbc100 commented Dec 5, 2024

Can we stick with .ruff.toml for now (i.e. a specific config file that is clearly for ruff alone) (at least until we have some other reason to have project.toml)

@cclauss
Copy link
Contributor Author

cclauss commented Dec 5, 2024

Is there any stuff that flake8 does that ruff does not catch?

https://docs.astral.sh/ruff/faq/#how-does-ruffs-linter-compare-to-flake8

Can we stick with .ruff.toml for now.

I would have migrated .coveragerc, .mypy.ini and requirements-dev.txt into pyproject.toml in this PR but I know that too many unrelated changes slows the code review process.

@sbc100
Copy link
Collaborator

sbc100 commented Dec 5, 2024

Lets just go with .ruff.toml initially maybe?

@cclauss cclauss force-pushed the ruff branch 4 times, most recently from b43b435 to 71f966f Compare December 7, 2024 09:28
@cclauss cclauss requested a review from sbc100 December 8, 2024 12:04
Copy link
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm % comments

@cclauss
Copy link
Contributor Author

cclauss commented Dec 9, 2024

  • ruff check --ignore=PLW1510
  • To revalidate these numbers, run ruff check --select=C901,PLR091.

Below, the Required ci/circleci: flake8 job was renamed to ci/circleci: ruff so the Required label should be removed from the old job and added to the new one.

@sbc100 sbc100 enabled auto-merge (squash) December 9, 2024 08:03
@sbc100 sbc100 changed the title Python lint: Use ruff instead of flake8 Python lint: Use ruff instead of flake8. NFC Dec 9, 2024
@cclauss
Copy link
Contributor Author

cclauss commented Dec 9, 2024

This PR will not auto-merge because the Required ci/circleci: flake8 job no longer exists.

@sbc100 sbc100 disabled auto-merge December 9, 2024 16:42
@sbc100 sbc100 merged commit 19345a7 into emscripten-core:main Dec 9, 2024
28 checks passed
@cclauss cclauss deleted the ruff branch December 9, 2024 16:42
@sbc100
Copy link
Collaborator

sbc100 commented Dec 13, 2024

@cclauss it looks like lost the check for the correct number of newlines between methods and functions: https://github.com/emscripten-core/emscripten/pull/23139/files#r1883095264

@cclauss
Copy link
Contributor Author

cclauss commented Dec 13, 2024

hedwigz pushed a commit to hedwigz/emscripten that referenced this pull request Dec 18, 2024
https://docs.astral.sh/ruff is an extremely fast Python linter and code
formatter, written in Rust. With over 800 linting rules, ruff can be
used to replace [Flake8](https://pypi.org/project/flake8/) (plus dozens
of plugins), [Black](https://github.com/psf/black),
[isort](https://pypi.org/project/isort/),
[pydocstyle](https://pypi.org/project/pydocstyle/),
[pyupgrade](https://pypi.org/project/pyupgrade/),
[autoflake](https://pypi.org/project/autoflake/), and more, all while
executing tens or hundreds of times faster than any individual tool.
@sbc100
Copy link
Collaborator

sbc100 commented Jan 7, 2025

It looks like we also regressed on the ability to detect trailing whitespace in function calls:

$ cat test.py 
print(1, 2 )
$ ruff check test.py
All checks passed!
$ flake8 test.py
test.py:1:11: E202 whitespace before ')'

Is this just something we need to enable?

@sbc100
Copy link
Collaborator

sbc100 commented Jan 7, 2025

I guess E202 is in preview too: https://docs.astral.sh/ruff/rules/whitespace-before-close-bracket/. Somewhat worrying that such simple checks are in preview.. and that we have to enable them explicitly on the command line.

@cclauss
Copy link
Contributor Author

cclauss commented Jan 7, 2025

This is solved in #23154.

% echo "print(1, 2 )" | pipx run ruff format -

print(1, 2)

Flake8 was created before great code formatters were available but Ruff was not.
I don't think the Ruff Team puts too many cycles into rules that are better solved by black or ruff format.

@sbc100
Copy link
Collaborator

sbc100 commented Jan 7, 2025

It would have been good to know that before we made the transition to ruff.

sbc100 added a commit to sbc100/emscripten that referenced this pull request Mar 14, 2025
Turns out ruff still doesn't support several flake8 warnings.  I've
enabled some more that are in preview-only mode.

See emscripten-core#23085
sbc100 added a commit that referenced this pull request Mar 17, 2025
Turns out ruff still doesn't support several flake8 warnings. I've
enabled some more that are in preview-only mode.

See #23085
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants