Skip to content

Commit 51d1035

Browse files
gh-93649: Split watcher API tests from _testcapimodule.c (#99532)
1 parent 9db1e17 commit 51d1035

File tree

8 files changed

+651
-616
lines changed

8 files changed

+651
-616
lines changed

Lib/test/test_capi/test_misc.py

-330
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# these are all functions _testcapi exports whose name begins with 'test_'.
33

44
from collections import OrderedDict
5-
from contextlib import contextmanager, ExitStack
65
import _thread
76
import importlib.machinery
87
import importlib.util
@@ -20,7 +19,6 @@
2019
import weakref
2120
from test import support
2221
from test.support import MISSING_C_DOCSTRINGS
23-
from test.support import catch_unraisable_exception
2422
from test.support import import_helper
2523
from test.support import threading_helper
2624
from test.support import warnings_helper
@@ -1705,333 +1703,5 @@ def func2(x=None):
17051703
self.do_test(func2)
17061704

17071705

1708-
class TestDictWatchers(unittest.TestCase):
1709-
# types of watchers testcapimodule can add:
1710-
EVENTS = 0 # appends dict events as strings to global event list
1711-
ERROR = 1 # unconditionally sets and signals a RuntimeException
1712-
SECOND = 2 # always appends "second" to global event list
1713-
1714-
def add_watcher(self, kind=EVENTS):
1715-
return _testcapi.add_dict_watcher(kind)
1716-
1717-
def clear_watcher(self, watcher_id):
1718-
_testcapi.clear_dict_watcher(watcher_id)
1719-
1720-
@contextmanager
1721-
def watcher(self, kind=EVENTS):
1722-
wid = self.add_watcher(kind)
1723-
try:
1724-
yield wid
1725-
finally:
1726-
self.clear_watcher(wid)
1727-
1728-
def assert_events(self, expected):
1729-
actual = _testcapi.get_dict_watcher_events()
1730-
self.assertEqual(actual, expected)
1731-
1732-
def watch(self, wid, d):
1733-
_testcapi.watch_dict(wid, d)
1734-
1735-
def unwatch(self, wid, d):
1736-
_testcapi.unwatch_dict(wid, d)
1737-
1738-
def test_set_new_item(self):
1739-
d = {}
1740-
with self.watcher() as wid:
1741-
self.watch(wid, d)
1742-
d["foo"] = "bar"
1743-
self.assert_events(["new:foo:bar"])
1744-
1745-
def test_set_existing_item(self):
1746-
d = {"foo": "bar"}
1747-
with self.watcher() as wid:
1748-
self.watch(wid, d)
1749-
d["foo"] = "baz"
1750-
self.assert_events(["mod:foo:baz"])
1751-
1752-
def test_clone(self):
1753-
d = {}
1754-
d2 = {"foo": "bar"}
1755-
with self.watcher() as wid:
1756-
self.watch(wid, d)
1757-
d.update(d2)
1758-
self.assert_events(["clone"])
1759-
1760-
def test_no_event_if_not_watched(self):
1761-
d = {}
1762-
with self.watcher() as wid:
1763-
d["foo"] = "bar"
1764-
self.assert_events([])
1765-
1766-
def test_del(self):
1767-
d = {"foo": "bar"}
1768-
with self.watcher() as wid:
1769-
self.watch(wid, d)
1770-
del d["foo"]
1771-
self.assert_events(["del:foo"])
1772-
1773-
def test_pop(self):
1774-
d = {"foo": "bar"}
1775-
with self.watcher() as wid:
1776-
self.watch(wid, d)
1777-
d.pop("foo")
1778-
self.assert_events(["del:foo"])
1779-
1780-
def test_clear(self):
1781-
d = {"foo": "bar"}
1782-
with self.watcher() as wid:
1783-
self.watch(wid, d)
1784-
d.clear()
1785-
self.assert_events(["clear"])
1786-
1787-
def test_dealloc(self):
1788-
d = {"foo": "bar"}
1789-
with self.watcher() as wid:
1790-
self.watch(wid, d)
1791-
del d
1792-
self.assert_events(["dealloc"])
1793-
1794-
def test_unwatch(self):
1795-
d = {}
1796-
with self.watcher() as wid:
1797-
self.watch(wid, d)
1798-
d["foo"] = "bar"
1799-
self.unwatch(wid, d)
1800-
d["hmm"] = "baz"
1801-
self.assert_events(["new:foo:bar"])
1802-
1803-
def test_error(self):
1804-
d = {}
1805-
with self.watcher(kind=self.ERROR) as wid:
1806-
self.watch(wid, d)
1807-
with catch_unraisable_exception() as cm:
1808-
d["foo"] = "bar"
1809-
self.assertIs(cm.unraisable.object, d)
1810-
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
1811-
self.assert_events([])
1812-
1813-
def test_two_watchers(self):
1814-
d1 = {}
1815-
d2 = {}
1816-
with self.watcher() as wid1:
1817-
with self.watcher(kind=self.SECOND) as wid2:
1818-
self.watch(wid1, d1)
1819-
self.watch(wid2, d2)
1820-
d1["foo"] = "bar"
1821-
d2["hmm"] = "baz"
1822-
self.assert_events(["new:foo:bar", "second"])
1823-
1824-
def test_watch_non_dict(self):
1825-
with self.watcher() as wid:
1826-
with self.assertRaisesRegex(ValueError, r"Cannot watch non-dictionary"):
1827-
self.watch(wid, 1)
1828-
1829-
def test_watch_out_of_range_watcher_id(self):
1830-
d = {}
1831-
with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID -1"):
1832-
self.watch(-1, d)
1833-
with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"):
1834-
self.watch(8, d) # DICT_MAX_WATCHERS = 8
1835-
1836-
def test_watch_unassigned_watcher_id(self):
1837-
d = {}
1838-
with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 1"):
1839-
self.watch(1, d)
1840-
1841-
def test_unwatch_non_dict(self):
1842-
with self.watcher() as wid:
1843-
with self.assertRaisesRegex(ValueError, r"Cannot watch non-dictionary"):
1844-
self.unwatch(wid, 1)
1845-
1846-
def test_unwatch_out_of_range_watcher_id(self):
1847-
d = {}
1848-
with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID -1"):
1849-
self.unwatch(-1, d)
1850-
with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"):
1851-
self.unwatch(8, d) # DICT_MAX_WATCHERS = 8
1852-
1853-
def test_unwatch_unassigned_watcher_id(self):
1854-
d = {}
1855-
with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 1"):
1856-
self.unwatch(1, d)
1857-
1858-
def test_clear_out_of_range_watcher_id(self):
1859-
with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID -1"):
1860-
self.clear_watcher(-1)
1861-
with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"):
1862-
self.clear_watcher(8) # DICT_MAX_WATCHERS = 8
1863-
1864-
def test_clear_unassigned_watcher_id(self):
1865-
with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 1"):
1866-
self.clear_watcher(1)
1867-
1868-
1869-
class TestTypeWatchers(unittest.TestCase):
1870-
# types of watchers testcapimodule can add:
1871-
TYPES = 0 # appends modified types to global event list
1872-
ERROR = 1 # unconditionally sets and signals a RuntimeException
1873-
WRAP = 2 # appends modified type wrapped in list to global event list
1874-
1875-
# duplicating the C constant
1876-
TYPE_MAX_WATCHERS = 8
1877-
1878-
def add_watcher(self, kind=TYPES):
1879-
return _testcapi.add_type_watcher(kind)
1880-
1881-
def clear_watcher(self, watcher_id):
1882-
_testcapi.clear_type_watcher(watcher_id)
1883-
1884-
@contextmanager
1885-
def watcher(self, kind=TYPES):
1886-
wid = self.add_watcher(kind)
1887-
try:
1888-
yield wid
1889-
finally:
1890-
self.clear_watcher(wid)
1891-
1892-
def assert_events(self, expected):
1893-
actual = _testcapi.get_type_modified_events()
1894-
self.assertEqual(actual, expected)
1895-
1896-
def watch(self, wid, t):
1897-
_testcapi.watch_type(wid, t)
1898-
1899-
def unwatch(self, wid, t):
1900-
_testcapi.unwatch_type(wid, t)
1901-
1902-
def test_watch_type(self):
1903-
class C: pass
1904-
with self.watcher() as wid:
1905-
self.watch(wid, C)
1906-
C.foo = "bar"
1907-
self.assert_events([C])
1908-
1909-
def test_event_aggregation(self):
1910-
class C: pass
1911-
with self.watcher() as wid:
1912-
self.watch(wid, C)
1913-
C.foo = "bar"
1914-
C.bar = "baz"
1915-
# only one event registered for both modifications
1916-
self.assert_events([C])
1917-
1918-
def test_lookup_resets_aggregation(self):
1919-
class C: pass
1920-
with self.watcher() as wid:
1921-
self.watch(wid, C)
1922-
C.foo = "bar"
1923-
# lookup resets type version tag
1924-
self.assertEqual(C.foo, "bar")
1925-
C.bar = "baz"
1926-
# both events registered
1927-
self.assert_events([C, C])
1928-
1929-
def test_unwatch_type(self):
1930-
class C: pass
1931-
with self.watcher() as wid:
1932-
self.watch(wid, C)
1933-
C.foo = "bar"
1934-
self.assertEqual(C.foo, "bar")
1935-
self.assert_events([C])
1936-
self.unwatch(wid, C)
1937-
C.bar = "baz"
1938-
self.assert_events([C])
1939-
1940-
def test_clear_watcher(self):
1941-
class C: pass
1942-
# outer watcher is unused, it's just to keep events list alive
1943-
with self.watcher() as _:
1944-
with self.watcher() as wid:
1945-
self.watch(wid, C)
1946-
C.foo = "bar"
1947-
self.assertEqual(C.foo, "bar")
1948-
self.assert_events([C])
1949-
C.bar = "baz"
1950-
# Watcher on C has been cleared, no new event
1951-
self.assert_events([C])
1952-
1953-
def test_watch_type_subclass(self):
1954-
class C: pass
1955-
class D(C): pass
1956-
with self.watcher() as wid:
1957-
self.watch(wid, D)
1958-
C.foo = "bar"
1959-
self.assert_events([D])
1960-
1961-
def test_error(self):
1962-
class C: pass
1963-
with self.watcher(kind=self.ERROR) as wid:
1964-
self.watch(wid, C)
1965-
with catch_unraisable_exception() as cm:
1966-
C.foo = "bar"
1967-
self.assertIs(cm.unraisable.object, C)
1968-
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
1969-
self.assert_events([])
1970-
1971-
def test_two_watchers(self):
1972-
class C1: pass
1973-
class C2: pass
1974-
with self.watcher() as wid1:
1975-
with self.watcher(kind=self.WRAP) as wid2:
1976-
self.assertNotEqual(wid1, wid2)
1977-
self.watch(wid1, C1)
1978-
self.watch(wid2, C2)
1979-
C1.foo = "bar"
1980-
C2.hmm = "baz"
1981-
self.assert_events([C1, [C2]])
1982-
1983-
def test_watch_non_type(self):
1984-
with self.watcher() as wid:
1985-
with self.assertRaisesRegex(ValueError, r"Cannot watch non-type"):
1986-
self.watch(wid, 1)
1987-
1988-
def test_watch_out_of_range_watcher_id(self):
1989-
class C: pass
1990-
with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID -1"):
1991-
self.watch(-1, C)
1992-
with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID 8"):
1993-
self.watch(self.TYPE_MAX_WATCHERS, C)
1994-
1995-
def test_watch_unassigned_watcher_id(self):
1996-
class C: pass
1997-
with self.assertRaisesRegex(ValueError, r"No type watcher set for ID 1"):
1998-
self.watch(1, C)
1999-
2000-
def test_unwatch_non_type(self):
2001-
with self.watcher() as wid:
2002-
with self.assertRaisesRegex(ValueError, r"Cannot watch non-type"):
2003-
self.unwatch(wid, 1)
2004-
2005-
def test_unwatch_out_of_range_watcher_id(self):
2006-
class C: pass
2007-
with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID -1"):
2008-
self.unwatch(-1, C)
2009-
with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID 8"):
2010-
self.unwatch(self.TYPE_MAX_WATCHERS, C)
2011-
2012-
def test_unwatch_unassigned_watcher_id(self):
2013-
class C: pass
2014-
with self.assertRaisesRegex(ValueError, r"No type watcher set for ID 1"):
2015-
self.unwatch(1, C)
2016-
2017-
def test_clear_out_of_range_watcher_id(self):
2018-
with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID -1"):
2019-
self.clear_watcher(-1)
2020-
with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID 8"):
2021-
self.clear_watcher(self.TYPE_MAX_WATCHERS)
2022-
2023-
def test_clear_unassigned_watcher_id(self):
2024-
with self.assertRaisesRegex(ValueError, r"No type watcher set for ID 1"):
2025-
self.clear_watcher(1)
2026-
2027-
def test_no_more_ids_available(self):
2028-
contexts = [self.watcher() for i in range(self.TYPE_MAX_WATCHERS)]
2029-
with ExitStack() as stack:
2030-
for ctx in contexts:
2031-
stack.enter_context(ctx)
2032-
with self.assertRaisesRegex(RuntimeError, r"no more type watcher IDs"):
2033-
self.add_watcher()
2034-
2035-
20361706
if __name__ == "__main__":
20371707
unittest.main()

0 commit comments

Comments
 (0)