Adding dependencies per "Packaging Python Projects"

Regarding Packaging Python Projects — Python Packaging User Guide

This is a pretty basic problem of just properly expressing dependencies.

I can adapt that tutorial to build a package for a project on which I’m working. The problem is that I haven’t figured out how to declare dependencies. I’ve tried a few things that all didn’t work so instead I figured I’d just ask here what is actually correct.

I was testing it by installing it into an empty virtualenv, and then running pip freeze in that environment to verify whether or not it was there. The only thing that shows up after installing is the package itself that I installed.

I am betting there are multiple ways to do it based on whatever. In my case, it’s rather simple: I just need json5 >= 0.9.6.

I’m also hoping something about dependencies can be put in that tutorial.

1 Like

The key word is dependencies, also called requirements.
If you are following the guide and using setuptools as a build tool, the info is here: Packaging and distributing projects — Python Packaging User Guide
Personally I find flit quite nice for simple projects (pure Python, no C extensions): The pyproject.toml config file — Flit 3.7.1 documentation

I’m trying to join the cool kids who are not using setup.py any more and I thought that tutorial I posted was about the newer packaging conventions. It looks like the documentation you sent was for using a setup.py and the install_requires is being listed as an argument to the setup() function.

Do I actually still need to use a setup.py if I have requirements?

No. If you’re using setuptools, you can specify the requirements in setup.cfg:

[options]
install_requires =
    json5 >= 0.9.6

The new cool thing is that we can have multiple installation frontend (pip, tox, etc) working with multiple build backends (setuptools, flit, etc). Setuptools is not shunned, it’s just demoted from its position as preferred tool that defines most of the experience and the de facto interface with pip. A project needs some config in pyproject.toml (to wire the build backend to frontends) and some other config in another section of that file or another file entirely depending on the build backend.

The guide you linked uses setuptools, so you should follow the setuptools docs guide I linked to add dependencies to your config file (setup.cfg or setup.py).

FYI, pip list is the command to run to see what’s currently installed in your environment.

Presumably, the dependency wasn’t listed under the appropriate key for listing run-time requirements for your chosen build backend (e.g. install_requires under [metadata] in setup.cfg for Setuptools, dependencies under [project] in pyproject.toml for the new format specified in PEP 621).

If you only listed your dependencies in a requirements.txt file, it will not be installed automatically. The use of requirements.txt files (often generated by pip freeze or pip-tools) is generally for manual installation for development and applications that need to reproduce a given environment exactly, or other specialized applications, and must be manually specified by e.g. pip install -r requirements.txt.

To elaborate, in reasonably recent versions of Setuptools, you can decoratively specify your metadata and build config in a static setup.cfg. This is simpler, much harder to mess up and has many benefits over using a dynamic setup.py file for the same purpose, and tools exist to convert the latter to the former. While in recent-enough Setuptools you can omit the setup.py completely, its probably best to retain a stub setup.py for now, e.g.

#!/usr/bin/env python3

"""Stub setup.py for use with legacy build tooling."""

import setuptools

if __name__ == "__main__":
    setuptools.setup()

to accommodate legacy tooling and use cases.

Furthermore, forthcoming versions of Setuptools will gain support for specifying dependencies in the PEP 621-specified [project] table of pyproject.toml, which will work with any build backend (Flit, Setuptools, Poetry, PDM, Hatch, etc) once they support it (which with Setuptools adding support, leaves Poetry as the main laggard here).

[project]
dependencies = [
    "json5 >= 0.9.6",
]

Thanks in particular to John’s response, I was able to figure out how to get the json5 requirement into the package metadata.

I also added a legacy setup.py as recommended. I actually think I need that to satisfy a goofy, internal back end we are using.

A lot of my confusion regarding these methods comes from stumbling into the new pattern with a project.toml and everything else when investigating a random setuptools bug that had happened a few months ago. From that correspondence, I had found out about the new paradigm. So when I had to make a new Python package, I decided to look it up in more detail and found the tutorial I was using.

The tutorial was still deferring to setuptools. It’s not that I need or want to use it. I’m trying to figure out what I should be doing to get ahead of the standard changes. So if there are additional steps I should be taking that I’ll want to get that out of the way here too. The situation I’m working on right now is what I’d consider to be a simplified, yet still typical one so it’s a decent place to learn the new conventions.

Basically, to summarize, if you’re using Setuptools the de-facto current packaging setup is as follows:

  • pyproject.toml → build system config (boilerplate from the packaging guide)
  • setup.cfg → Primary metadata and Setuptools configuration (e.g. deps, metadata, options, etc)
  • setup.py → Stub file for legacy tooling (boilerplate from above)

Once Setuptools support for metadata and configuration in pyproject.toml lands and stabilize, and tools that don’t support the new modern standards have been retired, or if you switch to another build system like flit that already does, this can be reduced to just a pyproject.toml with build system + metadata + setuptools config (what was previously in setup.cfg). You’ll want to make sure you require a minimum version of setuptools in the build system config that supports pyproject.toml metadata and config.

This is one of the things I love about PDM (and similar package managers): I really only need a pyproject.toml.

2 Likes

Yep—if we’re getting into tool recommendations, flit is a great tool for users developing library-type projects that want something that simplifies the process as much as practical while doing all the “right things” for them. But really, as you say, any of the modern PEP 517 backends are a good choice if you’re not (and don’t want to be) bound by supporting all the legacy ways of doing things.