Python setuptools v50 breaks pip installation on Debian/Ubuntu
github.com
github.com
pip, requirements.txt, and venv are madness. Look at how much suffering they cause! I was dealing with (different) pip issues today myself, and it's not an infrequent occurrence.
Rust's Cargo is a good reference point.
Still, both poetry and pipenv work better for me than conda because they do have a lockfile mechanism which I find to be essential.
I've also collaborated and discussed with hundreds of other conda users, and except for speed and the lack of official lockfiles[0], there were no complaints, only praise.
[0] The functionality was always available, but not straightforward and not named as such.
- Conda - Pipenv - Pip itself - Poetry
Which is quite symptomatic.
It’s odd how open source packages that big companies use can be reliant on so few people.
Edit: although checking the pip github, there more people committing so maybe I got something wrong, but there is far more activity starting around 2019.
Is there any good overview which packaging solution has which properties, and will work in specific situations?
My last attempt at using pip this very morning to install a bunch of deps crashed and burn with an obscure python stack dump.
Oh, and, in your taxonomy of partial and inadequate solutions, you forgot to mention anaconda, possibly the most invasive package management solution I have ever seen, which exhibits the same problems pip does, i.e. where each install is a total crapshoot.
I wish you could just install all packages (in multiple versions!) into one place like /usr/lib/pythoncache, and the module loader would then decide which package version to load at import time. You could define your dependencies as usual in reqirements.txt or pyproject.yaml. And you could use either pip or the system package manager to manage the global package cache.
Perhaps I'm just lucky but I never experienced any major issue with pip+venv. It always worked as advertised: create venv, activate venv, install/update pip locally, install packages listed in requirements.txt, and you're all set.
Which problems do you experience with pip?
If somebody else installs your package at a later time, pip will again install the latest fitting versions.
This install will be different from your install, or from other installs other people made in the meantime.
If a newer version if a dependency breaks your package because it has a backwards-incompatible change, your package won't work - and it will not show up in your testing because pip sees the existing, already installed packages and will think they are new enough. So, you'll find "it works for me".
A good examples are packages like python-opencv which break on python2 because the versioning implies they are backwards-compatible but they (or their given dependencies) use syntax which is not supported by python2.
And these things tend to snowball quickly because the number of packages which other packages depend on tends to grow exponentially, without a real upper bound.
And while there is lots of annoyment and cursing about package managers, I think a huge part of the problem is a cultural issue in the python community because people accept and create libraries with backwards-incompatible changes without marking that in the version numbers.
Obligatory link to Rich Hickey's brilliant talk on how to do it better: https://www.youtube.com/watch?v=oyLBGkS5ICk
And, as we are at talking about the future, how would you manage native extension modules which are written in Rust?
This issue was known for quite a while, and IIRC more recent Debian (and thus Ubuntu) versions contain a fix. But the change was not backported for other reasons I am not personally familar with.
Sorry, just joking (I hope). Also, we didn't have to suffer UTF-16.
Basically it seems the Debian python does not add site-packages to its search path, but manually installing pip (via get-pip.py) installs things there. And this all worked before, because Debian shipped a patched distutils, but now setuptools vendors that.
That would make it a complete non-issue for me. The global Python installation is managed by Debian. Pip is only ever used inside a venv.
IMO neither party really did anything wrong, the situation is caused by users depending on implementation detail that used to work but no longer does. I would’t say it’s a non-issue though. There are so many instructions out there that recommend `sudo pip install -U pip setuptools`, and Python packaging developers take all the blame when the spacebar no longer overheats the CPU. This situation is, in some way, a microcosm of how people get the impression that “Python packaging is a mess”.
Slightly exaggeratting, but in the same way, you could say "the designers of the C language did nothing wrong with creating that much undefined behavior, any competent programmer just needs to know what they can do and what not".
I am not blaming the developers of setuptools or pip which have very limited resources. But the solution they created so far is just not good enough, and its technical properties contribute to these problems. And other language environments clearly do it better.
Another aspect is that the community accepts libraries which break backward compatibility. This will become an ever increasing problem as soon as it becomes more frequent that programs rely on different libraries which in turn happen to have indirect dependencies which require conflicting versions of the same thing. There is no way to resolve that in Python. It would be much better that libraries which break backward compatibility would get a different name altogether each time, because in this case you could include the different libraries in the same program.
But Python's library authors don't, and this is probably because they want people to keep using their library by given the mere impression that it is compatible, while it is not. And this is just not honest communication. If you break compatibility, you should tell the users of your library. Or better even you don't break it in the first place.
Similarly, most software used to tell you in the README file to do a "make && make install", sometimes installing into /usr/local but not always. If a user followed these instructions and overwrote half of their distro-provided /usr/lib, things would break in the same way for the same reasons.
The solution is to not do that. Don't mix distro-provided files with things installed from other sources. (As for the problem of people providing bad instructions, I don't know the solution. Perhaps remind novices that all instructions come with an implicit context and may not apply fully to their specific case?)
Part of my day job is maintaining Python for an embedded OS. For self-evident reasons, software for the embedded OS needs to be cross compiled, and Python makes it impossible to cross-compile pip. Explicitly. Upstream's reaction is "you can't cross compile pip, you're on your own." To make matters worse, pip doesn't support non-native Python once it is built.
Of course, this is just the tip of the Python portability nightmare. Python is great and completely portable as long as your target is Linux running on x86_64 or aarch64. It has second-class support for Mac OS and Windows and there are private port projects for a few other system but they generally require heavy patching. For example, the Python code violates POSIX standards in favour of assuming that everything is Linux (hey guys, time_t is not a signed integer that can be used for arbitrary arithmetic!). Executing a whole lot of system code between fork() and exec() is always broken, although it appears to mostly work on Linux as long as you disable certain kernel features, and guess which code base relies on that?
Yeah. Pip is broken and now it's showing up on Debian too. Maybe it'll just get better by itself?
Lesson learned by that team: find a way to hard freeze the entire dependency tree or don’t use a tool.
More important than immutable containers is repeatable builds. I need to be able to go back to a version in 12 months and build the exact same output avec a few changes. Containers don’t help with that.
Docker containers builds are extremely repeatable. You run some software called Artifactory, which keeps the built versions of your artifacts, including base image tags. You store your applications in there, as well as the Docker containers that you built. If you need to go back a version, you pull it out of Artifactory. This is industry standard at this point.
Your comment makes no sense once you account for the fact that, as part of containerization security best practices, containers are continuously rebuilt to use the latest version of their base images and dependencies, and those images are then pushed to overwrite the latest release.
This isn't containerization best practice, this is just plain old Ops best practice that we've been doing for decades. The new part is doing it with containers and building immutable images rather than continuously muting state.
No, not really. You pull from image:latest, or debian, or debian:stable, or debian:stretch, or debian:stretch-slim. That's what you use as base images. The reason you do that is security. You need to incorporate security updates in your Docker images, and in order to ensure your images always incorporate the latest bug fixes and security updates you simply pull from the default base images expecting them to be continuously updated.
# In your CI pipeline, something like this:
$ cat Dockerfile
ARG release_tag
FROM baseimg:${release_tag}
# etc; i think the container metadata keeps the base version, or you can store it in the container with a RUN line
$ base_version_tag=$(./get-latest-versioned-tag.sh some-docker-repo:my_prefix/my_image:stable)
$ new_container_version=$(./make-new-version.sh)
$ docker build -t my_img:${new_container_version} --build-arg release_tag=${base_version_tag}
Then you run your tests on the new image, maybe do a gradual deployment, and can quickly roll back to the last base image or skip broken versions. Your own container version is always rolling-forward, and you can revert the base image version at any time.If you do this with a specific stable tree (like debian:8-stable or something) you should only get security updates, but if you use :latest, that's a total nightmare.
If you don't use Docker containers, use some other form of Immutable Infrastructure like Packer VM images. But those are huge and clunky, so you should really get used to Docker containers. You could instead make Linux packages, but they don't make for as Immutable a system as the OS state constantly changes, and packaging is more work in general.
If you allow the pip to make changes to things already present on your system, it make break expectations other parts of your OS may have.
And especially given Python's messy package-management story, combined with the less-than-ideal Python2 to 3 compatibility-story, transitioning a full OS and platform from Python 2 to Python 3 without breaking stuff is no small task.
I'm amazed it even works, and that's before you allow users to mess around with packages using pip.
I’ve been down most of the python installation and packaging rabbit holes, and this practice has served me well.
Maybe it’s more brittle than I’m imagining, but I’ve been doing this for years and have never had it break.
So you might need the latest python-opencv from pypi for work, and some desktop camera app needs the one from apt to not have broken dependencies.
> In short, packaged Debian/ubuntu Python installs things into dist-packages. site-packages is left alone in-case you like to build your own upstream python and install it. Debuntu overrides the distutils it ships to redirect installs to dist-packages. It gets blurry when you install pip from upstream. But it would call setuptools, which would use the patched distutils so things worked.
>
> Now setuptools vendors distutils this and always installs things into site-packages, which isn't on the packaged python interpreter path, so it can't find modules.
Now, who thinks that vendoring distutils is a good idea? And installing into the base system?
For instance I can't install mycli without removing calibre.
Of course it's most likely possible to get around that with virtualenv or whatever is used at the moment but that's a huge cognitive load just to run some apps.
Python is "broken" in that the C API does not maintain backwards compatibility. (See https://www.python.org/dev/peps/pep-0620/#the-c-api-blocks-c... for a proposed solution.)
RealSense is "broken" in that they don't provide builds for all the versions of Python their users use. But that's because "Officially, Librealsense is only currently supported up to Ubuntu 18.04 LTS"
Perhaps also your process is "broken" for depending only on pre-compiled builds; the published solution is to build from source. https://github.com/IntelRealSense/librealsense/issues/6296#i... says it's possible. Or "broken" for using an unsupported environment and expecting it will work.
Software in general is broken. It's amazing that it works as well as it does.
I tried that, it didn't work, and they keep closing issues about it instead of addressing it. They are a company with a 200B market cap, and it's already 5 months since the release of 20.04 and they still can't get their drivers straight.
People like me want to use their hardware. I don't have time to be debugging their drivers -- I have other work to do. I don't think it's unreasonable ask for them to support the latest LTS release of the most common Linux distribution in a timely manner with pre-compiled binaries that just work, in return for the $179 that I and tens of thousands of others paid for the device.
This isn't some open source community software that I'd be glad to pitch in some time and help debug. This is a driver for a device that I paid a for-profit company for and I expect timely support especially for an exceedingly common OS that I use.
Are you really just complaining about the version numbering? Ok, let's say they follow semantic versioning, and instead of calling it 3.8 they called it 4.0.
The package you use would still be broken.
Now what do you do?
Why don't you install Python 3.7 yourself and pip install the pre-built binary?
Remembering to start/stop virtualenv and remembering the names of your virtual envs is one too many steps
And how do I do that from inside an already running Emacs session? (Or Vscode?)
Needing to "prepare" an environment before you can work with it, prevents you from simply "jumping" into a task from an existing workflow.
Working with .NET, Rust, Node, go, Haskell, etc... I have no such issues.
This "venv"-requirement only applies to Python, and it's really the opposite of ergonomic. I really think it's about time the Python-community starts recognizing this.
Instead, I recommend just running the other executables in the bin directory. `./bin/python` will drop you into a Python REPL with all the packages installed in that virtualenv available. Your IDE should let you specify what Python interpreter to use for running tests and other integrations; give it the path to that Python executable and everything should Just Work. Likewise, if your package uses setuptools entrypoints, they'll be installed into the bin directory too, and you can just run them with no further configuration.
For what it's worth, Rust (for one) works pretty much the same way. The only difference is that Rust doesn't have a concept of dependencies installed system-wide, they're always handled per-project like venvs. As a result, they don't have decades of stale tutorials online telling people to do the wrong thing.
Now that I'm used to the subshell idea, I use the same concept across ruby, node, and python; it's almost always a better fit than what the usual packaging tools give you. I don't need `bundle exec` in ruby, for instance.
From VSCode I just use the integrated terminal to create the venv, vscode's default python extensions recognizes in-process venvs by default and uses them. (The way it installs tooling is broken for them, though, but that's a bug in the extension.)
Though it would be nice if Code integrated poetry so that just having a poetry-compatible pyproject.toml was sufficient.
> Working with .NET, Rust, Node, go, Haskell, etc... I have no such issues.
For most of those (Haskell with Cabal included), they have the same basic issue and solve it with a project file which is pretty similar in purpose and effect to poetry’s use of pyproject.toml.
Node definitely has this issue. There's a reason Node NVM[1] exists.
Respective solutions: NuGet, cargo, npm, go get, cabal. They all solve pretty much the same issue as venv and prepare your project in pretty much the same way. Raw venv can be a bit rough, but poetry solves that.
M-x pyvenv-activate
Or in your init: (pyvenv-activate "~/code/python/venv_dir/emacs_venv")
VSC: C-P
Python: select interpreter
It's pretty simple, and claiming nuget, node, and cabal/stack as superior means you're either lucky or inexperienced.The issue here is, that you are not storing hashsums and therefire are not safe.
To store hashsums you need a frozen requirements file, which needs another puthon lib … at this point it might be easier to use Poetry or Pipenv.
Pyenv is not unlike nvm for Node. Python moves too fast to expect the OS distributions to keep up.
From a stability, reliability, and CI/CD performance standpoint, I would discourage building your own Python. Things break more the more often your base version changes.
You'll be required to install a ton of apt system dependencies, then add a custom system apt package index (ppa), then install it from the new repository and hope you managed to get all required dependencies. I failed several times in a row last time before I got it working, after trying 2 different methods.
- Python -m venv + ./bin/activate + pip
- poetry
- pipenv
- pip (without isolation of projects)
You may add varying recommandations on the usage of OS python or a user installed one, but I think the same concern exists with most languages.
The main problem is that Python's mantra is supposed to be "There is only one way to do it". Less than two years ago, I read the official PEP doc about "Managing Application Dependencies", and it was a mess. pipenv was recommended (and still is), but there was a critical bug that ended in fatal errors on my development system.
Basically, start with "pip (without isolation of projects)" until it doesn't work for you because of library version clashes, then move to either pipenv or "Python -m venv + ./bin/activate + pip" (which are basically the same thing).
Poetry isn't as widely used.
I install pyenv first, then install any specific point releases I need, then switch to the python version I want to use, and then use pipenv to install dependencies or run commands in the virtual environment.