Change CompositeDevice to reject invalid identifiers

Also updated StatusBoard and StatusZero to reject duplicate identifiers
(namedtuple doesn't pick 'em up because they're passed in a dict and
thus the dups are squashed prior to the call). Added tests for all the
relevant stuff.
This commit is contained in:
Dave Jones
2017-06-25 20:44:13 +01:00
parent c9857d55ce
commit 56f2152daf
4 changed files with 43 additions and 20 deletions

View File

@@ -12,7 +12,7 @@ except ImportError:
from time import sleep from time import sleep
from itertools import repeat, cycle, chain from itertools import repeat, cycle, chain
from threading import Lock from threading import Lock
from collections import OrderedDict from collections import OrderedDict, Counter
from .exc import ( from .exc import (
DeviceClosed, DeviceClosed,
@@ -819,6 +819,8 @@ class StatusZero(LEDBoard):
.. _STATUS Zero: https://thepihut.com/statuszero .. _STATUS Zero: https://thepihut.com/statuszero
""" """
default_labels = ('one', 'two', 'three')
def __init__(self, *labels, **kwargs): def __init__(self, *labels, **kwargs):
pins = ( pins = (
(17, 4), (17, 4),
@@ -826,12 +828,14 @@ class StatusZero(LEDBoard):
(9, 10), (9, 10),
) )
if len(labels) == 0: if len(labels) == 0:
labels = ['one', 'two', 'three'] labels = self.default_labels
elif len(labels) > len(pins): elif len(labels) > len(pins):
raise ValueError raise ValueError("StatusZero doesn't support more than three labels")
dup, count = Counter(labels).most_common(1)[0]
if count > 1:
raise ValueError("Duplicate label %s" % dup)
strips = OrderedDict() strips = OrderedDict()
for index, label in enumerate(labels): for (green, red), label in zip(pins, labels):
green, red = pins[index]
strips[label] = LEDBoard(red=red, green=green, strips[label] = LEDBoard(red=red, green=green,
_order=('red', 'green'), **kwargs) _order=('red', 'green'), **kwargs)
super(StatusZero, self).__init__(_order=strips.keys(), **strips) super(StatusZero, self).__init__(_order=strips.keys(), **strips)
@@ -861,6 +865,8 @@ class StatusBoard(CompositeOutputDevice):
.. _STATUS: https://thepihut.com/status .. _STATUS: https://thepihut.com/status
""" """
default_labels = ('one', 'two', 'three', 'four', 'five')
def __init__(self, *labels, **kwargs): def __init__(self, *labels, **kwargs):
pins = ( pins = (
(17, 4, 14), (17, 4, 14),
@@ -870,12 +876,14 @@ class StatusBoard(CompositeOutputDevice):
(13, 6, 18), (13, 6, 18),
) )
if len(labels) == 0: if len(labels) == 0:
labels = ['one', 'two', 'three', 'four', 'five'] labels = self.default_labels
elif len(labels) > len(pins): elif len(labels) > len(pins):
raise ValueError raise ValueError("StatusBoard doesn't support more than five labels")
dup, count = Counter(labels).most_common(1)[0]
if count > 1:
raise ValueError("Duplicate label %s" % dup)
strips = OrderedDict() strips = OrderedDict()
for index, label in enumerate(labels): for (green, red, button), label in zip(pins, labels):
green, red, button = pins[index]
strips[label] = CompositeOutputDevice( strips[label] = CompositeOutputDevice(
button=Button(button), button=Button(button),
lights=LEDBoard( lights=LEDBoard(

View File

@@ -330,8 +330,7 @@ class CompositeDevice(Device):
raise CompositeDeviceBadDevice("%s doesn't inherit from Device" % dev) raise CompositeDeviceBadDevice("%s doesn't inherit from Device" % dev)
self._named = frozendict(kwargs) self._named = frozendict(kwargs)
self._namedtuple = namedtuple('%sValue' % self.__class__.__name__, chain( self._namedtuple = namedtuple('%sValue' % self.__class__.__name__, chain(
(str(i) for i in range(len(args))), self._order), ('device_%d' % i for i in range(len(args))), self._order))
rename=True)
def __getattr__(self, name): def __getattr__(self, name):
# if _named doesn't exist yet, pretend it's an empty dict # if _named doesn't exist yet, pretend it's an empty dict

View File

@@ -783,13 +783,21 @@ def test_energenie():
def test_statuszero_init(): def test_statuszero_init():
with StatusZero() as sz: with StatusZero() as sz:
assert sz assert sz.namedtuple._fields == ('one', 'two', 'three')
with StatusZero('a') as sz: with StatusZero('a') as sz:
assert sz assert sz.namedtuple._fields == ('a',)
with StatusZero('a', 'b') as sz: with StatusZero('a', 'b') as sz:
assert sz assert sz.namedtuple._fields == ('a', 'b')
with StatusZero('a', 'b', 'c') as sz: with StatusZero('a', 'b', 'c') as sz:
assert sz assert sz.namedtuple._fields == ('a', 'b', 'c')
with pytest.raises(ValueError):
StatusZero('a', 'b', 'c', 'd')
with pytest.raises(ValueError):
StatusZero('0')
with pytest.raises(ValueError):
StatusZero('foo', 'hello world')
with pytest.raises(ValueError):
StatusZero('foo', 'foo')
def test_statuszero(): def test_statuszero():
with StatusZero() as sz: with StatusZero() as sz:
@@ -824,13 +832,21 @@ def test_statuszero_named():
def test_statusboard_init(): def test_statusboard_init():
with StatusBoard() as sb: with StatusBoard() as sb:
assert sb assert sb.namedtuple._fields == ('one', 'two', 'three', 'four', 'five')
with StatusBoard('a') as sb: with StatusBoard('a') as sb:
assert sb assert sb.namedtuple._fields == ('a',)
with StatusBoard('a', 'b') as sb: with StatusBoard('a', 'b') as sb:
assert sb assert sb.namedtuple._fields == ('a', 'b',)
with StatusBoard('a', 'b', 'c', 'd', 'e') as sb: with StatusBoard('a', 'b', 'c', 'd', 'e') as sb:
assert sb assert sb.namedtuple._fields == ('a', 'b', 'c', 'd', 'e')
with pytest.raises(ValueError):
StatusBoard('a', 'b', 'c', 'd', 'e', 'f')
with pytest.raises(ValueError):
StatusBoard('0')
with pytest.raises(ValueError):
StatusBoard('foo', 'hello world')
with pytest.raises(ValueError):
StatusBoard('foo', 'foo')
def test_statusboard(): def test_statusboard():
with StatusBoard() as sb: with StatusBoard() as sb:

View File

@@ -91,7 +91,7 @@ def test_composite_device_sequence():
assert len(device) == 2 assert len(device) == 2
assert device[0].pin.number == 4 assert device[0].pin.number == 4
assert device[1].pin.number == 5 assert device[1].pin.number == 5
assert device.namedtuple._fields == ('_0', '_1') assert device.namedtuple._fields == ('device_0', 'device_1')
def test_composite_device_values(): def test_composite_device_values():
with CompositeDevice( with CompositeDevice(