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,6 +211,7 @@ 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 | ||||||
|  |         try: | ||||||
|             super(LEDCollection, self).__init__( |             super(LEDCollection, self).__init__( | ||||||
|                 *( |                 *( | ||||||
|                     pin_or_collection |                     pin_or_collection | ||||||
| @@ -225,6 +226,9 @@ class LEDCollection(CompositeOutputDevice): | |||||||
|                     LEDClass(pin_or_collection, active_high, initial_value) |                     LEDClass(pin_or_collection, active_high, initial_value) | ||||||
|                     for name, pin_or_collection in kwargs.items() |                     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