runtime
147 / 250
lesson 147 Packaging & Databases · advanced

pyproject.toml: describe the project

One file tells every tool what your project is: its name and version, the Python it needs, the packages it depends on, and the commands it installs. tomllib reads that file into ordinary dictionaries.

You'll be able to
  • Recognise TOML's tables, keys, quoted strings and arrays, and what the [project] table holds
  • Load TOML text with tomllib.loads, or a file opened in binary mode with tomllib.load
  • Read optional tables safely and keep your own tool's settings under [tool.<name>]
The idea

pip, build tools, linters and test runners all need to know something about your project: what it's called, which version this is, which Python it supports and which packages it needs. Since 2021 the standard place for all of it is `pyproject.toml` in the project's top folder.

The file is written in TOML. A line [project] starts a table, a group of settings; [project.scripts] is a table inside project. Each setting is key = value. Text is always in quotes, numbers and true/false aren't, and ["a", "b"] is an array. Python's tomllib module (3.11 and later) turns the whole file into dictionaries, lists, strings, numbers and booleans.

Run each example to see what it prints. Running examples doesn't count toward progress.

example-1.py · Read the [project] table
import tomllib

pyproject = """
[project]
name = "market-reports"
version = "1.4.2"
requires-python = ">=3.11"
dependencies = [
    "requests>=2.31",
    "pandas>=2.2,<3",
]

[project.optional-dependencies]
charts = ["matplotlib>=3.8"]
"""

data = tomllib.loads(pyproject)
project = data["project"]
print(project["name"], project["version"])
print(project["requires-python"])
print(project["dependencies"])
print(project["optional-dependencies"]["charts"])

TOML is stricter than it looks. A version number is text, so it needs quotes; without them the parser stops and says where.

example-2.py · An unquoted version
import tomllib

text = """
[project]
name = "market-reports"
version = 1.4.2
"""
data = tomllib.loads(text)
print(data)

Real files live on disk. tomllib.load reads an open file, and it needs the file opened in binary mode, "rb", because TOML defines its own text encoding (UTF-8).

Not every project has every table, so read optional ones with get and a default. Tools keep their own settings in [tool.<name>] tables ([tool.pytest.ini_options], [tool.ruff]): your own scripts can do the same instead of inventing another settings file.

example-3.py · Commands and tool settings from a file
import tomllib
from pathlib import Path

Path("pyproject.toml").write_text("""
[project]
name = "market-reports"
version = "1.4.2"

[project.scripts]
market-report = "market_reports.cli:main"

[tool.market-reports]
currency = "USD"
top_n = 5
""", encoding="utf-8")

def load_pyproject(path):
    with open(path, "rb") as f:
        return tomllib.load(f)

def commands(data):
    scripts = data["project"].get("scripts", {})
    lines = []
    for command, target in scripts.items():
        module, function = target.split(":")
        lines.append(f"{command}: runs {function}() from {module}")
    return lines

data = load_pyproject("pyproject.toml")
print(commands(data))
print(data["tool"]["market-reports"])
print(commands({"project": {"name": "invoice-tools"}}))

When to use it: whenever a program needs facts about the project (its version for a --version flag, its settings), read them from pyproject.toml instead of copying them into code. Mistakes to avoid: unquoted text values; opening the file without "rb"; and assuming an optional table such as [project.scripts] or [tool.x] exists.

Review
  • pyproject.toml describes the project: [project] has name, version, requires-python, dependencies and more.
  • tomllib.loads(text) or tomllib.load(open(path, "rb")) gives dictionaries whose values keep their TOML types.
  • Read optional tables with get and a default; keep your own settings in [tool.<name>].
  • Next: what a dependency line like pandas>=2.2,<3 means, and how to check what is actually installed.

Loading your progress…

To master this lesson, solve both challenges without hints or the answer, and pass the concept check.

pyproject.toml: describe the project — Packaging & Databases · Runtime