improve namedbidict

- raise TypeError if base_type is not a frozenbidict subclass
- rewrite the code to be clearer
This commit is contained in:
jab 2018-02-19 16:57:42 +11:00
parent 5a8ab033e8
commit f1749540cb
2 changed files with 41 additions and 23 deletions

View File

@ -79,8 +79,19 @@ Breaking API Changes
(So it is no longer possible to create an infinite chain like
``DuplicationPolicy.RAISE.RAISE.RAISE...``.)
- Pickling bidicts on Python 2 now requires the latest version of the
pickle protocol (specified via -1), e.g. ``pickle.dumps(mybidict, -1)``
- :func:`~bidict.namedbidict` now raises :class:`TypeError` if the provided
``base_type`` is not a subclass of :class:`~bidict.frozenbidict`.
- Pickling ordered bidicts now requires
at least version 2 of the pickle protocol.
If you are using Python 3,
:attr:`pickle.DEFAULT_PROTOCOL` is 3 anyway,
so this will not affect you.
However if you are using in Python 2,
:attr:`~pickle.DEFAULT_PROTOCOL` is 0,
so you must now explicitly specify the version
in your :func:`pickle.dumps` calls,
e.g. ``pickle.dumps(ob, 2)``.
0.14.2 (2017-12-06)

View File

@ -9,6 +9,7 @@
import re
from ._frozen import frozenbidict
from ._bidict import bidict
@ -22,36 +23,42 @@ def namedbidict(typename, keyname, valname, base_type=bidict):
Analagous to :func:`collections.namedtuple`.
"""
if not issubclass(base_type, frozenbidict):
raise TypeError('base_type must be a subclass of frozenbidict')
for name in typename, keyname, valname:
if not _LEGALNAMERE.match(name):
raise ValueError('"%s" does not match pattern %s' %
(name, _LEGALNAMEPAT))
raise ValueError('%r does not match pattern %s' % (name, _LEGALNAMEPAT))
getfwd = lambda self: self.inv if self._isinv else self # pylint: disable=protected-access
getfwd.__name__ = valname + '_for'
getfwd.__doc__ = u'%s forward %s: %s%s' % (typename, base_type.__name__, keyname, valname)
class _Named(base_type):
getinv = lambda self: self if self._isinv else self.inv # pylint: disable=protected-access
getinv.__name__ = keyname + '_for'
getinv.__doc__ = u'%s inverse %s: %s%s' % (typename, base_type.__name__, valname, keyname)
__slots__ = ()
__reduce__ = lambda self: (
_make_empty, (typename, keyname, valname, base_type), self.__getstate__())
__reduce__.__name__ = '__reduce__'
__reduce__.__doc__ = 'helper for pickle'
def _getfwd(self):
return self.inv if self._isinv else self # pylint: disable=protected-access
__dict__ = {
getfwd.__name__: property(getfwd),
getinv.__name__: property(getinv),
'__reduce__': __reduce__,
}
return type(typename, (base_type,), __dict__)
def _getinv(self):
return self if self._isinv else self.inv # pylint: disable=protected-access
def __reduce__(self):
return (_make_empty, (typename, keyname, valname, base_type), self.__getstate__())
bname = base_type.__name__
fname = valname + '_for'
iname = keyname + '_for'
names = dict(typename=typename, bname=bname, keyname=keyname, valname=valname)
fdoc = u'{typename} forward {bname}: {keyname}{valname}'.format(**names)
idoc = u'{typename} inverse {bname}: {valname}{keyname}'.format(**names)
setattr(_Named, fname, property(_Named._getfwd, doc=fdoc))
setattr(_Named, iname, property(_Named._getinv, doc=idoc))
_Named.__name__ = typename
return _Named
def _make_empty(typename, keyname, valname, base_type):
"""
Create a named bidict with the indicated arguments and return an empty instance.
"""Create a named bidict with the indicated arguments and return an empty instance.
Used to make :func:`bidict.namedbidict` instances picklable.
"""
cls = namedbidict(typename, keyname, valname, base_type=base_type)