6 ms·
Context: Ruff is a Rust-based linter for Python. It’s much faster than Python-based linters for Python because Rust is much faster than Python. There are a cou
by muglug 4y ago
Context: Ruff is a Rust-based linter for Python. It’s much faster than Python-based linters for Python because Rust is much faster than Python.
There are a couple of drawbacks to Ruff that mean it may not be right for everyone: since it’s a compiled tool, it’s harder to add custom rules — you need to fork the project to add your own, and also you need to know some Rust.
When checking the Pylint codebase it makes sense to use Ruff as well as Pylint — it’s a good sanity check that Pylint’s own tooling is not letting issues through that another tool is catching.
- bjourne 4y agoHow many thousands of lines of code do you need to lint before Rust's performance advantages actually matter? After all, performance optimizing a tool where N is too small to matter is the root of all evil. Keep in mind that you don't have to lint an already linted file if you cache its hash.
- vitorsr 4y agoTo add to your comment: I also think that these rewrites are throwing the baby out with the bathwater. Especially in projects related to code quality and analysis, things like widespread adoption and issues going back almost a decade are much more important than raw performance. For every aspect of functionality, there is a heavy baggage of quirks, bugs, edge cases etc. I am not convinced that a rewrite of a tool that is still in development and still limited to a subset of functionality is able to capture nearly a decade of community work.
- nindalf 4y agoThe author of Ruff mentioned that many people told him exactly what you're saying. He went ahead and built it anyway. I'm puzzled though. Those naysayers were naysaying about an attempt at rewriting. You appear to be naysaying a successfully built project. It's true that the folks who find it most helpful are the ones with large Python projects. But these are sometimes very important, widely used projects. See the testimonials from users (https://beta.ruff.rs/docs/#testimonials https://beta.ruff.rs/docs/#testimonials) > widespread adoption This makes little sense in the context of a linter. As long as my linter implements all the popular rules and my code is kept tidy, what's the issue? What does it matter if there's widespread adoption or not? > still limited to a subset of functionality You have a link to some functionality you're missing and necessary for your workflow? Or is this more of a theoretical concern? Sorry if this comes across as rude.
- vitorsr 4y agoI am happy if this tool works for you but I think writing off my comment as naysaying is not constructive. There is no support for all syntax features of the current language specification and reference implementation. There is also no legacy support. Again, especially in projects concerned with code quality and analysis, building is not more important than long-term maintenance: Both language specification and reference implementation are constantly evolving. The current approach relies on an upstream interpreter and thereby inherits idiosyncratic divergences and limitations. There is no extensibility. The majority of projects are neither public nor frameworks. Often projects employ a number of frameworks each with associated dialects, with both frameworks and dialects also constantly evolving. The current approach to extensibility is approximately one person manually rewriting rules individually from heterogeneous projects that span several contributors over several years. There is already outstanding maintenance work on manually introduced rules, only a subset of which gets flagged by a fraction of adopters. There is even outstanding work on feature parity.
- bastawhiz 4y agoI honestly thought the same thing. I used flake8 and it finished in about a second on my codebase. But when you have that on a git hook, you'd be surprised how the small amount of latency affects your productivity. I have been using ruff for about two months now, and I'm still thinking "Wow that was fast" every time I create a commit.
- OJFord 4y agoWe have a CI pipeline more complicated than it needs to be (=> also engineering time setting that up, maintaining it) because pylint was too slow. Not a snowflake, just a Python web app.
- avgcorrection 4y agoLinting is something that people might do on every commit these days. Commits that take more than 150ms to complete are annoying. And also on every CI run. Oh well every CI run doesn’t sound bad. Except you’re also running 30 other things in order to check 30 other “should always work/be the case” invariants. EDIT: Missed your point about caching already-linted file. In that case maybe performance doesn’t matter. > After all, performance optimizing a tool where N is too small to matter is the root of all evil. Picking a third-party tool that does what it does and happens to be relatively fast. Where’s the evil in that?
- brianwawok 4y agoMy note huge codebase takes 5s for flake8 The max we have ever had is 3 developers at a time, so if we were a 100 or 1000 person company, I bet it's a bit of time.
- minraws 4y ago> Keep in mind that you don't have to lint an already linted file if you cache its hash. yeah, if only that was the case. Even if the file hasn't changed if it's dependencies have changed you need to lint again. And doing dependency analysis on most weirdly coupled codebases out there, is not trivial nor accurate tbh. And not running a good linter and letting a bug through is the most shit feeling you will have in your life. Trust me on that.
- muglug 4y agoIt depends on the type of linting being done. For linters that perform type inference, fanout means having to relint files that might not have changed. But many linters don’t need the full graph — for example, file formatting problems aren’t subject to fanout.
- bjourne 4y agoWhat linter functionality requires global analysis?
- zackees 4y ago[dead]
- pjmlp 4y agoHow much Python code would remain written in Python, if caring about a proper JIT in CPython was actually a thing, one wonders.
- charliermarsh 4y agoYeah Ruff and Pylint can be used in a complementary way. Pylint implements rules that Ruff does not, and vice versa. I think Ruff might actually support more _total_ rules than Pylint at this point (not a great metric), but Pylint does more cross-file analysis and type inference. [1] Some teams use Ruff alongside Pylint, some have replaced Pylint with Ruff entirely (Dagster is a good example [2]), especially those already using a type checker alongside their linter. [1] https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-pylint https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-pyli... [2] https://twitter.com/schrockn/status/1612615862904827904 https://twitter.com/schrockn/status/1612615862904827904
- epage 4y agoWhen you are already using flake8/ruff and mypy, there is little room left for pylint in my opinion. It might have some extra checks but they aren't enough to justify the performance hit.
- charliermarsh 4y agoBy the way: epage, you are a hero. I am very grateful for everything you build.
- munch117 4y agoI use a script that identifies modified files in my working copies, runs pyflakes on them, and after that pylint -E. By the time pylint finishes, I'll have already fixed most of the low-hanging fruit that pyflakes found, and then I look through the pylint messages to see if there's anything additional. There's very little waiting for pylint that way.
- lost_tourist 4y agoI mean while it's harder to support dynamic stuff in rust than say in python, it's certainly not impossible to add dynamic rules in principle.
- muglug 4y agoYou can add dynamic rules, but they either need to be compiled alongside the tool or written in a format that the tool can parse. That interchange language can be anything from plaintext to WASM bytecode. I’ve used the former system (compiling things together in a custom build) in a Rust-based static analysis tool I’ve created. SWC uses the latter system, requiring plugins to be authored in WASM.
- froh 4y agodo you happen to know why ruff is not extensible in Python?
- toxik 4y agoBecause it is written in Rust, I presume.
- benrutter 4y agoIt's a design choice by its creator- funny enough I was listening to an episode of 'talk python to me' with the creator yesterday and he talked about exactly that. The main reason given was that each plugin has to reimplement a bunch of the same stuff slightly differently (like finding public methods eg) and keeping it all internal avoids this and keeps things speedy
- coldtea 4y ago>There are a couple of drawbacks to Ruff that mean it may not be right for everyone: since it’s a compiled tool, it’s harder to add custom rules — you need to fork the project to add your own, and also you need to know some Rust. That might not be unsurmisable, given that most don't write their own rules (just combine, configure, enable/disable existing ones), and that in could be possible to add to Ruff a way to write rules in a DSL (without knowing Rust itself, and without the need recompile).
- codetrotter 4y ago> add to Ruff a way to write rules in a DSL But if you start going down that route, isn’t it likely that you’d end up making Ruff into a full-blown interpreter? Or that you end up embedding an interpreter into Ruff. Such as for example.. Python. At either of which point, the motivation to use Ruff instead of e.g. Pylint might begin to disappear?
- Yoric 4y agoGiven how easy it is to write Rust code that calls Python or Python code that calls Rust, it might even be possible to write Ruff rules in Python.
- oblio 4y agoDo you happen to have handy a good article about these 2 way calls?
- KptMarchewa 4y agoThere is something better than article: documentation. https://pyo3.rs/v0.18.1/ https://pyo3.rs/v0.18.1/
- coldtea 4y ago>But if you start going down that route, isn’t it likely that you’d end up making Ruff into a full-blown interpreter? Not any more than with any other DSL. You can always stop at a subset enough to handle expressing linting rules. Or expressing many linting rules. Doesn't need to be able to express every possible rule -- which is the reason you'd make it a full blown interpeter. Pareto and all. Even if that's not possible (and you need turing completness to handle custom rules), then: >At either of which point, the motivation to use Ruff instead of e.g. Pylint might begin to disappear? Well, Ruff would still have crazily faster parsing and built-in rules. It's not like you'd be forced to use custom interpreted rules. And even in that case, a purpose-specific built interpeter, even if general, could be way faster (because it's optimized for a specific task and doesn't need to carry garbage as the GIL and other CPython decisions), and with better task-focused primitives, compared to embedding CPython.