DEV Community

Cover image for Python Dev Environment Part 3: dependencies with install_requires and requirements.txt
Jonathan Bowman
Jonathan Bowman

Posted on

Python Dev Environment Part 3: dependencies with install_requires and requirements.txt

In Part 1 of this series, we created a simple Python distribution package called "pygreet." In Part 2, we wrote tests and ran them using pytest.

Other than pytest, we did not install anything that wasn't already included in the Python standard library. Often, though, a package will have several dependencies. How do we best handle these?

Simply put, place package dependencies in the install_requires list in setup.py. Place development dependencies, such as test frameworks and linters, in requirements.txt.

Let's explore further.

Add a function with a dependency

Take the pygreet package we have been building and add a friendly time-telling function.

"""Functions useful for sending greetings."""

import arrow

def greet(greeting="Hello", recipient="World"):
    """Greet someone."""
    return f"{greeting}, {recipient}"

def greet_city(tz):
    """Greet a location."""
    now = arrow.now(tz)
    friendly_time = now.format("h:mm a")
    location = tz.split("/")[-1].replace("_"," ") 
    greeting = greet("Hello", location)
    greeting += f"! The time is {friendly_time}."
    return greeting
Enter fullscreen mode Exit fullscreen mode

We have added two things: import arrow and the greet_city function, which takes a timezone as input.

Include list of dependencies in install_requires in setup.py

Because arrow is an external dependency, let's add it to setup.py:

"""Package configuration."""
from setuptools import find_packages, setup

setup(
    name="pygreet",
    version="0.1",
    packages=find_packages(where="src"),
    package_dir={"": "src"},
    install_requires=["arrow"],
)
Enter fullscreen mode Exit fullscreen mode

(Re-)installing package also installs dependencies

Once this is saved, installing our package again (in editable mode, just as before) should bring in arrow and its dependencies.

$ pip install -e .
Enter fullscreen mode Exit fullscreen mode

The output should include "Successfully installed arrow..." and other packages, with version numbers.

Test

We cannot forget to write a test for our new function. In the tests directory, a new file called test_greet_city.py contains:

import greet

def test_greet_city():
    result = greet.greet_city("Asia/Shanghai")
    assert "Hello, Shanghai! The time is" in result
Enter fullscreen mode Exit fullscreen mode

Run pytest in the current directory. Feel proud.

Development dependencies

So, place dependent packages your software needs to run in the install_requires list in setup.py.

Dependencies you the developer want in order to build your software go in requirements.txt.

Such as pytest. Of course, if you are installing the development dependencies you need, then you will also want the actual package you are building to be installed as well. So, let's add both in a requirements.txt file:

-e .
pytest
Enter fullscreen mode Exit fullscreen mode

You can run this file with:

pip install -r requirements.txt
Enter fullscreen mode Exit fullscreen mode

I like to think of requirements.txt this way: a list of arguments, each of which is passed to pip, line by line. So, running the above requirements.txt file is equivalent to:

pip install -e .
pip install pytest
Enter fullscreen mode Exit fullscreen mode

Your source code tree should look something like this now:

pygreet/
├── requirements.txt
├── setup.py
├── src
│   └── greet.py
├── tests
│   ├── test_greet.py
│   └── test_greet_city.py
└── venv
Enter fullscreen mode Exit fullscreen mode

The next time you are on a fresh system, you can download/checkout/pull your Python code, setup your virtual environment, pip install your requirements, and keep coding.

Versions and upgrading

I have chosen not to specify versions of each package, or upper/lower bounds for version numbers. This way, at any time, I can

pip install -Ur requirements.txt
Enter fullscreen mode Exit fullscreen mode

And upgrade all the packages. This works great, unless I am not paying attention to breaking API changes in any of the dependent packages. If that happens, there will be pain.

The Python packaging guide details usage of both install_requires and requirements.txt.

You might also try pip freeze to see package versions. You may use the pip freeze output to create a requirements.txt file with pinned versions. That way, there will be no surprises, although your packages will grow out of date and insecure unless you update the versions.

This series is introductory in nature, and I hope you find it helpful. Please continue to explore, and enjoy, Python packaging and dependency management!

Top comments (0)