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 (So it is no longer possible to create an infinite chain like
``DuplicationPolicy.RAISE.RAISE.RAISE...``.) ``DuplicationPolicy.RAISE.RAISE.RAISE...``.)
- Pickling bidicts on Python 2 now requires the latest version of the - :func:`~bidict.namedbidict` now raises :class:`TypeError` if the provided
pickle protocol (specified via -1), e.g. ``pickle.dumps(mybidict, -1)`` ``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) 0.14.2 (2017-12-06)

View File

@ -9,6 +9,7 @@
import re import re
from ._frozen import frozenbidict
from ._bidict import bidict from ._bidict import bidict
@ -22,36 +23,42 @@ def namedbidict(typename, keyname, valname, base_type=bidict):
Analagous to :func:`collections.namedtuple`. 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: for name in typename, keyname, valname:
if not _LEGALNAMERE.match(name): if not _LEGALNAMERE.match(name):
raise ValueError('"%s" does not match pattern %s' % raise ValueError('%r does not match pattern %s' % (name, _LEGALNAMEPAT))
(name, _LEGALNAMEPAT))
getfwd = lambda self: self.inv if self._isinv else self # pylint: disable=protected-access class _Named(base_type):
getfwd.__name__ = valname + '_for'
getfwd.__doc__ = u'%s forward %s: %s%s' % (typename, base_type.__name__, keyname, valname)
getinv = lambda self: self if self._isinv else self.inv # pylint: disable=protected-access __slots__ = ()
getinv.__name__ = keyname + '_for'
getinv.__doc__ = u'%s inverse %s: %s%s' % (typename, base_type.__name__, valname, keyname)
__reduce__ = lambda self: ( def _getfwd(self):
_make_empty, (typename, keyname, valname, base_type), self.__getstate__()) return self.inv if self._isinv else self # pylint: disable=protected-access
__reduce__.__name__ = '__reduce__'
__reduce__.__doc__ = 'helper for pickle'
__dict__ = { def _getinv(self):
getfwd.__name__: property(getfwd), return self if self._isinv else self.inv # pylint: disable=protected-access
getinv.__name__: property(getinv),
'__reduce__': __reduce__, def __reduce__(self):
} return (_make_empty, (typename, keyname, valname, base_type), self.__getstate__())
return type(typename, (base_type,), __dict__)
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): 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. Used to make :func:`bidict.namedbidict` instances picklable.
""" """
cls = namedbidict(typename, keyname, valname, base_type=base_type) cls = namedbidict(typename, keyname, valname, base_type=base_type)