Skip to content

fix(pipx): honor install_before for uv and pipx installs#9190

Merged
jdx merged 6 commits intojdx:mainfrom
risu729:codex/pipx-uploaded-prior-to
Apr 17, 2026
Merged

fix(pipx): honor install_before for uv and pipx installs#9190
jdx merged 6 commits intojdx:mainfrom
risu729:codex/pipx-uploaded-prior-to

Conversation

@risu729
Copy link
Copy Markdown
Contributor

@risu729 risu729 commented Apr 17, 2026

Supersedes and includes #9105.

Summary

  • forward install_before to transitive Python package resolution when pipx: tools are installed through uv
  • pass uv's --exclude-newer <timestamp> flag on the uv install path
  • pass pip's --uploaded-prior-to <timestamp> flag through pipx install --pip-args on the pipx fallback path
  • remove any inherited PIPX_SHARED_LIBS before invoking pipx so pipx falls back to PIPX_HOME/shared
  • update docs now that both uv and pipx install paths forward the cutoff
  • add focused unit coverage for uv and pipx argument generation

uv compatibility

--exclude-newer was added in astral-sh/uv#2166 and is included in https://github.com/astral-sh/uv/releases/tag/0.1.15, so we do not need to check the uv version before using this flag.

Why we can always use --uploaded-prior-to

--uploaded-prior-to was added in pypa/pip#13625 and released in pip 26.0, announced on January 31, 2026: https://discuss.python.org/t/announcement-pip-26-0-release/105947. PyPI currently lists pip 26.0.1 as the latest release as of April 17, 2026: https://pypi.org/project/pip/.

pipx does not use a fixed bundled pip. It creates or reuses a shared virtual environment containing pip and ensures that shared pip is updated to the latest version: https://pipx.pypa.io/stable/how-pipx-works/. In the current pipx source, shared pip is installed/upgraded with pip >= 23.1 and no upper bound, so normal PyPI access gets the latest pip: https://github.com/pypa/pipx/blob/main/src/pipx/shared_libs.py.

mise sets PIPX_HOME to the tool version's install path when invoking the pipx fallback. That install path is prepared before the backend runs: fresh installs create a new install directory, and mise install --force uninstalls the existing version, removes the install path, and recreates it before calling pipx install. With pipx's default behavior, PIPX_SHARED_LIBS resolves to <install_path>/shared, which is new for that install and can be initialized/upgraded by pipx to the latest pip.

Why unsetting PIPX_SHARED_LIBS is acceptable

PIPX_SHARED_LIBS is a real pipx override: it can point pipx's shared pip virtualenv outside PIPX_HOME. pipx documents that it defaults to PIPX_HOME/shared, but can be overridden: https://pipx.pypa.io/stable/reference/environment-variables/. It can be useful for deliberate CI caching or ephemeral PIPX_HOME setups, but it only shares the packaging toolchain, especially pip. It does not make installed pipx apps share their application dependencies.

For mise-managed pipx: installs, inheriting an external PIPX_SHARED_LIBS is unsafe for this feature because it can point at an older shared pip that does not support --uploaded-prior-to. It also makes the mise install path no longer self-contained, because pipx app venvs can contain absolute .pth pointers into that external shared libs path. Moving, deleting, or changing that external path can break later pipx upgrade, pipx inject, or pipx runpip operations.

I could not find public discussions indicating that users commonly rely on PIPX_SHARED_LIBS for mise-managed pipx installs, and the default pipx guidance is to leave it unset unless there is a specific reason. So excluding this inherited variable for mise's pipx subprocesses should not be breaking for most users, and it avoids a footgun where a global shell/profile setting silently changes mise's install behavior.

Related pipx docs for shared pip maintenance:

Tests

  • cargo fmt --check
  • git diff --check
  • cargo test -q uv_exclude_newer_args
  • cargo test -q pip_uploaded_prior_to_args

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enables the install_before setting for the pipx backend, allowing transitive dependency resolution to respect a cutoff date using uv's --exclude-newer or pip's --uploaded-prior-to flags. The changes include updated documentation, new argument generation logic in the pipx backend with associated tests, and the isolation of shared libraries via the PIPX_SHARED_LIBS environment variable. Review feedback suggests using the = syntax for pip arguments to improve parsing reliability and recommends considering a centralized storage location for shared libraries to reduce disk usage while maintaining isolation.

Comment thread src/backend/pipx.rs
Comment thread src/backend/pipx.rs Outdated
@risu729 risu729 force-pushed the codex/pipx-uploaded-prior-to branch from 283e6db to 9c00ebb Compare April 17, 2026 10:25
@risu729 risu729 changed the title fix(pipx): forward install_before to pip fix(pipx): honor install_before for uv and pipx installs Apr 17, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 17, 2026

Greptile Summary

This PR extends install_before support to the pipx: backend so that the cutoff is forwarded into transitive dependency resolution during install. The uv path passes --exclude-newer <timestamp> to uv tool install, the pipx fallback passes --uploaded-prior-to=<timestamp> via --pip-args, and PIPX_SHARED_LIBS is unset before invoking pipx so the shared pip environment is self-contained and initialized with the latest pip. A new env_remove helper is added to CmdLineRunner.

  • The pip_uploaded_prior_to_args function formats the timestamp with {before_date} (jiff's Display), which produces up to 9 fractional digits for relative durations (e.g. 7d) derived from Timestamp::now(). pip's datetime.fromisoformat() only accepts up to 6 fractional digits, so any sub-microsecond nanoseconds will cause pip to raise a ValueError and fail every install on the pipx fallback path when a relative duration is used.

Confidence Score: 4/5

Not yet safe to merge — the pip fallback path will fail every install when install_before is a relative duration due to a datetime precision issue identified in the review thread that remains unaddressed in the current commit.

The uv path and all env-management changes are correct. However, pip_uploaded_prior_to_args formats the timestamp with jiff's Display (up to 9 nanosecond digits), and Python's datetime.fromisoformat() only accepts up to 6 fractional digits. Relative durations (the common case — e.g. "7d") produce Timestamp::now() values with sub-microsecond nanoseconds, causing pip to raise ValueError and fail the install on the pipx fallback path. This P1 issue was flagged in the review thread (line 302) but the fix ({before_date:.6} precision specifier) has not been applied in the current HEAD commit.

src/backend/pipx.rs — specifically pip_uploaded_prior_to_args and its test

Important Files Changed

Filename Overview
src/backend/pipx.rs Adds uv_exclude_newer_args and pip_uploaded_prior_to_args helpers; pip path uses jiff Timestamp::to_string() which can emit nanoseconds (up to 9 fractional digits) that Python's datetime.fromisoformat() cannot parse
src/cmd.rs Adds env_remove() delegating to std::process::Command::env_remove — straightforward and correct
docs/dev-tools/backends/pipx.md Adds a paragraph noting that install_before is forwarded via --exclude-newer (uv) and --uploaded-prior-to (pipx)
docs/dev-tools/mise-lock.md Updates transitive-dependency forwarding note to include pipx: alongside npm:
docs/tips-and-tricks.md Extends the install_before prose to mention both npm: and pipx: backends
settings.toml Adds a line to install_before docs mentioning that npm: and pipx: backends forward the cutoff to transitive deps

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[mise install pipx:tool] --> B{uv installed?}
    B -- Yes --> C[uv tool install pkg]
    B -- No --> D[pipx install pkg]

    E[ctx.before_date Option Timestamp] --> F{Some?}
    F -- No --> G[no extra args]
    F -- Yes --> H[uv_exclude_newer_args]
    F -- Yes --> I[pip_uploaded_prior_to_args]

    H --> J[--exclude-newer timestamp - uv handles nanoseconds]
    I --> K[--pip-args --uploaded-prior-to=timestamp - pip needs 6 fractional digits max]

    J --> C
    K --> D

    L[env_remove PIPX_SHARED_LIBS] --> D
    M[PIPX_HOME = install_path / PIPX_BIN_DIR = install_path/bin] --> D
Loading

Reviews (3): Last reviewed commit: "fix(pipx): use equals syntax for pip cut..." | Re-trigger Greptile

Comment thread src/backend/pipx.rs
Comment thread src/backend/pipx.rs
@risu729 risu729 force-pushed the codex/pipx-uploaded-prior-to branch from 9c00ebb to 2669f07 Compare April 17, 2026 10:42
@risu729 risu729 marked this pull request as ready for review April 17, 2026 10:53
@jdx jdx merged commit d405e4f into jdx:main Apr 17, 2026
35 checks passed
@risu729 risu729 deleted the codex/pipx-uploaded-prior-to branch April 17, 2026 18:48
jdx pushed a commit that referenced this pull request Apr 18, 2026
### 🐛 Bug Fixes

- **(backend)** respect install_before in latest lookup by @risu729 in
[#9193](#9193)
- **(backend)** route explicit latest through stable lookup by @risu729
in [#9228](#9228)
- **(backends)** deprecate b shorthand by @risu729 in
[#9234](#9234)
- **(config)** warn for deprecated env keys by @risu729 in
[#9205](#9205)
- **(config)** treat enable_tools empty as disable-all by @risu729 in
[#9108](#9108)
- **(github)** avoid auth on release asset downloads by @risu729 in
[#9060](#9060)
- **(gitlab)** warn when glab OAuth2 token is expired by @stanhu in
[#9195](#9195)
- **(npm)** honor install_before without day drift by @risu729 in
[#9157](#9157)
- **(npm)** warn on old bun and pnpm for install_before by @risu729 in
[#9232](#9232)
- **(pipx)** honor install_before for uv and pipx installs by @risu729
in [#9190](#9190)
- **(registry)** allow shfmt on Windows by @zeitlinger in
[#9191](#9191)

### 🚜 Refactor

- **(backend)** remove unused rolling release helper by @risu729 in
[#9175](#9175)
- **(backend)** use file util for removals by @risu729 in
[#9206](#9206)

### 📚 Documentation

- **(config)** clarify always_keep_download behavior by @risu729 in
[#9235](#9235)
- **(configuration)** add rust to idiomatic version files by @jjt in
[#9233](#9233)
- **(contributing)** expand contribution guide introduction by
@marianwolf in [#9208](#9208)
- **(github)** document multiple release assets workaround by @risu729
in [#9236](#9236)

### 📦️ Dependency Updates

- update actions/setup-node action to v6 by @renovate[bot] in
[#9183](#9183)
- update dependency @types/node to v25 by @renovate[bot] in
[#9187](#9187)
- update crazy-max/ghaction-import-gpg action to v7 by @renovate[bot] in
[#9186](#9186)
- update actions/cache action to v5 by @renovate[bot] in
[#9181](#9181)
- update amannn/action-semantic-pull-request action to v6 by
@renovate[bot] in [#9184](#9184)
- update apple-actions/import-codesign-certs action to v6 by
@renovate[bot] in [#9185](#9185)
- update dependency eslint to v10 by @renovate[bot] in
[#9200](#9200)
- update dependency toml to v4 by @renovate[bot] in
[#9201](#9201)
- update rust crate reqwest to 0.13 by @renovate[bot] in
[#9171](#9171)
- update ghcr.io/jdx/mise:deb docker digest to 523d826 by @renovate[bot]
in [#9198](#9198)
- update ghcr.io/jdx/mise:alpine docker digest to 05617e0 by
@renovate[bot] in [#9196](#9196)
- update ghcr.io/jdx/mise:rpm docker digest to c1992f9 by @renovate[bot]
in [#9199](#9199)
- update ghcr.io/jdx/mise:copr docker digest to 90db6cd by
@renovate[bot] in [#9197](#9197)
- update taiki-e/install-action digest to 58e8625 by @renovate[bot] in
[#9209](#9209)
- update fedora docker tag to v45 by @renovate[bot] in
[#9213](#9213)
- update docker/setup-buildx-action action to v4 by @renovate[bot] in
[#9212](#9212)
- update docker/metadata-action action to v6 by @renovate[bot] in
[#9211](#9211)
- update docker/login-action action to v4 by @renovate[bot] in
[#9210](#9210)
- update dependency typescript to v6 by @renovate[bot] in
[#9202](#9202)
- update docker/build-push-action action to v7 by @renovate[bot] in
[#9203](#9203)
- update github artifact actions (major) by @renovate[bot] in
[#9215](#9215)
- update rust crate duct to v1 by @renovate[bot] in
[#9220](#9220)
- update rust crate demand to v2 by @renovate[bot] in
[#9219](#9219)
- update rust crate clx to v2 by @renovate[bot] in
[#9218](#9218)
- update nick-fields/retry action to v4 by @renovate[bot] in
[#9217](#9217)
- update jdx/mise-action action to v4 by @renovate[bot] in
[#9216](#9216)
- update rust crate self_update to 0.44 by @renovate[bot] in
[#9174](#9174)
- migrate eslint config to flat format for v10 compat by @jdx in
[#9222](#9222)
- update actions/checkout action to v6 by @renovate[bot] in
[#9182](#9182)
- update rust crate toml to v1 by @renovate[bot] in
[#9225](#9225)
- update rust crate versions to v7 by @renovate[bot] in
[#9226](#9226)
- update rust crate which to v8 by @renovate[bot] in
[#9227](#9227)
- update rust crate rmcp to v1 by @renovate[bot] in
[#9221](#9221)

### 📦 Registry

- add sheldon by @3w36zj6 in
[#9104](#9104)
- add pocketbase by @ranfdev in
[#9123](#9123)
- add worktrunk ([aqua:max-sixty/worktrunk,
cargo:worktrunk](https://github.com/max-sixty/worktrunk,
cargo:worktrunk))#1 by @edouardr in
[#8796](#8796)
- add dependency-check
([aqua:dependency-check/DependencyCheck](https://github.com/dependency-check/DependencyCheck))
by @kapitoshka438 in [#9204](#9204)
- add janet by @ranfdev in
[#9241](#9241)

### New Contributors

- @ranfdev made their first contribution in
[#9241](#9241)
- @jjt made their first contribution in
[#9233](#9233)
- @marianwolf made their first contribution in
[#9208](#9208)
- @edouardr made their first contribution in
[#8796](#8796)

## 📦 Aqua Registry Updates

#### New Packages (3)

- [`LargeModGames/spotatui`](https://github.com/LargeModGames/spotatui)
-
[`android-sms-gateway/cli`](https://github.com/android-sms-gateway/cli)
- [`velero-io/velero`](https://github.com/velero-io/velero)

#### Updated Packages (1)

- [`skim-rs/skim`](https://github.com/skim-rs/skim)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants