mirror of
https://github.com/KevinMidboe/python-gpiozero.git
synced 2025-10-29 17:50:37 +00:00
Make CompositeDevice._named a frozendict (and add frozendict to compat.py)
This prevents it being modified post-construction (just like the way CompositeDevice._all and CompositeDevice._order are already 'frozen' by being tuples)
This commit is contained in:
@@ -9,6 +9,9 @@ from __future__ import (
|
|||||||
str = type('')
|
str = type('')
|
||||||
|
|
||||||
import cmath
|
import cmath
|
||||||
|
import collections
|
||||||
|
import operator
|
||||||
|
import functools
|
||||||
|
|
||||||
|
|
||||||
# Back-ported from python 3.5; see
|
# Back-ported from python 3.5; see
|
||||||
@@ -51,3 +54,30 @@ def median(data):
|
|||||||
i = n // 2
|
i = n // 2
|
||||||
return (data[i - 1] + data[i]) / 2
|
return (data[i - 1] + data[i]) / 2
|
||||||
|
|
||||||
|
|
||||||
|
# Copied from the MIT-licensed https://github.com/slezica/python-frozendict
|
||||||
|
class frozendict(collections.Mapping):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.__dict = dict(*args, **kwargs)
|
||||||
|
self.__hash = None
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return self.__dict[key]
|
||||||
|
|
||||||
|
def copy(self, **add_or_replace):
|
||||||
|
return frozendict(self, **add_or_replace)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.__dict)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.__dict)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<frozendict %s>' % repr(self.__dict)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
if self.__hash is None:
|
||||||
|
hashes = map(hash, self.items())
|
||||||
|
self.__hash = functools.reduce(operator.xor, hashes, 0)
|
||||||
|
return self.__hash
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ from .exc import (
|
|||||||
GPIOPinInUse,
|
GPIOPinInUse,
|
||||||
GPIODeviceClosed,
|
GPIODeviceClosed,
|
||||||
)
|
)
|
||||||
|
from .compat import frozendict
|
||||||
|
|
||||||
# Get a pin implementation to use as the default; we prefer RPi.GPIO's here
|
# Get a pin implementation to use as the default; we prefer RPi.GPIO's here
|
||||||
# as it supports PWM, and all Pi revisions. If no third-party libraries are
|
# as it supports PWM, and all Pi revisions. If no third-party libraries are
|
||||||
@@ -263,7 +264,7 @@ class CompositeDevice(Device):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self._all = ()
|
self._all = ()
|
||||||
self._named = {}
|
self._named = frozendict({})
|
||||||
self._namedtuple = None
|
self._namedtuple = None
|
||||||
self._order = kwargs.pop('_order', None)
|
self._order = kwargs.pop('_order', None)
|
||||||
if self._order is None:
|
if self._order is None:
|
||||||
@@ -278,7 +279,7 @@ class CompositeDevice(Device):
|
|||||||
for dev in self._all:
|
for dev in self._all:
|
||||||
if not isinstance(dev, Device):
|
if not isinstance(dev, Device):
|
||||||
raise CompositeDeviceBadDevice("%s doesn't inherit from Device" % dev)
|
raise CompositeDeviceBadDevice("%s doesn't inherit from Device" % dev)
|
||||||
self._named = 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),
|
(str(i) for i in range(len(args))), self._order),
|
||||||
rename=True)
|
rename=True)
|
||||||
@@ -286,7 +287,7 @@ class CompositeDevice(Device):
|
|||||||
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
|
||||||
if name == '_named':
|
if name == '_named':
|
||||||
return {}
|
return frozendict({})
|
||||||
try:
|
try:
|
||||||
return self._named[name]
|
return self._named[name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|||||||
Reference in New Issue
Block a user