PEP 829 – Structured Startup Configuration via .site.toml Files
- Author:
- Barry Warsaw <barry at python.org>
- Discussions-To:
- Pending
- Status:
- Draft
- Type:
- Standards Track
- Topic:
- Packaging
- Created:
- 31-Mar-2026
- Python-Version:
- 3.15
- Post-History:
Abstract
This PEP proposes a TOML-based configuration file format to replace
the .pth file mechanism used by site.py during interpreter
startup. The new format, using files named <package>.site.toml,
provides structured configuration for extending sys.path and
executing package initialization code, replacing the current ad-hoc
.pth format that conflates path configuration with arbitrary code
execution.
Motivation
Python’s .pth files (processed by Lib/site.py at startup)
support two functions:
- Extending
sys.path– Lines in this file (excluding comments and lines that start withimport) name directories to be appended tosys.path. Relative paths are implicitly anchored at the site-packages directory. - Executing code – lines starting with
import(orimport\\t) are executed immediately by passing the source string toexec().
This design has several problems:
- Code execution is a side effect of the implementation. Lines that
start with
importcan be extended by separating multiple statements with a semicolon. As long as all the code to be executed appears on the same line, it all gets executed when the.pthfile is processed. .pthfiles are essentially unstructured, leading to contents which are difficult to reason about or validate, and are often even difficult to read. It mixes two potentially useful features with different security constraints, and no way to separate out these concerns.- The lack of
.pthfile structure also means there’s no way to express metadata, no future-proofing of the format, and no defined execution or processing order of the contents. - Using
exec()on the file contents during interpreter startup is a broad attack surface. - There is no explicit concept of an entry point, which is an
established pattern in Python packaging. Packages that require
code execution and initialization at startup abuse
importlines rather than explicitly declaring entry points.
Specification
This PEP defines a new file format called <package>.site.toml
which addresses all of the stated problems with .pth files. Like
.pth files, <package>.site.toml files are processed at Python
startup time by the site.py module, which means that the -S
option, which disables site.py also disables
<package>.site.toml files.
The standard library tomllib package is used to read and process
<package>.site.toml files.
The presence of a <package>.site.toml file supersedes a parallel
<package>.pth file. This allows for both an easy migration path and
continued support for older Pythons in parallel.
Any parsing errors cause the entire <package>.site.toml file to be ignored
and not processed (but it still supersedes any parallel <package>.pth
file). Any errors that occur when importing entry point modules or calling
entry point functions are reported but do not abort the Python executable.
File Naming and Discovery
- As with
.pthfiles, packages may optionally install a single<package>.site.toml, just like the current.pthfile convention. - The file naming format is
<package>.site.toml. The.sitemarker distinguishes these from other TOML files that might exist in site-packages and describes the file’s purpose (processed bysite.py). - The
<package>prefix should match the package name, but just like with.pthfiles, the interpreter does not enforce this. Build backends and installers **MAY** impose stricter constraints if they so choose. - The package name (i.e. the
<package>prefix) MUST follow the standard name normalization rules. <package>.site.tomlfiles live in the same site-packages directories where<package>.pthfiles are found today.- The discovery rules for
<package>.site.tomlfiles is the same as<package>.pthfiles today. File names that start with a single.(e.g..site.toml) and files with OS-level hidden attributes (UF_HIDDEN,FILE_ATTRIBUTE_HIDDEN) are excluded. - The processing order is alphabetical by filename, matching
.pthbehavior. - If both
<package>.site.tomland<package>.pthexist in the same directory, only the<package>.site.tomlfile is processed. In other words, the presence of a<package>.site.tomlfile supersedes a parallel<package.pth>file, even if the format of the TOML file is invalid.
Processing Model
All <package>.site.toml files in a given site-packages directory
are read and parsed into an intermediate data structure before any
processing (i.e. path extension or entry point execution) occurs.
This two-phase approach (read then process) enables:
- A future policy mechanism that can inspect and modify the collected data before execution (e.g., disabling entry points for specific packages or enforcing path restrictions). NOTE: Such a policy framework is explicitly out-of-scope for this PEP.
- Future finer-grained control over the processing of path extensions
and entry point execution. For example, one could imagine special
-Xoptions, environment variables, or other types of configuration that allow path extensions only, or can explicitly manage allow or deny lists of entry points. NOTE: Such configuration options are explicitly out-of-scope for this PEP. - Better error reporting. All parsing, format, and data type errors can be surfaced before any processing occurs.
Within each site-packages directory, the processing order is:
- Discover and parse all
<package>.site.tomlfiles, sorted alphabetically. - Process all
[paths]entries from the parsed TOML files. - Execute all
[entrypoints]entries from the parsed TOML files. - Process any remaining
<package>.pthfiles that are not superseded by a<package>.site.tomlfile.
This ensures that path extensions are in place before any entry point code
runs, and that <package>.site.toml-declared paths are available to both
entry point imports and <package>.pth import lines.
TOML file schema
A <package>.site.toml file is defined to have three sections, all of which
are optional:
[metadata]
schema_version = 1
[paths]
dirs = ["../lib", "/opt/mylib", "{sitedir}/extra"]
[entrypoints]
init = ["foo.startup:initialize", "foo.plugins"]
The [metadata] section
This section contains package and/or file metadata. The only defined key is
the the optional schema_version key.
schema_version(integer, recommended)- The TOML file schema version number. Must be the integer
1for this specification. If present, Python guarantees forward-compatible handling: future versions will either process the file according to the declared schema or skip it with clear diagnostics. If theschema_versionis present but has an unsupported value, the entire file is skipped. Ifschema_versionis omitted, the file is processed on a best-effort basis with no forward-compatibility guarantees.
Additional keys are permitted and preserved, although they are ignored for the purposes of this PEP.
The [paths] section
Defined keys:
dirs- A list of strings specifying directories to append to
sys.path.
Path entries use a hybrid resolution scheme:
- Relative paths are anchored at the site-packages directory (sitedir),
matching current
.pthbehavior. For example,../libin a file under/usr/lib/python3.15/site-packages/resolves to/usr/lib/python3.15/lib. - Absolute paths are preserved as-is. For example,
/opt/mylibis used exactly as written. - Placeholder variables are supported using
{name}syntax. The placeholder{sitedir}expands to the site-packages directory where the<package>.site.tomlfile was found. Thus{sitedir}/relpathandrelpathresolve to the same path with the placeholder version being the explicit (and recommended) form of the relative path form.
While only {sitedir} is defined in this PEP, additional
placeholder variables (e.g., {prefix}, {exec_prefix},
{userbase}) may be defined in future PEPs.
If dirs is not a list of strings, a warning is emitted (visible
with -v) and the section is skipped.
Directories that do not exist on the filesystem are silently skipped, matching
<package>.pth behavior. Paths are de-duplicated, also matching
<package>.pth behavior.
The [entrypoints] section
init – a list of strings specifying entry point
references to execute at startup. Each item uses the standard Python
entry point syntax: package.module:callable.
- The
:callableportion is optional. If omitted (e.g.,package.module), the module is imported viaimportlib.import_module()but nothing is called. This covers the common<package>.pthpattern ofimport foofor side effects. - Callables are invoked with no arguments.
- Entries are executed in the listed order.
- The
[extras]syntax from the packaging entry point spec is not supported; it is installer metadata and has no meaning at interpreter startup.
General Schema Rules
- All three sections are optional. An empty
<package>.site.tomlfile is a valid no-op. - Unknown tables are silently ignored, providing forward compatibility for future extensions.
[paths]is always processed before[entrypoints], regardless of the order the sections appear in the TOML file.
Error Handling
Errors are handled differently depending on the phase:
- Phase 1: Reading and Parsing
- If a
<package>.site.tomlfile cannot be opened, decoded, or parsed as valid TOML, it is skipped and processing continues to the next file. Errors are reported only when-v(verbose) is given. Importantly, a<package>.site.tomlfile that fails to parse still supersedes its corresponding<package>.pthfile. The existence of the<package>.site.tomlfile is sufficient to suppress<package>.pthprocessing, regardless of whether the TOML file parses successfully. This prevents confusing dual-execution scenarios and ensures that a broken<package>.site.tomlis noticed rather than silently masked by fallback to the<package>.pthfile. - Phase 2: Execution
- If a path entry or entry point raises an exception during processing, the
traceback is printed to
sys.stderr, the failing entry is skipped, and processing continues with the remaining entries in that file and subsequent files.
This is a deliberate improvement over .pth behavior, which aborts
processing the remainder of a file on the first error.
Rationale
- TOML as the configuration format
- TOML is already used by
pyproject.tomland is familiar to the Python packaging ecosystem. It is an easily human readable and writable format that aids in validation and auditing. TOML files are structured and typed, and can be easily reasoned about. TOML files allows for easy future extensibility. Thetomllibmodule is available in the standard library since Python 3.11. - The
<package>.site.tomlnaming convention - A double extension clearly communicates purpose: the
.sitemarker indicates this is a site-startup configuration file, while.tomlindicates the format. This avoids ambiguity with other TOML files that might exist in site-packages now or in the future. The package name prefix preserves the current<package>.pthconvention of a single startup file per package. - Hybrid path resolution
- Implicit relative path joining (matching
<package>.pthbehavior) provides a smooth migration path, while{sitedir}and future placeholder variables offer explicit, extensible alternatives. As with<package>.pthfiles, absolute paths are preserved and used verbatim. importlib.import_module()instead ofexec()- Using the standard import machinery is more predictable and auditable than
exec(). It integrates with the import system’s hooks and logging, and thepackage.module:callablesyntax is already well-established in the Python packaging ecosystem (e.g.,console_scripts). Allowing for optional:callablesyntax preserves the import-side-effect functionality of<package>.pthfiles, making migration easier. - Two-phase processing
- Reading all configuration before executing any of it provides a natural extension point for future policy mechanisms and makes error reporting more predictable.
- Alphabetical ordering with no priority mechanism
- Packages are installed independently, and there is no external arbiter of
priority. Alphabetical ordering matches
<package>.pthbehavior and is simple to reason about. Priority could be addressed by a future site-wide policy configuration. schema_versionas recommended, not required- Requiring
schema_versionwould make the simplest valid file more verbose. Making it recommended strikes a balance: files that include it get forward-compatibility guarantees, while simple files that omit it still work on a best-effort basis. - Continue on error rather than abort
- The
<package>.pthbehavior of aborting the rest of a file on the first error is unnecessarily harsh. If a package declares three entry points and one fails, the other two should still run.
Backwards Compatibility
<package>.pthfile processing is not deprecated or removed. Both<package>.pthand<package>.site.tomlfiles are discovered in parallel within each site-packages directory. This preserves backward compatibility for all existing (pre-migration) packages. Deprecation of<package>.pthfiles is out-of-scope for this PEP.- When
<package>.site.tomlexists alongside<package>.pth, the<package>.site.tomltakes precedence and the<package>.pthfile is skipped, providing for a natural migration path and easy compatibility with older versions of Python which are unaware of<package>.site.tomlfiles. - Within a site-packages directory, all
<package>.site.tomlfiles are fully processed (paths and entry points) before any remaining<package>.pthfiles. - The
site.addsitedir()public API retains its existing signature and continues to acceptknown_paths.
Security Implications
This PEP improves the security posture of interpreter startup:
<package>.site.tomlfiles replaceexec()withimportlib.import_module()and explicitgetattr()calls, which are more constrained and auditable.io.open_code()is used to read<package>.site.tomlfiles, ensuring that audit hooks (PEP 578) can monitor file access.- The two-phase processing model creates a natural point where a future policy mechanism could inspect and restrict what gets executed.
- The
package.module:callablesyntax limits execution to importable modules and their attributes, unlikeexec()which can run arbitrary code.
The overall attack surface is not eliminated – a malicious package
can still cause arbitrary code execution via init entrypoints, but
the mechanism proposed in this PEP is more structured, auditable, and
amenable to future policy controls.
How to Teach This
For tool makers
Build backends and installers should generate <package>.site.toml
files alongside or instead of <package>.pth files, depending on
the package’s Python support matrix. The TOML format is easy to
generate programmatically using tomllib (for reading) or string
formatting (for writing, since the schema is simple).
Build backends SHOULD ensure that the <package> prefix matches
the package name.
Installers MAY validate or enforce that the <package> prefix
matches the package name.
Reference Implementation
A reference implementation
is provided as modifications to Lib/site.py, adding the following:
_SiteTOMLData– a__slots__class holding parsed data from a single<package>.site.tomlfile (metadata, dirs, init)._read_site_toml(sitedir, name)– reads and parses a single<package>.site.tomlfile, validates types, and returns a_SiteTOMLDatainstance orNoneon error._process_site_toml_paths(toml_data_list, known_paths)– processes[paths].dirsfrom all parsed files, expanding placeholders and adding directories tosys.pathas appropriate._process_site_toml_entrypoints(toml_data_list)– executes[entrypoints].initfrom all parsed files.- Modified
addsitedir()– orchestrates the three-phase flow: discover and parse<package>.site.tomlfiles, process paths and entry points, then process remaining<package>.pthfiles.
Tests are provided in Lib/test/test_site.py in the
SiteTomlTests class.
Rejected Ideas
- Single configuration file instead of per-package files
- A single site-wide configuration file was considered but rejected
because it would require coordination between independently
installed packages and would not mirror the
<package>.pthconvention that tools already understand. - JSON instead of TOML
- JSON lacks comments and is less human-friendly. TOML is already
the standard configuration format in the Python ecosystem via
pyproject.toml. - YAML instead of TOML
- There is no standard YAML parser in the standard library.
- Python instead of TOML
- Python is imperative, TOML is declarative. Thus TOML files are much more readily validated and reasoned about.
$schemaURL reference- Unlike JSON, TOML has no standard
$schemaconvention. A simple integerschema_versionis sufficient and self-contained. - Required
schema_version - Requiring
schema_versionwould make the simplest valid file more verbose without significant benefit. The recommended-but-optional approach balances simplicity with future-proofing. - Separate
loadandexecutekeys in[entrypoints] - Splitting import-only and callable entry points into separate lists
was considered but rejected because it complicates execution
ordering. A single
initlist with both forms keeps ordering explicit. - Priority or weight field for processing order
- Since packages are installed independently, there is no arbiter of
priority. Alphabetical ordering matches
<package>.pthbehavior. Priority could be addressed by a future site-wide policy configuration file, not per-package metadata. - Passing arguments to callables
- Callables are invoked with no arguments for simplicity and parity
with existing
<package>.pthimport behavior. Future PEPs may define an optional context argument (e.g., the parsed TOML data or a site info object).
Open Issues
- Should a warning be emitted when both
<package>.pthand<package>.site.tomlcoexist? - Should future
-Xoptions provide fine-grained control over error reporting, unknown table warnings, and entry point execution? - Should callables receive context (e.g., the path to the
<package>.site.tomlfile, the parsed TOML data, or a site info object)? - What additional placeholder variables should be supported beyond
{sitedir}? Candidates include{prefix},{exec_prefix}, and{userbase}.
Change History
None at this time.
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.
Source: https://github.com/python/peps/blob/main/peps/pep-0829.rst
Last modified: 2026-04-01 21:48:00 GMT