Synchronize requirements.txt to >= installed versions, safely and atomically.
Project description
🧩 reqsync
Upgrade a venv and rewrite requirements to
>= installed_version, safely and atomically. Preserves extras, markers, comments, encoding, and line endings.
✨ Design goals
-
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.
⭐ Features
- 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.
🚀 Installation
pip install reqsync
Requires Python 3.8+.
🧪 Quick start
# 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-diff
Common 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.txt
🛠️ CLI
reqsync 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.
⚙️ Configuration
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/.
🧬 Policy modes
-
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.
📁 Includes and constraints
-rincludes are followed (on by default) and processed with the same rules.-cconstraints are detected but not modified unless you pass--update-constraints.
🧵 Preservation rules
- 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.
🔒 Safety gates
- 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.
🤖 Python API (for agents/tools)
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.
📊 Exit codes
| 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 |
🔄 How this compares
| Tool | Primary goal | Edits your file | Hash-aware | Lockfile |
|---|---|---|---|---|
| reqsync | Floor to installed (>=) | 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.
📦 Versioning & release
- 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.
🧰 Development
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/reqsync
Contributing guidelines: CONTRIBUTING.md Changelog: CHANGELOG.md
📚 Docs
- Usage: docs/USAGE.md
- Config: docs/CONFIG.md
- Integration: docs/INTEGRATION.md
📜 License
MIT. See LICENSE.
⭐ Support
If this tool helps you, star the repo and share it. Issues and PRs welcome.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file reqsync-0.1.0.tar.gz.
File metadata
- Download URL: reqsync-0.1.0.tar.gz
- Upload date:
- Size: 22.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d0fb2343232f93e99260b00e705776091edf193d490e92c802ec46714fa7e8ba
|
|
| MD5 |
4d912b47f2a9be150bca994b152dac61
|
|
| BLAKE2b-256 |
d2ba3d546a98d12c72d0d30dc277fe9e03390b45aee9404915060c240178bf63
|
Provenance
The following attestation bundles were made for reqsync-0.1.0.tar.gz:
Publisher:
publish.yml on ImYourBoyRoy/reqsync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
reqsync-0.1.0.tar.gz -
Subject digest:
d0fb2343232f93e99260b00e705776091edf193d490e92c802ec46714fa7e8ba - Sigstore transparency entry: 412050169
- Sigstore integration time:
-
Permalink:
ImYourBoyRoy/reqsync@f32bedba35c55e4395b1639b19e4a5e8963487bb -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/ImYourBoyRoy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f32bedba35c55e4395b1639b19e4a5e8963487bb -
Trigger Event:
push
-
Statement type:
File details
Details for the file reqsync-0.1.0-py3-none-any.whl.
File metadata
- Download URL: reqsync-0.1.0-py3-none-any.whl
- Upload date:
- Size: 19.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8896309bee09a4fe1c57a1063205b17f87b49ad354574d7332a29e34beafaa7a
|
|
| MD5 |
7bc9b7bd1a93571415c240e92e0470d9
|
|
| BLAKE2b-256 |
2d97582e7e86c2b3bb21d46cfa55ff6f28fb208e75186975993d268fd7922a88
|
Provenance
The following attestation bundles were made for reqsync-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on ImYourBoyRoy/reqsync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
reqsync-0.1.0-py3-none-any.whl -
Subject digest:
8896309bee09a4fe1c57a1063205b17f87b49ad354574d7332a29e34beafaa7a - Sigstore transparency entry: 412050174
- Sigstore integration time:
-
Permalink:
ImYourBoyRoy/reqsync@f32bedba35c55e4395b1639b19e4a5e8963487bb -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/ImYourBoyRoy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f32bedba35c55e4395b1639b19e4a5e8963487bb -
Trigger Event:
push
-
Statement type: