2727import unittest
2828import weakref
2929import abc
30+ import signal
31+ import errno
3032from itertools import cycle , count
3133from collections import deque
3234from test import support
@@ -2463,6 +2465,75 @@ class CMiscIOTest(MiscIOTest):
24632465class PyMiscIOTest (MiscIOTest ):
24642466 io = pyio
24652467
2468+
2469+ @unittest .skipIf (os .name == 'nt' , 'POSIX signals required for this test.' )
2470+ class SignalsTest (unittest .TestCase ):
2471+
2472+ def setUp (self ):
2473+ self .oldalrm = signal .signal (signal .SIGALRM , self .alarm_interrupt )
2474+
2475+ def tearDown (self ):
2476+ signal .signal (signal .SIGALRM , self .oldalrm )
2477+
2478+ def alarm_interrupt (self , sig , frame ):
2479+ 1 / 0
2480+
2481+ @unittest .skipUnless (threading , 'Threading required for this test.' )
2482+ def check_interrupted_write (self , item , bytes , ** fdopen_kwargs ):
2483+ """Check that a partial write, when it gets interrupted, properly
2484+ invokes the signal handler."""
2485+ read_results = []
2486+ def _read ():
2487+ s = os .read (r , 1 )
2488+ read_results .append (s )
2489+ t = threading .Thread (target = _read )
2490+ t .daemon = True
2491+ r , w = os .pipe ()
2492+ try :
2493+ wio = self .io .open (w , ** fdopen_kwargs )
2494+ t .start ()
2495+ signal .alarm (1 )
2496+ # Fill the pipe enough that the write will be blocking.
2497+ # It will be interrupted by the timer armed above. Since the
2498+ # other thread has read one byte, the low-level write will
2499+ # return with a successful (partial) result rather than an EINTR.
2500+ # The buffered IO layer must check for pending signal
2501+ # handlers, which in this case will invoke alarm_interrupt().
2502+ self .assertRaises (ZeroDivisionError ,
2503+ wio .write , item * (1024 * 1024 ))
2504+ t .join ()
2505+ # We got one byte, get another one and check that it isn't a
2506+ # repeat of the first one.
2507+ read_results .append (os .read (r , 1 ))
2508+ self .assertEqual (read_results , [bytes [0 :1 ], bytes [1 :2 ]])
2509+ finally :
2510+ os .close (w )
2511+ os .close (r )
2512+ # This is deliberate. If we didn't close the file descriptor
2513+ # before closing wio, wio would try to flush its internal
2514+ # buffer, and block again.
2515+ try :
2516+ wio .close ()
2517+ except IOError as e :
2518+ if e .errno != errno .EBADF :
2519+ raise
2520+
2521+ def test_interrupted_write_unbuffered (self ):
2522+ self .check_interrupted_write (b"xy" , b"xy" , mode = "wb" , buffering = 0 )
2523+
2524+ def test_interrupted_write_buffered (self ):
2525+ self .check_interrupted_write (b"xy" , b"xy" , mode = "wb" )
2526+
2527+ def test_interrupted_write_text (self ):
2528+ self .check_interrupted_write ("xy" , b"xy" , mode = "w" , encoding = "ascii" )
2529+
2530+ class CSignalsTest (SignalsTest ):
2531+ io = io
2532+
2533+ class PySignalsTest (SignalsTest ):
2534+ io = pyio
2535+
2536+
24662537def test_main ():
24672538 tests = (CIOTest , PyIOTest ,
24682539 CBufferedReaderTest , PyBufferedReaderTest ,
@@ -2472,7 +2543,9 @@ def test_main():
24722543 StatefulIncrementalDecoderTest ,
24732544 CIncrementalNewlineDecoderTest , PyIncrementalNewlineDecoderTest ,
24742545 CTextIOWrapperTest , PyTextIOWrapperTest ,
2475- CMiscIOTest , PyMiscIOTest ,)
2546+ CMiscIOTest , PyMiscIOTest ,
2547+ CSignalsTest , PySignalsTest ,
2548+ )
24762549
24772550 # Put the namespaces of the IO module we are testing and some useful mock
24782551 # classes in the __dict__ of each test.
0 commit comments