| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 9130dd0 commit 78ce8d3
2 files changed
| Original file line number | Diff line number | Diff 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. | ||
| 32 | 7 | ||
| 33 | 8 | ```console | |
| 34 | - wppm -p pandas[.] -l1 | ||
| 9 | + pip install wppm | ||
| 35 | 10 | ``` | |
| 36 | 11 | ||
| 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? | ||
| 39 | 15 | ||
| 40 | 16 | ```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 | ||
| 43 | 27 | ``` | |
| 44 | 28 | ||
| 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: | ||
| 47 | 34 | ||
| 48 | 35 | ```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 | ||
| 50 | 54 | ``` | |
| 51 | 55 | ||
| 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: | ||
| 53 | 82 | ||
| 54 | 83 | ```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] | ||
| 56 | 87 | ``` | |
| 57 | 88 | ||
| 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: | ||
| 59 | 93 | ||
| 60 | 94 | ```console | |
| 61 | - wppm -md --json | ||
| 95 | + $ wppm -p ".[.]" -l9 | ||
| 62 | 96 | ``` | |
| 63 | 97 | ||
| 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 | + ``` | ||
| 65 | 116 | ||
| 66 | 117 | ```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)" | ||
| 68 | 119 | ``` | |
| 69 | 120 | ||
| 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 | + | ||
| 70 | 183 | ## Command line | |
| 71 | 184 | ||
| 72 | 185 | ```text | |
@@ -75,7 +188,7 @@ usage: wppm [-h] [-v] [--register] [--unregister] [--fix] [--movable] | |||
| 75 | 188 | [-l LEVELS] [-j] [-t TARGET] [-i] [-u] | |
| 76 | 189 | [package(s) or lockfile ...] | |
| 77 | 190 | ||
| 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) | ||
| 79 | 192 | ||
| 80 | 193 | positional arguments: | |
| 81 | 194 | package(s) or lockfile | |
@@ -84,22 +197,22 @@ positional arguments: | |||
| 84 | 197 | options: | |
| 85 | 198 | -h, --help show this help message and exit | |
| 86 | 199 | -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 | ||
| 89 | 202 | --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 | ||
| 91 | 204 | -ws WHEELSOURCE wheels location, ('.' = WheelHouse): wppm pylock.toml -ws source_of_wheels, wppm -ls -ws . | |
| 92 | 205 | -wd WHEELDRAIN wheels destination: wppm pylock.toml -wd destination_of_wheels | |
| 93 | 206 | -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 | ||
| 95 | 208 | -md markdown summary of the installation | |
| 96 | 209 | -p show Package (!= missing) dependencies of the given package[option], [.]=all: wppm -p pandas[.] | |
| 97 | 210 | -r show Reverse (!= constraining) dependancies of the given package[option]: wppm -r pytest![test] | |
| 98 | 211 | -l LEVELS show 'LEVELS' levels of dependencies (with -p, -r): wppm -p pandas -l1 | |
| 99 | 212 | -j, --json machine-readable JSON output (with -p, -r, -ls, -md): wppm -p pandas[.] -j | |
| 100 | 213 | -t TARGET path to target Python distribution (default: current environment) | |
| 101 | 214 | -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) | ||
| 103 | 216 | ``` | |
| 104 | 217 | ||
| 105 | 218 | ## Links | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,12 +23,17 @@ classifiers=[ | |||
| 23 | 23 | 'Operating System :: Unix', | |
| 24 | 24 | 'Programming Language :: Python :: 3', | |
| 25 | 25 | 'Development Status :: 5 - Production/Stable', | |
| 26 | + 'Environment :: Console', | ||
| 27 | + 'Intended Audience :: Developers', | ||
| 28 | + 'Intended Audience :: System Administrators', | ||
| 26 | 29 | 'Topic :: Scientific/Engineering', | |
| 30 | + 'Topic :: Software Development :: Build Tools', | ||
| 31 | + 'Topic :: System :: Software Distribution', | ||
| 27 | 32 | 'Topic :: Software Development :: Widget Sets', | |
| 28 | 33 | ] | |
| 29 | 34 | 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"] | ||
| 32 | 37 | ||
| 33 | 38 | [project.urls] | |
| 34 | 39 | Documentation = "https://winpython.github.io/" | |
| Back | FazBrowse Home | New Git URL |
0 commit comments