mirror of
https://github.com/KevinMidboe/python-gpiozero.git
synced 2025-10-29 17:50:37 +00:00
Fix #279
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:
@@ -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)
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -562,8 +562,11 @@ class SPI(object):
|
||||
| mode | polarity (CPOL) | phase (CPHA) |
|
||||
+======+=================+==============+
|
||||
| 0 | False | False |
|
||||
+------+-----------------+--------------+
|
||||
| 1 | False | True |
|
||||
+------+-----------------+--------------+
|
||||
| 2 | True | False |
|
||||
+------+-----------------+--------------+
|
||||
| 3 | True | True |
|
||||
+------+-----------------+--------------+
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user