Episode: 3072
Title: HPR3072: The joy of pip-tools and pyenv-virtualenv
Source: https://hub.hackerpublicradio.org/ccdn.php?filename=/eps/hpr3072/hpr3072.mp3
Transcribed: 2025-10-24 16:13:13

---

This is Hacker Public Radio Episode 3,072 for Tuesday 12 May 2020.
Today's show is entitled The Joy of Pittles and Pie and V Virtual NV,
and is part of the series A Little Bit of Python. It is hosted by Klacky
and is about 24 minutes long
and carries an explicit flag. The summary is
how to manage your dependencies and environment isolation when developing in Python.
This episode of HPR is brought to you by archive.org.
Support universal access to all knowledge by heading over to archive.org forward slash donate.
Thanks for watching.
Hi, I'm Klacky.
For the last year I've been working full time as a Python web application developer
and I learned many things along the way.
Now I knew Python the language and I've been using it for the better part of a decade
but working with build scripts and other automation that I was doing earlier
and working on an application that is going to be developed by maybe one core team
and then other temporary contributors which could be deployed in multiple places,
automatically installed and has a large set of dependencies.
That's quite a different thing.
Then you not only need to know the language, you also need to know the tooling around the language
and around all of these other concerns.
There's two main things that are really important.
First is the dependency management.
I have an application, I need these frameworks and libraries to make it work
and they in turn have their own dependencies that make them work.
That all needs to be handled especially when you start upgrading things
or otherwise playing around with different versions of things.
The other thing is you want to be able to develop these things in isolation.
Maybe I'm working on the latest version but there's a couple of days older version running in production
and there's an issue there.
I want to be able to check that out and not get a conflict between what's installed now
and the development version and what's installed in production.
Maybe I have several projects running.
Each project could have its own set of dependencies or it will have its own set of dependencies.
Between two different projects those dependencies might be in conflict
so you need to be able to isolate different projects from each other.
This is something you always want when you do application development doesn't really depend on the language
but the tooling is different for each language.
When you handle requirements you want to handle it in two steps.
First of all there's the requirements that I want.
We are using an application, it uses framework xyz and it needs to be version 4
because that's the API we're developing against.
Then there's the dependencies of xyz itself and the dependencies of those dependencies.
That's the set we call the transitive dependencies.
When we are developing on our level in the application we don't really care about these other dependencies
because we don't program against those interfaces.
When it comes to troubleshooting we want to be able to pinpoint where something is different
between this thing that is working and between this thing that is not working.
It's well known that in common developer excuses it works on my machine
and you want to be able to avoid that if it works on my machine it's supposed to work on your machine.
If I just say I'm depending on xyz version 4
maybe you have version 4 and I have version 4.1
or maybe we both have version 4.1 exactly
but your version 4.1 depends on some other package version 3
and maybe my xyz version 4.1 depends on that other package version 5
because maybe you installed your packages Thursday last week
when xyz dependency was released at version 4
and maybe I installed mine this week on Tuesday
and then maybe this dependency version 5 was out and xyz pulled in that instead.
So what you do is you define both.
You say here's the stuff that I want
and here's the stuff that that happens to mean today
and that's a snapshot of the situation right now
and that means if everyone installs from this same snapshot of versions
of all the dependencies and transitive dependencies
then if we ignore configuration and other such variation right now
then at least in terms of the software that is running
if it works on my machine it should work on your machine as well
and if I run into an issue you should be able to reproduce that issue
on your machine.
Let's call that first set of direct dependencies
the abstract dependencies and let's call the
very detailed locked down to each version of each transitive dependency
let's call that our locked dependencies.
Now some people say that if you develop a library
you should use abstract dependencies and if you develop an application
you should use these locked dependencies but I disagree
both are required whether you are developing an application
or you're developing a library because one describes the set of situations
where this thing is supposed to work that's the abstract dependencies
like according to all the documentation that we have
if you use version 4 of xyz with our library or application
it should work and if they break something they should call it version 5
but then there's the locked set of dependencies that we have actually verified
and which we can use to reproduce an issue from one system to another
and here comes the fun part in Python
there is no standard way to have abstract dependencies and locked dependencies
and here's where this distinction of application versus library comes in
so what people would do traditionally would be to say
okay I'm developing a library and in my setup.py
which is the project description that you need to have
to be able to upload your package to the Python package registry
in my setup.py if I'm developing a library I should say
a range of versions of my direct dependencies
because then someone who depends on my library
and depends on some other library needs to be able to allow the package manager
to figure out a set of dependencies that fits both of those dependencies
and then people who develop an application would say
well I want this application to always install the same versions of things
so they might have a locked down set of dependencies in their setup.py
and then there's requirements.text and that's what people would use
if they're just developing an application internally
and they're not really looking to upload it anywhere
so they don't need to have a full package description
they just want some executable documentation
that says here's what you need to install in order to run this thing
so what they would do would be to develop the application
install packages as they need them and then run pip frees
with output to this requirement.text and pip frees just looks at
what packages do I happen to have installed and dump that
and that's not really a way to manage a list of dependencies
so I started looking around and okay if there are no standard tools
that can keep an abstract set of dependencies and a locked set of dependencies
what other non-standard tools are there
but before we go into that let's talk about the other part
of the deployment and development problem
so we had this list of dependencies and how to manage that
and then we also have I have multiple applications
that I'm developing and they each have their own set of dependencies
so how do we isolate them from each other
and the way you do that in Python is you use virtual environments
a virtual environment is basically a directory that is a small fake
complete Python installation with Python and packages and everything
and when you are in a virtual environment
you install packages and you act as if you would be installing them globally on the system
but actually they are confined to this directory
it doesn't involve any kernel level containerization or anything like that
it's not a real virtual environment in that sense
it's just a Python virtual environment
like we pretend this is a system installation but actually it's contained to this directory
okay when I started my research I found two tools that seemed like the tools the people are using
and one was poetry and the other was PIPENF
so I looked at poetry first because it was supposed to be this modern thing
that addressed several issues with PIPEN with other tools
and it uses the standardized PIPROJECT.TOML
which is where you're supposed to put tools settings these days in Python
so that you don't spread it out over several different files
non-standard files
so I don't quite remember why I didn't like poetry
on paper it looks very good
but I think I felt like it was too big a tool
and it tries to do all kinds of things
and I really just wanted something to manage my requirements
I didn't need a tool to handle uploads to PIPI, the package index
and I didn't need to have a project specification and all that
I just wanted to go from list of abstract dependencies to list of log dependencies
so I may have running to some other issue also I don't know
but anyway I left poetry behind and I looked at PIPENF
PIPEN does pretty much what poetry does
except that it puts these package requirements in PIPFYLE
instead which is also a TOML file
it's just named PIPFYLE because PIPROJECT or TOML didn't exist at the time
and when you generate the locked dependencies
that ends up in a file called PIPFYLE.LOCK
so it's not in PIPROJECT or TOML
and I used PIPENF for my own personal use
but I didn't want to have to convince others to use this tool
or that tool I just wanted to have this simple requirements dot text
that people can just pip install dash R requirements dot text
and just get what they need
and all these others sophisticated stuff I felt was too much to try to explain
to maybe someone joining the team temporarily to just add some feature
or some external contributor
but then I learned from people on the Fediverse that what PIPENF does internally
is actually it uses PIPTools
PIPTools has a command line tool called PIPCompile
which just takes either it reads setup.py or runs setup.py and gets the requirements from there
or you can feed it a requirements dot in file
which is just a list of lines which would be parameters to a PIP install command
and that's your abstract dependencies
and then it outputs a requirements dot text
which is your locked dependencies
and that's also just in PIP install argument format
so it's all really neat and readable
and people have been using Python
know it and understand it
no tumble files, no built-in virtual van management
and just one text file
and you generate another text file
so we went with PIPTools and PIPCompile for all our projects
so that's the dependency management
just use PIPTools very simple
and what about the virtual end management?
well you could do it manually
just python.m virtual n which is an external package
or python.m vn which is the built-in functionality in Python 3
but it may or may not work depending on which version of Python
several different versions of Python break the vn functionality
and you might have to use virtual n anyway
and a condo which we use at office
is one example where vn doesn't work
and you have to use virtual n instead
so you can do that manually
python.m virtual n and then directory name
and then when you want to use that virtual environment
you source directory name slash bin slash activate
and then when you're done you deactivate
and these commands will put the right python on the path
and make sure that all the packages are handled
within this confined directory
but this is a little bit annoying
so there are several different ways to manage your virtual environments
first of all it could be nice to have the virtual environment
not be in your project directory
it's very common to create a virtual n vn inside your project directory
but then when you run some static analysis tools
or you search for things you might end up having this huge set of dependencies
that you're accidentally running analysis or searches on
so you want to keep it outside and then the path becomes longer to type
so then maybe you create some alias
so some people use something called virtual n wrapper
which basically allows you to have a global set of environments
and then when you want to work on your project
you just type work on project name
and that activates that particular virtual environment
now I had already been using pipnV
so even though I left the package management behind
I just ignored that there's a pip file in there
I still use the virtual n management of pipnV
so you can do pipnV shell then it creates a new shell
and in there now you're in the virtual environment for Python
or you can do pipnV run some command in that virtual environment
and then come back to you
so you don't have to activate and deactivate
and you just confine all that to a sub shell that runs this virtual environment
and when you exit then you're back in your normal world
but even that just running pipnV shell became a little bit annoying
especially if you're jumping back and forth
and now I need to fix something on production
or now I need to run this script over here
you have to exit and then pipnV shell somewhere else
so in the end I looked up how to use pinV and pinV virtual n
and I'm very satisfied with the result of that
so what pinV does is it's a manager for different Python versions
so you can use it to install these different Python versions
and then you can also use it to switch between them
so you could use Python 3.6 in this directory
and you could use Python 3.8 in another directory
and it has a file that you affect using just pinV local
and then name of one of these Python versions that you have installed
and then it runs it writes the name to this .python-version file in the directory
and it doesn't hijack the CD command or anything like that
it's just that you put the .pyn-slash-shims directory first in your path
and it has files there for any Python-related command
you might want to run and if you install a package
it adds the scripts of that package to the shims directory as well
so that when you run a Python-related command
in reality you run the shim from pin
and it looks up which Python version I'm also supposed to be using now
could be the default version or it could be something specific for this directory
and you can also even specify a specific version for this shell session
so you could say right now I want to use Python 3.5
and then you can do that in that shell and when you exit that preference is gone
that's pretty neat and pin-virtual-n adds virtual environments to this
and a virtual environment then becomes just like another Python
in the list of pythons that you can define
so you can say pin-virtual-n and then which Python version you want to use
and then name of a virtual environment that you want to create
and then you can say pin-v local name of that virtual environment
and now when you are in that project directory and you run Python
that means you run Python that belongs to that virtual environment
if you're somewhere else then you run Python
it means you are system Python for example or whatever Python you have set
in your pin-v preferences or for this specific shell
so you have a lot of flexibility here
installing PyNv is very easy
you just clone the PyNv repo to your home directory slash dot PyNv
and then installing PyNvirtual-n is very simple too
you just clone that repo into dot PyNv slash plugins
slash PyNvirtual-n and then to activate them you put three lines in your
dot bash profile
so first you need to add PyNv bin to your path
and then you run PyNv init- and eval the results of that
and then you run PyNvirtual-n init- and you evaluate the results of that
and that's all in the installation instructions for these packages
but I also show it in the show notes
now I mentioned that we use Anaconda in the office
and we have our own installation scripts for Anaconda
that configures it with our internal CA certificates and all of that stuff
so I don't use PyNv to install Python versions
but it works well together with Anaconda
so I can just do konda create dash p
and then I point inside dot PyNv slash versions
and I create a Python version there using Anaconda instead of using PyNv's built-in functionality
and it just works so that's pretty neat
and from that point I can use PyNv to create virtual n's for the Anaconda environment
and switch between different Python versions and virtual n's in different directories
even though they come from Anaconda originally
so that was a lot to unpack
and there's a lot of further here for more episodes
but I think this is good enough for today
so I think I owe you a show on just reproducible builds
what does it mean?
how can we make sure that runs on my machine?
it's not an excuse anymore
that's a whole episode
and when I'm working at home
or well I'm doing my hobby projects at home
I don't use PyNv or any of these things
I use Nix
and that's also a whole episode of its own
how do you use Nix together with Python?
and then of course
now that I mentioned setup Py
but actually setup Py is deprecated
and there's not really one standard to replace it
quite yet there's a bunch of tools
that do what setup Py used to do
there's poetry
which not only handles the dependencies part
but it also handles the project definition part
and one tool that I have come to like
is Flit I've used it in the office
to package some things that we actually want to package
to install them from somewhere else
Flit is very lightweight
and only does what you need to do
so that's worth all episode of its own as well
and also when you are handling all these environments
you're installing packages and all that
in the end you want to test this in a reliable way
and make sure you didn't make any human mistakes
so that the tests you run locally are the same
that the tests you run on for example Jenkins
and for that there's a tool called Talks
which is also worth an episode of its own
so I'll hold you a whole bunch of shows
we'll see when I get to them
my name is Klake
you can find me on the free social web
as Klake at Libranet.de
and until next time
this has been Hacker Public Radio
uh
uh
uh
uh
uh
uh
uh
uh
I've been listening to Hacker Public Radio
at Hacker Public Radio.ci
we are a community podcast network
work that releases shows every weekday Monday through Friday. Today's show, like all
our shows, was contributed by an HPR listener like yourself. If you ever thought of recording
a podcast and clicking our contribute link to find out how easy it really is, Tech
or Public Radio was founded by the Digital Dog Pound and the Infonomicon Computer Club,
and is part of the binary revolution at binwrap.com. If you have comments on today's show,
please email the host directly, leave a comment on the website, or record a follow-up
episode yourself. Unless otherwise stated, today's show is released under a creative
comment attribution. Share it like 3.0, isn't it?
