Convert Python projects from requirements.txt to uv-managed pyproject.toml with a single command.
- Automatic Detection: Intelligently detects project metadata from your codebase
- Comprehensive Parsing: Handles complex
requirements.txtformats including:- Standard PyPI packages with version specifiers
- Git dependencies (HTTP, SSH)
- Local path dependencies (editable and non-editable)
- Environment markers
- Package extras
- Poetry-style version constraints (converts
^syntax)
- Smart Merging: Merges with existing
pyproject.tomlfiles without losing data - Multiple Requirements Files: Auto-detects and categorizes dev, test, and docs dependencies
- Interactive & Non-Interactive Modes: Works in CI/CD pipelines or with user prompts
- Validation: Validates generated
pyproject.tomlwith optionaluvintegration
Get started in 30 seconds:
# 1. Install directly from GitHub (one command!)
uv tool install git+https://github.com/danilop/requirements-to-uv.git
# 2. Navigate to your Python project
cd /path/to/your/project
# 3. Run the conversion
req2uv
# 4. Install dependencies with uv
uv sync
# 5. Done! Your project now uses uvThe tool automatically:
- ✅ Detects your project metadata (name, version, Python version)
- ✅ Finds all requirements.txt files (main, dev, test, etc.)
- ✅ Converts them to modern pyproject.toml format
- ✅ Validates the output
Common usage patterns:
# Interactive mode (default) - prompts for confirmation
req2uv
# Non-interactive mode - great for CI/CD
req2uv --non-interactive
# Preview changes without writing
req2uv --dry-run
# Specify custom requirements files
req2uv --requirements custom-reqs.txt --dev dev-reqs.txt# Install as a uv tool (available globally)
uv tool install git+https://github.com/danilop/requirements-to-uv.git
# Or install in current environment
uv pip install git+https://github.com/danilop/requirements-to-uv.git# Clone the repository
git clone https://github.com/danilop/requirements-to-uv.git
cd requirements-to-uv
# Install with uv
uv pip install -e .
# Or install as a uv tool
uv tool install .# Interactive mode with auto-detection
req2uv
# Specify requirements file explicitly
req2uv --requirements requirements.txt
# Specify multiple requirements files
req2uv --requirements requirements.txt --dev requirements-dev.txt --test requirements-test.txt# Override detected metadata
req2uv --name myproject --version 1.0.0 --python ">=3.11"
# Overwrite existing pyproject.toml (creates backup)
req2uv --overwrite
# Skip validation
req2uv --no-validate
# Verbose output
req2uv --verboseThe tool automatically detects project metadata from:
- Project name: Directory name (normalized)
- Version:
__init__.py,setup.py, or git tags (defaults to0.1.0) - Python version:
.python-versionfile, classifiers, or current Python version - Description: First line of README
- Authors: Git configuration
- License: LICENSE file analysis
- README: Detects README.md, README.rst, or README.txt
Automatically finds and categorizes requirements files:
requirements.txt→ main dependenciesrequirements-dev.txt→ dev dependency grouprequirements-test.txt→ test dependency grouprequirements-docs.txt→ docs dependency grouprequirements-lint.txt→ lint dependency group
Handles all common requirements.txt patterns:
requests>=2.28.0
flask[async]>=3.0.0
celery[redis,msgpack]>=5.3.0
pytest>=8.0.0 ; python_version >= "3.8"git+https://github.com/user/repo.git@main#egg=mypackage
git+https://github.com/user/repo.git@v1.0.0
-e git+https://github.com/user/repo.git@developConverts to:
[project]
dependencies = ["mypackage"]
[tool.uv.sources]
mypackage = { git = "https://github.com/user/repo.git", branch = "main" }-e ./local-package
../another-packageConverts to:
[tool.uv.sources]
local-package = { path = "./local-package", editable = true }
another-package = { path = "../another-package" }django^4.2.0Converts to:
dependencies = ["django>=4.2.0,<5.0.0"]With a warning comment about the conversion.
When pyproject.toml exists:
- Preserves existing metadata and sections
- Appends new dependencies to existing lists
- Detects duplicate packages
- Creates backup file (
pyproject.toml.backup)
[project]
name = "my-project"
version = "0.1.0"
description = "My awesome project"
readme = "README.md"
requires-python = ">=3.8"
dependencies = [
"requests>=2.28.0",
"flask>=3.0.0",
][project]
name = "my-project"
version = "1.0.0"
description = "A production-ready Python project"
readme = "README.md"
requires-python = ">=3.11"
authors = [
{ name = "John Doe", email = "john@example.com" }
]
license = { text = "MIT" }
dependencies = [
"requests>=2.28.0",
"flask[async]>=3.0.0",
"sqlalchemy>=2.0.0",
"celery[redis,msgpack]>=5.3.0",
"mypackage",
]
[dependency-groups]
dev = [
"pytest>=8.0.0",
"black>=24.0.0",
"ruff>=0.3.0",
]
test = [
"coverage>=7.0.0",
"pytest-cov>=4.0.0",
]
[tool.uv.sources]
mypackage = { git = "https://github.com/user/repo.git", branch = "main" }
local-package = { path = "../local", editable = true }
# Note: Original requirements.txt contained --index-url https://custom-index.com
# Configure this via: uv pip install --index-url https://custom-index.com- ✅ Standard PyPI packages
- ✅ Version specifiers (
==,>=,>,<,<=,!=,~=) - ✅ Git URLs (HTTPS)
- ✅ Local paths (editable and non-editable)
- ✅ Environment markers
- ✅ Package extras
- ✅ Poetry
^syntax (with conversion) - ✅ Comments (stripped)
- ✅ Line continuations (
\)
--hash=sha256:...)
- Not supported in
pyproject.toml - Use
uv.lockfor reproducible installs - Warning generated, hashes stripped
--index-url, --extra-index-url)
- Not stored in
pyproject.toml - Comment added with original URL
- Configure via
uvCLI or config
hg+, svn+, bzr+)
- Not supported by
uv - Warning generated, dependency skipped
- Converted to HTTPS if possible (GitHub, GitLab, Bitbucket)
- Warning generated for unknown hosts
Input (requirements.txt):
requests>=2.28.0
flask>=3.0.0
pytest>=8.0.0Command:
req2uvOutput (pyproject.toml):
[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.8"
dependencies = [
"requests>=2.28.0",
"flask>=3.0.0",
"pytest>=8.0.0",
]Input:
requirements.txt:requests>=2.28.0requirements-dev.txt:pytest>=8.0.0
Command:
req2uvOutput:
[project]
name = "my-project"
version = "0.1.0"
dependencies = ["requests>=2.28.0"]
[dependency-groups]
dev = ["pytest>=8.0.0"]Input (requirements.txt):
requests>=2.28.0
git+https://github.com/psf/requests.git@mainOutput:
[project]
dependencies = [
"requests>=2.28.0",
"requests",
]
[tool.uv.sources]
requests = { git = "https://github.com/psf/requests.git", branch = "main" }# In CI/CD pipeline
req2uv --non-interactive --no-validate
# Or with all metadata specified
req2uv \
--non-interactive \
--name myproject \
--version 1.0.0 \
--python ">=3.11"# Clone repository
git clone https://github.com/danilop/requirements-to-uv.git
cd requirements-to-uv
# Install dependencies
uv sync
# Install development dependencies
uv sync --group dev# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov=requirements_to_uv --cov-report=html
# Run specific test
uv run pytest tests/test_parser.py -v# Format code
uv run black .
# Lint
uv run ruff check .
# Type checking
uv run mypy src/requirements_to_uvQ: Tool says "No requirements.txt found"
A: Specify the path explicitly: req2uv --requirements /path/to/requirements.txt
Q: Some dependencies are missing in the output
A: Check warnings in verbose mode: req2uv --verbose
Q: Package names are different from requirements.txt
A: PyPI normalizes package names (e.g., python-dateutil -> python_dateutil). This is expected.
Q: Git SSH URLs fail to convert
A: The tool converts GitHub/GitLab/Bitbucket SSH URLs to HTTPS automatically. For other hosts, manually edit the generated pyproject.toml.
Q: Validation fails with "uv lock" errors
A: Some dependencies might not be compatible. Review the generated pyproject.toml and adjust version constraints.
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Run the test suite
- Submit a pull request
MIT License - see LICENSE file for details.
Built with:
- uv - Fast Python package installer
- packaging - Core packaging utilities
- click - CLI framework
- rich - Terminal formatting
- questionary - Interactive prompts