Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Install and remove managed Python to and from the Windows Registry (PEP 514) #10634

Merged
merged 4 commits into from
Jan 23, 2025

Conversation

konstin
Copy link
Member

@konstin konstin commented Jan 15, 2025

Summary

In preview mode on windows, register und un-register the managed python build standalone installations in the Windows registry following PEP 514.

We write the values defined in the PEP plus the download URL and hash. We add an entry when installing a version, remove an entry when uninstalling and removing all values when uninstalling with --all. We update entries only by overwriting existing values, there is no "syncing" involved.

Since they are not official builds, pbs gets a prefix. py -V:Astral/CPython3.13.1 works, py -3.13 doesn't.

$ py --list-paths                                            
 -V:3.12 *        C:\Users\Konsti\AppData\Local\Programs\Python\Python312\python.exe
 -V:3.11.9        C:\Users\Konsti\.pyenv\pyenv-win\versions\3.11.9\python.exe
 -V:3.11          C:\Users\micro\AppData\Local\Programs\Python\Python311\python.exe
 -V:3.8           C:\Users\micro\AppData\Local\Programs\Python\Python38\python.exe
 -V:Astral/CPython3.13.1 C:\Users\Konsti\AppData\Roaming\uv\data\python\cpython-3.13.1-windows-x86_64-none\python.exe

Registry errors are reported but not fatal, except for operations on the company key since it's not bound to any specific python interpreter.

On uninstallation, we prune registry entries that have no matching Python installation (i.e. broken entries).

The code uses the official windows_registry crate of the winreg crate.

Best reviewed commit-by-commit.

Test Plan

We're reusing an existing system check to test different (un)installation scenarios.

@konstin konstin added windows Specific to the Windows platform preview Experimental behavior labels Jan 15, 2025
@konstin konstin requested review from Gankra and zanieb January 15, 2025 13:29
@zanieb zanieb self-assigned this Jan 15, 2025
@Gankra
Copy link
Contributor

Gankra commented Jan 15, 2025

Since the registry is global, we can't isolate this into integration tests.

It's not terribly pleasant but you can do runs-in-serial integration tests that save+restore (or clear) the relevant keys if they're vaguely easy to enumerate (e.g. if they're all under HKCU\Software\Python\Astral).

@zanieb
Copy link
Member

zanieb commented Jan 15, 2025

Need to review still, but could we add coverage to one of our integration tests? Like integration test | free-threaded on windows could assert that we can find it with -py etc.? We could create a dedicated "python install on windows" integration test too.

///
/// If windowed is true, `pythonw.exe` is selected over `python.exe` on windows, with no changes
/// on non-windows.
pub fn executable(&self, windowed: bool) -> PathBuf {
Copy link
Member

Choose a reason for hiding this comment

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

Is adding this bool here just for this one use?

    install_path.set_value(
        "WindowedExecutablePath",
        &Value::from(&HSTRING::from(installation.executable(true).as_os_str())),
    )?;

It makes me a little sad to need to pass false everywhere. Unfortunately the logic here is fairly involved... hm.

Copy link
Member Author

Choose a reason for hiding this comment

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

I bailed on rewriting the entire logic here, not sure either.

@zanieb
Copy link
Member

zanieb commented Jan 15, 2025

Looks good to me! I defer to @Gankra on approval since she's more familiar with Windows.

@zanieb
Copy link
Member

zanieb commented Jan 15, 2025

Just wondering, what's the motivation for

... plus the download URL and hash

Copy link
Contributor

@Gankra Gankra left a comment

Choose a reason for hiding this comment

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

Looks good. Biggest concern is the lack of special handling for the "official python install" special cases described in the PEP.

crates/uv-python/src/installation.rs Outdated Show resolved Hide resolved
crates/uv-python/src/lib.rs Outdated Show resolved Hide resolved
crates/uv-python/src/managed.rs Show resolved Hide resolved
crates/uv-python/src/windows_registry.rs Show resolved Hide resolved
crates/uv-python/src/windows_registry.rs Show resolved Hide resolved
crates/uv-python/src/windows_registry.rs Outdated Show resolved Hide resolved
crates/uv-python/src/windows_registry.rs Show resolved Hide resolved
crates/uv-python/src/windows_registry.rs Outdated Show resolved Hide resolved
Comment on lines +453 to +458
/// Link the binaries of a managed Python installation to the bin directory.
#[allow(clippy::fn_params_excessive_bools)]
fn create_bin_links(
Copy link
Contributor

Choose a reason for hiding this comment

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

i'm going to assume this is a direct cut-paste and not look at it unless you tell me otherwise

Copy link
Member Author

Choose a reason for hiding this comment

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

yes the first two commits are just moving code around

Comment on lines 342 to 346
if preview.is_enabled() {
uv_python::windows_registry::create_registry_entry(installation, &mut errors)?;
Copy link
Contributor

Choose a reason for hiding this comment

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

(i wonder if it would be better to pass in preview and have these functions decide whether they're enabled... nbd tho)

@konstin konstin marked this pull request as draft January 17, 2025 13:02
/// Find all Pythons registered in the Windows registry following PEP 514.
pub(crate) fn registry_pythons() -> Result<Vec<WindowsPython>, windows_result::Error> {
let mut registry_pythons = Vec::new();
for root_key in [CURRENT_USER, LOCAL_MACHINE] {
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh I just realized, it's not obvious to me that the current approach actually applies the "CURRENT_USER should shadow LOCAL_MACHINE if they overlap" rule? It seems like the logic here throws out that information completely. (I don't think this has to be a blocker, but it should at least be noted that this is "future work / a looming issue")?

Tools that list every installed environment may choose to include those even where the Company-Tag pairs match. They should ensure users can easily identify whether the registration was per-user or per-machine, and which registration has the higher priority.

Tools that aim to select a single installed environment from all registered environments based on the Company-Tag pair, such as the py.exe launcher, should always select the environment registered in HKEY_CURRENT_USER when than the matching one in HKEY_LOCAL_MACHINE.

Copy link
Member Author

Choose a reason for hiding this comment

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

The way its written sounds like its targeted at a different case than ours: When a user says py -V:Astral/CPython3.13.1, it should use HKEY_CURRENT_USER over HKEY_LOCAL_MACHINE if both have Astral\CPython3.13.1. In uv's discovery, we only support asking of an interpreter kind and a version or version range, and we'll return the first Python we find matching. If a user has the case that they have multiple installations of the same version and they have a specific preference, they can use an absolute path (this should be an edge case). By the order in the list we should still prefer HKEY_CURRENT_USER over HKEY_LOCAL_MACHINE if the version is the same, i added a comment about it.

@konstin konstin temporarily deployed to uv-test-publish January 20, 2025 13:30 — with GitHub Actions Inactive
@konstin konstin temporarily deployed to uv-test-publish January 21, 2025 09:44 — with GitHub Actions Inactive
@konstin konstin temporarily deployed to uv-test-publish January 21, 2025 10:01 — with GitHub Actions Inactive
@konstin konstin force-pushed the konsti/pep-514 branch 3 times, most recently from 3bed1ca to ab8340c Compare January 21, 2025 10:13
@konstin konstin temporarily deployed to uv-test-publish January 21, 2025 10:28 — with GitHub Actions Inactive
@konstin konstin temporarily deployed to uv-test-publish January 21, 2025 10:35 — with GitHub Actions Inactive
@konstin konstin changed the title Install managed Python into the Windows Registry (PEP 514) Install and remove managed Python into the Windows Registry (PEP 514) Jan 21, 2025
@konstin konstin changed the title Install and remove managed Python into the Windows Registry (PEP 514) Install and remove managed Python to and from the Windows Registry (PEP 514) Jan 21, 2025
@konstin konstin marked this pull request as ready for review January 21, 2025 10:42
@konstin konstin temporarily deployed to uv-test-publish January 21, 2025 10:44 — with GitHub Actions Inactive
.github/workflows/ci.yml Outdated Show resolved Hide resolved
@konstin konstin temporarily deployed to uv-test-publish January 23, 2025 11:50 — with GitHub Actions Inactive
@konstin konstin enabled auto-merge (squash) January 23, 2025 12:49
@konstin konstin merged commit db4ab9d into main Jan 23, 2025
71 checks passed
@konstin konstin deleted the konsti/pep-514 branch January 23, 2025 14:13
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Jan 26, 2025
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [astral-sh/uv](https://github.com/astral-sh/uv) | patch | `0.5.22` -> `0.5.24` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>astral-sh/uv (astral-sh/uv)</summary>

### [`v0.5.24`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#0524)

[Compare Source](astral-sh/uv@0.5.23...0.5.24)

##### Enhancements

-   Improve determinism of resolution by always setting package priorities ([#&#8203;10853](astral-sh/uv#10853))
-   Upgrade to `cargo-dist` 0.28.0; improves several installer behaviors ([#&#8203;10884](astral-sh/uv#10884))

##### Performance

-   Remove dependencies clone in resolver ([#&#8203;10880](astral-sh/uv#10880))
-   Use Hashbrown's raw entry API to reduce hashes and clone in resolver priority determination ([#&#8203;10881](astral-sh/uv#10881))

##### Bug fixes

-   Allow fallback to Python download on non-critical discovery errors ([#&#8203;10908](astral-sh/uv#10908))

##### Preview features

-   Register managed Python version with the Windows Registry (PEP 514) ([#&#8203;10634](astral-sh/uv#10634))

##### Documentation

-   Improve documentation for some environment variables ([#&#8203;10887](astral-sh/uv#10887))
-   Add git subdirectory example ([#&#8203;10894](astral-sh/uv#10894))

### [`v0.5.23`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#0523)

[Compare Source](astral-sh/uv@0.5.22...0.5.23)

##### Enhancements

-   Add `--refresh` to `uv venv` ([#&#8203;10834](astral-sh/uv#10834))
-   Add `--no-default-groups` command-line flag ([#&#8203;10618](astral-sh/uv#10618))

##### Bug fixes

-   Sort extras and groups when comparing lockfile requirements ([#&#8203;10856](astral-sh/uv#10856))
-   Include `commit_id` and `requested_revision` in `direct_url.json` ([#&#8203;10862](astral-sh/uv#10862))
-   Invalidate lockfile when static versions change ([#&#8203;10858](astral-sh/uv#10858))
-   Make GitHub fast path errors non-fatal ([#&#8203;10859](astral-sh/uv#10859))
-   Remove warnings for `--frozen` and `--locked` in `uv run --script` ([#&#8203;10840](astral-sh/uv#10840))
-   Resolve `find-links` paths relative to the configuration file ([#&#8203;10827](astral-sh/uv#10827))
-   Respect visitation order for proxy packages ([#&#8203;10833](astral-sh/uv#10833))
-   Treat version mismatch errors as non-fatal in fast paths ([#&#8203;10860](astral-sh/uv#10860))
-   Mark `--locked` and `--upgrade` are conflicting ([#&#8203;10836](astral-sh/uv#10836))
-   Relax error checking around unconditional enabling of conflicting extras ([#&#8203;10875](astral-sh/uv#10875))

##### Documentation

-   Reduce ambiguity in conflicting extras example ([#&#8203;10877](astral-sh/uv#10877))
-   Update pre-commit documentation  ([#&#8203;10756](astral-sh/uv#10756))

##### Error messages

-   Error when workspace contains conflicting Python requirements ([#&#8203;10841](astral-sh/uv#10841))
-   Improve uvx error message when uv is missing ([#&#8203;9745](astral-sh/uv#9745))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xMjIuMCIsInVwZGF0ZWRJblZlciI6IjM5LjEyNC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiXX0=-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
preview Experimental behavior windows Specific to the Windows platform
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants