Skip to content

Commit 6dd21e9

Browse files
cmaloneyvstinner
andauthored
gh-138013: Remove test_io load_tests namespace manipulation (#138366)
Reduce what happens in `load_tests` so that the next change, moving the `Buffered*` tests to `test_bufferdio` is purely mechanical movement and updating imports. This adds two classes, one per I/O implementation, to act as dispatch to the implementation-specific mocks as well as module members. Previously the mappings CTestCase and PyTestCase provide were injected directly during `load_tests`. CTestCase and PyTestCase inherit from `unittest.TestCase` so when the split happens default test discovery will work for the classes in `test_bufferedio`. `test_general` keeps a manual test list for this refactoring; some of the tests (ex. `ProtocolsTest`) aren't currently run and fixing that + helpers to not be picked up is out of my current scope. CTestCase and PyTestCase have an `io` class member which points to the implementation meaning that can be removed from individual test cases which now inherit from them. This code is picking up `MockRawIO` which is defined globally in the module but these should use the mock specific to the I/O implementation being tested. Co-authored-by: Victor Stinner <[email protected]>
1 parent 974532e commit 6dd21e9

File tree

1 file changed

+67
-52
lines changed

1 file changed

+67
-52
lines changed

Lib/test/test_io/test_general.py

Lines changed: 67 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,45 @@ class PyMockNonBlockWriterIO(MockNonBlockWriterIO, pyio.RawIOBase):
317317
BlockingIOError = pyio.BlockingIOError
318318

319319

320+
# Build classes which point to all the right mocks per io implementation
321+
class CTestCase(unittest.TestCase):
322+
io = io
323+
is_C = True
324+
325+
MockRawIO = CMockRawIO
326+
MisbehavedRawIO = CMisbehavedRawIO
327+
MockFileIO = CMockFileIO
328+
CloseFailureIO = CCloseFailureIO
329+
MockNonBlockWriterIO = CMockNonBlockWriterIO
330+
MockUnseekableIO = CMockUnseekableIO
331+
MockRawIOWithoutRead = CMockRawIOWithoutRead
332+
SlowFlushRawIO = CSlowFlushRawIO
333+
MockCharPseudoDevFileIO = CMockCharPseudoDevFileIO
334+
335+
# Use the class as a proxy to the io module members.
336+
def __getattr__(self, name):
337+
return getattr(io, name)
338+
339+
340+
class PyTestCase(unittest.TestCase):
341+
io = pyio
342+
is_C = False
343+
344+
MockRawIO = PyMockRawIO
345+
MisbehavedRawIO = PyMisbehavedRawIO
346+
MockFileIO = PyMockFileIO
347+
CloseFailureIO = PyCloseFailureIO
348+
MockNonBlockWriterIO = PyMockNonBlockWriterIO
349+
MockUnseekableIO = PyMockUnseekableIO
350+
MockRawIOWithoutRead = PyMockRawIOWithoutRead
351+
SlowFlushRawIO = PySlowFlushRawIO
352+
MockCharPseudoDevFileIO = PyMockCharPseudoDevFileIO
353+
354+
# Use the class as a proxy to the _pyio module members.
355+
def __getattr__(self, name):
356+
return getattr(pyio, name)
357+
358+
320359
class IOTest(unittest.TestCase):
321360

322361
def setUp(self):
@@ -1083,7 +1122,7 @@ def reader(file, barrier):
10831122
write_count * thread_count)
10841123

10851124

1086-
class CIOTest(IOTest):
1125+
class CIOTest(IOTest, CTestCase):
10871126

10881127
def test_IOBase_finalize(self):
10891128
# Issue #12149: segmentation fault on _PyIOBase_finalize when both a
@@ -1207,7 +1246,7 @@ def test_stringio_setstate(self):
12071246
obj.__setstate__(('', '', 0, {}))
12081247
self.assertEqual(obj.getvalue(), '')
12091248

1210-
class PyIOTest(IOTest):
1249+
class PyIOTest(IOTest, PyTestCase):
12111250
pass
12121251

12131252

@@ -1438,7 +1477,7 @@ def test_buffer_freeing(self) :
14381477
bufio.close()
14391478
self.assertEqual(sys.getsizeof(bufio), size)
14401479

1441-
class BufferedReaderTest(unittest.TestCase, CommonBufferedTests):
1480+
class BufferedReaderTest(CommonBufferedTests):
14421481
read_mode = "rb"
14431482

14441483
def test_constructor(self):
@@ -1773,7 +1812,7 @@ def test_seek_character_device_file(self):
17731812
self.assertEqual(buf.seek(0, io.SEEK_CUR), 0)
17741813

17751814

1776-
class CBufferedReaderTest(BufferedReaderTest, SizeofTest):
1815+
class CBufferedReaderTest(BufferedReaderTest, SizeofTest, CTestCase):
17771816
tp = io.BufferedReader
17781817

17791818
def test_initialization(self):
@@ -1832,11 +1871,11 @@ def test_bad_readinto_type(self):
18321871
self.assertIsInstance(cm.exception.__cause__, TypeError)
18331872

18341873

1835-
class PyBufferedReaderTest(BufferedReaderTest):
1874+
class PyBufferedReaderTest(BufferedReaderTest, PyTestCase):
18361875
tp = pyio.BufferedReader
18371876

18381877

1839-
class BufferedWriterTest(unittest.TestCase, CommonBufferedTests):
1878+
class BufferedWriterTest(CommonBufferedTests):
18401879
write_mode = "wb"
18411880

18421881
def test_constructor(self):
@@ -2131,8 +2170,7 @@ def test_slow_close_from_thread(self):
21312170
t.join()
21322171

21332172

2134-
2135-
class CBufferedWriterTest(BufferedWriterTest, SizeofTest):
2173+
class CBufferedWriterTest(BufferedWriterTest, SizeofTest, CTestCase):
21362174
tp = io.BufferedWriter
21372175

21382176
def test_initialization(self):
@@ -2172,10 +2210,10 @@ def test_args_error(self):
21722210
self.tp(self.BytesIO(), 1024, 1024, 1024)
21732211

21742212

2175-
class PyBufferedWriterTest(BufferedWriterTest):
2213+
class PyBufferedWriterTest(BufferedWriterTest, PyTestCase):
21762214
tp = pyio.BufferedWriter
21772215

2178-
class BufferedRWPairTest(unittest.TestCase):
2216+
class BufferedRWPairTest:
21792217

21802218
def test_constructor(self):
21812219
pair = self.tp(self.MockRawIO(), self.MockRawIO())
@@ -2204,14 +2242,14 @@ def test_constructor_max_buffer_size_removal(self):
22042242
self.tp(self.MockRawIO(), self.MockRawIO(), 8, 12)
22052243

22062244
def test_constructor_with_not_readable(self):
2207-
class NotReadable(MockRawIO):
2245+
class NotReadable(self.MockRawIO):
22082246
def readable(self):
22092247
return False
22102248

22112249
self.assertRaises(OSError, self.tp, NotReadable(), self.MockRawIO())
22122250

22132251
def test_constructor_with_not_writeable(self):
2214-
class NotWriteable(MockRawIO):
2252+
class NotWriteable(self.MockRawIO):
22152253
def writable(self):
22162254
return False
22172255

@@ -2357,9 +2395,9 @@ def writer_close():
23572395
writer.close = lambda: None
23582396

23592397
def test_isatty(self):
2360-
class SelectableIsAtty(MockRawIO):
2398+
class SelectableIsAtty(self.MockRawIO):
23612399
def __init__(self, isatty):
2362-
MockRawIO.__init__(self)
2400+
super().__init__()
23632401
self._isatty = isatty
23642402

23652403
def isatty(self):
@@ -2383,10 +2421,10 @@ def test_weakref_clearing(self):
23832421
brw = None
23842422
ref = None # Shouldn't segfault.
23852423

2386-
class CBufferedRWPairTest(BufferedRWPairTest):
2424+
class CBufferedRWPairTest(BufferedRWPairTest, CTestCase):
23872425
tp = io.BufferedRWPair
23882426

2389-
class PyBufferedRWPairTest(BufferedRWPairTest):
2427+
class PyBufferedRWPairTest(BufferedRWPairTest, PyTestCase):
23902428
tp = pyio.BufferedRWPair
23912429

23922430

@@ -2645,7 +2683,7 @@ def test_interleaved_readline_write(self):
26452683
test_truncate_on_read_only = None
26462684

26472685

2648-
class CBufferedRandomTest(BufferedRandomTest, SizeofTest):
2686+
class CBufferedRandomTest(BufferedRandomTest, SizeofTest, CTestCase):
26492687
tp = io.BufferedRandom
26502688

26512689
def test_garbage_collection(self):
@@ -2658,7 +2696,7 @@ def test_args_error(self):
26582696
self.tp(self.BytesIO(), 1024, 1024, 1024)
26592697

26602698

2661-
class PyBufferedRandomTest(BufferedRandomTest):
2699+
class PyBufferedRandomTest(BufferedRandomTest, PyTestCase):
26622700
tp = pyio.BufferedRandom
26632701

26642702

@@ -4058,8 +4096,7 @@ def _to_memoryview(buf):
40584096
return memoryview(arr)
40594097

40604098

4061-
class CTextIOWrapperTest(TextIOWrapperTest):
4062-
io = io
4099+
class CTextIOWrapperTest(TextIOWrapperTest, CTestCase):
40634100
shutdown_error = "LookupError: unknown encoding: ascii"
40644101

40654102
def test_initialization(self):
@@ -4159,8 +4196,7 @@ def write(self, data):
41594196
buf._write_stack)
41604197

41614198

4162-
class PyTextIOWrapperTest(TextIOWrapperTest):
4163-
io = pyio
4199+
class PyTextIOWrapperTest(TextIOWrapperTest, PyTestCase):
41644200
shutdown_error = "LookupError: unknown encoding: ascii"
41654201

41664202

@@ -4282,6 +4318,8 @@ def test_translate(self):
42824318
self.assertEqual(decoder.decode(b"\r\r\n"), "\r\r\n")
42834319

42844320
class CIncrementalNewlineDecoderTest(IncrementalNewlineDecoderTest):
4321+
IncrementalNewlineDecoder = io.IncrementalNewlineDecoder
4322+
42854323
@support.cpython_only
42864324
def test_uninitialized(self):
42874325
uninitialized = self.IncrementalNewlineDecoder.__new__(
@@ -4293,7 +4331,7 @@ def test_uninitialized(self):
42934331

42944332

42954333
class PyIncrementalNewlineDecoderTest(IncrementalNewlineDecoderTest):
4296-
pass
4334+
IncrementalNewlineDecoder = pyio.IncrementalNewlineDecoder
42974335

42984336

42994337
# XXX Tests for open()
@@ -4662,8 +4700,7 @@ def test_text_encoding(self):
46624700
self.assertEqual(b"utf-8", proc.out.strip())
46634701

46644702

4665-
class CMiscIOTest(MiscIOTest):
4666-
io = io
4703+
class CMiscIOTest(MiscIOTest, CTestCase):
46674704
name_of_module = "io", "_io"
46684705
extra_exported = "BlockingIOError",
46694706

@@ -4728,8 +4765,7 @@ def test_daemon_threads_shutdown_stderr_deadlock(self):
47284765
self.check_daemon_threads_shutdown_deadlock('stderr')
47294766

47304767

4731-
class PyMiscIOTest(MiscIOTest):
4732-
io = pyio
4768+
class PyMiscIOTest(MiscIOTest, PyTestCase):
47334769
name_of_module = "_pyio", "io"
47344770
extra_exported = "BlockingIOError", "open_code",
47354771
not_exported = "valid_seek_flags",
@@ -4990,11 +5026,11 @@ def test_interrupted_write_retry_text(self):
49905026
self.check_interrupted_write_retry("x", mode="w", encoding="latin1")
49915027

49925028

4993-
class CSignalsTest(SignalsTest):
4994-
io = io
5029+
class CSignalsTest(SignalsTest, CTestCase):
5030+
pass
49955031

4996-
class PySignalsTest(SignalsTest):
4997-
io = pyio
5032+
class PySignalsTest(SignalsTest, PyTestCase):
5033+
pass
49985034

49995035
# Handling reentrancy issues would slow down _pyio even more, so the
50005036
# tests are disabled.
@@ -5034,27 +5070,6 @@ def load_tests(loader, tests, pattern):
50345070
ProtocolsTest,
50355071
)
50365072

5037-
# Put the namespaces of the IO module we are testing and some useful mock
5038-
# classes in the __dict__ of each test.
5039-
mocks = (MockRawIO, MisbehavedRawIO, MockFileIO, CloseFailureIO,
5040-
MockNonBlockWriterIO, MockUnseekableIO, MockRawIOWithoutRead,
5041-
SlowFlushRawIO, MockCharPseudoDevFileIO)
5042-
all_members = io.__all__
5043-
c_io_ns = {name : getattr(io, name) for name in all_members}
5044-
py_io_ns = {name : getattr(pyio, name) for name in all_members}
5045-
globs = globals()
5046-
c_io_ns.update((x.__name__, globs["C" + x.__name__]) for x in mocks)
5047-
py_io_ns.update((x.__name__, globs["Py" + x.__name__]) for x in mocks)
5048-
for test in tests:
5049-
if test.__name__.startswith("C"):
5050-
for name, obj in c_io_ns.items():
5051-
setattr(test, name, obj)
5052-
test.is_C = True
5053-
elif test.__name__.startswith("Py"):
5054-
for name, obj in py_io_ns.items():
5055-
setattr(test, name, obj)
5056-
test.is_C = False
5057-
50585073
suite = loader.suiteClass()
50595074
for test in tests:
50605075
suite.addTest(loader.loadTestsFromTestCase(test))

0 commit comments

Comments
 (0)