Merge branch 'master' of github.com:rpi-distro/python-gpiozero

This commit is contained in:
Ben Nuttall
2015-09-29 21:55:11 +01:00
3 changed files with 109 additions and 39 deletions

View File

@@ -1,11 +1,6 @@
from __future__ import absolute_import from __future__ import absolute_import
import atexit
from RPi import GPIO
from .devices import ( from .devices import (
_gpio_threads_shutdown,
GPIODeviceError, GPIODeviceError,
GPIODevice, GPIODevice,
) )
@@ -35,11 +30,3 @@ from .boards import (
TrafficHat, TrafficHat,
) )
def gpiozero_shutdown():
_gpio_threads_shutdown()
GPIO.cleanup()
atexit.register(gpiozero_shutdown)
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

View File

@@ -1,31 +1,96 @@
import atexit
import weakref import weakref
from threading import Thread, Event from threading import Thread, Event, RLock
from collections import deque from collections import deque
from RPi import GPIO from RPi import GPIO
_GPIO_THREADS = set()
_GPIO_PINS = set()
# Due to interactions between RPi.GPIO cleanup and the GPIODevice.close()
# method the same thread may attempt to acquire this lock, leading to deadlock
# unless the lock is re-entrant
_GPIO_PINS_LOCK = RLock()
def _gpio_threads_shutdown():
while _GPIO_THREADS:
for t in _GPIO_THREADS.copy():
t.stop()
with _GPIO_PINS_LOCK:
while _GPIO_PINS:
GPIO.remove_event_detect(_GPIO_PINS.pop())
GPIO.cleanup()
atexit.register(_gpio_threads_shutdown)
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
class GPIODeviceError(Exception): class GPIODeviceError(Exception):
pass pass
class GPIODeviceClosed(GPIODeviceError):
pass
class GPIODevice(object): class GPIODevice(object):
""" """
Generic GPIO Device. Generic GPIO Device.
""" """
def __init__(self, pin=None): def __init__(self, pin=None):
# self._pin must be set before any possible exceptions can be raised
# because it's accessed in __del__. However, it mustn't be given the
# value of pin until we've verified that it isn't already allocated
self._pin = None
if pin is None: if pin is None:
raise GPIODeviceError('No GPIO pin number given') raise GPIODeviceError('No GPIO pin number given')
with _GPIO_PINS_LOCK:
if pin in _GPIO_PINS:
raise GPIODeviceError(
'pin %d is already in use by another gpiozero object' % pin)
_GPIO_PINS.add(pin)
self._pin = pin self._pin = pin
self._active_state = GPIO.HIGH self._active_state = GPIO.HIGH
self._inactive_state = GPIO.LOW self._inactive_state = GPIO.LOW
def __del__(self):
self.close()
def _read(self): def _read(self):
return GPIO.input(self.pin) == self._active_state try:
return GPIO.input(self.pin) == self._active_state
except TypeError:
self._check_open()
raise
def _fire_events(self): def _fire_events(self):
pass pass
def _check_open(self):
if self.closed:
raise GPIODeviceClosed(
'%s is closed or uninitialized' % self.__class__.__name__)
@property
def closed(self):
return self._pin is None
def close(self):
with _GPIO_PINS_LOCK:
pin = self._pin
self._pin = None
if pin in _GPIO_PINS:
_GPIO_PINS.remove(pin)
GPIO.remove_event_detect(pin)
GPIO.cleanup(pin)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, exc_tb):
self.close()
@property @property
def pin(self): def pin(self):
return self._pin return self._pin
@@ -35,17 +100,11 @@ class GPIODevice(object):
return self._read() return self._read()
def __repr__(self): def __repr__(self):
return "<gpiozero.%s object on pin=%d, is_active=%s>" % ( try:
self.__class__.__name__, self.pin, self.is_active) return "<gpiozero.%s object on pin=%d, is_active=%s>" % (
self.__class__.__name__, self.pin, self.is_active)
except GPIODeviceClosed:
_GPIO_THREADS = set() return "<gpiozero.%s object closed>" % self.__class__.__name__
def _gpio_threads_shutdown():
while _GPIO_THREADS:
for t in _GPIO_THREADS.copy():
t.stop()
class GPIOThread(Thread): class GPIOThread(Thread):

View File

@@ -9,7 +9,7 @@ from threading import Event
from RPi import GPIO from RPi import GPIO
from w1thermsensor import W1ThermSensor from w1thermsensor import W1ThermSensor
from .devices import GPIODeviceError, GPIODevice, GPIOQueue from .devices import GPIODeviceError, GPIODeviceClosed, GPIODevice, GPIOQueue
def _alias(key): def _alias(key):
@@ -33,8 +33,12 @@ class InputDevice(GPIODevice):
'GPIO pins 2 and 3 are fitted with physical pull up ' 'GPIO pins 2 and 3 are fitted with physical pull up '
'resistors; you cannot initialize them with pull_up=False' 'resistors; you cannot initialize them with pull_up=False'
) )
super(InputDevice, self).__init__(pin) # _pull_up should be assigned first as __repr__ relies upon it to
# support the case where __repr__ is called during debugging of an
# instance that has failed to initialize (due to an exception in the
# super-class __init__)
self._pull_up = pull_up self._pull_up = pull_up
super(InputDevice, self).__init__(pin)
self._active_edge = GPIO.FALLING if pull_up else GPIO.RISING self._active_edge = GPIO.FALLING if pull_up else GPIO.RISING
self._inactive_edge = GPIO.RISING if pull_up else GPIO.FALLING self._inactive_edge = GPIO.RISING if pull_up else GPIO.FALLING
self._active_state = GPIO.LOW if pull_up else GPIO.HIGH self._active_state = GPIO.LOW if pull_up else GPIO.HIGH
@@ -59,8 +63,11 @@ class InputDevice(GPIODevice):
return self._pull_up return self._pull_up
def __repr__(self): def __repr__(self):
return "<gpiozero.%s object on pin=%d, pull_up=%s, is_active=%s>" % ( try:
self.__class__.__name__, self.pin, self.pull_up, self.is_active) return "<gpiozero.%s object on pin=%d, pull_up=%s, is_active=%s>" % (
self.__class__.__name__, self.pin, self.pull_up, self.is_active)
except:
return super(InputDevice, self).__repr__()
class WaitableInputDevice(InputDevice): class WaitableInputDevice(InputDevice):
@@ -174,9 +181,6 @@ class DigitalInputDevice(WaitableInputDevice):
# Call _fire_events once to set initial state of events # Call _fire_events once to set initial state of events
super(DigitalInputDevice, self)._fire_events() super(DigitalInputDevice, self)._fire_events()
def __del__(self):
GPIO.remove_event_detect(self.pin)
def _fire_events(self, channel): def _fire_events(self, channel):
super(DigitalInputDevice, self)._fire_events() super(DigitalInputDevice, self)._fire_events()
@@ -188,27 +192,50 @@ class SmoothedInputDevice(WaitableInputDevice):
def __init__( def __init__(
self, pin=None, pull_up=False, threshold=0.5, self, pin=None, pull_up=False, threshold=0.5,
queue_len=5, sample_wait=0.0, partial=False): queue_len=5, sample_wait=0.0, partial=False):
self._queue = None
super(SmoothedInputDevice, self).__init__(pin, pull_up) super(SmoothedInputDevice, self).__init__(pin, pull_up)
self._queue = GPIOQueue(self, queue_len, sample_wait, partial) self._queue = GPIOQueue(self, queue_len, sample_wait, partial)
self.threshold = float(threshold) self.threshold = float(threshold)
def close(self):
try:
self._queue.stop()
except AttributeError:
if self._queue is not None:
raise
except RuntimeError:
# Cannot join thread before it starts; we don't care about this
# because we're trying to close the thread anyway
pass
else:
self._queue = None
super(SmoothedInputDevice, self).close()
def __repr__(self): def __repr__(self):
if self.partial or self._queue.full.wait(0): try:
self._check_open()
except GPIODeviceClosed:
return super(SmoothedInputDevice, self).__repr__() return super(SmoothedInputDevice, self).__repr__()
else: else:
return "<gpiozero.%s object on pin=%d, pull_up=%s>" % ( if self.partial or self._queue.full.wait(0):
self.__class__.__name__, self.pin, self.pull_up) return super(SmoothedInputDevice, self).__repr__()
else:
return "<gpiozero.%s object on pin=%d, pull_up=%s>" % (
self.__class__.__name__, self.pin, self.pull_up)
@property @property
def queue_len(self): def queue_len(self):
self._check_open()
return self._queue.queue.maxlen return self._queue.queue.maxlen
@property @property
def partial(self): def partial(self):
self._check_open()
return self._queue.partial return self._queue.partial
@property @property
def value(self): def value(self):
self._check_open()
return self._queue.value return self._queue.value
def _get_threshold(self): def _get_threshold(self):
@@ -284,9 +311,6 @@ class LightSensor(SmoothedInputDevice):
) )
self._queue.start() self._queue.start()
def __del__(self):
GPIO.remove_event_detect(self.pin)
@property @property
def charge_time_limit(self): def charge_time_limit(self):
return self._charge_time_limit return self._charge_time_limit