Whether you are a beginner or seasoned software developer, this is a great getting started guide for standing up a complete, “professional” python development environment in Linux or MacOS.
Table of Contents
Introduction
I’ve been writing Python code for the last 4 years. During that time, I’ve had to install many different python versions on many different Operating Systems. I’ve struggled with dependency management and have learned lessons along the way that I’d like to share here. By the end of this article, you’ll be able to:
- Install any verison of python you want
- Have isolated environments for each of your projects
- Be able to run tests in multiple python languages
The tools I am using currently are:
- Pyenv - to manage python installations
- Pyenv-virtualenv - to manage python versions
- tox - for testing in mulitple python versions
In the future, I will be transitioning to pipenv but that will be its own blog post. Pipenv is definitively the better package manager, but there are some integration parts that need to be ironed out before I feel comfortable moving over. I believe once pip 2.0.0 comes out, I will likely switch over. Until then, here’s what I’ve got!
Installing Python
We will be demonstrating how to install python on Ubuntu, but the general process is the same on most Linux distributions. Before we can actually install Python, we have to install some of the base dependencies. These dependencies are used to build Python itself. Without them, we simply cannot install Python. So, open up your favorite package manager, and its time to install some dependencies:
- ubuntu
- macos
- centos
- alpine
1 |
|
Next, we will be installing pyenv
which will require git. So we’re going to install git as well.
- ubuntu
- macos
- centos
- alpine
1 |
|
With git installed, we are going to install Pyenv. Pyenv helps us manage multiple versions of python simultaneously. Having multiple python versions on your machine is very valuable. Even if the application you’re going to build/work on is only targeted for a single python installation, it is likely that you’ll want to update to the latest version. Alternatively, if you’re creating a package for use by other developers, you might want to test integration with older python versions. Pyenv makes managing these installations much easier.
1 |
|
This will install pyenv into your home directory. To activate pyenv and make it usable in your environment you’ll need to put the following in your shell’s profile. If you’re using Zsh, you’ll place this in ~/.zshenv
for Ubuntu/Fedora users, the following will be placed in ~/.bashrc
1 |
|
Now either source this file, or restart your terminal session
1
exec $SHELL
Now that we have pyenv installed along with the python dependencies, installing will be easy! There are many different python installations that you could want. You can see all the options available by running:
1 |
|
If the version of python you want was not listed by the above command, try cloning the newest version of the git repo. Altenratively, you could install pyenv-update and run pyenv update
Now we can install any python version we want. For this post, I’ll be showing a small package which attempts to support Python 2 and Python 3. We can install each:
1 |
|
This may take a bit, but at the end you should have a python installation. Feel free to install whatever versions you want including pypy, ironpython or any other python version. The rest of the tutorial will assume you want at least two different versions.
A Quick Look at Pyenv
The pyenv documentation is a great resource to understand how pyenv works, but I will try to summarize the bits I think are important here and demo some of the main commands you will want to use. In short, pyenv works by modifying your PATH
dynamically. This has a couple of weird consequences, but they are worth living with. First, let’s inspect what version of Python we are running:
All of the following output is based on ubuntu 16.04 LTS, so if you see differences, don’t panic!
1 |
|
Hmm, we installed python 2.7.14 and 3.6.4 above, so why is this 2.7.12? Well, it just so happens that Ubuntu ships with its own python. So let’s try to figure out which python we’re refrencing when we run python
.
1 |
|
If you were expecting /usr/bin/python
or something else, don’t worry so was I the first time I ran this command. This shims directory is how pyenv is able to edit your python at runtime. It injects this into your PATH
and then dynamically changes the linking.
Great but, where is the python installation on disk? Pyenv allows you to answer that with the following commmand:
1 |
|
Here we can see that we are using the system python. Preferably, we never want to touch the system’s python. There is an easy way to do this. We simply need to tell pyenv that we want to use a different python.
1 |
|
This sets your global python as 3.6.4. So now when you ask for the version:
1 |
|
But what if we want to run python 2.7.14? Let’s try it:
1 |
|
Pyenv successfully found your executable, but it cannot run it because that python environment is not active. We could of course swap the environment by simply running pyenv global 2.7.14
but this is a bit of a pain when you want to switch back and forth. I like to have both python verisons on my path at the same time so I can quickly test different versions when necessary. Luckily, pyenv allows you to do this:
1 |
|
We can see that by default, python
will resolve to 3.6.4, because we placed it first on the list. However, each individual python is callable at the same time.
Setting up a Virtual Environment
Now that we know how pyenv works, let’s try to apply it on a project. I have a simple toy package up on GitHub that we can clone. Let’s clone our toy package.
1 |
|
In theory, we could just go ahead and install all of our requirements with pip install
but that would muddy up our global Python 3.6.4 version. We really don’t want that. That’s where the concept of virtual environments come into play. You should have at least one virtual environment per project. Luckily, there is a python package named virtualenv that can help with this problem. Even more lucky is that there is a plugin to pyenv that allows us to use this tool. Let’s install pyenv-virtualenv
1 |
|
Similar to pyenv itself, we now need to activate pyenv-virtualenv Add the following to your ~/.bashrc
(or equivalent):
1 |
|
Restart your shell or source your ~/.bashrc
1 |
|
Now we can create a virtual environment for this toy package:
1 |
|
This created a new virtual environment named “toy_package”. We specified that this virtual environment should use python 3.6.4. Virtual environments are very important concepts. They help to keep our python environments clean. This is important because it is very easy to accidentally install different versions of the same package which can cause all kinds of nasty bugs and waste countless hours (don’t ask me how I know). To avoid them, we will activate our new virtual environment. Think of this virtual environment as a personal playground for your project. You can do whatever you want to this playground and always be a few commands away from a fresh start.
1 |
|
This tells pyenv that we want to use the python associated with this virtual environment. Not only is it using a separate python
, it is also using a separate pip
! We can prove that via:
1 |
|
Here you can see that there is a separate virtual environment in ~/.pyenv/versions/toy_package
with its own pip installation and everything. Now we can install our dependencies safely!
1 |
|
This installs all our python dependencies. Python has a rich environment of over 250,000 packages so its quite common to see a requirements.txt
file in the root of our repository. Sometimes you’ll see them broken down into requirements.txt
and requirements_dev.txt
. Their purpose is to list all the package dependencies our project needs to do local development. One of the packages our project lists is pytest. This allows us to run tests on our application. Let’s run some and make sure they work!
1 |
|
Great! Our tests passed. If you are only interested in running on a single python version, then you can stop here. However, if you want to test on multiple python versions then you’ll need to use a different tool called tox
Testing against Multiple Python Versions
Testing in multiple python versions is a common occurrence for package maintainers. If you are interested in writing a package or just interested in maximizing portability, you should really be using tox. Tox is a tool for testing. It can test with differing virtual environments and even switch python versions.
Tox requires a configuration file to tell it what tests you would like to run. This file is the tox.ini
file and should be found at the same level the setup.py
file is found. The toy package already has a tox.ini
, let’s take a look at it:
1 |
|
Basically, this file tells us we want two environments: py27
and py36
. For each test environment we are then going to install the dependencies by installing what is in requirements.txt
. This is specified by deps = -rrequirements.txt
. Finally, in each environment we are going to run the command pytest
. Let’s initiate tox
1 |
|
Yikes! Lots of errors. But the error message is actually coming from not being able to resolve the differing python environments. Tox is attempting to create special virtual environments for each of the test environments. Of course it needs python to be able to do that. So let’s make sure python3.6 and 2.7 are available. The way we did this before was by using pyenv global
to set up multiple versions. It turns out pyenv allows us to do the same thing with the local
command:
1 |
|
Now that each of the python versions is on the path and available, we can run tox again:
1 |
|
This time, we have a new failure, but it is because of a bug in our code. I’ll leave solving this bug up to you! If you can’t figure it out you could checkout the answer in the solution
branch:
1 |
|
Conclusion
This is just how I choose to do my python development. You can always modify, and I encourage you to look at other tools if you’d like. I’ve found that this setup works quite well for me and lets me support easily swapping between projects quickly. I’ve barely scratched the surface with lots of these tools and there is still lots more to learn. At the very least I hope you can now:
- Install python easily -
pyenv install X.X.X
- Setup multiple virutal environments easily -
pyenv virtualenv myenv
- Activate multiple python versions -
pyenv local myenv 3.6.4 2.7.14
- Run tests against multiple versions -
tox
Thanks for reading! If you have questions or topic you’d like to see me blog about, hit me up on twitter @loganasherjones