Skip to main content
Conversion Module

Convert requirements.txt to pyproject.toml

Convert a requirements.txt (or requirements.in) into a pyproject.toml — runtime and dev dependency groups, with extras, version pins and environment markers preserved. Runs in your browser.

Why use this tool?

Use this converter when you're moving a project from a requirements.txt (or a pip-tools requirements.in) to a modern pyproject.toml — the layout uv, Hatch and pip all read. Paste your runtime requirements (and dev requirements separately), fill in the project name/version/requires-python, and get a pyproject.toml with your dependencies under [project] and your dev tools under [dependency-groups].dev. Extras, version specifiers and environment markers are carried over verbatim, and anything that can't be represented as a dependency — hashes, index URLs, constraints, editable installs — is reported rather than silently dropped.

Advertisement
Project metadata → [project]
requirements.txt → dependencies
dev requirements (optional) → [dependency-groups].dev

Paste a requirements.txt (or a requirements.in). A pip freeze file works too, but it lists transitive packages as well — review which ones actually belong in your project's dependencies.

Converted 4 runtime + 2 dev dependencies. This is a faithful syntax conversion — dependencies are not resolved or pinned; run uv lock locally to resolve and verify.
pyproject.toml
Line-by-line mapping (6)
linesourceresult
1requests>=2.31,<3requests>=2.31,<3
2flask[async]==3.0.1flask[async]==3.0.1
3numpy>=1.26 ; python_version >= "3.10"numpy>=1.26 ; python_version >= "3.10"
4click~=8.1click~=8.1
1pytest>=8pytest>=8
2ruffruff
Finish with uv (separate workflow)

The file above is an offline syntax conversion. To actually resolve and lock versions, download it and run uv locally — this is where dependency resolution happens (this tool doesn't resolve or contact PyPI):

# after saving pyproject.toml in your project: uv lock          # resolve + pin all versions into uv.lock uv sync          # create the environment

Or skip this converter and let uv build the project from scratch — uv resolves versions and may add its own bounds, so the result can differ from the offline conversion above:

uv init --bare
uv add "click~=8.1" "flask[async]==3.0.1" "numpy>=1.26 ; python_version >= "3.10"" "requests>=2.31,<3"
uv add --dev "pytest>=8" "ruff"

Runs entirely in your browser. It converts requirement declarations and reports the installation instructions it can't carry over (hashes, index URLs, constraints, editable installs) — an output can be valid TOML yet not reproduce your original install, so review the unresolved items before shipping.

Tool facts

Supported Python
PEP 621 [project] · PEP 735 dependency-groups · uv-style output
Input limit
Limited only by your device
Last reviewed
2026-09-05

What it can't do

  • Resolve or pin versions — it converts syntax only; run 'uv lock' locally to resolve and verify.
  • Contact PyPI or check that a package (or a wheel for your Python/OS) actually exists.
  • Follow -r / -c file includes — paste each referenced file's contents into the matching input.
  • Auto-migrate editable installs (-e) or turn a pip freeze into a curated dependency list — those need manual review.

About requirements.txt → pyproject.toml Converter

A pyproject.toml declares your project's dependencies in a standard, tool-agnostic way: runtime dependencies go in [project].dependencies (PEP 621), and development dependencies go in [dependency-groups] (PEP 735) — standardized dev groups that are excluded from your built package's metadata. This tool maps a requirements.txt onto that structure: each requirement line becomes a PEP 508 dependency string, and a separate dev input becomes the dev group.

It preserves meaning before formatting. Version pins, extras (like flask[async]) and environment markers (like ; python_version >= "3.10") are kept exactly — markers are not rewritten (python_version and python_full_version are different variables), and entries are never de-duplicated by name, because two lines for the same package can carry different markers for different Python versions or platforms. Sorting the output is a presentation choice, not a correctness one.

Crucially, a requirements file is a mix of dependency declarations and installation instructions, and a converted file can be valid TOML yet not reproduce the original install. So the tool separates four things: TOML validity, dependency-syntax validity, conversion completeness, and dependency resolution. It does the first three; it does not resolve versions or contact PyPI. Dropped --hash verification, --index-url configuration, and skipped -c constraints are flagged prominently as an 'unresolved items' list, and the output is labelled Incomplete until you review them. To actually resolve and pin, download the file and run uv lock locally.

Related: package your project with the Python-to-EXE builder, or format and lint it with the Ruff tool and type checker.

Supported requirements syntax

What the converter recognises and how each line is handled. Declarations become dependencies; installation instructions are reported as unresolved items.

name, version specifiers, extras

requests>=2.31,<3 and flask[async]==3.0.1 convert directly, kept verbatim.

environment markers

numpy>=1.26 ; python_version >= "3.10" is preserved exactly (never rewritten to python_full_version).

direct URL references

pkg @ https://host/pkg-1.0-py3-none-any.whl is kept, including any #fragment (URL fragments are not mistaken for comments).

dev requirements

the separate dev input becomes [dependency-groups].dev (PEP 735).

line continuations & comments

backslash continuations are joined first (like pip), then inline comments are stripped — a '#' inside a URL is left intact.

--hash=…

removed from the dependency and reported: pyproject dependencies don't carry hashes, so pinned-artifact verification is no longer enforced (use uv lock).

--index-url / --extra-index-url / -f

reported, not converted — these change where packages come from and belong in uv/pip config, not pyproject.

-c constraints

reported as incomplete — constraints restrict versions without adding dependencies and can't become [project] dependencies.

-r includes

reported — the nested file isn't loaded; paste its contents into the matching input.

-e editable / -e .

reported for manual migration; -e . is your own project, not a dependency, so it is never converted into a self-dependency.

Frequently Asked Questions

No. It's a faithful syntax conversion — it doesn't contact PyPI or resolve a version set. Your pins, ranges and markers are carried over as written, but the result isn't locked. Download the pyproject.toml and run 'uv lock' (then 'uv sync') locally to resolve and pin everything into a lockfile.