I’ve been working with Python for more than a decade now and my local environment setup has gone through numerous iterations over that time. In the past few years I’ve used both Linux (Ubuntu) and OS X for my primary development operation system at work. Recently I setup a new Linux laptop and spent the time to better document how I had everything setup and configured. I chose tools which I could setup the same on both my personal Ubuntu machine as well as my Mac for work. I hope this can help you if you are new to Python and want a good starting place for your development environment. I don’t have any recent experience using Python on Windows so if that’s you, sorry I can’t help.
If you are using a Mac or Linux system then you likely already have some version of Python installed. However, the first thing I do when setting up a development machine for Python is to have a way to install different versions of Python and make sure that I’m not using or modifying the system level Python install. When both my work and personal machines ran Ubuntu I was happy to do this using apt repositories but when I switched to a Mac at work this was no longer an option. That’s when I started using pyenv and it’s been great. It’s easy to install on a Mac through Homebrew and easy enough to install on Linux through the git checkout. Having something that works consistently on both operating systems is important to me. It lowers the cognitive switching required when transitioning from my work laptop to my personal laptop. I wish I had made that transition sooner. I clung onto using the OS packing tools for so long it really hurt my ability to work with developers who preferred to use OS X for development. This is a consistent setup that can work for me or any of my team members and that’s a big win.
Once this is setup (please refer to the pyenv docs on how to do that for your system), I’ll go ahead and install the latest version of Python, which is 3.8.1 at the time of this writing, and make that my default Python version instead of the system Python.
# List out all available versions to install
$ pyenv install --list
# Install Python 3.8.1
$ pyenv install 3.8.1
# Make this the default instead of the system Python
$ pyenv global 3.8.1
# Check that it worked
Python 3.8.1 (default, Jan 10 2020, 12:45:58)
[GCC 7.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
Hooray! We now have installed the latest release of Python and we aren’t risking breaking our system Python. That’s a good start.
Managing Virtual Environments
I’m always juggling multiple Python projects at once. At work I’ve got different client projects and at home I have various small side projects or experiments and I need to keep their dependencies separate. Some people like to put their virtualenvs into their project directories, similar to what NodeJS would do with its node_modules. If you go that route then you might be fine using the built-in venv module but that’s not my preference. I prefer to keep my project directories clean of the virtualenv so instead, I’ve paired pyenv with pyenv-virtualenv to manage virtual environments so that I can isolate any package dependencies from project to project. This is managed by the same teams that manage pyenv and has similar installation methods (Homebrew or git checkout). Once you get through that setup (again see their documentation for the most up to date installation methods) we can create our first virtualenv.
# Create a new virtualenv
$ pyenv virtualenv 3.8.1 new-project
# Activate the virtualenv before install packages
$ pyenv activate new-project
Now we have an isolated environment to install packages for our next project. You will need to activate this virtualenv each time you open a new shell to work on this project. To make this easier you can set a .python-version file in your project directory with the name of your virtualenv and it will activate when you enter the directory.
# Set the virtualenv name (should be done in the project directory)
$ echo "new-project" > .python-version
If you are working on a team project you might not want to include this file in your repository unless everyone can agree on using the same virtual environment name.
The last piece of my Python setup is linting tools. This is an optional step but it’s one that I’ve gotten a lot of value out of recently. There are three packages that I install and configure globally (in the new default Python, not the system one) so that I can incorporate them into my text editor. These are:
All of these can be installed with pip:
# Check confirm I'm not using the system Python
$ python --version
# Install the latest versions with pip
$ python -m pip install black flake8 isort
Each of these serve a complementary role in improving my Python code. black is an auto-formatting tool to ensure your code fits with PEP 8 style. I originally hesitated using it because I didn’t like some of the choices that it made, in particular I still like single quotes over double quotes as the default. However, I’ve since embraced the freedom of not having to think about tedious things like how to wrap long lines or large imports. black does all that for me within the comfort of my editor on save. I encourage you to let go of control and let black auto-format your code for you. It’s very freeing.
isort fits a similar role as black but it’s focused on sorting imports. It cleanly sorts imports alphabetically and groups them into standard library, third-party packages, and internal packages. It might just be confirmation bias but it feels like conflicting import order is a common source of merge conflicts on the teams I’ve worked on. Once I fully embraced auto-formatting it was a natural step to continue on with isort. It again removes one more choice I have to make when coding. isort does require a little configuration to play nicely with black.
# This should live as ~/.isort.cfg
Now I don’t have to think about how to sort my imports or work to wrap my lines properly but I can still make plenty of mistakes in my code. While flake8 doesn’t automatically fix issues that it finds, it does help highlight potential problems in my code. flake8 helps me find issues like unused imports, unused variables, or variables that are used but never defined. Again that helps me find issues before I try to run the application or test suite saves me time of jumping back and forth finding errors. Here I also let the conventions from black take precedence and I’ve configured flake8 to ignore issues when they would be in conflict:
# This should live as ~/.config/flake8
max-line-length = 88
extend-ignore = E203,E231,W503,W504
I have mine configured globally but will also configure these on a project by project basis in the case where teams might have slightly different conventions. The only downside that I’ve seen so far when adopting these tools is that the first save/clean up of an existing file can create a lot of noise or unrelated changes. So far it’s been worth it for me and if you’ve never used black, isort, or flake8 then I would encourage you to dive in and give them a try.
I hope you enjoyed this little journey into how I like to setup my Python environment. Be on the look out for follow up posts on how I configure my editor or more project specific setup. Ping me if you found this helpful or if there are other useful tools that I can include in my setup.