pip-upgrader rewrites the pins in your requirements files
An interactive pip requirements upgrader. Supports requirements.txt, pyproject.toml, and Pipfile
At a glance
- What is it?
- pip-upgrader is an Apache-2.0 command line tool that finds the newest allowed version of each dependency and edits the pin in place across requirements.txt, pyproject.toml and Pipfile. It is a pin editor rather than an installer, which is exactly why it belongs in a controlled upgrade process.
- Who is it for?
- pip-upgrader is a good fit for a team that keeps dependencies pinned in text files and wants a repeatable, reviewable way to move those pins forward, especially in CI where a non-interactive run and a diff are the whole workflow. It is the wrong tool for a lockfile-first project, for anyone who wants the upgrade and the install in one step, and for a codebase where dependencies are deliberately left unpinned.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 74 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A pin editor, not an installer
The distinction that matters most about pip-upgrader is what it does not do. It does not install anything. The README is explicit about the sequence: you run the tool, it updates the pinned versions in your requirements files, and then you install yourself with uv sync, pip install -r requirements.txt, or whatever your project uses. The tool is therefore the editing step in an upgrade, separated from the resolution and installation step, and that separation is a design choice rather than a limitation of the implementation. It means the diff lands in version control, can be reviewed on its own, and can be tested by your own suite before anything is installed. It also means a CI pipeline that runs it produces a pull request rather than a deployed environment.
Four file formats and the pins it will actually touch
The tool reads requirements.txt and its .pip and .in variants, pyproject.toml in both PEP 621 and Poetry layouts, and Pipfile for Pipenv projects. If you pass no file, it attempts to detect requirements files, pyproject.toml and Pipfile in the current directory. Within those files it works on a defined set of pins: ==, >= and ~= . Anything else is left alone. In Poetry projects that means caret, tilde and wildcard constraints are skipped, and in Pipfiles the same string and dict formats as Poetry are handled across the [packages] and [dev-packages] sections. The compatible release operator gets specific treatment, with ~=1.2.3 treated as >=1.2.3, <1.3 per PEP 440, so an upgrade inside that range is a change and a jump past it is not offered.
The Python version check that quietly filters results
One behaviour is worth understanding before you compare outputs. The README lists the tool as Python version aware, meaning versions whose requires_python is incompatible with your current Python are automatically skipped. In practice that means running the tool inside a container pinned to an older interpreter can hide newer releases that would install cleanly on the interpreter your production image actually uses. If you are comparing two runs and they disagree, the interpreter is the first thing to check. This is a sensible default, since offering a version that cannot install would be worse, but it is a default that depends on where you run the command rather than on where you will use the result.
The flag set for a controlled upgrade
Most of the interface exists so you can narrow what moves. --prerelease includes prerelease versions when querying PyPI, which you rarely want in a normal upgrade. --minor keeps the upgrade inside the same major version, so 1.2.3 moves to 1.x.y, and --patch keeps it inside the same major and minor, which the README calls the safest option. --skip-greater-equal leaves packages pinned with >= or ~= untouched, so only == pins are considered. --skip takes a package name or a regular expression and can be combined with --non-interactive, so a regex can exclude a whole family such as django.* . --dry-run simulates the upgrade without executing it, and it is the flag to reach for first. --use-default-index skips searching for a custom index-url in pip configuration files, and --timeout sets a custom timeout for PyPI requests against a default of 15 seconds.
Running it without a human at the keyboard
The interactive mode is the default, and the CI mode is one flag away. The README describes --non-interactive as upgrading all packages without prompting, and notes that it is equivalent to -p all, that -p is overridden if both are given, and that a warning is printed when that happens. The -p option pre-chooses which packages to upgrade and skips the prompt for them, so you can name specific packages such as django and celery, or use all. The examples section shows the shape of a non-interactive run:
pip-upgrade requirements.txt --non-interactive
pip-upgrade --non-interactive # auto-discovers requirements filesand the shape of a selective one, with a regex skip alongside:
pip-upgrade --non-interactive --skip django --skip celery
pip-upgrade --non-interactive --skip "django.*" # regex: skip all django-* packagesPositional arguments accept a file, a wildcard path to multiple files such as requirements/dev.txt and requirements/production.txt, pyproject.toml or Pipfile, so a single invocation can cover a dev and a production requirement set.
How the package itself is built
The distribution is a hatchling project with hatch-vcs supplying the version from version control, so the published version comes from the tag rather than a hand-edited field. The console script is pip-upgrade, mapped to pip_upgrader.cli:main, and the project metadata sets requires-python to >=3.10, matching the README's Python 3.10+ note. The runtime dependencies are small and each has a visible job: docopt-ng for the argument grammar, packaging for version comparison, questionary for the interactive prompt, requests for the PyPI queries, and tomli as a conditional dependency for Python versions below 3.11, where the standard library has no TOML reader. The optional test extra adds coverage, pytest, pytest-cov and responses, the last of which is the library for mocking HTTP responses in tests, which tells you the network layer is exercised in tests rather than left to the network.
The development loop and the release path
Development uses uv, with the dependency set locked in uv.lock at the repository root, and the README gives the full loop:
uv sync --extra test --extra dev # install all dependencies
uv run pytest # run tests
uv run ruff check . # lint
uv run ruff format --check . # check formattingThe pytest configuration in pyproject.toml enables coverage reporting by default with a term-missing report. The ruff configuration targets py310, sets a line length of 120, and selects the E, F, W and I rule sets, with quote style preserved rather than normalised. Releases are published to PyPI automatically by a GitHub Actions workflow when a version tag is pushed, using trusted publishers with OIDC:
git tag v2.4.0
git push origin v2.4.0The last push was on 2026-08-10, the same day as v2.4.11, v2.4.10 and v2.4.9, all three published on 2026-08-10, so the project moves quickly and the changelog in CHANGELOG.rst is the place to read what changed between the versions you would otherwise pin.
Where this tool is the wrong choice
Two clear exclusions. The first is a lockfile-first workflow: if your project resolves dependencies through a lock file that must be regenerated and committed, editing the pin in a requirements file is only half the job, and the tool does not claim to touch the lock. The second is a project that deliberately leaves dependencies unpinned, since the tool operates on pins and a constraint-free entry is not something it can move forward. Compared with the alternative of running pip install --upgrade per package and editing the files by hand, the difference in approach is that pip-upgrader does the whole set in one pass, keeps the same pin syntax it found, and reports what it changed, which is why it fits a review-and-merge process. What it does not do is decide whether the upgrade is safe: that is still your test suite's job, and the tool has no opinion about it.
Editorial conclusion
pip-upgrader is a good fit for a team that keeps dependencies pinned in text files and wants a repeatable, reviewable way to move those pins forward, especially in CI where a non-interactive run and a diff are the whole workflow. It is the wrong tool for a lockfile-first project, for anyone who wants the upgrade and the install in one step, and for a codebase where dependencies are deliberately left unpinned. Check three things before you wire it into a pipeline: that the version of the tool you pin matches the format of the file you point it at, that you pass --dry-run first to see exactly which pins it intends to move, and that the ranges you keep are narrow enough for the tool to have anything to do, since caret, tilde and wildcard pins are skipped and only ==, >= and ~= entries are considered.
Frequently asked questions
Which requirement files does pip-upgrader support?
It supports requirements.txt and its .pip and .in variants, pyproject.toml in both PEP 621 and Poetry layouts, and Pipfile for Pipenv projects. With no arguments it attempts to detect requirements files, pyproject.toml and Pipfile in the current directory.
Does pip-upgrader install the upgraded packages?
No. It updates the pinned versions in your requirements files, and the README says you then install yourself with uv sync, pip install -r requirements.txt, or whatever you use. You can also use --dry-run to simulate the upgrade without executing it.
How do I run pip-upgrader in CI without prompts?
Use --non-interactive, which upgrades all packages without prompting and is equivalent to -p all. Individual packages can be excluded with --skip by name or regular expression, and the README notes that -p is overridden if it is given together with --non-interactive.
Can pip-upgrader restrict upgrades to patch or minor versions?
Yes. The --minor flag upgrades only within the same major version, for example 1.2.3 to 1.x.y, and --patch upgrades only within the same major.minor version, which the README describes as the safest option. The --skip-greater-equal flag leaves packages pinned with >= or ~= untouched.
What Python version does pip-upgrader need?
The README states Python 3.10+ is required and the packaging metadata sets requires-python to >=3.10. The README also notes that versions whose requires_python is incompatible with your current Python are automatically skipped.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/simion-pip-upgrader)Was this useful?