Shutil.which will a full path but without an extension on Windows

I was playing with some python code I have that uses shutil.which(cmd[0]) to try and figure out if the the first argument of a given command would be executable from the current directory.

I passed in the full path of an app (or so I thought) to the code and it didn’t work. I thought about it a bit more and realized I was passing something like: C:\program instead of C:\program.exe which was making shutil.which(cmd[0]) returned None.

I don’t think this behavior matches up at least with my expectations. I had expected shutil.which to test with appending PATHEXT extensions to return a pseudo-matching executable.

For fun, I tested the same idea with a build of GNU which for Windows and it matches my expectations:

c:\>which --version
GNU which v2.20, Copyright (C) 1999 - 2008 Carlo Wood.
GNU which comes with ABSOLUTELY NO WARRANTY;
This program is free software; your freedom to use, change
and distribute this program is protected by the GPL.

c:\>which cmd
C:\WINDOWS\system32\cmd.EXE

c:\>which C:\WINDOWS\system32\cmd
C:\WINDOWS\system32\cmd.EXE

c:\>which C:\WINDOWS\system32\cmd.EXE
C:\WINDOWS\system32\cmd.EXE

While Python doesn’t work with passing the non-exe’d full path:

Python 3.11.0 (main, Oct 24 2022, 18:26:48) [MSC v.1933 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import shutil
>>> shutil.which('cmd')
'C:\\WINDOWS\\system32\\cmd.EXE'
>>> shutil.which(r'C:\WINDOWS\system32\cmd')
>>> shutil.which(r'C:\WINDOWS\system32\cmd.EXE')
'C:\\WINDOWS\\system32\\cmd.EXE'
>>>

In both cases, my PATHEXT was:

PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW

I’d like to put in a vote that we match the behavior of GNU which here, which would have made it so:

>>> shutil.which(r'C:\WINDOWS\system32\cmd')

would have returned

'C:\\WINDOWS\\system32\\cmd.EXE'

Any thoughts or objections? (If no objections, I could even try to PR it :slight_smile: ). Thanks folks!

For previous discussions and proposals to improve shutil.which() on Windows, refer to the open issues gh-68693 (2015) and gh-75586 (2017).

Ack. Looks like this has come up before.

I put in a PR: GH-75586 - Fix case where PATHEXT isn't applied to items in PATH (Win… by csm10495 · Pull Request #103179 · python/cpython · GitHub

Linked it back to: shutil.which doesn't find files without PATHEXT extension on Windows · Issue #75586 · python/cpython · GitHub