Commit d1a1def7 authored by Serhiy Storchaka's avatar Serhiy Storchaka Committed by Victor Stinner

bpo-30197: Enhance functions swap_attr() and swap_item() in test.support. (#1341)

* bpo-30197: Enhance functions swap_attr() and swap_item() in test.support.

They now work when delete replaced attribute or item inside the with
statement.  The old value of the attribute or item (or None if it doesn't
exist) now will be assigned to the target of the "as" clause, if there is
one.

* Update docstrings.
parent 80a3da4d
...@@ -2119,12 +2119,15 @@ def swap_attr(obj, attr, new_val): ...@@ -2119,12 +2119,15 @@ def swap_attr(obj, attr, new_val):
restoring the old value at the end of the block. If `attr` doesn't restoring the old value at the end of the block. If `attr` doesn't
exist on `obj`, it will be created and then deleted at the end of the exist on `obj`, it will be created and then deleted at the end of the
block. block.
The old value (or None if it doesn't exist) will be assigned to the
target of the "as" clause, if there is one.
""" """
if hasattr(obj, attr): if hasattr(obj, attr):
real_val = getattr(obj, attr) real_val = getattr(obj, attr)
setattr(obj, attr, new_val) setattr(obj, attr, new_val)
try: try:
yield yield real_val
finally: finally:
setattr(obj, attr, real_val) setattr(obj, attr, real_val)
else: else:
...@@ -2132,7 +2135,8 @@ def swap_attr(obj, attr, new_val): ...@@ -2132,7 +2135,8 @@ def swap_attr(obj, attr, new_val):
try: try:
yield yield
finally: finally:
delattr(obj, attr) if hasattr(obj, attr):
delattr(obj, attr)
@contextlib.contextmanager @contextlib.contextmanager
def swap_item(obj, item, new_val): def swap_item(obj, item, new_val):
...@@ -2146,12 +2150,15 @@ def swap_item(obj, item, new_val): ...@@ -2146,12 +2150,15 @@ def swap_item(obj, item, new_val):
restoring the old value at the end of the block. If `item` doesn't restoring the old value at the end of the block. If `item` doesn't
exist on `obj`, it will be created and then deleted at the end of the exist on `obj`, it will be created and then deleted at the end of the
block. block.
The old value (or None if it doesn't exist) will be assigned to the
target of the "as" clause, if there is one.
""" """
if item in obj: if item in obj:
real_val = obj[item] real_val = obj[item]
obj[item] = new_val obj[item] = new_val
try: try:
yield yield real_val
finally: finally:
obj[item] = real_val obj[item] = real_val
else: else:
...@@ -2159,7 +2166,8 @@ def swap_item(obj, item, new_val): ...@@ -2159,7 +2166,8 @@ def swap_item(obj, item, new_val):
try: try:
yield yield
finally: finally:
del obj[item] if item in obj:
del obj[item]
def strip_python_stderr(stderr): def strip_python_stderr(stderr):
"""Strip the stderr of a Python process from potential debug output """Strip the stderr of a Python process from potential debug output
......
...@@ -295,17 +295,34 @@ class TestSupport(unittest.TestCase): ...@@ -295,17 +295,34 @@ class TestSupport(unittest.TestCase):
def test_swap_attr(self): def test_swap_attr(self):
class Obj: class Obj:
x = 1 pass
obj = Obj() obj = Obj()
with support.swap_attr(obj, "x", 5): obj.x = 1
with support.swap_attr(obj, "x", 5) as x:
self.assertEqual(obj.x, 5) self.assertEqual(obj.x, 5)
self.assertEqual(x, 1)
self.assertEqual(obj.x, 1) self.assertEqual(obj.x, 1)
with support.swap_attr(obj, "y", 5) as y:
self.assertEqual(obj.y, 5)
self.assertIsNone(y)
self.assertFalse(hasattr(obj, 'y'))
with support.swap_attr(obj, "y", 5):
del obj.y
self.assertFalse(hasattr(obj, 'y'))
def test_swap_item(self): def test_swap_item(self):
D = {"item":1} D = {"x":1}
with support.swap_item(D, "item", 5): with support.swap_item(D, "x", 5) as x:
self.assertEqual(D["item"], 5) self.assertEqual(D["x"], 5)
self.assertEqual(D["item"], 1) self.assertEqual(x, 1)
self.assertEqual(D["x"], 1)
with support.swap_item(D, "y", 5) as y:
self.assertEqual(D["y"], 5)
self.assertIsNone(y)
self.assertNotIn("y", D)
with support.swap_item(D, "y", 5):
del D["y"]
self.assertNotIn("y", D)
class RefClass: class RefClass:
attribute1 = None attribute1 = None
......
...@@ -273,13 +273,12 @@ class TestGetDefaultTempdir(BaseTestCase): ...@@ -273,13 +273,12 @@ class TestGetDefaultTempdir(BaseTestCase):
tempfile._get_default_tempdir() tempfile._get_default_tempdir()
self.assertEqual(os.listdir(our_temp_directory), []) self.assertEqual(os.listdir(our_temp_directory), [])
open = io.open
def bad_writer(*args, **kwargs): def bad_writer(*args, **kwargs):
fp = open(*args, **kwargs) fp = orig_open(*args, **kwargs)
fp.write = raise_OSError fp.write = raise_OSError
return fp return fp
with support.swap_attr(io, "open", bad_writer): with support.swap_attr(io, "open", bad_writer) as orig_open:
# test again with failing write() # test again with failing write()
with self.assertRaises(FileNotFoundError): with self.assertRaises(FileNotFoundError):
tempfile._get_default_tempdir() tempfile._get_default_tempdir()
......
...@@ -1107,6 +1107,12 @@ Tools/Demos ...@@ -1107,6 +1107,12 @@ Tools/Demos
Tests Tests
----- -----
- bpo-30197: Enhanced functions swap_attr() and swap_item() in the
test.support module. They now work when delete replaced attribute or item
inside the with statement. The old value of the attribute or item (or None
if it doesn't exist) now will be assigned to the target of the "as" clause,
if there is one.
- Issue #24932: Use proper command line parsing in _testembed - Issue #24932: Use proper command line parsing in _testembed
- Issue #28950: Disallow -j0 to be combined with -T/-l in regrtest - Issue #28950: Disallow -j0 to be combined with -T/-l in regrtest
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment