mirror of https://github.com/python/cpython
gh-99442: Fix handling in py.exe launcher when argv[0] does not include a file extension (GH-99542)
This commit is contained in:
parent
4f5e1cb00a
commit
a220c6d1ee
|
@ -173,7 +173,7 @@ class RunPyMixin:
|
||||||
errors="ignore",
|
errors="ignore",
|
||||||
) as p:
|
) as p:
|
||||||
p.stdin.close()
|
p.stdin.close()
|
||||||
version = next(p.stdout).splitlines()[0].rpartition(" ")[2]
|
version = next(p.stdout, "\n").splitlines()[0].rpartition(" ")[2]
|
||||||
p.stdout.read()
|
p.stdout.read()
|
||||||
p.wait(10)
|
p.wait(10)
|
||||||
if not sys.version.startswith(version):
|
if not sys.version.startswith(version):
|
||||||
|
@ -467,6 +467,15 @@ class TestLauncher(unittest.TestCase, RunPyMixin):
|
||||||
self.assertEqual("3.100-arm64", data["SearchInfo.tag"])
|
self.assertEqual("3.100-arm64", data["SearchInfo.tag"])
|
||||||
self.assertEqual("X.Y-arm64.exe -X fake_arg_for_test -arg", data["stdout"].strip())
|
self.assertEqual("X.Y-arm64.exe -X fake_arg_for_test -arg", data["stdout"].strip())
|
||||||
|
|
||||||
|
def test_py_default_short_argv0(self):
|
||||||
|
with self.py_ini(TEST_PY_COMMANDS):
|
||||||
|
for argv0 in ['"py.exe"', 'py.exe', '"py"', 'py']:
|
||||||
|
with self.subTest(argv0):
|
||||||
|
data = self.run_py(["--version"], argv=f'{argv0} --version')
|
||||||
|
self.assertEqual("PythonTestSuite", data["SearchInfo.company"])
|
||||||
|
self.assertEqual("3.100", data["SearchInfo.tag"])
|
||||||
|
self.assertEqual(f'X.Y.exe --version', data["stdout"].strip())
|
||||||
|
|
||||||
def test_py_default_in_list(self):
|
def test_py_default_in_list(self):
|
||||||
data = self.run_py(["-0"], env=TEST_PY_ENV)
|
data = self.run_py(["-0"], env=TEST_PY_ENV)
|
||||||
default = None
|
default = None
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix handling in :ref:`launcher` when ``argv[0]`` does not include a file
|
||||||
|
extension.
|
|
@ -491,62 +491,39 @@ dumpSearchInfo(SearchInfo *search)
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
findArgumentLength(const wchar_t *buffer, int bufferLength)
|
findArgv0Length(const wchar_t *buffer, int bufferLength)
|
||||||
{
|
{
|
||||||
if (bufferLength < 0) {
|
// Note: this implements semantics that are only valid for argv0.
|
||||||
bufferLength = (int)wcsnlen_s(buffer, MAXLEN);
|
// Specifically, there is no escaping of quotes, and quotes within
|
||||||
}
|
// the argument have no effect. A quoted argv0 must start and end
|
||||||
if (bufferLength == 0) {
|
// with a double quote character; otherwise, it ends at the first
|
||||||
return 0;
|
// ' ' or '\t'.
|
||||||
}
|
int quoted = buffer[0] == L'"';
|
||||||
const wchar_t *end;
|
for (int i = 1; bufferLength < 0 || i < bufferLength; ++i) {
|
||||||
int i;
|
switch (buffer[i]) {
|
||||||
|
case L'\0':
|
||||||
if (buffer[0] != L'"') {
|
return i;
|
||||||
end = wcschr(buffer, L' ');
|
case L' ':
|
||||||
if (!end) {
|
case L'\t':
|
||||||
return bufferLength;
|
if (!quoted) {
|
||||||
}
|
return i;
|
||||||
i = (int)(end - buffer);
|
|
||||||
return i < bufferLength ? i : bufferLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while (i < bufferLength) {
|
|
||||||
end = wcschr(&buffer[i + 1], L'"');
|
|
||||||
if (!end) {
|
|
||||||
return bufferLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
i = (int)(end - buffer);
|
|
||||||
if (i >= bufferLength) {
|
|
||||||
return bufferLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
int j = i;
|
|
||||||
while (j > 1 && buffer[--j] == L'\\') {
|
|
||||||
if (j > 0 && buffer[--j] == L'\\') {
|
|
||||||
// Even number, so back up and keep counting
|
|
||||||
} else {
|
|
||||||
// Odd number, so it's escaped and we want to keep searching
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
case L'"':
|
||||||
// Non-escaped quote with space after it - end of the argument!
|
if (quoted) {
|
||||||
if (i + 1 >= bufferLength || isspace(buffer[i + 1])) {
|
return i + 1;
|
||||||
return i + 1;
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bufferLength;
|
return bufferLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const wchar_t *
|
const wchar_t *
|
||||||
findArgumentEnd(const wchar_t *buffer, int bufferLength)
|
findArgv0End(const wchar_t *buffer, int bufferLength)
|
||||||
{
|
{
|
||||||
return &buffer[findArgumentLength(buffer, bufferLength)];
|
return &buffer[findArgv0Length(buffer, bufferLength)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -562,11 +539,16 @@ parseCommandLine(SearchInfo *search)
|
||||||
return RC_NO_COMMANDLINE;
|
return RC_NO_COMMANDLINE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *tail = findArgumentEnd(search->originalCmdLine, -1);
|
const wchar_t *argv0End = findArgv0End(search->originalCmdLine, -1);
|
||||||
const wchar_t *end = tail;
|
const wchar_t *tail = argv0End; // will be start of the executable name
|
||||||
search->restOfCmdLine = tail;
|
const wchar_t *end = argv0End; // will be end of the executable name
|
||||||
|
search->restOfCmdLine = argv0End; // will be first space after argv0
|
||||||
while (--tail != search->originalCmdLine) {
|
while (--tail != search->originalCmdLine) {
|
||||||
if (*tail == L'.' && end == search->restOfCmdLine) {
|
if (*tail == L'"' && end == argv0End) {
|
||||||
|
// Move the "end" up to the quote, so we also allow moving for
|
||||||
|
// a period later on.
|
||||||
|
end = argv0End = tail;
|
||||||
|
} else if (*tail == L'.' && end == argv0End) {
|
||||||
end = tail;
|
end = tail;
|
||||||
} else if (*tail == L'\\' || *tail == L'/') {
|
} else if (*tail == L'\\' || *tail == L'/') {
|
||||||
++tail;
|
++tail;
|
||||||
|
|
Loading…
Reference in New Issue