Windows Store package to become installer/launcher?

I think there’s a certain section[1] of the userbase that values the credibility of getting Python from python.org rather than from “indygreg/python-build-standalone”. Getting it from uv/hatch/PDM (which sort of hides the fact that it’s actually from the latter) is a bit of a middle ground, as workflow tools are sort of expected to include “manage the runtime” functionality.

I think that we should care, though, as unlike Linux distros, which have a clear support infrastructure, people will view the version of Python they get from (say) uv python install as being “just normal Python”, so bug reports will come to us, often without clear attribution of where the distribution came from. If the version used by uv has patches that aren’t part of core, we’ll have maintenance issues to worry about.

Once uv is distributing binaries built from the exact source we ship, it’s less important to us where they get them from, but we have the infrastructure, and people want “official” binaries, so why shouldn’t we provide them?


  1. maybe not a large section, though… ↩︎

3 Likes

I don’t like it. I believe adding this fake python.exe was quite borderline already. It should have been py.exe from the beginning. Adding those commands to py.exe would make sense to me. I am still undecided whether I like the idea of having a py.exe (or the current fake python.exe) pre-installed on the operating system, though.

2 Likes

Just to be clear, the pre-installed one belongs to Windows, not us.[1] And Microsoft are far more strict on compatibility, so I wouldn’t expect it to go anywhere.

We can suggest which app in the Store the preinstalled python.exe will open, and as long as they agree, our suggestion will probably be taken. But that’s the extent. They could easily point it at their own app instead of ours.


  1. And for those who are aware of where I work, I’m not on the team that owns it, and not in the management hierarchy for them. I have to ask just as politely as anyone external, and probably have more influence as a core dev than as a colleague. ↩︎

1 Like

I have to say that the combination of not having to build from source on Linux and being able to get security fix versions on Windows made me give up that position. I only use pyenv[1] for prereleases and my default python command at this point. However I’m actually more reluctant to recommend uv to newcomers due to its own install method[2] rather than the source of its Python installs.


  1. and pyenv-win ↩︎

  2. Downloading and running a Powershell script for Windows ↩︎

We can only change it in ways that distros will actually follow, otherwise the document becomes irrelevant :‍)

A new tool to manage installs from python.org (and possibly even other places) sounds like a reasonable idea. But I doubt you can get a package manager that can install from python.org to be installed by default on major distros – especially one called python.
Aside from the issue of convincing people, there are relevant use cases for using Python that’s already installed by other means, where you don’t want install and list as subcommands of the interpreter. Like in a venv, for example.

I agree that we should reclaim python, but as a way to launch the interpreter. Specifically: launch the “preferred” version for the current environment – which means different things in a venv, a Linux distro, or a fresh install of Windows. It could mean downloading/installing first, but not everywhere.

This is what I’m proposing. I assume you didn’t miss that (global) python <no subcommand> just forwards args directly to the preferred interpreter? I’ve said it enough times, but a lot of responses don’t seem to acknowledge it.

Notably (on Windows), python.exe in a venv is a totally different executable from any other python.exe (it’s not a symlink[1]). So the default behaviour is to just work as expected. I’ve explicitly said that won’t change - the downside being that it means you lose the subcommands in a venv, which may surprise some users, but they can easily be forwarded through if that seems like a problem.

Whether or not things spread to other platforms is a discussion for others to take up. I’m not specifically offering the Windows Store install as a testbed - I really do think this is for the benefit of this particular subset of our distribution users - but I’m happy for others to see it that way if it helps influence our ecosystem-wide user experience.


  1. Unless you specifically request it, but a symlink would behave much the same i.e. be as inconsistent as ever :wink: ↩︎

So we (in effect) have three variations of python.exe on Windows: the one in a venv, the global one installed by the OS, and the one installed by the python.org installers. And on top of all that, we have py.exe which will redirect to any one[1] of those, depending on varions factors.

That’s a bit of a mess, to be honest.

IMO, what’s most important is that all of those executables behave in the same way. The one exception is that the global OS python.exe has additional functionality whereby, if it is run on a system with no Python installed[2] then it will install Python (currently by opening Windows Store, but if I understand correctly this might change under the current proposal).

So specifically, I think all of the subcommands should be available in all of the python.exe interpreters or none of them. Anything else would just make an already-confusing situation worse.

How any of the above is implemented is a technical detail that I’m not too worried about. What’s important to me is the user experience.

As far as non-Windows platforms are concerned, I’m happy to leave that decision to others. If people are OK with the proposed subcommands only existing (for now) on Windows, that’s fine. If consistency across platforms is preferred, then fine, either we implement the subcommands everywhere or we remove them from the proposed Windows solution. I’d like to see the subcommands available on Windows, but I don’t think that’s my call. Honestly, I suspect questions around the cross-platform behaviour of the python command are probably something the SC should make the call on.


  1. maybe never to the global OS one, I’m not sure about that ↩︎

  2. which is a special case, because all of the other executables by definition only exist if Python is installed… And I’m not clear if this is “no Python at all”, or “no Store Python”, but that’s a digression for now ↩︎

2 Likes

This one is a 4th, that is owned by Microsoft, not us. All it can do is open the Windows Store to a particular app. When a user installs our Store package, it replaces Microsoft’s one. There’s no detection logic, just a flag that lets our package replace the global shortcut (typically user preference overrules new installs, in the Windows Store model).

You didn’t even mention the versioned commands, or the experimental free-threaded builds :slight_smile: So things are already worse than you think. And if we don’t add the subcommands, then we’ll just get another top-level command, so “worse” is inevitable (at least by this measure).

Oh absolutely, this all goes into a (core) PEP before anything changes. But I want to take some kind of consensus in a PEP, not an untested proposal.

OK. After a lot of thought and false starts, I think I finally understand the issue. Let me try to restate it:

  1. The python.exe shipped with the OS just opens the Store on “some app” called Python. In order to do what the user is expecting, the app needs to install a python.exe which runs the Python interpreter. That’s not negotiable, it’s a consequence of how the python command is exposed, and what the UX needs to be.
  2. (This is the bit I missed, because you only mentioned it in passing in a later message). We can’t simply rename the “Python 3.13” app as “Python” because the Windows Store auto-upgrades installed applications. Python’s compatibility policy doesn’t guarantee that 3.14 is suitable for auto-upgrade from 3.13, so we’d break users if we did this.
  3. Given these two constraints, if we’re not happy with the status quo, we really don’t have any choice other than to have the “Python” app install yet another launcher, whose purpose is to manage the Python interpreters on the user’s machine while still by default launching Python (because of (1) in this list).

Honestly, that sucks. We can argue whether things should have been done differently, but there’s no point - we have to live with how things are.

My first thought is that the status quo doesn’t seem that bad, given the alternative. Obviously I can’t comment on the release management overhead, but other than that, I don’t recall seeing many people complain.

If we do have to switch, I’d be in favour of a simple installer that just downloaded the appropriate Python version if there wasn’t one present, and ran it. That’s simple to describe, understand, and maintain. It does leave us with the problem of how does the user manage Python in this situation? There’s no store app that lets them upgrade to newer versions, so we need to provide something. But why can’t we do that via a standard library module? The UI for managing your Python installations could be as simple as python -m manage and we could add whatever we felt was necessary in there - commands to list and install Python versions at a minimum, and maybe other features if we felt they would be useful. The stdlib module would be available everywhere, maintaining consistency. On Unix systems, at least for now, it could simply print a message saying “You should use your distribution’s package manager to install and maintain Python versions”, so we’re not immediately faced with how we deal with distro policies.

I’m sad that doing it this way means that there’s no justification for sneaking in pipx-style functionality, but I’d rather do it right (and that functionality could be added to the runpy module at a later date anyway…)

4 Likes

Or an earlier date :slight_smile:

2 Likes

Isn’t this the chicken-and-egg problem that led in part to conda’s existence and uv being written in Rust?

2 Likes

It’s manageable (we’d just distribute a private Python runtime and tell/hope that users won’t touch it or they’ll break themselves… hmm… guess I’m not such a fan :upside_down_face: ), but it doesn’t solve discoverability.

Of course the implementation of any manager that’s a python-dev project is largely going to be in Python. Having a dedicated entry point lets us protect the private runtime, so that users don’t modify/install/uninstall pieces and find themselves broken.

In the sense that python -m uv has exactly the interpreter listing and installation commands I think you’re talking about (as well as tool and script running :slightly_smiling_face:) it’s definitely manageable. We wouldn’t bring uv into the stdlib, obviously, but as a proof of concept it demonstrates that it works.

Protecting the running interpreter is a problem any tool written in Python has to solve. Worst case, ship an executable and subprocess.run it (maybe with some “wait for the parent to terminate” senannigans because we don’t have exec…).

I don’t see how discoverability is so much worse? Finding python install is no easier than finding python -m manage. You can put either one into python -h.

The difference is that python -m is meant to go to the installed runtime, not the launcher. So we would still have a subcommand, it would just be -m manage is the subcommand.

It also runs into all the issues with conflicting with existing third-party libraries, so we probably end up with -m managelib at best :frowning:

In contrast, python install only conflicts with a literal install file in the current directory, not with libraries or modules that may have been written by someone else, and python ./install (or python run install) is an easy way to work around it. (Though I suppose python run -m install is equally discoverable, as in, not very.)

Are we talking at cross purposes? My point is that -m manage would be handled by the installed runtime. The launcher would just start the runtime and then get out of the way. No different than py -m manage.

Although I’m not wedded to the idea of having a stdlib module for the manager. I’m just -1 on having it be a launcher subcommand. It should be a subcommand of the interpreter if you want to go down the subcommand route, as that’s the only way to ensure the capabilities are consistent no matter how you invoke python.

For the same reason that pip install -U pip doesn’t work, we can’t really do this. We also would have to deal with different versions floating around trying to synchronise on the same OS state/configurations. I don’t doubt we can do that, if needed, but virtually nobody else in the Python ecosystem does, so I think it would scare off contributions.

For the same reason that pip install -U pip doesn’t work, we can’t really do this.

Was that meant as sarcasm? When I run an outdated version of pip it absolutely reminds me:

[notice] A new release of pip is available: 23.0.1 → 24.3.1
[notice] To update, run: python3 -m pip install --upgrade pip

Maybe I’m missing some nuance… Or do you mean it’s only broken on Windows?

pip install -U pip can’t (on Windows) replace the pip.exe file, because the OS doesn’t let you replace the running program. python -m pip install -U pip isn’t subject to that issue.

Having said that, I believe there are ways round this - I know, for example, that uv self update works, even though that replaces the running copy of uv. I don’t know how it works, but it does. So while this is an annoying issue that Steve is right to point out, it’s not an absolute showstopper.

I still insist that we should ensure that the program invoked by typing python has the same capabilities no matter how it’s invoked, though. Technical challenges might make this difficult, or even impossible for all practical purposes, but IMO user experience is the priority here. We should stick with the status quo if we can’t find a workable solution for the new proposal within the constraints we have.

rust has this crate GitHub - mitsuhiko/self-replace: Utility library that helps to implement processes that replace themselves, which describes the trick it uses in its docs: self_replace - Rust.

Having said that, I believe there are ways round this - I know, for example, that uv self update works, even though that replaces the running copy of uv. I don’t know how it works, but it does. So while this is an annoying issue that Steve is right to point out, it’s not an absolute showstopper.

It only very recently started working where it actually replaces itself. Before could run into the usual access denied errors.

rust has this crate GitHub - mitsuhiko/self-replace: Utility library that helps to implement processes that replace themselves, which describes the trick it uses in its docs: self_replace - Rust.

Yes, uv has recently started using that: https://github.com/astral-sh/uv/pull/8914. And they have run into issues with Microsoft security tools: https://github.com/astral-sh/uv/issues/9144. So something to be aware of.

2 Likes