| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent df8d2cd commit 96d37db
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,6 +8,7 @@ | |||
| 8 | 8 | import copy | |
| 9 | 9 | import threading | |
| 10 | 10 | import time | |
| 11 | + import random | ||
| 11 | 12 | ||
| 12 | 13 | from test import support | |
| 13 | 14 | from test.support import script_helper | |
@@ -1688,6 +1689,87 @@ def test_threaded_weak_valued_consistency(self): | |||
| 1688 | 1689 | self.assertEqual(len(d), 1) | |
| 1689 | 1690 | o = None # lose ref | |
| 1690 | 1691 | ||
| 1692 | + def check_threaded_weak_dict_copy(self, type_, deepcopy): | ||
| 1693 | + # `type_` should be either WeakKeyDictionary or WeakValueDictionary. | ||
| 1694 | + # `deepcopy` should be either True or False. | ||
| 1695 | + exc = [] | ||
| 1696 | + | ||
| 1697 | + class DummyKey: | ||
| 1698 | + def __init__(self, ctr): | ||
| 1699 | + self.ctr = ctr | ||
| 1700 | + | ||
| 1701 | + class DummyValue: | ||
| 1702 | + def __init__(self, ctr): | ||
| 1703 | + self.ctr = ctr | ||
| 1704 | + | ||
| 1705 | + def dict_copy(d, exc): | ||
| 1706 | + try: | ||
| 1707 | + if deepcopy is True: | ||
| 1708 | + _ = copy.deepcopy(d) | ||
| 1709 | + else: | ||
| 1710 | + _ = d.copy() | ||
| 1711 | + except Exception as ex: | ||
| 1712 | + exc.append(ex) | ||
| 1713 | + | ||
| 1714 | + def pop_and_collect(lst): | ||
| 1715 | + gc_ctr = 0 | ||
| 1716 | + while lst: | ||
| 1717 | + i = random.randint(0, len(lst) - 1) | ||
| 1718 | + gc_ctr += 1 | ||
| 1719 | + lst.pop(i) | ||
| 1720 | + if gc_ctr % 10000 == 0: | ||
| 1721 | + gc.collect() # just in case | ||
| 1722 | + | ||
| 1723 | + self.assertIn(type_, (weakref.WeakKeyDictionary, weakref.WeakValueDictionary)) | ||
| 1724 | + | ||
| 1725 | + d = type_() | ||
| 1726 | + keys = [] | ||
| 1727 | + values = [] | ||
| 1728 | + # Initialize d with many entries | ||
| 1729 | + for i in range(70000): | ||
| 1730 | + k, v = DummyKey(i), DummyValue(i) | ||
| 1731 | + keys.append(k) | ||
| 1732 | + values.append(v) | ||
| 1733 | + d[k] = v | ||
| 1734 | + del k | ||
| 1735 | + del v | ||
| 1736 | + | ||
| 1737 | + t_copy = threading.Thread(target=dict_copy, args=(d, exc,)) | ||
| 1738 | + if type_ is weakref.WeakKeyDictionary: | ||
| 1739 | + t_collect = threading.Thread(target=pop_and_collect, args=(keys,)) | ||
| 1740 | + else: # weakref.WeakValueDictionary | ||
| 1741 | + t_collect = threading.Thread(target=pop_and_collect, args=(values,)) | ||
| 1742 | + | ||
| 1743 | + t_copy.start() | ||
| 1744 | + t_collect.start() | ||
| 1745 | + | ||
| 1746 | + t_copy.join() | ||
| 1747 | + t_collect.join() | ||
| 1748 | + | ||
| 1749 | + # Test exceptions | ||
| 1750 | + if exc: | ||
| 1751 | + raise exc[0] | ||
| 1752 | + | ||
| 1753 | + def test_threaded_weak_key_dict_copy(self): | ||
| 1754 | + # Issue #35615: Weakref keys or values getting GC'ed during dict | ||
| 1755 | + # copying should not result in a crash. | ||
| 1756 | + self.check_threaded_weak_dict_copy(weakref.WeakKeyDictionary, False) | ||
| 1757 | + | ||
| 1758 | + def test_threaded_weak_key_dict_deepcopy(self): | ||
| 1759 | + # Issue #35615: Weakref keys or values getting GC'ed during dict | ||
| 1760 | + # copying should not result in a crash. | ||
| 1761 | + self.check_threaded_weak_dict_copy(weakref.WeakKeyDictionary, True) | ||
| 1762 | + | ||
| 1763 | + def test_threaded_weak_value_dict_copy(self): | ||
| 1764 | + # Issue #35615: Weakref keys or values getting GC'ed during dict | ||
| 1765 | + # copying should not result in a crash. | ||
| 1766 | + self.check_threaded_weak_dict_copy(weakref.WeakValueDictionary, False) | ||
| 1767 | + | ||
| 1768 | + def test_threaded_weak_value_dict_deepcopy(self): | ||
| 1769 | + # Issue #35615: Weakref keys or values getting GC'ed during dict | ||
| 1770 | + # copying should not result in a crash. | ||
| 1771 | + self.check_threaded_weak_dict_copy(weakref.WeakValueDictionary, True) | ||
| 1772 | + | ||
| 1691 | 1773 | ||
| 1692 | 1774 | from test import mapping_tests | |
| 1693 | 1775 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -171,10 +171,11 @@ def copy(self): | |||
| 171 | 171 | if self._pending_removals: | |
| 172 | 172 | self._commit_removals() | |
| 173 | 173 | new = WeakValueDictionary() | |
| 174 | - for key, wr in self.data.items(): | ||
| 175 | - o = wr() | ||
| 176 | - if o is not None: | ||
| 177 | - new[key] = o | ||
| 174 | + with _IterationGuard(self): | ||
| 175 | + for key, wr in self.data.items(): | ||
| 176 | + o = wr() | ||
| 177 | + if o is not None: | ||
| 178 | + new[key] = o | ||
| 178 | 179 | return new | |
| 179 | 180 | ||
| 180 | 181 | __copy__ = copy | |
@@ -184,10 +185,11 @@ def __deepcopy__(self, memo): | |||
| 184 | 185 | if self._pending_removals: | |
| 185 | 186 | self._commit_removals() | |
| 186 | 187 | new = self.__class__() | |
| 187 | - for key, wr in self.data.items(): | ||
| 188 | - o = wr() | ||
| 189 | - if o is not None: | ||
| 190 | - new[deepcopy(key, memo)] = o | ||
| 188 | + with _IterationGuard(self): | ||
| 189 | + for key, wr in self.data.items(): | ||
| 190 | + o = wr() | ||
| 191 | + if o is not None: | ||
| 192 | + new[deepcopy(key, memo)] = o | ||
| 191 | 193 | return new | |
| 192 | 194 | ||
| 193 | 195 | def get(self, key, default=None): | |
@@ -408,21 +410,23 @@ def __setitem__(self, key, value): | |||
| 408 | 410 | ||
| 409 | 411 | def copy(self): | |
| 410 | 412 | new = WeakKeyDictionary() | |
| 411 | - for key, value in self.data.items(): | ||
| 412 | - o = key() | ||
| 413 | - if o is not None: | ||
| 414 | - new[o] = value | ||
| 413 | + with _IterationGuard(self): | ||
| 414 | + for key, value in self.data.items(): | ||
| 415 | + o = key() | ||
| 416 | + if o is not None: | ||
| 417 | + new[o] = value | ||
| 415 | 418 | return new | |
| 416 | 419 | ||
| 417 | 420 | __copy__ = copy | |
| 418 | 421 | ||
| 419 | 422 | def __deepcopy__(self, memo): | |
| 420 | 423 | from copy import deepcopy | |
| 421 | 424 | new = self.__class__() | |
| 422 | - for key, value in self.data.items(): | ||
| 423 | - o = key() | ||
| 424 | - if o is not None: | ||
| 425 | - new[o] = deepcopy(value, memo) | ||
| 425 | + with _IterationGuard(self): | ||
| 426 | + for key, value in self.data.items(): | ||
| 427 | + o = key() | ||
| 428 | + if o is not None: | ||
| 429 | + new[o] = deepcopy(value, memo) | ||
| 426 | 430 | return new | |
| 427 | 431 | ||
| 428 | 432 | def get(self, key, default=None): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,3 @@ | |||
| 1 | + :mod:`weakref`: Fix a RuntimeError when copying a WeakKeyDictionary or a | ||
| 2 | + WeakValueDictionary, due to some keys or values disappearing while | ||
| 3 | + iterating. | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments