depkeeper
Safe, conflict-aware dependency updates for pip requirements files.
depkeeper analyses requirements.txt-style files, computes a safe upgrade target for every
requirement, cross-validates those targets against each other, and rewrites the file in place —
without discarding the constraints you authored.
It is a single-purpose CLI. It does not manage virtual environments, install packages, replace
pip, or introduce a lock file format.
Contents
- Why depkeeper · What it guarantees
- Install · Quick start · Commands
- Configuration · Common workflows
- Architecture · Limitations
- Troubleshooting · Documentation · Contributing
Why depkeeper
pip list --outdated reports the latest version, which is frequently a major release that
will break your build. Lock-file tools solve this properly but require adopting a new workflow and
file format.
depkeeper fills the gap. It answers one question, precisely:
What is the largest upgrade I can take for each requirement without crossing a major version, without breaking Python compatibility, without violating the constraints I declared, and without conflicting with the other packages in my file?
Then it applies the answer to the file you already have.
| pip | pip-tools | Poetry | depkeeper | |
|---|---|---|---|---|
| Reports available updates | latest only | no | yes | yes, with a safe target |
| Enforces major-version boundaries | no | no | no | yes |
| Preserves your declared ranges on write | n/a | regenerates | rewrites | yes |
| Full transitive resolution | at install time | yes | yes | no — see Limitations |
| Requires a new file format | no | .in files |
pyproject.toml + lock |
no |
What it guarantees
These are enforced in code, not advisory:
| Guarantee | Meaning |
|---|---|
| Major-version boundary | A package on 1.x is never moved to 2.x. |
| Python compatibility | Releases whose requires_python excludes your interpreter are never proposed. |
| Constraint preservation | Upper bounds, exclusions and ~= bands are preserved verbatim and never violated. |
| No pre-releases | Alpha/beta/rc versions are excluded from every candidate list. |
| Atomic, reversible writes | Multi-file updates are all-or-nothing, with rollback and optional backups. |
| Hash integrity | Updates that would strip --hash entries are refused unless explicitly allowed. |
| Clean stdout | --format json puts the payload on stdout and every diagnostic on stderr. |
| Idempotency | Re-running produces no change. Safe to run on a schedule. |
The trade-off is explicit: depkeeper is deliberately conservative. It will never propose the major upgrade you eventually need.
Install
python -m pip install depkeeper # into the project's environment (recommended)
pipx install depkeeper # isolated global tool
Requires Python 3.8+.
[!IMPORTANT] depkeeper filters candidate versions against the interpreter it runs on, not your project's. Installing it on 3.8 while your service targets 3.12 makes recommendations silently conservative. Install it into the project environment, or use
pipx install --python python3.12 depkeeper.
Verify:
depkeeper --version # depkeeper 0.1.1
python -m depkeeper --help # equivalent module entry point
Quick start
Given this file:
requests==2.28.0
flask>=2.0,<2.3
celery[redis]>=5.0,<6.0
click~=8.0
certifi
urllib3==1.26.0
1. Inspect (read-only, never writes):
$ depkeeper check
┏━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Status ┃ Package ┃ Current ┃ Latest ┃ Recommended ┃ Update Type ┃ Conflicts ┃
┡━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ [OUTDATED] │ requests │ 2.28.0 │ 2.34.2 │ 2.34.2 │ minor │ - │
│ [OUTDATED] │ flask │ 2.0 │ 3.1.3 │ 2.2.5 │ minor │ - │
│ [OUTDATED] │ celery │ 5.0 │ 5.6.3 │ 5.6.3 │ minor │ - │
│ [OUTDATED] │ click │ 8.0 │ 8.4.2 │ 8.4.2 │ minor │ - │
│ [OK] │ certifi │ - │ 2026.7.22 │ - │ - │ - │
│ [OUTDATED] │ urllib3 │ 1.26.0 │ 2.7.0 │ 1.26.20 │ patch │ - │
└────────────┴──────────┴─────────┴───────────┴─────────────┴─────────────┴───────────┘
[WARNING] 5 package(s) have updates available
2. Preview the exact write:
depkeeper update --dry-run
3. Apply:
$ depkeeper update --backup
Update 6 packages? (y, n) [y]: y
[OK] ✓ Successfully updated 6 package(s)
requests==2.34.2
flask>=2.2.5,<2.3 # your <2.3 cap held — latest is 3.1.3
celery[redis]>=5.6.3,<6.0 # extra and upper bound preserved
click~=8.4 # compatible-release form preserved
certifi==2026.7.22 # unversioned requirement gained a pin
urllib3==1.26.20 # major boundary held — latest is 2.7.0
4. Verify — this step is not optional:
python -m pip install -r requirements.txt && pytest
depkeeper validates the packages you declared against each other. It does not expand the
transitive graph, so pip remains the authority on whether the set installs.
Commands
depkeeper check [FILE]
Read-only analysis. Never modifies the filesystem. Exits 0 on success regardless of findings.
| Option | Description |
|---|---|
--outdated-only |
Show only packages with an update or a conflict |
-f, --format table|simple|json |
Output format (default table) |
--strict-version-matching |
Only a sole == pin counts as a current version |
--check-conflicts / --no-check-conflicts |
Cross-package conflict resolution (default on) |
depkeeper update [FILE]
Applies recommended versions in place.
| Option | Description |
|---|---|
--dry-run |
Run the full pipeline, print the plan, write nothing |
-y, --yes |
Skip the confirmation prompt |
--backup |
Timestamped backup of every affected file before writing |
--pin |
Replace every specifier with an exact == pin |
--allow-hash-removal |
Permit updating --hash-pinned requirements (removes the digests) |
-p, --packages NAME |
Restrict to specific packages (repeatable) |
--strict-version-matching |
As above |
--check-conflicts / --no-check-conflicts |
As above |
Global options
-c/--config PATH · -v / -vv (INFO / DEBUG, always on stderr) · --color/--no-color ·
--version · -h/--help
Global options must precede the subcommand: depkeeper -v check, not depkeeper check -v.
Full specification: CLI reference.
Exit codes
| Code | Meaning |
|---|---|
0 |
Success — including "updates are available" |
1 |
Application error (parse, config, write, refused hash removal) |
2 |
Usage error (unknown option, missing file) |
130 |
Interrupted (Ctrl+C) |
Gate automation on the JSON payload, never on check's exit code.
Configuration
Two settings are persistable, in depkeeper.toml or pyproject.toml. Everything else is a
per-invocation flag, deliberately — options that change what gets written stay visible in the
command line.
# depkeeper.toml
[depkeeper]
check_conflicts = true # cross-package conflict resolution
strict_version_matching = false # treat only `==` as a current version
# pyproject.toml
[tool.depkeeper]
check_conflicts = true
strict_version_matching = false
Precedence: defaults < config file < CLI flag. Unknown keys and wrong types are hard errors. Discovery is limited to the current working directory (not recursive).
Environment: DEPKEEPER_CONFIG, DEPKEEPER_COLOR, NO_COLOR, CI, plus httpx's
HTTPS_PROXY / SSL_CERT_FILE.
Common workflows
Daily
depkeeper check --outdated-only
depkeeper update --dry-run
depkeeper update --backup && pip install -r requirements.txt && pytest
One package at a time (keeps regressions bisectable)
depkeeper update -p django -y && pytest
Application release — freeze to exact versions
depkeeper update --pin --backup
CI drift report — stdout stays parseable even with -v
depkeeper -v check --format json --no-check-conflicts > report.json 2> depkeeper.log
jq '[.[] | select(.status == "outdated")] | length' report.json
Multi-file projects — -r includes are followed and rewritten
depkeeper update requirements/dev.txt --dry-run # also writes base.txt
Hash-pinned files — depkeeper refuses these by default
depkeeper update --allow-hash-removal -y
pip-compile --generate-hashes requirements.in # regenerate; not optional
More: User guide · CI/CD integration
Architecture
Synchronous CLI around an asynchronous core. No daemon, no persistent state, no cache directory.
requirements.txt
│
▼
RequirementsParser ──► PyPIDataStore ──► VersionChecker ──► DependencyAnalyzer ──┐
(text → model) (one HTTP call (per-package (cross-package │
per package) target version) consistency) │
▼
renderer (table/simple/json)
writer (atomic, rollback-safe)
| Component | Responsibility |
|---|---|
core/parser.py |
Requirements text → Requirement objects, -r/-c resolution, provenance |
core/data_store.py |
PyPI metadata cache with per-key request coalescing |
core/checker.py |
Per-package recommendation under boundary, Python and constraint filters |
core/dependency_analyzer.py |
Iterative conflict resolution within major boundaries |
commands/ |
User interaction, rendering, the write pipeline, exit codes |
utils/ |
HTTP, console, logging, atomic filesystem, naming, version algebra |
See ARCHITECTURE.md for the contributor-facing map, or the architecture deep dive for design decisions, concurrency model and error propagation.
Limitations
Honest, verified, and documented in full at Known limitations. The ones most likely to affect you:
- Not a resolver. The transitive graph is not expanded. Always
pip installand test. - Only
pypi.orgis queried.--index-urllines are parsed and ignored; private packages report as errors. - Calendar versions hit the major boundary.
certifi 2023.xnever auto-bumps to2024.x. - Major upgrades are yours to make. Raise the floor by hand, then let depkeeper continue.
- Backslash line continuations are not parsed, so default
pip-compile --generate-hashesoutput is unreadable to depkeeper. - No runtime tuning. Timeouts, retries and concurrency are constants; override them via the Python API if you must.
Troubleshooting
Start here:
depkeeper -vv check --format json > report.json 2> debug.log
debug.log names the file parsed, the effective configuration, every HTTP retry and every
resolution decision.
| Symptom | Cause |
|---|---|
Recommendation far below Latest |
Your declared cap, the major boundary, a conflict, or requires_python |
| Recommendations differ between laptop and CI | Different Python versions running depkeeper |
✗ ERROR rows |
Package not on public PyPI, network/proxy failure, or TLS interception |
update changed a package check showed as ✓ OK |
Unversioned requirement — it received a pin |
| A proposed downgrade | Declared version is unusable; review before accepting |
Full guide: Troubleshooting
Documentation
Published at rahulkaushal04.github.io/depkeeper.
| Section | Contents |
|---|---|
| Getting started | Install, quick start, reading the output |
| Concepts | Architecture, version recommendation, conflict resolution, parsing, write safety |
| User guide | Checking, updating, configuration, CI/CD, operations, best practices, troubleshooting |
| Reference | CLI, config, exit codes, file formats, JSON schema, errors, limitations, Python API |
| Contributing | Setup, code style, testing, extending, releases |
Repository documents: ARCHITECTURE.md · CONTRIBUTING.md · CHANGELOG.md · SECURITY.md · SUPPORT.md · CODE_OF_CONDUCT.md
Project status
Version 0.1.1, classified Development Status :: 4 - Beta.
The write path is defensive — atomic replacement, two-phase commit, rollback, optional backups — and behaviour is documented and covered by tests across Linux, macOS and Windows on Python 3.8–3.12. Pin the version in automation: recommendation logic is a behavioural contract, and a version bump can legitimately change an update plan.
Contributing
Contributions are welcome. Start with CONTRIBUTING.md, and read the
system invariants
before changing anything in core/.
git clone https://github.com/rahulkaushal04/depkeeper.git
cd depkeeper
pip install -e ".[dev,docs]"
pre-commit install
python -m pytest tests -q --no-cov
python -m mypy depkeeper --python-version 3.13
Report vulnerabilities privately — see SECURITY.md, never a public issue.
License
Apache License 2.0. Copyright 2025-2026 Rahul Kaushal.
Built with Click,
Rich, httpx and
packaging — the last of which is why depkeeper agrees with pip
about what a version means.
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 depkeeper-0.1.1.tar.gz.
File metadata
- Download URL: depkeeper-0.1.1.tar.gz
- Upload date:
- Size: 104.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bec25e06f363a0c2123a56a52e6f4d32ffee89210868b7f3ba62360ffc106847
|
|
| MD5 |
a43ad8fb04c4cf97d012cdf59f0134f0
|
|
| BLAKE2b-256 |
143f7cd0633963bde6aba45ebc0faa566b6e223f948e17ed69acf60b78c1e9b6
|
Provenance
The following attestation bundles were made for depkeeper-0.1.1.tar.gz:
Publisher:
publish.yml on rahulkaushal04/depkeeper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
depkeeper-0.1.1.tar.gz -
Subject digest:
bec25e06f363a0c2123a56a52e6f4d32ffee89210868b7f3ba62360ffc106847 - Sigstore transparency entry: 2477280951
- Sigstore integration time:
-
Permalink:
rahulkaushal04/depkeeper@9e39b8b40fdb787dc4a6a1635c8a7591dfbf9b35 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/rahulkaushal04
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9e39b8b40fdb787dc4a6a1635c8a7591dfbf9b35 -
Trigger Event:
push
-
Statement type:
File details
Details for the file depkeeper-0.1.1-py3-none-any.whl.
File metadata
- Download URL: depkeeper-0.1.1-py3-none-any.whl
- Upload date:
- Size: 107.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f28842cbb2e80b6d908d2e2ef9d5147a366c184af94dd2b8541f03827be963e
|
|
| MD5 |
bf90ac0e0c4f8e02d2692426787af349
|
|
| BLAKE2b-256 |
9c042b24c783b78b7876be669b08e97c91f02f5af1f1e56f5ac2cc2c61229bb2
|
Provenance
The following attestation bundles were made for depkeeper-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on rahulkaushal04/depkeeper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
depkeeper-0.1.1-py3-none-any.whl -
Subject digest:
7f28842cbb2e80b6d908d2e2ef9d5147a366c184af94dd2b8541f03827be963e - Sigstore transparency entry: 2477281469
- Sigstore integration time:
-
Permalink:
rahulkaushal04/depkeeper@9e39b8b40fdb787dc4a6a1635c8a7591dfbf9b35 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/rahulkaushal04
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9e39b8b40fdb787dc4a6a1635c8a7591dfbf9b35 -
Trigger Event:
push
-
Statement type: