Making Sense of pyproject.toml, setup.cfg, and setup.py
Published: 2022-09-22 15:54:00

So you decided you want to make a python package thats awesome! But after reading through the documentation and numerous blog posts you might be feeling utterly confused as to what exactly you need to make a pypi package. I'm here to explain the connections between `pyproject.toml`, `setup.cfg` and`setup.py` .I'll show you how you can make sure your project will be future proof.

## TLDR

1. `pyproject.toml`: use to declare your package's metadata and specify what build tools(like setuptools or poetry) to use to build your package. Use the command `python -m build` to build your package wheel and sdist.
2. `setup.cfg`:  use to declare your package's metadata (if you didn't already use pyproject.toml to declare your metadata).
3. `setup.py` (deprecated) : used to declare your package's metadata and configuration options. Use the command `setup.py sdist setup.py bdist_wheel` to build your package wheel and sdist.


## What is a pyproject.toml?
`pyproject.toml` tells pip what build tools it needs to build sdist and wheels. Before pyproject.toml was introduced pip had no way of knowing what build tools (tools like peotry, setuptools, etc.) it needed to use to build the wheel for your project. With `pyproject.toml` you can specify exactly what version of the build tool you need to correctly build your pip package in your virtual environment. For example, the following `pyproject.toml` tells pip the build tool it needs to build your package with `setuptools`, specifically a version greater than or equal to 61.0.


```
[build-system]
requires = ["setuptools>=61.0"]
build-backend = ["setuptools.build_meta"]

```

You can read more about how pyproject.toml works [here](https://snarky.ca/what-the-heck-is-pyproject-toml/).

### Why do I need `pyproject.toml`?
You need it because it tells pip what build tools pip needs to use build your package in your virtual environment. Not to mention `setup.py` is (deprecated)[https://setuptools.pypa.io/en/latest/deprecated/commands.html] so you should declare you package's metadata in your `pyproject.toml` file too. Of course you  can also specify the [project's metadata](#whats-metadata) in your `setup.cfg` if you'd like too, but I prefer to keep it all in `pyproject.toml`.
Additionally, certain tools like `poetry` and `flit` use this file to specify how to build you package, its dependencies, etc. [This article](https://www.python-engineer.com/posts/python-poetry-tutorial/#:~:text=By%20default%2C%20poetry%20creates%20a,is%20not%20stored%20in%20pyproject.) explains more in detail how `poetry` uses the `pyproject.toml`. If you want to know how to structure your pyproject.toml file check out the [guide](https://dev.to/2320sharon/how-to-build-a-pyprojecttoml-file-4mk8) I wrote.

- In case you're curious, you must name your package's `.toml` file `pyproject.toml` otherwise you package won't build correctly.

## What is a `setup.py`?
`setup.py` is a script that can executed on the command line that is used to build a source distribution and wheel for your package with the command `setup.py sdist setup.py bdist_wheel` . You can also declare metadata and package options in your `setup.py`. However, I do not recommend you use `setup.py` as [it is being deprecated](https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html). Instead you should use `pyproject.toml` to declare your package's metadata and build dependencies.

### Do I need `setup.py`?
You shouldn't use `setup.py` because its deprecated instead you should use `pyproject.toml` to declare the build tools and metadata for your package. You shouldn't invoke `setup.py` directly to perform tasks like uploading to pypi or building you package. The following table shows what commands to use instead of the deprecated `setup.py` commands: 

| setup.py command                    | New Command |
|-------------------------------------|-------------|
| setup.py sdist setup.py bdist_wheel | python -m build (with build)         |
| setup.py test                       |  pytest (usually via tox or nox)           | 
| setup.py install                    | pip install          |
| setup.py develop                    | pip install -e           | 
| setup.py upload                     | twine upload (with twine)          | 
| setup.py check                      |  twine check (this doesn't do all the same checks but it's a start)           |  
This table I got from [Why you should not invoke setup.py directly](https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html#:~:text=You%20may%20still%20find%20advice,CLI%20tools%20like%20pip%2C%20build). It explains why setup.py shouldn't be used as well as why there is so much confusion around why setup.py shouldn't be used. I highly recommend reading it if you are wondering why `setup.py` is being deprecated.


## What is a `setup.cfg`?
A `setup.cfg` is a configuration file used to declare the metadata for your package. Metadata is information that describes your package such as package's name, version, description, etc. All this information can be included in your `setup.cfg` or your `pyproject.toml`. If you want more details on how to use `setup.cfg` check out [setuptools page](https://setuptools.pypa.io/en/latest/userguide/declarative_config.html).

If you want to use `setup.cfg` to declare your package's metadata and want to use `setup.py` still here is what your `setup.py` should look like:

```
from setuptools import setup
if __name__ == '__main__':
    setup()
```
Using `setup.cfg` and `setup.py` together will cause `setup.py` to use `setup.cfg`'s metadata to create the python package so you don't have to declare the metadata directly in the `setup.py` file.

## Do I need `setup.cfg`?
If you only used your `pyproject.toml` file to declare your build tools then you can use `setup.cfg` to declare all your metadata like your package's name, version number and so forth.

`pyproject.toml` could declare build dependencies:
```
[build-system]
requires = ["setuptools>=61.0"]
build-backend = ["setuptools.build_meta"]

```
`setup.cfg` could contain all your package's metadata:
```
[metadata]
name = sample_pkg
version = 1.0.0
author = Sharon Fitzpatrick
description = this is the smallest description
long_description = This is a longest description
url = https://github.com/pypa/packaging.python.org
keywords = python,example
[options]
python_requires = >=3.8, <4
install_requires = 
    numpy
    matplotlib
[options.extras_require]
test = 
    pytest
    coverage
[options.package_data]
sample = 
    sample_data.csv'
```

### What's Metadata?
Metadata is information that describes your package. Metadata is information like the package's name, version, and description.