On the other hand, both `ruff` and `ty` are about code style. They both edit the code, either to format or fix typing / lint issues. They are good candidates to be merged.
On the other hand, both `ruff` and `ty` are about code style. They both edit the code, either to format or fix typing / lint issues. They are good candidates to be merged.
uv is like cargo for python.
If you only need a fast type checker you can just use ty, if you just need a fast formatter and linter you can just use ruff.
Combining ruff and ty doesn't make sense if you think about like this.
The analogy would be to Cargo: `cargo fmt` just runs `rustfmt`, but you can also run `rustfmt` separately if you want.
The whole point is you just install `uv` and stop thinking about the pantheon of tools.
The goal here is to see if users like a more streamlined experience with an opinionated default, like you have in Rust or Go: install uv, use `uv init` to create a project, use `uv run` to run your code, `uv format` to format it, etc. Maybe they won't like it! TBD.
(Ruff is installed when you invoke `uv format`, rather than bundled with the uv binary, so if you never use `uv format`, there aren't any material downsides to the experiment.)
The reality is, ecosystems evolve. First, we had mypy. Then more type checkers came out: pyre, pyright, etc. Then basedpyright. The era of rust arrived and now we have `ty` and `pyrefly` being worked on heavily.
On the linter side we saw flake8, black, and then ruff.
Decoupling makes adapting to evolution much easier. As long as both continue to offer LSP integrations it allows engineers to pick and chose what's best.
That’s thoughtful design and could be worth mentioning in the blog post.
Stupidly I ran `uv format` without `--check` (no harm done and I can `git diff` it) so I didn't see the changes it made but `ruff check` does still show things that can be fixed with `ruff check --fix`. If I'm guessing correctly the difference is coming down to the fact that I have (in my submodule where all changes were made) a pyproject.toml file with ruff rules (there's also a .flake8 file. Repo is being converted). Either way, I find this a bit confusing userside. Not sure what to expect.
I think one thing I would like is that by default `uv format` spits out what files were changed like `uv format --check` does (s/Would reformat/Reformatted/g). Fine for the actual changes not to be displayed but I think this could help with error reduction. Running it again I can see it knows 68 files were changed. Where is that information being stored? It's pretty hard to grep out a number like that (`grep -R \<68\>`) and there's a lot of candidates (honestly there's nothing that looks like a good candidate).
Also, there's a `--quiet` flag, but the output is already pretty quiet. As far as I can tell the only difference is that quiet suppresses the warning (does `--quiet` also suppress errors?)
uv format
warning: `uv format` is experimental and may change without warning. Pass `--preview-features format` to disable this warning.
36 files reformatted, 31 files left unchanged
uv format --quiet
36 files reformatted, 31 files left unchanged
I like the result for `--quiet` but I have a strong preference that `uv format` match the verbosity of `uv format --check`. I can always throw information away but not recover. I have a strong bias that it is better to fail by displaying too much information than fail by displaying too little. The latter failure mode is more harmful as the former is much more easily addressed by existing tools. If you're taking votes, that's mine.Anyways, looking forward to seeing how this matures. Loved everything so far!
But most importantly, apart from breaking away from "UNIX-philosophy tools", what do you lose in practical terms?
All 3 scenarios benefit from removing the choice of build tool, package manager, venv manager, formatter, linter, etc., and saying, "here, use this and get on with your life".
My understanding was that uv is for installing dependencies (e.g. like pip) with the added benefit of also installing/managing python interpreters (which can be reasonably thought of as a dependency). This makes sense. Adding more stuff doesn't make sense.
I remember David Beazley mentioning that code with Makefiles were relatively easier to analyze based on ~Terabyte of C++ code and no internet connection (pycon 2014) https://youtube.com/watch?v=RZ4Sn-Y7AP8
`uv format` is similar (you don't need to know about `ruff format` / black / yapf ).
But formatting code is a completely new area that does not fit uv.
I would love to see a world where there is a single or a set of standard commands that would prepare your python project (format, lint, test, publish). Maybe that’s the vision here?
Given uv is openly strongly inspired by cargo and astral also has tooling for code formatting, the integration was never a question of “if”.
And why is any of this relevant to first-time Python learners? (It's already a lot to ask that they have to understand version control at the same time that they're learning specific language syntax along with the general concept of a programming language....)
You can use uv without ruff. You can use ruff without uv. You can invoke ruff yourself if that’s what you want. Or use any other formatter.
I don’t think I understand what your complaint is.
Perhaps even better… cargo-like commands such as uv check, uv doc, and uv test could subsume ruff, ty, and other tools that we haven’t seen yet ;)
A pyup command that installs python-build-standalone, uv, python docs, etc. would be totally clutch, as would standalone installers [0] that bundle it all together.
[0] https://forge.rust-lang.org/infra/other-installation-methods...
I am not against auto formatters in general, but they need to be flexible and semantically aware. A log call is not the same as other calls in significance. If the auto formatter is too silly to do that, then I prefer no auto formatter at all and keep my code well formatted myself, which I do anyway while I am writing the code. I do it for my own sake and for anyone who comes along later. My formatting is already pretty much standard.
However, to answer the question generally: people want this for the same reason that most people call `cargo fmt` instead of running rustfmt[1] directly: it's a better developer experience, particularly if you don't already think of code formatting as an XY-type problem ("I want to format my code, and now I have to discover a formatter" versus "I want to format my code, and my tool already has that").
It requires less knowledge at the front end, which is when people are being bombarded with a ton of new things to learn.
Learners don’t have to even know what ruff is immediately. They just know that when they add “format” to the command they already know, uv, their code is formatted. At some later date when they know Python better and have more opinions, they can look into how and why that’s accomplished, but until then they can focus on learning Python.
uv isn’t a package manager only, its best thought of as a project manager, just like go or cargo. Its “one thing” is managing your Python project.
ruff at least seems to be compiled into uv, as the format worked here without a local ruff. This is significant more than just an interface. Whether they are managed and developed as separate tools doesn't matter.
> This is more about providing a simpler experience for users that don't want to think about their formatter as a separate tool.
Then build a separate interface, some script/binary acting as a unified interface, maybe with its separate distribution of all tools. Pushing it into uv is just adding a burden to those who don't want this.
uv and ruff are poor names anyway, this could be used to at least introduce a good name for this everything-python-tool they seem to aim for.
Maybe I'm wrong about that. But I don't know that it can actually be A/B tested fairly, given network effects (people teaching each other or proselytizing to each other about the new way).
Translating this to uv, this will streamline having multiple python packages in the same directory/git repo, and leave e.g. vendored dependencies alone.
Also, since their goal really is "making cargo for python", it will likely support package-scoped ruff config files, instead of begin file- or directory-based.
But I'll also add another suggestion/ask. I think this could be improved
$ ruff format --help
Run the Ruff formatter on the given files or directories
$ uv format --help
Format Python code in the project
I think just a little more can go a long way. When --help is the docs instead of man I think there needs to be a bit more description. Just something like this tells users a lot more $ ruff format --help
Formats the specified files. Acts as a drop-in replacement for black.
$ uv format --help
Experimental uv formatting. Alias to `ruff format`
I think man/help pages are underappreciated. I know I'm not the only one that discovers new capabilities by reading them. Or even the double tab because I can't remember the flag name but see something I didn't notice before. Or maybe I did notice before but since the tool was new I focused on main features first. Having the ability to read enough information to figure out what these things do then and there really speeds up usage. When the help lines don't say much I often never explore them (unless there's some gut feeling). I know the browser exists, but when I'm using the tool I'm not in the browser.I'd like to hear more about that. I'm also curious what makes Rust particularly suited to "API-first" backends. My understanding of the language is that it's concurrency primitives are excellent but difficult to learn and it's gc-less runtime.
They're actually incredibly easy to learn if your software paradigm is the request-response flow.
The borrow checker might kill your productivity if you're writing large, connected, multi-threaded data structures, but that simply isn't the nature of 90% of services.
If you want to keep around global state (db connectors, in-memory caches, etc.) Arc<Mutex<T>> is a simple recipe that works for most shared objects. It's dead simple.
You can think of Rust with Axum/Actix as a drop-in replacement for Go or Python/Flask. With the added benefits of (1) no GC / bare metal performance, (2) much lower defect rate as a consequence of the language ergonomics, (3) run it and forget it - no GC tuning, very simple scaling.
Rust has effectively made writing with the performance of C++ feel like writing Ruby, but with unparalleled low defect rates and safety on account of the type system.
Let's imagine you're learning a new language, which has tools with names that I just made up. Which has a clearer pattern to you?
1. Check code with `blargle check`
2. Format code with `blargle format`
3. Run code with `blargle run`
Or 1. Check code with `zoop`
2. Format code with `chungus`
3. Run code with `kachow`
Clearly, the first is easier to remember. The benefit of namespacing is that you only need to remember one weird/unique name, and then everything under that can have normal names. If you don't have namespacing, you need weird/unique names for every different tool.This is a little overblown.. speaking VERY HAND-WAVILY, sea_orm < active record by a factor of about 10x more mental overhead but is at least that much more performant...
but yea, vibe-coding rust micro services is pretty amazing lately, almost no interactions with borrow checker, and I'm even using cucumber specs...
I currently wouldn't recommend any Rust ORM, Diesel included. They're just not quite ready for prime time.
If you're not one to shy away from raw SQL, then SQLx is rock-solid. I actually prefer it over ORMs in general. It's type-checked at runtime or compile time against your schema, no matter how complex the query gets. It manages to do this with an incredibly simple design.
It's like an even nicer version of Java's popular jOOQ framework, which I always felt was incredibly ugly.
SQLx might be my very favorite SQL library of any language.