import unittest from contextlib import contextmanager, ExitStack from test.support import catch_unraisable_exception, import_helper, gc_collect # Skip this test if the _testcapi module isn't available. _testcapi = import_helper.import_module('_testcapi') class TestDictWatchers(unittest.TestCase): # types of watchers testcapimodule can add: EVENTS = 0 # appends dict events as strings to global event list ERROR = 1 # unconditionally sets and signals a RuntimeException SECOND = 2 # always appends "second" to global event list def add_watcher(self, kind=EVENTS): return _testcapi.add_dict_watcher(kind) def clear_watcher(self, watcher_id): _testcapi.clear_dict_watcher(watcher_id) @contextmanager def watcher(self, kind=EVENTS): wid = self.add_watcher(kind) try: yield wid finally: self.clear_watcher(wid) def assert_events(self, expected): actual = _testcapi.get_dict_watcher_events() self.assertEqual(actual, expected) def watch(self, wid, d): _testcapi.watch_dict(wid, d) def unwatch(self, wid, d): _testcapi.unwatch_dict(wid, d) def test_set_new_item(self): d = {} with self.watcher() as wid: self.watch(wid, d) d["foo"] = "bar" self.assert_events(["new:foo:bar"]) def test_set_existing_item(self): d = {"foo": "bar"} with self.watcher() as wid: self.watch(wid, d) d["foo"] = "baz" self.assert_events(["mod:foo:baz"]) def test_clone(self): d = {} d2 = {"foo": "bar"} with self.watcher() as wid: self.watch(wid, d) d.update(d2) self.assert_events(["clone"]) def test_no_event_if_not_watched(self): d = {} with self.watcher() as wid: d["foo"] = "bar" self.assert_events([]) def test_del(self): d = {"foo": "bar"} with self.watcher() as wid: self.watch(wid, d) del d["foo"] self.assert_events(["del:foo"]) def test_pop(self): d = {"foo": "bar"} with self.watcher() as wid: self.watch(wid, d) d.pop("foo") self.assert_events(["del:foo"]) def test_clear(self): d = {"foo": "bar"} with self.watcher() as wid: self.watch(wid, d) d.clear() self.assert_events(["clear"]) def test_dealloc(self): d = {"foo": "bar"} with self.watcher() as wid: self.watch(wid, d) del d self.assert_events(["dealloc"]) def test_unwatch(self): d = {} with self.watcher() as wid: self.watch(wid, d) d["foo"] = "bar" self.unwatch(wid, d) d["hmm"] = "baz" self.assert_events(["new:foo:bar"]) def test_error(self): d = {} with self.watcher(kind=self.ERROR) as wid: self.watch(wid, d) with catch_unraisable_exception() as cm: d["foo"] = "bar" self.assertIn( "Exception ignored in " "PyDict_EVENT_ADDED watcher callback for