Upgrade a venv and rewrite requirements to match installed versions, safely and atomically. Preserves operators (
==,>=,~=), extras, markers, comments, encoding, and line endings.
-
Safety first Refuse outside a venv by default. Abort on
--hashunless you opt in to skipping those stanzas. Atomic writes with rollback. -
Do one job well Upgrade env, then floor top-level requirements to whatβs actually installed. No lockfile management.
-
Zero ceremony Works from the CLI without any config. Optional TOML/pyproject config if you want it.
-
Agent friendly Clean Python API that returns a structured
Resultfor tool-calling, MCP, CrewAI, AG2, etc.
- Agent & CI Friendly: Structured Python API and
--json-reportflag for easy integration into automated workflows. - Venv guard on by default;
--system-okto override. - Preserves extras
[a,b], markers; sys_platform != "win32", inline comments, encoding (BOM), and newline style (LF/CRLF). - Includes recursion: follows
-r other.txtby default. - Constraints awareness: detects
-c constraints.txtand skips modifying unless--update-constraints. - Policy modes:
lower-bound(default),floor-only,floor-and-cap(< next major). - Pre/dev handling:
--allow-prereleaseto adopt,--keep-localto keep+localsuffixes. - Hash-aware: refuses by default if
--hashis present;--allow-hashesskips those stanzas without editing. - File locking with
portalocker. Backup + atomic replace. Rollback on failure. - UX for humans and CI:
--dry-run,--show-diff,--check,--json-report,--only/--exclude. - Minimal runtime deps.
pip install reqsyncRequires Python 3.8+.
# Activate your venv (required by default)
source .venv/bin/activate
# Preview
reqsync run --path requirements.txt --dry-run --show-diff
# Apply with backup
reqsync run --path requirements.txt --show-diffCommon variations:
# Read-only sync to current env (donβt run pip)
reqsync run --no-upgrade --show-diff
# CI gate: fail if changes would be made
reqsync run --check --no-upgrade --path requirements.txtreqsync run [OPTIONS]Key options:
--path PATHtarget requirements (defaultrequirements.txt)--follow-includes / --no-follow-includesfollow-rrecursively (default true)--update-constraintsallow modifying constraint files (default false)--policy [lower-bound|floor-only|floor-and-cap]defaultlower-bound--allow-prereleaseadopt pre/dev;--keep-localkeep+local--no-upgradeskippip install -U -r--pip-args "..."allowlisted pip flags (indexes, proxies,-r,-c)--only PATTERNSand--exclude PATTERNSscope by canonical names--checkno writes; exit 11 if changes would be made--dry-runno writes; pair with--show-difffor unified diff--json-report FILEmachine-readable changes--backup-suffix S.bakby default;--timestamped-backups/--no-timestamped-backups--system-okallow outside a venv (not recommended)--allow-hashesskip hashed stanzas instead of refusing
Full option details: see docs/USAGE.md.
Config is optional. CLI flags always win.
Resolution order:
- CLI flags
reqsync.tomlat project root[tool.reqsync]inpyproject.tomlreqsync.json- Built-in defaults
Examples: see docs/CONFIG.md and examples/.
-
lower-bound Always set
>= installed_version. -
floor-only Only raise existing lower bounds. If none exists, leave it unchanged.
-
floor-and-cap Set
>= installed_version,<next_major. Useful guardrail in larger teams. -
update-in-place Preserve the original operator (
==,~=,>=) and only update the version. Adds>=if no operator is present.
-rincludes are followed (on by default) and processed with the same rules.-cconstraints are detected but not modified unless you pass--update-constraints.
- Lines that are comments, directives (
-r,-c,--index-url, etc.),-e/--editable, VCS/URL/local paths are preserved and not edited. - Inline comments are preserved. URLs containing
#are handled safely. - Encoding and newline style are preserved exactly.
- Refuses to run outside a venv unless
--system-ok. - Refuses to edit hashed files (
--hash=) by default. Use--allow-hashesto skip those stanzas instead. - File lock prevents concurrent edits. Atomic replace with backup and rollback.
from pathlib import Path
from reqsync._types import Options
from reqsync.core import sync
opts = Options(
path=Path("requirements.txt"),
follow_includes=True,
policy="lower-bound",
dry_run=True,
show_diff=True,
no_upgrade=True,
)
result = sync(opts)
print("Changed:", result.changed)
print(result.diff or "")More integration patterns: docs/INTEGRATION.md.
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Generic error |
| 2 | Requirements file not found |
| 3 | Hashes present without --allow-hashes |
| 4 | Pip upgrade failed |
| 5 | Parse error |
| 6 | Constraint conflict detected |
| 7 | Refused to run outside venv without --system-ok |
| 8 | Repo dirty and guard blocked (if enabled) |
| 9 | Lock acquisition timeout |
| 10 | Write failed; backup restored |
| 11 | --check and changes would be made |
| Tool | Primary goal | Edits your file | Hash-aware | Lockfile |
|---|---|---|---|---|
| reqsync | Sync to installed versions | Yes, preserves formatting | Refuses or skips | No |
| pip-tools | Deterministic pins | Generates pinned file | Yes | Yes |
| pip-upgrader/pur | Bump versions | Rewrites pins (==) | No | No |
| uv/poetry/pdm | Env + lock mgmt | Manage lockfiles | Yes | Yes |
Use reqsync when you want your requirements.txt to reflect the versions youβre actually running, while keeping future installs flexible.
- Version is derived from Git tags via
hatch-vcs. Tag format:vX.Y.Z. - Repo: https://github.com/ImYourBoyRoy/reqsync
- CI: tests, lint, type-check, build, twine metadata check, wheel smoke test.
- Publishing: PyPI Trusted Publishing via OIDC on tags.
python -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -e .[dev]
pre-commit install
pre-commit run -a
pytest -q
ruff check .
ruff format --check .
mypy src/reqsyncContributing guidelines: CONTRIBUTING.md Changelog: CHANGELOG.md
- Usage: docs/USAGE.md
- Config: docs/CONFIG.md
- Integration: docs/INTEGRATION.md
MIT. See LICENSE.
If this tool helps you, star the repo and share it. Issues and PRs welcome.