# What is PEP 508?


[PEP 508: Dependency specification for Python Software Packages](https://peps.python.org/pep-0508/) defines the standard format for declaring dependencies on Python packages. Every [pyproject.toml](https://pydevtools.com/handbook/reference/pyproject.toml.md) dependency list and every [pip](https://pydevtools.com/handbook/reference/pip.md) requirement string uses PEP 508 syntax, and so does every [Poetry](https://pydevtools.com/handbook/reference/poetry.md) dependency.

## How a requirement string is built

A PEP 508 requirement string specifies a package name, optional version constraint, optional extras, and optional environment markers:

```
package-name>=1.0,<2.0; extra == 'dev' and python_version >= '3.9'
```

The components are:

1. **Package name**: `requests`, `django`, `numpy`
2. **Version specifier** (optional): `>=1.0`, `==2.3.4`, `~=1.2.3` (see [PEP 440](https://pydevtools.com/handbook/explanation/versioning-python-packages-semver-calver-and-pep-440.md) for syntax)
3. **Extras** (optional): `[dev,test]` to request optional feature bundles
4. **Environment markers** (optional): `;` followed by conditions like `python_version >= '3.9'` or `sys_platform == 'win32'`

## Examples

| Requirement string | What it installs |
|---|---|
| `numpy` | Any version, any platform |
| `requests>=2.25.0,<3.0` | requests 2.x only |
| `flask[async]` | flask with async extras |
| `pandas[excel,plot]` | pandas with multiple extras |
| `pywin32; sys_platform == 'win32'` | Windows only |
| `numpy>=1.20; python_version >= '3.9'` | Version and platform combined |

## Environment markers

Markers allow conditional dependencies based on the installation environment:

- `python_version`: active Python version, e.g. `'3.9'`, `'3.10'`
- `sys_platform`: `'linux'`, `'darwin'` (macOS), `'win32'`, etc.
- `platform_machine`: CPU architecture, e.g. `'x86_64'`, `'arm64'`
- `platform_system`: OS name, `'Linux'`, `'Darwin'`, or `'Windows'`
- `implementation_name`: Python implementation, `'cpython'`, `'pypy'`, etc.
- `extra`: the optional-dependency group being installed

PEP 508 also defines `os_name`, `python_full_version`, `platform_release`, and `platform_version`.

## Where PEP 508 appears

In `pyproject.toml` dependencies:

```toml
[project]
dependencies = [
    "requests>=2.25",
    "dataclasses-json; python_version < '3.7'",
    "typing-extensions; python_version < '3.8'",
]

[project.optional-dependencies]
dev = [
    "pytest>=6.0",
    "mypy; python_version >= '3.8'",
]
```

In `requirements.txt` files:

```
requests>=2.25
numpy>=1.20; python_version >= '3.9'
```

In [pip](https://pydevtools.com/handbook/reference/pip.md) and [uv](https://pydevtools.com/handbook/reference/uv.md) commands:

```bash
pip install 'requests>=2.25,<3.0'
uv add 'numpy>=1.20; python_version >= "3.9"'
```

## Version specifier vs. environment marker

Both constrain a dependency, but they operate differently:

- **Version specifier** (`>=1.0,<2.0`): filters which package versions are acceptable
- **Environment marker** (`; python_version >= '3.9'`): controls whether to install the dependency at all

`numpy>=1.20; python_version >= '3.9'` means "install numpy 1.20 or later, but only on Python 3.9+."

## Why one syntax works across tools

PEP 508 standardizes the format so pip, uv, Poetry, and Hatch all parse the same dependency strings. A project can switch tools without rewriting its dependency list.

## Learn More

- [PEP 508 Specification](https://peps.python.org/pep-0508/)
- [PEP 440: Versioning specification](https://pydevtools.com/handbook/explanation/versioning-python-packages-semver-calver-and-pep-440.md) for version specifier syntax
- [pyproject.toml reference](https://pydevtools.com/handbook/reference/pyproject.toml.md)
- [pip requirements file format](https://pip.pypa.io/en/stable/reference/requirements-file-format/)
