# π§© reqsync
[](https://pypi.org/project/reqsync/)
[](https://pypi.org/project/reqsync/)
[](https://github.com/ImYourBoyRoy/reqsync/actions/workflows/ci.yml)
[](https://opensource.org/licenses/MIT)
[](https://peps.python.org/pep-0561/)
> Upgrade a venv and rewrite requirements to match installed versions, safely and atomically. Preserves operators (`==`, `>=`, `~=`), extras, markers, comments, encoding, and line endings.
---
## β¨ Design goals
* **Safety first**
Refuse outside a venv by default. Abort on `--hash` unless 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 `Result` for tool-calling, MCP, CrewAI, AG2, etc.
---
## β Features
* **Agent & CI Friendly**: Structured Python API and `--json-report` flag for easy integration into automated workflows.
* Venv guard on by default; `--system-ok` to override.
* Preserves extras `[a,b]`, markers `; sys_platform != "win32"`, inline comments, encoding (BOM), and newline style (LF/CRLF).
* Includes recursion: follows `-r other.txt` by default.
* Constraints awareness: detects `-c constraints.txt` and skips modifying unless `--update-constraints`.
* Policy modes: `lower-bound` (default), `floor-only`, `floor-and-cap` (`< next major`).
* Pre/dev handling: `--allow-prerelease` to adopt, `--keep-local` to keep `+local` suffixes.
* Hash-aware: refuses by default if `--hash` is present; `--allow-hashes` skips 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
```bash
pip install reqsync
```
> Requires Python 3.8+.
---
## π§ͺ Quick start
```bash
# 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:
```bash
# 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
```bash
reqsync run [OPTIONS]
```
Key options:
* `--path PATH` target requirements (default `requirements.txt`)
* `--follow-includes / --no-follow-includes` follow `-r` recursively (default true)
* `--update-constraints` allow modifying constraint files (default false)
* `--policy [lower-bound|floor-only|floor-and-cap]` default `lower-bound`
* `--allow-prerelease` adopt pre/dev; `--keep-local` keep `+local`
* `--no-upgrade` skip `pip install -U -r`
* `--pip-args "..."` allowlisted pip flags (indexes, proxies, `-r`, `-c`)
* `--only PATTERNS` and `--exclude PATTERNS` scope by canonical names
* `--check` no writes; exit 11 if changes would be made
* `--dry-run` no writes; pair with `--show-diff` for unified diff
* `--json-report FILE` machine-readable changes
* `--backup-suffix S` `.bak` by default; `--timestamped-backups/--no-timestamped-backups`
* `--system-ok` allow outside a venv (not recommended)
* `--allow-hashes` skip hashed stanzas instead of refusing
Full option details: see [docs/USAGE.md](docs/USAGE.md).
---
## βοΈ Configuration
Config is optional. CLI flags always win.
Resolution order:
1. CLI flags
2. `reqsync.toml` at project root
3. `[tool.reqsync]` in `pyproject.toml`
4. `reqsync.json`
5. Built-in defaults
Examples: see [docs/CONFIG.md](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.
* **update-in-place**
Preserve the original operator (`==`, `~=`, `>=`) and only update the version. Adds `>=` if no operator is present.
---
## π Includes and constraints
* `-r` includes are followed (on by default) and processed with the same rules.
* `-c` constraints 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-hashes` to skip those stanzas instead.
* File lock prevents concurrent edits. Atomic replace with backup and rollback.
---
## π€ Python API (for agents/tools)
```python
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](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** | 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.
---
## π¦ Versioning & release
* Version is derived from Git tags via `hatch-vcs`. Tag format: `vX.Y.Z`.
* Repo: [https://github.com/ImYourBoyRoy/reqsync](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
```bash
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](CONTRIBUTING.md)
Changelog: [CHANGELOG.md](CHANGELOG.md)
---
## π Docs
* Usage: [docs/USAGE.md](docs/USAGE.md)
* Config: [docs/CONFIG.md](docs/CONFIG.md)
* Integration: [docs/INTEGRATION.md](docs/INTEGRATION.md)
---
## π License
MIT. See [LICENSE](LICENSE).
---
## β Support
If this tool helps you, star the repo and share it. Issues and PRs welcome.
Raw data
{
"_id": null,
"home_page": null,
"name": "reqsync",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "agent, ai-tool, automation, ci-cd, cli-tool, dependencies, dependency-management, devops, packaging, pip, requirements, venv",
"author": null,
"author_email": "ImYourBoyRoy <roy.dawson.iv@gmail.com.com>",
"download_url": "https://files.pythonhosted.org/packages/0e/ff/dcd4c8f0d4d8c2463b7b4ef357cadc854d9da1e95422c2a9e383c2bc998e/reqsync-0.1.2.tar.gz",
"platform": null,
"description": "# \ud83e\udde9 reqsync\n\n[](https://pypi.org/project/reqsync/)\n[](https://pypi.org/project/reqsync/)\n[](https://github.com/ImYourBoyRoy/reqsync/actions/workflows/ci.yml)\n[](https://opensource.org/licenses/MIT)\n[](https://peps.python.org/pep-0561/)\n\n> Upgrade a venv and rewrite requirements to match installed versions, safely and atomically. Preserves operators (`==`, `>=`, `~=`), extras, markers, comments, encoding, and line endings.\n\n---\n\n## \u2728 Design goals\n\n* **Safety first**\n Refuse outside a venv by default. Abort on `--hash` unless you opt in to skipping those stanzas. Atomic writes with rollback.\n\n* **Do one job well**\n Upgrade env, then floor top-level requirements to what\u2019s actually installed. No lockfile management.\n\n* **Zero ceremony**\n Works from the CLI without any config. Optional TOML/pyproject config if you want it.\n\n* **Agent friendly**\n Clean Python API that returns a structured `Result` for tool-calling, MCP, CrewAI, AG2, etc.\n\n---\n\n## \u2b50 Features\n\n* **Agent & CI Friendly**: Structured Python API and `--json-report` flag for easy integration into automated workflows.\n* Venv guard on by default; `--system-ok` to override.\n* Preserves extras `[a,b]`, markers `; sys_platform != \"win32\"`, inline comments, encoding (BOM), and newline style (LF/CRLF).\n* Includes recursion: follows `-r other.txt` by default.\n* Constraints awareness: detects `-c constraints.txt` and skips modifying unless `--update-constraints`.\n* Policy modes: `lower-bound` (default), `floor-only`, `floor-and-cap` (`< next major`).\n* Pre/dev handling: `--allow-prerelease` to adopt, `--keep-local` to keep `+local` suffixes.\n* Hash-aware: refuses by default if `--hash` is present; `--allow-hashes` skips those stanzas without editing.\n* File locking with `portalocker`. Backup + atomic replace. Rollback on failure.\n* UX for humans and CI: `--dry-run`, `--show-diff`, `--check`, `--json-report`, `--only/--exclude`.\n* Minimal runtime deps.\n\n---\n\n## \ud83d\ude80 Installation\n\n```bash\npip install reqsync\n```\n\n> Requires Python 3.8+.\n\n---\n\n## \ud83e\uddea Quick start\n\n```bash\n# Activate your venv (required by default)\nsource .venv/bin/activate\n\n# Preview\nreqsync run --path requirements.txt --dry-run --show-diff\n\n# Apply with backup\nreqsync run --path requirements.txt --show-diff\n```\n\nCommon variations:\n\n```bash\n# Read-only sync to current env (don\u2019t run pip)\nreqsync run --no-upgrade --show-diff\n\n# CI gate: fail if changes would be made\nreqsync run --check --no-upgrade --path requirements.txt\n```\n\n---\n\n## \ud83d\udee0\ufe0f CLI\n\n```bash\nreqsync run [OPTIONS]\n```\n\nKey options:\n\n* `--path PATH` target requirements (default `requirements.txt`)\n* `--follow-includes / --no-follow-includes` follow `-r` recursively (default true)\n* `--update-constraints` allow modifying constraint files (default false)\n* `--policy [lower-bound|floor-only|floor-and-cap]` default `lower-bound`\n* `--allow-prerelease` adopt pre/dev; `--keep-local` keep `+local`\n* `--no-upgrade` skip `pip install -U -r`\n* `--pip-args \"...\"` allowlisted pip flags (indexes, proxies, `-r`, `-c`)\n* `--only PATTERNS` and `--exclude PATTERNS` scope by canonical names\n* `--check` no writes; exit 11 if changes would be made\n* `--dry-run` no writes; pair with `--show-diff` for unified diff\n* `--json-report FILE` machine-readable changes\n* `--backup-suffix S` `.bak` by default; `--timestamped-backups/--no-timestamped-backups`\n* `--system-ok` allow outside a venv (not recommended)\n* `--allow-hashes` skip hashed stanzas instead of refusing\n\nFull option details: see [docs/USAGE.md](docs/USAGE.md).\n\n---\n\n## \u2699\ufe0f Configuration\n\nConfig is optional. CLI flags always win.\n\nResolution order:\n\n1. CLI flags\n2. `reqsync.toml` at project root\n3. `[tool.reqsync]` in `pyproject.toml`\n4. `reqsync.json`\n5. Built-in defaults\n\nExamples: see [docs/CONFIG.md](docs/CONFIG.md) and `examples/`.\n\n---\n\n## \ud83e\uddec Policy modes\n\n* **lower-bound**\n Always set `>= installed_version`.\n\n* **floor-only**\n Only raise existing lower bounds. If none exists, leave it unchanged.\n\n* **floor-and-cap**\n Set `>= installed_version,<next_major`. Useful guardrail in larger teams.\n\n* **update-in-place**\n Preserve the original operator (`==`, `~=`, `>=`) and only update the version. Adds `>=` if no operator is present.\n\n---\n\n## \ud83d\udcc1 Includes and constraints\n\n* `-r` includes are followed (on by default) and processed with the same rules.\n* `-c` constraints are detected but not modified unless you pass `--update-constraints`.\n\n---\n\n## \ud83e\uddf5 Preservation rules\n\n* Lines that are comments, directives (`-r`, `-c`, `--index-url`, etc.), `-e/--editable`, VCS/URL/local paths are preserved and not edited.\n* Inline comments are preserved. URLs containing `#` are handled safely.\n* Encoding and newline style are preserved exactly.\n\n---\n\n## \ud83d\udd12 Safety gates\n\n* Refuses to run outside a venv unless `--system-ok`.\n* Refuses to edit hashed files (`--hash=`) by default. Use `--allow-hashes` to skip those stanzas instead.\n* File lock prevents concurrent edits. Atomic replace with backup and rollback.\n\n---\n\n## \ud83e\udd16 Python API (for agents/tools)\n\n```python\nfrom pathlib import Path\nfrom reqsync._types import Options\nfrom reqsync.core import sync\n\nopts = Options(\n path=Path(\"requirements.txt\"),\n follow_includes=True,\n policy=\"lower-bound\",\n dry_run=True,\n show_diff=True,\n no_upgrade=True,\n)\nresult = sync(opts)\nprint(\"Changed:\", result.changed)\nprint(result.diff or \"\")\n```\n\nMore integration patterns: [docs/INTEGRATION.md](docs/INTEGRATION.md).\n\n---\n\n## \ud83d\udcca Exit codes\n\n| Code | Meaning |\n| ---: | ------------------------------------------------- |\n| 0 | Success |\n| 1 | Generic error |\n| 2 | Requirements file not found |\n| 3 | Hashes present without `--allow-hashes` |\n| 4 | Pip upgrade failed |\n| 5 | Parse error |\n| 6 | Constraint conflict detected |\n| 7 | Refused to run outside venv without `--system-ok` |\n| 8 | Repo dirty and guard blocked (if enabled) |\n| 9 | Lock acquisition timeout |\n| 10 | Write failed; backup restored |\n| 11 | `--check` and changes would be made |\n\n---\n\n## \ud83d\udd04 How this compares\n\n| Tool | Primary goal | Edits your file | Hash-aware | Lockfile |\n| ---------------- | -------------------------- | ------------------------- | ---------------- | -------- |\n| **reqsync** | Sync to installed versions | Yes, preserves formatting | Refuses or skips | No |\n| pip-tools | Deterministic pins | Generates pinned file | Yes | Yes |\n| pip-upgrader/pur | Bump versions | Rewrites pins (==) | No | No |\n| uv/poetry/pdm | Env + lock mgmt | Manage lockfiles | Yes | Yes |\n\nUse reqsync when you want your `requirements.txt` to reflect the versions you\u2019re actually running, while keeping future installs flexible.\n\n---\n\n## \ud83d\udce6 Versioning & release\n\n* Version is derived from Git tags via `hatch-vcs`. Tag format: `vX.Y.Z`.\n* Repo: [https://github.com/ImYourBoyRoy/reqsync](https://github.com/ImYourBoyRoy/reqsync)\n* CI: tests, lint, type-check, build, twine metadata check, wheel smoke test.\n* Publishing: PyPI Trusted Publishing via OIDC on tags.\n\n---\n\n## \ud83e\uddf0 Development\n\n```bash\npython -m venv .venv\nsource .venv/bin/activate\npip install -U pip\npip install -e .[dev]\npre-commit install\n\npre-commit run -a\npytest -q\nruff check .\nruff format --check .\nmypy src/reqsync\n```\n\nContributing guidelines: [CONTRIBUTING.md](CONTRIBUTING.md)\nChangelog: [CHANGELOG.md](CHANGELOG.md)\n\n---\n\n## \ud83d\udcda Docs\n\n* Usage: [docs/USAGE.md](docs/USAGE.md)\n* Config: [docs/CONFIG.md](docs/CONFIG.md)\n* Integration: [docs/INTEGRATION.md](docs/INTEGRATION.md)\n\n---\n\n## \ud83d\udcdc License\n\nMIT. See [LICENSE](LICENSE).\n\n---\n\n## \u2b50 Support\n\nIf this tool helps you, star the repo and share it. Issues and PRs welcome.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Synchronize requirements.txt to match installed versions, safely and atomically.",
"version": "0.1.2",
"project_urls": {
"Bug Tracker": "https://github.com/ImYourBoyRoy/reqsync/issues",
"Homepage": "https://github.com/ImYourBoyRoy/reqsync",
"Repository": "https://github.com/ImYourBoyRoy/reqsync"
},
"split_keywords": [
"agent",
" ai-tool",
" automation",
" ci-cd",
" cli-tool",
" dependencies",
" dependency-management",
" devops",
" packaging",
" pip",
" requirements",
" venv"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "69736fdc21ad3dabea678d331a7ddc02cc18eab202ffd1166c6b924abb35fabb",
"md5": "3aa031150d90f1302f821a92d03ea612",
"sha256": "fcad7cd7bc1579318b3b5d8d92bb3b86a1dc2f67f932530b419a72a01d383b54"
},
"downloads": -1,
"filename": "reqsync-0.1.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "3aa031150d90f1302f821a92d03ea612",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 19734,
"upload_time": "2025-08-20T06:34:34",
"upload_time_iso_8601": "2025-08-20T06:34:34.587890Z",
"url": "https://files.pythonhosted.org/packages/69/73/6fdc21ad3dabea678d331a7ddc02cc18eab202ffd1166c6b924abb35fabb/reqsync-0.1.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "0effdcd4c8f0d4d8c2463b7b4ef357cadc854d9da1e95422c2a9e383c2bc998e",
"md5": "773e3eacd138464b1f9fb9280cdb3c8b",
"sha256": "0164e55e684c7437647441d1f4d3e32d6cb089e7f1d297e526fd200b9041fbfc"
},
"downloads": -1,
"filename": "reqsync-0.1.2.tar.gz",
"has_sig": false,
"md5_digest": "773e3eacd138464b1f9fb9280cdb3c8b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 23096,
"upload_time": "2025-08-20T06:34:36",
"upload_time_iso_8601": "2025-08-20T06:34:36.489747Z",
"url": "https://files.pythonhosted.org/packages/0e/ff/dcd4c8f0d4d8c2463b7b4ef357cadc854d9da1e95422c2a9e383c2bc998e/reqsync-0.1.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-20 06:34:36",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ImYourBoyRoy",
"github_project": "reqsync",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "typer",
"specs": [
[
">=",
"0.16.1"
]
]
},
{
"name": "packaging",
"specs": [
[
">=",
"25.0"
]
]
},
{
"name": "portalocker",
"specs": [
[
">=",
"3.2.0"
]
]
},
{
"name": "tomli",
"specs": [
[
">=",
"2.2.1"
]
]
},
{
"name": "typing-extensions",
"specs": [
[
">=",
"4.14.1"
]
]
},
{
"name": "rich",
"specs": [
[
">=",
"14.1.0"
]
]
}
],
"lcname": "reqsync"
}