mirror of
				https://github.com/KevinMidboe/python-gpiozero.git
				synced 2025-10-29 17:50:37 +00:00 
			
		
		
		
	Fix #436
Permit partial, partialmethod, and wraps to be used with event handlers
This commit is contained in:
		| @@ -9,7 +9,7 @@ str = type('') | ||||
|  | ||||
| import inspect | ||||
| import weakref | ||||
| from functools import wraps | ||||
| from functools import wraps, partial | ||||
| from threading import Event | ||||
| from collections import deque | ||||
| from time import time | ||||
| @@ -255,7 +255,16 @@ class EventsMixin(object): | ||||
|             return None | ||||
|         elif not callable(fn): | ||||
|             raise BadEventHandler('value must be None or a callable') | ||||
|         elif inspect.isbuiltin(fn): | ||||
|         # If fn is wrapped with partial (i.e. partial, partialmethod, or wraps | ||||
|         # has been used to produce it) we need to dig out the "real" function | ||||
|         # that's been wrapped along with all the mandatory positional args | ||||
|         # used in the wrapper so we can test the binding | ||||
|         args = () | ||||
|         wrapped_fn = fn | ||||
|         while isinstance(wrapped_fn, partial): | ||||
|             args = wrapped_fn.args + args | ||||
|             wrapped_fn = wrapped_fn.func | ||||
|         if inspect.isbuiltin(wrapped_fn): | ||||
|             # We can't introspect the prototype of builtins. In this case we | ||||
|             # assume that the builtin has no (mandatory) parameters; this is | ||||
|             # the most reasonable assumption on the basis that pre-existing | ||||
| @@ -267,13 +276,13 @@ class EventsMixin(object): | ||||
|             # If this works, assume the function is capable of accepting no | ||||
|             # parameters | ||||
|             try: | ||||
|                 inspect.getcallargs(fn) | ||||
|                 inspect.getcallargs(wrapped_fn, *args) | ||||
|                 return fn | ||||
|             except TypeError: | ||||
|                 try: | ||||
|                     # If the above fails, try binding with a single parameter | ||||
|                     # (ourselves). If this works, wrap the specified callback | ||||
|                     inspect.getcallargs(fn, self) | ||||
|                     inspect.getcallargs(wrapped_fn, *(args + (self,))) | ||||
|                     @wraps(fn) | ||||
|                     def wrapper(): | ||||
|                         return fn(self) | ||||
|   | ||||
| @@ -10,6 +10,7 @@ str = type('') | ||||
| import sys | ||||
| import pytest | ||||
| from threading import Event | ||||
| from functools import partial | ||||
|  | ||||
| from gpiozero.pins.mock import ( | ||||
|     MockPin, | ||||
| @@ -79,6 +80,20 @@ def test_input_event_deactivated(): | ||||
|         pin.drive_low() | ||||
|         assert event.is_set() | ||||
|  | ||||
| def test_input_partial_callback(): | ||||
|     event = Event() | ||||
|     pin = MockPin(2) | ||||
|     def foo(a, b): | ||||
|         event.set() | ||||
|         return a + b | ||||
|     bar = partial(foo, 1) | ||||
|     baz = partial(bar, 2) | ||||
|     with DigitalInputDevice(pin) as device: | ||||
|         device.when_activated = baz | ||||
|         assert not event.is_set() | ||||
|         pin.drive_high() | ||||
|         assert event.is_set() | ||||
|  | ||||
| def test_input_wait_active(): | ||||
|     pin = MockPin(2) | ||||
|     with DigitalInputDevice(pin) as device: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user