Merge pull request #455 from waveform80/partial-handlers

Fix #436
This commit is contained in:
Dave Jones
2016-09-20 12:07:26 +01:00
committed by GitHub
2 changed files with 28 additions and 4 deletions

View File

@@ -9,7 +9,7 @@ str = type('')
import inspect import inspect
import weakref import weakref
from functools import wraps from functools import wraps, partial
from threading import Event from threading import Event
from collections import deque from collections import deque
from time import time from time import time
@@ -255,7 +255,16 @@ class EventsMixin(object):
return None return None
elif not callable(fn): elif not callable(fn):
raise BadEventHandler('value must be None or a callable') 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 # We can't introspect the prototype of builtins. In this case we
# assume that the builtin has no (mandatory) parameters; this is # assume that the builtin has no (mandatory) parameters; this is
# the most reasonable assumption on the basis that pre-existing # 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 # If this works, assume the function is capable of accepting no
# parameters # parameters
try: try:
inspect.getcallargs(fn) inspect.getcallargs(wrapped_fn, *args)
return fn return fn
except TypeError: except TypeError:
try: try:
# If the above fails, try binding with a single parameter # If the above fails, try binding with a single parameter
# (ourselves). If this works, wrap the specified callback # (ourselves). If this works, wrap the specified callback
inspect.getcallargs(fn, self) inspect.getcallargs(wrapped_fn, *(args + (self,)))
@wraps(fn) @wraps(fn)
def wrapper(): def wrapper():
return fn(self) return fn(self)

View File

@@ -10,6 +10,7 @@ str = type('')
import sys import sys
import pytest import pytest
from threading import Event from threading import Event
from functools import partial
from gpiozero.pins.mock import ( from gpiozero.pins.mock import (
MockPin, MockPin,
@@ -79,6 +80,20 @@ def test_input_event_deactivated():
pin.drive_low() pin.drive_low()
assert event.is_set() 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(): def test_input_wait_active():
pin = MockPin(2) pin = MockPin(2)
with DigitalInputDevice(pin) as device: with DigitalInputDevice(pin) as device: