forked from python/cpython
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pythongh-90329: Add _winapi.GetLongPathName and GetShortPathName and …
…use in venv to reduce warnings (pythonGH-117817)
- Loading branch information
Showing
6 changed files
with
494 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
# Test the Windows-only _winapi module | ||
|
||
import os | ||
import pathlib | ||
import random | ||
import re | ||
import threading | ||
import time | ||
import unittest | ||
from test.support import import_helper | ||
|
||
_winapi = import_helper.import_module('_winapi', required_on=['win']) | ||
|
||
MAXIMUM_WAIT_OBJECTS = 64 | ||
MAXIMUM_BATCHED_WAIT_OBJECTS = (MAXIMUM_WAIT_OBJECTS - 1) ** 2 | ||
|
||
class WinAPIBatchedWaitForMultipleObjectsTests(unittest.TestCase): | ||
def _events_waitall_test(self, n): | ||
evts = [_winapi.CreateEventW(0, False, False, None) for _ in range(n)] | ||
|
||
with self.assertRaises(TimeoutError): | ||
_winapi.BatchedWaitForMultipleObjects(evts, True, 100) | ||
|
||
# Ensure no errors raised when all are triggered | ||
for e in evts: | ||
_winapi.SetEvent(e) | ||
try: | ||
_winapi.BatchedWaitForMultipleObjects(evts, True, 100) | ||
except TimeoutError: | ||
self.fail("expected wait to complete immediately") | ||
|
||
# Choose 8 events to set, distributed throughout the list, to make sure | ||
# we don't always have them in the first chunk | ||
chosen = [i * (len(evts) // 8) for i in range(8)] | ||
|
||
# Replace events with invalid handles to make sure we fail | ||
for i in chosen: | ||
old_evt = evts[i] | ||
evts[i] = -1 | ||
with self.assertRaises(OSError): | ||
_winapi.BatchedWaitForMultipleObjects(evts, True, 100) | ||
evts[i] = old_evt | ||
|
||
|
||
def _events_waitany_test(self, n): | ||
evts = [_winapi.CreateEventW(0, False, False, None) for _ in range(n)] | ||
|
||
with self.assertRaises(TimeoutError): | ||
_winapi.BatchedWaitForMultipleObjects(evts, False, 100) | ||
|
||
# Choose 8 events to set, distributed throughout the list, to make sure | ||
# we don't always have them in the first chunk | ||
chosen = [i * (len(evts) // 8) for i in range(8)] | ||
|
||
# Trigger one by one. They are auto-reset events, so will only trigger once | ||
for i in chosen: | ||
with self.subTest(f"trigger event {i} of {len(evts)}"): | ||
_winapi.SetEvent(evts[i]) | ||
triggered = _winapi.BatchedWaitForMultipleObjects(evts, False, 10000) | ||
self.assertSetEqual(set(triggered), {i}) | ||
|
||
# Trigger all at once. This may require multiple calls | ||
for i in chosen: | ||
_winapi.SetEvent(evts[i]) | ||
triggered = set() | ||
while len(triggered) < len(chosen): | ||
triggered.update(_winapi.BatchedWaitForMultipleObjects(evts, False, 10000)) | ||
self.assertSetEqual(triggered, set(chosen)) | ||
|
||
# Replace events with invalid handles to make sure we fail | ||
for i in chosen: | ||
with self.subTest(f"corrupt event {i} of {len(evts)}"): | ||
old_evt = evts[i] | ||
evts[i] = -1 | ||
with self.assertRaises(OSError): | ||
_winapi.BatchedWaitForMultipleObjects(evts, False, 100) | ||
evts[i] = old_evt | ||
|
||
|
||
def test_few_events_waitall(self): | ||
self._events_waitall_test(16) | ||
|
||
def test_many_events_waitall(self): | ||
self._events_waitall_test(256) | ||
|
||
def test_max_events_waitall(self): | ||
self._events_waitall_test(MAXIMUM_BATCHED_WAIT_OBJECTS) | ||
|
||
|
||
def test_few_events_waitany(self): | ||
self._events_waitany_test(16) | ||
|
||
def test_many_events_waitany(self): | ||
self._events_waitany_test(256) | ||
|
||
def test_max_events_waitany(self): | ||
self._events_waitany_test(MAXIMUM_BATCHED_WAIT_OBJECTS) | ||
|
||
|
||
class WinAPITests(unittest.TestCase): | ||
def test_getlongpathname(self): | ||
testfn = pathlib.Path(os.getenv("ProgramFiles")).parents[-1] / "PROGRA~1" | ||
if not os.path.isdir(testfn): | ||
raise unittest.SkipTest("require x:\\PROGRA~1 to test") | ||
|
||
# pathlib.Path will be rejected - only str is accepted | ||
with self.assertRaises(TypeError): | ||
_winapi.GetLongPathName(testfn) | ||
|
||
actual = _winapi.GetLongPathName(os.fsdecode(testfn)) | ||
|
||
# Can't assume that PROGRA~1 expands to any particular variation, so | ||
# ensure it matches any one of them. | ||
candidates = set(testfn.parent.glob("Progra*")) | ||
self.assertIn(pathlib.Path(actual), candidates) | ||
|
||
def test_getshortpathname(self): | ||
testfn = pathlib.Path(os.getenv("ProgramFiles")) | ||
if not os.path.isdir(testfn): | ||
raise unittest.SkipTest("require '%ProgramFiles%' to test") | ||
|
||
# pathlib.Path will be rejected - only str is accepted | ||
with self.assertRaises(TypeError): | ||
_winapi.GetShortPathName(testfn) | ||
|
||
actual = _winapi.GetShortPathName(os.fsdecode(testfn)) | ||
|
||
# Should contain "PROGRA~" but we can't predict the number | ||
self.assertIsNotNone(re.match(r".\:\\PROGRA~\d", actual.upper()), actual) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
Misc/NEWS.d/next/Windows/2024-04-12-14-02-58.gh-issue-90329.YpEeaO.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Suppress the warning displayed on virtual environment creation when the | ||
requested and created paths differ only by a short (8.3 style) name. | ||
Warnings will continue to be shown if a junction or symlink in the path | ||
caused the venv to be created in a different location than originally | ||
requested. |
Oops, something went wrong.