mirror of
https://github.com/KevinMidboe/python-gpiozero.git
synced 2025-10-29 17:50:37 +00:00
More tidying up
Ensure LEDCollection cleans up upon construction failure, rename some internals to be a bit more obvious, rename PinGPIOUnsupported to PinUnsupported, and some other stuff I've forgotten!
This commit is contained in:
@@ -105,7 +105,7 @@ Errors
|
|||||||
|
|
||||||
.. autoexception:: PinEdgeDetectUnsupported
|
.. autoexception:: PinEdgeDetectUnsupported
|
||||||
|
|
||||||
.. autoexception:: PinGPIOUnsupported
|
.. autoexception:: PinUnsupported
|
||||||
|
|
||||||
.. autoexception:: PinSPIUnsupported
|
.. autoexception:: PinSPIUnsupported
|
||||||
|
|
||||||
|
|||||||
@@ -211,20 +211,24 @@ class LEDCollection(CompositeOutputDevice):
|
|||||||
initial_value = kwargs.pop('initial_value', False)
|
initial_value = kwargs.pop('initial_value', False)
|
||||||
order = kwargs.pop('_order', None)
|
order = kwargs.pop('_order', None)
|
||||||
LEDClass = PWMLED if pwm else LED
|
LEDClass = PWMLED if pwm else LED
|
||||||
super(LEDCollection, self).__init__(
|
try:
|
||||||
*(
|
super(LEDCollection, self).__init__(
|
||||||
pin_or_collection
|
*(
|
||||||
if isinstance(pin_or_collection, LEDCollection) else
|
pin_or_collection
|
||||||
LEDClass(pin_or_collection, active_high, initial_value)
|
if isinstance(pin_or_collection, LEDCollection) else
|
||||||
for pin_or_collection in args
|
LEDClass(pin_or_collection, active_high, initial_value)
|
||||||
),
|
for pin_or_collection in args
|
||||||
_order=order,
|
),
|
||||||
**{
|
_order=order,
|
||||||
name: pin_or_collection
|
**{
|
||||||
if isinstance(pin_or_collection, LEDCollection) else
|
name: pin_or_collection
|
||||||
LEDClass(pin_or_collection, active_high, initial_value)
|
if isinstance(pin_or_collection, LEDCollection) else
|
||||||
for name, pin_or_collection in kwargs.items()
|
LEDClass(pin_or_collection, active_high, initial_value)
|
||||||
})
|
for name, pin_or_collection in kwargs.items()
|
||||||
|
})
|
||||||
|
except:
|
||||||
|
self.close()
|
||||||
|
raise
|
||||||
leds = []
|
leds = []
|
||||||
for item in self:
|
for item in self:
|
||||||
if isinstance(item, LEDCollection):
|
if isinstance(item, LEDCollection):
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import os
|
|||||||
import atexit
|
import atexit
|
||||||
import weakref
|
import weakref
|
||||||
import warnings
|
import warnings
|
||||||
from collections import namedtuple
|
from collections import namedtuple, defaultdict
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from types import FunctionType
|
from types import FunctionType
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
@@ -194,7 +194,7 @@ class Device(ValuesMixin, GPIOBase):
|
|||||||
property, the :attr:`value` property, and the :meth:`close` method).
|
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 = {} # maps pin addresses to lists of devices
|
_reservations = defaultdict(list) # maps pin addresses to lists of devices
|
||||||
_res_lock = Lock()
|
_res_lock = Lock()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@@ -226,19 +226,14 @@ class Device(ValuesMixin, GPIOBase):
|
|||||||
)
|
)
|
||||||
with self._res_lock:
|
with self._res_lock:
|
||||||
for address in addresses:
|
for address in addresses:
|
||||||
try:
|
for device_ref in Device._reservations[address]:
|
||||||
conflictors = self._reservations[address]
|
|
||||||
except KeyError:
|
|
||||||
conflictors = []
|
|
||||||
self._reservations[address] = conflictors
|
|
||||||
for device_ref in conflictors:
|
|
||||||
device = device_ref()
|
device = device_ref()
|
||||||
if device is not None and self._conflicts_with(device):
|
if device is not None and self._conflicts_with(device):
|
||||||
raise GPIOPinInUse(
|
raise GPIOPinInUse(
|
||||||
'pin %s is already in use by %r' % (
|
'pin %s is already in use by %r' % (
|
||||||
'/'.join(address), device)
|
'/'.join(address), device)
|
||||||
)
|
)
|
||||||
conflictors.append(weakref.ref(self))
|
Device._reservations[address].append(weakref.ref(self))
|
||||||
|
|
||||||
def _release_pins(self, *pins_or_addresses):
|
def _release_pins(self, *pins_or_addresses):
|
||||||
"""
|
"""
|
||||||
@@ -254,7 +249,7 @@ class Device(ValuesMixin, GPIOBase):
|
|||||||
)
|
)
|
||||||
with self._res_lock:
|
with self._res_lock:
|
||||||
for address in addresses:
|
for address in addresses:
|
||||||
self._reservations[address] = [
|
Device._reservations[address] = [
|
||||||
ref for ref in self._reservations[address]
|
ref for ref in self._reservations[address]
|
||||||
if ref() not in (self, None) # may as well clean up dead refs
|
if ref() not in (self, None) # may as well clean up dead refs
|
||||||
]
|
]
|
||||||
@@ -265,13 +260,13 @@ class Device(ValuesMixin, GPIOBase):
|
|||||||
:meth:`_release_pins` for further information).
|
:meth:`_release_pins` for further information).
|
||||||
"""
|
"""
|
||||||
with self._res_lock:
|
with self._res_lock:
|
||||||
Device._reservations = {
|
Device._reservations = defaultdict(list, {
|
||||||
address: [
|
address: [
|
||||||
ref for ref in conflictors
|
ref for ref in conflictors
|
||||||
if ref() not in (self, None)
|
if ref() not in (self, None)
|
||||||
]
|
]
|
||||||
for address, conflictors in self._reservations.items()
|
for address, conflictors in self._reservations.items()
|
||||||
}
|
})
|
||||||
|
|
||||||
def _conflicts_with(self, other):
|
def _conflicts_with(self, other):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -118,8 +118,8 @@ class PinFixedPull(PinError, AttributeError):
|
|||||||
class PinEdgeDetectUnsupported(PinError, AttributeError):
|
class PinEdgeDetectUnsupported(PinError, AttributeError):
|
||||||
"Error raised when attempting to use edge detection on unsupported pins"
|
"Error raised when attempting to use edge detection on unsupported pins"
|
||||||
|
|
||||||
class PinGPIOUnsupported(PinError, NotImplementedError):
|
class PinUnsupported(PinError, NotImplementedError):
|
||||||
"Error raised when attempting to obtain a GPIO interface on unsupported pins"
|
"Error raised when attempting to obtain a pin interface on unsupported pins"
|
||||||
|
|
||||||
class PinSPIUnsupported(PinError, NotImplementedError):
|
class PinSPIUnsupported(PinError, NotImplementedError):
|
||||||
"Error raised when attempting to obtain an SPI interface on unsupported pins"
|
"Error raised when attempting to obtain an SPI interface on unsupported pins"
|
||||||
|
|||||||
@@ -71,9 +71,9 @@ class SourceMixin(object):
|
|||||||
def close(self):
|
def close(self):
|
||||||
try:
|
try:
|
||||||
super(SourceMixin, self).close()
|
super(SourceMixin, self).close()
|
||||||
|
self.source = None
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
self.source = None
|
|
||||||
|
|
||||||
def _copy_values(self, source):
|
def _copy_values(self, source):
|
||||||
for v in source:
|
for v in source:
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from ..exc import (
|
|||||||
PinInvalidFunction,
|
PinInvalidFunction,
|
||||||
PinSetInput,
|
PinSetInput,
|
||||||
PinFixedPull,
|
PinFixedPull,
|
||||||
|
PinUnsupported,
|
||||||
PinSPIUnsupported,
|
PinSPIUnsupported,
|
||||||
PinPWMUnsupported,
|
PinPWMUnsupported,
|
||||||
PinEdgeDetectUnsupported,
|
PinEdgeDetectUnsupported,
|
||||||
@@ -58,7 +59,7 @@ class Factory(object):
|
|||||||
:meth:`pin` for the same pin specification must return the same
|
:meth:`pin` for the same pin specification must return the same
|
||||||
object.
|
object.
|
||||||
"""
|
"""
|
||||||
raise PinGPIOUnsupported("GPIO not supported by this pin factory")
|
raise PinUnsupported("Individual pins are not supported by this pin factory")
|
||||||
|
|
||||||
def pin_address(self, spec):
|
def pin_address(self, spec):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -32,10 +32,12 @@ class LocalPiFactory(PiFactory):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(LocalPiFactory, self).__init__()
|
super(LocalPiFactory, self).__init__()
|
||||||
self.spi_hardware_class = LocalPiHardwareSPI
|
self.spi_classes = {
|
||||||
self.spi_software_class = LocalPiSoftwareSPI
|
('hardware', 'exclusive'): LocalPiHardwareSPI,
|
||||||
self.shared_spi_hardware_class = LocalPiHardwareSPIShared
|
('hardware', 'shared'): LocalPiHardwareSPIShared,
|
||||||
self.shared_spi_software_class = LocalPiSoftwareSPIShared
|
('software', 'exclusive'): LocalPiSoftwareSPI,
|
||||||
|
('software', 'shared'): LocalPiSoftwareSPIShared,
|
||||||
|
}
|
||||||
# Override the pins dict to be this class' pins dict. This is a bit of
|
# Override the pins dict to be this class' pins dict. This is a bit of
|
||||||
# a dirty hack, but ensures that anyone evil enough to mix pin
|
# a dirty hack, but ensures that anyone evil enough to mix pin
|
||||||
# implementations doesn't try and control the same pin with different
|
# implementations doesn't try and control the same pin with different
|
||||||
@@ -74,7 +76,7 @@ class LocalPiHardwareSPI(SPI, Device):
|
|||||||
raise ImportError('failed to import spidev')
|
raise ImportError('failed to import spidev')
|
||||||
self._port = port
|
self._port = port
|
||||||
self._device = device
|
self._device = device
|
||||||
self._intf = None
|
self._interface = None
|
||||||
self._address = factory.address + ('SPI(port=%d, device=%d)' % (port, device),)
|
self._address = factory.address + ('SPI(port=%d, device=%d)' % (port, device),)
|
||||||
super(LocalPiHardwareSPI, self).__init__()
|
super(LocalPiHardwareSPI, self).__init__()
|
||||||
self._reserve_pins(
|
self._reserve_pins(
|
||||||
@@ -83,9 +85,9 @@ class LocalPiHardwareSPI(SPI, Device):
|
|||||||
factory.pin_address(9),
|
factory.pin_address(9),
|
||||||
factory.pin_address((8, 7)[device])
|
factory.pin_address((8, 7)[device])
|
||||||
)
|
)
|
||||||
self._intf = SpiDev()
|
self._interface = SpiDev()
|
||||||
self._intf.open(port, device)
|
self._interface.open(port, device)
|
||||||
self._intf.max_speed_hz = 500000
|
self._interface.max_speed_hz = 500000
|
||||||
|
|
||||||
def _conflicts_with(self, other):
|
def _conflicts_with(self, other):
|
||||||
return not (
|
return not (
|
||||||
@@ -94,17 +96,17 @@ class LocalPiHardwareSPI(SPI, Device):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if self._intf:
|
if self._interface:
|
||||||
try:
|
try:
|
||||||
self._intf.close()
|
self._interface.close()
|
||||||
finally:
|
finally:
|
||||||
self._intf = None
|
self._interface = None
|
||||||
self._release_all()
|
self._release_all()
|
||||||
super(LocalPiHardwareSPI, self).close()
|
super(LocalPiHardwareSPI, self).close()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def closed(self):
|
def closed(self):
|
||||||
return self._intf is None
|
return self._interface is None
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
try:
|
try:
|
||||||
@@ -119,31 +121,31 @@ class LocalPiHardwareSPI(SPI, Device):
|
|||||||
:attr:`bits_per_word` bits or less) to the SPI interface, and reads an
|
:attr:`bits_per_word` bits or less) to the SPI interface, and reads an
|
||||||
equivalent number of words, returning them as a list of integers.
|
equivalent number of words, returning them as a list of integers.
|
||||||
"""
|
"""
|
||||||
return self._intf.xfer2(data)
|
return self._interface.xfer2(data)
|
||||||
|
|
||||||
def _get_clock_mode(self):
|
def _get_clock_mode(self):
|
||||||
return self._intf.mode
|
return self._interface.mode
|
||||||
|
|
||||||
def _set_clock_mode(self, value):
|
def _set_clock_mode(self, value):
|
||||||
self._intf.mode = value
|
self._interface.mode = value
|
||||||
|
|
||||||
def _get_lsb_first(self):
|
def _get_lsb_first(self):
|
||||||
return self._intf.lsbfirst
|
return self._interface.lsbfirst
|
||||||
|
|
||||||
def _set_lsb_first(self, value):
|
def _set_lsb_first(self, value):
|
||||||
self._intf.lsbfirst = bool(value)
|
self._interface.lsbfirst = bool(value)
|
||||||
|
|
||||||
def _get_select_high(self):
|
def _get_select_high(self):
|
||||||
return self._intf.cshigh
|
return self._interface.cshigh
|
||||||
|
|
||||||
def _set_select_high(self, value):
|
def _set_select_high(self, value):
|
||||||
self._intf.cshigh = bool(value)
|
self._interface.cshigh = bool(value)
|
||||||
|
|
||||||
def _get_bits_per_word(self):
|
def _get_bits_per_word(self):
|
||||||
return self._intf.bits_per_word
|
return self._interface.bits_per_word
|
||||||
|
|
||||||
def _set_bits_per_word(self, value):
|
def _set_bits_per_word(self, value):
|
||||||
self._intf.bits_per_word = value
|
self._interface.bits_per_word = value
|
||||||
|
|
||||||
|
|
||||||
class LocalPiSoftwareSPI(SPI, OutputDevice):
|
class LocalPiSoftwareSPI(SPI, OutputDevice):
|
||||||
|
|||||||
@@ -35,10 +35,12 @@ class PiFactory(Factory):
|
|||||||
self._info = None
|
self._info = None
|
||||||
self.pins = {}
|
self.pins = {}
|
||||||
self.pin_class = None
|
self.pin_class = None
|
||||||
self.spi_hardware_class = None
|
self.spi_classes = {
|
||||||
self.spi_software_class = None
|
('hardware', 'exclusive'): None,
|
||||||
self.shared_spi_hardware_class = None
|
('hardware', 'shared'): None,
|
||||||
self.shared_spi_software_class = None
|
('software', 'exclusive'): None,
|
||||||
|
('software', 'shared'): None,
|
||||||
|
}
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
for pin in self.pins.values():
|
for pin in self.pins.values():
|
||||||
@@ -93,7 +95,7 @@ class PiFactory(Factory):
|
|||||||
doesn't matter).
|
doesn't matter).
|
||||||
"""
|
"""
|
||||||
spi_args, kwargs = self._extract_spi_args(**spi_args)
|
spi_args, kwargs = self._extract_spi_args(**spi_args)
|
||||||
shared = kwargs.pop('shared', False)
|
shared = 'shared' if kwargs.pop('shared', False) else 'exclusive'
|
||||||
if kwargs:
|
if kwargs:
|
||||||
raise SPIBadArgs(
|
raise SPIBadArgs(
|
||||||
'unrecognized keyword argument %s' % kwargs.popitem()[0])
|
'unrecognized keyword argument %s' % kwargs.popitem()[0])
|
||||||
@@ -108,10 +110,7 @@ class PiFactory(Factory):
|
|||||||
'port': 0,
|
'port': 0,
|
||||||
'device': {8: 0, 7: 1}[spi_args['select_pin']],
|
'device': {8: 0, 7: 1}[spi_args['select_pin']],
|
||||||
}
|
}
|
||||||
if shared:
|
return self.spi_classes[('hardware', shared)](self, **hardware_spi_args)
|
||||||
return self.shared_spi_hardware_class(self, **hardware_spi_args)
|
|
||||||
else:
|
|
||||||
return self.spi_hardware_class(self, **hardware_spi_args)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
SPISoftwareFallback(
|
SPISoftwareFallback(
|
||||||
@@ -125,10 +124,7 @@ class PiFactory(Factory):
|
|||||||
key: pin.number if isinstance(pin, Pin) else pin
|
key: pin.number if isinstance(pin, Pin) else pin
|
||||||
for key, pin in spi_args.items()
|
for key, pin in spi_args.items()
|
||||||
}
|
}
|
||||||
if shared:
|
return self.spi_classes[('software', shared)](self, **spi_args)
|
||||||
return self.shared_spi_software_class(self, **spi_args)
|
|
||||||
else:
|
|
||||||
return self.spi_software_class(self, **spi_args)
|
|
||||||
|
|
||||||
def _extract_spi_args(self, **kwargs):
|
def _extract_spi_args(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -76,10 +76,12 @@ class PiGPIOFactory(PiFactory):
|
|||||||
port=int(os.getenv('PIGPIO_PORT', 8888))):
|
port=int(os.getenv('PIGPIO_PORT', 8888))):
|
||||||
super(PiGPIOFactory, self).__init__()
|
super(PiGPIOFactory, self).__init__()
|
||||||
self.pin_class = PiGPIOPin
|
self.pin_class = PiGPIOPin
|
||||||
self.spi_hardware_class = PiGPIOHardwareSPI
|
self.spi_classes = {
|
||||||
self.spi_software_class = PiGPIOSoftwareSPI
|
('hardware', 'exclusive'): PiGPIOHardwareSPI,
|
||||||
self.shared_spi_hardware_class = PiGPIOHardwareSPIShared
|
('hardware', 'shared'): PiGPIOHardwareSPIShared,
|
||||||
self.shared_spi_software_class = PiGPIOSoftwareSPIShared
|
('software', 'exclusive'): PiGPIOSoftwareSPI,
|
||||||
|
('software', 'shared'): PiGPIOSoftwareSPIShared,
|
||||||
|
}
|
||||||
self._connection = pigpio.pi(host, port)
|
self._connection = pigpio.pi(host, port)
|
||||||
self._host = host
|
self._host = host
|
||||||
self._port = port
|
self._port = port
|
||||||
|
|||||||
Reference in New Issue
Block a user