Understanding Constraints in Astral's uv

Astral's uv has taken the Python ecosystem by storm due to its exceptional speed and modern dependency resolution. When migrating legacy projects—especially those managed via standard pip—you often end up with a large constraints.txt file. Constraints define version bounds for packages if they end up being required by your dependencies, without forcing them to be installed directly.

In uv, project-level constraints are defined under the [tool.uv] table inside your pyproject.toml using the constraint-dependencies key:

[tool.uv]
constraint-dependencies = [
    "Jinja2==3.1.6",
    "requests==2.33.1"
]

Currently, uv does not provide a built-in CLI command like uv import constraints.txt to automatically write those constraints into pyproject.toml. However, you have two clean and idiomatic ways to resolve this.

Method 1: Keep using constraints.txt via configuration (Recommended for Large Files)

If your constraints.txt file contains dozens or hundreds of lines, pasting them into pyproject.toml can quickly clutter your configuration file. Instead of converting the file contents into inline TOML strings, you can instruct uv to read your existing constraints.txt directly.

Add the following setting to your pyproject.toml:

[tool.uv]
override-dependencies = []
# Pass constraints directly during resolution

When running commands, you can use the --constraint (or -c) flag:

uv lock --constraint constraints.txt
# or
uv pip sync --constraint constraints.txt

Alternatively, set the UV_CONSTRAINT environment variable in your CI/CD pipeline or local environment:

export UV_CONSTRAINT=constraints.txt

Method 2: Convert constraints.txt to pyproject.toml via Python Script

If you prefer to keep everything in a single pyproject.toml file and remove constraints.txt entirely, you can run a simple Python script to parse the requirement specs and update pyproject.toml programmatically.

Using Python 3.11+ (built-in tomllib) and the standard library (or tomli_w for writing TOML):

import path
from pathlib import Path
import tomllib
import tomli_w

# 1. Parse valid lines from constraints.txt
constraints_path = Path("constraints.txt")
constraints = []

if constraints_path.exists():
    for line in constraints_path.read_text().splitlines():
        line = line.strip()
        # Skip comments and empty lines
        if line and not line.startswith("#"):
            constraints.append(line)

# 2. Read pyproject.toml
pyproject_path = Path("pyproject.toml")
with open(pyproject_path, "rb") as f:
    pyproject_data = tomllib.load(f)

# 3. Update [tool.uv] constraint-dependencies
tool_section = pyproject_data.setdefault("tool", {})
uv_section = tool_section.setdefault("uv", {})
uv_section["constraint-dependencies"] = constraints

# 4. Write back to pyproject.toml
with open(pyproject_path, "wb") as f:
    tomli_w.dump(pyproject_data, f)

print(f"Successfully imported {len(constraints)} constraints into pyproject.toml!")

Note: Install tomli_w via uv pip install tomli-w or uv run --with tomli-w python script.py if you don't already have a TOML writer package installed.

Method 3: Quick One-Liner with Python

If you just want a quick inline command to update your pyproject.toml without extra dependencies, you can use a Python one-liner combined with standard regex formatting if your TOML structure is simple:

python3 -c '
from pathlib import Path
lines = [f"    \"{l.strip()}\"," for l in Path("constraints.txt").read_text().splitlines() if l.strip() and not l.startswith("#")]
print("[tool.uv]\nconstraint-dependencies = [\n" + "\n".join(lines) + "\n]")
'

Summary

  • Is there a native command? Not currently—uv doesn't have an automated parser command to populate constraint-dependencies directly from a file.
  • Best Practice: For very long constraint files, pass --constraint constraints.txt or set UV_CONSTRAINT=constraints.txt to keep pyproject.toml readable.
  • For single-file projects: Use the Python script with tomli_w to cleanly migrate your entries into pyproject.toml.