Lately I had the pleasure to work on a Python project again. Therefore I needed to manage my dependencies somehow. Having worked with Django, I reached to a requirements.txt first, but ran into some problems when I started writing tests. Here’s how I overcame them.

requirements.txt

A requirements.txt is a simple text file, which contains the name of a package and the version you want to have. Personally I prefer to pin the version, that is, my requirements.txt looks like this:

# Everybody loves emoji these days!
emoji==0.5.1

Nice and sweat. However, there’s a problem. But let me first explain how a setup.py looks like.

setup.py

Normally, setup.py is used to make your package distributable. Therefore you fill meta information in it. Something like this:

import setuptools

with open("README.md", "r") as fh:
    long_description = fh.read()

setuptools.setup(
    name="example_pkg",
    version="0.0.1",
    author="Example Author",
    author_email="author@example.com",
    description="A small example package",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://github.com/pypa/sampleproject",
    packages=setuptools.find_packages(),
    install_requires=[
        "emoji==0.5.1"
    ],
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
)

If you want to learn more about what the arguments mean, read Packaging and distributing projects. We are especially interested in install_requires here. This is something, I’d prefer to have in my requirements.txt.

Bringing together the best of both worlds

So, let’s do this! As I said, requirements.txt is very simple. So replace your setup.py with the following:

import setuptools

with open("README.md", "r") as fh:
    long_description = fh.read()

with open("requirements.txt", "r") as fh:
   requirements = fh.readlines()

setuptools.setup(
    name="example_pkg",
    version="0.0.1",
    author="Example Author",
    author_email="author@example.com",
    description="A small example package",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://github.com/pypa/sampleproject",
    packages=setuptools.find_packages(),
    install_requires=[req for req in requirements if req[:2] != "# "],
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
)

So we read the whole requirements.txt into the variable requirements (with every line becoming an item of a list) and then do some list comprehension to filter out the lines containing a comment (which starts with # ).

Note, that there are differences between „abstract” and „concrete” dependencies (which I won’t go into details here).

Which problem did this solve?

When running make html of sphinx and pytest I often encountered the situation that I saw this error:

`SystemError: Parent module ‘’ not loaded, cannot perform relative import

So I browsed StackOverflow what to do. By defining the above setup.py and running pip install -e . I can install my package locally.

This, I can use python -m app to have it run properly.

Further reading

During the research of this article I discovered more article which I want to re-read, since I have to catch up with some newer Python practices. I leave them here in case you find them helpful as well:

I have to double-check, whether I’m treating Sphinx right …