wppm is a small companion to pip, for any Python environment (it was born in
WinPython, the portable Windows distribution, but does
not require it). Keep using pip to install and remove things — use wppm to see
what is actually there.
pip install wppmYou installed flit. Its [doc] and [test] extras promise more. What is missing?
$ wppm -p "flit![.]"
flit[doc]==3.12.0 ,
pygments-github-lexers==? ;extra==doc
sphinx==? ;extra==doc
sphinxcontrib-github-alt==? ;extra==doc
flit[test]==3.12.0 ,
pytest-cov==? ;extra==test
responses==? ;extra==test
testpath==? ;extra==test
tomli==? ;extra==test[.] means every extra, ! means only show what is missing, and ==? marks a
requirement that is not installed. Extras with nothing missing are simply not printed —
so an empty answer means "everything this package offers is ready to use".
Drop the ! to see the whole picture instead, installed versions included:
$ wppm -p "requests[.]" -l1
requests==2.34.2 ,
certifi==2026.6.17 >=2023.5.7
charset-normalizer==3.4.9 <4,>=2
idna==3.18 <4,>=2.5
urllib3==2.7.0 <3,>=1.26
requests[socks]==2.34.2 ,
certifi==2026.6.17 >=2023.5.7
charset-normalizer==3.4.9 <4,>=2
idna==3.18 <4,>=2.5
pysocks==? !=1.5.7,>=1.5.6;extra==socks
urllib3==2.7.0 <3,>=1.26
requests[use-chardet-on-py3]==2.34.2 ,
certifi==2026.6.17 >=2023.5.7
chardet==? <8,>=3.0.2;extra==use-chardet-on-py3
charset-normalizer==3.4.9 <4,>=2
idna==3.18 <4,>=2.5
urllib3==2.7.0 <3,>=1.26The reverse direction, -r, is extras-aware too — it tells you why something is in
your environment, down to the extra that asked for it:
$ wppm -r "pytest[.]"
pytest==9.0.3
pytest[all]==9.0.3 ,
idna[all]==3.18 [requires: pytest>=8.3.2;extra==all]
pandas[all]==3.0.3 [requires: pytest>=8.3.4;extra==all]
pytest[dev]==9.0.3
pytest[test]==9.0.3 ,
flit[test]==3.12.0 [requires: pytest>=2.7.3;extra==test]
pandas[test]==3.0.3 [requires: pytest>=8.3.4;extra==test]
pytest[testing]==9.0.3 ,
pluggy[testing]==1.6.0 [requires: pytest;extra==testing]
pytest[tests]==9.0.3 ,
pillow[tests]==12.3.0 [requires: pytest;extra==tests]With -r, the ! filter keeps only the packages that pin or cap the one you name —
the handful that will actually fight your next upgrade, instead of the long list of
packages that merely depend on it:
$ wppm -r "pluggy!"
pluggy==1.6.0 ,
pytest==9.0.3 [requires: pluggy<2,>=1.5]An empty answer here is good news: nothing constrains it, upgrade away.
And the whole constraint web of an environment — every package, every extra, nine levels deep — is one command:
$ wppm -p ".[.]" -l9Any of -p, -r, -ls, -md accepts -j / --json, so the same answers can gate a
CI job or be diffed between two environments:
$ wppm -p pluggy -j
[
{
"package": "pluggy",
"extra": "",
"version": "1.6.0",
"installed": true,
"constraint": "",
"depends": []
}
]$ wppm -p myapp -j | python -c "import sys,json; s=json.load(sys.stdin); [s.extend(n['depends']) for n in s]; sys.exit(1 if any(not n['installed'] for n in s) else 0)"The tree engine is a plain importable module — no subprocess, no parsing of terminal
output. down() walks dependencies, up() walks them backwards, and both return
indented text by default or a JSON string with format="json":
import json
from wppm import piptree
pip = piptree.PipData() # or PipData(target=r"D:\WPy64\python")
tree = json.loads(pip.down("pandas", "mysql", format="json"))
missing = [d["package"] for d in tree[0]["depends"] if not d["installed"]]
print(f"pandas[mysql] needs: {missing}")pandas[mysql] needs: ['pymysql', 'sqlalchemy']>>> print(pip.up("pluggy!")) # who caps pluggy?
pluggy==1.6.0 ,
pytest==9.0.3 [requires: pluggy<2,>=1.5]
>>> pip.summary("pandas")
'Powerful data structures for data analysis, time series, and statistics'-t points wppm at another Python distribution, and -ws at a plain directory of
wheels — so you can inspect a portable distribution, or an offline bundle, without
installing it first:
$ wppm -ls -ws .\wheelhouse\included.wheels --json
$ wppm -p "pandas[.]" -t D:\WPy64\pythonBeyond inspection, wppm installs from a wheelhouse or a pylock.toml (-i, -ws,
-wd), emits a one-document environment manifest — distribution, tools, packages,
wheelhouse — as Markdown or JSON (-md, a lightweight SBOM), and does portability
housekeeping: on any Windows Python, --movable / --fix rewrite the Scripts\
launchers and shebangs between relative and absolute paths, so a directory can be moved
(or pinned back down) without breaking its entry points.
--register / --unregister associate file extensions, icons, context menu and start
menu entries with the target Python. Each distribution gets its own start menu folder,
so registering one never disturbs another — but note that the target is declared under
the WinPython PEP-514 vendor key.
wppm adds per-[extra] granularity in both directions, the ! filter (missing
dependencies forward, constraining dependencies backward), and the ability to inspect
another environment (-t) or a bare directory of wheels (-ws) without installing
anything into it.
Quoting:
!and[are shell metacharacters in POSIX shells, so quote the argument (wppm -p "flit![.]"). Incmd.exethe quotes are optional.
usage: wppm [-h] [-v] [--register] [--unregister] [--fix] [--movable]
[-ws WHEELSOURCE] [-wd WHEELDRAIN] [-ls] [-lsa] [-md] [-p] [-r]
[-l LEVELS] [-j] [-t TARGET] [-i] [-u]
[package(s) or lockfile ...]
WinPython Package Manager: handle a Python distribution (WinPython or not) and its packages (17.10.20260808)
positional arguments:
package(s) or lockfile
optional package names, wheels, or lockfile
options:
-h, --help show this help message and exit
-v, --verbose show more details on packages and actions
--register Register the target Python in Windows (file extensions, icons, context menu, start menu), under the 'WinPython' PEP-514 vendor key
--unregister Unregister the target Python from Windows: de-associate file extensions, icons and context menu, and remove its start menu folder
--fix make the target Python use absolute (fixed) paths in launchers and shebangs
--movable make the target Python (any Windows Python) movable/portable: relative paths in launchers and shebangs
-ws WHEELSOURCE wheels location, ('.' = WheelHouse): wppm pylock.toml -ws source_of_wheels, wppm -ls -ws .
-wd WHEELDRAIN wheels destination: wppm pylock.toml -wd destination_of_wheels
-ls, --list list installed packages matching [optional] expression: wppm -ls, wppm -ls pand
-lsa list details of packages matching [optional] expression: wppm -lsa pandas -l1
-md markdown summary of the installation
-p show Package (!= missing) dependencies of the given package[option], [.]=all: wppm -p pandas[.]
-r show Reverse (!= constraining) dependancies of the given package[option]: wppm -r pytest![test]
-l LEVELS show 'LEVELS' levels of dependencies (with -p, -r): wppm -p pandas -l1
-j, --json machine-readable JSON output (with -p, -r, -ls, -md): wppm -p pandas[.] -j
-t TARGET path to target Python distribution (default: current environment)
-i, --install install a given package wheel or pylock file (use pip for more features)
-u, --uninstall uninstall package (use pip for more features)
- Source code: https://github.com/winpython/winpython
- Issues and feature requests: https://github.com/winpython/winpython/issues
- Discussions: https://github.com/winpython/winpython/discussions
- WinPython distribution: https://winpython.github.io/