Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite98fa71

Browse files
gh-101467: Correct py.exe handling of prefix matches and cases when only one runtime is installed (GH-101468)
(cherry picked from commiteda6091)Co-authored-by: Steve Dower <steve.dower@python.org>
1 parent89442e1 commite98fa71

File tree

4 files changed

+114
-18
lines changed

4 files changed

+114
-18
lines changed

‎Doc/using/windows.rst‎

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -730,22 +730,47 @@ command::
730730

731731
py -2
732732

733-
You should find the latest version of Python 3.x starts.
734-
735733
If you see the following error, you do not have the launcher installed::
736734

737735
'py' is not recognized as an internal or external command,
738736
operable program or batch file.
739737

740-
Per-user installations of Python do not add the launcher to:envvar:`PATH`
741-
unless the option was selected on installation.
742-
743738
The command::
744739

745740
py --list
746741

747742
displays the currently installed version(s) of Python.
748743

744+
The ``-x.y`` argument is the short form of the ``-V:Company/Tag`` argument,
745+
which allows selecting a specific Python runtime, including those that may have
746+
come from somewhere other than python.org. Any runtime registered by following
747+
:pep:`514` will be discoverable. The ``--list`` command lists all available
748+
runtimes using the ``-V:`` format.
749+
750+
When using the ``-V:`` argument, specifying the Company will limit selection to
751+
runtimes from that provider, while specifying only the Tag will select from all
752+
providers. Note that omitting the slash implies a tag::
753+
754+
# Select any '3.*' tagged runtime
755+
py -V:3
756+
757+
# Select any 'PythonCore' released runtime
758+
py -V:PythonCore/
759+
760+
# Select PythonCore's latest Python 3 runtime
761+
py -V:PythonCore/3
762+
763+
The short form of the argument (``-3``) only ever selects from core Python
764+
releases, and not other distributions. However, the longer form (``-V:3``) will
765+
select from any.
766+
767+
The Company is matched on the full string, case-insenitive. The Tag is matched
768+
oneither the full string, or a prefix, provided the next character is a dot or a
769+
hyphen. This allows ``-V:3.1`` to match ``3.1-32``, but not ``3.10``. Tags are
770+
sorted using numerical ordering (``3.10`` is newer than ``3.1``), but are
771+
compared using text (``-V:3.01`` does not match ``3.1``).
772+
773+
749774
Virtual environments
750775
^^^^^^^^^^^^^^^^^^^^
751776

‎Lib/test/test_launcher.py‎

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,17 @@
5757
None:sys.prefix,
5858
}
5959
},
60-
}
60+
},
61+
"PythonTestSuite1": {
62+
"DisplayName":"Python Test Suite Single",
63+
"3.100": {
64+
"DisplayName":"Single Interpreter",
65+
"InstallPath": {
66+
None:sys.prefix,
67+
"ExecutablePath":sys.executable,
68+
}
69+
}
70+
},
6171
}
6272

6373

@@ -207,6 +217,7 @@ def run_py(self, args, env=None, allow_fail=False, expect_returncode=0, argv=Non
207217
**{k.upper():vfork,vinos.environ.items()ifk.upper()notinignore},
208218
"PYLAUNCHER_DEBUG":"1",
209219
"PYLAUNCHER_DRYRUN":"1",
220+
"PYLAUNCHER_LIMIT_TO_COMPANY":"",
210221
**{k.upper():vfork,vin (envor {}).items()},
211222
}
212223
ifnotargv:
@@ -389,23 +400,33 @@ def test_filter_to_tag(self):
389400
self.assertEqual(company,data["env.company"])
390401
self.assertEqual("3.100",data["env.tag"])
391402

392-
data=self.run_py([f"-V:3.100-3"])
403+
data=self.run_py([f"-V:3.100-32"])
393404
self.assertEqual("X.Y-32.exe",data["LaunchCommand"])
394405
self.assertEqual(company,data["env.company"])
395406
self.assertEqual("3.100-32",data["env.tag"])
396407

397-
data=self.run_py([f"-V:3.100-a"])
408+
data=self.run_py([f"-V:3.100-arm64"])
398409
self.assertEqual("X.Y-arm64.exe -X fake_arg_for_test",data["LaunchCommand"])
399410
self.assertEqual(company,data["env.company"])
400411
self.assertEqual("3.100-arm64",data["env.tag"])
401412

402413
deftest_filter_to_company_and_tag(self):
403414
company="PythonTestSuite"
404-
data=self.run_py([f"-V:{company}/3.1"])
415+
data=self.run_py([f"-V:{company}/3.1"],expect_returncode=103)
416+
417+
data=self.run_py([f"-V:{company}/3.100"])
405418
self.assertEqual("X.Y.exe",data["LaunchCommand"])
406419
self.assertEqual(company,data["env.company"])
407420
self.assertEqual("3.100",data["env.tag"])
408421

422+
deftest_filter_with_single_install(self):
423+
company="PythonTestSuite1"
424+
data=self.run_py(
425+
[f"-V:Nonexistent"],
426+
env={"PYLAUNCHER_LIMIT_TO_COMPANY":company},
427+
expect_returncode=103,
428+
)
429+
409430
deftest_search_major_3(self):
410431
try:
411432
data=self.run_py(["-3"],allow_fail=True)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The ``py.exe`` launcher now correctly filters when only a single runtime is
2+
installed. It also correctly handles prefix matches on tags so that ``-3.1``
3+
does not match ``3.11``, but would still match ``3.1-32``.

‎PC/launcher2.c‎

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,30 @@ _startsWithArgument(const wchar_t *x, int xLen, const wchar_t *y, int yLen)
295295
}
296296

297297

298+
// Unlike regular startsWith, this function requires that the following
299+
// character is either NULL (that is, the entire string matches) or is one of
300+
// the characters in 'separators'.
301+
bool
302+
_startsWithSeparated(constwchar_t*x,intxLen,constwchar_t*y,intyLen,constwchar_t*separators)
303+
{
304+
if (!x|| !y) {
305+
return false;
306+
}
307+
yLen=yLen<0 ? (int)wcsnlen_s(y,MAXLEN) :yLen;
308+
xLen=xLen<0 ? (int)wcsnlen_s(x,MAXLEN) :xLen;
309+
if (xLen<yLen) {
310+
return false;
311+
}
312+
if (xLen==yLen) {
313+
return0==_compare(x,xLen,y,yLen);
314+
}
315+
returnseparators&&
316+
0==_compare(x,yLen,y,yLen)&&
317+
wcschr(separators,x[yLen])!=NULL;
318+
}
319+
320+
321+
298322
/******************************************************************************\
299323
*** HELP TEXT ***
300324
\******************************************************************************/
@@ -409,6 +433,9 @@ typedef struct {
409433
boollistPaths;
410434
// if true, display help message before contiuning
411435
boolhelp;
436+
// if set, limits search to registry keys with the specified Company
437+
// This is intended for debugging and testing only
438+
constwchar_t*limitToCompany;
412439
// dynamically allocated buffers to free later
413440
struct_SearchInfoBuffer*_buffer;
414441
}SearchInfo;
@@ -485,6 +512,7 @@ dumpSearchInfo(SearchInfo *search)
485512
DEBUG_BOOL(list);
486513
DEBUG_BOOL(listPaths);
487514
DEBUG_BOOL(help);
515+
DEBUG(limitToCompany);
488516
#undef DEBUG_BOOL
489517
#undef DEBUG_2
490518
#undef DEBUG
@@ -1602,6 +1630,10 @@ registrySearch(const SearchInfo *search, EnvironmentInfo **result, HKEY root, in
16021630
}
16031631
break;
16041632
}
1633+
if (search->limitToCompany&&0!=_compare(search->limitToCompany,-1,buffer,cchBuffer)) {
1634+
debug(L"# Skipping %s due to PYLAUNCHER_LIMIT_TO_COMPANY\n",buffer);
1635+
continue;
1636+
}
16051637
HKEYsubkey;
16061638
if (ERROR_SUCCESS==RegOpenKeyExW(root,buffer,0,KEY_READ,&subkey)) {
16071639
exitCode=_registrySearchTags(search,result,subkey,sortKey,buffer,fallbackArch);
@@ -1880,6 +1912,11 @@ collectEnvironments(const SearchInfo *search, EnvironmentInfo **result)
18801912
}
18811913
}
18821914

1915+
if (search->limitToCompany) {
1916+
debug(L"# Skipping APPX search due to PYLAUNCHER_LIMIT_TO_COMPANY\n");
1917+
return0;
1918+
}
1919+
18831920
for (structAppxSearchInfo*info=APPX_SEARCH;info->familyName;++info) {
18841921
exitCode=appxSearch(search,result,info->familyName,info->tag,info->sortKey);
18851922
if (exitCode&&exitCode!=RC_NO_PYTHON) {
@@ -2049,12 +2086,15 @@ _companyMatches(const SearchInfo *search, const EnvironmentInfo *env)
20492086

20502087

20512088
bool
2052-
_tagMatches(constSearchInfo*search,constEnvironmentInfo*env)
2089+
_tagMatches(constSearchInfo*search,constEnvironmentInfo*env,intsearchTagLength)
20532090
{
2054-
if (!search->tag|| !search->tagLength) {
2091+
if (searchTagLength<0) {
2092+
searchTagLength=search->tagLength;
2093+
}
2094+
if (!search->tag|| !searchTagLength) {
20552095
return true;
20562096
}
2057-
return_startsWith(env->tag,-1,search->tag,search->tagLength);
2097+
return_startsWithSeparated(env->tag,-1,search->tag,searchTagLength,L".-");
20582098
}
20592099

20602100

@@ -2091,7 +2131,7 @@ _selectEnvironment(const SearchInfo *search, EnvironmentInfo *env, EnvironmentIn
20912131
}
20922132

20932133
if (!search->oldStyleTag) {
2094-
if (_companyMatches(search,env)&&_tagMatches(search,env)) {
2134+
if (_companyMatches(search,env)&&_tagMatches(search,env,-1)) {
20952135
// Because of how our sort tree is set up, we will walk up the
20962136
// "prev" side and implicitly select the "best" best. By
20972137
// returning straight after a match, we skip the entire "next"
@@ -2116,7 +2156,7 @@ _selectEnvironment(const SearchInfo *search, EnvironmentInfo *env, EnvironmentIn
21162156
}
21172157
}
21182158

2119-
if (_startsWith(env->tag,-1,search->tag,tagLength)) {
2159+
if (_tagMatches(search,env,tagLength)) {
21202160
if (exclude32Bit&&_is32Bit(env)) {
21212161
debug(L"# Excluding %s/%s because it looks like 32bit\n",env->company,env->tag);
21222162
}elseif (only32Bit&& !_is32Bit(env)) {
@@ -2143,10 +2183,6 @@ selectEnvironment(const SearchInfo *search, EnvironmentInfo *root, EnvironmentIn
21432183
*best=NULL;
21442184
returnRC_NO_PYTHON_AT_ALL;
21452185
}
2146-
if (!root->next&& !root->prev) {
2147-
*best=root;
2148-
return0;
2149-
}
21502186

21512187
EnvironmentInfo*result=NULL;
21522188
intexitCode=_selectEnvironment(search,root,&result);
@@ -2556,6 +2592,17 @@ process(int argc, wchar_t ** argv)
25562592
debug(L"argv0: %s\nversion: %S\n",argv[0],PY_VERSION);
25572593
}
25582594

2595+
DWORDlen=GetEnvironmentVariableW(L"PYLAUNCHER_LIMIT_TO_COMPANY",NULL,0);
2596+
if (len>1) {
2597+
wchar_t*limitToCompany=allocSearchInfoBuffer(&search,len);
2598+
search.limitToCompany=limitToCompany;
2599+
if (0==GetEnvironmentVariableW(L"PYLAUNCHER_LIMIT_TO_COMPANY",limitToCompany,len)) {
2600+
exitCode=RC_INTERNAL_ERROR;
2601+
winerror(0,L"Failed to read PYLAUNCHER_LIMIT_TO_COMPANY variable");
2602+
gotoabort;
2603+
}
2604+
}
2605+
25592606
search.originalCmdLine=GetCommandLineW();
25602607

25612608
exitCode=performSearch(&search,&envs);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp