class ObjectsCleaner(object): def __init__(self, attribute_name): self.attribute_name = attribute_name self.objects = WeakSet() def add(self, obj): self.objects.add(obj) def clear(self, instance=None): if instance is None: self._clear_all_objects() else: self._clear_obj(instance) try: self.objects.remove(instance) except KeyError: pass def _clear_all_objects(self): for obj in self.objects: self._clear_obj(obj) self.objects.clear() def _clear_obj(self, obj): try: delattr(obj, self.attribute_name) except AttributeError: pass
class BaseSubject(object): """ Object holding all the observers, aliased to dirigent.subject """ def __init__(self, init=None): if init: self.observers = WeakSet(init) else: self.observers = WeakSet() def register(self, func): """ Registers a callable. Can be used as a decorator. """ self.observers.add(func) return func def unregister(self, func): """ Unregisters a callable. """ if func in self.observers: self.observers.remove(func) return func return False def notify(self, *args, **kwargs): """ Notifies all registered observers of an event. """ return [observer(*args, **kwargs) for observer in self.observers] def __iter__(self): return (observer for observer in self.observers) __call__ = notify on = bind = register off = unbind = unregister
class _OngoingCalls: """Internal class used for have visibility of the ongoing calls.""" _calls: AbstractSet[_base_call.RpcContext] def __init__(self): self._calls = WeakSet() def _remove_call(self, call: _base_call.RpcContext): try: self._calls.remove(call) except KeyError: pass @property def calls(self) -> AbstractSet[_base_call.RpcContext]: """Returns the set of ongoing calls.""" return self._calls def size(self) -> int: """Returns the number of ongoing calls.""" return len(self._calls) def trace_call(self, call: _base_call.RpcContext): """Adds and manages a new ongoing call.""" self._calls.add(call) call.add_done_callback(self._remove_call)
class Signal(object): def __init__(self): self._functions = WeakSet() self._methods = WeakKeyDictionary() def __call__(self, *args, **kargs): for f in self._functions: f(*args, **kargs) for obj, functions in self._methods.items(): for f in functions: f(obj, *args, **kargs) def connect(self, slot): if inspect.ismethod(slot): if not slot.__self__ in self._methods: self._methods[slot.__self__] = set() self._methods[slot.__self__].add(slot.__func__) else: self._functions.add(slot) def disconnect(self, slot): if inspect.ismethod(slot): if slot.__self__ in self._methods: self._methods[slot.__self__].remove(slot.__func__) else: if slot in self._functions: self._functions.remove(slot)
class Signal(object): def __init__(self): self._functions = WeakSet() self._methods = WeakKeyDictionary() def __call__(self, *args, **kargs): # Call handler functions to_be_removed = [] for func in self._functions.copy(): try: func(*args, **kargs) except RuntimeError: Warning.warn( 'Signals func->RuntimeError: func "{}" will be removed.'. format(func)) to_be_removed.append(func) for remove in to_be_removed: self._functions.discard(remove) # Call handler methods to_be_removed = [] emitters = self._methods.copy() for obj, funcs in emitters.items(): msg_debug('obj is type "{}"'.format(type(obj))) for func in funcs.copy(): try: func(obj, *args, **kargs) except RuntimeError: warnings.warn( 'Signals methods->RuntimeError, obj.func "{}.{}" will be removed' .format(obj, func)) to_be_removed.append((obj, func)) for obj, func in to_be_removed: self._methods[obj].discard(func) def connect(self, slot): if inspect.ismethod(slot): if slot.__self__ not in self._methods: self._methods[slot.__self__] = set() self._methods[slot.__self__].add(slot.__func__) else: self._functions.add(slot) def disconnect(self, slot): if inspect.ismethod(slot): if slot.__self__ in self._methods: self._methods[slot.__self__].remove(slot.__func__) else: if slot in self._functions: self._functions.remove(slot) def clear(self): self._functions.clear() self._methods.clear()
class Signal(object): """ Simple class to emit signals to connected callable receivers. """ def __init__(self): """ Instantiate a new object """ self.funcs = WeakSet() self.meths = WeakKeyDictionary() def connect(self, c): """ Connect a callable as receiver for the signal @param c: signal receiver @type c: Callable """ if inspect.ismethod(c): if c.__self__ not in self.meths: self.meths[c.__self__] = set() self.meths[c.__self__].add(c.__func__) else: if c not in self.funcs: self.funcs.add(c) def disconnect(self, c): """ Disconnect the callable from receiving the signal @param c: signal receiver @type c: Callable """ if inspect.ismethod(c): if c.__self__ in self.meths: self.meths[c.__self__].remove(c.__func__) else: if c in self.funcs: self.funcs.remove(c) def disconnectAll(self): """ Disconnects all signal receivers """ self.funcs.clear() self.meths.clear() def emit(self, *args, **kwargs): """ Fires the signal to all connected receivers """ for c in self.funcs: c(*args, **kwargs) for obj, funcs in self.meths.items(): for func in funcs: func(obj, *args, **kwargs)
class Signal(object): def __init__(self): self._functions = WeakSet() self._methods = WeakKeyDictionary() self._activated = True def __call__(self, *args, **kargs): # call connected functions only if activated if self._activated: # Call handler functions for func in self._functions: func(*args, **kargs) # Call handler methods for obj, funcs in self._methods.items(): for func in funcs: func(obj, *args, **kargs) def connect(self, slot): if inspect.ismethod(slot): if slot.__self__ not in self._methods: self._methods[slot.__self__] = set() self._methods[slot.__self__].add(slot.__func__) else: self._functions.add(slot) def disconnect(self, slot): if inspect.ismethod(slot): if slot.__self__ in self._methods: self._methods[slot.__self__].remove(slot.__func__) else: if slot in self._functions: self._functions.remove(slot) def clear(self): self._functions.clear() self._methods.clear() def activate(self): """ Activate the signal to emit. """ self._activated = True def deactivate(self): """ Deactivate the signal to emit. """ self._activated = False
class Signal(object): def __init__(self): self._functions = WeakSet() self._methods = WeakKeyDictionary() def __call__(self, *args, **kargs): # Call handler functions to_be_removed = [] for func in self._functions.copy(): try: func(*args, **kargs) except RuntimeError: Warning.warn('Signals func->RuntimeError: func "{}" will be removed.'.format(func)) to_be_removed.append(func) for remove in to_be_removed: self._functions.discard(remove) # Call handler methods to_be_removed = [] emitters = self._methods.copy() for obj, funcs in emitters.items(): msg_debug('obj is type "{}"'.format(type(obj))) for func in funcs.copy(): try: func(obj, *args, **kargs) except RuntimeError: warnings.warn('Signals methods->RuntimeError, obj.func "{}.{}" will be removed'.format(obj, func)) to_be_removed.append((obj, func)) for obj, func in to_be_removed: self._methods[obj].discard(func) def connect(self, slot): if inspect.ismethod(slot): if slot.__self__ not in self._methods: self._methods[slot.__self__] = set() self._methods[slot.__self__].add(slot.__func__) else: self._functions.add(slot) def disconnect(self, slot): if inspect.ismethod(slot): if slot.__self__ in self._methods: self._methods[slot.__self__].remove(slot.__func__) else: if slot in self._functions: self._functions.remove(slot) def clear(self): self._functions.clear() self._methods.clear()
class ConnectionPool: def __init__(self): self._config_dict = None self._queue = Queue() self._outstanding_connections = WeakSet() async def get_conn(self): self._check_config() try: while True: conn = self._queue.get_nowait() if conn.is_open(): break try: await conn.close() except Exception: l.debug('Exception in close rethink connection', exc_info=True) except QueueEmpty: conn = await r.connect(**self._config_dict) self._outstanding_connections.add(conn) return conn async def put_conn(self, conn): self._queue.put_nowait(conn) self._outstanding_connections.remove(conn) def set_config(self, config): self._config_dict = config def get_config(self): self._check_config() return self._config_dict async def teardown(self): while True: try: conn = self._queue.get_nowait() except QueueEmpty: break self._outstanding_connections.add(conn) for conn in self._outstanding_connections: try: await conn.close() except Exception: l.debug('Exception in close rethink connection', exc_info=True) def _check_config(self): assert self._config_dict is not None, "Did you remember to run resync.setup()?"
class Account(Entity): """A user account.""" _uid_code = "A" type = "account" def __init__(self, data=None, active=False, savable=True): super().__init__(data, active, savable) self._sessions = WeakSet() def __repr__(self): if hasattr(self, "name") and self.name: return joins("Account<", self.name, ">", sep="") else: return "Account<(unnamed)>" def login(self, session): """Process an account login for a session. :param sessions.Session session: The session logging in :returns None: """ with EVENTS.fire("account_login", session, self): log.info("%s has logged in to %s.", session, self) session.send("\nMOTD will go here!") if session not in self._sessions: self._sessions.add(session) if not self.active: self.active = True def logout(self, session): """Process an account logout for a session. :param sessions.Session session: The session logging out :returns None: """ with EVENTS.fire("account_logout", session, self): log.info("%s has logged out of %s.", session, self) if session in self._sessions: self._sessions.remove(session) if not self._sessions: self.active = False
class SIGNAL(object): def __init__( self, name = None ): self._functions = WeakSet() self._methods = WeakKeyDictionary() self._name = name def __call__(self, *args, **kargs): # Call handler functions for func in self._functions: func(*args, **kargs) # Call handler methods for obj, funcs in self._methods.items(): for func in funcs: func(obj, *args, **kargs) def connect(self, slot): if inspect.ismethod(slot): if slot.__self__ not in self._methods: self._methods[slot.__self__] = set() self._methods[slot.__self__].add(slot.__func__) else: self._functions.add(slot) def disconnect(self, slot): if inspect.ismethod(slot): if slot.__self__ in self._methods: self._methods[slot.__self__].remove(slot.__func__) else: if slot in self._functions: self._functions.remove(slot) def clear(self): self._functions.clear() self._methods.clear()
class Signal(object): def __init__(self): self._functions = WeakSet() self._methods = WeakKeyDictionary() def __call__(self, *args, **kargs): res = [] # Call handler functions for func in self._functions: res.append(func(*args, **kargs)) # Call handler methods for obj, funcs in self._methods.items(): for func in funcs: res.append(func(obj, *args, **kargs)) return res def connect(self, slot): if inspect.ismethod(slot): if slot.__self__ not in self._methods: self._methods[slot.__self__] = set() self._methods[slot.__self__].add(slot.__func__) else: self._functions.add(slot) def disconnect(self, slot): if inspect.ismethod(slot): if slot.__self__ in self._methods: self._methods[slot.__self__].remove(slot.__func__) else: if slot in self._functions: self._functions.remove(slot) def clear(self): self._functions.clear() self._methods.clear()
class SIGNAL(object): def __init__(self, name=None): self._functions = WeakSet() self._methods = WeakKeyDictionary() self._name = name def __call__(self, *args, **kargs): # Call handler functions for func in self._functions: func(*args, **kargs) # Call handler methods for obj, funcs in self._methods.items(): for func in funcs: func(obj, *args, **kargs) def connect(self, slot): if inspect.ismethod(slot): if slot.__self__ not in self._methods: self._methods[slot.__self__] = set() self._methods[slot.__self__].add(slot.__func__) else: self._functions.add(slot) def disconnect(self, slot): if inspect.ismethod(slot): if slot.__self__ in self._methods: self._methods[slot.__self__].remove(slot.__func__) else: if slot in self._functions: self._functions.remove(slot) def clear(self): self._functions.clear() self._methods.clear()
class TestWeakSet(unittest.TestCase): def setUp(self): # need to keep references to them self.items = [SomeClass(c) for c in ('a', 'b', 'c')] self.items2 = [SomeClass(c) for c in ('x', 'y', 'z')] self.letters = [SomeClass(c) for c in string.ascii_letters] self.ab_items = [SomeClass(c) for c in 'ab'] self.abcde_items = [SomeClass(c) for c in 'abcde'] self.def_items = [SomeClass(c) for c in 'def'] self.ab_weakset = WeakSet(self.ab_items) self.abcde_weakset = WeakSet(self.abcde_items) self.def_weakset = WeakSet(self.def_items) self.s = WeakSet(self.items) self.d = dict.fromkeys(self.items) self.obj = SomeClass('F') self.fs = WeakSet([self.obj]) def test_methods(self): weaksetmethods = dir(WeakSet) for method in dir(set): if method == 'test_c_api' or method.startswith('_'): continue self.assertIn(method, weaksetmethods, "WeakSet missing method " + method) def test_new_or_init(self): self.assertRaises(TypeError, WeakSet, [], 2) def test_len(self): self.assertEqual(len(self.s), len(self.d)) self.assertEqual(len(self.fs), 1) del self.obj test_support.gc_collect() # len of weak collections is eventually consistent on # Jython. In practice this does not matter because of the # nature of weaksets - we cannot rely on what happens in the # reaper thread and how it interacts with gc self.assertIn(len(self.fs), (0, 1)) def test_contains(self): for c in self.letters: self.assertEqual(c in self.s, c in self.d) # 1 is not weakref'able, but that TypeError is caught by __contains__ self.assertNotIn(1, self.s) self.assertIn(self.obj, self.fs) del self.obj test_support.gc_collect() self.assertNotIn(SomeClass('F'), self.fs) def test_union(self): u = self.s.union(self.items2) for c in self.letters: self.assertEqual(c in u, c in self.d or c in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(u), WeakSet) self.assertRaises(TypeError, self.s.union, [[]]) for C in set, frozenset, dict.fromkeys, list, tuple: x = WeakSet(self.items + self.items2) c = C(self.items2) self.assertEqual(self.s.union(c), x) del c test_support.gc_collect() self.assertEqual(len(list(u)), len(list(self.items)) + len(list(self.items2))) self.items2.pop() test_support.gc_collect() self.assertEqual(len(list(u)), len(list(self.items)) + len(list(self.items2))) def test_or(self): i = self.s.union(self.items2) self.assertEqual(self.s | set(self.items2), i) self.assertEqual(self.s | frozenset(self.items2), i) def test_intersection(self): s = WeakSet(self.letters) i = s.intersection(self.items2) for c in self.letters: self.assertEqual(c in i, c in self.items2 and c in self.letters) self.assertEqual(s, WeakSet(self.letters)) self.assertEqual(type(i), WeakSet) for C in set, frozenset, dict.fromkeys, list, tuple: x = WeakSet([]) self.assertEqual(i.intersection(C(self.items)), x) self.assertEqual(len(i), len(self.items2)) self.items2.pop() test_support.gc_collect() self.assertEqual(len(list(i)), len(list(self.items2))) def test_isdisjoint(self): self.assertTrue(self.s.isdisjoint(WeakSet(self.items2))) self.assertTrue(not self.s.isdisjoint(WeakSet(self.letters))) def test_and(self): i = self.s.intersection(self.items2) self.assertEqual(self.s & set(self.items2), i) self.assertEqual(self.s & frozenset(self.items2), i) def test_difference(self): i = self.s.difference(self.items2) for c in self.letters: self.assertEqual(c in i, c in self.d and c not in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) self.assertRaises(TypeError, self.s.difference, [[]]) def test_sub(self): i = self.s.difference(self.items2) self.assertEqual(self.s - set(self.items2), i) self.assertEqual(self.s - frozenset(self.items2), i) def test_symmetric_difference(self): i = self.s.symmetric_difference(self.items2) for c in self.letters: self.assertEqual(c in i, (c in self.d) ^ (c in self.items2)) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) self.assertRaises(TypeError, self.s.symmetric_difference, [[]]) self.assertEqual(len(i), len(self.items) + len(self.items2)) self.items2.pop() test_support.gc_collect() self.assertEqual(len(list(i)), len(list(self.items)) + len(list(self.items2))) def test_xor(self): i = self.s.symmetric_difference(self.items2) self.assertEqual(self.s ^ set(self.items2), i) self.assertEqual(self.s ^ frozenset(self.items2), i) def test_sub_and_super(self): self.assertTrue(self.ab_weakset <= self.abcde_weakset) self.assertTrue(self.abcde_weakset <= self.abcde_weakset) self.assertTrue(self.abcde_weakset >= self.ab_weakset) self.assertFalse(self.abcde_weakset <= self.def_weakset) self.assertFalse(self.abcde_weakset >= self.def_weakset) self.assertTrue(set('a').issubset('abc')) self.assertTrue(set('abc').issuperset('a')) self.assertFalse(set('a').issubset('cbs')) self.assertFalse(set('cbs').issuperset('a')) def test_lt(self): self.assertTrue(self.ab_weakset < self.abcde_weakset) self.assertFalse(self.abcde_weakset < self.def_weakset) self.assertFalse(self.ab_weakset < self.ab_weakset) self.assertFalse(WeakSet() < WeakSet()) def test_gt(self): self.assertTrue(self.abcde_weakset > self.ab_weakset) self.assertFalse(self.abcde_weakset > self.def_weakset) self.assertFalse(self.ab_weakset > self.ab_weakset) self.assertFalse(WeakSet() > WeakSet()) def test_gc(self): # Create a nest of cycles to exercise overall ref count check s = WeakSet(Foo() for i in range(1000)) for elem in s: elem.cycle = s elem.sub = elem elem.set = WeakSet([elem]) def test_subclass_with_custom_hash(self): # Bug #1257731 class H(WeakSet): def __hash__(self): return int(id(self) & 0x7fffffff) s=H() f=set() f.add(s) self.assertIn(s, f) f.remove(s) f.add(s) f.discard(s) def test_init(self): s = WeakSet() s.__init__(self.items) self.assertEqual(s, self.s) s.__init__(self.items2) self.assertEqual(s, WeakSet(self.items2)) self.assertRaises(TypeError, s.__init__, s, 2); self.assertRaises(TypeError, s.__init__, 1); def test_constructor_identity(self): s = WeakSet(self.items) t = WeakSet(s) self.assertNotEqual(id(s), id(t)) def test_hash(self): self.assertRaises(TypeError, hash, self.s) def test_clear(self): self.s.clear() self.assertEqual(self.s, WeakSet([])) self.assertEqual(len(self.s), 0) def test_copy(self): dup = self.s.copy() self.assertEqual(self.s, dup) self.assertNotEqual(id(self.s), id(dup)) def test_add(self): x = SomeClass('Q') self.s.add(x) self.assertIn(x, self.s) dup = self.s.copy() self.s.add(x) self.assertEqual(self.s, dup) if not test_support.is_jython: # Jython/JVM can weakly reference list and other objects self.assertRaises(TypeError, self.s.add, []) self.fs.add(Foo()) test_support.gc_collect() # CPython assumes Foo() went out of scope and was collected, so ensure the same self.assertEqual(len(list(self.fs)), 1) self.fs.add(self.obj) self.assertEqual(len(list(self.fs)), 1) def test_remove(self): x = SomeClass('a') self.s.remove(x) self.assertNotIn(x, self.s) self.assertRaises(KeyError, self.s.remove, x) if not test_support.is_jython: # Jython/JVM can weakly reference list and other objects self.assertRaises(TypeError, self.s.remove, []) def test_discard(self): a, q = SomeClass('a'), SomeClass('Q') self.s.discard(a) self.assertNotIn(a, self.s) self.s.discard(q) if not test_support.is_jython: # Jython/JVM can weakly reference list and other objects self.assertRaises(TypeError, self.s.discard, []) def test_pop(self): for i in range(len(self.s)): elem = self.s.pop() self.assertNotIn(elem, self.s) self.assertRaises(KeyError, self.s.pop) def test_update(self): retval = self.s.update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): self.assertIn(c, self.s) self.assertRaises(TypeError, self.s.update, [[]]) def test_update_set(self): self.s.update(set(self.items2)) for c in (self.items + self.items2): self.assertIn(c, self.s) def test_ior(self): self.s |= set(self.items2) for c in (self.items + self.items2): self.assertIn(c, self.s) def test_intersection_update(self): retval = self.s.intersection_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if c in self.items2 and c in self.items: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) self.assertRaises(TypeError, self.s.intersection_update, [[]]) def test_iand(self): self.s &= set(self.items2) for c in (self.items + self.items2): if c in self.items2 and c in self.items: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) def test_difference_update(self): retval = self.s.difference_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if c in self.items and c not in self.items2: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) if not test_support.is_jython: # Jython/JVM can weakly reference list and other objects self.assertRaises(TypeError, self.s.difference_update, [[]]) self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) def test_isub(self): self.s -= set(self.items2) for c in (self.items + self.items2): if c in self.items and c not in self.items2: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) def test_symmetric_difference_update(self): retval = self.s.symmetric_difference_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if (c in self.items) ^ (c in self.items2): self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) def test_ixor(self): self.s ^= set(self.items2) for c in (self.items + self.items2): if (c in self.items) ^ (c in self.items2): self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) def test_inplace_on_self(self): t = self.s.copy() t |= t self.assertEqual(t, self.s) t &= t self.assertEqual(t, self.s) t -= t self.assertEqual(t, WeakSet()) t = self.s.copy() t ^= t self.assertEqual(t, WeakSet()) def test_eq(self): # issue 5964 (http://bugs.python.org/issue5964) self.assertEqual(self.s, self.s) self.assertEqual(self.s, WeakSet(self.items)) # Jython diverges here in the next test because it constructs # WeakSet as a subclass of set; this seems to be the proper # thing to do given what is the typical comparison self.assertEqual(self.s, set(self.items)) self.assertNotEqual(self.s, list(self.items)) self.assertNotEqual(self.s, tuple(self.items)) self.assertNotEqual(self.s, 1) def test_weak_destroy_while_iterating(self): # Issue #7105: iterators shouldn't crash when a key is implicitly removed # Create new items to be sure no-one else holds a reference items = [SomeClass(c) for c in ('a', 'b', 'c')] s = WeakSet(items) it = iter(s) next(it) # Trigger internal iteration # Destroy an item del items[-1] test_support.gc_collect() # just in case # We have removed either the first consumed items, or another one self.assertIn(len(list(it)), [len(items), len(items) - 1]) del it extra_collect() # The removal has been committed self.assertEqual(len(s), len(items)) def test_weak_destroy_and_mutate_while_iterating(self): # Issue #7105: iterators shouldn't crash when a key is implicitly removed items = [SomeClass(c) for c in string.ascii_letters] s = WeakSet(items) @contextlib.contextmanager def testcontext(): try: it = iter(s) next(it) # Schedule an item for removal and recreate it u = SomeClass(str(items.pop())) test_support.gc_collect() # just in case yield u finally: it = None # should commit all removals test_support.gc_collect() with testcontext() as u: self.assertNotIn(u, s) with testcontext() as u: self.assertRaises(KeyError, s.remove, u) self.assertNotIn(u, s) with testcontext() as u: s.add(u) self.assertIn(u, s) t = s.copy() with testcontext() as u: s.update(t) self.assertEqual(len(s), len(t)) with testcontext() as u: s.clear() self.assertEqual(len(s), 0) def test_len_cycles(self): N = 20 items = [RefCycle() for i in range(N)] s = WeakSet(items) del items # do some gc test_support.gc_collect() it = iter(s) try: next(it) except StopIteration: pass # do some gc test_support.gc_collect() n1 = len(s) del it # do some gc test_support.gc_collect() n2 = len(s) # one item may be kept alive inside the iterator self.assertIn(n1, (0, 1)) self.assertEqual(n2, 0) @unittest.skipIf(test_support.is_jython, "GarbageCollection not deterministic in Jython") def test_len_race(self): # Extended sanity checks for len() in the face of cyclic collection self.addCleanup(gc.set_threshold, *gc.get_threshold()) for th in range(1, 100): N = 20 gc.collect(0) gc.set_threshold(th, th, th) items = [RefCycle() for i in range(N)] s = WeakSet(items) del items # All items will be collected at next garbage collection pass it = iter(s) try: next(it) except StopIteration: pass n1 = len(s) del it n2 = len(s) self.assertGreaterEqual(n1, 0) self.assertLessEqual(n1, N) self.assertGreaterEqual(n2, 0) self.assertLessEqual(n2, n1)
class Signal: ## Signal types. # These indicate the type of a signal, that is, how the signal handles calling the connected # slots. # - Direct connections immediately call the connected slots from the thread that called emit(). # - Auto connections will push the call onto the event loop if the current thread is # not the main thread, but make a direct call if it is. # - Queued connections will always push # the call on to the event loop. Direct = 1 Auto = 2 Queued = 3 ## Initialize the instance. # # \param kwargs Keyword arguments. # Possible keywords: # - type: The signal type. Defaults to Auto. def __init__(self, **kwargs): self.__functions = WeakSet() self.__methods = WeakKeyDictionary() self.__signals = WeakSet() self.__type = kwargs.get("type", Signal.Auto) self.__emitting = False self.__connect_queue = [] self.__disconnect_queue = [] ## \exception NotImplementedError def __call__(self): raise NotImplementedError("Call emit() to emit a signal") ## Get type of the signal # \return \type{int} Direct(1), Auto(2) or Queued(3) def getType(self): return self.__type ## Emit the signal which indirectly calls all of the connected slots. # # \param args The positional arguments to pass along. # \param kwargs The keyword arguments to pass along. # # \note If the Signal type is Queued and this is not called from the application thread # the call will be posted as an event to the application main thread, which means the # function will be called on the next application event loop tick. @call_if_enabled(_traceEmit, _isTraceEnabled()) def emit(self, *args, **kwargs): try: if self.__type == Signal.Queued: Signal._app.functionEvent(CallFunctionEvent(self.emit, args, kwargs)) return if self.__type == Signal.Auto: if threading.current_thread() is not Signal._app.getMainThread(): Signal._app.functionEvent(CallFunctionEvent(self.emit, args, kwargs)) return except AttributeError: # If Signal._app is not set return self.__emitting = True # Call handler functions for func in self.__functions: func(*args, **kwargs) # Call handler methods for dest, funcs in self.__methods.items(): for func in funcs: func(dest, *args, **kwargs) # Emit connected signals for signal in self.__signals: signal.emit(*args, **kwargs) self.__emitting = False for connector in self.__connect_queue: self.connect(connector) self.__connect_queue.clear() for connector in self.__disconnect_queue: self.disconnect(connector) self.__connect_queue.clear() ## Connect to this signal. # \param connector The signal or slot (function) to connect. @call_if_enabled(_traceConnect, _isTraceEnabled()) def connect(self, connector): if self.__emitting: # When we try to connect to a signal we change the dictionary of connectors. # This will cause an Exception since we are iterating over a dictionary that changed. # So instead, defer the connections until after we are done emitting. self.__connect_queue.append(connector) return if isinstance(connector, Signal): if connector == self: return self.__signals.add(connector) elif inspect.ismethod(connector): if connector.__self__ not in self.__methods: self.__methods[connector.__self__] = set() self.__methods[connector.__self__].add(connector.__func__) else: self.__functions.add(connector) ## Disconnect from this signal. # \param connector The signal or slot (function) to disconnect. @call_if_enabled(_traceDisconnect, _isTraceEnabled()) def disconnect(self, connector): if self.__emitting: # See above. self.__disconnect_queue.append(connector) return try: if connector in self.__signals: self.__signals.remove(connector) elif inspect.ismethod(connector) and connector.__self__ in self.__methods: self.__methods[connector.__self__].remove(connector.__func__) else: if connector in self.__functions: self.__functions.remove(connector) except KeyError: #Ignore errors when connector is not connected to this signal. pass ## Disconnect all connected slots. def disconnectAll(self): if self.__emitting: raise RuntimeError("Tried to disconnect signal while signal is being emitted") self.__functions.clear() self.__methods.clear() self.__signals.clear() ## To support Pickle # # Since Weak containers cannot be serialized by Pickle we just return an empty dict as state. def __getstate__(self): return {} ## To proerly handle deepcopy in combination with __getstate__ # # Apparently deepcopy uses __getstate__ internally, which is not documented. The reimplementation # of __getstate__ then breaks deepcopy. On the other hand, if we do not reimplement it like that, # we break pickle. So instead make sure to also reimplement __deepcopy__. def __deepcopy__(self, memo): signal = Signal(type = self.__type) signal.__functions = copy.deepcopy(self.__functions, memo) signal.__methods = copy.deepcopy(self.__methods, memo) signal.__signals = copy.deepcopy(self.__signals, memo) return signal ## private: # To avoid circular references when importing Application, this should be # set by the Application instance. _app = None
class AsyncTaskContext(AbstractAsyncTaskContext): def __init__(self, daemon=False, parent=None, name=None): self._setup() self.daemon = daemon self.parent = parent self.name = name self.eventloop = parent.eventloop self.parent.add_child(self) def _setup(self): self.children = WeakSet() self.daemon_children = WeakSet() self.exception = None self.tb_list = None self.stack_list = None self.except_func = None self.finally_func = None def add_child(self, child): if DEBUG: log.debug("Adding child:%r ", child) if child.daemon: self.daemon_children.add(child) else: self.children.add(child) def remove_child(self, child): if DEBUG: log.debug("Removing child %r %r", child, self) log.debug("--------------------BEFORE REMOVE---------------------") log_task_context(self, log) log.debug("------------------------------------------------------") if child.daemon: self.daemon_children.remove(child) else: self.children.remove(child) if self.daemon_children and not self.children: self.parent.cancel() if not self.children and not self.daemon_children: if self.exception is not None and self.except_func is not None: self._execute_except(self.exception) else: self._execute_finally() def handle_exception(self, exception, tb_list=None): if DEBUG: log.debug("Handling exception %r %r", self, exception) if self.exception is None \ or not isinstance(exception, CancellationError): self.exception = exception self.tb_list = tb_list for child in self.children.union(self.daemon_children): child.cancel() def cancel(self): if DEBUG: log.debug("Cancelling %r", self) self.handle_exception(CancellationError(), self.tb_list) def update_parent(self): if DEBUG: log.debug("updating parent %r of self %r", self.parent, self) if self.parent is None: return if self.exception is not None: self.parent.handle_exception(self.exception, self.tb_list) self.parent.remove_child(self) self.parent = None # gc def schedule_task(self, task, now=False): if DEBUG: log.debug("Scheduling task %r", task) if now: self.eventloop.execute_now(task) else: self.eventloop.execute(task) def set_stack(self, stack_list): stack_before, stack_after = split_stack(stack_list) if self.parent.stack_list is None: self.parent.stack_list = stack_before self.stack_list = stack_after def _execute_except(self, err): if DEBUG: log.debug("Executing except %r %r", self, err) if self.except_func is not None: with self: except_func = self.except_func self.except_func = None task = AsyncTask(except_func, (err, ), context=self, name=except_func.__name__) task.cancellable = False task.daemon = self.daemon # execute exceptions asap to have a chance to cancel any # pending tasks task.execute_now() else: self._execute_finally() def _execute_finally(self): if DEBUG: log.debug("Executiong finally %r with exception %r", self, self.exception) if isinstance(self.exception, CancellationError): self.exception = None if self.finally_func is not None: with self: task = AsyncTask(self.finally_func, context=self, name=self.finally_func.__name__) task.daemon = self.daemon task.cancellable = False task.execute() self.finally_func = None else: self.update_parent() def __enter__(self): self._parent_context = get_async_context() set_async_context(self) return self def __exit__(self, exc_type, err, tb): set_async_context(self._parent_context) self._parent_context = None # gc def __repr__(self): args = [] if self.name is not None: args.append("name=%s" % self.name) if self.parent: args.append("parent=%s" % self.parent) if self.children: args.append("children=%d" % len(self.children)) if self.daemon_children: args.append("daemon_children=%d" % len(self.daemon_children)) if self.except_func: args.append("except_func=%r" % self.except_func.__name__) if self.finally_func: args.append("finally_func=%r" % self.finally_func.__name__) if self.exception: args.append("exception=%r" % self.exception) if args: args.insert(0, "") return "<%s at %s%s>" % (self.__class__.__name__, hex( id(self)), " ".join(args)) def __str__(self): return "<%s at %s>" % (self.__class__.__name__, hex(id(self)))
class Signal(object): ''' The Signal class is an approximation of Qt's signals+slot system. Each event that an object would like to produce requires a Signal() object. All interested parties on the event must register themselves as receivers via connect() or the '+=' operator. The event source calls emit() to produce an event (or treat it as a callable). All registered receivers will receive it, synchronously. ''' def __init__(self, description=None): ''' Create a Signal() object. Pass 'True' to constructor if locking around emit() is required. ''' self.description = description self._functions = WeakSet() self._methods = WeakKeyDictionary() self._forwards = WeakKeyDictionary() self.disabled = False def disable(self): self.disabled = True def enable(self): self.disabled = False def __len__(self): return len(self._functions) + len(self._methods) def __call__(self, *args, **kwargs): return self.emit(*args, **kwargs) def emit(self, *args, **kwargs): 'Invoke the signal with |args| and |kwargs|' results = [] if self.disabled: return results for f in self._functions: if '__predicate__' in f.__dict__: if not f.__dict__['__predicate__'](): continue results.append(f(*args, **kwargs)) for obj, funcs in self._methods.items(): for f in funcs: if '__predicate__' in f.__dict__: if not f.__dict__['__predicate__'](): continue results.append(f(obj, *args, **kwargs)) return results def connect(self, dest, predicate=None): ''' Connect |dest| to the signal. If |predicate| is set, it is treated as a nullary callable whose return value determines if the signal is fired. NOTE: Passing identical values to multiple invocations of connect() with different values of predicate will overwrite previous predicates and persist the last-used value. To achieve a similar effect, wrap |dest| in a function. ''' assert callable(dest) if inspect.ismethod(dest): obj, impl = dest.__self__, dest.__func__ if predicate is not None: impl.__dict__['__predicate__'] = predicate self._methods.setdefault(obj, set()).add(impl) else: if predicate is not None: dest.__dict__['__predicate__'] = predicate self._functions.add(dest) for signal, methods in self._forwards.items(): for method in methods: signal.connect(method) self._forwards.clear() def when(self, obj, signal, arg=False): ''' This forwards signal from obj ''' #will reemit forwarded signal prepending obj to arguments if arg: method = MethodType( lambda *args, **kwargs: self.emit(*args, **kwargs), obj) else: method = self.emit if len(self): signal.connect(method) else: self._forwards.setdefault(signal, set()).add(method) def __iadd__(self, dest): self.connect(dest) return self def disconnect(self, dest): try: if inspect.ismethod(dest): obj, impl = dest.__self__, dest.__func__ self._methods[obj].remove(impl) else: self._functions.remove(dest) except KeyError: raise SignalDisconnectedError() def __isub__(self, dest): self.disconnect(dest) return self def reset(self): self._functions.clear() self._methods.clear() self._forwards.clear()
class HealthMonitor(object): """ Monitors whether a particular host is marked as up or down. This class is primarily intended for internal use, although applications may find it useful to check whether a given node is up or down. """ is_up = True """ A boolean representing the current state of the node. """ def __init__(self, conviction_policy): self._conviction_policy = conviction_policy self._host = conviction_policy.host # self._listeners will hold, among other things, references to # Cluster objects. To allow those to be GC'ed (and shutdown) even # though we've implemented __del__, use weak references. self._listeners = WeakSet() self._lock = RLock() def register(self, listener): with self._lock: self._listeners.add(listener) def unregister(self, listener): with self._lock: self._listeners.remove(listener) def set_up(self): if self.is_up: return self._conviction_policy.reset() log.info("Host %s is considered up", self._host) with self._lock: listeners = self._listeners.copy() for listener in listeners: listener.on_up(self._host) self.is_up = True def set_down(self): if not self.is_up: return self.is_up = False log.info("Host %s is considered down", self._host) with self._lock: listeners = self._listeners.copy() for listener in listeners: listener.on_down(self._host) def reset(self): return self.set_up() def signal_connection_failure(self, connection_exc): is_down = self._conviction_policy.add_failure(connection_exc) if is_down: self.set_down() return is_down
class BaseService(ABC, CancellableMixin): # Use a WeakSet so that we don't have to bother updating it when tasks finish. _child_services = None # : 'WeakSet[BaseService]' _tasks = None # : 'WeakSet[asyncio.Future[Any]]' _finished_callbacks = None # : List[Callable[['BaseService'], None]] # Number of seconds cancel() will wait for run() to finish. _wait_until_finished_timeout = 5 # the custom event loop to run in, or None if the default loop should be used _loop = None # : asyncio.AbstractEventLoop def __init__( self, token: CancelToken = None, loop: asyncio.AbstractEventLoop = None ) -> None: self.events = ServiceEvents() self._run_lock = asyncio.Lock() self._child_services = WeakSet() self._tasks = WeakSet() self._finished_callbacks = [] self._loop = loop base_token = CancelToken(type(self).__name__, loop=loop) if token is None: self.cancel_token = base_token else: self.cancel_token = base_token.chain(token) self._executor = get_asyncio_executor() @property def logger(self) -> Logger: return Logger def get_event_loop(self) -> asyncio.AbstractEventLoop: if self._loop is None: return asyncio.get_event_loop() else: return self._loop async def run( self, finished_callback: Optional[Callable[["BaseService"], None]] = None ) -> None: """Await for the service's _run() coroutine. Once _run() returns, triggers the cancel token, call cleanup() and finished_callback (if one was passed). """ if self.is_running: raise ValidationError("Cannot start the service while it's already running") elif self.is_cancelled: raise ValidationError( "Cannot restart a service that has already been cancelled" ) if finished_callback: self._finished_callbacks.append(finished_callback) try: async with self._run_lock: self.events.started.set() await self._run() except OperationCancelled as e: self.logger.debug("%s finished: %s" % (self, e)) except Exception: self.logger.exception("Unexpected error in %r, exiting" % self) finally: # Trigger our cancel token to ensure all pending asyncio tasks and background # coroutines started by this service exit cleanly. self.events.cancelled.set() self.cancel_token.trigger() await self.cleanup() for callback in self._finished_callbacks: callback(self) self.events.finished.set() self.logger.debug("%s halted cleanly" % self) def add_finished_callback( self, finished_callback: Callable[["BaseService"], None] ) -> None: self._finished_callbacks.append(finished_callback) def run_task(self, awaitable: Awaitable[Any]) -> None: """Run the given awaitable in the background. The awaitable should return whenever this service's cancel token is triggered. If it raises OperationCancelled, that is caught and ignored. """ @functools.wraps(awaitable) # type: ignore async def _run_task_wrapper() -> None: self.logger.debug("Running task %s" % awaitable) try: await awaitable except OperationCancelled: pass except Exception as e: self.logger.warning("Task %s finished unexpectedly: %s" % (awaitable, e)) self.logger.debug("Task failure traceback") else: self.logger.debug("Task %s finished with no errors" % awaitable) self._tasks.add(asyncio.ensure_future(_run_task_wrapper())) def run_daemon_task(self, awaitable: Awaitable[Any]) -> None: """Run the given awaitable in the background. Like :meth:`run_task` but if the task ends without cancelling, then this this service will terminate as well. """ @functools.wraps(awaitable) # type: ignore async def _run_daemon_task_wrapper() -> None: try: await awaitable finally: if not self.is_cancelled: self.logger.debug( "%s finished while %s is still running, terminating as well" % (awaitable, self) ) self.cancel_token.trigger() self.run_task(_run_daemon_task_wrapper()) def run_child_service(self, child_service: "BaseService") -> None: """ Run a child service and keep a reference to it to be considered during the cleanup. """ if child_service.is_running: raise ValidationError( "Can't start service {}, child of {}: it's already running".format(repr(child_service), repr(self)) ) elif child_service.is_cancelled: raise ValidationError( "Can't restart {}, child of {}: it's already completed".format(repr(child_service), repr(self)) ) self._child_services.add(child_service) self.run_task(child_service.run()) def run_daemon(self, service: "BaseService") -> None: """ Run a service and keep a reference to it to be considered during the cleanup. If the service finishes while we're still running, we'll terminate as well. """ if service.is_running: raise ValidationError( "Can't start daemon {}, child of {}: it's already running".format(repr(service), repr(self)) ) elif service.is_cancelled: raise ValidationError( "Can't restart daemon {}, child of {}: it's already completed".format(repr(service), repr(self)) ) self._child_services.add(service) @functools.wraps(service.run) async def _run_daemon_wrapper() -> None: try: await service.run() except OperationCancelled: pass except Exception as e: self.logger.warning( "Daemon Service %s finished unexpectedly: %s" % (service, e) ) self.logger.debug("Daemon Service failure traceback") finally: if not self.is_cancelled: self.logger.debug( "%s finished while %s is still running, terminating as well" % (service, self) ) self.cancel_token.trigger() self.run_task(_run_daemon_wrapper()) def call_later( self, delay: float, callback: "Callable[..., None]", *args: Any ) -> None: @functools.wraps(callback) async def _call_later_wrapped() -> None: await self.sleep(delay) callback(*args) self.run_task(_call_later_wrapped()) async def _run_in_executor(self, callback: Callable[..., Any], *args: Any) -> Any: loop = self.get_event_loop() return await self.wait(loop.run_in_executor(self._executor, callback, *args)) async def cleanup(self) -> None: """ Run the ``_cleanup()`` coroutine and set the ``cleaned_up`` event after the service as well as all child services finished their cleanup. The ``_cleanup()`` coroutine is invoked before the child services may have finished their cleanup. """ if self._child_services: self.logger.debug( "Waiting for child services: %s" % list(self._child_services) ) await asyncio.gather( *[ child_service.events.cleaned_up.wait() for child_service in self._child_services ] ) self.logger.debug("All child services finished") if self._tasks: self.logger.debug("Waiting for tasks: %s" % list(self._tasks)) await asyncio.gather(*self._tasks) self.logger.debug("All tasks finished") await self._cleanup() self.events.cleaned_up.set() def cancel_nowait(self) -> None: if self.is_cancelled: self.logger.warning( "Tried to cancel %s, but it was already cancelled" % self ) return elif not self.is_running: raise ValidationError("Cannot cancel a service that has not been started") self.logger.debug("Cancelling %s" % self) self.events.cancelled.set() self.cancel_token.trigger() async def cancel(self) -> None: """Trigger the CancelToken and wait for the cleaned_up event to be set.""" self.cancel_nowait() try: await asyncio.wait_for( self.events.cleaned_up.wait(), timeout=self._wait_until_finished_timeout ) except asyncio.futures.TimeoutError: self.logger.info( "Timed out waiting for %s to finish its cleanup, forcibly cancelling pending " "tasks and exiting anyway" % self, ) if self._tasks: self.logger.debug("Pending tasks: %s" % list(self._tasks)) if self._child_services: self.logger.debug( "Pending child services: %s" % list(self._child_services) ) self._forcibly_cancel_all_tasks() # Sleep a bit because the Future.cancel() method just schedules the callbacks, so we # need to give the event loop a chance to actually call them. await asyncio.sleep(0.5) else: self.logger.debug("%s finished cleanly" % self) def _forcibly_cancel_all_tasks(self) -> None: for task in self._tasks: task.cancel() @property def is_cancelled(self) -> bool: return self.cancel_token.triggered @property def is_operational(self) -> bool: return self.events.started.is_set() and not self.cancel_token.triggered @property def is_running(self) -> bool: return self._run_lock.locked() async def threadsafe_cancel(self) -> None: """ Cancel service in another thread. Block until service is cleaned up. :param poll_period: how many seconds to wait in between each check for service cleanup """ asyncio.run_coroutine_threadsafe(self.cancel(), loop=self.get_event_loop()) await asyncio.wait_for( self.events.cleaned_up.wait(), timeout=self._wait_until_finished_timeout ) async def sleep(self, delay: float) -> None: """Coroutine that completes after a given time (in seconds).""" await self.wait(asyncio.sleep(delay)) @abstractmethod async def _run(self) -> None: """Run the service's loop. Should return or raise OperationCancelled when the CancelToken is triggered. """ raise NotImplementedError() async def _cleanup(self) -> None: """Clean up any resources held by this service. Called after the service's _run() method returns. """ pass def gc(self) -> None: for cs in self._child_services.copy(): if cs.events.finished.is_set(): self._child_services.remove(cs) for t in self._tasks.copy(): if t.done(): self._tasks.remove(t)
class TestWeakSet(unittest.TestCase): def setUp(self): self.items = [ustr(c) for c in ('a', 'b', 'c')] self.items2 = [ustr(c) for c in ('x', 'y', 'z')] self.ab_items = [ustr(c) for c in 'ab'] self.abcde_items = [ustr(c) for c in 'abcde'] self.def_items = [ustr(c) for c in 'def'] self.ab_weakset = WeakSet(self.ab_items) self.abcde_weakset = WeakSet(self.abcde_items) self.def_weakset = WeakSet(self.def_items) self.letters = [ustr(c) for c in string.ascii_letters] self.s = WeakSet(self.items) self.d = dict.fromkeys(self.items) self.obj = ustr('F') self.fs = WeakSet([self.obj]) def test_methods(self): weaksetmethods = dir(WeakSet) for method in dir(set): if method == 'test_c_api' or method.startswith('_'): continue self.assertIn(method, weaksetmethods, 'WeakSet missing method ' + method) def test_new_or_init(self): self.assertRaises(TypeError, WeakSet, [], 2) def test_len(self): self.assertEqual(len(self.s), len(self.d)) self.assertEqual(len(self.fs), 1) del self.obj self.assertEqual(len(self.fs), 0) def test_contains(self): for c in self.letters: self.assertEqual(c in self.s, c in self.d) self.assertNotIn(1, self.s) self.assertIn(self.obj, self.fs) del self.obj self.assertNotIn(ustr('F'), self.fs) def test_union(self): u = self.s.union(self.items2) for c in self.letters: self.assertEqual(c in u, c in self.d or c in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(u), WeakSet) self.assertRaises(TypeError, self.s.union, [[]]) for C in (set, frozenset, dict.fromkeys, list, tuple): x = WeakSet(self.items + self.items2) c = C(self.items2) self.assertEqual(self.s.union(c), x) del c self.assertEqual(len(u), len(self.items) + len(self.items2)) self.items2.pop() gc.collect() self.assertEqual(len(u), len(self.items) + len(self.items2)) def test_or(self): i = self.s.union(self.items2) self.assertEqual(self.s | set(self.items2), i) self.assertEqual(self.s | frozenset(self.items2), i) def test_intersection(self): s = WeakSet(self.letters) i = s.intersection(self.items2) for c in self.letters: self.assertEqual(c in i, c in self.items2 and c in self.letters) self.assertEqual(s, WeakSet(self.letters)) self.assertEqual(type(i), WeakSet) for C in (set, frozenset, dict.fromkeys, list, tuple): x = WeakSet([]) self.assertEqual(i.intersection(C(self.items)), x) self.assertEqual(len(i), len(self.items2)) self.items2.pop() gc.collect() self.assertEqual(len(i), len(self.items2)) def test_isdisjoint(self): self.assertTrue(self.s.isdisjoint(WeakSet(self.items2))) self.assertTrue(not self.s.isdisjoint(WeakSet(self.letters))) def test_and(self): i = self.s.intersection(self.items2) self.assertEqual(self.s & set(self.items2), i) self.assertEqual(self.s & frozenset(self.items2), i) def test_difference(self): i = self.s.difference(self.items2) for c in self.letters: self.assertEqual(c in i, c in self.d and c not in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) self.assertRaises(TypeError, self.s.difference, [[]]) def test_sub(self): i = self.s.difference(self.items2) self.assertEqual(self.s - set(self.items2), i) self.assertEqual(self.s - frozenset(self.items2), i) def test_symmetric_difference(self): i = self.s.symmetric_difference(self.items2) for c in self.letters: self.assertEqual(c in i, (c in self.d) ^ (c in self.items2)) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) self.assertRaises(TypeError, self.s.symmetric_difference, [[]]) self.assertEqual(len(i), len(self.items) + len(self.items2)) self.items2.pop() gc.collect() self.assertEqual(len(i), len(self.items) + len(self.items2)) def test_xor(self): i = self.s.symmetric_difference(self.items2) self.assertEqual(self.s ^ set(self.items2), i) self.assertEqual(self.s ^ frozenset(self.items2), i) def test_sub_and_super(self): self.assertTrue(self.ab_weakset <= self.abcde_weakset) self.assertTrue(self.abcde_weakset <= self.abcde_weakset) self.assertTrue(self.abcde_weakset >= self.ab_weakset) self.assertFalse(self.abcde_weakset <= self.def_weakset) self.assertFalse(self.abcde_weakset >= self.def_weakset) self.assertTrue(set('a').issubset('abc')) self.assertTrue(set('abc').issuperset('a')) self.assertFalse(set('a').issubset('cbs')) self.assertFalse(set('cbs').issuperset('a')) def test_lt(self): self.assertTrue(self.ab_weakset < self.abcde_weakset) self.assertFalse(self.abcde_weakset < self.def_weakset) self.assertFalse(self.ab_weakset < self.ab_weakset) self.assertFalse(WeakSet() < WeakSet()) def test_gt(self): self.assertTrue(self.abcde_weakset > self.ab_weakset) self.assertFalse(self.abcde_weakset > self.def_weakset) self.assertFalse(self.ab_weakset > self.ab_weakset) self.assertFalse(WeakSet() > WeakSet()) def test_gc(self): s = WeakSet(Foo() for i in range(1000)) for elem in s: elem.cycle = s elem.sub = elem elem.set = WeakSet([elem]) def test_subclass_with_custom_hash(self): class H(WeakSet): def __hash__(self): return int(id(self) & 2147483647) s = H() f = set() f.add(s) self.assertIn(s, f) f.remove(s) f.add(s) f.discard(s) def test_init(self): s = WeakSet() s.__init__(self.items) self.assertEqual(s, self.s) s.__init__(self.items2) self.assertEqual(s, WeakSet(self.items2)) self.assertRaises(TypeError, s.__init__, s, 2) self.assertRaises(TypeError, s.__init__, 1) def test_constructor_identity(self): s = WeakSet(self.items) t = WeakSet(s) self.assertNotEqual(id(s), id(t)) def test_hash(self): self.assertRaises(TypeError, hash, self.s) def test_clear(self): self.s.clear() self.assertEqual(self.s, WeakSet([])) self.assertEqual(len(self.s), 0) def test_copy(self): dup = self.s.copy() self.assertEqual(self.s, dup) self.assertNotEqual(id(self.s), id(dup)) def test_add(self): x = ustr('Q') self.s.add(x) self.assertIn(x, self.s) dup = self.s.copy() self.s.add(x) self.assertEqual(self.s, dup) self.assertRaises(TypeError, self.s.add, []) self.fs.add(Foo()) self.assertTrue(len(self.fs) == 1) self.fs.add(self.obj) self.assertTrue(len(self.fs) == 1) def test_remove(self): x = ustr('a') self.s.remove(x) self.assertNotIn(x, self.s) self.assertRaises(KeyError, self.s.remove, x) self.assertRaises(TypeError, self.s.remove, []) def test_discard(self): a, q = ustr('a'), ustr('Q') self.s.discard(a) self.assertNotIn(a, self.s) self.s.discard(q) self.assertRaises(TypeError, self.s.discard, []) def test_pop(self): for i in range(len(self.s)): elem = self.s.pop() self.assertNotIn(elem, self.s) self.assertRaises(KeyError, self.s.pop) def test_update(self): retval = self.s.update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): self.assertIn(c, self.s) self.assertRaises(TypeError, self.s.update, [[]]) def test_update_set(self): self.s.update(set(self.items2)) for c in (self.items + self.items2): self.assertIn(c, self.s) def test_ior(self): self.s |= set(self.items2) for c in (self.items + self.items2): self.assertIn(c, self.s) def test_intersection_update(self): retval = self.s.intersection_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if c in self.items2 and c in self.items: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) self.assertRaises(TypeError, self.s.intersection_update, [[]]) def test_iand(self): self.s &= set(self.items2) for c in (self.items + self.items2): if c in self.items2 and c in self.items: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) def test_difference_update(self): retval = self.s.difference_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if c in self.items and c not in self.items2: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) self.assertRaises(TypeError, self.s.difference_update, [[]]) self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) def test_isub(self): self.s -= set(self.items2) for c in (self.items + self.items2): if c in self.items and c not in self.items2: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) def test_symmetric_difference_update(self): retval = self.s.symmetric_difference_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if (c in self.items) ^ (c in self.items2): self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) def test_ixor(self): self.s ^= set(self.items2) for c in (self.items + self.items2): if (c in self.items) ^ (c in self.items2): self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) def test_inplace_on_self(self): t = self.s.copy() t |= t self.assertEqual(t, self.s) t &= t self.assertEqual(t, self.s) t -= t self.assertEqual(t, WeakSet()) t = self.s.copy() t ^= t self.assertEqual(t, WeakSet()) def test_eq(self): self.assertTrue(self.s == self.s) self.assertTrue(self.s == WeakSet(self.items)) self.assertFalse(self.s == set(self.items)) self.assertFalse(self.s == list(self.items)) self.assertFalse(self.s == tuple(self.items)) self.assertFalse(self.s == WeakSet([Foo])) self.assertFalse(self.s == 1) def test_ne(self): self.assertTrue(self.s != set(self.items)) s1 = WeakSet() s2 = WeakSet() self.assertFalse(s1 != s2) def test_weak_destroy_while_iterating(self): items = [ustr(c) for c in ('a', 'b', 'c')] s = WeakSet(items) it = iter(s) next(it) del items[-1] gc.collect() self.assertIn(len(list(it)), [len(items), len(items) - 1]) del it self.assertEqual(len(s), len(items)) def test_weak_destroy_and_mutate_while_iterating(self): items = [ustr(c) for c in string.ascii_letters] s = WeakSet(items) @contextlib.contextmanager def testcontext(): try: it = iter(s) yielded = ustr(str(next(it))) u = ustr(str(items.pop())) if yielded == u: next(it) gc.collect() yield u finally: it = None with testcontext() as u: self.assertNotIn(u, s) with testcontext() as u: self.assertRaises(KeyError, s.remove, u) self.assertNotIn(u, s) with testcontext() as u: s.add(u) self.assertIn(u, s) t = s.copy() with testcontext() as u: s.update(t) self.assertEqual(len(s), len(t)) with testcontext() as u: s.clear() self.assertEqual(len(s), 0) def test_len_cycles(self): N = 20 items = [RefCycle() for i in range(N)] s = WeakSet(items) del items it = iter(s) try: next(it) except StopIteration: pass gc.collect() n1 = len(s) del it gc.collect() n2 = len(s) self.assertIn(n1, (0, 1)) self.assertEqual(n2, 0) def test_len_race(self): self.addCleanup(gc.set_threshold, *gc.get_threshold()) for th in range(1, 100): N = 20 gc.collect(0) gc.set_threshold(th, th, th) items = [RefCycle() for i in range(N)] s = WeakSet(items) del items it = iter(s) try: next(it) except StopIteration: pass n1 = len(s) del it n2 = len(s) self.assertGreaterEqual(n1, 0) self.assertLessEqual(n1, N) self.assertGreaterEqual(n2, 0) self.assertLessEqual(n2, n1)
class Signal(object): """ The Signalling class """ def __init__(self, optimized=False): self._functions = WeakSet() self._after_functions = WeakSet() self._methods = WeakKeyDictionary() self._after_methods = WeakKeyDictionary() self._optimized = optimized def __call__(self, *args, **kargs): res_list = [] # Call handler functions for func in self._functions: res = func(*args, **kargs) if res and self._optimized: return res res_list.append(res) # Call handler methods for obj, funcs in self._methods.items(): for func in funcs: res = func(obj, *args, **kargs) if res and self._optimized: return res res_list.append(res) for func in self._after_functions: res = func(*args, **kargs) if res and self._optimized: return res res_list.append(res) # Call handler methods for obj, funcs in self._after_methods.items(): for func in funcs: res = func(obj, *args, **kargs) if res and self._optimized: return res res_list.append(res) if self._optimized: return None return res_list def connect(self, slot): """ @slot: The method to be called on signal emission Connects to @slot """ if inspect.ismethod(slot): if slot.__self__ not in self._methods: self._methods[slot.__self__] = set() self._methods[slot.__self__].add(slot.__func__) else: self._functions.add(slot) def connect_after(self, slot): """ @slot: The method to be called at last stage of signal emission Connects to the signal after the signals has been handled by other connect callbacks. """ if inspect.ismethod(slot): if slot.__self__ not in self._after_methods: self._after_methods[slot.__self__] = set() self._after_methods[slot.__self__].add(slot.__func__) else: self._after_functions.add(slot) def disconnect(self, slot): """ Disconnect @slot from the signal """ if inspect.ismethod(slot): if slot.__self__ in self._methods: self._methods[slot.__self__].remove(slot.__func__) elif slot.__self__ in self._after_methods: self._after_methods[slot.__self__].remove(slot.__func__) else: if slot in self._functions: self._functions.remove(slot) elif slot in self._after_functions: self._after_functions.remove(slot) def clear(self): """ Cleanup the signal """ self._functions.clear() self._methods.clear() self._after_functions.clear() self._after_methods.clear()
class ElementObject: def __init__(self, parent, type, save_id=None): self.__parent = ref(parent) self.__type = type self.__data = type.ufl_type.build_default( ElementValueGenerator(parent, type)) self.__connections = [] self.__children = [] self.__diagrams = [] self.__visuals = WeakSet() self.__cache = ModelTemporaryDataCache(None) if save_id is None: self.__save_id = uuid4() else: self.__save_id = save_id def add_visual(self, visual): if visual.object is not self: raise Exception self.__visuals.add(visual) def remove_visual(self, visual): self.__visuals.remove(visual) @property def visuals(self): yield from self.__visuals @property def cache(self): return self.__cache @property def parent(self): return self.__parent() def change_parent(self, new_parent, new_index): if self.project is not new_parent and self.project is not new_parent.project: raise Exception self.__notify_node_change_parent(0) self.__parent().remove_child(self) self.__parent = ref(new_parent) self.__parent().add_child(self, new_index) self.__notify_node_change_children(0) @property def type(self): return self.__type @property def data(self): return self.__data @property def connections(self): yield from self.__connections def get_display_name(self): return self.__type.get_display_name(self) def create_appearance_object(self, ruler): return self.__type.create_appearance_object(self, ruler) def connect_with(self, connection_type, second_element, save_id=None): connection = ConnectionObject(connection_type, self, second_element, save_id) self.__connections.append(connection) if second_element is not self: second_element.__connections.append(connection) return connection def get_connections_to(self, element): for connection in self.__connections: if connection.is_connected_with(element): yield connection def add_connection(self, connection): if not connection.is_connected_with(self): raise Exception if connection in self.__connections: raise Exception self.__connections.append(connection) def remove_connection(self, connection): if not connection.is_connected_with(self): raise Exception if connection not in self.__connections: raise Exception self.__connections.remove(connection) def reconnect(self, connection): if connection in self.__connections: raise Exception if not connection.is_connected_with(self): raise Exception self.__connections.append(connection) other = connection.get_other_end(self) if other is not self: other.__connections.append(connection) def disconnect(self, connection): if connection not in self.__connections: raise Exception self.__connections.remove(connection) other = connection.get_other_end(self) if other is not self: other.__connections.remove(connection) @property def project(self): if isinstance(self.__parent(), ElementObject): return self.__parent().project else: return self.__parent() @property def children_count(self): return len(self.__children) @property def children(self): yield from self.__children @property def diagram_count(self): return len(self.__diagrams) @property def diagrams(self): yield from self.__diagrams @property def save_id(self): return self.__save_id def create_child_element(self, type, save_id=None): obj = ElementObject(self, type, save_id) self.__children.append(obj) self.__notify_node_change_parent(0) self.__notify_node_change_children(0) return obj def create_child_diagram(self, type, save_id=None): from ..diagram import Diagram # circular imports diagram = Diagram(self, type, save_id) self.__diagrams.append(diagram) return diagram def get_child_index(self, obj): if isinstance(obj, ElementObject): return self.__children.index(obj) else: return self.__diagrams.index(obj) def add_child(self, obj, index=None): if obj.parent is not self: raise Exception if isinstance(obj, ElementObject): if obj in self.__children: raise Exception if index is None: self.__children.append(obj) else: self.__children.insert(index, obj) self.__notify_node_change_parent(0) self.__notify_node_change_children(0) else: if obj in self.__diagrams: raise Exception if index is None: self.__diagrams.append(obj) else: self.__diagrams.insert(index, obj) def remove_child(self, obj): if obj.parent is not self: raise Exception if isinstance(obj, ElementObject): if obj not in self.__children: raise Exception self.__children.remove(obj) self.__notify_node_change_parent(0) self.__notify_node_change_children(0) else: if obj not in self.__diagrams: raise Exception self.__diagrams.remove(obj) def apply_ufl_patch(self, patch): self.__data.apply_patch(patch) self.__cache.refresh() @property def has_ufl_dialog(self): return self.__type.ufl_type.has_attributes def create_ufl_dialog(self, options=UflDialogOptions.standard): if not self.__type.ufl_type.has_attributes: raise Exception dialog = UflDialog(self.__type.ufl_type, options) dialog.associate(self.__data) return dialog def __notify_node_change_parent(self, depth): if depth <= self.__type.node_access_depth.parent: self.__cache.refresh() parent = self.__parent() if isinstance(parent, ElementObject): parent.__notify_node_change_parent(depth + 1) def __notify_node_change_children(self, depth): if depth <= self.__type.node_access_depth.child: self.__cache.refresh() for child in self.__children: child.__notify_node_change_children(depth + 1) def invalidate_all_caches(self): self.__cache.invalidate() for element in self.__children: element.invalidate_all_caches() for connection in self.__connections: connection.cache.invalidate()
class Signal: ## Signal types. # These indicate the type of a signal, that is, how the signal handles calling the connected # slots. # - Direct connections immediately call the connected slots from the thread that called emit(). # - Auto connections will push the call onto the event loop if the current thread is # not the main thread, but make a direct call if it is. # - Queued connections will always push # the call on to the event loop. Direct = 1 Auto = 2 Queued = 3 ## Initialize the instance. # # \param kwargs Keyword arguments. # Possible keywords: # - type: The signal type. Defaults to Direct. def __init__(self, **kwargs): self.__functions = WeakSet() self.__methods = WeakKeyDictionary() self.__signals = WeakSet() self.__type = kwargs.get("type", Signal.Auto) self.__emitting = False self.__connect_queue = [] self.__disconnect_queue = [] ## \exception NotImplementedError def __call__(self): raise NotImplementedError("Call emit() to emit a signal") ## Get type of the signal # \return \type{int} Direct(1), Auto(2) or Queued(3) def getType(self): return self.__type ## Emit the signal which indirectly calls all of the connected slots. # # \param args The positional arguments to pass along. # \param kargs The keyword arguments to pass along. # # \note If the Signal type is Queued and this is not called from the application thread # the call will be posted as an event to the application main thread, which means the # function will be called on the next application event loop tick. def emit(self, *args, **kargs): try: if self.__type == Signal.Queued: Signal._app.functionEvent( CallFunctionEvent(self.emit, args, kargs)) return if self.__type == Signal.Auto: if threading.current_thread() is not Signal._app.getMainThread( ): Signal._app.functionEvent( CallFunctionEvent(self.emit, args, kargs)) return except AttributeError: # If Signal._app is not set return self.__emitting = True # Call handler functions for func in self.__functions: func(*args, **kargs) # Call handler methods for dest, funcs in self.__methods.items(): for func in funcs: func(dest, *args, **kargs) # Emit connected signals for signal in self.__signals: signal.emit(*args, **kargs) self.__emitting = False for connector in self.__connect_queue: self.connect(connector) self.__connect_queue.clear() for connector in self.__disconnect_queue: self.disconnect(connector) self.__connect_queue.clear() ## Connect to this signal. # \param connector The signal or slot (function) to connect. def connect(self, connector): if self.__emitting: # When we try to connect to a signal we change the dictionary of connectors. # This will cause an Exception since we are iterating over a dictionary that changed. # So instead, defer the connections until after we are done emitting. self.__connect_queue.append(connector) return if type(connector) == Signal: if connector == self: return self.__signals.add(connector) elif inspect.ismethod(connector): if connector.__self__ not in self.__methods: self.__methods[connector.__self__] = set() self.__methods[connector.__self__].add(connector.__func__) else: self.__functions.add(connector) ## Disconnect from this signal. # \param connector The signal or slot (function) to disconnect. def disconnect(self, connector): if self.__emitting: # See above. self.__disconnect_queue.append(connector) return try: if connector in self.__signals: self.__signals.remove(connector) elif inspect.ismethod( connector) and connector.__self__ in self.__methods: self.__methods[connector.__self__].remove(connector.__func__) else: if connector in self.__functions: self.__functions.remove(connector) except KeyError: #Ignore errors when connector is not connected to this signal. pass ## Disconnect all connected slots. def disconnectAll(self): if self.__emitting: raise RuntimeError( "Tried to disconnect signal while signal is being emitted") self.__functions.clear() self.__methods.clear() self.__signals.clear() ## private: # To avoid circular references when importing Application, this should be # set by the Application instance. _app = None
class TestWeakSet(unittest.TestCase): def setUp(self): # need to keep references to them self.items = [ustr(c) for c in ('a', 'b', 'c')] self.items2 = [ustr(c) for c in ('x', 'y', 'z')] self.letters = [ustr(c) for c in string.ascii_letters] self.s = WeakSet(self.items) self.d = dict.fromkeys(self.items) self.obj = ustr('F') self.fs = WeakSet([self.obj]) def test_methods(self): weaksetmethods = dir(WeakSet) for method in dir(set): if method == 'test_c_api' or method.startswith('_'): continue self.assert_(method in weaksetmethods, "WeakSet missing method " + method) def test_new_or_init(self): self.assertRaises(TypeError, WeakSet, [], 2) def test_len(self): self.assertEqual(len(self.s), len(self.d)) self.assertEqual(len(self.fs), 1) del self.obj self.assertEqual(len(self.fs), 0) def test_contains(self): for c in self.letters: self.assertEqual(c in self.s, c in self.d) self.assertRaises(TypeError, self.s.__contains__, [[]]) self.assert_(self.obj in self.fs) del self.obj self.assert_(ustr('F') not in self.fs) def test_union(self): u = self.s.union(self.items2) for c in self.letters: self.assertEqual(c in u, c in self.d or c in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(u), WeakSet) self.assertRaises(TypeError, self.s.union, [[]]) for C in set, frozenset, dict.fromkeys, list, tuple: x = WeakSet(self.items + self.items2) c = C(self.items2) self.assertEqual(self.s.union(c), x) def test_or(self): i = self.s.union(self.items2) self.assertEqual(self.s | set(self.items2), i) self.assertEqual(self.s | frozenset(self.items2), i) def test_intersection(self): i = self.s.intersection(self.items2) for c in self.letters: self.assertEqual(c in i, c in self.d and c in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) for C in set, frozenset, dict.fromkeys, list, tuple: x = WeakSet([]) self.assertEqual(self.s.intersection(C(self.items2)), x) def test_isdisjoint(self): self.assert_(self.s.isdisjoint(WeakSet(self.items2))) self.assert_(not self.s.isdisjoint(WeakSet(self.letters))) def test_and(self): i = self.s.intersection(self.items2) self.assertEqual(self.s & set(self.items2), i) self.assertEqual(self.s & frozenset(self.items2), i) def test_difference(self): i = self.s.difference(self.items2) for c in self.letters: self.assertEqual(c in i, c in self.d and c not in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) self.assertRaises(TypeError, self.s.difference, [[]]) def test_sub(self): i = self.s.difference(self.items2) self.assertEqual(self.s - set(self.items2), i) self.assertEqual(self.s - frozenset(self.items2), i) def test_symmetric_difference(self): i = self.s.symmetric_difference(self.items2) for c in self.letters: self.assertEqual(c in i, (c in self.d) ^ (c in self.items2)) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) self.assertRaises(TypeError, self.s.symmetric_difference, [[]]) def test_xor(self): i = self.s.symmetric_difference(self.items2) self.assertEqual(self.s ^ set(self.items2), i) self.assertEqual(self.s ^ frozenset(self.items2), i) def test_sub_and_super(self): pl, ql, rl = map(lambda s: [ustr(c) for c in s], ['ab', 'abcde', 'def']) p, q, r = map(WeakSet, (pl, ql, rl)) self.assert_(p < q) self.assert_(p <= q) self.assert_(q <= q) self.assert_(q > p) self.assert_(q >= p) self.failIf(q < r) self.failIf(q <= r) self.failIf(q > r) self.failIf(q >= r) self.assert_(set('a').issubset('abc')) self.assert_(set('abc').issuperset('a')) self.failIf(set('a').issubset('cbs')) self.failIf(set('cbs').issuperset('a')) def test_gc(self): # Create a nest of cycles to exercise overall ref count check class A: pass s = set(A() for i in range(1000)) for elem in s: elem.cycle = s elem.sub = elem elem.set = set([elem]) def test_subclass_with_custom_hash(self): # Bug #1257731 class H(WeakSet): def __hash__(self): return int(id(self) & 0x7fffffff) s=H() f=set() f.add(s) self.assert_(s in f) f.remove(s) f.add(s) f.discard(s) def test_init(self): s = WeakSet() s.__init__(self.items) self.assertEqual(s, self.s) s.__init__(self.items2) self.assertEqual(s, WeakSet(self.items2)) self.assertRaises(TypeError, s.__init__, s, 2); self.assertRaises(TypeError, s.__init__, 1); def test_constructor_identity(self): s = WeakSet(self.items) t = WeakSet(s) self.assertNotEqual(id(s), id(t)) def test_set_literal(self): s = set([1,2,3]) t = {1,2,3} self.assertEqual(s, t) def test_hash(self): self.assertRaises(TypeError, hash, self.s) def test_clear(self): self.s.clear() self.assertEqual(self.s, set()) self.assertEqual(len(self.s), 0) def test_copy(self): dup = self.s.copy() self.assertEqual(self.s, dup) self.assertNotEqual(id(self.s), id(dup)) def test_add(self): x = ustr('Q') self.s.add(x) self.assert_(x in self.s) dup = self.s.copy() self.s.add(x) self.assertEqual(self.s, dup) self.assertRaises(TypeError, self.s.add, []) self.fs.add(Foo()) self.assert_(len(self.fs) == 1) self.fs.add(self.obj) self.assert_(len(self.fs) == 1) def test_remove(self): x = ustr('a') self.s.remove(x) self.assert_(x not in self.s) self.assertRaises(KeyError, self.s.remove, x) self.assertRaises(TypeError, self.s.remove, []) def test_discard(self): a, q = ustr('a'), ustr('Q') self.s.discard(a) self.assert_(a not in self.s) self.s.discard(q) self.assertRaises(TypeError, self.s.discard, []) def test_pop(self): for i in range(len(self.s)): elem = self.s.pop() self.assert_(elem not in self.s) self.assertRaises(KeyError, self.s.pop) def test_update(self): retval = self.s.update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): self.assert_(c in self.s) self.assertRaises(TypeError, self.s.update, [[]]) def test_update_set(self): self.s.update(set(self.items2)) for c in (self.items + self.items2): self.assert_(c in self.s) def test_ior(self): self.s |= set(self.items2) for c in (self.items + self.items2): self.assert_(c in self.s) def test_intersection_update(self): retval = self.s.intersection_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if c in self.items2 and c in self.items: self.assert_(c in self.s) else: self.assert_(c not in self.s) self.assertRaises(TypeError, self.s.intersection_update, [[]]) def test_iand(self): self.s &= set(self.items2) for c in (self.items + self.items2): if c in self.items2 and c in self.items: self.assert_(c in self.s) else: self.assert_(c not in self.s) def test_difference_update(self): retval = self.s.difference_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if c in self.items and c not in self.items2: self.assert_(c in self.s) else: self.assert_(c not in self.s) self.assertRaises(TypeError, self.s.difference_update, [[]]) self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) def test_isub(self): self.s -= set(self.items2) for c in (self.items + self.items2): if c in self.items and c not in self.items2: self.assert_(c in self.s) else: self.assert_(c not in self.s) def test_symmetric_difference_update(self): retval = self.s.symmetric_difference_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if (c in self.items) ^ (c in self.items2): self.assert_(c in self.s) else: self.assert_(c not in self.s) self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) def test_ixor(self): self.s ^= set(self.items2) for c in (self.items + self.items2): if (c in self.items) ^ (c in self.items2): self.assert_(c in self.s) else: self.assert_(c not in self.s) def test_inplace_on_self(self): t = self.s.copy() t |= t self.assertEqual(t, self.s) t &= t self.assertEqual(t, self.s) t -= t self.assertEqual(t, WeakSet()) t = self.s.copy() t ^= t self.assertEqual(t, WeakSet())
class Signal: ## Signal types. # These indicate the type of a signal, that is, how the signal handles calling the connected # slots. # - Direct connections immediately call the connected slots from the thread that called emit(). # - Auto connections will push the call onto the event loop if the current thread is # not the main thread, but make a direct call if it is. # - Queued connections will always push # the call on to the event loop. Direct = 1 Auto = 2 Queued = 3 ## Initialize the instance. # # \param kwargs Keyword arguments. # Possible keywords: # - type: The signal type. Defaults to Direct. def __init__(self, **kwargs): self.__functions = WeakSet() self.__methods = WeakKeyDictionary() self.__signals = WeakSet() self.__type = kwargs.get("type", Signal.Auto) ## \exception NotImplementedError def __call__(self): raise NotImplementedError("Call emit() to emit a signal") ## Get type of the signal # \return \type{int} Direct(1), Auto(2) or Queued(3) def getType(self): return self.__type ## Emit the signal which indirectly calls all of the connected slots. # # \param args The positional arguments to pass along. # \param kargs The keyword arguments to pass along. # # \note If the Signal type is Queued and this is not called from the application thread # the call will be posted as an event to the application main thread, which means the # function will be called on the next application event loop tick. def emit(self, *args, **kargs): try: if self.__type == Signal.Queued: Signal._app.functionEvent(CallFunctionEvent(self.emit, args, kargs)) return if self.__type == Signal.Auto: if threading.current_thread() is not Signal._app.getMainThread(): Signal._app.functionEvent(CallFunctionEvent(self.emit, args, kargs)) return except AttributeError: # If Signal._app is not set return # Call handler functions for func in self.__functions: func(*args, **kargs) # Call handler methods for dest, funcs in self.__methods.items(): for func in funcs: func(dest, *args, **kargs) # Emit connected signals for signal in self.__signals: signal.emit(*args, **kargs) ## Connect to this signal. # \param connector The signal or slot (function) to connect. def connect(self, connector): if type(connector) == Signal: if connector == self: return self.__signals.add(connector) elif inspect.ismethod(connector): if connector.__self__ not in self.__methods: self.__methods[connector.__self__] = set() self.__methods[connector.__self__].add(connector.__func__) else: self.__functions.add(connector) ## Disconnect from this signal. # \param connector The signal or slot (function) to disconnect. def disconnect(self, connector): try: if connector in self.__signals: self.__signals.remove(connector) elif inspect.ismethod(connector) and connector.__self__ in self.__methods: self.__methods[connector.__self__].remove(connector.__func__) else: if connector in self.__functions: self.__functions.remove(connector) except KeyError: #Ignore errors when connector is not connected to this signal. pass ## Disconnect all connected slots. def disconnectAll(self): self.__functions.clear() self.__methods.clear() self.__signals.clear() ## private: # To avoid circular references when importing Application, this should be # set by the Application instance. _app = None
class TestWeakSet(unittest.TestCase): def setUp(self): # need to keep references to them self.items = [ustr(c) for c in ("a", "b", "c")] self.items2 = [ustr(c) for c in ("x", "y", "z")] self.letters = [ustr(c) for c in string.ascii_letters] self.s = WeakSet(self.items) self.d = dict.fromkeys(self.items) self.obj = ustr("F") self.fs = WeakSet([self.obj]) def test_methods(self): weaksetmethods = dir(WeakSet) for method in dir(set): if method == "test_c_api" or method.startswith("_"): continue self.assert_(method in weaksetmethods, "WeakSet missing method " + method) def test_new_or_init(self): self.assertRaises(TypeError, WeakSet, [], 2) def test_len(self): self.assertEqual(len(self.s), len(self.d)) self.assertEqual(len(self.fs), 1) del self.obj self.assertEqual(len(self.fs), 0) def test_contains(self): for c in self.letters: self.assertEqual(c in self.s, c in self.d) self.assertRaises(TypeError, self.s.__contains__, [[]]) self.assert_(self.obj in self.fs) del self.obj self.assert_(ustr("F") not in self.fs) def test_union(self): u = self.s.union(self.items2) for c in self.letters: self.assertEqual(c in u, c in self.d or c in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(u), WeakSet) self.assertRaises(TypeError, self.s.union, [[]]) for C in set, frozenset, dict.fromkeys, list, tuple: x = WeakSet(self.items + self.items2) c = C(self.items2) self.assertEqual(self.s.union(c), x) def test_or(self): i = self.s.union(self.items2) self.assertEqual(self.s | set(self.items2), i) self.assertEqual(self.s | frozenset(self.items2), i) def test_intersection(self): i = self.s.intersection(self.items2) for c in self.letters: self.assertEqual(c in i, c in self.d and c in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) for C in set, frozenset, dict.fromkeys, list, tuple: x = WeakSet([]) self.assertEqual(self.s.intersection(C(self.items2)), x) def test_isdisjoint(self): self.assert_(self.s.isdisjoint(WeakSet(self.items2))) self.assert_(not self.s.isdisjoint(WeakSet(self.letters))) def test_and(self): i = self.s.intersection(self.items2) self.assertEqual(self.s & set(self.items2), i) self.assertEqual(self.s & frozenset(self.items2), i) def test_difference(self): i = self.s.difference(self.items2) for c in self.letters: self.assertEqual(c in i, c in self.d and c not in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) self.assertRaises(TypeError, self.s.difference, [[]]) def test_sub(self): i = self.s.difference(self.items2) self.assertEqual(self.s - set(self.items2), i) self.assertEqual(self.s - frozenset(self.items2), i) def test_symmetric_difference(self): i = self.s.symmetric_difference(self.items2) for c in self.letters: self.assertEqual(c in i, (c in self.d) ^ (c in self.items2)) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) self.assertRaises(TypeError, self.s.symmetric_difference, [[]]) def test_xor(self): i = self.s.symmetric_difference(self.items2) self.assertEqual(self.s ^ set(self.items2), i) self.assertEqual(self.s ^ frozenset(self.items2), i) def test_sub_and_super(self): pl, ql, rl = map(lambda s: [ustr(c) for c in s], ["ab", "abcde", "def"]) p, q, r = map(WeakSet, (pl, ql, rl)) self.assert_(p < q) self.assert_(p <= q) self.assert_(q <= q) self.assert_(q > p) self.assert_(q >= p) self.failIf(q < r) self.failIf(q <= r) self.failIf(q > r) self.failIf(q >= r) self.assert_(set("a").issubset("abc")) self.assert_(set("abc").issuperset("a")) self.failIf(set("a").issubset("cbs")) self.failIf(set("cbs").issuperset("a")) def test_gc(self): # Create a nest of cycles to exercise overall ref count check s = WeakSet(Foo() for i in range(1000)) for elem in s: elem.cycle = s elem.sub = elem elem.set = WeakSet([elem]) def test_subclass_with_custom_hash(self): # Bug #1257731 class H(WeakSet): def __hash__(self): return int(id(self) & 0x7FFFFFFF) s = H() f = set() f.add(s) self.assert_(s in f) f.remove(s) f.add(s) f.discard(s) def test_init(self): s = WeakSet() s.__init__(self.items) self.assertEqual(s, self.s) s.__init__(self.items2) self.assertEqual(s, WeakSet(self.items2)) self.assertRaises(TypeError, s.__init__, s, 2) self.assertRaises(TypeError, s.__init__, 1) def test_constructor_identity(self): s = WeakSet(self.items) t = WeakSet(s) self.assertNotEqual(id(s), id(t)) def test_hash(self): self.assertRaises(TypeError, hash, self.s) def test_clear(self): self.s.clear() self.assertEqual(self.s, WeakSet([])) self.assertEqual(len(self.s), 0) def test_copy(self): dup = self.s.copy() self.assertEqual(self.s, dup) self.assertNotEqual(id(self.s), id(dup)) def test_add(self): x = ustr("Q") self.s.add(x) self.assert_(x in self.s) dup = self.s.copy() self.s.add(x) self.assertEqual(self.s, dup) self.assertRaises(TypeError, self.s.add, []) self.fs.add(Foo()) self.assert_(len(self.fs) == 1) self.fs.add(self.obj) self.assert_(len(self.fs) == 1) def test_remove(self): x = ustr("a") self.s.remove(x) self.assert_(x not in self.s) self.assertRaises(KeyError, self.s.remove, x) self.assertRaises(TypeError, self.s.remove, []) def test_discard(self): a, q = ustr("a"), ustr("Q") self.s.discard(a) self.assert_(a not in self.s) self.s.discard(q) self.assertRaises(TypeError, self.s.discard, []) def test_pop(self): for i in range(len(self.s)): elem = self.s.pop() self.assert_(elem not in self.s) self.assertRaises(KeyError, self.s.pop) def test_update(self): retval = self.s.update(self.items2) self.assertEqual(retval, None) for c in self.items + self.items2: self.assert_(c in self.s) self.assertRaises(TypeError, self.s.update, [[]]) def test_update_set(self): self.s.update(set(self.items2)) for c in self.items + self.items2: self.assert_(c in self.s) def test_ior(self): self.s |= set(self.items2) for c in self.items + self.items2: self.assert_(c in self.s) def test_intersection_update(self): retval = self.s.intersection_update(self.items2) self.assertEqual(retval, None) for c in self.items + self.items2: if c in self.items2 and c in self.items: self.assert_(c in self.s) else: self.assert_(c not in self.s) self.assertRaises(TypeError, self.s.intersection_update, [[]]) def test_iand(self): self.s &= set(self.items2) for c in self.items + self.items2: if c in self.items2 and c in self.items: self.assert_(c in self.s) else: self.assert_(c not in self.s) def test_difference_update(self): retval = self.s.difference_update(self.items2) self.assertEqual(retval, None) for c in self.items + self.items2: if c in self.items and c not in self.items2: self.assert_(c in self.s) else: self.assert_(c not in self.s) self.assertRaises(TypeError, self.s.difference_update, [[]]) self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) def test_isub(self): self.s -= set(self.items2) for c in self.items + self.items2: if c in self.items and c not in self.items2: self.assert_(c in self.s) else: self.assert_(c not in self.s) def test_symmetric_difference_update(self): retval = self.s.symmetric_difference_update(self.items2) self.assertEqual(retval, None) for c in self.items + self.items2: if (c in self.items) ^ (c in self.items2): self.assert_(c in self.s) else: self.assert_(c not in self.s) self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) def test_ixor(self): self.s ^= set(self.items2) for c in self.items + self.items2: if (c in self.items) ^ (c in self.items2): self.assert_(c in self.s) else: self.assert_(c not in self.s) def test_inplace_on_self(self): t = self.s.copy() t |= t self.assertEqual(t, self.s) t &= t self.assertEqual(t, self.s) t -= t self.assertEqual(t, WeakSet()) t = self.s.copy() t ^= t self.assertEqual(t, WeakSet()) def test_eq(self): # issue 5964 self.assertTrue(self.s == self.s) self.assertTrue(self.s == WeakSet(self.items)) self.assertFalse(self.s == set(self.items)) self.assertFalse(self.s == list(self.items)) self.assertFalse(self.s == tuple(self.items)) self.assertFalse(self.s == WeakSet([Foo])) self.assertFalse(self.s == 1)
class StreamInterfaceBase(object): def __init__(self, kernel, endpoint=None, handler=None): self.kernel = proxy(kernel) self.handler = handler self.meta = Metadata() s = self.create_socket(endpoint) self.listen_w = kernel.loop.io(s, pyev.EV_READ, self.on_accept, s) self.listen_w.start() self.timer_w = kernel.loop.timer(30, 30, self.on_timer) self.timer_w.start() self.clients = WeakSet() @property def alive(self): return self.listen_w is not None def create_socket(self, endpoint): pass def create_handler(self, sock, endpoint): h = self.handler(self.kernel, sock, endpoint) return h def getsocket(self): if self.listen_w: return self.listen_w.data def on_accept(self, watcher, revent): sock, endpoint = watcher.data.accept() try: sock.settimeout(3) handler = self.create_handler(sock, endpoint) self.clients.add(handler) except Exception: logger.exception("Unhandle error while create connection handler") sock.close() def on_timer(self, watcher, revent): zombie = [] for h in self.clients: if (not h.alive) or h.is_timeout: zombie.append(h) for h in zombie: if h.alive: logger.debug("%s connection timeout", h) h.close() else: logger.debug("Clean zombie connection %s", h) self.clients.remove(h) def close(self): if self.listen_w: self.timer_w.stop() self.timer_w = None self.listen_w.stop() self.listen_w.data.close() self.listen_w = None while self.clients: h = self.clients.pop() h.close()
class TestWeakSet(unittest.TestCase): def setUp(self): # need to keep references to them self.items = [ustr(c) for c in ('a', 'b', 'c')] self.items2 = [ustr(c) for c in ('x', 'y', 'z')] self.ab_items = [ustr(c) for c in 'ab'] self.abcde_items = [ustr(c) for c in 'abcde'] self.def_items = [ustr(c) for c in 'def'] self.ab_weakset = WeakSet(self.ab_items) self.abcde_weakset = WeakSet(self.abcde_items) self.def_weakset = WeakSet(self.def_items) self.letters = [ustr(c) for c in string.ascii_letters] self.s = WeakSet(self.items) self.d = dict.fromkeys(self.items) self.obj = ustr('F') self.fs = WeakSet([self.obj]) def test_methods(self): weaksetmethods = dir(WeakSet) for method in dir(set): if method == 'test_c_api' or method.startswith('_'): continue self.assertIn(method, weaksetmethods, "WeakSet missing method " + method) def test_new_or_init(self): self.assertRaises(TypeError, WeakSet, [], 2) @support.impl_detail("finalization", graalvm=False) def test_len(self): self.assertEqual(len(self.s), len(self.d)) self.assertEqual(len(self.fs), 1) del self.obj self.assertEqual(len(self.fs), 0) @support.impl_detail("finalization", graalvm=False) def test_contains(self): for c in self.letters: self.assertEqual(c in self.s, c in self.d) # 1 is not weakref'able, but that TypeError is caught by __contains__ self.assertNotIn(1, self.s) self.assertIn(self.obj, self.fs) del self.obj self.assertNotIn(ustr('F'), self.fs) @support.impl_detail("finalization", graalvm=False) def test_union(self): u = self.s.union(self.items2) for c in self.letters: self.assertEqual(c in u, c in self.d or c in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(u), WeakSet) self.assertRaises(TypeError, self.s.union, [[]]) for C in set, frozenset, dict.fromkeys, list, tuple: x = WeakSet(self.items + self.items2) c = C(self.items2) self.assertEqual(self.s.union(c), x) del c self.assertEqual(len(u), len(self.items) + len(self.items2)) self.items2.pop() gc.collect() self.assertEqual(len(u), len(self.items) + len(self.items2)) def test_or(self): i = self.s.union(self.items2) self.assertEqual(self.s | set(self.items2), i) self.assertEqual(self.s | frozenset(self.items2), i) @support.impl_detail("finalization", graalvm=False) def test_intersection(self): s = WeakSet(self.letters) i = s.intersection(self.items2) for c in self.letters: self.assertEqual(c in i, c in self.items2 and c in self.letters) self.assertEqual(s, WeakSet(self.letters)) self.assertEqual(type(i), WeakSet) for C in set, frozenset, dict.fromkeys, list, tuple: x = WeakSet([]) self.assertEqual(i.intersection(C(self.items)), x) self.assertEqual(len(i), len(self.items2)) self.items2.pop() gc.collect() self.assertEqual(len(i), len(self.items2)) def test_isdisjoint(self): self.assertTrue(self.s.isdisjoint(WeakSet(self.items2))) self.assertTrue(not self.s.isdisjoint(WeakSet(self.letters))) def test_and(self): i = self.s.intersection(self.items2) self.assertEqual(self.s & set(self.items2), i) self.assertEqual(self.s & frozenset(self.items2), i) def test_difference(self): i = self.s.difference(self.items2) for c in self.letters: self.assertEqual(c in i, c in self.d and c not in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) self.assertRaises(TypeError, self.s.difference, [[]]) def test_sub(self): i = self.s.difference(self.items2) self.assertEqual(self.s - set(self.items2), i) self.assertEqual(self.s - frozenset(self.items2), i) @support.impl_detail("finalization", graalvm=False) def test_symmetric_difference(self): i = self.s.symmetric_difference(self.items2) for c in self.letters: self.assertEqual(c in i, (c in self.d) ^ (c in self.items2)) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) self.assertRaises(TypeError, self.s.symmetric_difference, [[]]) self.assertEqual(len(i), len(self.items) + len(self.items2)) self.items2.pop() gc.collect() self.assertEqual(len(i), len(self.items) + len(self.items2)) def test_xor(self): i = self.s.symmetric_difference(self.items2) self.assertEqual(self.s ^ set(self.items2), i) self.assertEqual(self.s ^ frozenset(self.items2), i) def test_sub_and_super(self): self.assertTrue(self.ab_weakset <= self.abcde_weakset) self.assertTrue(self.abcde_weakset <= self.abcde_weakset) self.assertTrue(self.abcde_weakset >= self.ab_weakset) self.assertFalse(self.abcde_weakset <= self.def_weakset) self.assertFalse(self.abcde_weakset >= self.def_weakset) self.assertTrue(set('a').issubset('abc')) self.assertTrue(set('abc').issuperset('a')) self.assertFalse(set('a').issubset('cbs')) self.assertFalse(set('cbs').issuperset('a')) def test_lt(self): self.assertTrue(self.ab_weakset < self.abcde_weakset) self.assertFalse(self.abcde_weakset < self.def_weakset) self.assertFalse(self.ab_weakset < self.ab_weakset) self.assertFalse(WeakSet() < WeakSet()) def test_gt(self): self.assertTrue(self.abcde_weakset > self.ab_weakset) self.assertFalse(self.abcde_weakset > self.def_weakset) self.assertFalse(self.ab_weakset > self.ab_weakset) self.assertFalse(WeakSet() > WeakSet()) def test_gc(self): # Create a nest of cycles to exercise overall ref count check s = WeakSet(Foo() for i in range(1000)) for elem in s: elem.cycle = s elem.sub = elem elem.set = WeakSet([elem]) def test_subclass_with_custom_hash(self): # Bug #1257731 class H(WeakSet): def __hash__(self): return int(id(self) & 0x7fffffff) s=H() f=set() f.add(s) self.assertIn(s, f) f.remove(s) f.add(s) f.discard(s) def test_init(self): s = WeakSet() s.__init__(self.items) self.assertEqual(s, self.s) s.__init__(self.items2) self.assertEqual(s, WeakSet(self.items2)) self.assertRaises(TypeError, s.__init__, s, 2); self.assertRaises(TypeError, s.__init__, 1); def test_constructor_identity(self): s = WeakSet(self.items) t = WeakSet(s) self.assertNotEqual(id(s), id(t)) def test_hash(self): self.assertRaises(TypeError, hash, self.s) def test_clear(self): self.s.clear() self.assertEqual(self.s, WeakSet([])) self.assertEqual(len(self.s), 0) def test_copy(self): dup = self.s.copy() self.assertEqual(self.s, dup) self.assertNotEqual(id(self.s), id(dup)) @support.impl_detail("refcounting", graalvm=False) def test_add(self): x = ustr('Q') self.s.add(x) self.assertIn(x, self.s) dup = self.s.copy() self.s.add(x) self.assertEqual(self.s, dup) self.assertRaises(TypeError, self.s.add, []) self.fs.add(Foo()) self.assertTrue(len(self.fs) == 1) self.fs.add(self.obj) self.assertTrue(len(self.fs) == 1) def test_remove(self): x = ustr('a') self.s.remove(x) self.assertNotIn(x, self.s) self.assertRaises(KeyError, self.s.remove, x) self.assertRaises(TypeError, self.s.remove, []) def test_discard(self): a, q = ustr('a'), ustr('Q') self.s.discard(a) self.assertNotIn(a, self.s) self.s.discard(q) self.assertRaises(TypeError, self.s.discard, []) def test_pop(self): for i in range(len(self.s)): elem = self.s.pop() self.assertNotIn(elem, self.s) self.assertRaises(KeyError, self.s.pop) def test_update(self): retval = self.s.update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): self.assertIn(c, self.s) self.assertRaises(TypeError, self.s.update, [[]]) def test_update_set(self): self.s.update(set(self.items2)) for c in (self.items + self.items2): self.assertIn(c, self.s) def test_ior(self): self.s |= set(self.items2) for c in (self.items + self.items2): self.assertIn(c, self.s) def test_intersection_update(self): retval = self.s.intersection_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if c in self.items2 and c in self.items: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) self.assertRaises(TypeError, self.s.intersection_update, [[]]) def test_iand(self): self.s &= set(self.items2) for c in (self.items + self.items2): if c in self.items2 and c in self.items: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) def test_difference_update(self): retval = self.s.difference_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if c in self.items and c not in self.items2: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) self.assertRaises(TypeError, self.s.difference_update, [[]]) self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) def test_isub(self): self.s -= set(self.items2) for c in (self.items + self.items2): if c in self.items and c not in self.items2: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) def test_symmetric_difference_update(self): retval = self.s.symmetric_difference_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if (c in self.items) ^ (c in self.items2): self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) def test_ixor(self): self.s ^= set(self.items2) for c in (self.items + self.items2): if (c in self.items) ^ (c in self.items2): self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) def test_inplace_on_self(self): t = self.s.copy() t |= t self.assertEqual(t, self.s) t &= t self.assertEqual(t, self.s) t -= t self.assertEqual(t, WeakSet()) t = self.s.copy() t ^= t self.assertEqual(t, WeakSet()) def test_eq(self): # issue 5964 self.assertTrue(self.s == self.s) self.assertTrue(self.s == WeakSet(self.items)) self.assertFalse(self.s == set(self.items)) self.assertFalse(self.s == list(self.items)) self.assertFalse(self.s == tuple(self.items)) self.assertFalse(self.s == WeakSet([Foo])) self.assertFalse(self.s == 1) def test_ne(self): self.assertTrue(self.s != set(self.items)) s1 = WeakSet() s2 = WeakSet() self.assertFalse(s1 != s2) @support.impl_detail("finalization", graalvm=False) def test_weak_destroy_while_iterating(self): # Issue #7105: iterators shouldn't crash when a key is implicitly removed # Create new items to be sure no-one else holds a reference items = [ustr(c) for c in ('a', 'b', 'c')] s = WeakSet(items) it = iter(s) next(it) # Trigger internal iteration # Destroy an item del items[-1] gc.collect() # just in case # We have removed either the first consumed items, or another one self.assertIn(len(list(it)), [len(items), len(items) - 1]) del it # The removal has been committed self.assertEqual(len(s), len(items)) @support.impl_detail("finalization", graalvm=False) def test_weak_destroy_and_mutate_while_iterating(self): # Issue #7105: iterators shouldn't crash when a key is implicitly removed items = [ustr(c) for c in string.ascii_letters] s = WeakSet(items) @contextlib.contextmanager def testcontext(): try: it = iter(s) # Start iterator yielded = ustr(str(next(it))) # Schedule an item for removal and recreate it u = ustr(str(items.pop())) if yielded == u: # The iterator still has a reference to the removed item, # advance it (issue #20006). next(it) gc.collect() # just in case yield u finally: it = None # should commit all removals with testcontext() as u: self.assertNotIn(u, s) with testcontext() as u: self.assertRaises(KeyError, s.remove, u) self.assertNotIn(u, s) with testcontext() as u: s.add(u) self.assertIn(u, s) t = s.copy() with testcontext() as u: s.update(t) self.assertEqual(len(s), len(t)) with testcontext() as u: s.clear() self.assertEqual(len(s), 0) @support.impl_detail("finalization", graalvm=False) def test_len_cycles(self): N = 20 items = [RefCycle() for i in range(N)] s = WeakSet(items) del items it = iter(s) try: next(it) except StopIteration: pass gc.collect() n1 = len(s) del it gc.collect() n2 = len(s) # one item may be kept alive inside the iterator self.assertIn(n1, (0, 1)) self.assertEqual(n2, 0) @support.impl_detail("finalization", graalvm=False) def test_len_race(self): # Extended sanity checks for len() in the face of cyclic collection self.addCleanup(gc.set_threshold, *gc.get_threshold()) for th in range(1, 100): N = 20 gc.collect(0) gc.set_threshold(th, th, th) items = [RefCycle() for i in range(N)] s = WeakSet(items) del items # All items will be collected at next garbage collection pass it = iter(s) try: next(it) except StopIteration: pass n1 = len(s) del it n2 = len(s) self.assertGreaterEqual(n1, 0) self.assertLessEqual(n1, N) self.assertGreaterEqual(n2, 0) self.assertLessEqual(n2, n1) def test_repr(self): assert repr(self.s) == repr(self.s.data)
class Dataset(object): def __init__(self, name): self.name = name self.sources = [] self._observers = WeakSet() def __repr__(self): return self.name def add(self, datasource): if isinstance(datasource, DataSource): self.sources.append(datasource) else: for source in datasource: self.add(source) for o in self._observers: o(self) def remove(self, datasource): self.sources.remove(datasource) for o in self._observers: o(self) def count(self, lookup_unknown=True): total = 0 for datasource in self.sources: s = datasource.size(lookup=lookup_unknown) if s is not None: total += s return total def on_change(self, callback): self._observers.add(callback) def remove_on_change(self, callback): self._observers.remove(callback) def __iadd__(self, other): if isinstance(other, DataSource): self.add(other) if isinstance(other, Dataset): self.sources = self.sources + other.sources for o in self._observers: o(self) return self def __add__(self, other): newset = Dataset("%s + %s" % (self.name, other.name)) if isinstance(other, DataSource): newset.add(other) if isinstance(other, Dataset): newset.sources = self.sources + other.sources return newset @staticmethod def load(directory, indices=None, name=None, max_scenes=None, assume_same_frames=True, assume_same_shapes=True): import os from .fluidformat import Scene if name is None: name = os.path.basename(directory) dataset = Dataset(name) shape_map = dict() if assume_same_shapes else None frames = None indexfilter = None if indices is None else lambda i: i in indices scene_iterator = Scene.list(directory, max_count=max_scenes, indexfilter=indexfilter) for scene in scene_iterator: if assume_same_frames and frames is None: frames = scene.frames dataset.add(SceneSource(scene, frames=frames, shape_map=shape_map)) if dataset.count() == 0: raise ValueError("No data sets found in '%s' " % directory) return dataset
class AsyncPoolWrapper: def __init__(self, pool: SessionPool, loop: 'ProactorEventLoop' = None): if loop == None: loop = asyncio.get_running_loop() self._thread_pool = ThreadPoolExecutor( max_workers=max(DEFAULT_MAXIMUM_WORKER_NUM, pool.max << DEFAULT_MAXIMUM_WORKER_TIMES)) self._thread_pool.set_daemon_opts(min_workers=max(4, pool.min << 1)) self._loop = loop self._pool = pool self._occupied = WeakSet() def acquire(self): coro = self._loop.run_in_executor(self._thread_pool, self._acquire) return AsyncConnectionWrapper_context(coro) def _acquire(self): wrapper = AsyncConnectionWrapper(self._pool.acquire(), self._loop, self._thread_pool, self._pool, self) self._occupied.add(wrapper) return wrapper def _ofree(self, obj: AsyncConnectionWrapper): ''' A performance optimization tip: When there's no exception raised , `try` way perform 20%-30% faster than `if` way. If there do have a exception , `try` way will be 100% slower than `if` way , it takes about 500ns to recover the stack. So in this perticular situation when there's far more chance no exception raised rather than exception raised, use `try` provides better performance. ''' try: self._occupied.remove(obj) except: pass # if obj in self._occupied: # self._occupied.remove(obj) async def release(self, conn: AsyncConnectionWrapper): self._ofree(conn) return await self._loop.run_in_executor(self._thread_pool, self._pool.release, conn._conn) async def drop(self, conn: Connection): return await self._loop.run_in_executor(self._thread_pool, self._pool.drop, conn) async def close(self, force: bool = False, interrupt: bool = False): ''' WARNING: option `interrupt` will force cancel all running connections before close the pool. This may cause fetching thread no response forever in some legacy version of oracle database such as 11 or lower. Do make sure this option works fine with your working enviornment. ''' while self._occupied: wrapper = self._occupied.pop() if interrupt: await self._loop.run_in_executor(self._thread_pool, wrapper._conn.cancel) return await self._loop.run_in_executor(self._thread_pool, self._pool.close, force)
class Agent: def __init__(self, loop: AbstractEventLoop = None): self._loop = loop self._links = WeakSet() self._monitors = WeakSet() self._queue = Queue(loop=self._loop) self._task = asyncio.ensure_future(self._main(), loop=self._loop) @property def alive(self) -> bool: return self._task is not None @property def exited(self) -> bool: return self._task is None @property def loop(self) -> AbstractEventLoop: return self._loop @property def task(self) -> Task: return self._task def bidirectional_link(self, to: 'Agent'): self._links.add(to) to._links.add(self) def unlink(self, from_: 'Agent'): self._links.remove(from_) from_._links.remove(self) def monitor(self, monitored: 'Agent'): self._links.add(monitored) monitored._monitors.add(self) def spawn_linked_task(self, coro_or_future, unlink_on_success: bool = True) -> 'LinkedTask': return LinkedTask(self, coro_or_future, unlink_on_success=unlink_on_success, loop=self._loop) async def _main(self): # noinspection PyBroadException try: while self.alive: fun = await self._queue.get() await fun() except Exception as exc: self.exit(exc) else: self.exit(None) def exit(self, exc: Optional[Exception] = None): old_task = self._task try: if self.exited: return self._task = None if exc: logger.debug("%s is exiting because %s", self, exc) else: logger.debug("%s is exiting normally", self) for link in self._links: link.exit(exc) for monitor in self._monitors: method = getattr(monitor, 'on_monitored_exit', None) if method: method(self, exc) else: logger.warning("%s was monitoring an agent %s, but doesn't implemented on_monitored_exit", monitor, self) finally: if old_task is not None: old_task.cancel() self._links = WeakSet() self._monitors = WeakSet()
class TestWeakSet(unittest.TestCase): def setUp(self): # need to keep references to them self.items = [SomeClass(c) for c in ('a', 'b', 'c')] self.items2 = [SomeClass(c) for c in ('x', 'y', 'z')] self.letters = [SomeClass(c) for c in string.ascii_letters] self.s = WeakSet(self.items) self.d = dict.fromkeys(self.items) self.obj = SomeClass('F') self.fs = WeakSet([self.obj]) def test_methods(self): weaksetmethods = dir(WeakSet) for method in dir(set): if method == 'test_c_api' or method.startswith('_'): continue self.assertIn(method, weaksetmethods, "WeakSet missing method " + method) def test_new_or_init(self): self.assertRaises(TypeError, WeakSet, [], 2) def test_len(self): self.assertEqual(len(self.s), len(self.d)) self.assertEqual(len(self.fs), 1) del self.obj self.assertEqual(len(self.fs), 0) def test_contains(self): for c in self.letters: self.assertEqual(c in self.s, c in self.d) # 1 is not weakref'able, but that TypeError is caught by __contains__ self.assertNotIn(1, self.s) self.assertIn(self.obj, self.fs) del self.obj self.assertNotIn(SomeClass('F'), self.fs) def test_union(self): u = self.s.union(self.items2) for c in self.letters: self.assertEqual(c in u, c in self.d or c in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(u), WeakSet) self.assertRaises(TypeError, self.s.union, [[]]) for C in set, frozenset, dict.fromkeys, list, tuple: x = WeakSet(self.items + self.items2) c = C(self.items2) self.assertEqual(self.s.union(c), x) def test_or(self): i = self.s.union(self.items2) self.assertEqual(self.s | set(self.items2), i) self.assertEqual(self.s | frozenset(self.items2), i) def test_intersection(self): i = self.s.intersection(self.items2) for c in self.letters: self.assertEqual(c in i, c in self.d and c in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) for C in set, frozenset, dict.fromkeys, list, tuple: x = WeakSet([]) self.assertEqual(self.s.intersection(C(self.items2)), x) def test_isdisjoint(self): self.assertTrue(self.s.isdisjoint(WeakSet(self.items2))) self.assertTrue(not self.s.isdisjoint(WeakSet(self.letters))) def test_and(self): i = self.s.intersection(self.items2) self.assertEqual(self.s & set(self.items2), i) self.assertEqual(self.s & frozenset(self.items2), i) def test_difference(self): i = self.s.difference(self.items2) for c in self.letters: self.assertEqual(c in i, c in self.d and c not in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) self.assertRaises(TypeError, self.s.difference, [[]]) def test_sub(self): i = self.s.difference(self.items2) self.assertEqual(self.s - set(self.items2), i) self.assertEqual(self.s - frozenset(self.items2), i) def test_symmetric_difference(self): i = self.s.symmetric_difference(self.items2) for c in self.letters: self.assertEqual(c in i, (c in self.d) ^ (c in self.items2)) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) self.assertRaises(TypeError, self.s.symmetric_difference, [[]]) def test_xor(self): i = self.s.symmetric_difference(self.items2) self.assertEqual(self.s ^ set(self.items2), i) self.assertEqual(self.s ^ frozenset(self.items2), i) def test_sub_and_super(self): pl, ql, rl = map(lambda s: [SomeClass(c) for c in s], ['ab', 'abcde', 'def']) p, q, r = map(WeakSet, (pl, ql, rl)) self.assertTrue(p < q) self.assertTrue(p <= q) self.assertTrue(q <= q) self.assertTrue(q > p) self.assertTrue(q >= p) self.assertFalse(q < r) self.assertFalse(q <= r) self.assertFalse(q > r) self.assertFalse(q >= r) self.assertTrue(set('a').issubset('abc')) self.assertTrue(set('abc').issuperset('a')) self.assertFalse(set('a').issubset('cbs')) self.assertFalse(set('cbs').issuperset('a')) def test_gc(self): # Create a nest of cycles to exercise overall ref count check s = WeakSet(Foo() for i in range(1000)) for elem in s: elem.cycle = s elem.sub = elem elem.set = WeakSet([elem]) def test_subclass_with_custom_hash(self): # Bug #1257731 class H(WeakSet): def __hash__(self): return int(id(self) & 0x7fffffff) s=H() f=set() f.add(s) self.assertIn(s, f) f.remove(s) f.add(s) f.discard(s) def test_init(self): s = WeakSet() s.__init__(self.items) self.assertEqual(s, self.s) s.__init__(self.items2) self.assertEqual(s, WeakSet(self.items2)) self.assertRaises(TypeError, s.__init__, s, 2); self.assertRaises(TypeError, s.__init__, 1); def test_constructor_identity(self): s = WeakSet(self.items) t = WeakSet(s) self.assertNotEqual(id(s), id(t)) def test_hash(self): self.assertRaises(TypeError, hash, self.s) def test_clear(self): self.s.clear() self.assertEqual(self.s, WeakSet([])) self.assertEqual(len(self.s), 0) def test_copy(self): dup = self.s.copy() self.assertEqual(self.s, dup) self.assertNotEqual(id(self.s), id(dup)) def test_add(self): x = SomeClass('Q') self.s.add(x) self.assertIn(x, self.s) dup = self.s.copy() self.s.add(x) self.assertEqual(self.s, dup) self.assertRaises(TypeError, self.s.add, []) self.fs.add(Foo()) self.assertTrue(len(self.fs) == 1) self.fs.add(self.obj) self.assertTrue(len(self.fs) == 1) def test_remove(self): x = SomeClass('a') self.s.remove(x) self.assertNotIn(x, self.s) self.assertRaises(KeyError, self.s.remove, x) self.assertRaises(TypeError, self.s.remove, []) def test_discard(self): a, q = SomeClass('a'), SomeClass('Q') self.s.discard(a) self.assertNotIn(a, self.s) self.s.discard(q) self.assertRaises(TypeError, self.s.discard, []) def test_pop(self): for i in range(len(self.s)): elem = self.s.pop() self.assertNotIn(elem, self.s) self.assertRaises(KeyError, self.s.pop) def test_update(self): retval = self.s.update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): self.assertIn(c, self.s) self.assertRaises(TypeError, self.s.update, [[]]) def test_update_set(self): self.s.update(set(self.items2)) for c in (self.items + self.items2): self.assertIn(c, self.s) def test_ior(self): self.s |= set(self.items2) for c in (self.items + self.items2): self.assertIn(c, self.s) def test_intersection_update(self): retval = self.s.intersection_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if c in self.items2 and c in self.items: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) self.assertRaises(TypeError, self.s.intersection_update, [[]]) def test_iand(self): self.s &= set(self.items2) for c in (self.items + self.items2): if c in self.items2 and c in self.items: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) def test_difference_update(self): retval = self.s.difference_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if c in self.items and c not in self.items2: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) self.assertRaises(TypeError, self.s.difference_update, [[]]) self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) def test_isub(self): self.s -= set(self.items2) for c in (self.items + self.items2): if c in self.items and c not in self.items2: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) def test_symmetric_difference_update(self): retval = self.s.symmetric_difference_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if (c in self.items) ^ (c in self.items2): self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) def test_ixor(self): self.s ^= set(self.items2) for c in (self.items + self.items2): if (c in self.items) ^ (c in self.items2): self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) def test_inplace_on_self(self): t = self.s.copy() t |= t self.assertEqual(t, self.s) t &= t self.assertEqual(t, self.s) t -= t self.assertEqual(t, WeakSet()) t = self.s.copy() t ^= t self.assertEqual(t, WeakSet()) def test_eq(self): # issue 5964 self.assertTrue(self.s == self.s) self.assertTrue(self.s == WeakSet(self.items)) self.assertFalse(self.s == set(self.items)) self.assertFalse(self.s == list(self.items)) self.assertFalse(self.s == tuple(self.items)) self.assertFalse(self.s == 1) def test_weak_destroy_while_iterating(self): # Issue #7105: iterators shouldn't crash when a key is implicitly removed # Create new items to be sure no-one else holds a reference items = [SomeClass(c) for c in ('a', 'b', 'c')] s = WeakSet(items) it = iter(s) next(it) # Trigger internal iteration # Destroy an item del items[-1] gc.collect() # just in case # We have removed either the first consumed items, or another one self.assertIn(len(list(it)), [len(items), len(items) - 1]) del it # The removal has been committed self.assertEqual(len(s), len(items)) def test_weak_destroy_and_mutate_while_iterating(self): # Issue #7105: iterators shouldn't crash when a key is implicitly removed items = [SomeClass(c) for c in string.ascii_letters] s = WeakSet(items) @contextlib.contextmanager def testcontext(): try: it = iter(s) next(it) # Schedule an item for removal and recreate it u = SomeClass(str(items.pop())) gc.collect() # just in case yield u finally: it = None # should commit all removals with testcontext() as u: self.assertNotIn(u, s) with testcontext() as u: self.assertRaises(KeyError, s.remove, u) self.assertNotIn(u, s) with testcontext() as u: s.add(u) self.assertIn(u, s) t = s.copy() with testcontext() as u: s.update(t) self.assertEqual(len(s), len(t)) with testcontext() as u: s.clear() self.assertEqual(len(s), 0)
class Privacy(LineOfSight): _PRIVACY_SURFACE_BLOCKING_FOOTPRINT_COST = 100000 _PRIVACY_DISCOURAGEMENT_COST = routing.get_default_discouragement_cost() _SHOO_CONSTRAINT_RADIUS = Tunable(description='\n The radius of the constraint a Shooed Sim will attempt to route to.\n ', tunable_type=float, default=2.5) _UNAVAILABLE_TOOLTIP = TunableLocalizedStringFactory(description='\n Tooltip displayed when an object is not accessible due to being inside\n a privacy region.\n ') _EMBARRASSED_AFFORDANCE = TunableReference(description='\n The affordance a Sim will play when getting embarrassed by walking in\n on a privacy situation.\n ', manager=services.get_instance_manager(Types.INTERACTION)) def __init__(self, *, interaction=None, tests=None, shoo_exempt_tests=None, max_line_of_sight_radius=None, map_divisions=None, simplification_ratio=None, boundary_epsilon=None, facing_offset=None, routing_surface_only=None, shoo_constraint_radius=None, unavailable_tooltip=None, embarrassed_affordance=None, reserved_surface_space=None, vehicle_tests=None, central_object=None, post_route_affordance=None, add_to_privacy_service=True, privacy_cost_override=None, additional_exit_offsets=None, persistent_instance=False, privacy_violators=None): super().__init__(max_line_of_sight_radius, map_divisions, simplification_ratio, boundary_epsilon) logger.assert_raise(bool(interaction) != bool(central_object), 'Privacy must define either one of interaction or central object, and never both.') self._max_line_of_sight_radius = max_line_of_sight_radius self._interaction = interaction self._tests = tests self._shoo_exempt_tests = shoo_exempt_tests self._privacy_constraints = [] self._allowed_sims = WeakSet() self._disallowed_sims = WeakSet() self._violators = WeakSet() self._late_violators = WeakSet() self._exempt_sims = WeakSet() self.is_active = False self.has_shooed = False self.central_object = central_object self.additional_exit_offsets = additional_exit_offsets self._multi_surface = True self.persistent_instance = persistent_instance self._routing_surface_only = routing_surface_only self._shoo_constraint_radius = shoo_constraint_radius self._unavailable_tooltip = unavailable_tooltip self._embarrassed_affordance = embarrassed_affordance self._reserved_surface_space = reserved_surface_space self._post_route_affordance = post_route_affordance self._privacy_cost_override = privacy_cost_override self.privacy_violators = privacy_violators self._vehicle_tests = vehicle_tests self._pushed_interactions = [] if add_to_privacy_service: self.add_privacy() @property def shoo_constraint_radius(self): return self._shoo_constraint_radius or self._SHOO_CONSTRAINT_RADIUS @property def unavailable_tooltip(self): return self._unavailable_toolip or self._UNAVAILABLE_TOOLTIP @property def embarrassed_affordance(self): return self._embarrassed_affordance or self._EMBARRASSED_AFFORDANCE @property def privacy_discouragement_cost(self): return self._privacy_cost_override or self._PRIVACY_DISCOURAGEMENT_COST @property def interaction(self): return self._interaction @property def is_active(self) -> bool: return self._is_active @is_active.setter def is_active(self, value): self._is_active = value def _is_sim_allowed(self, sim): if self._tests: resolver = SingleSimResolver(sim.sim_info) if self._interaction is None else self._interaction.get_resolver(target=sim) if self._tests and self._tests.run_tests(resolver): return True elif self._interaction is not None and self._interaction.can_sim_violate_privacy(sim): return True if self._interaction is not None and self._interaction.can_sim_violate_privacy(sim): return True return False def evaluate_sim(self, sim): if self._is_sim_allowed(sim): self._allowed_sims.add(sim) return True self._disallowed_sims.add(sim) return False def build_privacy(self, target=None): self.is_active = True if self.central_object is None: target_object = self._interaction.get_participant(ParticipantType.Object) target_object = None if target_object.is_sim else target_object self.central_object = target_object or (target or self._interaction.sim) if self._routing_surface_only: allow_object_routing_surface = True routing_surface = self.central_object.provided_routing_surface if routing_surface is None: return False else: allow_object_routing_surface = False routing_surface = self.central_object.routing_surface self.generate(self.central_object.position, routing_surface, allow_object_routing_surface=allow_object_routing_surface) for poly in self.constraint.geometry.polygon: self._privacy_constraints.append(PolygonFootprint(poly, routing_surface=routing_surface, cost=self.privacy_discouragement_cost, footprint_type=FootprintType.FOOTPRINT_TYPE_PATH, enabled=True)) if self._reserved_surface_space is not None and target is not None: reserved_space = self._reserved_surface_space.reserved_space try: polygon = _generate_single_poly_rectangle_points(target.position, target.part_owner.orientation.transform_vector(sims4.math.Vector3.Z_AXIS()), target.part_owner.orientation.transform_vector(sims4.math.Vector3.X_AXIS()), reserved_space.left, reserved_space.right, reserved_space.front, reserved_space.back) except AttributeError as exc: raise AttributeError('Interaction: {} is trying to reserve surface space with sim as target. Exception:{}'.format(self._interaction, exc)) routing_surface = self.central_object.provided_routing_surface if routing_surface is None: routing_surface = target.routing_surface footprint_cost = self.privacy_discouragement_cost if self._reserved_surface_space.allow_routing else self._PRIVACY_SURFACE_BLOCKING_FOOTPRINT_COST self._privacy_constraints.append(PolygonFootprint(polygon, routing_surface=routing_surface, cost=footprint_cost, footprint_type=FootprintType.FOOTPRINT_TYPE_PATH, enabled=True)) if self.privacy_violators & PrivacyViolators.SIM: if self._interaction is not None: self._allowed_sims.update(self._interaction.get_participants(ParticipantType.AllSims)) for sim in services.sim_info_manager().instanced_sims_gen(): if sim not in self._allowed_sims: self.evaluate_sim(sim) violating_sims = self.find_violating_sims() self._exempt_sims = set([s for s in violating_sims if self.is_sim_shoo_exempt(s)]) self._cancel_unavailable_interactions(violating_sims) self._add_overrides_and_constraints_if_needed(violating_sims) if self.privacy_violators & PrivacyViolators.VEHICLES: violating_vehicles = self.find_violating_vehicles() for vehicle in violating_vehicles: vehicle.objectrouting_component.handle_privacy_violation(self) return True def cleanup_privacy_instance(self): if self.is_active: self.is_active = False for sim in self._allowed_sims: self.remove_override_for_sim(sim) for sim in self._late_violators: self.remove_override_for_sim(sim) del self._privacy_constraints[:] self._allowed_sims.clear() self._disallowed_sims.clear() self._violators.clear() self._late_violators.clear() self._exempt_sims.clear() self._cancel_pushed_interactions() def add_privacy(self): services.privacy_service().add_instance(self) def remove_privacy(self): self.cleanup_privacy_instance() services.privacy_service().remove_instance(self) def intersects_with_object(self, obj): if obj.routing_surface != self.central_object.routing_surface: return False delta = obj.position - self.central_object.position distance = delta.magnitude_2d_squared() if distance > self.max_line_of_sight_radius*self.max_line_of_sight_radius: return False object_footprint = obj.footprint_polygon if object_footprint is None: object_footprint = sims4.geometry.CompoundPolygon([sims4.geometry.Polygon([obj.position])]) return self.constraint.geometry.polygon.intersects(object_footprint) def vehicle_violates_privacy(self, vehicle): if vehicle.objectrouting_component is None: return False if self._vehicle_tests is not None: resolver = SingleObjectResolver(vehicle) if self._vehicle_tests.run_tests(resolver): return False elif not self.intersects_with_object(vehicle): return False elif not self.intersects_with_object(vehicle): return False return True def find_violating_vehicles(self): violators = [] privacy_service = services.privacy_service() for vehicle in privacy_service.get_potential_vehicle_violators(): if self.vehicle_violates_privacy(vehicle): violators.append(vehicle) return violators def find_violating_sims(self, consider_exempt=True): if not self.is_active: return [] if not self.privacy_violators & PrivacyViolators.SIM: return [] check_all_surfaces_on_level = not self._routing_surface_only nearby_sims = placement.get_nearby_sims_gen(self.central_object.position, self._routing_surface, radius=self.max_line_of_sight_radius, exclude=self._allowed_sims, only_sim_position=True, check_all_surfaces_on_level=check_all_surfaces_on_level) violators = [] for sim in nearby_sims: if consider_exempt and sim in self._exempt_sims: continue if any(sim_primitive.is_traversing_portal() for sim_primitive in sim.primitives if isinstance(sim_primitive, FollowPath)): continue if sim not in self._disallowed_sims and self.evaluate_sim(sim): continue if sims4.geometry.test_point_in_compound_polygon(sim.position, self.constraint.geometry.polygon): violators.append(sim) return violators def is_sim_shoo_exempt(self, sim): if sim in self._exempt_sims: return True if self.central_object.provided_routing_surface == sim.location.routing_surface: return False elif self._shoo_exempt_tests: resolver = SingleSimResolver(sim.sim_info) if self._shoo_exempt_tests.run_tests(resolver): return True return False def add_exempt_sim(self, sim): self._exempt_sims.add(sim) def _add_overrides_and_constraints_if_needed(self, violating_sims): for sim in self._allowed_sims: self.add_override_for_sim(sim) for sim in violating_sims: self._violators.add(sim) if sim in self._exempt_sims: continue liabilities = ((SHOO_LIABILITY, ShooLiability(self, sim)),) result = self._route_sim_away(sim, liabilities=liabilities) if result: self._pushed_interactions.append(result.interaction) def _cancel_unavailable_interactions(self, violating_sims): for sim in violating_sims: if sim in self._exempt_sims: continue interactions_to_cancel = set() if sim.queue.running is not None: interactions_to_cancel.add(sim.queue.running) for interaction in sim.si_state: if interaction.is_super: if interaction.target is not None: if sim.locked_from_obj_by_privacy(interaction.target): interactions_to_cancel.add(interaction) for interaction in sim.queue: if interaction.target is not None and sim.locked_from_obj_by_privacy(interaction.target): interactions_to_cancel.add(interaction) elif interaction.target is not None: break for interaction in interactions_to_cancel: interaction.cancel(FinishingType.INTERACTION_INCOMPATIBILITY, cancel_reason_msg='Canceled due to incompatibility with privacy instance.') def _route_sim_away(self, sim, liabilities=()): context = InteractionContext(sim, InteractionContext.SOURCE_SCRIPT, Priority.High, insert_strategy=QueueInsertStrategy.NEXT) from interactions.utils.satisfy_constraint_interaction import BuildAndForceSatisfyShooConstraintInteraction result = sim.push_super_affordance(BuildAndForceSatisfyShooConstraintInteraction, None, context, liabilities=liabilities, privacy_inst=self, name_override='BuildShooFromPrivacy') if result: if self._post_route_affordance is not None: def route_away_callback(_): post_route_context = context.clone_for_continuation(result.interaction) sim.push_super_affordance(self._post_route_affordance, None, post_route_context) result.interaction.register_on_finishing_callback(route_away_callback) else: logger.debug('Failed to push BuildAndForceSatisfyShooConstraintInteraction on Sim {} to route them out of a privacy area. Result: {}', sim, result, owner='tastle') if self.interaction is not None: self.interaction.cancel(FinishingType.TRANSITION_FAILURE, cancel_reason_msg='Failed to shoo Sims away.') return result def _cancel_pushed_interactions(self): for interaction in self._pushed_interactions: interaction.cancel(FinishingType.AUTO_EXIT, cancel_reason_msg='Privacy finished and is cleaning up.') self._pushed_interactions.clear() def handle_late_violator(self, sim): self._cancel_unavailable_interactions((sim,)) self.add_override_for_sim(sim) liabilities = ((LATE_SHOO_LIABILITY, LateShooLiability(self, sim)),) result = self._route_sim_away(sim, liabilities=liabilities) if not result: return if not self._violators: context = InteractionContext(sim, InteractionContext.SOURCE_SCRIPT, Priority.High, insert_strategy=QueueInsertStrategy.NEXT) if self.interaction is None: result = sim.push_super_affordance(self.embarrassed_affordance, sim, context) else: result = sim.push_super_affordance(self.embarrassed_affordance, self.interaction.get_participant(ParticipantType.Actor), context) if not result and not services.sim_spawner_service().sim_is_leaving(sim): logger.warn('Failed to push the embarrassed affordance on Sim {}. Interaction {}. Result {}. Context {} ', sim, self.interaction, result, context, owner='tastle') return self._late_violators.add(sim) def add_override_for_sim(self, sim): for footprint in self._privacy_constraints: sim.routing_context.ignore_footprint_contour(footprint.footprint_id) def remove_override_for_sim(self, sim): for footprint in self._privacy_constraints: sim.routing_context.remove_footprint_contour_override(footprint.footprint_id) @property def allowed_sims(self): return self._allowed_sims @property def disallowed_sims(self): return self._disallowed_sims def remove_sim_from_allowed_disallowed(self, sim): if sim in self._allowed_sims: self._allowed_sims.remove(sim) if sim in self._disallowed_sims: self._disallowed_sims.remove(sim) @property def violators(self): return self._violators def remove_violator(self, sim): self.remove_override_for_sim(sim) self._violators.discard(sim) @property def late_violators(self): return self._late_violators def remove_late_violator(self, sim): self.remove_override_for_sim(sim) self._late_violators.discard(sim)
class Hub(threading.Thread, ConnectionHandler): def __init__(self): super().__init__() self.__running = False # sockets self.__sockets = set() self.__sockets_lock = threading.RLock() # listeners self.__listeners = WeakSet() self.__listeners_lock = threading.Lock() # # Listeners # def add_listener(self, listener: HubListener): with self.__listeners_lock: assert listener not in self.__listeners, 'listener already added: %s' % listener self.__listeners.add(listener) def remove_listener(self, listener: HubListener): with self.__listeners_lock: assert listener in self.__listeners, 'listener not exists: %s' % listener self.__listeners.remove(listener) return True # # Sockets # def __any_socket(self) -> Optional[Socket]: """ get arbitrary socket """ with self.__sockets_lock: for sock in self.__sockets: return sock def __all_sockets(self) -> set: """ get all sockets """ with self.__sockets_lock: return self.__sockets.copy() def __get_sockets(self, port: int) -> set: """ get all sockets bond to this port """ with self.__sockets_lock: matched_sockets = set() for sock in self.__sockets: assert isinstance(sock, Socket), 'socket error: %s' % sock if port == sock.port: matched_sockets.add(sock) return matched_sockets def __get_socket(self, port: int, host: str=None) -> Optional[Socket]: if host is None: # get arbitrary socket bond to this port with self.__sockets_lock: for sock in self.__sockets: assert isinstance(sock, Socket), 'socket error: %s' % sock if port == sock.port: return sock else: # get the socket bond to this address (host, port) with self.__sockets_lock: for sock in self.__sockets: assert isinstance(sock, Socket), 'socket error: %s' % sock if port == sock.port and host == sock.host: return sock def _create_socket(self, host: str, port: int) -> Socket: sock = Socket(host=host, port=port) sock.handler = self sock.start() return sock def open(self, port: int, host: str='0.0.0.0') -> Socket: """ create a socket on this port """ with self.__sockets_lock: sock = self.__get_socket(host=host, port=port) if sock is None: sock = self._create_socket(host=host, port=port) self.__sockets.add(sock) return sock def close(self, port: int, host: str=None) -> set: """ remove the socket(s) on this port """ with self.__sockets_lock: closed_sockets = set() while True: sock = self.__get_socket(host=host, port=port) if sock is None: break sock.close() closed_sockets.add(sock) self.__sockets.remove(sock) return closed_sockets def start(self): if self.isAlive(): return self.__running = True super().start() def stop(self): self.__running = False # close all sockets with self.__sockets_lock: for sock in self.__sockets: assert isinstance(sock, Socket), 'socket error: %s' % sock sock.stop() self.__sockets.clear() # # Connections # def get_connection(self, destination: tuple, source: tuple) -> Optional[Connection]: sock = self.__get_socket(host=source[0], port=source[1]) if sock is not None: return sock.get_connection(remote_address=destination) def get_connections(self, destination: tuple, source: int=0) -> set: if source is 0: sockets = self.__all_sockets() else: sockets = self.__get_sockets(port=source) # get connections from these sockets connections = set() for sock in sockets: assert isinstance(sock, Socket), 'socket error: %s' % sock conn = sock.get_connection(remote_address=destination) if conn is not None: connections.add(conn) return connections def connect(self, destination: tuple, source: Union[tuple, int]=None) -> Optional[Connection]: """ Connect to the destination address by the socket bond to this source :param destination: remote address (IP and port) :param source: local address or port :return: connection """ if source is None: # connect from any socket sock = self.__any_socket() elif isinstance(source, int): # connect from the port sock = self.__get_socket(port=source) else: # connect from the address sock = self.__get_socket(host=source[0], port=source[1]) if sock is not None: return sock.connect(remote_address=destination) @staticmethod def __disconnect(destination: tuple, sockets: set) -> set: all_removed = set() for sock in sockets: removed = sock.disconnect(remote_address=destination) if len(removed) > 0: all_removed = all_removed.union(removed) return all_removed def disconnect(self, destination: tuple, source: Union[tuple, int]=None) -> set: """ Disconnect from the destination by the socket bond to this source :param destination: remote address (IP and port) :param source: local address or port :return: removed connections """ if source is None: # disconnect from all sockets return self.__disconnect(destination=destination, sockets=self.__all_sockets()) elif isinstance(source, int): # disconnect from sockets on this port return self.__disconnect(destination=destination, sockets=self.__get_sockets(port=source)) else: # disconnect from the address sock = self.__get_socket(host=source[0], port=source[1]) if sock is not None: return sock.disconnect(remote_address=destination) def send(self, data: bytes, destination: tuple, source: Union[tuple, int]=None) -> int: """ Send data from source address(port) to destination address :param data: data package :param destination: remote address :param source: local address :return: """ if source is None: # connect from any socket sock = self.__any_socket() elif isinstance(source, int): # connect from the port sock = self.__get_socket(port=source) else: # connect from the address sock = self.__get_socket(host=source[0], port=source[1]) if sock is not None: return sock.send(data=data, remote_address=destination) @classmethod def __receive_from_sockets(cls, sockets: set) -> Optional[Cargo]: """ Receive data from these given sockets """ for sock in sockets: assert isinstance(sock, Socket), 'socket error: %s' % sock packet = sock.receive() if packet is not None: # got one return Cargo.create(packet=packet, socket=sock) @classmethod def __block_receive_from_sockets(cls, sockets: set, timeout: float) -> Optional[Cargo]: """ Block to receive data from these given sockets """ while time.time() <= timeout: cargo = cls.__receive_from_sockets(sockets=sockets) if cargo is None: # receive nothing, have a rest for next job time.sleep(0.1) else: return cargo FOREVER = 31558150 # 3600 * 24 * 365.25636 (365d 6h 9m 10s) def receive(self, host: str=None, port: int=0, timeout: float=None) -> Optional[Cargo]: """ Receive data; if timeout is given, block to receive data :return: cargo with data and addresses """ if timeout is None: if port is 0: return self.__receive_from_sockets(sockets=self.__all_sockets()) elif host is None: return self.__receive_from_sockets(sockets=self.__get_sockets(port=port)) else: sock = self.__get_socket(host=host, port=port) if sock is not None: return self.__receive_from_sockets(sockets={sock}) else: now = time.time() if timeout < 0: timeout = now + self.FOREVER else: timeout = now + timeout if port is 0: return self.__block_receive_from_sockets(sockets=self.__all_sockets(), timeout=timeout) elif host is None: return self.__block_receive_from_sockets(sockets=self.__get_sockets(port=port), timeout=timeout) else: sock = self.__get_socket(host=host, port=port) if sock is not None: return self.__block_receive_from_sockets(sockets={sock}, timeout=timeout) def run(self): expired = time.time() + Connection.EXPIRES while self.__running: try: # try to receive data cargo = self.__receive_from_sockets(sockets=self.__all_sockets()) if cargo is None: # received nothing, have a rest time.sleep(0.1) else: # dispatch data and got responses responses = self.__dispatch(data=cargo.data, source=cargo.source, destination=cargo.destination) for res in responses: self.send(data=res, destination=cargo.source, source=cargo.destination) # check time for next heartbeat now = time.time() if now > expired: expired = now + 2 # try heart beat for all connections in all sockets self._heartbeat() except Exception as error: print('Hub.run error: %s' % error) def _heartbeat(self): with self.__sockets_lock: for sock in self.__sockets: assert isinstance(sock, Socket), 'socket error: %s' % sock sock.ping() # try to keep all connections alive sock.purge() # remove error connections def __dispatch(self, data: bytes, source: tuple, destination: tuple) -> list: responses = [] with self.__listeners_lock: for listener in self.__listeners: assert isinstance(listener, HubListener), 'listener error: %s' % listener f = listener.filter if f is not None and not f.check_data(data=data, source=source, destination=destination): continue res = listener.data_received(data=data, source=source, destination=destination) if res is None: continue responses.append(res) return responses # # ConnectionHandler # def connection_status_changed(self, connection: Connection, old_status: ConnectionStatus, new_status: ConnectionStatus): with self.__listeners_lock: for listener in self.__listeners: assert isinstance(listener, HubListener), 'listener error: %s' % listener f = listener.filter if f is not None and not f.check_connection(connection=connection): continue listener.status_changed(connection=connection, old_status=old_status, new_status=new_status) def connection_received_data(self, connection: Connection): # if self.__running: # # process by run() # return True # address = connection.local_address # cargo = self.receive(host=address[0], port=address[1]) # if cargo is None: # # assert False # return False # # dispatch data and got responses # responses = self.__dispatch(data=cargo.data, source=cargo.source, destination=cargo.destination) # for res in responses: # self.send(data=res, destination=cargo.source, source=cargo.destination) pass
class UpdateModel(object): """Provide the object with a way to notify other objects that depend on it. """ # Slots ensures we're explicit and fast __slots__ = ('_sources', '_listeners', '_up_to_date', '_metadata', '_notify_callback', '__weakref__') def __init__(self, metadata=None, notify_callback=None, *args, **kwargs): """Initialize the chain. By tracking both sources and listeners, we can make a graph of what gets updated by what. The metadata is a bucket for runtime info to be checked during notifications. """ # Initialize mixins - NOPE Update is not cooperative. # It expects to be a base class #super(UpdateModel, self).__init__(*args, **kwargs) self._sources = tuple() self._listeners = WeakSet() self._metadata = metadata self._notify_callback = notify_callback self._up_to_date = True # Initialize mixins super(UpdateModel, self).__init__(*args, **kwargs) @property def metadata(self): return self._metadata def subscribe(self, listener): """Add a listener to the subscriber list. This isn't a set - order will likely help efficiency, the list will be updated infrequently, and the list should never get very big anyhow. Note that Calc objects have a source to act as their publisher list. (In case we want to backtrace.) """ if not listener in self._listeners: self._listeners.add(listener) def unsubscribe(self, listener): """Remove a listener from the subscriber list. """ while listener in self._listeners: self._listeners.remove(listener) def notify(self, old_selector, new_selector, source=None, depth=0): """Fires an update to make sure dependents are updated, if needed. The selectors show what happened in the update. """ for dependent in self._listeners: try: # TODO: verify that for each update, only one update is marshalled and fired # for example, if an update forces a Composable to clear, # then we can expect that it'll fire for both the clear _and_ the pass-thru update if dependent.up_to_date or old_selector: dependent.update(old_selector, new_selector, source or self, depth+1) except NotImplementedError: pass except AttributeError: pass if self._notify_callback: try: self._notify_callback(old_selector, new_selector, source or self, depth) except: pass # isolate event failures def update(self, old_selector, new_selector, source=None, depth=0): """Execute the update. Each class will have its own way to implement this.""" # (None, None) signals that the data is out of date, # but there is nothing for dependents to do yet. self._up_to_date = False # Pass-through updates without triggering self.notify(old_selector, new_selector, source or self, depth) # NOTE: super calls in subclasses should mark up_to_date when they're brought up @property def listeners(self): return self._listeners @property def up_to_date(self): return self._up_to_date @listeners.setter def listeners(self, new_listeners): self._replace_listeners(new_listeners) def _replace_listeners(self, new_listeners): """If the listeners are changed en masse, break all the subscriptions. This setter makes sure the subscription methods are never skipped. """ while self._listeners: listener = self._listeners[0] self.unsubscribe(listener) for listener in new_listeners: self.subscribe(listener) @property def sources(self): return self._sources @sources.setter def sources(self, new_sources): self._replace_sources(new_sources) def _replace_sources(self, new_sources): for source in set(self._sources).difference(set(new_sources)): source.unsubscribe(self) for source in new_sources: source.subscribe(self) self._sources = new_sources
class CallbackCollection(Set): __slots__ = "__sender", "__callbacks", "__weak_callbacks", "__lock" def __init__(self, sender): self.__sender = ref(sender) self.__callbacks = set() self.__weak_callbacks = WeakSet() self.__lock = Lock() def add(self, callback: Callable, weak=True): if self.is_frozen: raise RuntimeError("Collection frozen") if not callable(callback): raise ValueError("Callback is not callable") with self.__lock: if weak: self.__weak_callbacks.add(callback) else: self.__callbacks.add(callback) def remove(self, callback: Callable): if self.is_frozen: raise RuntimeError("Collection frozen") with self.__lock: try: self.__callbacks.remove(callback) except KeyError: self.__weak_callbacks.remove(callback) def clear(self): if self.is_frozen: raise RuntimeError("Collection frozen") with self.__lock: self.__callbacks.clear() self.__weak_callbacks.clear() @property def is_frozen(self) -> bool: return isinstance(self.__callbacks, frozenset) def freeze(self): if self.is_frozen: raise RuntimeError("Collection already frozen") with self.__lock: self.__callbacks = frozenset(self.__callbacks) self.__weak_callbacks = WeakSet(self.__weak_callbacks) def unfreeze(self): if not self.is_frozen: raise RuntimeError("Collection is not frozen") with self.__lock: self.__callbacks = set(self.__callbacks) self.__weak_callbacks = WeakSet(self.__weak_callbacks) def __contains__(self, x: object) -> bool: return x in self.__callbacks or x in self.__weak_callbacks def __len__(self) -> int: return len(self.__callbacks) + len(self.__weak_callbacks) def __iter__(self) -> Iterable[Callable]: return iter(chain(self.__callbacks, self.__weak_callbacks)) def __bool__(self): return bool(self.__callbacks) or bool(self.__weak_callbacks) def __copy__(self): instance = self.__class__(self.__sender()) with self.__lock: for cb in self.__callbacks: instance.add(cb, weak=False) for cb in self.__weak_callbacks: instance.add(cb, weak=True) if self.is_frozen: instance.freeze() return instance def __call__(self, *args, **kwargs): with self.__lock: for cb in self: try: cb(self.__sender(), *args, **kwargs) except Exception: log.exception("Callback error")
class MountManager: _running = False _last_run = None _stop = False _mounting = False def __init__(self, latency): self.latency = latency self.mounts = WeakKeyDictionary() self.contexts = WeakSet() self.lock = RLock() self.cell_updates = deque() self._tick = Event() self.paths = WeakKeyDictionary() @lockmethod def add_mount(self, cell, path, mode, authority, persistent, **kwargs): root = cell._root() if root not in self.paths: paths = set() self.paths[root] = paths else: paths = self.paths[root] assert path not in paths, path #print("add mount", path, cell) paths.add(path) item = MountItem(self, cell, path, mode, authority, persistent, **kwargs) self.mounts[cell] = item return item @lockmethod def add_link(self, link, path, persistent): paths = self.paths[link._root()] assert path not in paths, path #print("add link", path, link) paths.add(path) item = LinkItem(link, path, persistent) self.mounts[link] = item return item @lockmethod def unmount(self, cell_or_link, from_del=False): assert not is_dummy_mount(cell_or_link._mount), cell_or_link root = cell_or_link._root() if from_del and (cell_or_link not in self.mounts or root not in self.paths): return mountitem = self.mounts.pop(cell_or_link) if not mountitem._destroyed: paths = self.paths[root] path = cell_or_link._mount["path"] paths.discard(path) mountitem.destroy() @lockmethod def unmount_context(self, context, from_del=False): if context in self.contexts: self.contexts.remove(context) elif not from_del: return mount = context._mount if context._root() is context: self.paths.pop(context, None) if mount is None: return #print("unmount context", context) assert not is_dummy_mount(mount), context try: paths = self.paths[context._root()] except KeyError: if not from_del: return paths = set() try: paths.remove(mount["path"]) except KeyError: pass if mount["persistent"] == False: dirpath = mount["path"].replace("/", os.sep) try: #print("rmdir", dirpath) os.rmdir(dirpath) except: print("Error: cannot remove directory %s" % dirpath) @lockmethod def add_context(self, context, path, as_parent): #print("add context", path, context, as_parent, context._mount["persistent"]) paths = self.paths[context._root()] if not as_parent: if context not in self.contexts: assert path not in paths, path paths.add(path) self.contexts.add(context) else: if path in paths: assert context in self.contexts, (path, context) def _check_context(self, context, as_parent): mount = context._mount assert not is_dummy_mount(mount), context dirpath = mount["path"].replace("/", os.sep) persistent, authority = mount["persistent"], mount["authority"] if os.path.exists(dirpath): if authority == "cell" and not as_parent: print("Warning: Directory path '%s' already exists" % dirpath) #TODO: log warning else: if authority == "file-strict": raise Exception("Directory path '%s' does not exist, but authority is 'file-strict'" % dirpath) os.mkdir(dirpath) @lockmethod def add_cell_update(self, cell): if self._mounting: return assert cell in self.mounts, (cell, hex(id(cell))) self.cell_updates.append(cell) def _run(self): for cell, mount_item in list(self.mounts.items()): if isinstance(cell, Link): continue if cell in self.cell_updates: continue try: mount_item.conditional_read() except Exception: exc = traceback.format_exc() if exc != mount_item.last_exc: print(exc) mount_item.last_exc = exc while 1: try: cell = self.cell_updates.popleft() except IndexError: break mount_item = self.mounts.get(cell) if mount_item is None: #cell was deleted continue try: mount_item.conditional_write(with_none=True) except Exception: exc = traceback.format_exc() if exc != mount_item.last_exc: print(exc) mount_item.last_exc = exc self._tick.set() def run(self): try: self._running = True while not self._stop: t = time.time() self._run() while time.time() - t < self.latency: if not self._tick.is_set(): break time.sleep(0.01) finally: self._running = False def start(self): self._stop = False t = self.thread = Thread(target=self.run) t.setDaemon(True) t.start() def stop(self, wait=False, waiting_loop_period=0.01): self._stop = True if wait: while self._running: time.sleep(waiting_loop_period) def tick(self): """Waits until one iteration of the run() loop has finished""" if self._running: self._tick.clear() self._tick.wait() def destroy(self): for path in list(self.mounts.keys()): self.unmount(path) for context in sorted(self.contexts,key=lambda l:-len(l.path)): self.unmount_context(context)
class Signal(object): """Signal/slot implementation. Author: Thiago Marcos P. Santos Author: Christopher S. Case Author: David H. Bronke Created: August 28, 2008 Updated: December 12, 2011 License: MIT Sample usage: \code class Model(object): def __init__(self, value): self.__value = value self.changed = Signal() def set_value(self, value): self.__value = value self.changed() # Emit signal def get_value(self): return self.__value class View(object): def __init__(self, model): self.model = model model.changed.connect(self.model_changed) def model_changed(self): print(" New value:", self.model.get_value()) print("Beginning Tests:") model = Model(10) view1 = View(model) view2 = View(model) view3 = View(model) print("Setting value to 20...") model.set_value(20) print("Deleting a view, and setting value to 30...") del view1 model.set_value(30) print("Clearing all listeners, and setting value to 40...") model.changed.clear() model.set_value(40) print("Testing non-member function...") def bar(): print(" Calling Non Class Function!") model.changed.connect(bar) model.set_value(50) \endcode """ def __init__(self): """Initialize a new signal""" self._functions = WeakSet() self._methods = WeakKeyDictionary() def __call__(self, *args, **kargs): """Emits the signal and calls all connections""" # Call handler functions for func in self._functions: func(*args, **kargs) # Call handler methods for obj, funcs in self._methods.items(): for func in funcs: func(obj, *args, **kargs) def connect(self, slot): """Connects a slot to the signal so that when the signal is emitted, the slot is called.""" if inspect.ismethod(slot): if slot.__self__ not in self._methods: self._methods[slot.__self__] = set() self._methods[slot.__self__].add(slot.__func__) else: self._functions.add(slot) def disconnect(self, slot): """Disconnects a slot from the signal""" if inspect.ismethod(slot): if slot.__self__ in self._methods: self._methods[slot.__self__].remove(slot.__func__) else: if slot in self._functions: self._functions.remove(slot) def clear(self): """Removes all slots from the signal""" self._functions.clear() self._methods.clear()
class ElementVisual: def __init__(self, diagram, object): self.__cache = ModelTemporaryDataCache(self.__create_appearance_object) self.__cache.depend_on(object.cache) self.__diagram = ref(diagram) self.__object = object self.__cached_appearance = None self.__position = Point(0, 0) self.__size = None self.__connections = WeakSet() def add_connection(self, connection): if connection.source != self and connection.destination != self: raise Exception( "Cannot add connection not connected to the element") self.__connections.add(connection) def remove_connection(self, connection): if connection.source != self and connection.destination != self: raise Exception( "Cannot remove connection not connected to the element") self.__connections.remove(connection) @property def cache(self): return self.__cache @property def object(self): return self.__object @property def diagram(self): return self.__diagram() @property def connections(self): yield from self.__connections def get_position(self, ruler): self.__cache.ensure_valid(ruler=ruler) return self.__position def get_size(self, ruler): self.__cache.ensure_valid(ruler=ruler) return self.__size def get_minimal_size(self, ruler): self.__cache.ensure_valid(ruler=ruler) return self.__cached_appearance.get_minimal_size() def get_bounds(self, ruler): self.__cache.ensure_valid(ruler=ruler) return Rectangle.from_point_size(self.__position, self.__size) def draw(self, canvas): self.__cache.ensure_valid(ruler=canvas.get_ruler()) self.__cached_appearance.draw(canvas) def resize(self, ruler, new_size): self.__size = new_size self.__cache.invalidate() def move(self, ruler, new_position): self.__position = new_position self.__cache.invalidate() def is_at_position(self, ruler, position): return self.get_bounds(ruler).contains(position) def is_resizable(self, ruler): self.__cache.ensure_valid(ruler=ruler) return self.__cached_appearance.is_resizable() def __create_appearance_object(self, ruler): self.__cached_appearance = self.__object.create_appearance_object( ruler) self.__cached_appearance.move(self.__position) min_size = self.__cached_appearance.get_minimal_size() if self.__size is None: self.__size = min_size else: undersize_width = self.__size.width < min_size.width undersize_height = self.__size.height < min_size.height resizable_x, resizable_y = self.__cached_appearance.is_resizable() if (undersize_width and undersize_height) or (not resizable_x and not resizable_y): self.__size = min_size else: if undersize_width or not resizable_x: self.__size = Size(min_size.width, self.__size.height) elif undersize_height or not resizable_y: self.__size = Size(self.__size.width, min_size.height) self.__cached_appearance.resize(self.__size)
class Signal(object): """ basic signal emitter fired signals are added to this object's calling frame - if this becomes excessive, this also includes mode to add function calls to queue instead of directly firing connnected functions queue support not complete yet, as nothing I use needs it. """ queues = {"default": deque()} debugConnection = False def __init__(self, name="", queue="", useQueue=False): """:param queue : name of queue to use, or external queue object """ self.name = name self._functions = WeakSet() self._methods = WeakKeyDictionary() # separate register for references to explicitly strong functions # they are still placed in main weak sets above, # little bit spaghetti self._strongItemRefs = set() # is signal active self._active = True # event queue support self._useQueue = useQueue self._queue = queue or "default" def __hash__(self): return hash(id(self)) def __repr__(self): return f"Signal({self.name})" def __call__(self, *args, **kwargs): #print("emit", self.debugLog()) if not self._active: return queue = self.getQueue() # Call handler functions for func in list(self._functions): if self._useQueue: queue.append(partial(func, *args, **kwargs)) else: func(*args, **kwargs) # Call handler methods for obj, funcs in dict(self._methods).items(): for func in funcs: if self._useQueue: queue.append(partial(func, obj, *args, **kwargs)) else: func(obj, *args, **kwargs) def debugLog(self): return str((*self._functions, dict(self._methods))) def activate(self): self._active = True def mute(self): self._active = False def getQueue(self, name="default", create=True): """return one of the event queues attended by signal objects""" name = name or self._queue or "default" if not name in self.queues and create: self.queues[name] = deque() return self.queues[name] def setQueue(self, queueName): """ set signal to use given queue """ self._queue = queueName def emit(self, *args, **kwargs): """ brings this object up to rough parity with qt signals """ self(*args, **kwargs) def connect(self, slot, strong=False): """add given callable to function or method register flag as strong to allow local lambdas or closures""" if inspect.ismethod(slot): if self.debugConnection: print() try: hash(slot.__self__) if slot.__self__ not in self._methods: self._methods[slot.__self__] = set() self._methods[slot.__self__].add(slot.__func__) except TypeError: self._functions.add(slot) pass else: self._functions.add(slot) if strong: self._strongItemRefs.add(slot) def disconnect(self, slot): if inspect.ismethod(slot): try: hash(slot.__self__) if slot.__self__ in self._methods: self._methods[slot.__self__].remove(slot.__func__) return except TypeError: # self._functions.remove(slot) pass if slot in self._functions: self._functions.remove(slot) if slot in self._strongItemRefs: self._strongItemRefs.remove(slot) def disconnectFromPool(self, pool): """remove any function in the pool from signal's connections """ for fn in pool: self.disconnect(fn) def clear(self): self._functions.clear() self._methods.clear()
class Signal(object): """ class for signal slot concept Example ------- A simple example for a callback is >>> event = Signal() >>> event.connect(mfunc) >>> # raise the signal >>> event("hello") >>> >>> # functions can be disconnected >>> even.disconnect(myfunc) Since weak references are used, care has to be taken with object functions >>> obj = MyClass() >>> event.connect(obj.myfunc) # works >>> event.connect(MyClass().myfunc) # will not work The second example for member functions will not work since the Signal class uses weakref and therefore does not increase the reference counter. MyClass() only exists for the time of the function call and will be deleted afterwards and the weakref will become invalid. """ def __init__(self): self._functions = WeakSet() self._methods = WeakKeyDictionary() def __call__(self, *args, **kargs): """ raise the event """ # Call handler functions for func in self._functions: func(*args, **kargs) # Call handler methods for obj, funcs in self._methods.items(): for func in funcs: func(obj, *args, **kargs) def connect(self, slot): """ connect a function / member function to the signal """ if inspect.ismethod(slot): if slot.__self__ not in self._methods: self._methods[slot.__self__] = set() self._methods[slot.__self__].add(slot.__func__) else: self._functions.add(slot) def disconnect(self, slot): """ disconnect a function from the signal """ if inspect.ismethod(slot): if slot.__self__ in self._methods: self._methods[slot.__self__].remove(slot.__func__) else: if slot in self._functions: self._functions.remove(slot) def clear(self): """ remove all callbacks from the signal """ self._functions.clear() self._methods.clear()
class TestWeakSet(unittest.TestCase): def setUp(self): # need to keep references to them self.items = [ustr(c) for c in ('a', 'b', 'c')] self.items2 = [ustr(c) for c in ('x', 'y', 'z')] self.ab_items = [ustr(c) for c in 'ab'] self.abcde_items = [ustr(c) for c in 'abcde'] self.def_items = [ustr(c) for c in 'def'] self.ab_weakset = WeakSet(self.ab_items) self.abcde_weakset = WeakSet(self.abcde_items) self.def_weakset = WeakSet(self.def_items) self.letters = [ustr(c) for c in string.ascii_letters] self.s = WeakSet(self.items) self.d = dict.fromkeys(self.items) self.obj = ustr('F') self.fs = WeakSet([self.obj]) def test_methods(self): weaksetmethods = dir(WeakSet) for method in dir(set): if method == 'test_c_api' or method.startswith('_'): continue self.assertIn(method, weaksetmethods, "WeakSet missing method " + method) def test_new_or_init(self): self.assertRaises(TypeError, WeakSet, [], 2) def test_len(self): self.assertEqual(len(self.s), len(self.d)) self.assertEqual(len(self.fs), 1) del self.obj support.gc_collect() self.assertEqual(len(self.fs), 0) def test_contains(self): for c in self.letters: self.assertEqual(c in self.s, c in self.d) # 1 is not weakref'able, but that TypeError is caught by __contains__ self.assertNotIn(1, self.s) self.assertIn(self.obj, self.fs) del self.obj support.gc_collect() self.assertNotIn(ustr('F'), self.fs) def test_union(self): u = self.s.union(self.items2) for c in self.letters: self.assertEqual(c in u, c in self.d or c in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(u), WeakSet) self.assertRaises(TypeError, self.s.union, [[]]) for C in set, frozenset, dict.fromkeys, list, tuple: x = WeakSet(self.items + self.items2) c = C(self.items2) self.assertEqual(self.s.union(c), x) del c self.assertEqual(len(u), len(self.items) + len(self.items2)) self.items2.pop() gc.collect() self.assertEqual(len(u), len(self.items) + len(self.items2)) def test_or(self): i = self.s.union(self.items2) self.assertEqual(self.s | set(self.items2), i) self.assertEqual(self.s | frozenset(self.items2), i) def test_intersection(self): s = WeakSet(self.letters) i = s.intersection(self.items2) for c in self.letters: self.assertEqual(c in i, c in self.items2 and c in self.letters) self.assertEqual(s, WeakSet(self.letters)) self.assertEqual(type(i), WeakSet) for C in set, frozenset, dict.fromkeys, list, tuple: x = WeakSet([]) self.assertEqual(i.intersection(C(self.items)), x) self.assertEqual(len(i), len(self.items2)) self.items2.pop() gc.collect() self.assertEqual(len(i), len(self.items2)) def test_isdisjoint(self): self.assertTrue(self.s.isdisjoint(WeakSet(self.items2))) self.assertTrue(not self.s.isdisjoint(WeakSet(self.letters))) def test_and(self): i = self.s.intersection(self.items2) self.assertEqual(self.s & set(self.items2), i) self.assertEqual(self.s & frozenset(self.items2), i) def test_difference(self): i = self.s.difference(self.items2) for c in self.letters: self.assertEqual(c in i, c in self.d and c not in self.items2) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) self.assertRaises(TypeError, self.s.difference, [[]]) def test_sub(self): i = self.s.difference(self.items2) self.assertEqual(self.s - set(self.items2), i) self.assertEqual(self.s - frozenset(self.items2), i) def test_symmetric_difference(self): i = self.s.symmetric_difference(self.items2) for c in self.letters: self.assertEqual(c in i, (c in self.d) ^ (c in self.items2)) self.assertEqual(self.s, WeakSet(self.items)) self.assertEqual(type(i), WeakSet) self.assertRaises(TypeError, self.s.symmetric_difference, [[]]) self.assertEqual(len(i), len(self.items) + len(self.items2)) self.items2.pop() gc.collect() self.assertEqual(len(i), len(self.items) + len(self.items2)) def test_xor(self): i = self.s.symmetric_difference(self.items2) self.assertEqual(self.s ^ set(self.items2), i) self.assertEqual(self.s ^ frozenset(self.items2), i) def test_sub_and_super(self): self.assertTrue(self.ab_weakset <= self.abcde_weakset) self.assertTrue(self.abcde_weakset <= self.abcde_weakset) self.assertTrue(self.abcde_weakset >= self.ab_weakset) self.assertFalse(self.abcde_weakset <= self.def_weakset) self.assertFalse(self.abcde_weakset >= self.def_weakset) self.assertTrue(set('a').issubset('abc')) self.assertTrue(set('abc').issuperset('a')) self.assertFalse(set('a').issubset('cbs')) self.assertFalse(set('cbs').issuperset('a')) def test_lt(self): self.assertTrue(self.ab_weakset < self.abcde_weakset) self.assertFalse(self.abcde_weakset < self.def_weakset) self.assertFalse(self.ab_weakset < self.ab_weakset) self.assertFalse(WeakSet() < WeakSet()) def test_gt(self): self.assertTrue(self.abcde_weakset > self.ab_weakset) self.assertFalse(self.abcde_weakset > self.def_weakset) self.assertFalse(self.ab_weakset > self.ab_weakset) self.assertFalse(WeakSet() > WeakSet()) def test_gc(self): # Create a nest of cycles to exercise overall ref count check s = WeakSet(Foo() for i in range(1000)) for elem in s: elem.cycle = s elem.sub = elem elem.set = WeakSet([elem]) def test_subclass_with_custom_hash(self): # Bug #1257731 class H(WeakSet): def __hash__(self): return int(id(self) & 0x7fffffff) s=H() f=set() f.add(s) self.assertIn(s, f) f.remove(s) f.add(s) f.discard(s) def test_init(self): s = WeakSet() s.__init__(self.items) self.assertEqual(s, self.s) s.__init__(self.items2) self.assertEqual(s, WeakSet(self.items2)) self.assertRaises(TypeError, s.__init__, s, 2); self.assertRaises(TypeError, s.__init__, 1); def test_constructor_identity(self): s = WeakSet(self.items) t = WeakSet(s) self.assertNotEqual(id(s), id(t)) def test_hash(self): self.assertRaises(TypeError, hash, self.s) def test_clear(self): self.s.clear() self.assertEqual(self.s, WeakSet([])) self.assertEqual(len(self.s), 0) def test_copy(self): dup = self.s.copy() self.assertEqual(self.s, dup) self.assertNotEqual(id(self.s), id(dup)) def test_add(self): x = ustr('Q') self.s.add(x) self.assertIn(x, self.s) dup = self.s.copy() self.s.add(x) self.assertEqual(self.s, dup) self.assertRaises(TypeError, self.s.add, []) self.fs.add(Foo()) support.gc_collect() self.assertTrue(len(self.fs) == 1) self.fs.add(self.obj) self.assertTrue(len(self.fs) == 1) def test_remove(self): x = ustr('a') self.s.remove(x) self.assertNotIn(x, self.s) self.assertRaises(KeyError, self.s.remove, x) self.assertRaises(TypeError, self.s.remove, []) def test_discard(self): a, q = ustr('a'), ustr('Q') self.s.discard(a) self.assertNotIn(a, self.s) self.s.discard(q) self.assertRaises(TypeError, self.s.discard, []) def test_pop(self): for i in range(len(self.s)): elem = self.s.pop() self.assertNotIn(elem, self.s) self.assertRaises(KeyError, self.s.pop) def test_update(self): retval = self.s.update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): self.assertIn(c, self.s) self.assertRaises(TypeError, self.s.update, [[]]) def test_update_set(self): self.s.update(set(self.items2)) for c in (self.items + self.items2): self.assertIn(c, self.s) def test_ior(self): self.s |= set(self.items2) for c in (self.items + self.items2): self.assertIn(c, self.s) def test_intersection_update(self): retval = self.s.intersection_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if c in self.items2 and c in self.items: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) self.assertRaises(TypeError, self.s.intersection_update, [[]]) def test_iand(self): self.s &= set(self.items2) for c in (self.items + self.items2): if c in self.items2 and c in self.items: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) def test_difference_update(self): retval = self.s.difference_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if c in self.items and c not in self.items2: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) self.assertRaises(TypeError, self.s.difference_update, [[]]) self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) def test_isub(self): self.s -= set(self.items2) for c in (self.items + self.items2): if c in self.items and c not in self.items2: self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) def test_symmetric_difference_update(self): retval = self.s.symmetric_difference_update(self.items2) self.assertEqual(retval, None) for c in (self.items + self.items2): if (c in self.items) ^ (c in self.items2): self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) def test_ixor(self): self.s ^= set(self.items2) for c in (self.items + self.items2): if (c in self.items) ^ (c in self.items2): self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) def test_inplace_on_self(self): t = self.s.copy() t |= t self.assertEqual(t, self.s) t &= t self.assertEqual(t, self.s) t -= t self.assertEqual(t, WeakSet()) t = self.s.copy() t ^= t self.assertEqual(t, WeakSet()) def test_eq(self): # issue 5964 self.assertTrue(self.s == self.s) self.assertTrue(self.s == WeakSet(self.items)) self.assertFalse(self.s == set(self.items)) self.assertFalse(self.s == list(self.items)) self.assertFalse(self.s == tuple(self.items)) self.assertFalse(self.s == WeakSet([Foo])) self.assertFalse(self.s == 1) def test_ne(self): self.assertTrue(self.s != set(self.items)) s1 = WeakSet() s2 = WeakSet() self.assertFalse(s1 != s2) def test_weak_destroy_while_iterating(self): # Issue #7105: iterators shouldn't crash when a key is implicitly removed # Create new items to be sure no-one else holds a reference items = [ustr(c) for c in ('a', 'b', 'c')] s = WeakSet(items) it = iter(s) next(it) # Trigger internal iteration # Destroy an item del items[-1] gc.collect() # just in case # We have removed either the first consumed items, or another one self.assertIn(len(list(it)), [len(items), len(items) - 1]) del it # The removal has been committed self.assertEqual(len(s), len(items)) def test_weak_destroy_and_mutate_while_iterating(self): # Issue #7105: iterators shouldn't crash when a key is implicitly removed items = [ustr(c) for c in string.ascii_letters] s = WeakSet(items) @contextlib.contextmanager def testcontext(): try: it = iter(s) # Start iterator yielded = ustr(str(next(it))) # Schedule an item for removal and recreate it u = ustr(str(items.pop())) if yielded == u: # The iterator still has a reference to the removed item, # advance it (issue #20006). next(it) gc.collect() # just in case yield u finally: it = None # should commit all removals with testcontext() as u: self.assertNotIn(u, s) with testcontext() as u: self.assertRaises(KeyError, s.remove, u) self.assertNotIn(u, s) with testcontext() as u: s.add(u) self.assertIn(u, s) t = s.copy() with testcontext() as u: s.update(t) self.assertEqual(len(s), len(t)) with testcontext() as u: s.clear() self.assertEqual(len(s), 0) def test_len_cycles(self): N = 20 items = [RefCycle() for i in range(N)] s = WeakSet(items) del items it = iter(s) try: next(it) except StopIteration: pass gc.collect() n1 = len(s) del it gc.collect() gc.collect() n2 = len(s) # one item may be kept alive inside the iterator self.assertIn(n1, (0, 1)) self.assertEqual(n2, 0) @support.impl_detail("PyPy has no cyclic collection", pypy=False) def test_len_race(self): # Extended sanity checks for len() in the face of cyclic collection self.addCleanup(gc.set_threshold, *gc.get_threshold()) for th in range(1, 100): N = 20 gc.collect(0) gc.set_threshold(th, th, th) items = [RefCycle() for i in range(N)] s = WeakSet(items) del items # All items will be collected at next garbage collection pass it = iter(s) try: next(it) except StopIteration: pass n1 = len(s) del it n2 = len(s) self.assertGreaterEqual(n1, 0) self.assertLessEqual(n1, N) self.assertGreaterEqual(n2, 0) self.assertLessEqual(n2, n1)
class AsyncTaskContext(AbstractAsyncTaskContext): def __init__(self, daemon=False, parent=None, name=None): self._setup() self.daemon = daemon self.parent = parent self.name = name self.eventloop = parent.eventloop self.parent.add_child(self) def _setup(self): self.children = WeakSet() self.daemon_children = WeakSet() self.exception = None self.tb_list = None self.stack_list = None self.except_func = None self.finally_func = None def add_child(self, child): if DEBUG: log.debug("Adding child:%r ", child) if child.daemon: self.daemon_children.add(child) else: self.children.add(child) def remove_child(self, child): if DEBUG: log.debug("Removing child %r %r", child, self) log.debug("--------------------BEFORE REMOVE---------------------") log_task_context(self, log) log.debug("------------------------------------------------------") if child.daemon: self.daemon_children.remove(child) else: self.children.remove(child) if self.daemon_children and not self.children: self.parent.cancel() if not self.children and not self.daemon_children: if self.exception is not None and self.except_func is not None: self._execute_except(self.exception) else: self._execute_finally() def handle_exception(self, exception, tb_list=None): if DEBUG: log.debug("Handling exception %r %r", self, exception) if self.exception is None \ or not isinstance(exception, CancellationError): self.exception = exception self.tb_list = tb_list for child in self.children.union(self.daemon_children): child.cancel() def cancel(self): if DEBUG: log.debug("Cancelling %r", self) self.handle_exception(CancellationError(), self.tb_list) def update_parent(self): if DEBUG: log.debug("updating parent %r of self %r", self.parent, self) if self.parent is None: return if self.exception is not None: self.parent.handle_exception(self.exception, self.tb_list) self.parent.remove_child(self) self.parent = None # gc def schedule_task(self, task, now=False): if DEBUG: log.debug("Scheduling task %r", task) if now: self.eventloop.execute_now(task) else: self.eventloop.execute(task) def set_stack(self, stack_list): stack_before, stack_after = split_stack(stack_list) if self.parent.stack_list is None: self.parent.stack_list = stack_before self.stack_list = stack_after def _execute_except(self, err): if DEBUG: log.debug("Executing except %r %r", self, err) if self.except_func is not None: with self: except_func = self.except_func self.except_func = None task = AsyncTask(except_func, (err,), context=self, name=except_func.__name__) task.cancellable = False task.daemon = self.daemon # execute exceptions asap to have a chance to cancel any # pending tasks task.execute_now() else: self._execute_finally() def _execute_finally(self): if DEBUG: log.debug("Executiong finally %r with exception %r", self, self.exception) if isinstance(self.exception, CancellationError): self.exception = None if self.finally_func is not None: with self: task = AsyncTask(self.finally_func, context=self, name=self.finally_func.__name__) task.daemon = self.daemon task.cancellable = False task.execute() self.finally_func = None else: self.update_parent() def __enter__(self): self._parent_context = get_async_context() set_async_context(self) return self def __exit__(self, exc_type, err, tb): set_async_context(self._parent_context) self._parent_context = None # gc def __repr__(self): args = [] if self.name is not None: args.append("name=%s" % self.name) if self.parent: args.append("parent=%s" % self.parent) if self.children: args.append("children=%d" % len(self.children)) if self.daemon_children: args.append("daemon_children=%d" % len(self.daemon_children)) if self.except_func: args.append("except_func=%r" % self.except_func.__name__) if self.finally_func: args.append("finally_func=%r" % self.finally_func.__name__) if self.exception: args.append("exception=%r" % self.exception) if args: args.insert(0, "") return "<%s at %s%s>" % ( self.__class__.__name__, hex(id(self)), " ".join(args)) def __str__(self): return "<%s at %s>" % (self.__class__.__name__, hex(id(self)))