Permit replacement of pin_factory without closing old factory. However,
continue closing devices associated with extant pin factory at script
termination.
This commit is contained in:
Dave Jones
2017-06-22 22:45:00 +01:00
parent 6e71f20aa6
commit a99e0746c3
16 changed files with 328 additions and 338 deletions

View File

@@ -34,7 +34,6 @@ from .exc import (
GPIOPinInUse,
GPIODeviceClosed,
PinFactoryFallback,
PinReservationsExist,
)
from .compat import frozendict
@@ -194,33 +193,13 @@ class Device(ValuesMixin, GPIOBase):
services applicable to all devices (specifically the :attr:`is_active`
property, the :attr:`value` property, and the :meth:`close` method).
"""
_pin_factory = None # instance of a Factory sub-class
pin_factory = None # instance of a Factory sub-class
_reservations = defaultdict(list) # maps pin addresses to lists of devices
_res_lock = Lock()
def __repr__(self):
return "<gpiozero.%s object>" % (self.__class__.__name__)
@classmethod
def _set_pin_factory(cls, new_factory):
reserved_devices = {
dev
for ref_list in cls._reservations.values()
for ref in ref_list
for dev in (ref(),)
if dev is not None
}
if new_factory is None:
for dev in reserved_devices:
dev.close()
elif reserved_devices:
raise PinReservationsExist(
"can't change factory while devices still hold pin "
"reservations (%r)" % dev)
if cls._pin_factory is not None:
cls._pin_factory.close()
cls._pin_factory = new_factory
def _reserve_pins(self, *pins_or_addresses):
"""
Called to indicate that the device reserves the right to use the
@@ -439,8 +418,8 @@ class GPIODevice(Device):
self._reserve_pins(pin)
else:
# Check you can reserve *before* constructing the pin
self._reserve_pins(Device._pin_factory.pin_address(pin))
pin = Device._pin_factory.pin(pin)
self._reserve_pins(Device.pin_factory.pin_address(pin))
pin = Device.pin_factory.pin(pin)
self._pin = pin
self._active_state = True
self._inactive_state = False
@@ -524,11 +503,27 @@ def _default_pin_factory(name=os.getenv('GPIOZERO_PIN_FACTORY', None)):
return factory.load()()
raise BadPinFactory('Unable to find pin factory "%s"' % name)
Device._set_pin_factory(_default_pin_factory())
def _devices_shutdown():
if Device.pin_factory:
with Device._res_lock:
reserved_devices = {
dev
for ref_list in Device._reservations.values()
for ref in ref_list
for dev in (ref(),)
if dev is not None
}
for dev in reserved_devices:
dev.close()
Device.pin_factory.close()
Device.pin_factory = None
def _shutdown():
_threads_shutdown()
Device._set_pin_factory(None)
_devices_shutdown()
Device.pin_factory = _default_pin_factory()
atexit.register(_shutdown)

View File

@@ -145,9 +145,6 @@ class PinNoPins(PinError, RuntimeError):
class PinInvalidPin(PinError, ValueError):
"Error raised when an invalid pin specification is provided"
class PinReservationsExist(PinError, RuntimeError):
"Error raised when pin factory is changed while reservations still exist"
class GPIOZeroWarning(Warning):
"Base class for all warnings in GPIO Zero"

View File

@@ -562,8 +562,11 @@ class SPI(object):
| mode | polarity (CPOL) | phase (CPHA) |
+======+=================+==============+
| 0 | False | False |
+------+-----------------+--------------+
| 1 | False | True |
+------+-----------------+--------------+
| 2 | True | False |
+------+-----------------+--------------+
| 3 | True | True |
+------+-----------------+--------------+

View File

@@ -1140,7 +1140,7 @@ def pi_info(revision=None):
# The reason this import is located here is to avoid a circular
# dependency; devices->pins.local->pins.data->devices
from ..devices import Device
result = Device._pin_factory.pi_info
result = Device.pin_factory.pi_info
if result is None:
raise PinUnknownPi('The default pin_factory is not attached to a Pi')
else:

View File

@@ -312,10 +312,10 @@ class MockSPIDevice(object):
self, clock_pin, mosi_pin=None, miso_pin=None, select_pin=None,
clock_polarity=False, clock_phase=False, lsb_first=False,
bits_per_word=8, select_high=False):
self.clock_pin = Device._pin_factory.pin(clock_pin, pin_class=MockSPIClockPin)
self.mosi_pin = None if mosi_pin is None else Device._pin_factory.pin(mosi_pin)
self.miso_pin = None if miso_pin is None else Device._pin_factory.pin(miso_pin)
self.select_pin = None if select_pin is None else Device._pin_factory.pin(select_pin, pin_class=MockSPISelectPin)
self.clock_pin = Device.pin_factory.pin(clock_pin, pin_class=MockSPIClockPin)
self.mosi_pin = None if mosi_pin is None else Device.pin_factory.pin(mosi_pin)
self.miso_pin = None if miso_pin is None else Device.pin_factory.pin(miso_pin)
self.select_pin = None if select_pin is None else Device.pin_factory.pin(select_pin, pin_class=MockSPISelectPin)
self.clock_polarity = clock_polarity
self.clock_phase = clock_phase
self.lsb_first = lsb_first

View File

@@ -29,7 +29,7 @@ class SPIDevice(Device):
def __init__(self, **spi_args):
self._spi = None
super(SPIDevice, self).__init__()
self._spi = self._pin_factory.spi(**spi_args)
self._spi = self.pin_factory.spi(**spi_args)
def close(self):
if self._spi: