1+ import contextlib
2+ import logging
13import errno
24import importlib
35import io
2426TESTFN = os_helper .TESTFN
2527
2628
29+ class LogCaptureHandler (logging .StreamHandler ):
30+ """
31+ A logging handler that stores log records and the log text.
32+
33+ derrived from pytest caplog
34+
35+ The MIT License (MIT)
36+
37+ Copyright (c) 2004 Holger Krekel and others
38+
39+ Permission is hereby granted, free of charge, to any person obtaining a copy of
40+ this software and associated documentation files (the "Software"), to deal in
41+ the Software without restriction, including without limitation the rights to
42+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
43+ of the Software, and to permit persons to whom the Software is furnished to do
44+ so, subject to the following conditions:
45+
46+ The above copyright notice and this permission notice shall be included in all
47+ copies or substantial portions of the Software.
48+
49+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
50+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
51+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
52+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
53+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
54+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
55+ SOFTWARE.
56+ """
57+
58+ def __init__ (self ) -> None :
59+ """Create a new log handler."""
60+ super ().__init__ (io .StringIO ())
61+ self .records = []
62+
63+ def emit (self , record : logging .LogRecord ) -> None :
64+ """Keep the log records in a list in addition to the log text."""
65+ self .records .append (record )
66+ super ().emit (record )
67+
68+ def reset (self ):
69+ self .records = []
70+ self .stream = io .StringIO ()
71+
72+ def clear (self ):
73+ self .records .clear ()
74+ self .stream = io .StringIO ()
75+
76+ def handleError (self , record ):
77+ if logging .raiseExceptions :
78+ # Fail the test if the log message is bad (emit failed).
79+ # The default behavior of logging is to print "Logging error"
80+ # to stderr with the call stack and some extra details.
81+ # pytest wants to make such mistakes visible during testing.
82+ raise
83+
84+
85+ @contextlib .contextmanager
86+ def _caplog ():
87+ handler = LogCaptureHandler ()
88+ root_logger = logging .getLogger ()
89+ root_logger .addHandler (handler )
90+ try :
91+ yield handler
92+ finally :
93+ root_logger .removeHandler (handler )
94+
95+
2796class TestSupport (unittest .TestCase ):
2897 @classmethod
2998 def setUpClass (cls ):
@@ -187,7 +256,7 @@ def test_temp_dir__existing_dir__quiet_true(self):
187256 path = os .path .realpath (path )
188257
189258 try :
190- with warnings_helper .check_warnings () as recorder :
259+ with warnings_helper .check_warnings () as recorder , _caplog () as caplog :
191260 with os_helper .temp_dir (path , quiet = True ) as temp_path :
192261 self .assertEqual (path , temp_path )
193262 warnings = [str (w .message ) for w in recorder .warnings ]
@@ -196,11 +265,14 @@ def test_temp_dir__existing_dir__quiet_true(self):
196265 finally :
197266 shutil .rmtree (path )
198267
199- self .assertEqual (len (warnings ), 1 , warnings )
200- warn = warnings [0 ]
201- self .assertTrue (warn .startswith (f'tests may fail, unable to create '
202- f'temporary directory { path !r} : ' ),
203- warn )
268+ self .assertListEqual (warnings , [])
269+ self .assertEqual (len (caplog .records ), 1 )
270+ record , = caplog .records
271+ self .assertStartsWith (
272+ record .getMessage (),
273+ f'tests may fail, unable to create '
274+ f'temporary directory { path !r} : '
275+ )
204276
205277 @support .requires_fork ()
206278 def test_temp_dir__forked_child (self ):
@@ -260,35 +332,41 @@ def test_change_cwd__non_existent_dir__quiet_true(self):
260332
261333 with os_helper .temp_dir () as parent_dir :
262334 bad_dir = os .path .join (parent_dir , 'does_not_exist' )
263- with warnings_helper .check_warnings () as recorder :
335+ with warnings_helper .check_warnings () as recorder , _caplog () as caplog :
264336 with os_helper .change_cwd (bad_dir , quiet = True ) as new_cwd :
265337 self .assertEqual (new_cwd , original_cwd )
266338 self .assertEqual (os .getcwd (), new_cwd )
267339 warnings = [str (w .message ) for w in recorder .warnings ]
268340
269- self .assertEqual (len (warnings ), 1 , warnings )
270- warn = warnings [0 ]
271- self .assertTrue (warn .startswith (f'tests may fail, unable to change '
272- f'the current working directory '
273- f'to { bad_dir !r} : ' ),
274- warn )
341+ self .assertListEqual (warnings , [])
342+ self .assertEqual (len (caplog .records ), 1 )
343+ record , = caplog .records
344+ self .assertStartsWith (
345+ record .getMessage (),
346+ f'tests may fail, unable to change '
347+ f'the current working directory '
348+ f'to { bad_dir !r} : '
349+ )
275350
276351 # Tests for change_cwd()
277352
278353 def test_change_cwd__chdir_warning (self ):
279354 """Check the warning message when os.chdir() fails."""
280355 path = TESTFN + '_does_not_exist'
281- with warnings_helper .check_warnings () as recorder :
356+ with warnings_helper .check_warnings () as recorder , _caplog () as caplog :
282357 with os_helper .change_cwd (path = path , quiet = True ):
283358 pass
284359 messages = [str (w .message ) for w in recorder .warnings ]
285360
286- self .assertEqual (len (messages ), 1 , messages )
287- msg = messages [0 ]
288- self .assertTrue (msg .startswith (f'tests may fail, unable to change '
289- f'the current working directory '
290- f'to { path !r} : ' ),
291- msg )
361+ self .assertListEqual (messages , [])
362+ self .assertEqual (len (caplog .records ), 1 )
363+ record , = caplog .records
364+ self .assertStartsWith (
365+ record .getMessage (),
366+ f'tests may fail, unable to change '
367+ f'the current working directory '
368+ f'to { path !r} : ' ,
369+ )
292370
293371 # Tests for temp_cwd()
294372
0 commit comments