Safeguard
Developer Tools•

Which pyproject.toml fields are required for a Python library?

Only name must be static in the project table, and version is required but may be dynamic. See which other fields to add before you publish a library.

Safeguard Security Team
4 min read

In the pyproject.toml project table, only name must be written out statically. version is also required, but you may list it as dynamic so your build backend fills it in. Every other field is optional, though a library you publish should set several more.

Required, required but dynamic, and optional

The pyproject.toml specification splits the project table into three groups. This is the part people usually get wrong.

GroupFieldsNotes
Must be staticnameCannot be listed as dynamic
Required, may be dynamicversionStatic value or filled by the backend
Optionaleverything elseStatic, dynamic, or omitted

If your pyproject.toml has no project table at all, the spec says the build backend provides all keys dynamically. That is legal, but it makes your metadata invisible to anyone reading the file.

What dynamic means

Static metadata is written directly in pyproject.toml. Dynamic metadata is listed under the dynamic key and computed by the backend at build time.

[project]
name = "example-library"
dynamic = ["version"]

When a field is dynamic, the specification makes it the backend's responsibility to fill it, and a backend must raise an error if it cannot. A common use is reading the version from a Git tag or from a __version__ attribute in your code.

One more rule is worth knowing. A list or table field can be specified statically and also listed as dynamic. The static entries are then fixed, and the backend may only append to them.

What a published library should also set

The minimum above gets you a build. A package other people depend on should carry more, because these fields are what appear on the PyPI project page and in installers.

  • requires-python. Declares the Python versions you support. Installers use it to pick a compatible release.
  • dependencies. Your runtime requirements, with bounds you have tested.
  • readme. Points at a README file whose extension sets how PyPI renders it.
  • license and license-files. A license expression plus the files that carry the text.
  • classifiers and urls. Search and browsing aids, plus links to source, issues and changelog.
  • authors or maintainers. Who to contact.

For the license pair, see the PEP 639 explainer.

A realistic small example

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "example-library"
dynamic = ["version"]
description = "One line that shows on the PyPI project page."
readme = "README.md"
requires-python = ">= 3.9"
license = "MIT"
license-files = ["LICENSE"]
dependencies = ["httpx >= 0.27"]

The build backend decides where a dynamic version comes from, so check its documentation for the matching setting.

Why this matters beyond tidiness

Scanners and installers read this metadata. The name and version decide which advisories match your package, and the declared dependencies decide what your users pull in. If the version is wrong or missing from published artifacts, nothing downstream can reason about it correctly.

Safeguard's dependency scanning and SBOM generation read declared dependencies and versions from projects. A free scan shows what your own metadata exposes.

Quick check

Can name be dynamic?

No. The specification lists name as the only key required to be statically defined.

Do I have to set a version by hand?

No. Version is required, but you can mark it dynamic and let your build backend compute it from a tag or from your code.

What happens with no project table?

The backend is then expected to provide all keys dynamically. It is valid, but the metadata is hidden from readers of the file.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.