mirror of
				https://github.com/KevinMidboe/python-gpiozero.git
				synced 2025-10-29 17:50:37 +00:00 
			
		
		
		
	Rework when_changed attribute to use weakrefs
Some fairly major changes to ensure that the Pin.when_changed property doesn't keep references to the objects owning the callbacks that are assigned. This is vaguely tricky given that ordinary weakref's can't be used with bound methods (which are ephemeral), so I've back-ported weakref.WeakMethod from Py3.4. This solves a whole pile of things like Button instances not disappearing when they're deleted, and makes composite devices containing Buttons much easier to construct as we don't need to worry about partially constructed things not getting deleted.
This commit is contained in:
		| @@ -9,6 +9,7 @@ from __future__ import ( | ||||
| str = type('') | ||||
|  | ||||
| import cmath | ||||
| import weakref | ||||
| import collections | ||||
| import operator | ||||
| import functools | ||||
| @@ -81,3 +82,59 @@ class frozendict(collections.Mapping): | ||||
|             hashes = map(hash, self.items()) | ||||
|             self.__hash = functools.reduce(operator.xor, hashes, 0) | ||||
|         return self.__hash | ||||
|  | ||||
|  | ||||
| # Backported from py3.4 | ||||
| class WeakMethod(weakref.ref): | ||||
|     """ | ||||
|     A custom `weakref.ref` subclass which simulates a weak reference to | ||||
|     a bound method, working around the lifetime problem of bound methods. | ||||
|     """ | ||||
|  | ||||
|     __slots__ = "_func_ref", "_meth_type", "_alive", "__weakref__" | ||||
|  | ||||
|     def __new__(cls, meth, callback=None): | ||||
|         try: | ||||
|             obj = meth.__self__ | ||||
|             func = meth.__func__ | ||||
|         except AttributeError: | ||||
|             raise TypeError("argument should be a bound method, not {0}" | ||||
|                             .format(type(meth))) from None | ||||
|         def _cb(arg): | ||||
|             # The self-weakref trick is needed to avoid creating a reference | ||||
|             # cycle. | ||||
|             self = self_wr() | ||||
|             if self._alive: | ||||
|                 self._alive = False | ||||
|                 if callback is not None: | ||||
|                     callback(self) | ||||
|         self = weakref.ref.__new__(cls, obj, _cb) | ||||
|         self._func_ref = weakref.ref(func, _cb) | ||||
|         self._meth_type = type(meth) | ||||
|         self._alive = True | ||||
|         self_wr = weakref.ref(self) | ||||
|         return self | ||||
|  | ||||
|     def __call__(self): | ||||
|         obj = super(WeakMethod, self).__call__() | ||||
|         func = self._func_ref() | ||||
|         if obj is None or func is None: | ||||
|             return None | ||||
|         return self._meth_type(func, obj) | ||||
|  | ||||
|     def __eq__(self, other): | ||||
|         if isinstance(other, WeakMethod): | ||||
|             if not self._alive or not other._alive: | ||||
|                 return self is other | ||||
|             return weakref.ref.__eq__(self, other) and self._func_ref == other._func_ref | ||||
|         return False | ||||
|  | ||||
|     def __ne__(self, other): | ||||
|         if isinstance(other, WeakMethod): | ||||
|             if not self._alive or not other._alive: | ||||
|                 return self is not other | ||||
|             return weakref.ref.__ne__(self, other) or self._func_ref != other._func_ref | ||||
|         return True | ||||
|  | ||||
|     __hash__ = weakref.ref.__hash__ | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user