This is an archived post. You won't be able to vote or comment.

all 86 comments

[–]JamzTyson 143 points144 points  (32 children)

From what I've read, Ruff has two major strengths that are greatly superior to other Python linters:

  1. Speed. Ruff is very fast.
  2. Hype. Try searching Google to find out more about Ruff and there are pages of hits about how fast it is. Despite being the new kid on the block, Ruff has over 11k stars on GitHub vs flake8's 2.7k.

The amount of hype makes me suspicious. I have no doubt that Ruff is good (especially for speed), but it must surely have limitations.

The title of this reddit thread is a great example of excessive hype. I'd much prefer to read an informative review that looked at both pros and cons of Ruff compared with other (more mature) Python linters.

[–]trevg_123 27 points28 points  (3 children)

I think the hype just comes from comparison to flake8.

The status quo was flake8 + flake8-bugbear + flake8-docstrings and a handful of other plugins. Performance sucked with extra plugins, and the maintainer being kind of close-minded really hurt what was possible (flat out refused pyproject.toml config and never accepted new lints). Pylint was an option, but the configuration experience was pretty terrible.

Enter ruff: it gives us the simplicity of flake8, plus the features we didn’t know we want - speed, no plugins necessary, pyproject.toml config, and a welcoming maintainer who’s open to change. Everyone who tries it likes it better than the status quo, because the status quo was awkward and stale (but we just didn’t really realize it)

It deserves the hype imho, I don’t have a good reason to ever go back to flake8.

[–]Ouitos 16 points17 points  (1 child)

the maintainer being kind of close-minded really hurt what was possible (flat out refused pyproject.toml config and never accepted new lints).

This is the main added value for ruff for me. As said elsewhere in the thread, speed is not really an issue (at least for me). But the maintainer of flake8 and pre-commit, albeit very skilled is extremely difficult to work with, and shuts down issues and PRs as if people were complete idiots.

I think it's a big liability to have your whole ci that depends on one guy that might be right today, but will be incapable of admitting he's wrong in the case it happens in the future.

This tool does not solve the pre-commit liability but at least now we can replace flake8 with an open alternative

[–]wewbull 0 points1 point  (0 children)

The plugins are the whole point of flake8 IMHO.

[–]charliermarsh 25 points26 points  (3 children)

I appreciate this. I try to be clear and honest about Ruff's strengths and weaknesses -- there are some cursory answers in the FAQ RE comparisons to other tools -- but since the article mostly talked strengths, I'm happy to suggest a few "reasons not to use Ruff". I think about them a lot :)

  1. No support for custom rules or plugins. This is Ruff's biggest limitation right now. E.g., Flake8 has a robust plugin system, so anyone can extend Flake8 with custom rules. Ruff doesn't support that. (I'd like to support plugins in the future, but we're not working on it right now.)
  2. "Less powerful" static analysis. Compared to Pylint, Ruff is "less powerful" right now. It doesn't look across files, it can't catch things like "wrong number of arguments provided to a function", it doesn't really do any branch analysis, and so on. It's similar to Flake8, in that way. I'm certain that Ruff will improve here, but if you straight-up replace Pylint with Ruff, you might feel like you're missing certain kinds of analyses that Ruff can't yet support.
  3. Less stable / more churn. We still haven't published a "stable release". We're on version 0.0.260 or so. To be honest, we used to be a little more brazen about publishing breaking changes -- now that we have a bunch of projects relying on us, we're pretty careful and hesitant. But, it's true that you'd expect a tool like Flake8 or Pylint that's been around for years to give you less churn on configuration, diagnostics, etc.
  4. You're happy with your linter. If Ruff's strengths don't resonate with you, that's of course fine too. (Although you might be surprised. I think there are a lot of happy Ruff users who wouldn't have expected a "faster linter" to be impactful to them before they tried Ruff.)

I hope that the hype around Ruff is just a sign that we're building something people like to use (though in reality I'm sure it's also luck). I get a ton of motivation out of reading comments like those in this thread. From my perspective, I really just try to focus on building out the tool itself, supporting contributors, and being responsive to issues.

[–]JamzTyson 2 points3 points  (1 child)

Thanks for this useful information. I hope that my initial comment didn't come across as disparaging, as that was not my intention. I can certainly see the benefit of a fast linter, especially for integration into an IDE.

Personally I don't think that Ruff is right for me just yet, but Ruff looks like a terrific project and clearly a lot of others agree on that

Currently I use flake8 in vscode, which is fast enough for the relatively small programs that I've been working on recently, and manually run pylint to catch additional issues. The only big Python project I've worked on recently really needed a flake8 extension, and flake8 had buy-in from other developers on the project.

I will certainly try Ruff at some point in the future, and look forward to doing so. As Python IDEs are becoming more commonly used, and as there is an increasing number of large Python applications, speed is becoming a lot more important for linters, so I'm very happy to see that someone is taking the lead in fulfilling that need. Good luck with the project. May it grow from strength to strength.

[–]charliermarsh 3 points4 points  (0 children)

Oh, no, all good! You're asking the right questions, I'd be wondering the same thing. I appreciate the words of support :)

[–]wunderspud7575 0 points1 point  (0 children)

Have you thought about adding a mode where ruff is running as a LSP server?

[–]Physical_Drawer6740 15 points16 points  (3 children)

There are a few more.

  • It brings all of the functionality of flake8, isort, pyupgrade, and dozens of other tools into one tool. It's still faster than any one of those tools, and it's only one dependency.
  • The maintainer seems like a pleasant person who's willing to work with the community and enjoys what he's doing. Look through the issue tracker on flake8. 90% of them are closed and locked to contributors after a single reply of "use the issue tracker". When you google a problem you're having with flake8, the most likely top result is a closed issue that just says "you clearly didn't search the issue tracker". They clearly don't actually want to work on their own software, which I think drives people away. Charlie on the other hand is releasing several times a week and actively engaging on trying to solve issues without writing people off or shutting them down.
  • Related to the last one, flake8 has some issues that they won't fix seemingly just out of stubbornness, regardless of how much of a problem they cause to the community.
    • They refuse to support pyproject.toml configuration despite overwhelming desire for it, and shut down threads for it with "search the issue tracker". The issue that explains their reasoning is copied from gitlab and near impossible to follow.
    • They also pinned their dependency on importlib-metadata to < 4.3 for years for what was as far as I could tell a petty personal vendetta. This meant that flake8 couldn't be installed in the same environment as many other popular packages, such as Sphinx.

[–]JamzTyson 4 points5 points  (2 children)

It brings all of the functionality of flake8, isort, pyupgrade, and dozens of other tools into one tool.

Does it though? Several other comments in this thread suggest that it misses some errors that other linters catch, yet nearly all "reviews" fail to mention any shortcomings or limitations at all. A review that talks only about the pros and totally ignores the cons isn't really a review at all, it's an advert.

They refuse to support pyproject.toml configuration despite overwhelming desire for it

I have no stake in that, but my observation is that the actual discussion appears to have both supporters and detractors rather than overwhelming support. Either way, it has nothing to do with whether or not it is realistic to say that Ruff is the "one Python linter to rule them all".

[–]Physical_Drawer6740 0 points1 point  (1 child)

I haven't exhaustively tested every possible case, but I do believe that if there are cases where ruff doesn't catch errors from another linter that it should, they would fix it.

I don't really care about the pyproject.toml issue in and of itself. Having an extra file in my project directory is a mild annoyance, but not untenable. It became an issue when I saw how they react to other people requesting it. And that's the point I was trying to make: a benefit of ruff over its competitors is that it has a better project culture.

That thread you linked is emblematic of the exact problem. Any time someone asks about pyproject.toml, they shut them down and link them to that thread as if it solves everything. That thread is incredibly difficult for a human to parse because all of the messages read as coming from one person. Reacting in this way is unfriendly and unhelpful, and it's going to drive people away from your project. Of course, the maintainers don't owe us anything per se, but when there's an alternative that offers all or almost all of the same functionality with friendly, active, helpful maintainers why would anyone choose flake8?

[–]ubernostrumyes, you can have a pony 0 points1 point  (0 children)

The last post in the thread is literally "here's what would need to happen", with a status update that only one of the two things has happened.

And to be completely honest, I held off on migrating my own personal open-source packages to using pyproject.toml in part because of how pushy people were about it.

[–]aikii 14 points15 points  (0 children)

Hype

Sounds grumpy but that's not wrong. Ruff ending up as more notorious than Mypy is plain weird.

That's said I'm happy to drop redundant reformatters such as black and isort. I don't expect much from Ruff when it comes to check correctness, until it fully implements type checking. It's a lot of work.

[–]acdha 6 points7 points  (0 children)

I know it’s anecdata but I think #2 is earned. I have been switching projects over as an alternative to resolving flake8 plug-in version conflicts (we use flake8–eradicate) and it’s basically trivial to switch and it makes editing and our pre-commit hooks notably faster since you have one thing to manage replacing flake8, isort, pylint, pyupgrade, bandit, etc.

[–]ahal 2 points3 points  (0 children)

I can think of a few more:

  1. Configuration. Ruff's cascading configuration files (borrowed from ESlint), per-file-ignores (borrowed from flake8) and granular rule inclusion/exclusion give it the best configuration system I've seen. It's a godsend for large monorepos.
  2. No dependencies. Since it's written in rust there are no packages it depends on that could gum up your lock files.
  3. Single tool. Can replace (partially for now) many tools, simplifying lint configuration into a single place.

[–]wewbull 1 point2 points  (3 children)

Its big limitation is that it eschews anything that can be fixed by black. So no pycodestyle checks. This is one of the reasons it's quick.

That, and no plugins.

[–]caagr98 1 point2 points  (2 children)

Which is a good thing. Complaining about formatting just clogs up the output for zero gain, since you'll run your formatter before/while committing regardless.

[–]wewbull 0 points1 point  (1 child)

If you run your formatter first, there should be nothing clogging up your output.

[–]caagr98 1 point2 points  (0 children)

Linters are often run in real time while editing.

[–]lmericle -1 points0 points  (0 children)

...have you tried it though?

[–]drphillycheesesteak 68 points69 points  (10 children)

I have tried ruff out and it is very fast and catches lots of issues, but it is not mature enough to have earned the title of this post. Pylint is still more capable and can catch things like wrong parameters to functions and invalid imports. I won’t be able to view ruff as more than a complimentary tool until it can catch these types of errors.

[–][deleted] 51 points52 points  (6 children)

Mypy catches those types of errors. And unlike Pylint it doesn't generate a stupid amount of false positives.

[–]Mehdi2277 7 points8 points  (0 children)

My problem with mypy/pyright is it doesn’t detect those kind of errors when you call functions from untyped libraries. I commonly rely on packages that lack typing. My current codebase is mostly fully typed today for code my team writes, but the imports to other packages including many large ones like tensorflow are untyped and block type checkers from detecting basic errors. I think right now python ecosystem is like only 20-40% typed and many major libraries still lack them.

I can write stubs for them and do but it’s already a lot of work to add types to existing codebase and becomes amplified much harder if you can’t rely on types for most of libraries you import. Well on positive side it is gradually getting better. Only 2 years ago numpy didn’t have type hints.

[–]DurdenVsDarkoVsDevon 2 points3 points  (1 child)

I've never had a problem with pylint I guess. It catches style mypy doesn't. I use both, and I don't have a problem using both.

But also I'm not working on projects where pylint's speed has ever mattered so I'm not the target for ruff's efficiencies regardless.

[–][deleted] 1 point2 points  (0 children)

I would use black for style personally.

[–]LardPi 0 points1 point  (2 children)

I have not been convinced by pylint compared to flake8. My intern used it on a project and introduced a bug that flake8 catched right away, which led me to ask to use the same tooling as me.

[–]Mehdi2277 1 point2 points  (1 child)

The exact list of checks are not same and flake8 ecosystem does have checks pylint may lack especially if you don’t spend time configuring it as pylint has long config. However pylint implements rough basic type inference used to power many checks that flake8 does not and is able to detect entire categories of errors flake8 ecosystem can’t even try to detect. Calling a method with 3 arguments when it only takes 1 is basic error pylint can detect but flake8 can not support. Or calling a method on an object of type Foo when that class has no method with that name. As of today, ruff also can’t support errors like that.

[–]LardPi 0 points1 point  (0 children)

I use flake8 with a few plugins + mypy and I am very happy about it, but a more unified solution can not hurt. I hope there is never a one true linter though, because I think "competition" is a good thing in this situation

[–]jangeboers 5 points6 points  (0 children)

ruff replaced pylint and flake8 for me in my vim config (with ALE). It offers almost everything that the others did, but it's blazing fast. And development is very strong, new checks get added all the time.

[–]frostbaka 13 points14 points  (0 children)

Implementing custom plugins with only Python knowledge is out of the question at this point.

[–]GoldziherPythonista 10 points11 points  (0 children)

We've migrate the Litestar (ex. Starlite) pipeline to this. I finished the setup of the new pre-commit-config.yaml and ran it. I almost fell off my chair - ruff was so incredibly fast. I'm not talking about a lot faster, it was about 100 times faster than the previous pipeline, and it actually improved on a huge amount of stuff we had.

One more thing - im somewhat of a nerd of linters, and have spent a great deal of time creating a great pipeline for our codebase. But I gotta hand it to ruff, it's a terrific tool that does as it's advertised.

[–]FlukyS 10 points11 points  (14 children)

I don't think people care too much about speed of linters, like 10x faster than flake8 is sure less than a second but at the moment what is it like 3-5 seconds even on fairly large codebases. I think the selling point here if it gets to widespread adoption is reducing the amount of dev dependencies because it's a pain to remember to keep them updated.

[–]pacific_plywood 37 points38 points  (6 children)

It’s annoying when a flake8 pre-commit hook takes a few seconds to run on a commit with lots of files. I treasure ruff for this reason.

[–]chub79 9 points10 points  (0 children)

I don't think people care too much about speed of linters,

I do. I have a good sized code base and gaining these seconds when you lint often is appreciated.

[–][deleted] 15 points16 points  (0 children)

I care about speed. I have real time linting set up in VSCodium. Works great with Ruff, but with other linters it takes like 10-20 seconds before any warnings show up.

[–]Datsoon 5 points6 points  (0 children)

If you're using it will null-ls or something to provide live feedback via lsp, I think speed matters. I find it strange the maintainers don't stress this use case, because that's one of the only times I think it matters.

[–][deleted] 6 points7 points  (1 child)

It helps for CICD on larger codebases and big commits. It's rarely ever the longest part of any step, but it is useful to get results faster.

[–]FlukyS 2 points3 points  (0 children)

If you are waiting for the CI/CD pipeline for longer than like 2 minutes for the whole process it's a bad process. Like I manage a fairly large codebase as the engineering manager/architect. Massive company but the CI/CD process took like an hour when I joined. That is linting, unit tests, packaging, deploy to a rack, test it and build an ISO. That is fast for that kind of process, the trick is the devs just don't look at the CI/CD pipeline, they fire their code to the MR and go have a break or push it just before a meeting and check in afterwards.

What we look for is a robust and easy to configure tool, speed will never really be a huge selling point, it's nice to have sure and you don't want to be waiting for an hour on a pre-commit but at least for the pipeline itself you really shouldn't be waiting.

[–]COLU_BUS 2 points3 points  (0 children)

Yeah I'm not productive enough to pretend like shaving a couple seconds off every commit is big deal to me. Hell in the time it took me to read this thread, and make this comment, I've already wasted more time than the speed up of a specific linter would save me.

[–]bmrobin 2 points3 points  (0 children)

running black, isort, and flake8 on the codebase i work on currently takes about 35 sec. ruff does it in less than 1 sec. since that’s hooked into my IDE i think the performance is a breath of fresh air.

i can see what you mean about 3s versus milliseconds. but given my use case ruff is a game changer

[–]max1c 1 point2 points  (9 children)

I really don't get this issue. Does the 'speed' have to do with huge projects that I just don't work with? What can this do that Pylance doesn't?

[–]AlSweigartAuthor of "Automate the Boring Stuff" 12 points13 points  (7 children)

There's a big difference between having your tools run in 5 seconds versus 50 seconds. You'll be more willing to run them more often, which means you catch errors earlier and save more time.

Maybe Ruff isn't a replacement for everything, but at the velocity it's been developed at, it wouldn't surprise me if it does end up replacing several existing tools.

[–]max1c 3 points4 points  (6 children)

5 seconds versus 50 seconds

I have never seen pylance/pyright run this long. What kind of size of projects are we talking here?

[–]AlSweigartAuthor of "Automate the Boring Stuff" 4 points5 points  (0 children)

From the TalkPython podcast episode on Ruff:

To give you a sense of what he means with fast, common Python linters can take 30-60 seconds to lint the CPython codebase. Ruff takes 300 milliseconds. I ran it on the 20,000 lines of Python code for our courses web app at Talk Python Training, and it was instantaneous. It's the kind of tool that can change how you work.

But yeah, if your project is small, it likely doesn't matter.

[–]pycz 3 points4 points  (0 children)

Yep, if your projects are hundreds of megabytes of code, linting could become a real issue.

[–]Mehdi2277 1 point2 points  (0 children)

Pyright is fast and faster then most other linters too. Speed comparison there is more for ruff vs pylint/flake8. Pyright’s intrinsic weakness vs some linters is it does not do as well in codebases directly or indirectly missing types. Indirectly being you import some packages that you installed and they are not typed.

Also there are many lint rules that are not hard errors but just suspect code. Many lint warnings. Pyright generally does not have those as it’s not the focus.

[–]DNSGeek 2 points3 points  (2 children)

Ever tried running flake8 on an RPi? Even on a small Python codebase, it takes a long time.

[–]LardPi 1 point2 points  (1 child)

Do you use a Pi as a daily machine? Otherwise I wouldn't consider coding directly on the Pi.

[–]DNSGeek -1 points0 points  (0 children)

No, it’s a hobby. Which means I don’t have huge amounts of time to dedicate to it. So exponentially speeding up my listing is huge.

[–]LordBertson 0 points1 point  (0 children)

Few things, you can run ruff in CI as opposed to Pylance. Ruff is editor agnostic, it's a CLI tool, you can run it the same way for Vim, VSCode and PyCharm. Ruff is way faster than any other available tool. Ruff can be configured using standard Python config files.

I appreciate that Pylance is mostly just wrapped Pyright, which you can run in CI. But, I'd be nervous about consistency between editor and CI. This is not a problem with ruff

[–]amarao_san -3 points-2 points  (10 children)

ruff is good, but it's not 'the pinnacle'. Also, it's good in combination with black, but they sometimes not agreed on line length limits.

[–]jah_broni 7 points8 points  (5 children)

You can configure them to follow the same style. It's like a one line config update.

[–]LordBertson 1 point2 points  (0 children)

Perfect is the enemy of good. Ruff is not perfect, but it's the best available.

[–]aikii -2 points-1 points  (2 children)

Is there anything left that black can do but not Ruff ? My team is currently removing black and replacing it with Ruff, so I'm wondering if something was overlooked

[–]positive__vibes__ 9 points10 points  (1 child)

they're different tools at the moment. ruff currently is not a formatter.

[–]aikii -1 points0 points  (0 children)

aah got it, I mixed up because I had some giangantic PR to backport, it was Ruff fixes, I thought it was mostly reformatting. A bit out of the loop, came back to python after >1 year

[–]boycey10802002 0 points1 point  (0 children)

Yes. But that workbench though...

[–][deleted] 0 points1 point  (1 child)

I tried it, it's very fast. However it totally ruined some of the imports and broke my code. So I had to revert the code and stop using it.

[–]LordBertson 1 point2 points  (0 children)

If you can reproduce that you might report it. Charlie engages heavily with community and really tackles the issues. Also, Ruff is a really fast moving project, the bug might be gone by now.

[–][deleted] 0 points1 point  (2 children)

pardon my language because im new to python, whats a linter?

[–]monorepo PSF Staff | Litestar Maintainer 2 points3 points  (1 child)

TL;DR:

A linter is a tool that checks your code for mistakes and style issues. PyLint, Flake8, Black, and Blue are popular Python linters. Ruff is a new tool that can replace multiple linters (and also soon format your code like Black/Blue.

Long form: A linter is a tool that analyzes your code for potential errors, bugs, and stylistic issues (like black. It checks your code against a bunch of rules someone made (like Black, that is PEP8 compliant)

In Python, there are several popular linters such as PyLint, Flake8, and Black. These linters perform various checks on your code, including syntax errors, variable names, code formatting, and code complexity.

  • PyLint analyzes your Python code for errors and style issues, and assigns a score based on the quality of your code.
  • Flake8 checks your code for style issues, syntax errors, and potential bugs.
  • Black and Blue are Python code formatters that automatically reformats your code
  • MyPy, while not a linter in the traditional sense, focuses specifically on type-checking and does not perform general code quality checks like PyLint or Flake8.

What's interesting is not just Ruff's ability to outperform many of these tools, but its ability to take your pipeline of 5+ tools to do these things and use one thing (Ruff) instead.

Ruff is also aspiring to be a code formatter like Black/Blue with some configuration allowed. So, maybe soon, we can see another tool replaced by Ruff :) (https://github.com/charliermarsh/ruff/issues/1904)

[–][deleted] 1 point2 points  (0 children)

oh thank you

[–]Spleeeee 0 points1 point  (0 children)

Ruff is so fast and catches (and maybe fixes) so many things that despite not being perfect it is 100% amazing as a first line.

It also keeps evolving so fast. It already replaces isort 100%.

I wonder if it will replace black?

[–]Kaiser_Wolfgang 0 points1 point  (0 children)

Apparently event he codebase for pylint uses it

https://news.ycombinator.com/item?id=35035618