Skip to content

Commit

Permalink
Merge pull request #942 from gpiozero/coverage-bump
Browse files Browse the repository at this point in the history
Coverage bump
  • Loading branch information
waveform80 committed Mar 14, 2021
2 parents 9506ac1 + dd5fcdd commit a4e1574
Show file tree
Hide file tree
Showing 28 changed files with 692 additions and 322 deletions.
4 changes: 2 additions & 2 deletions coverage.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ ignore_errors = True
show_missing = True
exclude_lines =
pragma: no cover
if self\.debug
assert False
raise AssertionError
raise NotImplementedError
if 0:
pass
if __name__ == .__main__.:

[html]
Expand Down
25 changes: 18 additions & 7 deletions gpiozero/boards.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ def __init__(self, *args, **kwargs):
def get_new_handler(device):
def fire_both_events(ticks, state):
device._fire_events(ticks, device._state_to_value(state))
self._fire_events(ticks, self.value)
self._fire_events(ticks, self.is_active)
return fire_both_events
# _handlers only exists to ensure that we keep a reference to the
# generated fire_both_events handler for each Button (remember that
Expand Down Expand Up @@ -982,9 +982,16 @@ class LEDCharDisplay(LEDCollection):
.. _Multi-segment LED displays: https://en.wikipedia.org/wiki/Seven-segment_display
"""
def __init__(
self, *pins, dp=None, font=None, pwm=False, active_high=True,
initial_value=" ", pin_factory=None):
def __init__(self, *pins, **kwargs):
dp = kwargs.pop('dp', None)
font = kwargs.pop('font', None)
pwm = kwargs.pop('pwm', False)
active_high = kwargs.pop('active_high', True)
initial_value = kwargs.pop('initial_value', " ")
pin_factory = kwargs.pop('pin_factory', None)
if kwargs:
raise TypeError(
'unexpected keyword argument: %s' % kwargs.popiem()[0])
if not 1 < len(pins) <= 26:
raise PinInvalidPin(
'Must have between 2 and 26 LEDs in LEDCharDisplay')
Expand Down Expand Up @@ -1128,9 +1135,13 @@ class LEDMultiCharDisplay(CompositeOutputDevice):
.. _multiplexed: https://en.wikipedia.org/wiki/Multiplexed_display
.. _persistence of vision: https://en.wikipedia.org/wiki/Persistence_of_vision
"""
def __init__(
self, char, *pins, active_high=True, initial_value=None,
pin_factory=None):
def __init__(self, char, *pins, **kwargs):
active_high = kwargs.pop('active_high', True)
initial_value = kwargs.pop('initial_value', None)
pin_factory = kwargs.pop('pin_factory', None)
if kwargs:
raise TypeError(
'unexpected keyword argument: %s' % kwargs.popiem()[0])
if not isinstance(char, LEDCharDisplay):
raise ValueError('char must be an LEDCharDisplay')
if initial_value is None:
Expand Down
10 changes: 10 additions & 0 deletions gpiozero/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ def median(data):
return (data[i - 1] + data[i]) / 2


# Backported from py3.4
def mean(data):
if iter(data) is data:
data = list(data)
n = len(data)
if n < 1:
raise ValueError('mean requires at least one data point')
return sum(data) / n


# Backported from py3.3
def log2(x):
return math.log(x, 2)
Expand Down
45 changes: 36 additions & 9 deletions gpiozero/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
from collections import namedtuple, OrderedDict
from itertools import chain
from types import FunctionType
from threading import Lock

from .threads import _threads_shutdown
from .mixins import (
Expand Down Expand Up @@ -101,9 +100,9 @@ def __call__(cls, *args, **kwargs):
# already exists. Only construct the instance if the key's new.
key = cls._shared_key(*args, **kwargs)
try:
self = cls._instances[key]
self = cls._instances[key]()
self._refs += 1
except (KeyError, ReferenceError) as e:
except (KeyError, AttributeError) as e:
self = super(GPIOMeta, cls).__call__(*args, **kwargs)
self._refs = 1
# Replace the close method with one that merely decrements
Expand All @@ -124,7 +123,7 @@ def close():
# it's already gone
pass
self.close = close
cls._instances[key] = weakref.proxy(self)
cls._instances[key] = weakref.ref(self)
else:
# Construct the instance as normal
self = super(GPIOMeta, cls).__call__(*args, **kwargs)
Expand Down Expand Up @@ -154,6 +153,26 @@ def __setattr__(self, name, value):
return super(GPIOBase, self).__setattr__(name, value)

def __del__(self):
# NOTE: Yes, we implicitly call close() on __del__(), and yes for you
# dear hacker-on-this-library, this means pain!
#
# It's entirely for the convenience of command line experimenters and
# newbies who want to re-gain those pins when stuff falls out of scope
# without managing their object lifetimes "properly" with "with" (but,
# hey, this is an educational library at heart so that's the way we
# roll).
#
# What does this mean for you? It means that in close() you cannot
# assume *anything*. If someone calls a constructor with a fundamental
# mistake like the wrong number of params, then your close() method is
# going to be called before __init__ ever ran so all those attributes
# you *think* exist, erm, don't. Basically if you refer to anything in
# "self" within your close method, be preprared to catch AttributeError
# on its access to avoid spurious warnings for the end user.
#
# "But we're exiting anyway; surely exceptions in __del__ get
# squashed?" Yes, but they still cause verbose warnings and remember
# that this is an educational library; keep it friendly!
self.close()

def close(self):
Expand Down Expand Up @@ -199,6 +218,8 @@ def close(self):
# safely called from subclasses without worrying whether super-classes
# have it (which in turn is useful in conjunction with the mixin
# classes).
#
# P.S. See note in __del__ above.
pass

@property
Expand Down Expand Up @@ -310,7 +331,11 @@ def _default_pin_factory():
raise BadPinFactory('Unable to find pin factory "%s"' % name)

def __repr__(self):
return "<gpiozero.%s object>" % (self.__class__.__name__)
try:
self._check_open()
return "<gpiozero.%s object>" % (self.__class__.__name__)
except DeviceClosed:
return "<gpiozero.%s object closed>" % (self.__class__.__name__)

def _conflicts_with(self, other):
"""
Expand Down Expand Up @@ -465,7 +490,7 @@ def __repr__(self):
len(self)
)
except DeviceClosed:
return "<gpiozero.%s object closed>" % (self.__class__.__name__)
return super(CompositeDevice, self).__repr__()

def __len__(self):
return len(self._all)
Expand All @@ -484,8 +509,7 @@ def all(self):
def close(self):
if getattr(self, '_all', None):
for device in self._all:
if isinstance(device, Device):
device.close()
device.close()
self._all = ()

@property
Expand Down Expand Up @@ -567,7 +591,10 @@ def close(self):

@property
def closed(self):
return self._pin is None
try:
return self._pin is None
except AttributeError:
return True

def _check_open(self):
try:
Expand Down
25 changes: 8 additions & 17 deletions gpiozero/input_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
try:
from statistics import median, mean
except ImportError:
from .compat import median
from .compat import median, mean

from .exc import InputDeviceError, DeviceClosed, DistanceSensorNoEcho, \
PinInvalidState, PWMSoftwareFallback
Expand Down Expand Up @@ -295,8 +295,7 @@ def close(self):
except AttributeError:
# If the queue isn't initialized (it's None), or _queue hasn't been
# set ignore the error because we're trying to close anyway
if self._queue is not None:
raise
pass
except RuntimeError:
# Cannot join thread before it starts; we don't care about this
# because we're trying to close the thread anyway
Expand Down Expand Up @@ -525,11 +524,7 @@ def __init__(
threshold=threshold, queue_len=queue_len,
sample_wait=1 / sample_rate, partial=partial,
pin_factory=pin_factory)
try:
self._queue.start()
except:
self.close()
raise
self._queue.start()

@property
def value(self):
Expand Down Expand Up @@ -617,11 +612,7 @@ def __init__(
pin, pull_up=pull_up, active_state=active_state,
threshold=threshold, queue_len=queue_len, sample_wait=1 /
sample_rate, partial=partial, pin_factory=pin_factory, average=mean)
try:
self._queue.start()
except:
self.close()
raise
self._queue.start()

@property
def value(self):
Expand Down Expand Up @@ -734,7 +725,7 @@ def _read(self):
if self._charge_time is None:
return 0.0
else:
return 1.0 - min(1.0,
return 1.0 - min(1.0,
(self.pin_factory.ticks_diff(self._charge_time, start) /
self.charge_time_limit))

Expand Down Expand Up @@ -891,8 +882,7 @@ def close(self):
try:
self._trigger.close()
except AttributeError:
if self._trigger is not None:
raise
pass
self._trigger = None
super(DistanceSensor, self).close()

Expand Down Expand Up @@ -1193,9 +1183,10 @@ def __init__(

def __repr__(self):
try:
self._check_open()
return "<gpiozero.%s object on pins %r and %r>" % (
self.__class__.__name__, self.a.pin, self.b.pin)
except:
except DeviceClosed:
return super(RotaryEncoder, self).__repr__()

def _a_changed(self, ticks, state):
Expand Down

0 comments on commit a4e1574

Please sign in to comment.