GithubHelp home page GithubHelp logo

astral-sh / ruff Goto Github PK

View Code? Open in Web Editor NEW
26.9K 74.0 857.0 45.1 MB

An extremely fast Python linter and code formatter, written in Rust.

Home Page: https://docs.astral.sh/ruff

License: MIT License

Rust 96.70% Python 2.21% HTML 0.02% TypeScript 0.82% CSS 0.02% JavaScript 0.02% Shell 0.20% Dockerfile 0.01%
linter pep8 python python3 rust rustpython static-analysis static-code-analysis style-guide styleguide

ruff's Issues

Handle multiple unused submodule imports

pylint does this properly, Flake8 does not.

For example, with this:

import multiprocessing.pool
import multiprocessing.process

Flake8 will only mark the second import as unused.

`F401 imported but unused` when symbol is only used for type hints

Hi Charlie,

maybe similar to #83, ruff also marks spots as "imported but unused", where the imported symbols are only used within type hints.

With kind regards,
Andreas.

Example

import dataclasses
from datetime import datetime


@dataclasses.dataclass
class Message:
    message: str
    datetime: datetime

Refactor AST traversal to support batched visitors

Right now, we're able to get away with a single visitor implementation that implements all rules. We'll need to evolve this to something closer to Fixit's model: multiple visitors that deal with isolated concerns, and can be batched and run in a single traversal.

Erroneous F821 on classes with parameter type annotations

If I run ruff on a file containing a class with a type annotation at class level, it incorrectly gives F821 (undefined name).

Examples:

from dataclasses import dataclass


@dataclass
class Bad:
    x: int


@dataclass
class AlsoBad:
    x: int = 3


class StillBad:
    x: int

    def __init__(self):
        self.x = 3


class Ok:
    x = 3

Running ruff <filename> on this file gives:

ruff_test.py:6:5: F821 Undefined name `x`
ruff_test.py:11:5: F821 Undefined name `x`
ruff_test.py:15:5: F821 Undefined name `x`

(flake8 gives no errors)

Ruff version 0.0.28, python 3.8.13, ubuntu 22.04.1.

`F821 Undefined name` when definition is after usage

Ruff looks great, congratulations. I'd love to use it one day on pydantic instead of flake8.


The following code is valid python and runs fine

def main():
    foo()

def foo():
    print('this is foo')

main()

However ruff returns:

test.py:2:5: F821 Undefined name `foo`

Found 1 error(s).

Running ruff on pydantic's code base is currently returning 151 F821 errors, so it's a fairly common problem.

`F821` on use of `Literal` with strings, e.g. `Literal['foo']`

Since #119 was fixed via #125, I tried ruff again (build from main) with pydantic.

I get a few remaining F821 errors due to use of strings in Literal, the line numbers are also incorrect - seems to always be 1.

From this code (and some other places in that file), I get:

pydantic/config.py:1:1: F821 Undefined name `deep`
pydantic/config.py:1:1: F821 Undefined name `none`
pydantic/config.py:1:1: F821 Undefined name `deep`
pydantic/config.py:1:1: F821 Undefined name `shallow`
pydantic/config.py:1:1: F821 Undefined name `before_validation`
pydantic/config.py:1:1: F821 Undefined name `after_validation`
pydantic/config.py:1:1: F821 Undefined name `none`
pydantic/config.py:1:1: F821 Undefined name `shallow`
pydantic/config.py:1:1: F821 Undefined name `before_validation`
pydantic/config.py:1:1: F821 Undefined name `after_validation`

Example code:

    copy_on_model_validation: Literal['none', 'deep', 'shallow']
    post_init_call: Literal['before_validation', 'after_validation']

Implement AST-to-source code generation

Not critical but this could be somewhat useful for autofixing. Similar to astor. The main issue with AST-to-code generation is that it's impossible to preserve formatting, including comments and docstrings, since those aren't captured in the AST. That's a big limitation, and means that you can really only use this for reconstructing small subtrees.

Support for E712 and others

Dear Charlie,

thanks a stack for conceiving this excellent program. While working on modernizing an outdated code base [1,2], we started using ruff just recently, and are amazed by its speed. Currently, we would never look back.

As we got the chance to work on this code base, originally from Python 2, but now already ported to Python 3 and polished off with ruff and black, we thought it would be a good idea to report back what flake8 would still observe on it.

The error codes are: E203, E711, E712, E713, E741, F841, where E712 was the most popular, followed by its neighbors E711 and E713, as well as F841.

With kind regards,
Andreas.

[1] https://github.com/isarengineering/SecPi/tree/next
[2] SecPi/SecPi#120

Make throwaway variable (`_`) pattern configurable

There are cases where _ cannot be used to mark a throwaway variable and it would be great if ruff allowed configuring it. This is common in projects that use gettext for example, as _ is used for translating strings there. One of the ways unused variables are marked in such projects is using __ (double underscore) but it probably would be a good idea to just make it configurable with a sane default to appeal to more uses.

Notably, pylint allows configuring the dummy variable pattern with a regex:
https://pylint.pycqa.org/en/latest/user_guide/configuration/all-options.html#dummy-variables-rgx

ruff incorrectly flags imports included in __all__

Steps to reproduce

  1. Create two files, __init__.py and blah.py.

blah.py

class MyBlahClass:
    pass

__init__.py

from blah import MyBlahClass

__all__ = ["MyBlahClass"]
  1. Run ruff

Expected results
2. ruff should not have an issue with the import in MyBlahClass as it's included in __all__.

Actual results
2. ruff reports a F401, class imported but unused

ruff output:

ruff . -v  
[2022-09-01][15:17:28][ruff][DEBUG] Identified files to lint in: 25.778µs
[2022-09-01][15:17:28][ruff::linter][DEBUG] Cache hit for: ./blah.py
[2022-09-01][15:17:28][ruff::linter][DEBUG] Cache hit for: ./__init__.py
[2022-09-01][15:17:28][ruff][DEBUG] Checked files in: 588.674µs
Found 1 error(s).

./__init__.py:1:1: F401 `blah.MyBlahClass` imported but unused

flake8 output:

flake8 . -v
flake8.checker            MainProcess     53 INFO     Making checkers
flake8.checker            MainProcess     54 INFO     Checking 2 files
flake8.main.application   MainProcess     71 INFO     Finished running
flake8.main.application   MainProcess     71 INFO     Reporting errors
flake8.main.application   MainProcess     71 INFO     Found a total of 0 violations and reported 0

Settings: Support the `ignore` option also within `pyproject.toml`

Hi again,

after running into #119 as well, we tried to ignore F821 by adding it to the pyproject.toml file within the [tool.ruff] section, like:

[tool.ruff]
ignore = ["F821"]

However, ruff complained with:

Failed to load pyproject.toml: unknown field `ignore`, expected one of `line-length`, `exclude`, `select` for key `tool.ruff` at line 199 column 1

It looks like the option, although being implemented within settings.rs, is currently only available as a command line option, to be used like ruff --ignore=F821 .. I think it would be sweet to be en par with flake8 in this case, because ignore will probably be a popular option.

With kind regards,
Andreas.

Crash on parenthesized with statement

I tried running ruff on a large project, which resulted in this crash:

[2022-08-31][10:33:40][ruff][ERROR] Failed to check repr-with.py: invalid syntax. Got unexpected token 'as' at line 9 column 13

It seems like the crash happens on parenthesized context managers introduced in Python 3.10. Minimal reproducible example:

from contextlib import contextmanager

@contextmanager
def ctx():
    yield

with (ctx() as foo):
    ...

Results that do not meet expectations

main.py

from flask import Flask

def make_app():
    app = Flask(__name__)
    return app

app = make_app()

Results:

$ ruff main.py
Found 1 error(s).

main.py:7:7: F821 Undefined name `make_app`
$ flake8 main.py
main.py:3:1: E302 expected 2 blank lines, found 1
main.py:7:1: E305 expected 2 blank lines after class or function definition, found 1

No obvious way to use a glob or regexp pattern in `exclude`

With many Django apps in one project, one usually has a lot of auto-generated DB migrations, each under its own app directory. It looks like so:

apps/foo/migrations/...
apps/bar/migrations/...

It would be great to be able to match them in one exclude pattern, like apps/*/migrations, or, **/migrations/, or even just /migrations/.

None of these appears to work right now; the only to list every app explicitly.

Implement autofix

What's the right strategy for autofixing? Should we modify the AST? Should we use the generated rules, and modify via regex? (This is what autoflake does, and there's a start on implementing autofix for F401 in #34.)

Providing a directory as an argument causes panic

RUST_BACKTRACE=full ruff celery
thread '<unnamed>' panicked at 'removal index (is 20) should be < len (is 20)', src/check_lines.rs:35:16
stack backtrace:
   0:        0x10ca52807 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h85521558a183f368
   1:        0x10c876a4b - core::fmt::write::h01631fae0d2b98bc
   2:        0x10ca4d8bc - std::io::stdio::_print::h3f48a1f06c5dc1b5
   3:        0x10ca56db0 - std::panicking::default_hook::h18647b59f1a84ee2
   4:        0x10ca56acc - std::panicking::default_hook::h18647b59f1a84ee2
   5:        0x10ca57368 - std::panicking::rust_panic_with_hook::hd9ead35a68ccc55e
   6:        0x10ca572a4 - <std::panicking::begin_panic_handler::StrPanicPayload as core::panic::BoxMeUp>::get::h4eaac086dd1f05c5
   7:        0x10ca55da7 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h85521558a183f368
   8:        0x10ca56fc0 - _rust_begin_unwind
   9:        0x10ca7f683 - core::panicking::panic_fmt::h3d9f795ee387ef8d
  10:        0x10ca7b176 - alloc::vec::Vec<T,A>::remove::assert_failed::h110daa08131a2fb6
  11:        0x10c9232a8 - ruff::linter::check_path::he0f23175143f4a67
  12:        0x10c938891 - ruff::settings::Settings::ignore::h00b9e252c6691a0f
  13:        0x10c936a43 - ruff::settings::Settings::ignore::h00b9e252c6691a0f
  14:        0x10c92a320 - ruff::settings::Settings::ignore::h00b9e252c6691a0f
  15:        0x10c936ddf - ruff::settings::Settings::ignore::h00b9e252c6691a0f
  16:        0x10c92a3ff - ruff::settings::Settings::ignore::h00b9e252c6691a0f
  17:        0x10c936ddf - ruff::settings::Settings::ignore::h00b9e252c6691a0f
  18:        0x10c92a3ff - ruff::settings::Settings::ignore::h00b9e252c6691a0f
  19:        0x10c936ddf - ruff::settings::Settings::ignore::h00b9e252c6691a0f
  20:        0x10c92a320 - ruff::settings::Settings::ignore::h00b9e252c6691a0f
  21:        0x10c936ddf - ruff::settings::Settings::ignore::h00b9e252c6691a0f
  22:        0x10c937693 - ruff::settings::Settings::ignore::h00b9e252c6691a0f
  23:        0x10ca82306 - rayon_core::registry::WorkerThread::wait_until_cold::h6c4b4c23a2ed2f5c
  24:        0x10c92a4d2 - ruff::settings::Settings::ignore::h00b9e252c6691a0f
  25:        0x10c936ddf - ruff::settings::Settings::ignore::h00b9e252c6691a0f
  26:        0x10c937693 - ruff::settings::Settings::ignore::h00b9e252c6691a0f
  27:        0x10ca82306 - rayon_core::registry::WorkerThread::wait_until_cold::h6c4b4c23a2ed2f5c
  28:        0x10c8acdf0 - rayon_core::registry::ThreadBuilder::run::ha91e61928ff63c4e
  29:        0x10c8a9e72 - _rust_eh_personality
  30:        0x10c8ab17f - _rust_eh_personality
  31:        0x10ca5a8f9 - std::sys::unix::thread::Thread::new::h2ee6b50b075520a6
  32:     0x7ff80e4b94e1 - __pthread_start
zsh: abort      RUST_BACKTRACE=full ruff celery

`F401` when type used in annotation

Kind of the opposite to #128.

E.g. NamedTuple is imported here, and used here.

But shows up in errors:

pydantic/annotated_types.py:2:1: F401 `typing.NamedTuple` imported but unused

There are currently 30 of these errors from running ruff on pydantic.

As with #128, I've build ruff from main (a8f4faa).

Crash if no pyproject.toml file is found

Hey!

Great project! I just found a bug that occurs if ruff cannot find a pyproject.toml file. It will assume that
there is a pyproject.toml file at $HOME/.ruff. When trying to parse it, the program crashes (silently) with a file not found error.

To reproduce:

Try running ruff in a location where it cannot find a pyproject.tomlfile at all.

I already forked and fixed the bug :)

Comparison with flake8?

As mentioned in #119, ruff looks great and I'd love to adopt it in pydantic, however I'd like to have a better understanding of what I loose by switching from flake8 to ruff?

I've run ruff on pydantic and apart from #119, ruff has found a few legitimate things that I would either fix or ignore. The problem is what checks am I no longer getting?

This is harder to find without either a lot of trial or waiting to get bitten.

I would therefore find it really helpful if there was a good comparison of ruff and flake8. I'm sure other potential users would also find this helpful.

F401 `__future__.annotations` imported but unused

ruff marks from __future__ import annotations imports as unused. They are not supposed to be used, and so shouldn't be reported.

models.py:1:1: F401 `__future__.annotations` imported but unused

Consider using `libcst`

https://github.com/Instagram/LibCST

It is partially written in Rust and allows you to have way more over the syntax tree.
It will allow code rewrite and will allow more syntax checks.

I highly recommend trying this before the project will have a lot of exisiting source code.

`Invalid syntax. Got unexpected token 'as'` on multiple `... as ...` inside a with statement

Found this bug while testing ruff on my code, here is a simplified example to reproduce:

main.py:

with (
    open("test", "r") as f,
    open("test2", "r") as f2,
):
    print("hello")

ruff output:

[2022-09-01][14:00:00][ruff::pyproject][DEBUG] Found pyproject.toml at: pyproject.toml
[2022-09-01][14:00:00][ruff][DEBUG] Identified files to lint in: 3.25µs
[2022-09-01][14:00:00][ruff][ERROR] Failed to check main.py: invalid syntax. Got unexpected token 'as' at line 2 column 23
[2022-09-01][14:00:00][ruff][DEBUG] Checked files in: 174.625µs
Found 0 error(s).

pre-commit hook does not consider configuration in pyproject.toml.

I have tested pre-commit hook and it works fine but when I tried to implement a new configuration in pyproject.toml, I realized it does not consider the configuration.

[tool.ruff]
line-length = 200
ignore = ['E501', 'F841']

I still get the error E501 even though I have ignored it.
Screenshot from 2022-09-09 23-50-25

Lint a directory recursively

It'd be useful to have a way to lint all Python files in a directory and its subdirectories using a CLI option.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.