Managing Python Versions with Pyenv
thepythoncorner.com
thepythoncorner.com
All of the following are real tools for Python package/environment management: virtualenv; pipenv; venv; pyenv; poetry; pyenv-virtualenv; and pip-tools. There are probably more. Which one is "best" varies by who you ask and with time. If you do a bunch of Python work, you'll probably need most of them installed since different projects use different tools.
The messiness of the whole situation just takes the fun out of it for me. I don't hate Python, but when I code in my free time, I'd simply prefer to use a language that doesn't have those problems.
So I think even if one of those packaging tools worked really well, a library somewhere could undo your best efforts at isolation.
I also don't hate python itself, but I wish the one way of doing things ethos applied to managing dependencies and python versions.
Only part 2 is a problem of the Python ecosystem, part 1 is a problem of any environment to run binary programs. It applies to C-compilers, node, ..., perl...
Naturally, as most people involved with Python seemed to agree with that view, there was a lot of room for "improvement". And people got their improvement aplenty. Other people just realized that this is a general problem and nowadays instead of keeping around their /opt they now use something like spack. Which allows you to install a range of python-versions just fine. As well as C-Compilers. As well as perl-versions.
- Managing python versions (no packaging/enviroments) Pyenv is the solution if you don't want to use a global OS version.
- Environments, you don't need anything that isn't bundled with python, `python -m venv env` is the "official solution" and works really well.
- Packaging your app for running in production, `pip freeze > requirements.txt` and `pip install -r requirements.txt` is the "official solution", it works well enough.
- Packaging a lib for public distribution, setuptools... I don't have enough experience there but not many people need to actually use it. But again it is the "official solution"
Pyenv is the only none standard tool that I would recommend.
As far as I'm concerned all of these are redundant or not needed: virtualenv, pipenv, poetry pyenv-virtualenv, and pip-tools.
I just have:
requirements-dev.txt (dev only requirements)
requirements.txt (top level requirements)
requirements-freeze.txt. (all requirements frozen)It's a problem that I hope PIP will solve one day, but I like to keep tooling simple.
However because it's so easy to create venvs I regally just rebuild from scratch, do a `pip freeze > requirements-freeze.txt` then install dev dependencies (so not freezing dev dependencies). In many ways I think it's better to not mutate your venv and just rebuild if making significant changes.
Fortunately the packaging culture in Python isn't quite the same as with Node and you don't tend to end up with hundreds or thousands of dependancies.
The way I've been doing is to not refer to dev dependencies in requirements.txt and then install the dependencies from requirements.txt before doing a requirements-freeze.txt. I made pip-chill to make the process a bit easier.
It doesn't though. The default format for requirements doesn't specify which requirements are top-level and which are not. Updating dependencies on a project that doesn't have this distinction is complete pain. It also doesn't work when "production" might mean different environments with different Python versions.
> Packaging a lib for public distribution, setuptools... I don't have enough experience there but not many people need to actually use it.
Actually, using setuptools and setup.py (or a modern equivalent) is very helpful for packaging scripts and commands, defining top-level dependencies or other things. It's much easier to distribute a wheel file to production than a full repository.
It's a mess once you start digging a bit, Python dependency management is extremely basic. It's no wonder why alternative tools appear, they're not redundant, they actually fix pain points in the default tools.
Shameless plug: you can `pip install pip-chill && pip-chill > requirements.txt` and work as usual. If you feel brave, you can `pip-chill --no-version > requirements.txt` and check if your thing works with the latest of everything.
This is why (imo) you should include immediate dependencies in setup.py install_requires, and use requirements.txt for the "lock file" that contains all dependencies.
I use pyenv + direnv to automatically configure my Python interpreter and virtual environment for each repo. It is pretty painless.
In addition to the redundant tools you already mentioned: conda.
I have to agree with the OP here, Python's dependency management story is just awful.
Except when you run on Debian based systems, where you need to install the module separately for the OS Python
$ python -m venv env
/usr/bin/python: No module named venvIMO the way most Linux Distro package managers install stuff is absolutely awful (system wide, no isolation, shared libs, shared PATH...)
OTOH, if you will just use the OS, you won't need venv, as Debian packagers make sure everything works out of the box without it.
Poetry solves this to an extent by including these dependencies along with their markers in the lock file.
I've used poetry, pipenv and others... Nowadays my opinion is that I want my package managers to be simple.
For each app, I mantain requirements.txt, requirements.dev.txt, requirements.test.txt... I edit those files manually and only use pip freeze for pinning requirements.freeze.txt
For libs, flit + pyproject.toml is a breeze.
pip, pip-tools (to generate a lock file), and virtualenv are all I've needed for years now.
I just started using pyenv to install Python on Ubuntu. Before that I was using the deadsnakes ppa.
The tools are there to use as needed. You don't have to install or learn them until...you have to. Which may very well be never.
If you avoid Python and all its benefits because of its packaging situation, it's really your loss.
I just hope no one reads this and gets scared off thinking that this top voted comment matches reality. It's not that hard to get your bearings and the Python foundation has invested heavily into the packaging ecosystem the last few years.
For example: https://packaging.python.org/en/latest/
Having said that, I dislike the way pyenv works by adding proxy interpreters that are globally visible.
I now refuse to use any packaging tools for anything other than generating a requirements.txt, and refuse to use any version matrix tools like tox. I don't have a preference for/against pyenv for sourcing python interpreters (I'll use an AUR or scoop sometimes), or poetry or pipenv or anything else for specifying dependencies, but whatever method is used, I will always only use them to export a requirements.txt, and I will manually create my own venv for each interpreter, and run anything I want inside each venv via oneliners like:
bash -c '. "$0/bin/activate" && exec "$@"' venv38 pip install --upgrade pip setuptools wheel
bash -c '. "$0/bin/activate" && exec "$@"' venv38 pip install -r requirements.txt
bash -c '. "$0/bin/activate" && exec "$@"' venv38 pytest --doctest-modules -v
Usually I'll wrap that `bash -c` preamble into a simple with_venv.sh script, and make a similar with_venv.ps1 for Windows. Create a Makefile with targets for those commands for each interpreter you want to test, and you can run everything in knowledge that you'll always know what's happening, instead of relying on stacked magic.pyenv install 3.8:latest
Will install the latest 3.8 Python version.
Q: Now you want to build your Fortran extension with a compiler supporting OpenACC. What now?
This should show relatively clear that this tool is just an edgecase of a quite general problem. I recommend a general solution (like spack).
If you need that level of control, use Spack or Nix or whatever.
Whenever threads like this come up, people have a weird tendency to hold Python on some kind of pedestal above other languages.
I can't think of a single language package manager or installer or version manager that makes even a faint attempt to control system level dependencies like a Fortran compiler. Why is Python somehow bad for failing to manage this? The problem is and should remain out of scope for any particular language tool chain.
I also don't think this is a failure of "python" as some others have raised that in the vincinity. Managing binaries is just out-of-scope and should be left to another tool - such as given with pyenv. It's solving this problem in a narrow way, which most commenters find confusing and I find a bit useless compared to alternatives!
If you need to install multiple versions of Python that are not available from your distribution packages, simply download the code, install it to a place like /opt and then point your virtualenv to that version when you need it.
Here's what's needed:
> ./configure --prefix=/opt/python-<VERSION>; make; make install
This is not more complicated than what pyenv asks you to do (read the article).
Once you have all the versions installed, no need to use a tool to switch between them. When you use virtualenv (which you should, and no need for pipenv, poetry and the rest), just do this to create a new virtualenv:
> /opt/python-<VERSION>/bin/python -m venv /path/to/where/you/want/to/store/your/virtualenv
This will pick up the correct version of Python. Once you activate the virtual environment, that's it.
There's no need to use anything besides what default Python installation already gives you.
PS. What about the dependencies? Glad you asked: `apt build-dep python3` on Debian/Ubuntu or `dnf builddep python` on Fedora. No need to go through them manually or copy/paste some list off someone's blog and hope it's still valid.
PPS. All this applies to Python 2 with minor tweaks, but hopefully you're not starting new Python2 projects in 2022.
I agree the rest of the pyenv (shims, shell magic etc.) tends to make a mess.
In recent versions, the shims are installed to a separate directory, which you can omit from your PATH if you never want to use them.
Usually the distros suffice for that. There are very few cases where it's worth to roll out your own Python.
> In recent versions, the shims are installed to a separate directory
This is a huge progress!
Edit: As a concrete example, say you need a C based python package to work locally, but also build within Gitlab and be deployable to an AWS Lambda. There's a gap there where Python really wants absolute paths that don't change.
My workflow on a Mac is to use Brew to install Pyenv and then Pyenv to install Python versions. Similarly I use Brew to install NVM and then NVM to install and manage Node versions.
For python virtual environments I go with vanilla `python -m venv env`, it works well and I like that the `env` directory is under the project I'm working on.
Might I suggest asdf (asdf-vm.com). I use it for managing ruby, node, go and python versions and it has been rock-solid. You can also install it from homebrew.
I often have to debug not-so-tech-savvy colleagues' python scripts/environments.
If we used pyenv, I could picture me telling them that they forgot to switch system python environment.
I'm really drawn to Nix for it's power and purity, but until it has a first class way to install specific older software versions the 'worse is better' asdf way wins.
The first, that has always been my practice.
Secondly, the vast majority of solutions offered by the many people who face similar issues don't work for me or, apparently, for other people reading the solution as well.
I prefered alt install for a specific version (python3.3, python 3.X, etc. with associated pip) and virtualenv. Then doing `python3.X -m venv` i got what i need. VScode or pycharm can bind to it.
Recently i've tryed Fedora and they ship python versions compiled so you just need to use the package manager. It's easier and it deals with updates. I stick with that now (i'm migrating from deb to fedora for my servers).
It is however slow as molasses and its error messages are sometimes worse than a stack trace.
If you want to use poetry + direnv, that's also an option.
https://github.com/direnv/direnv/wiki/Python#pyenv https://github.com/direnv/direnv/wiki/Python#poetry
Hatch aims to do what Poetry does, but is strictly compliant with Python standards.
Who?
> have just officially adopted Hatch:
Do you have a link?
There's a discussion here:
Say you need to have a venv but for a specific Python version:
pyenv install 3.8.5
pyenv shell 3.8.5
python -m venv env
source env/bin/activate
Now your venv is for 3.8.5, and running `source env/bin/activate` will always activate that version within the environment.A common scenario: Python 3.10 comes out and you want to make sure your project (and all of the upstream dependencies, libraries) work with it. With pyenv you get virtualenv isolation as well as isolation of the language itself. You can have a unique Python for each project, not just a unique virtualenv sharing the same install. This also makes benchmarking kinda fun, you can jump back and forth between versions and run the test suite to gauge if any big changes have occurred time wise.
Personally, I love idempotent environments that I can create and destroy on a whim. Something go wrong? Package install failed half way through and left clutter? Destroy the directory, make new pyenv/virtenv and install again. Leaves little room for doubt.
I used to share global pythons but as I started to take on more and more consulting projects it became imperative to keep them completely isolated.
But I definitely see why you‘d need it. It’s just a bit sad to me that there’s yet another tool with a potentially confusing name, orthogonal to the system Python. “Wanna get started with Python? Sure just install another Python (ignore the one that comes with your OS!) using pyenv, then it’s just a matter of getting a virtualenv and pip-installing your requirements.txt! Or was it Pipenv? Or setup.py even?“. Oh and it’s almost guaranteed that your IDE will pick the wrong instance.
It‘s just too much. I see non-Python enthusiasts struggle with this stuff on an almost dsily basis. Especially the ones who only have exposure on an irregular basis.
I wish OS distributions would just STOP providing Python packages at all.
So much about Python the language and its standard library is great. But many peripheral things (the Python 2->3 debacle first and foremost) are just badly executed.
https://github.com/pypa/virtualenv/issues/1549
Edit: Ah, yep, somehow read this as virtualenv.
pyenv virtualenv <python-version> <env-name>
pyenv activate <env-name>
pip install ...It’s also not that complex at the end of the day. You have two things involved in managing python versions: the executable you’re running, and the PYTHONPATH.
I actually don’t understand why people find that so complex. A virtual environment is: an alias to a python executable; a folder called ‘site-packages’ which is then added to the PYTHONPATH; and a script to set you PATH and PYTHONPATH.
Pyenv is brilliant, defiantly recumbent it as the standard way to manage python versions. It's basically nvm but for python.
Pyenv is mainly for installing versions of Python. It's useful if you work on multiple packages that each needs a different version of Python, or if you have one package that you test with multiple versions of Python. (Alternative to apt/yum/brew or manually downloading Python from python.org)
pipenv is for installing Python packages for your project. (Alternative to pip, poetry, etc)
Installing python: `mamba install python=3.9`
Frankly the invocations are so arcane for these various supporting tools that I have been distributing a standard Makefile to projects to hide the complexity - "What's old is new again"!
To run it from cronjob and other places where environment is harder to define, the following can be used:
0 2 * * * /home/user/.pyenv/shims/python /home/user/bin/script.py
I’m talking about the “full” virtualenv. This one: https://pypi.org/project/virtualenv/
In Python everything sort of almost works good enough for each use case, so half thought solutions keep staying in each niche...