1+ from  __future__ import  annotations 
2+ 
13import  getopt 
24import  os 
35import  re 
6+ import  shutil 
47import  sys 
58import  traceback 
69import  unittest 
7- 
8- try :
9-     this_file  =  __file__ 
10- except  NameError :
11-     this_file  =  sys .argv [0 ]
12- 
13- win32com_src_dir  =  os .path .abspath (os .path .join (this_file , "../.." ))
10+ from  collections .abc  import  Callable 
11+ from  textwrap  import  dedent 
12+ from  types  import  ModuleType 
13+ from  typing  import  NoReturn 
1414
1515import  win32com 
1616
17+ this_folder  =  os .path .dirname (__file__ )
18+ win32com_src_dir  =  os .path .abspath (os .path .join (this_folder , ".." ))
1719# We'd prefer the win32com namespace to be the parent of __file__ - ie, our source-tree, 
1820# rather than the version installed - otherwise every .py change needs a full install to 
1921# test! 
2022# We can't patch win32comext as most of them have a .pyd in their root :( 
21- # This clearly ins 't ideal or perfect :) 
23+ # This clearly isn 't ideal or perfect :) 
2224win32com .__path__ [0 ] =  win32com_src_dir 
2325
2426import  pythoncom 
25- import  win32com .client 
27+ import  win32com .client . gencache 
2628from  pywin32_testutil  import  TestLoader , TestRunner 
2729from  win32com .test .util  import  (
2830    CapturingFunctionTestCase ,
3537verbosity  =  1   # default unittest verbosity. 
3638
3739
38- def  GenerateAndRunOldStyle ():
39-     from  . import  GenTestScripts 
40- 
41-     GenTestScripts .GenerateAll ()
42-     try :
43-         pass   # 
44-     finally :
45-         GenTestScripts .CleanAll ()
46- 
47- 
48- def  CleanGenerated ():
49-     import  shutil 
50- 
51-     import  win32com 
52- 
40+ def  CleanGenerated () ->  None :
5341    if  os .path .isdir (win32com .__gen_path__ ):
5442        if  verbosity  >  1 :
55-             print ("Deleting files from %s"    %  ( win32com .__gen_path__ ) )
43+             print (f "Deleting files from { win32com .__gen_path__ } "  )
5644        shutil .rmtree (win32com .__gen_path__ )
57-     import  win32com .client .gencache 
5845
5946    win32com .client .gencache .__init__ ()  # Reset 
6047
6148
62- def  RemoveRefCountOutput (data ) :
63-     while  1 :
49+ def  RemoveRefCountOutput (data :  str )  ->   str :
50+     while  True :
6451        last_line_pos  =  data .rfind ("\n " )
6552        if  not  re .match (r"\[\d+ refs\]" , data [last_line_pos  +  1  :]):
6653            break 
@@ -72,47 +59,45 @@ def RemoveRefCountOutput(data):
7259    return  data 
7360
7461
75- def  ExecuteSilentlyIfOK (cmd , testcase ) :
62+ def  ExecuteSilentlyIfOK (cmd :  str , testcase :  TestCase )  ->   str :
7663    f  =  os .popen (cmd )
7764    data  =  f .read ().strip ()
7865    rc  =  f .close ()
7966    if  rc :
8067        print (data )
81-         testcase .fail ("Executing '%s ' failed (%d)"    %  ( cmd ,  rc ) )
68+         testcase .fail (f "Executing '{ cmd }  ' failed ({ rc } )"  )
8269    # for "_d" builds, strip the '[xxx refs]' line 
8370    return  RemoveRefCountOutput (data )
8471
8572
8673class  PyCOMTest (TestCase ):
8774    no_leak_tests  =  True   # done by the test itself 
8875
89-     def  testit (self ):
76+     def  testit (self )  ->   None :
9077        # Check that the item is registered, so we get the correct 
9178        # 'skipped' behaviour (and recorded as such) rather than either 
9279        # error or silence due to non-registration. 
9380        RegisterPythonServer (
94-             os .path .join (
95-                 os .path .dirname (__file__ ), ".." , "servers" , "test_pycomtest.py" 
96-             ),
81+             os .path .join (win32com_src_dir , "servers" , "test_pycomtest.py" ),
9782            "Python.Test.PyCOMTest" ,
9883        )
9984
10085        # Execute testPyComTest in its own process so it can play 
10186        # with the Python thread state 
102-         fname  =  os .path .join (os . path . dirname ( this_file ) , "testPyComTest.py" )
87+         fname  =  os .path .join (this_folder , "testPyComTest.py" )
10388        cmd  =  f'{ sys .executable }   "{ fname }  " -q 2>&1' 
10489        data  =  ExecuteSilentlyIfOK (cmd , self )
10590
10691
10792class  PippoTest (TestCase ):
108-     def  testit (self ):
93+     def  testit (self )  ->   None :
10994        # Check we are registered before spawning the process. 
11095        from  win32com .test  import  pippo_server 
11196
11297        RegisterPythonServer (pippo_server .__file__ , "Python.Test.Pippo" )
11398
11499        python  =  sys .executable 
115-         fname  =  os .path .join (os . path . dirname ( this_file ) , "testPippo.py" )
100+         fname  =  os .path .join (this_folder , "testPippo.py" )
116101        cmd  =  f'{ python }   "{ fname }  " 2>&1' 
117102        ExecuteSilentlyIfOK (cmd , self )
118103
@@ -121,35 +106,44 @@ def testit(self):
121106# function in that module if the module isn't unitest based... 
122107unittest_modules  =  [
123108    # Level 1 tests - fast and few dependencies - good for CI! 
124-     """testIterators testvbscript_regexp testStorage 
125-           testStreams testWMI policySemantics testShell testROT 
126-           testxslt testCollections 
127-           errorSemantics.test testArrays 
128-           testClipboard 
129-           testConversionErrors 
130-         """ .split (),
109+     [
110+         "testIterators" ,
111+         "testvbscript_regexp" ,
112+         "testStorage" ,
113+         "testStreams" ,
114+         "testWMI" ,
115+         "policySemantics" ,
116+         "testShell" ,
117+         "testROT" ,
118+         "testxslt" ,
119+         "testCollections" ,
120+         "errorSemantics.test" ,
121+         "testArrays" ,
122+         "testClipboard" ,
123+         "testConversionErrors" ,
124+     ],
131125    # Level 2 tests - wants our demo COM objects registered. 
132126    # (these are strange; on github CI they get further than expected when 
133127    # our objects are not installed, so fail to quietly fail with "can't 
134128    # register" like they do locally. So really just a nod to CI) 
135-     """ 
136-         testAXScript testDictionary testServers testvb testMarshal 
137-         """ .split (),
129+     ["testAXScript" , "testDictionary" , "testServers" , "testvb" , "testMarshal" ],
138130    # Level 3 tests - Requires Office or other non-free stuff. 
139-     """testMSOffice.TestAll testMSOfficeEvents.test testAccess.test 
140-            testExplorer.TestAll testExchange.test 
141-         """ .split (),
131+     [
132+         "testMSOffice.TestAll" ,
133+         "testMSOfficeEvents.test" ,
134+         "testAccess.test" ,
135+         "testExplorer.TestAll" ,
136+         "testExchange.test" ,
137+     ],
142138    # Level 4 tests - we try and run `makepy` over every typelib installed! 
143-     """testmakepy.TestAll 
144-         """ .split (),
139+     ["testmakepy.TestAll" ],
145140]
146141
147142# A list of other unittest modules we use - these are fully qualified module 
148143# names and the module is assumed to be unittest based. 
149144unittest_other_modules  =  [
150145    # Level 1 tests. 
151-     """win32com.directsound.test.ds_test 
152-         """ .split (),
146+     ["win32com.directsound.test.ds_test" ],
153147    # Level 2 tests. 
154148    [],
155149    # Level 3 tests. 
@@ -192,7 +186,9 @@ def testit(self):
192186]
193187
194188
195- def  get_test_mod_and_func (test_name , import_failures ):
189+ def  get_test_mod_and_func (
190+     test_name : str , import_failures : list [tuple [str , BaseException ]]
191+ ) ->  tuple [None , None ] |  tuple [ModuleType , Callable [[], object ] |  None ]:
196192    if  test_name .find ("." ) >  0 :
197193        mod_name , func_name  =  test_name .split ("." )
198194    else :
@@ -202,19 +198,21 @@ def get_test_mod_and_func(test_name, import_failures):
202198    try :
203199        __import__ (fq_mod_name )
204200        mod  =  sys .modules [fq_mod_name ]
205-     except :
206-         import_failures .append ((mod_name , sys . exc_info ()[: 2 ] ))
201+     except   Exception   as   error :
202+         import_failures .append ((mod_name , error ))
207203        return  None , None 
208204    func  =  None  if  func_name  is  None  else  getattr (mod , func_name )
209205    return  mod , func 
210206
211207
212208# Return a test suite all loaded with the tests we want to run 
213- def  make_test_suite (test_level = 1 ):
209+ def  make_test_suite (
210+     test_level : int ,
211+ ) ->  tuple [unittest .TestSuite , list [tuple [str , BaseException ]]]:
214212    suite  =  unittest .TestSuite ()
215-     import_failures  =  []
213+     import_failures :  list [ tuple [ str ,  BaseException ]]  =  []
216214    loader  =  TestLoader ()
217-     for  i  in  range (testLevel ):
215+     for  i  in  range (test_level ):
218216        for  mod_name  in  unittest_modules [i ]:
219217            mod , func  =  get_test_mod_and_func (mod_name , import_failures )
220218            if  mod  is  None :
@@ -234,12 +232,12 @@ def make_test_suite(test_level=1):
234232        for  test_class  in  custom_test_cases [i ]:
235233            suite .addTest (unittest .defaultTestLoader .loadTestsFromTestCase (test_class ))
236234    # other "normal" unittest modules. 
237-     for  i  in  range (testLevel ):
235+     for  i  in  range (test_level ):
238236        for  mod_name  in  unittest_other_modules [i ]:
239237            try :
240238                __import__ (mod_name )
241-             except :
242-                 import_failures .append ((mod_name , sys . exc_info ()[: 2 ] ))
239+             except   Exception   as   error :
240+                 import_failures .append ((mod_name , error ))
243241                continue 
244242
245243            mod  =  sys .modules [mod_name ]
@@ -253,13 +251,15 @@ def make_test_suite(test_level=1):
253251    return  suite , import_failures 
254252
255253
256- def  usage (why ):
257-     print (why )
258-     print ()
259-     print ("win32com test suite" )
260-     print ("usage: testall [-v] test_level" )
261-     print ("  where test_level is an integer 1-3.  Level 1 tests are quick," )
262-     print ("  level 2 tests invoke Word, IE etc, level 3 take ages!" )
254+ def  usage (why : str ) ->  NoReturn :
255+     print (f"""\  
256+ { why } 
257+ 
258+ win32com test suite 
259+ usage: testall [-v] test_level 
260+   where test_level is an integer 1-4.  Level 1 tests are quick, 
261+   level 2 tests invoke Word, IE etc, level 3 take ages! 
262+   level 4 we try and run `makepy` over every typelib installed""" )
263263    sys .exit (1 )
264264
265265
@@ -271,44 +271,47 @@ def usage(why):
271271    for  opt , val  in  opts :
272272        if  opt  ==  "-v" :
273273            verbosity  +=  1 
274-     testLevel  =  2   # default to quick test with local objects 
275-     test_names  =  []
276-     for  arg  in  args :
277-         try :
278-             testLevel  =  int (arg )
279-             if  testLevel  <  0  or  testLevel  >  4 :
280-                 raise  ValueError ("Only levels 1-4 are supported" )
281-         except  ValueError :
282-             test_names .append (arg )
283-     if  test_names :
274+ 
275+     if  len (args ) >  1 :
276+         usage (
277+             "Only a single test level argument is supported. " 
278+             +  "Test names are not supported yet" 
279+         )
280+     try :
281+         test_level  =  int (args [0 ])
282+     except  IndexError :
283+         test_level  =  2   # default to quick test with local objects 
284+     except  ValueError :
284285        usage ("Test names are not supported yet" )
286+     if  test_level  <  1  or  test_level  >  4 :
287+         usage ("Only levels 1-4 are supported" )
285288    CleanGenerated ()
286289
287-     suite , import_failures  =  make_test_suite (testLevel )
290+     suite , import_failures  =  make_test_suite (test_level )
288291    if  verbosity :
289292        if  hasattr (sys , "gettotalrefcount" ):
290-             print ("This is a debug build - memory leak tests will also be run." )
291-             print ("These tests may take *many* minutes to run - be patient!" )
292-             print ("(running from python.exe will avoid these leak tests)" )
293+             print (
294+                 dedent ("""\  
295+                  This is a debug build - memory leak tests will also be run.
296+                 These tests may take *many* minutes to run - be patient! 
297+                 (running from python.exe will avoid these leak tests""" )
298+             )
293299        print (
294-             "Executing level %d  tests - %d test cases will be run " 
295-             %  ( testLevel ,  suite .countTestCases ()) 
300+             f "Executing level { test_level }   tests "
301+             +   f"-  { suite .countTestCases ()}  test cases will be run" 
296302        )
297303        if  verbosity  ==  1  and  suite .countTestCases () <  70 :
298304            # A little row of markers so the dots show how close to finished 
299305            print ("|"  *  suite .countTestCases ())
300-     testRunner  =  TestRunner (verbosity = verbosity )
301-     testResult  =  testRunner .run (suite )
306+     testResult  =  TestRunner (verbosity = verbosity ).run (suite )
302307    if  import_failures :
303308        testResult .stream .writeln (
304309            "*** The following test modules could not be imported ***" 
305310        )
306-         for  mod_name , ( exc_type ,  exc_val )  in  import_failures :
307-             desc  =  "\n " .join (traceback .format_exception_only (exc_type ,  exc_val ))
311+         for  mod_name , error  in  import_failures :
312+             desc  =  "\n " .join (traceback .format_exception_only (type ( error ),  error ))
308313            testResult .stream .write (f"{ mod_name }  : { desc }  " )
309-         testResult .stream .writeln (
310-             "*** %d test(s) could not be run ***"  %  len (import_failures )
311-         )
314+         testResult .stream .writeln (f"*** { import_failures }   test(s) could not be run ***" )
312315
313316    # re-print unit-test error here so it is noticed 
314317    if  not  testResult .wasSuccessful ():
0 commit comments