|
13 | 13 | import warnings |
14 | 14 |
|
15 | 15 | from test import support |
| 16 | +if support.MS_WINDOWS: |
| 17 | + import _winapi |
16 | 18 |
|
17 | 19 |
|
18 | 20 | # Filename used for testing |
@@ -854,36 +856,72 @@ def __new__(mcls, name, bases, dct, *, source_date_epoch): |
854 | 856 |
|
855 | 857 | try: |
856 | 858 | if support.MS_WINDOWS: |
857 | | - import ctypes |
| 859 | + import ctypes.util |
858 | 860 | kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) |
859 | | - |
860 | | - ERROR_FILE_NOT_FOUND = 2 |
861 | | - DDD_REMOVE_DEFINITION = 2 |
862 | | - DDD_EXACT_MATCH_ON_REMOVE = 4 |
863 | | - DDD_NO_BROADCAST_SYSTEM = 8 |
864 | 861 | else: |
865 | 862 | raise AttributeError |
866 | 863 | except (ImportError, AttributeError): |
867 | 864 | def subst_drive(path): |
868 | 865 | raise unittest.SkipTest('ctypes or kernel32 is not available') |
| 866 | + |
| 867 | + def handle_count(): |
| 868 | + return 0 |
869 | 869 | else: |
| 870 | + ERROR_FILE_NOT_FOUND = 2 |
| 871 | + DDD_REMOVE_DEFINITION = 2 |
| 872 | + DDD_EXACT_MATCH_ON_REMOVE = 4 |
| 873 | + DDD_NO_BROADCAST_SYSTEM = 8 |
| 874 | + |
| 875 | + @ctypes.util.wrap_dll_function(kernel32) |
| 876 | + def DefineDosDeviceW( |
| 877 | + dwFlags: ctypes.wintypes.DWORD, |
| 878 | + lpDeviceName: ctypes.c_wchar_p, |
| 879 | + lpTargetPath: ctypes.c_wchar_p, |
| 880 | + ) -> ctypes.wintypes.BOOL: |
| 881 | + pass |
| 882 | + |
| 883 | + @ctypes.util.wrap_dll_function(kernel32) |
| 884 | + def QueryDosDeviceW( |
| 885 | + lpDeviceName: ctypes.c_wchar_p, |
| 886 | + lpTargetPath: ctypes.c_wchar_p, |
| 887 | + ucchMax: ctypes.wintypes.DWORD, |
| 888 | + ) -> ctypes.wintypes.DWORD: |
| 889 | + pass |
| 890 | + |
870 | 891 | @contextlib.contextmanager |
871 | 892 | def subst_drive(path): |
872 | 893 | """Temporarily yield a substitute drive for a given path.""" |
873 | 894 | for c in reversed(string.ascii_uppercase): |
874 | 895 | drive = f'{c}:' |
875 | | - if (not kernel32.QueryDosDeviceW(drive, None, 0) and |
| 896 | + if (not QueryDosDeviceW(drive, None, 0) and |
876 | 897 | ctypes.get_last_error() == ERROR_FILE_NOT_FOUND): |
877 | 898 | break |
878 | 899 | else: |
879 | 900 | raise unittest.SkipTest('no available logical drive') |
880 | | - if not kernel32.DefineDosDeviceW( |
881 | | - DDD_NO_BROADCAST_SYSTEM, drive, path): |
| 901 | + |
| 902 | + if not DefineDosDeviceW(DDD_NO_BROADCAST_SYSTEM, drive, path): |
882 | 903 | raise ctypes.WinError(ctypes.get_last_error()) |
| 904 | + |
883 | 905 | try: |
884 | 906 | yield drive |
885 | 907 | finally: |
886 | | - if not kernel32.DefineDosDeviceW( |
887 | | - DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE, |
888 | | - drive, path): |
| 908 | + flags = DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE |
| 909 | + if not DefineDosDeviceW(flags, drive, path): |
889 | 910 | raise ctypes.WinError(ctypes.get_last_error()) |
| 911 | + |
| 912 | + @ctypes.util.wrap_dll_function(kernel32) |
| 913 | + def GetProcessHandleCount(khProcess: ctypes.wintypes.HANDLE, |
| 914 | + pdwHandleCount: ctypes.wintypes.LPDWORD) -> ctypes.wintypes.BOOL: |
| 915 | + pass |
| 916 | + |
| 917 | + del kernel32 |
| 918 | + |
| 919 | + def handle_count(): |
| 920 | + # Pseudo-handle that doesn't need to be closed |
| 921 | + hproc = _winapi.GetCurrentProcess() |
| 922 | + |
| 923 | + handle_count = ctypes.wintypes.DWORD() |
| 924 | + if not GetProcessHandleCount(hproc, ctypes.byref(handle_count)): |
| 925 | + raise ctypes.WinError(ctypes.get_last_error()) |
| 926 | + |
| 927 | + return handle_count.value |
0 commit comments