[HN Gopher] Switching Pip to Uv in a Dockerized Flask / Django App
       ___________________________________________________________________
        
       Switching Pip to Uv in a Dockerized Flask / Django App
        
       Author : tosh
       Score  : 231 points
       Date   : 2025-06-24 09:46 UTC (13 hours ago)
        
 (HTM) web link (nickjanetakis.com)
 (TXT) w3m dump (nickjanetakis.com)
        
       | j4mie wrote:
       | It's worth noting that uv also supports a workflow that directly
       | replaces pyenv, virtualenv and pip without mandating a change to
       | a lockfile/pyproject.toml approach.
       | 
       |  _uv python pin <version>_ will create a .python-version file in
       | the current directory.
       | 
       |  _uv virtualenv_ will download the version of Python specified in
       | your .python-version file (like _pyenv install_ ) and create a
       | virtualenv in the current directory called .venv using that
       | version of Python (like _pyenv exec python -m venv .venv_ )
       | 
       |  _uv pip install -r requirements.txt_ will behave the same as
       | _.venv /bin/pip install -r requirements.txt_.
       | 
       |  _uv run <command>_ will run the command in the virtualenv and
       | will also expose any env vars specified in a .env file (although
       | be careful of precedence issues: https://github.com/astral-
       | sh/uv/issues/9465)
        
         | slau wrote:
         | uv and its flexibility is an a absolute marvel. Where pip took
         | 10 minutes, uv can handle it in 20-30s.
        
           | ljm wrote:
           | It's an absolute godsend. I thought poetry was a nice
           | improvement but it had its flaws as well (constant merge
           | conflicts in the lock file in particular).
           | 
           | Uv works more or less the same as I'm used to with other
           | tooling in Ruby, JS, Rust, etc.
        
         | politelemon wrote:
         | Doesn't it store the python version in the pyproject.toml
         | though, is the python version file needed?
        
           | JimDabell wrote:
           | It's not:
           | 
           | > uv will respect Python requirements defined in requires-
           | python in the pyproject.toml file during project command
           | invocations. The first Python version that is compatible with
           | the requirement will be used, unless a version is otherwise
           | requested, e.g., via a .python-version file or the --python
           | flag.
           | 
           | -- https://docs.astral.sh/uv/concepts/python-
           | versions/#project-...
        
             | politelemon wrote:
             | cheers
        
         | smeeth wrote:
         | +1, this is the exact reason I started using uv. Extremely
         | convenient.
         | 
         | For some reason uv pip has been very slow, however. Unsure why,
         | might be my org doing weird network stuff.
        
           | greenavocado wrote:
           | Or very difficult package spec
        
       | gchamonlive wrote:
       | # Ensure we always have an up to date lock file.       if ! test
       | -f uv.lock || ! uv lock --check 2>/dev/null; then         uv lock
       | fi
       | 
       | Doesn't this defeat the purpose of having a lock file? If it
       | doesn't exist or if it's invalid something catastrophic happened
       | to the lock file and it should be handled by someone familiar
       | with the project. Otherwise, why have a lock file at all? The CI
       | will silently replace the lock file and cause potential
       | confusion.
        
         | freetonik wrote:
         | In the Python world, I often see lockfiles treated a one "weird
         | step in the installation process", and not committed to version
         | control.
        
           | burnt-resistor wrote:
           | In the almost every world, Ruby and elsewhere too,
           | constraints in _library_ package metadata are supposed to
           | express the full supported possibilities of allowed
           | constraints while lock files represent current specific
           | state. That 's why they're not committed in that case to
           | allow greater flexibility/interoperability for downstream
           | users.
           | 
           | For _applications,_ it 's recommended (but still optional) to
           | commit lock files so that very specific and consistent
           | dependencies are maintained to prevent arbitrary,
           | unsupervised package upgrades leading to breakage.
        
             | MrJohz wrote:
             | I know Cargo recommended your approach for a while, but
             | ended up recommending that all projects always check in a
             | lock file. This is also the norm in most other ecosystems
             | I've used including Javascript and other Python package
             | managers.
             | 
             | When you're developing a library, you still want
             | consistent, reproducible dependency installs. You don't
             | want, for example, a random upgrade to a testing library to
             | break your CI pipelines or cause delays while releasing. So
             | you check in the lock file for the people working on the
             | library.
             | 
             | But when someone installs the library via a package
             | manager, that package manager will ignore the lock file and
             | just use the constraints in the package metadata. This
             | avoids any interoperability issues for downstream users.
             | 
             | I've heard of setups where there are even multiple lock
             | files checked in so different combinations of dependency
             | can be tested in CI, but I've not seen that in practice,
             | and I imagine it's very much dependent on how the ecosystem
             | as a whole operates.
        
           | oceansky wrote:
           | It's what I used to do with package-lock.json when I had
           | little production experience.
        
           | slau wrote:
           | In my experience, this is fundamentally untrue. pip-tools has
           | extensive support for recording the explicit version numbers,
           | package hashes and whatnot directly in the requirements.txt
           | based on requirements.in and constraints files.
           | 
           | There are many projects that use pip-compile to lock things
           | down. You couldn't use python in a regulated environment if
           | you didn't. I've written many Makefiles that explicitly
           | forbid CI from ever creating or updating the actual
           | requirements.txt. It has to be reviewed by a human, or more.
        
             | Hasnep wrote:
             | They're not saying that's how it's supposed to be used,
             | they're saying that's how it's often used by people who are
             | unfamiliar with lock files
        
             | MrJohz wrote:
             | There are lots of tools that allow you to generate what are
             | essentially lock files. But I think what the previous
             | poster is saying is that most people either don't use these
             | tools or don't use them correctly. That certainly matches
             | my experience, where I've seen some quite complicated
             | projects get put into production without any sort of
             | dependency locking whatsoever - and where I've also seen
             | the consequences of that where random dependencies have
             | upgraded and broken everything and it's been almost
             | impossible to figure out why.
             | 
             | To me, one of the big advantages of UV (and similar tools)
             | is that they make locked dependencies the default, rather
             | than something you need to learn about and opt into. These
             | sorts of better defaults are sorely needed in the Python
             | ecosystem.
        
           | bckr wrote:
           | This is kinda how I treat it. I figured that I have already
           | set the requirements in the pyproject.toml file.
           | 
           | Should I be committing the lock file?
        
             | gcarvalho wrote:
             | If your pyproject.toml does not list all your dependencies
             | (including dependencies of your dependencies) and a fixed
             | version for each, you may get different versions of the
             | dependencies in future installs.
             | 
             | A lock file ensures all installations resolve the same
             | versions, and the environment doesn't differ simply because
             | installations were made on different dates. Which is
             | usually what you want for an application running in
             | production.
        
         | 9dev wrote:
         | What are the possible remediation steps, however? If there is
         | no lock file at all, this is likely the first run, or it will
         | be overwritten from a git upstream later on anyway; if it's
         | broken, chances are high someone messed up a package
         | installation and creating a fresh lock file seems like the only
         | sensible thing to do.
         | 
         | I also feel like this handles rare edge cases, but it seems
         | like a pretty straightforward way to do so.
        
           | globular-toast wrote:
           | The fix is to generate the lockfile and commit it to the
           | repository. Every build should be based on the untouched
           | lockfile from the repo. It's the entire point of it.
        
           | stavros wrote:
           | If there's no lock file at all, you haven't locked your
           | dependencies, and you should just install whatever is current
           | (don't create a lockfile). If it's broken, you have problems,
           | and you need to abort the deploy.
           | 
           | There is never a reason for an automated system to create a
           | lockfile.
        
             | ealexhudson wrote:
             | The reason is simple: it allows you to do the install using
             | "sync" in all cases, whether the lockfile exists or not.
             | 
             | Where the lockfile doesn't exist, it creates it from
             | whatever current is, and the lockfile then gets thrown away
             | later. So it's equivalent to what you're saying, it just
             | avoids having two completely separate install paths. I
             | think it's the correct approach.
        
               | stavros wrote:
               | I don't understand, you can already run `uv sync` if the
               | lockfile doesn't exist. It just creates a new one. Why do
               | it explicitly, like here?
        
           | JimDabell wrote:
           | If the lock file is missing the only sensible thing to do is
           | require human intervention. Either it's the unusual case of
           | somebody initialising a project but never syncing it, or
           | something has gone seriously wrong - with potential security
           | implications. The upside to automating this is negligible and
           | the downside is large.
        
             | guappa wrote:
             | ? It has always been the case that if you don't specify a
             | version, the latest is implied.
        
               | slau wrote:
               | Whether it's the latest or not is irrelevant. What's
               | important is the actual package hash. This is the only
               | way to have fully reproducible builds that are immune to
               | poison-the-well attacks.
        
               | guappa wrote:
               | That would be true if anyone actually ever reviewed the
               | dependencies. Which is not the case. So the version
               | doesn't matter when any version is as likely to contain
               | malware.
        
           | ufmace wrote:
           | IMO, this is the process for building an application image
           | for deployment to production. If the lock file is not
           | present, then the developer has done something wrong and the
           | deployment _should_ fail catastrophically because only manual
           | intervention by the developer can fix it correctly.
        
         | silvester23 wrote:
         | This is actually covered by the --locked option that uv sync
         | provides.
         | 
         | If you do `uv sync --locked` it will not succeed if the lock
         | file does not exist or is out of date.
         | 
         | Edit: I slightly misread your comment. I strongly agree that
         | having no lock file or a lockfile that does not match your
         | specified dependencies is a case where a human should
         | intervene. That's why I suggest you should always use the
         | --locked option in your build.
        
         | remram wrote:
         | Yes this is a major bug in the process. I came to the comments
         | to say this as well.
         | 
         | They say this but do the exact opposite as you point out:
         | 
         | > The --frozen flag ensures the lock file doesn't get updated.
         | That's exactly what we want because we expect the lock file to
         | have a complete list of exact versions we want to use for all
         | dependencies that get installed.
        
           | nickjj wrote:
           | It's not a major bug, check my reply in:
           | https://news.ycombinator.com/item?id=44370311
        
             | remram wrote:
             | You changed the code in the article to fix the problem. So
             | there is no bug _anymore_ is what you mean.
        
         | nickjj wrote:
         | Hi author here.
         | 
         | If you end up with an invalid lock file, it doesn't silently
         | fail and move on with a generated lock file. The `uv lock`
         | command fails with a helpful message and then errexit from the
         | shell script kicks in.
         | 
         | The reason I redirected the uv lock --check command's errors to
         | /dev/null is because `uv lock` throws the same error and I
         | wanted to avoid outputting it twice.
         | 
         | For example, I made my lock file invalid by manually switching
         | one of the dependencies to a version that doesn't match the
         | expected SHA.
         | 
         | Then I ran the same script you partially quoted and it yields
         | this error which blocks the build and gives a meaningful
         | message that a human can react to:                   1.712
         | Using CPython 3.13.3 interpreter at: /usr/local/bin/python3
         | 1.716 error: Failed to parse `uv.lock`         1.716   Caused
         | by: The entry for package `amqp` v5.3.4 has wheel
         | `amqp-5.3.1-py3-none-any.whl` with inconsistent version: v5.3.1
         | ------         failed to solve: process "/bin/sh -c chmod 0755
         | bin/* && bin/uv-install" did not complete successfully: exit
         | code: 2
         | 
         | This error is produced from `uv lock` when the if condition
         | evaluates to true.
         | 
         | With that said, this logic would be much clearer which I just
         | commit and pushed:                   if test -f uv.lock; then
         | uv lock --check         else           uv lock         fi
         | 
         | As for a missing lock file, yep it will generate one but we
         | want that. The expectation there is we have nothing to base
         | things off of, so let's generate a fresh one and use it moving
         | forward. The human expectation in a majority of the cases is to
         | generate one in this spot and then you can commit it so moving
         | forward one exists.
        
           | remram wrote:
           | I see you just changed your article from what it was when we
           | commented:                 if ! test -f uv.lock || ! uv lock
           | --check 2>/dev/null; then uv lock; fi
           | 
           | Your new version no longer has the bug we are talking about.
           | I don't know why you are trying to pretend it was never there
           | though?
        
             | nickjj wrote:
             | > Your new version no longer has the bug we are talking
             | about. I don't know why you are trying to pretend it was
             | never there though?
             | 
             | I'm not sure I understand what you mean?
             | 1. I posted the article last week on my site         2. I
             | noticed it was on HN today (yay)         3. I looked at the
             | parent's comment         4. The parent's description isn't
             | what happens with the original code         5. I made the
             | comment you're replying to on HN to address their concerns
             | and included a refactored version of the original condition
             | for clarity then said I pushed the updates         6. I
             | pushed the updates to both git and my site so both match up
             | 
             | There's nothing to pretend about and there's no bug because
             | both versions of the code do the same thing, the 2nd
             | version is just easier to read and requires less `uv`
             | knowledge to know what happens when `uv lock` runs with an
             | invalid lock file. The history is in the HN comment I wrote
             | and git history.
             | 
             | It doesn't make sense to leave the original code in the
             | blog post and then write a wall of text to explain how it
             | worked fine but here's a modified version for clarity. Both
             | versions of the code have the same outcome which is
             | ensuring there's a valid lock file before syncing.
             | 
             | What would you have done differently? I saw feedback, saw
             | room for improvement, left an audit trail in the comments
             | and moved on.
             | 
             | Here's the commits https://github.com/nickjj/docker-flask-
             | example/commit/d1b7b9... and
             | https://github.com/nickjj/docker-django-
             | example/commit/a12e2... btw.
        
               | remram wrote:
               | > The parent's description isn't what happens with the
               | original code
               | 
               | Yes, it is: both gchamonlive and myself pointed out that
               | if your lock file exists and is out of date, your
               | (previous) script would silently update it before
               | installing. This would happen because `uv lock --check`
               | would return false, triggering the call to `uv lock`.
               | 
               | Your new version no longer does that, because you removed
               | `! uv lock --check` from the condition.
        
       | bsenftner wrote:
       | I'd like to see a security breakdown of uv versus pip versus
       | conda versus whatever fashionable package manager I've not heard
       | of yet.
       | 
       | Speed is okay, but security of a package manager is far more
       | important.
        
         | Bengalilol wrote:
         | uv is generally more secure than pip. It resolves dependencies
         | without executing arbitrary code, verifies package hashes by
         | default, and avoids common risks like typosquatting and code
         | execution during install. It's also faster and more
         | reproducible.
         | 
         | https://chaitalks.tech/uv-a-modern-python-package-manager-in...
         | 
         | https://docs.astral.sh/uv/pip/compatibility/
        
           | glaucon wrote:
           | I'd be interested to know under what circumstances pip
           | executes arbitrary code while resolving dependencies ... how
           | does that work ?
           | 
           | And while I'm here ... how does uv go about mitigating
           | typosquatting risks ? I could imagine how it might issue
           | warnings if you perhaps it notices you requesting "dlango",
           | which would work OK for the top 10% but are you suggesting
           | there's some more general solution built into uv ?
           | 
           | I did a quick search but 'typosquatting' is not an easy
           | string to cut through.
        
             | alexchamberlain wrote:
             | For a source package based on setup tools, setup.py is
             | executed with a minimal environment and can run arbitrary
             | code.
        
               | ericvsmith wrote:
               | You can (and should!) tell pip not to do this with '--
               | only-binary=:all:'. Building from source is a lousy
               | default.
        
               | soulofmischief wrote:
               | Requiring increasingly long arcane incantations in the
               | name of backwards compatibility is a terrible design
               | philosophy and introduces security fatigue. Most users
               | will not use aliases, and it's poor security posture to
               | ask them to.
               | 
               | Given how often the python community already deals with
               | breaking changes, it shouldn't be much different for pip
               | to adopt saner defaults in a new major version.
        
               | ericvsmith wrote:
               | While I agree, pip has very strong backward compatibility
               | requirements. I'm not sure why, maybe because people tend
               | to upgrade it without considering the consequences.
        
             | un_ess wrote:
             | a)"Thanks to backwards compatibility, a package offered
             | only as a source distribution and with the legacy setup.py
             | file for configuration and metadata specification will run
             | the code in setup.py as part of the installation."
             | https://blog.phylum.io/python-package-installation-attacks/
             | 
             | b) pip now has an option _not_ to run arbitrary code by
             | disallowing source distributions, by passing --only-binary
             | :all:
             | 
             | "By default, pip does not perform any checks to protect
             | against remote tampering and involves running arbitrary
             | code from distributions. It is, however, possible to use
             | pip in a manner that changes these behaviours, to provide a
             | more secure installation mechanism."
             | https://pip.pypa.io/en/stable/topics/secure-installs/
        
             | db48x wrote:
             | To install a package and its dependencies, you need the
             | list of dependencies. This metadata is not always
             | statically available!
             | 
             | Python packages are often just a zip file full of py files,
             | with one of them called 'setup.py'. Running this file
             | installs the package (originally using [distutils](https://
             | docs.python.org/3.9/install/index.html#install-index)).
             | This installation may fail if dependencies are not present,
             | but there's no method provided for installing those
             | dependencies. You're supposed to read the error message, go
             | download the source for the missing dependencies, then run
             | their setup.py scripts to install them.
        
               | badmintonbaseba wrote:
               | How does uv get around this?
        
               | bckr wrote:
               | Don't think it does. You see the import errors when you
               | run your code and you add the requirements to your
               | project.
        
               | nonethewiser wrote:
               | because instead of running setup.py it directly fetches
               | the specified dependencies
        
               | db48x wrote:
               | I don't know; I've only just heard of uv. I know about
               | the packaging problem because I've had to deal with that
               | nonsense a few times.
        
         | diggan wrote:
         | > security breakdown of uv versus pip versus conda versus
         | whatever fashionable package manager
         | 
         | In the end, every package manager (so far at least) download
         | and runs untrusted (unless you've verified it manually) 3rd
         | party code. Whatever the security difference is between uv and
         | pip implementation-wise is dwarfed compared to if you haven't
         | found a way of handling untrusted 3rd party code yet.
        
       | 0xbadcafebee wrote:
       | Some thoughts on the patterns here:
       | 
       | - Removing requirements.txt makes it harder to track the high-
       | level deps your code requires (and their install options/flags).
       | Typically requirements.txt should be the high level requirements,
       | and you should pass them to another process that produces pinned
       | versions. You regenerate the pinned versions/deps from the
       | requirements.txt, so you have a way to reset all dependencies as
       | your core ones gain or lose nested dependencies.
       | 
       | - _+COPY --from=ghcr.io /astral-sh/uv:0.7.13 /uv /uvx
       | /usr/local/bin/_ seems useful, but the upstream docker tag could
       | be repinned on a different hash, causing conflicts. Use the hash,
       | or use a different way to stage your dependencies and copy them
       | into the file. Whenever possible, confirm your artifacts match
       | known hashes.
       | 
       | - Installing into the container's _/ home/project/.local_ may
       | preserve the uv pattern, but it's going to make a container
       | that's harder to debug. Production containers (if not _all_
       | containers) should install files into normal global paths so that
       | it 's easy to find the, reason about them, and use standard tools
       | to troubleshoot. This allows non- _uv_ users to diagnose the
       | application running, and removes extra abstraction layers which
       | create unneeded complexity.
       | 
       | - _+RUN chmod 0755 bin /_ && bin/uv-install* - using scripts
       | makes things easier to edit, but it makes it harder to understand
       | what's going on in a container, because you have to run around
       | the file tree reading files and building a mental map of
       | execution. Whenever possible, just shove all the commands into
       | RUN lines in the Dockerfile. This allows a user to just view the
       | Dockerfile and know the entire execution without extra effort. It
       | also removes some complexity in terms of checking out files,
       | building Docker context, etc.
       | 
       | - Try to avoid _docker compose_ and other platform-constrained
       | tools for the running of your tests, for the freezing of
       | versions, etc. You SDLC should first be composed of your build
       | tools /steps using just native tools/environments. Then on top of
       | that should go the CI tools. This separation of "dev/test
       | environment" from CI allows you to take your "dev/test
       | environment" and run it on _any CI platform_ - Docker Compose,
       | GitHub Actions, CircleCI, GitLab CI, Jenkins, etc - without
       | modifying the  "dev/test environment" tools or workflow.
       | Personally I have a _dev.sh_ that sets up the dev environment,
       | _build.sh_ to run any build steps, _test.sh_ to run all the test
       | stuff, _ci.sh_ to run ci /cd specific stuff (it just calls the
       | CI/CD system's API and waits for status), and _release.sh_ to cut
       | new releases.
        
         | IgorPartola wrote:
         | Agreed, custom scripts are great for the person who wrote them
         | and/or uses them all the time but I much prefer to add as
         | little veneer over upstream tools as possible lest I get
         | messages like "hey how do I actually restart this process/get
         | these logs/upgrade this package?"
         | 
         | Only thing I'm not sure about: why is having your list of
         | requirements in requirements.txt vs project.toml? Isn't it just
         | one file vs another?
        
         | jffry wrote:
         | > Removing requirements.txt makes it harder to track the high-
         | level deps your code requires
         | 
         | The very first section of the article talks about replacing
         | requirements.txt with pyproject.toml which contains a similar
         | high-level list of deps
        
         | cpburns2009 wrote:
         | I find it's clearer to store all pinned dependencies in
         | requirements.txt using pip-compile (or pip freeze). There's no
         | finagling in trying to determine which file contains the
         | dependency snapshot for installing an application. High level
         | dependencies can be defined in requirements.in or
         | pyproject.toml.
        
           | greener_grass wrote:
           | If you write out `requirements.txt` by hand, do you also need
           | to resolve deps and transitive deps by hand?
           | 
           | This is what pushed me to use Poetry.
        
             | jerrygenser wrote:
             | You do not write requirements by hand. You write
             | requirements.in and uv pip-compile to requirements.txt
        
               | greener_grass wrote:
               | Does `pip-compile` solve deps and transitive deps?
               | 
               | I thought it only locks down hashes?
        
               | jerrygenser wrote:
               | Yes it does. And it will fail if there is a combination
               | that is not resolvable.
        
             | cpburns2009 wrote:
             | I write my high level dependencies by hand in a
             | "requirements.in" for applications, and in "pyproject.toml"
             | for libraries.
             | 
             | A simple "requirements.in" I did over this weekend was a
             | single dependency:                   miniboss >=0.4, <0.5
             | 
             | And used pip-compile to pin all transitive dependencies:
             | pip-compile -o requirements.txt requirements.in
             | 
             | This generated a "requirements.txt" with 14 dependencies
             | with pinned versions:                   attrs==25.3.0
             | ...13 more dependencies
             | 
             | It's then only a matter of running "pip install -r
             | requirements.txt" in the venv for my "application" (wrapper
             | scripts for Docker).
             | 
             | I've largely settled on this scheme for work and person
             | projects because it's simple (only dev dependency is pip-
             | tools or uv), and it doesn't tie me to a particular Python
             | project management tool (pipenv, pdm, poetry, etc.).
        
           | 0xbadcafebee wrote:
           | That's what I meant, I just call my requirements.in
           | requirements.txt, and the pinned versions go in
           | requirements.txt.frozen or pyproject.toml. As long as there's
           | one file with high level and one file with pinned
        
       | 0xblinq wrote:
       | 2025 and python packaging and dependencies management is still a
       | mess.
        
         | incognito124 wrote:
         | It's only a mess because not everyone has adopted uv yet (IMO)
        
           | soulofmischief wrote:
           | Python package management is a classic example of xkcd #927,
           | and the community cannot be blamed for developing a Pavlovian
           | response when it comes to yet-another-package-manager that
           | promises to be the final solution.
        
           | greener_grass wrote:
           | UV looks very promising but I can assure you, if everyone
           | adopted it tomorrow we would see a long tail of scenarios
           | that UV does not work well for.
        
             | bckr wrote:
             | I would love to see that happen and see how astral
             | responds. I would love to see uv get built into Python 4
        
             | tempest_ wrote:
             | I am perfectly content for a 90% solution, we should not
             | let the perfect be the enemy of the good.
        
           | 0xblinq wrote:
           | It's a mess because there's no first party, properly thought
           | out and working solution.
           | 
           | So every year we get a new "new way" to do it. Like that
           | xkcd... this time this is the standard that will work!
        
         | ericfrederich wrote:
         | Yes, it's a mess (New: now with Rust!)
        
         | db48x wrote:
         | Yep. The lesson is to get this right early in your language
         | design. Do not punt until version 2.0. Think twice before
         | putting the package/module/whatever metadata in an executable
         | script. If you do decide to do that, think a third time. It
         | works out better for some languages (like Common Lisp) than
         | others (like Python).
        
         | ikrenji wrote:
         | never had a problem with dependencies. how is it a mess? you
         | have requirements.txt and venv per project. doesn't get easier
         | than that
        
       | b0a04gl wrote:
       | been using uv on a flask container and honestly the diff in build
       | times is just boringly huge. not even the speed tho, it's how
       | predictable things get. no stupid "why did pip install this
       | version" moments. you write a pyproject.toml, freeze with uv
       | lock, done.
       | 
       | >In docker you can just raw COPY pyproject.toml uv.lock* . then
       | run uv sync --frozen --no-install-project. this skips your own
       | app so your install layer stays cacheable. real ones know how
       | painful it is to rebuild entire layers just cuz one package
       | changed.
       | 
       | >UV_PROJECT_ENVIRONMENT=/home/python/.local bypasses venv. which
       | means base images can be pre-warmed or shared across builds.
       | saves infra cost silently. just flip UV_COMPILE_BYTECODE=1 and
       | get .pyc at build.
       | 
       | > It kills off mutable environments. forces you to respect
       | reproducibility. if your build is broken, it's your lockfile's
       | fault now. accountability becomes visible
        
       | cmiller1 wrote:
       | I have software people use up on pypi and I'd love to switch to
       | uv for personal use to benefit from the improved speed but I'd
       | need some guarantees that things work EXACTLY how they work on
       | pip or that I could run them concurrently. If I put instructions
       | up for users to "just run pip install xxx" I need to know that if
       | they see any errors I can see those too for
       | debugging/troubleshooting.
        
         | oblvious-earth wrote:
         | It does not work EXACTLY how pip works, big differences are
         | covered here: https://docs.astral.sh/uv/pip/compatibility/
         | 
         | Some of these are uv following the standards while pip is still
         | migrating away from legacy behavior, some of these are design
         | choices that uv has made, because the standard is underdefined,
         | it's a tool specific choice, or uv decided not to follow the
         | standards for whatever reason.
        
       | ericfrederich wrote:
       | I am totally against Python tooling being written in a language
       | other than Python. I get that C extensions exist and for the most
       | part Python is synonymous with CPython.
       | 
       | I think 2 languages are enough, we don't need a 3rd one that
       | nobody asked for.
       | 
       | I have nothing against Rust. If you want a new tool, go for it.
       | If you want a re-write of an existing tool, go for it. I'm
       | against it creeping into an existing eco-system for no reason.
       | 
       | A popular Python package called Pendulum went over 7 months
       | without support for 3.13. I have to imagine this is because
       | nobody in the Python community knew enough Rust to fix it. Had
       | the native portion of Pendulum been written in C I would have
       | fixed it myself.
       | 
       | https://github.com/python-pendulum/pendulum/issues/844
       | 
       | In my ideal world if someone wanted fast datetimes written in
       | Rust (or any other language other than C) they'd write a proper
       | library suitable for any language to consume over FFI.
       | 
       | So far this Rust stuff has left a bad taste in my mouth and I
       | don't blame the Linux community for being resistant.
        
         | moolcool wrote:
         | > I am totally against Python tooling being written in a
         | language other than Python
         | 
         | I will be out enjoying the sunshine while you are waiting for
         | your Pylint execution to finish
        
           | throwawaysleep wrote:
           | Linting is the new "compiling!"
        
           | carlhjerpe wrote:
           | Linting and type checking are very CPU intensive tasks so I
           | would excuse anyone implementing those types of tools in
           | $LANG where using all CPU juice matters.
           | 
           | I can't help but think uv is fast not because it's written in
           | Rust but because it's a fast reimplementation. Dependency
           | solving in the average Python project is hardly
           | computationally expensive, it's just downloading and
           | unpacking packages with a "global" package cache. I don't see
           | why uv couldn't have been implemented in Python and be 95% as
           | fast.
           | 
           | Edit: Except implementing uv in Python requires shipping a
           | Python interpreter kinda defeating some of it's purpose of
           | being a package manager able to install Python as well.
        
             | nonethewiser wrote:
             | You also have to factor in startup time and concurrency.
             | Caching an SAT solvers can't get python to 95% of uv.
        
         | hoppp wrote:
         | I love rust but I tend to agree, python tooling should be
         | maintainable by the community without learning a new language.
         | 
         | However rust is a thousand times faster than python.
         | 
         | At the end, if you don't like it don't use it.
        
         | nchmy wrote:
         | What, exactly, is your objection to using rust (or any non-
         | python/C language) for python tooling? You didn't actually give
         | any reasons
        
           | jftuga wrote:
           | I believe he alluded to it here...
           | 
           | "I have to imagine this is because nobody in the Python
           | community knew enough Rust to fix it. Had the native portion
           | of Pendulum been written in C I would have fixed it myself."
        
             | ericfrederich wrote:
             | Correct. There better be a damn good reason to add another
             | language to the ecosystem other than it's that particular
             | developer's new favorite language.
             | 
             | Is there anything being done in uv that couldn't be done in
             | Python?
        
               | nyzs wrote:
               | speed
        
               | ericfrederich wrote:
               | I don't see any meaningful speedup. The 10x claims are
               | not reproducible. He's also comparing it to the much
               | older style of requirements.txt projects and not a poetry
               | project with a lockfile.
               | 
               | I detailed this in another comment but pip (via
               | requirements.txt): 8.1s, poetry: 3.7s, uv: 2.1s.
               | 
               | Not even 10x against pip and certainly not against
               | poetry.
        
               | nchmy wrote:
               | You must be holding it wrong, because everyone else raves
               | about uv
        
               | cpburns2009 wrote:
               | Usually uv pip is only about x2 as fast as regular pip
               | for me. Occasionally I'll have some combination of
               | dependencies that will cause pip to take 2-5 minutes to
               | resolve that uv will handle in 10-20 seconds.
        
               | nchmy wrote:
               | They said "no meaningful speedup". 2x is meaningful
        
               | cpburns2009 wrote:
               | The impact of a 2x speedup is relative. For a quick test
               | on one of my projects it's 10 seconds with pip and 4
               | seconds with uv. That's roughly in line with my previous
               | testing. It's a nice minor speedup on average. It really
               | shines when pip does some non-optimal resolving in the
               | background that takes a minute or more.
        
               | bckr wrote:
               | How complex are the requirements for this project?
        
               | ericfrederich wrote:
               | https://github.com/nickjj/docker-django-
               | example/blob/bbe3486...
        
               | bckr wrote:
               | I see. I encourage you to try it with larger projects and
               | see if it makes a difference.
               | 
               | That said, the speed is only one reason I use it. I find
               | its ergonomics are the best in the Python tools I've
               | tried. For example it has better dependency resolution
               | than poetry in my estimation, and you can use the uv run
               | ---with command to try things before adding them to your
               | environment.
        
               | nchmy wrote:
               | How many people are digging into and contributing to any
               | python tooling? How is C meaningfully more accessible
               | than rust? Plenty of people (yet also a significant
               | minority overall) write each of them.
               | 
               | > Is there anything being done in uv that couldn't be
               | done in Python?
               | 
               | Speed, at the very least.
               | 
               | You could just ignore uv and use whatever you want...
        
               | gamegod wrote:
               | > How is C meaningfully more accessible than rust
               | 
               | They've been teaching C in universities for like 40 years
               | to every Computer Science and Engineering student. The
               | number of professionally trained developers who know C
               | compared to Rust is not even close. (And a lot of us are
               | writing Python because it's easy and productive, not
               | because we don't know other languages.)
        
               | nchmy wrote:
               | If c + Python is so wonderful and so ubiquitous, why
               | hasn't someone already created uv in C?
               | 
               | Ps the government and others have all recommended moving
               | from C/C++ to Rust... It's irrelevant whether or not
               | that's well-founded - it simply is.
               | 
               | And plenty of other cli tools have been successfully and
               | popularly ported to Rust.
        
               | hobofan wrote:
               | To quote Movie Mark Zuckerberg from The Social Network:
               | 
               | > If Python developers were the inventors of uv - they'd
               | have invented uv
        
         | masklinn wrote:
         | According to the very link you provide, the sticking point was
         | a dependency which does not use rust, and the maintainer
         | probably being busy.
         | 
         | I updated a rust-implemented wheel to 3.13 compat myself and
         | literally all that required was bumping pyo3 (which added
         | support back in June) and adding the classifier. Afaik
         | cryptography had no trouble either, iirc what they had to wait
         | on was a 3.13 compatible cffi .
        
           | ericfrederich wrote:
           | The PR which enabled 3.13 did have changes to Rust code.
           | 
           | https://github.com/python-pendulum/pendulum/pull/871
        
             | masklinn wrote:
             | Because they did more than _just_ support 3.13:
             | 
             | > I'm sure some of the changes are going too far. We are
             | open to revert them if there's an interest from maintainers
             | to merge this PR :)
             | 
             | Notably they bumped the bindings ("O3") for better
             | architecture coverage, and that required some renaming as
             | 0.23 completed an API migration.
        
         | patcon wrote:
         | Upvoting for interesting/important/sympathetic perspective, but
         | am very much in disagreement
        
           | mh- wrote:
           | Offtopic, but thank you. I really wish this way of treating
           | up/downvotes was more widespread. Down _should_ mean it doesn
           | 't contribute to the conversation, not that you disagree with
           | their opinion.
        
         | guardian5x wrote:
         | you say "I'm against it creeping into an existing eco-system
         | for no reason.", while you ignore that there is at least one
         | good reason: A lot better performance.
        
           | ericfrederich wrote:
           | The 10x performance wasn't mentioned in the article at all
           | except the title.
           | 
           | I watched the video and he does mention it going from 30s to
           | 3s when switching from a requirements.txt approach to a uv
           | based approach. No comparison was done against poetry.
           | 
           | I am unable to reproduce these results.
           | 
           | I just copied his dependencies from the pyproject.toml file
           | into a new poetry project. I ran `poetry install` from within
           | Docker (to avoid using my local cache) `docker run --rm -it
           | -v `pwd`:/work python:3.13 /bin/bash` and it took 3.7s
           | 
           | I did the same with an empty repo and a requirements.txt file
           | and it took 8.1s.
           | 
           | I also did through `uv` and it took 2.1s.
           | 
           | Better performance?, sure. A lot better performence?, I can't
           | say that with the numbers I got. 10x performance?...
           | absolutely not.
           | 
           | Also, this isn't a major part of anybody's workflow. Docker
           | builds happen typically on release. Maybe when running tests
           | during CI/CD after the majority of work has been done
           | locally.
        
             | mixmastamyk wrote:
             | I personally don't care about the performance:
             | 
             | https://news.ycombinator.com/item?id=44359183
             | 
             | I agree it would be better if it was in Python but pypa
             | _did not_ step up, for decades! On the other hand, it is
             | not powershell or ruby, it is a single deployed executable
             | that works. I find that acceptable if not perfect.
        
           | cjaybo wrote:
           | Better performance than C? This is news to me
        
             | kibwen wrote:
             | There are cases where single-threaded Rust and C are faster
             | than each other, though usually only by single-digit
             | percentages. But Rust is so much easier to parallelize than
             | C that it isn't even funny.
        
         | greener_grass wrote:
         | Rust offers a feature-set that neither Python nor C has. If
         | Rust is the right tool for the job, I would rather the code be
         | written in Rust. Support has more to do with incentive
         | structures than implementation language.
        
         | Gabrys1 wrote:
         | I, on the other hand, don't care what language the tools are
         | written in.
         | 
         | I do get the sentiment that a user of these tools, being a
         | Python developer could in theory contribute to them.
         | 
         | But, if a tool does its job, I don't care if it's not "in
         | Python". Moreover, I imagine there is a class of problems with
         | the Python environment setup that'd break the tool that could
         | help you fix it if the tool itself is written in Python.
        
           | HelloNurse wrote:
           | It is well known, and not Python-specific, that using a
           | different language/interpreter for development tools
           | eliminates large classes of bootstrapping complications and
           | conflicts.
           | 
           | If there are two versions of X, it becomes possible to use
           | the wrong one.
           | 
           | If a tool to manage X depends on X, some of the changes that
           | we would like the tool to perform are more difficult,
           | imperfect or practically impossible.
        
         | manojlds wrote:
         | Did you even read the issue that you pointed to? It's not even
         | the rust part that was the issue.
        
         | bodge5000 wrote:
         | In theory, I can get behind what your saying, but in practice I
         | just haven't found any package manager written in Python to be
         | as good as uv, and I'm not even talking about speed. uv as I
         | like it could be written in Python, but it hasn't been
        
           | RamblingCTO wrote:
           | I really dig rye, have you tried that?
        
             | gschizas wrote:
             | rye is also written in Rust and it's being replaced by uv.
             | 
             | From its homepage: https://rye.astral.sh/
             | 
             | > If you're getting started with Rye, consider uv, the
             | successor project from the same maintainers.
             | 
             | > While Rye is actively maintained, uv offers a more stable
             | and feature-complete experience, and is the recommended
             | choice for new projects.
             | 
             | > Having trouble migrating? Let us know what's missing.
        
             | Kwpolska wrote:
             | It's also Rust.
        
         | mvieira38 wrote:
         | Or maybe the community will embrace Rust as it is
         | implemented... There's no reason to think because you or the
         | current gen of Python devs are focused on C then the next gen
         | or further will too.
        
         | lvl155 wrote:
         | I understand this sentiment. Part of it was people trying to
         | build up their cv for Rust. On the other hand, some
         | tools/libraries in Python were old. Take pandas for example, it
         | was not good for modern use. We desperately needed something
         | like polars and even that is being outpaced by current trends.
        
           | theLiminator wrote:
           | Curious what you see as outpacing polars, hybrid
           | analytical/streaming query engines?
        
         | sgarland wrote:
         | Python is my favorite language, but I have fully embraced uv.
         | It's so easy, and so fast, that there is nothing else remotely
         | close.
         | 
         | Need modern Python on an ancient server running with EOL'd
         | distro that no one will touch for fear of breaking everything?
         | uv.
         | 
         | Need a dependency or two for a small script, and don't want to
         | hassle with packaging to share it? uv.
         | 
         | That said, I do somewhat agree with your take on extensions. I
         | have a side project I've been working on for some years, which
         | started as pure Python. I used it as a way to teach myself
         | Python's slow spots, and how to work around them. Then I
         | started writing the more intensive parts in C, and used ctypes
         | to interface. Then I rewrote them using the Python API. I
         | eventually wrote so much of it in C that I asked myself why I
         | didn't just write all of it in C, to which my answer was
         | "because I'm not good enough at C to trust myself to not blow
         | it up," so now I'm slowly rewriting it in Rust, mostly to learn
         | Rust. That was a long-winded way to say that I think if your
         | external library functions start eclipsing the core Python
         | code, that's probably a sign you should write the entire thing
         | in the other language.
        
         | coldtea wrote:
         | > _I am totally against Python tooling being written in a
         | language other than Python._
         | 
         | Cool story bro.
         | 
         | I'm totally against Python tooling being in dismal dissaray for
         | 30 years I've been using the language, and if it takes some
         | Rust projects to improve upon it, I'm all for it.
         | 
         | I also not rather have the chicken-and-egg dependency issue
         | with Python tooling written in Python.
         | 
         | > _A popular Python package called Pendulum went over 7 months
         | without support for 3.13. I have to imagine this is because
         | nobody in the Python community knew enough Rust to fix it. Had
         | the native portion of Pendulum been written in C I would have
         | fixed it myself._
         | 
         | Somehow the availability and wide knowledge of C didn't make
         | anyone bother writing a datetime management lib in C and making
         | it as popular. It took those Pendulum Rust coders.
         | 
         | And you could of course use pytz or dateutil or some other,
         | but, no, you wanted to use the Rust-Python lib.
         | 
         | Well, when you start the project yourself, you get to decide
         | what language it would be in.
        
         | nonethewiser wrote:
         | >I am totally against Python tooling being written in a
         | language other than Python. I get that C extensions exist and
         | for the most part Python is synonymous with CPython.
         | 
         | >I think 2 languages are enough, we don't need a 3rd one that
         | nobody asked for.
         | 
         | Enough for what? The uv users dont have to deal with that. Most
         | ecosystems use a mix of language for tooling. It's not a detail
         | the user of the tool has to worry about.
         | 
         | >I'm against it creeping into an existing eco-system for no
         | reason.
         | 
         | It's much faster. Because its not written in Python.
         | 
         | The tooling is for the user. The language of the tooling is for
         | the developer of the tooling. These dont need to be the same
         | people.
         | 
         | The important thing is if the tool solves a real problem in the
         | ecosystem (it does). Do people like it?
        
         | ufmace wrote:
         | I appreciate this perspective, but I think building a tool like
         | uv in Rust is a good idea because it's a tool for managing
         | Python stuff, not a tool to be called from within Python code.
         | 
         | Having your python management tools also be written in python
         | creates a chicken-and-egg situation. Now you have to have a
         | working python install before you can start your python
         | management tool, which you are presumably using because it's
         | superior to managing python stuff any other way. Then you get a
         | bunch of extra complex questions like, what python version and
         | specific executable is this management tool using? Is the
         | actual code you're running using the same or a different one?
         | How about the dependency tree? What's managing the required
         | python packages for the installation that the management tool
         | is running in? How do you know that the code you're running is
         | using its own completely independent package environment? What
         | happens if it isn't, and there's a conflict between a package
         | or version your app needs and what the management tool needs?
         | How do you debug and fix it if any of this stuff isn't actually
         | working quite how you expected?
         | 
         | Having the management tool be a compiled binary you can just
         | download and use, regardless of what language it was written
         | in, blows up all of those tricky questions. Now the tool
         | actually does manage everything about python usage on your
         | system and you don't have to worry about using some separate
         | toolchain to manage the tool itself and whether that tool
         | potentially has any conflicts with the tool you actually wanted
         | to use.
        
         | peterhadlaw wrote:
         | I had a situation, admittedly niche, where some git based
         | package dependency wasn't being updated properly (tags vs.
         | commit hashes) and thanks to poetry being written in Python I
         | was able to quickly debug and solve the problem. I think it's
         | more a matter of core functionality (that affects everyone) vs.
         | more esoteric or particular use cases (like dataframe
         | libraries) that make sense to FFI.
        
         | kzrdude wrote:
         | > I think 2 languages are enough, we don't need a 3rd one that
         | nobody asked for.
         | 
         | Look at the number of stars ruff and uv got on github. That's a
         | meteoric rise. So they were validated with ruff, and continued
         | with uv, this we can call "was asked for".
         | 
         | > I'm against it creeping into an existing eco-system for no
         | reason.
         | 
         | It's not no reason. A lot of other things have been tried. It's
         | for big reasons: Good performance, and secondly independence
         | from Python is a feature. When your python managing tool does
         | not depend on Python itself, it simplifies some things.
        
         | 0x457 wrote:
         | > I'm against it creeping into an existing eco-system for no
         | reason.
         | 
         | There is a reason: tools that exist today are awful and
         | unusable if you ever wrote anything other than python _.
         | 
         | _ : I'm saying it because the only way I can see someone not
         | realizing it is that they have never seen anything better.
         | 
         | Okay, maybe C and C++ have even worse tooling in some areas,
         | but python is still the top language of having the worst
         | tooling.
        
       | bodge5000 wrote:
       | I really like uv, easily my favourite Python package manager, but
       | the last time I tried it with Docker and Django it was a
       | nightmare, particularly with environment variables for some
       | reason. Maybe this will help
        
       | ing33k wrote:
       | UV just works. Easily one of the best things to happen to Python
       | packaging in years.
        
       | nodesocket wrote:
       | Is there no apt repo to install uv? My Docker builds using pip
       | take around 2 minutes, not sure the juice is worth the squeeze
       | upgrading to uv.
       | 
       | Current Dockerfile pip is as simple as:                   COPY
       | --chown=python:python requirements.txt .         RUN pip install
       | --no-cache-dir --upgrade pip && \             pip install --no-
       | cache-dir --compile -r requirements.txt         COPY
       | --chown=python:python . .         RUN python -m compileall -f .
        
         | cpburns2009 wrote:
         | uv is not in Debian's or Ubuntu's apt repositories to my
         | knowledge. What I do instead because I don't like piping shell
         | scripts from URLs is "pip install --upgrade pip uv", and then
         | run "uv pip ...".
        
         | zanie wrote:
         | Installation in Docker just looks like                   COPY
         | --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
         | 
         | https://docs.astral.sh/uv/guides/integration/docker/#using-u...
         | 
         | (We'd recommend pinning the version or SHA in production)
        
         | remram wrote:
         | With a cache directory that can be 0 minute.
         | RUN --mount=type=cache,target=/root/.cache/pip pip install ...
        
       | ndr wrote:
       | PSA careful replacing `pip` with `uv` thinking it's a drop-in
       | replacement.
       | 
       | By default `uv` won't generate `pyc` files which might make your
       | service much slower to start.
       | 
       | See https://docs.astral.sh/uv/reference/settings/#pip_compile-
       | by...
        
         | elyall wrote:
         | uv's guide for use in containers is a better reference for
         | this:
         | https://docs.astral.sh/uv/guides/integration/docker/#compili...
        
           | ndr wrote:
           | Agreed if you're going there from the start.
           | 
           | I stumbled on this by porting something using that was
           | previously pip, and that surprisingly different default has
           | been a foot gun.
        
       | rowanseymour wrote:
       | I got excited about poetry a few years back because it seemed
       | that the community might be finally able to rally around _one_
       | packaging solution. My favorite thing about Go is that there
       | isn't a shiny new packaging tool every year.. just go.mod. uv
       | definitely looks cool and the performance is very impressive..
       | but should I switch all my projects over? Are you going to be
       | around in 5 years time?
        
         | frankwiles wrote:
         | It takes 5 minutes to switch most projects. And less to go back
         | to pip. Seems silly to waste a bunch of wall time when using
         | pip when there is a super easy alternative that doesn't have
         | high switching costs.
        
           | rowanseymour wrote:
           | Ok but that's really not the cost for a company that's been
           | using tool X for years, accumulated some expertise in that,
           | built their own tooling on top of it etc.
        
         | bckr wrote:
         | Just try it. The hype is real. I stopped using pyenv, pyenv-
         | virtualenv, poetry, and native pip because uv is just that
         | sticky.
         | 
         | Just install it and try running something using the ---with
         | flag. That's where I became intrigued.
        
       | adolph wrote:
       | What is Astral's economic model? This is my primary hesitation to
       | full adoption. Is Astral's future the same story as Anaconda?
        
         | steveklabnik wrote:
         | https://news.ycombinator.com/item?id=44358216
        
           | adolph wrote:
           | Ok, same story as Anaconda but they haven't gotten as far.
        
       | jdboyd wrote:
       | What originally convinced me to try uv was the promise of faster
       | container builds, and it certainly delivered on that.
       | 
       | As someone who usually used platform pythons, despite advise
       | against that, uv is now what got me to finally stop doing so.
        
       | kh_hk wrote:
       | Just generate a requirements.txt with uv, ship that with docker,
       | and then there's no need for all this dance
        
       ___________________________________________________________________
       (page generated 2025-06-24 23:01 UTC)