Skip to content

Commit 64aef5c

Browse files
authored
Merge pull request RustPython#4649 from dalinaum/test/test_contextlib.py
Update test/test_contextlib.py from CPython 3.11.2
2 parents 3fe5a36 + 5d6a48b commit 64aef5c

File tree

1 file changed

+233
-17
lines changed

1 file changed

+233
-17
lines changed

Lib/test/test_contextlib.py

Lines changed: 233 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
"""Unit tests for contextlib.py, and other context managers."""
22

33
import io
4+
import os
45
import sys
56
import tempfile
67
import threading
8+
import traceback
79
import unittest
810
from contextlib import * # Tests __all__
911
from test import support
@@ -86,6 +88,58 @@ def woohoo():
8688
raise ZeroDivisionError()
8789
self.assertEqual(state, [1, 42, 999])
8890

91+
# TODO: RUSTPYTHON
92+
@unittest.expectedFailure
93+
def test_contextmanager_traceback(self):
94+
@contextmanager
95+
def f():
96+
yield
97+
98+
try:
99+
with f():
100+
1/0
101+
except ZeroDivisionError as e:
102+
frames = traceback.extract_tb(e.__traceback__)
103+
104+
self.assertEqual(len(frames), 1)
105+
self.assertEqual(frames[0].name, 'test_contextmanager_traceback')
106+
self.assertEqual(frames[0].line, '1/0')
107+
108+
# Repeat with RuntimeError (which goes through a different code path)
109+
class RuntimeErrorSubclass(RuntimeError):
110+
pass
111+
112+
try:
113+
with f():
114+
raise RuntimeErrorSubclass(42)
115+
except RuntimeErrorSubclass as e:
116+
frames = traceback.extract_tb(e.__traceback__)
117+
118+
self.assertEqual(len(frames), 1)
119+
self.assertEqual(frames[0].name, 'test_contextmanager_traceback')
120+
self.assertEqual(frames[0].line, 'raise RuntimeErrorSubclass(42)')
121+
122+
class StopIterationSubclass(StopIteration):
123+
pass
124+
125+
for stop_exc in (
126+
StopIteration('spam'),
127+
StopIterationSubclass('spam'),
128+
):
129+
with self.subTest(type=type(stop_exc)):
130+
try:
131+
with f():
132+
raise stop_exc
133+
except type(stop_exc) as e:
134+
self.assertIs(e, stop_exc)
135+
frames = traceback.extract_tb(e.__traceback__)
136+
else:
137+
self.fail(f'{stop_exc} was suppressed')
138+
139+
self.assertEqual(len(frames), 1)
140+
self.assertEqual(frames[0].name, 'test_contextmanager_traceback')
141+
self.assertEqual(frames[0].line, 'raise stop_exc')
142+
89143
def test_contextmanager_no_reraise(self):
90144
@contextmanager
91145
def whee():
@@ -126,19 +180,22 @@ def woohoo():
126180
self.assertEqual(state, [1, 42, 999])
127181

128182
def test_contextmanager_except_stopiter(self):
129-
stop_exc = StopIteration('spam')
130183
@contextmanager
131184
def woohoo():
132185
yield
133-
try:
134-
with self.assertWarnsRegex(DeprecationWarning,
135-
"StopIteration"):
136-
with woohoo():
137-
raise stop_exc
138-
except Exception as ex:
139-
self.assertIs(ex, stop_exc)
140-
else:
141-
self.fail('StopIteration was suppressed')
186+
187+
class StopIterationSubclass(StopIteration):
188+
pass
189+
190+
for stop_exc in (StopIteration('spam'), StopIterationSubclass('spam')):
191+
with self.subTest(type=type(stop_exc)):
192+
try:
193+
with woohoo():
194+
raise stop_exc
195+
except Exception as ex:
196+
self.assertIs(ex, stop_exc)
197+
else:
198+
self.fail(f'{stop_exc} was suppressed')
142199

143200
# TODO: RUSTPYTHON
144201
@unittest.expectedFailure
@@ -230,6 +287,8 @@ class A:
230287
def woohoo(a, b):
231288
a = weakref.ref(a)
232289
b = weakref.ref(b)
290+
# Allow test to work with a non-refcounted GC
291+
support.gc_collect()
233292
self.assertIsNone(a())
234293
self.assertIsNone(b())
235294
yield
@@ -318,13 +377,13 @@ def testWithOpen(self):
318377
tfn = tempfile.mktemp()
319378
try:
320379
f = None
321-
with open(tfn, "w") as f:
380+
with open(tfn, "w", encoding="utf-8") as f:
322381
self.assertFalse(f.closed)
323382
f.write("Booh\n")
324383
self.assertTrue(f.closed)
325384
f = None
326385
with self.assertRaises(ZeroDivisionError):
327-
with open(tfn, "r") as f:
386+
with open(tfn, "r", encoding="utf-8") as f:
328387
self.assertFalse(f.closed)
329388
self.assertEqual(f.read(), "Booh\n")
330389
1 / 0
@@ -486,26 +545,30 @@ def method(self, a, b, c=None):
486545
self.assertEqual(test.b, 2)
487546

488547

548+
# TODO: RUSTPYTHON
549+
@unittest.expectedFailure
489550
def test_typo_enter(self):
490551
class mycontext(ContextDecorator):
491552
def __unter__(self):
492553
pass
493554
def __exit__(self, *exc):
494555
pass
495556

496-
with self.assertRaises(AttributeError):
557+
with self.assertRaisesRegex(TypeError, 'the context manager'):
497558
with mycontext():
498559
pass
499560

500561

562+
# TODO: RUSTPYTHON
563+
@unittest.expectedFailure
501564
def test_typo_exit(self):
502565
class mycontext(ContextDecorator):
503566
def __enter__(self):
504567
pass
505568
def __uxit__(self, *exc):
506569
pass
507570

508-
with self.assertRaises(AttributeError):
571+
with self.assertRaisesRegex(TypeError, 'the context manager.*__exit__'):
509572
with mycontext():
510573
pass
511574

@@ -608,9 +671,9 @@ def _exit(*args, **kwds):
608671
stack.callback(arg=1)
609672
with self.assertRaises(TypeError):
610673
self.exit_stack.callback(arg=2)
611-
with self.assertWarns(DeprecationWarning):
674+
with self.assertRaises(TypeError):
612675
stack.callback(callback=_exit, arg=3)
613-
self.assertEqual(result, [((), {'arg': 3})])
676+
self.assertEqual(result, [])
614677

615678
def test_push(self):
616679
exc_raised = ZeroDivisionError
@@ -665,6 +728,27 @@ def _exit():
665728
result.append(2)
666729
self.assertEqual(result, [1, 2, 3, 4])
667730

731+
# TODO: RUSTPYTHON
732+
@unittest.expectedFailure
733+
def test_enter_context_errors(self):
734+
class LacksEnterAndExit:
735+
pass
736+
class LacksEnter:
737+
def __exit__(self, *exc_info):
738+
pass
739+
class LacksExit:
740+
def __enter__(self):
741+
pass
742+
743+
with self.exit_stack() as stack:
744+
with self.assertRaisesRegex(TypeError, 'the context manager'):
745+
stack.enter_context(LacksEnterAndExit())
746+
with self.assertRaisesRegex(TypeError, 'the context manager'):
747+
stack.enter_context(LacksEnter())
748+
with self.assertRaisesRegex(TypeError, 'the context manager'):
749+
stack.enter_context(LacksExit())
750+
self.assertFalse(stack._exit_callbacks)
751+
668752
def test_close(self):
669753
result = []
670754
with self.exit_stack() as stack:
@@ -700,6 +784,40 @@ def test_exit_suppress(self):
700784
stack.push(lambda *exc: True)
701785
1/0
702786

787+
# TODO: RUSTPYTHON
788+
@unittest.expectedFailure
789+
def test_exit_exception_traceback(self):
790+
# This test captures the current behavior of ExitStack so that we know
791+
# if we ever unintendedly change it. It is not a statement of what the
792+
# desired behavior is (for instance, we may want to remove some of the
793+
# internal contextlib frames).
794+
795+
def raise_exc(exc):
796+
raise exc
797+
798+
try:
799+
with self.exit_stack() as stack:
800+
stack.callback(raise_exc, ValueError)
801+
1/0
802+
except ValueError as e:
803+
exc = e
804+
805+
self.assertIsInstance(exc, ValueError)
806+
ve_frames = traceback.extract_tb(exc.__traceback__)
807+
expected = \
808+
[('test_exit_exception_traceback', 'with self.exit_stack() as stack:')] + \
809+
self.callback_error_internal_frames + \
810+
[('_exit_wrapper', 'callback(*args, **kwds)'),
811+
('raise_exc', 'raise exc')]
812+
813+
self.assertEqual(
814+
[(f.name, f.line) for f in ve_frames], expected)
815+
816+
self.assertIsInstance(exc.__context__, ZeroDivisionError)
817+
zde_frames = traceback.extract_tb(exc.__context__.__traceback__)
818+
self.assertEqual([(f.name, f.line) for f in zde_frames],
819+
[('test_exit_exception_traceback', '1/0')])
820+
703821
def test_exit_exception_chaining_reference(self):
704822
# Sanity check to make sure that ExitStack chaining matches
705823
# actual nested with statements
@@ -781,6 +899,42 @@ def suppress_exc(*exc_details):
781899
self.assertIsInstance(inner_exc, ValueError)
782900
self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
783901

902+
# TODO: RUSTPYTHON
903+
@unittest.expectedFailure
904+
def test_exit_exception_explicit_none_context(self):
905+
# Ensure ExitStack chaining matches actual nested `with` statements
906+
# regarding explicit __context__ = None.
907+
908+
class MyException(Exception):
909+
pass
910+
911+
@contextmanager
912+
def my_cm():
913+
try:
914+
yield
915+
except BaseException:
916+
exc = MyException()
917+
try:
918+
raise exc
919+
finally:
920+
exc.__context__ = None
921+
922+
@contextmanager
923+
def my_cm_with_exit_stack():
924+
with self.exit_stack() as stack:
925+
stack.enter_context(my_cm())
926+
yield stack
927+
928+
for cm in (my_cm, my_cm_with_exit_stack):
929+
with self.subTest():
930+
try:
931+
with cm():
932+
raise IndexError()
933+
except MyException as exc:
934+
self.assertIsNone(exc.__context__)
935+
else:
936+
self.fail("Expected IndexError, but no exception was raised")
937+
784938
def test_exit_exception_non_suppressing(self):
785939
# http://bugs.python.org/issue19092
786940
def raise_exc(exc):
@@ -893,12 +1047,16 @@ def test_excessive_nesting(self):
8931047
for i in range(10000):
8941048
stack.callback(int)
8951049

1050+
# TODO: RUSTPYTHON
1051+
@unittest.expectedFailure
8961052
def test_instance_bypass(self):
8971053
class Example(object): pass
8981054
cm = Example()
1055+
cm.__enter__ = object()
8991056
cm.__exit__ = object()
9001057
stack = self.exit_stack()
901-
self.assertRaises(AttributeError, stack.enter_context, cm)
1058+
with self.assertRaisesRegex(TypeError, 'the context manager'):
1059+
stack.enter_context(cm)
9021060
stack.push(cm)
9031061
self.assertIs(stack._exit_callbacks[-1][1], cm)
9041062

@@ -939,6 +1097,10 @@ def first():
9391097

9401098
class TestExitStack(TestBaseExitStack, unittest.TestCase):
9411099
exit_stack = ExitStack
1100+
callback_error_internal_frames = [
1101+
('__exit__', 'raise exc_details[1]'),
1102+
('__exit__', 'if cb(*exc_details):'),
1103+
]
9421104

9431105

9441106
class TestRedirectStream:
@@ -1064,5 +1226,59 @@ def test_cm_is_reentrant(self):
10641226
1/0
10651227
self.assertTrue(outer_continued)
10661228

1229+
1230+
class TestChdir(unittest.TestCase):
1231+
def make_relative_path(self, *parts):
1232+
return os.path.join(
1233+
os.path.dirname(os.path.realpath(__file__)),
1234+
*parts,
1235+
)
1236+
1237+
# TODO: RUSTPYTHON
1238+
@unittest.expectedFailure
1239+
def test_simple(self):
1240+
old_cwd = os.getcwd()
1241+
target = self.make_relative_path('data')
1242+
self.assertNotEqual(old_cwd, target)
1243+
1244+
with chdir(target):
1245+
self.assertEqual(os.getcwd(), target)
1246+
self.assertEqual(os.getcwd(), old_cwd)
1247+
1248+
# TODO: RUSTPYTHON
1249+
@unittest.expectedFailure
1250+
def test_reentrant(self):
1251+
old_cwd = os.getcwd()
1252+
target1 = self.make_relative_path('data')
1253+
target2 = self.make_relative_path('ziptestdata')
1254+
self.assertNotIn(old_cwd, (target1, target2))
1255+
chdir1, chdir2 = chdir(target1), chdir(target2)
1256+
1257+
with chdir1:
1258+
self.assertEqual(os.getcwd(), target1)
1259+
with chdir2:
1260+
self.assertEqual(os.getcwd(), target2)
1261+
with chdir1:
1262+
self.assertEqual(os.getcwd(), target1)
1263+
self.assertEqual(os.getcwd(), target2)
1264+
self.assertEqual(os.getcwd(), target1)
1265+
self.assertEqual(os.getcwd(), old_cwd)
1266+
1267+
# TODO: RUSTPYTHON
1268+
@unittest.expectedFailure
1269+
def test_exception(self):
1270+
old_cwd = os.getcwd()
1271+
target = self.make_relative_path('data')
1272+
self.assertNotEqual(old_cwd, target)
1273+
1274+
try:
1275+
with chdir(target):
1276+
self.assertEqual(os.getcwd(), target)
1277+
raise RuntimeError("boom")
1278+
except RuntimeError as re:
1279+
self.assertEqual(str(re), "boom")
1280+
self.assertEqual(os.getcwd(), old_cwd)
1281+
1282+
10671283
if __name__ == "__main__":
10681284
unittest.main()

0 commit comments

Comments
 (0)