I had an issue with executing the Python scripts from the PowerShell console - the script always opened a new console window. When I wrote .\script.py
in Command Prompt (cmd.exe
), it was executed in the same window, but in PowerShell console a new window was always opened. It used file association "C:\WINDOWS\py.exe" "%L" %*
and when I tried to run it directly (py.exe .\script.py
), it was executed in the same window even in PowerShell console. I reported it to PowerShell guys, and they recommended to set PATHEXT
to add ;.PY
at the end. So, I did it and the Python script started to run correctly in the same console window. I checked the installer documentation and there is a note that you need to check “Add Python X.Y to PATH”, which I did not have checked. I just have the system-wide launcher installed. So I think it makes sense to modify PATHEXT
when the launcher is installed, not only when the PATH
is modified.
This doesn’t belong in the packaging section, as it relates to the core distribution. But please report it on GitHub - python/cpython: The Python programming language so the idea is recorded.
Currently, the installer only modifies PATHEXT
for an all-users installation, which has administrator access. Setting PATHEXT
in the user environment would replace the system value. It’s possible to prepend the system value by setting a REG_EXPAND_SZ
value such as “%PATHEXT%;.PY;.PYW”. However, I haven’t ever seen this done, and it complicates maintenance a little bit.
Also, what about the app distribution on the Microsoft Store?
Background
Back in the 1990s, the CMD shell introduced the use of PATHEXT
as a list of optional file extensions to append when searching PATH
. In CMD, PATHEXT
has nothing to do with which files are allowed to be executed, nor in what manner they’re allowed to be executed.
To run a file, CMD first tries CreateProcessW()
. If the latter fails due to either ERROR_BAD_EXE_FORMAT
or ERROR_ELEVATION_REQUIRED
, CMD falls back on the shell API. It calls ShellExecuteExW()
, for which it uses the flag SEE_MASK_NO_CONSOLE
in order to avoid creating a new console session. If CreateProcessW()
fails with any other error code, CMD stops and reports the error to the user, such as ERROR_ACCESS_DENIED
. CMD thus relies solely on file security to determine execute permission[1][2].
PowerShell uses PATHEXT
to restrict the file types that can be executed via CreateProcessW()
[3]. If the file type is directly supported by CreateProcessW()
(e.g. “.exe”, “.com”, “.bat”, “.cmd”), then its execute permission is honored. Otherwise PowerShell uses the shell API function FindExecutableW()
to get the path of the associated executable, and it constructs a new command line[4]. In this case the file’s execute permission is ignored.
If the file type isn’t in PATHEXT
, then PowerShell uses ShellExecuteExW()
[3:1], which ignores the file’s execute permission except for directly executable file types (e.g. “.exe”). Also, unlike CMD, PowerShell calls ShellExecuteExW()
without using the flag SEE_MASK_NO_CONSOLE
, and thus a new console session is created.
Files usually inherit inherit discretionary access control that grants execute access to users (or a single user in a profile directory), as well as implicit mandatory access control that allows execution at medium integrity level. There is no technical reason for this relaxed security. However, the way CMD is implemented is problematic, since it doesn’t try
ShellExecuteExW()
ifCreateProcessW()
fails due to a permission error. ↩︎One could argue that this is a mistake when running a script file (it’s certainly a mistake when opening a data file) by taking the point of view that
FILE_EXECUTE
permission on a regular file only relates to the right to map the file as an image section object with page execute access for section views – at least as far as the system API is concerned. According to the documentation, whether to honorFILE_EXECUTE
access on a script file is left up to the interpreter. (Python doesn’t check this.) That said,CreateProcessW()
requires execute access to run a batch script, but running"cmd.exe /c <script>"
does not, so they’re inconsistent in how this is applied. ↩︎In actuality, PowerShell uses the .NET
Process.Start()
method with aProcessStartInfo
record, but ultimately it ends up calling eitherCreateProcessW()
orShellExecuteExW()
. ↩︎ ↩︎PowerShell naively assumes that the command line should be of the form “{executable} {filename} {arguments}”. But an associated application may require particular command-line options to open the given file type. To implement this properly, PowerShell should interpolate the associated command template string that’s returned by
AssocQueryString()
:ASSOCSTR_COMMAND
. ↩︎
The Microsoft Store version of Python is not usable for me, Google Cloud CLI cannot run with it. There are known limitations for the Store app: 4. Using Python on Windows — Python 3.11.1 documentation
Thanks for very detailed answer. Do you think I should create an issue on the PowerShell Core GitHub Issue Tracker to use CreateNoWindow
in the Process.Start(ProcessStartInfo)
call?
I was asking about the situation for users of the store app. I think the most we can reasonably do is to expand the documentation to explain in detail how to manually add “.PY” to the PATHEXT
environment variable, and to explain why it’s needed.
No, the CreateNoWindow
setting translates to the CREATE_NO_WINDOW
process creation flag. This flag spawns the process in a new console session that has no user interface (i.e. no terminal window).
The Process
class in .NET does not support the shell API flag SEE_MASK_NO_CONSOLE
, which prevents creating a new console session. Even if it did, the PowerShell team probably think that it’s appropriate to a create a new console session if the file type isn’t in PATHEXT
. I don’t agree with how PowerShell (and I think also MSYS bash) has chosen to use PATHEXT
, but CMD is far from perfect here as well because it requires execute access in order to open a data file[1].
The shell API should support a “run” operation akin to the already supported “runas” operation, and both should require that the user has the right to execute the file. Even without assistance from the shell API, the “Python.File” progid could define a “run” operation as the default instead of “open”, for which the interpreter could require the user to have the right to execute scripts and modules (e.g. via the use of a
-X restrict_execution
command-line option). Model behavior on Windows would be to require execute access for modules as well as scripts, just as execute access is required for DLL shared libraries (in contrast to POSIX). It’s not like most people would notice the change. Generally inherited permissions are so lax on Windows that execute permission is always granted along with read permission, if the user has any access at all. ↩︎
Are you able to link us to any more information on Google Cloud CLI’s issues? (We’re familiar with the OS limitations you linked from our own docs, but those shouldn’t affect any single application - only multiple, distinct applications that are attempting to share private files.)
I remember the last time I tried using it with the Store version of Python (around July 2022), I had a problem with actually running the gcloud CLI. Since then, I’ve been using the version of Python installed with winget
and haven’t had any problems. So based on your comment, I have now tried to install gcloud CLI and install some components into it with the Store version of Python and it seems to be working fine. So sorry for the noise, it looks like I can switch to the Store version once again
Well, with Microsoft Store version I cannot run python scripts simply by writing their name. There is no way how to dynamically find the correct python executable, so when I got it working from normal command line, it does not work from venv
(wrong python executable is started). Reverting back to the configuration which actually works - with Python Installer + Python Launcher and updated PATHEXT.
Okay, so it’s the lack of the launcher that’s an issue. Unfortunately there’s no good way to install it via the Store, but it might be time to make it easier to install the launcher on its own (it has no trouble launching Store installs), at least for people who have admin/install permissions on their machines.
Yes, it turns out that my current problem is actually missing the launcher functionality (i.e. finding the correct version of Python to run .\script.py
, be it the one from the global installation or from venv
). I just uninstalled the “normal” Python version and kept only the launcher installed and the Store version works as intended even from the venv
environment (./script.py
is executed fine with the PATHEXT
update).
Another issue of the launcher functionality and/or Python installation (both the “normal” and Store one) is that python3
shebang (#!/usr/bin/env python3
) does not work in venv
environment - global Python environment is used during execution instead on the venv
one. The reason is that the launcher does not find python3.exe
executable in .\venv\Scripts
and selects the global python.exe
(workaround is to copy python.exe
to python3.exe
inside .\venv\Scripts\
). Strange is that the venv
environment contains pip.exe
, pip3.exe
, pip3.11.exe
, but only python.exe
and no python3.exe
nor python3.11.exe
.
pip decided to generate all the versioned shortcuts for things it creates. Python decided to only create the unversioned one for its own shortcuts. Not too surprising that different tools made different decisions.
That is fine, people make many decisions. As a result, Pip works, Python does not.