-
-
Notifications
You must be signed in to change notification settings - Fork 31.1k
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
On Windows, shutil.which incorrectly resolves to non-executable extensionless files (python >= 3.12.0b1) #109590
Comments
More research as to the history of the change: The original name, without added extension from PATHEXT, was added to the search list in #103179 to address #75586:
But, later in #75586, there is this:
Assuming that is true, then the original name should only be prepended to the search list if it has an extension. Extensionless names should not be considered when searching for an executable. |
@eryksun can you give some thoughts here? I'm a bit nervous about this type of case hitting others in 3.12 and wondering your thoughts. We could add logic to
I think this gets it in-line with what CMD would do, but I'm not 100% on it. |
Some testing with CMD:
So i guess if you give a directory component, it still consults PATHEXT first. |
An extensionless file will on be attempted if '.' is in PATHEXT Fix up the tests to make better use of the bytes testing of shutil.which
An extensionless file will on be attempted if '.' is in PATHEXT Fix up the tests to make better use of the bytes testing of shutil.which
I implemented the '.' in pathext to check in this draft pr: #109797 I cleared C:\Program Files\nodejs\npm to be an empty file. If it was called it shouldn't really do anything.. but it kept hitting npm.cmd instead.
I'll wait for @eryksun 's thoughts. |
One last thing before bed :) Maybe it does work but only if the rest of PATHEXT can't resolve something first (as in: it ignores the order in PATHEXT. Even if the dot is first, it will do all the other extensions before checking for an extensionless file). I renamed npm.cmd to .npm.cmd, then:
If that's the desired behavior, it wouldn't be too hard to update the PR. |
@zooba any thoughts on this? |
This sounds reasonable at face value. CMD is not the hallmark of consistent behaviour. I wouldn't want to set it as our standard. If you want CMD behaviour, pass The closer we get to the underlying API behaviour, the happier I'll be - in this case, it's SearchPathW. |
So unfortunately (or fortunately), Via pywin32:
So it seems fine giving back a the file without an extension. So I guess we match right now. I can't seem to find any definitive documentation on the expectation of |
Okay, I have another option. When If We should document this clearly (as well as marking it as a change in 3.12.1, and probably clarifying that it's more consistent with 3.11 and earlier as a result of the change). |
How'd you come up with that behavior combo? I figured out behavior should match some existing documentation or behavior but can't think of how you came up with that. :P Would it allow a direct match without an extension if a dot is in PATHEXT? Also if a dot is in there would it match a file with a dot as the last character but nothing after? |
But basically, it's a combo that works for our own venv activate scripts. All of
I don't think any of the examples above care about a dot in PATHEXT, but some of them will treat a trailing dot as a sign to ignore PATHEXT (and then the dot gets normalised away to match the dotless file). I don't think you can't create a file association for an extensionless file (only by creating an association for all files), which means it will never be executable, so I'd be inclined to not support it unless someone comes up with a legitimate use (perhaps Git Bash? I'd rather special case "bash that works on Windows" though, rather than abusing PATHEXT). |
…EXT matches by default
Having seen the bugs that are getting in vs. being deferred, I don't think we want this one in. The risk far outweighs the value right now. If we'd found it before RC1, then definitely. Right now, the important thing to do is to make sure that anyone who fixes this in 3.12.0 is still going to work in 3.12.1 (which is why we check if there's already a PATHEXT extension on the requested name). |
…EXT matches by default
…on on executable files (GH-109995) The default arguments for shutil.which() request an executable file, but extensionless files are not executable on Windows and should be ignored.
…xtension on executable files (pythonGH-109995) The default arguments for shutil.which() request an executable file, but extensionless files are not executable on Windows and should be ignored. (cherry picked from commit 29b875b) Co-authored-by: Charles Machalow <csm10495@gmail.com>
…extension on executable files (GH-109995) (#110202) gh-109590: Update shutil.which on Windows to prefer a PATHEXT extension on executable files (GH-109995) The default arguments for shutil.which() request an executable file, but extensionless files are not executable on Windows and should be ignored. (cherry picked from commit 29b875b) Co-authored-by: Charles Machalow <csm10495@gmail.com>
This works around a bug in python 3.12.0's `shutil.which` when running on Windows. See python/cpython#109590
This works around a bug in python 3.12.0's `shutil.which` when running on Windows. See python/cpython#109590
This works around a bug in python 3.12.0's `shutil.which` when running on Windows. See python/cpython#109590
This works around a bug in python 3.12.0's `shutil.which` when running on Windows. See python/cpython#109590
This works around a bug in python 3.12.0's `shutil.which` when running on Windows. See python/cpython#109590
This works around a bug in python 3.12.0's `shutil.which` when running on Windows. See python/cpython#109590
* test: test under python 3.12 * fix: use shell=True rather than shutil.which to find npm binary This works around a bug in python 3.12.0's `shutil.which` when running on Windows. See python/cpython#109590
Closing as the PRs have been merged. Thanks! |
…xtension on executable files (pythonGH-109995) The default arguments for shutil.which() request an executable file, but extensionless files are not executable on Windows and should be ignored.
Bug report
Bug description:
This change (935aa45, PR #103179) changed the behavior of
shutil.which
so that it checks for the extensionless filename first, before checking extensions from PATHEXT. Previously, the extensionless name was not checked at all.This is causing problems because, at least in my case, there is both an extensionless and extensioned version of the program, and the extensionless version is a bash script (which apparently can't be executed directly on Windows.)
Full Description
On a GitHub Windows shared runner, node.js is installed.
Here,
npm
is a bash script, whilenpm.cmd
is acmd.exe
script.In python 3.11 (and before),
shutil.which("npm")
returns"C:\\Program Files\\nodejs\\npm.CMD"
. That can be executed successfully bysubprocess.run
.In python 3.12rc3,
shutil.which("npm")
returns"C:\\Program Files\\nodejs\\npm"
, which, being a bash script, appears not to be by executable bysubprocess
.fails with
OSError: [WinError 193] %1 is not a valid Win32 application
on python 3.12rc3.Notes
On the system in question
Get-Command npm
returnsC:\Program Files\nodejs\npm.cmd
. So PowerShell gets it right.(
PATHEXT
is.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.CPL
.)I'm not absolutely convinced that this is a bug. But again, I'm also not sure how to work around it without essentially reimplementing
shutil.which
.CPython versions tested on:
3.12
Operating systems tested on:
Windows
Linked PRs
The text was updated successfully, but these errors were encountered: