1- # Utilities for the pywin32 tests
1+ """Utilities for the pywin32 tests"""
2+
3+ from __future__ import annotations
4+
25import gc
36import os
47import site
58import sys
69import unittest
10+ from typing import TYPE_CHECKING
711
12+ import pythoncom
13+ import pywintypes
814import winerror
15+ from win32com .shell .shell import IsUserAnAdmin
16+
17+ if TYPE_CHECKING :
18+ from _typeshed import OptExcInfo
919
1020##
1121## unittest related stuff
@@ -145,20 +155,17 @@ def loadTestsFromName(self, name, module=None):
145155
146156# win32 error codes that probably mean we need to be elevated (ie, if we
147157# aren't elevated, we treat these error codes as 'skipped')
148- non_admin_error_codes = [
158+ non_admin_error_codes = {
149159 winerror .ERROR_ACCESS_DENIED ,
150160 winerror .ERROR_PRIVILEGE_NOT_HELD ,
151- ]
161+ }
152162
153163_is_admin = None
154164
155165
156166def check_is_admin ():
157167 global _is_admin
158168 if _is_admin is None :
159- import pythoncom
160- from win32com .shell .shell import IsUserAnAdmin
161-
162169 try :
163170 _is_admin = IsUserAnAdmin ()
164171 except pythoncom .com_error as exc :
@@ -197,30 +204,19 @@ def find_test_fixture(basename, extra_dir="."):
197204 d = os .path .normcase (d )
198205 if os .path .commonprefix ([this_file , d ]) == d :
199206 # looks like we are in an installed Python, so skip the text.
200- raise TestSkipped (f"Can't find test fixture '{ fname } '" )
207+ raise unittest . SkipTest (f"Can't find test fixture '{ fname } '" )
201208 # Looks like we are running from source, so this is fatal.
202209 raise RuntimeError (f"Can't find test fixture '{ fname } '" )
203210
204211
205- # If this exception is raised by a test, the test is reported as a 'skip'
206- class TestSkipped (Exception ):
207- pass
208-
209-
210212# The 'TestResult' subclass that records the failures and has the special
211- # handling for the TestSkipped exception.
213+ # handling for the unittest.SkipTest exception.
212214class TestResult (unittest .TextTestResult ):
213- def __init__ (self , * args , ** kw ):
214- super ().__init__ (* args , ** kw )
215- self .skips = {} # count of skips for each reason.
215+ def addError (self , test : unittest .TestCase , err : OptExcInfo ) -> None :
216+ """Called when an error has occurred.
216217
217- def addError (self , test , err ):
218- """Called when an error has occurred. 'err' is a tuple of values as
219- returned by sys.exc_info().
218+ Translate a couple of 'well-known' exceptions into 'skipped'
220219 """
221- # translate a couple of 'well-known' exceptions into 'skipped'
222- import pywintypes
223-
224220 exc_val = err [1 ]
225221 # translate ERROR_ACCESS_DENIED for non-admin users to be skipped.
226222 # (access denied errors for an admin user aren't expected.)
@@ -229,42 +225,28 @@ def addError(self, test, err):
229225 and exc_val .winerror in non_admin_error_codes
230226 and not check_is_admin ()
231227 ):
232- exc_val = TestSkipped (exc_val )
228+ return self . addSkip ( test , str (exc_val ) )
233229 # and COM errors due to objects not being registered (the com test
234230 # suite will attempt to catch this and handle it itself if the user
235231 # is admin)
236- elif isinstance (exc_val , pywintypes .com_error ) and exc_val .hresult in [
232+ elif isinstance (exc_val , pywintypes .com_error ) and exc_val .hresult in {
237233 winerror .CO_E_CLASSSTRING ,
238234 winerror .REGDB_E_CLASSNOTREG ,
239235 winerror .TYPE_E_LIBNOTREGISTERED ,
240- ]:
241- exc_val = TestSkipped (exc_val )
242- # NotImplemented generally means the platform doesn't support the
243- # functionality.
236+ }:
237+ return self .addSkip (test , str (exc_val ))
238+ # NotImplemented generally means the platform doesn't support the functionality.
244239 elif isinstance (exc_val , NotImplementedError ):
245- exc_val = TestSkipped (NotImplementedError )
246-
247- if isinstance (exc_val , TestSkipped ):
248- reason = exc_val .args [0 ]
249- # if the reason itself is another exception, get its args.
250- try :
251- reason = tuple (reason .args )
252- except (AttributeError , TypeError ):
253- pass
254- self .skips .setdefault (reason , 0 )
255- self .skips [reason ] += 1
256- if self .showAll :
257- self .stream .writeln (f"SKIP ({ reason } )" )
258- elif self .dots :
259- self .stream .write ("S" )
260- self .stream .flush ()
261- return
240+ return self .addSkip (test , str (exc_val ))
241+
262242 super ().addError (test , err )
263243
264- def printErrors (self ):
244+ def printErrors (self ) -> None :
265245 super ().printErrors ()
266- for reason , num_skipped in self .skips .items ():
267- self .stream .writeln ("SKIPPED: %d tests - %s" % (num_skipped , reason ))
246+ reasons = [reason for (test , reason ) in self .skipped ]
247+ reason_counts = [(reason , reasons .count (reason )) for reason in set (reasons )]
248+ for reason , num_skipped in reason_counts :
249+ self .stream .writeln (f"SKIPPED: { num_skipped } tests - { reason } " )
268250
269251
270252# TestRunner subclass necessary just to get our TestResult hooked up.
0 commit comments