Skip to content

Commit 712114b

Browse files
[3.6] bpo-30197: Enhance functions swap_attr() and swap_item() in test.support. (GH-1341) (#1345)
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. (cherry picked from commit d1a1def)
1 parent e005dd9 commit 712114b

File tree

4 files changed

+46
-13
lines changed

4 files changed

+46
-13
lines changed

Lib/test/support/__init__.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2105,20 +2105,24 @@ def swap_attr(obj, attr, new_val):
21052105
restoring the old value at the end of the block. If `attr` doesn't
21062106
exist on `obj`, it will be created and then deleted at the end of the
21072107
block.
2108+
2109+
The old value (or None if it doesn't exist) will be assigned to the
2110+
target of the "as" clause, if there is one.
21082111
"""
21092112
if hasattr(obj, attr):
21102113
real_val = getattr(obj, attr)
21112114
setattr(obj, attr, new_val)
21122115
try:
2113-
yield
2116+
yield real_val
21142117
finally:
21152118
setattr(obj, attr, real_val)
21162119
else:
21172120
setattr(obj, attr, new_val)
21182121
try:
21192122
yield
21202123
finally:
2121-
delattr(obj, attr)
2124+
if hasattr(obj, attr):
2125+
delattr(obj, attr)
21222126

21232127
@contextlib.contextmanager
21242128
def swap_item(obj, item, new_val):
@@ -2132,20 +2136,24 @@ def swap_item(obj, item, new_val):
21322136
restoring the old value at the end of the block. If `item` doesn't
21332137
exist on `obj`, it will be created and then deleted at the end of the
21342138
block.
2139+
2140+
The old value (or None if it doesn't exist) will be assigned to the
2141+
target of the "as" clause, if there is one.
21352142
"""
21362143
if item in obj:
21372144
real_val = obj[item]
21382145
obj[item] = new_val
21392146
try:
2140-
yield
2147+
yield real_val
21412148
finally:
21422149
obj[item] = real_val
21432150
else:
21442151
obj[item] = new_val
21452152
try:
21462153
yield
21472154
finally:
2148-
del obj[item]
2155+
if item in obj:
2156+
del obj[item]
21492157

21502158
def strip_python_stderr(stderr):
21512159
"""Strip the stderr of a Python process from potential debug output

Lib/test/test_support.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -282,17 +282,34 @@ def test_python_is_optimized(self):
282282

283283
def test_swap_attr(self):
284284
class Obj:
285-
x = 1
285+
pass
286286
obj = Obj()
287-
with support.swap_attr(obj, "x", 5):
287+
obj.x = 1
288+
with support.swap_attr(obj, "x", 5) as x:
288289
self.assertEqual(obj.x, 5)
290+
self.assertEqual(x, 1)
289291
self.assertEqual(obj.x, 1)
292+
with support.swap_attr(obj, "y", 5) as y:
293+
self.assertEqual(obj.y, 5)
294+
self.assertIsNone(y)
295+
self.assertFalse(hasattr(obj, 'y'))
296+
with support.swap_attr(obj, "y", 5):
297+
del obj.y
298+
self.assertFalse(hasattr(obj, 'y'))
290299

291300
def test_swap_item(self):
292-
D = {"item":1}
293-
with support.swap_item(D, "item", 5):
294-
self.assertEqual(D["item"], 5)
295-
self.assertEqual(D["item"], 1)
301+
D = {"x":1}
302+
with support.swap_item(D, "x", 5) as x:
303+
self.assertEqual(D["x"], 5)
304+
self.assertEqual(x, 1)
305+
self.assertEqual(D["x"], 1)
306+
with support.swap_item(D, "y", 5) as y:
307+
self.assertEqual(D["y"], 5)
308+
self.assertIsNone(y)
309+
self.assertNotIn("y", D)
310+
with support.swap_item(D, "y", 5):
311+
del D["y"]
312+
self.assertNotIn("y", D)
296313

297314
class RefClass:
298315
attribute1 = None

Lib/test/test_tempfile.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,13 +273,12 @@ def raise_OSError(*args, **kwargs):
273273
tempfile._get_default_tempdir()
274274
self.assertEqual(os.listdir(our_temp_directory), [])
275275

276-
open = io.open
277276
def bad_writer(*args, **kwargs):
278-
fp = open(*args, **kwargs)
277+
fp = orig_open(*args, **kwargs)
279278
fp.write = raise_OSError
280279
return fp
281280

282-
with support.swap_attr(io, "open", bad_writer):
281+
with support.swap_attr(io, "open", bad_writer) as orig_open:
283282
# test again with failing write()
284283
with self.assertRaises(FileNotFoundError):
285284
tempfile._get_default_tempdir()

Misc/NEWS

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,15 @@ Documentation
114114

115115
- bpo-26985: Add missing info of code object in inspect documentation.
116116

117+
Tests
118+
-----
119+
120+
- bpo-30197: Enhanced functions swap_attr() and swap_item() in the
121+
test.support module. They now work when delete replaced attribute or item
122+
inside the with statement. The old value of the attribute or item (or None
123+
if it doesn't exist) now will be assigned to the target of the "as" clause,
124+
if there is one.
125+
117126

118127
What's New in Python 3.6.1?
119128
===========================

0 commit comments

Comments
 (0)