Skip to content

Commit 78ce8d3

Browse files
stonebigclaude
andcommitted
README_PYPI: lead with real output, and make the package findable
The page listed what wppm can do without ever showing a result, so a visitor had to imagine the output. Each section now asks a question and answers it with actual terminal output, all of it run and pasted verbatim: - wppm -p "flit![.]" as the opener: which extras of an installed package are unusable here, and exactly what is missing, - wppm -r "pytest[.]": who pulls a package in, through which extra -- the granularity pipdeptree does not have, - wppm -r "pluggy!": the handful of packages that cap it, ie what will actually fight the next upgrade. Adds a library section (piptree.PipData().down(..., format="json")), since the tree engine is importable and takes a target= -- no subprocess, no parsing of terminal output. pyproject: the PyPI search line read "WinPython Package Management" and the keywords were Portable/Windows, so nobody looking for a dependency tool could match it. Description and keywords now say what it does, plus console/build-tools classifiers. Usage block regenerated from --help; it had drifted, and now differs only in the -t default, kept generic instead of a local path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 9130dd0 commit 78ce8d3

2 files changed

Lines changed: 171 additions & 53 deletions

File tree

README_PYPI.md

Lines changed: 164 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,185 @@
1-
# wppm — dependency trees, offline wheelhouses, portable Pythons
2-
3-
`wppm` complements `pip` on **any** Python environment (it was born in
4-
[WinPython](https://winpython.github.io/), the portable distribution for Windows,
5-
but does not require it). `pip` remains the recommended way to add or remove
6-
packages; `wppm` covers what `pip` doesn't show or do:
7-
8-
- **extras-aware dependency trees**: what does `pandas[test]` pull in? What does
9-
*each* extra of a package pull in (`pandas[.]`)? Which installed packages use
10-
`pytest`, through which extra?
11-
- **constraint hunting**: `wppm -r numpy!` shows only the packages that *pin or cap*
12-
numpy — the ones that will hurt when you upgrade,
13-
- **missing-dependency detection**: trees flag requirements that are not installed
14-
(`lxml==? >=5.3.0;extra==xml`),
15-
- **JSON everywhere** (`-j`): dependency trees, package lists and environment
16-
manifests as machine-readable output, for CI gates and diffing,
17-
- **offline wheelhouse tooling**: install from a directory of wheels or a
18-
`pylock.toml`, inventory a wheelhouse without installing anything (`-ls -ws`),
19-
- **environment manifest** (`-md`): one document — Markdown or JSON — describing the
20-
distribution, its tools, its packages and its wheelhouse; a lightweight SBOM,
21-
- **portability housekeeping**: make any target Python movable (relative shebangs
22-
and launchers) or fixed, register/unregister it in Windows.
23-
24-
Compared with `pipdeptree`: `wppm` adds per-`[extra]` granularity in both
25-
directions, the constraining-dependency filter (`!`), missing-dependency flags,
26-
and it can inspect another environment (`-t`) or a plain directory of wheels
27-
(`-ws`) — no need to install anything into it first.
28-
29-
## Examples
30-
31-
What each extra of `pandas` would pull in, one level deep:
1+
# wppm — the dependency questions `pip` won't answer
2+
3+
`wppm` is a small companion to `pip`, for **any** Python environment (it was born in
4+
[WinPython](https://winpython.github.io/), the portable Windows distribution, but does
5+
not require it). Keep using `pip` to install and remove things — use `wppm` to *see*
6+
what is actually there.
327

338
```console
34-
wppm -p pandas[.] -l1
9+
pip install wppm
3510
```
3611

37-
Which installed packages depend on `pytest` (through which extra), and which ones
38-
constrain it hard (`!`):
12+
## Which extras of a package are actually usable here?
13+
14+
You installed `flit`. Its `[doc]` and `[test]` extras promise more. What is missing?
3915

4016
```console
41-
wppm -r pytest[test]
42-
wppm -r pytest![test]
17+
$ wppm -p "flit![.]"
18+
flit[doc]==3.12.0 ,
19+
pygments-github-lexers==? ;extra==doc
20+
sphinx==? ;extra==doc
21+
sphinxcontrib-github-alt==? ;extra==doc
22+
flit[test]==3.12.0 ,
23+
pytest-cov==? ;extra==test
24+
responses==? ;extra==test
25+
testpath==? ;extra==test
26+
tomli==? ;extra==test
4327
```
4428

45-
The full constraint web of your environment — every package, every extra, nine
46-
levels deep:
29+
`[.]` means *every extra*, `!` means *only show what is missing*, and `==?` marks a
30+
requirement that is not installed. Extras with nothing missing are simply not printed —
31+
so an empty answer means "everything this package offers is ready to use".
32+
33+
Drop the `!` to see the whole picture instead, installed versions included:
4734

4835
```console
49-
wppm -p .[.] -l9
36+
$ wppm -p "requests[.]" -l1
37+
requests==2.34.2 ,
38+
certifi==2026.6.17 >=2023.5.7
39+
charset-normalizer==3.4.9 <4,>=2
40+
idna==3.18 <4,>=2.5
41+
urllib3==2.7.0 <3,>=1.26
42+
requests[socks]==2.34.2 ,
43+
certifi==2026.6.17 >=2023.5.7
44+
charset-normalizer==3.4.9 <4,>=2
45+
idna==3.18 <4,>=2.5
46+
pysocks==? !=1.5.7,>=1.5.6;extra==socks
47+
urllib3==2.7.0 <3,>=1.26
48+
requests[use-chardet-on-py3]==2.34.2 ,
49+
certifi==2026.6.17 >=2023.5.7
50+
chardet==? <8,>=3.0.2;extra==use-chardet-on-py3
51+
charset-normalizer==3.4.9 <4,>=2
52+
idna==3.18 <4,>=2.5
53+
urllib3==2.7.0 <3,>=1.26
5054
```
5155

52-
A JSON inventory of an offline wheel bundle, without installing it:
56+
## Who pulls in `pytest`, and through which extra?
57+
58+
The reverse direction, `-r`, is extras-aware too — it tells you *why* something is in
59+
your environment, down to the extra that asked for it:
60+
61+
```console
62+
$ wppm -r "pytest[.]"
63+
pytest==9.0.3
64+
pytest[all]==9.0.3 ,
65+
idna[all]==3.18 [requires: pytest>=8.3.2;extra==all]
66+
pandas[all]==3.0.3 [requires: pytest>=8.3.4;extra==all]
67+
pytest[dev]==9.0.3
68+
pytest[test]==9.0.3 ,
69+
flit[test]==3.12.0 [requires: pytest>=2.7.3;extra==test]
70+
pandas[test]==3.0.3 [requires: pytest>=8.3.4;extra==test]
71+
pytest[testing]==9.0.3 ,
72+
pluggy[testing]==1.6.0 [requires: pytest;extra==testing]
73+
pytest[tests]==9.0.3 ,
74+
pillow[tests]==12.3.0 [requires: pytest;extra==tests]
75+
```
76+
77+
## What will break when I upgrade?
78+
79+
With `-r`, the `!` filter keeps only the packages that *pin or cap* the one you name —
80+
the handful that will actually fight your next upgrade, instead of the long list of
81+
packages that merely depend on it:
5382

5483
```console
55-
wppm -ls -ws .\wheelhouse\included.wheels --json
84+
$ wppm -r "pluggy!"
85+
pluggy==1.6.0 ,
86+
pytest==9.0.3 [requires: pluggy<2,>=1.5]
5687
```
5788

58-
A manifest of the current environment (distribution, tools, packages, wheelhouse):
89+
An empty answer here is good news: nothing constrains it, upgrade away.
90+
91+
And the whole constraint web of an environment — every package, every extra, nine
92+
levels deep — is one command:
5993

6094
```console
61-
wppm -md --json
95+
$ wppm -p ".[.]" -l9
6296
```
6397

64-
Fail a CI job if anything in the tree is missing:
98+
## Everything is available as JSON
99+
100+
Any of `-p`, `-r`, `-ls`, `-md` accepts `-j` / `--json`, so the same answers can gate a
101+
CI job or be diffed between two environments:
102+
103+
```console
104+
$ wppm -p pluggy -j
105+
[
106+
{
107+
"package": "pluggy",
108+
"extra": "",
109+
"version": "1.6.0",
110+
"installed": true,
111+
"constraint": "",
112+
"depends": []
113+
}
114+
]
115+
```
65116

66117
```console
67-
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)"
118+
$ 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)"
68119
```
69120

121+
## Or use it from Python
122+
123+
The tree engine is a plain importable module — no subprocess, no parsing of terminal
124+
output. `down()` walks dependencies, `up()` walks them backwards, and both return
125+
indented text by default or a JSON string with `format="json"`:
126+
127+
```python
128+
import json
129+
from wppm import piptree
130+
131+
pip = piptree.PipData() # or PipData(target=r"D:\WPy64\python")
132+
133+
tree = json.loads(pip.down("pandas", "mysql", format="json"))
134+
missing = [d["package"] for d in tree[0]["depends"] if not d["installed"]]
135+
print(f"pandas[mysql] needs: {missing}")
136+
```
137+
138+
```console
139+
pandas[mysql] needs: ['pymysql', 'sqlalchemy']
140+
```
141+
142+
```python
143+
>>> print(pip.up("pluggy!")) # who caps pluggy?
144+
pluggy==1.6.0 ,
145+
pytest==9.0.3 [requires: pluggy<2,>=1.5]
146+
>>> pip.summary("pandas")
147+
'Powerful data structures for data analysis, time series, and statistics'
148+
```
149+
150+
## It also works on environments you have not installed anything into
151+
152+
`-t` points `wppm` at *another* Python distribution, and `-ws` at a plain directory of
153+
wheels — so you can inspect a portable distribution, or an offline bundle, without
154+
installing it first:
155+
156+
```console
157+
$ wppm -ls -ws .\wheelhouse\included.wheels --json
158+
$ wppm -p "pandas[.]" -t D:\WPy64\python
159+
```
160+
161+
Beyond inspection, `wppm` installs from a wheelhouse or a `pylock.toml` (`-i`, `-ws`,
162+
`-wd`), emits a one-document environment manifest — distribution, tools, packages,
163+
wheelhouse — as Markdown or JSON (`-md`, a lightweight SBOM), and does portability
164+
housekeeping: on any Windows Python, `--movable` / `--fix` rewrite the `Scripts\`
165+
launchers and shebangs between relative and absolute paths, so a directory can be moved
166+
(or pinned back down) without breaking its entry points.
167+
168+
`--register` / `--unregister` associate file extensions, icons, context menu and start
169+
menu entries with the target Python. Each distribution gets its own start menu folder,
170+
so registering one never disturbs another — but note that the target is declared under
171+
the `WinPython` PEP-514 vendor key.
172+
173+
## Compared with `pipdeptree`
174+
175+
`wppm` adds per-`[extra]` granularity in **both** directions, the `!` filter (missing
176+
dependencies forward, constraining dependencies backward), and the ability to inspect
177+
another environment (`-t`) or a bare directory of wheels (`-ws`) without installing
178+
anything into it.
179+
180+
> Quoting: `!` and `[` are shell metacharacters in POSIX shells, so quote the argument
181+
> (`wppm -p "flit![.]"`). In `cmd.exe` the quotes are optional.
182+
70183
## Command line
71184

72185
```text
@@ -75,7 +188,7 @@ usage: wppm [-h] [-v] [--register] [--unregister] [--fix] [--movable]
75188
[-l LEVELS] [-j] [-t TARGET] [-i] [-u]
76189
[package(s) or lockfile ...]
77190
78-
WinPython Package Manager: handle a Python distribution (WinPython or not) and its packages
191+
WinPython Package Manager: handle a Python distribution (WinPython or not) and its packages (17.9.20260805)
79192
80193
positional arguments:
81194
package(s) or lockfile
@@ -84,22 +197,22 @@ positional arguments:
84197
options:
85198
-h, --help show this help message and exit
86199
-v, --verbose show more details on packages and actions
87-
--register Register the target Python: associate file extensions, icons and context menu with it (useful for portable distributions like WinPython)
88-
--unregister Unregister the target Python: de-associate file extensions, icons and context menu from it
200+
--register Register the target Python in Windows (file extensions, icons, context menu, start menu), under the 'WinPython' PEP-514 vendor key
201+
--unregister Unregister the target Python from Windows: de-associate file extensions, icons and context menu, and remove its start menu folder
89202
--fix make the target Python use absolute (fixed) paths in launchers and shebangs
90-
--movable make the target Python movable/portable: relative paths in launchers and shebangs
203+
--movable make the target Python (any Windows Python) movable/portable: relative paths in launchers and shebangs
91204
-ws WHEELSOURCE wheels location, ('.' = WheelHouse): wppm pylock.toml -ws source_of_wheels, wppm -ls -ws .
92205
-wd WHEELDRAIN wheels destination: wppm pylock.toml -wd destination_of_wheels
93206
-ls, --list list installed packages matching [optional] expression: wppm -ls, wppm -ls pand
94-
-lsa list details of packages matching [optional] expression: wppm -lsa pandas -l1
207+
-lsa list details of packages matching [optional] expression: wppm -lsa pandas -l1
95208
-md markdown summary of the installation
96209
-p show Package (!= missing) dependencies of the given package[option], [.]=all: wppm -p pandas[.]
97210
-r show Reverse (!= constraining) dependancies of the given package[option]: wppm -r pytest![test]
98211
-l LEVELS show 'LEVELS' levels of dependencies (with -p, -r): wppm -p pandas -l1
99212
-j, --json machine-readable JSON output (with -p, -r, -ls, -md): wppm -p pandas[.] -j
100213
-t TARGET path to target Python distribution (default: current environment)
101214
-i, --install install a given package wheel or pylock file (use pip for more features)
102-
-u, --uninstall uninstall package (use pip for more features)
215+
-u, --uninstall uninstall package (use pip for more features)
103216
```
104217

105218
## Links

pyproject.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,17 @@ classifiers=[
2323
'Operating System :: Unix',
2424
'Programming Language :: Python :: 3',
2525
'Development Status :: 5 - Production/Stable',
26+
'Environment :: Console',
27+
'Intended Audience :: Developers',
28+
'Intended Audience :: System Administrators',
2629
'Topic :: Scientific/Engineering',
30+
'Topic :: Software Development :: Build Tools',
31+
'Topic :: System :: Software Distribution',
2732
'Topic :: Software Development :: Widget Sets',
2833
]
2934
dynamic = ["version",]
30-
description="WinPython Package Management"
31-
keywords = ["Portable","Windows"]
35+
description="pip companion: extras-aware dependency trees, offline wheelhouses, portable Pythons"
36+
keywords = ["dependency","dependencies","dependency-tree","pipdeptree","extras","requirements","wheelhouse","pip","sbom","portable","windows"]
3237

3338
[project.urls]
3439
Documentation = "https://winpython.github.io/"

0 commit comments

Comments
 (0)