mirror of https://github.com/jab/bidict.git
resurrect delegating mixins instead of __delegate__ + other refinements
e.g. s/__repr_delegate__/_repr_delegate Also - update - update from pytest 3 to 4 - add CII best practices badge - prepare for 0.17.5 release
This commit is contained in:
parent
e70816c6fa
commit
d13fd9bb8a
|
@ -22,6 +22,26 @@ Tip: `Subscribe to bidict releases <https://libraries.io/pypi/bidict>`__
|
|||
on libraries.io to be notified when new versions of bidict are released.
|
||||
|
||||
|
||||
0.17.5 (2018-11-19)
|
||||
-------------------
|
||||
|
||||
Improvements to performance and delegation logic,
|
||||
with minor breaking changes to semi-private APIs.
|
||||
|
||||
- Remove the ``__delegate__`` instance attribute added in the previous release.
|
||||
It was overly general and not worth the cost.
|
||||
|
||||
Instead of checking ``self.__delegate__`` and delegating accordingly
|
||||
each time a possibly-delegating method is called,
|
||||
revert back to using "delegated-to-fwdm" mixin classes
|
||||
(now found in ``bidict._delegating_mixins``),
|
||||
and resurrect a mutable bidict parent class that omits the mixins
|
||||
as :class:`bidict.MutableBidict`.
|
||||
|
||||
- Rename ``__repr_delegate__`` to
|
||||
:class:`~bidict.BidictBase._repr_delegate`.
|
||||
|
||||
|
||||
0.17.4 (2018-11-14)
|
||||
-------------------
|
||||
|
||||
|
@ -39,8 +59,8 @@ Minor code, interop, and (semi-)private API improvements.
|
|||
and instead move their methods
|
||||
into :class:`~bidict.BidictBase`,
|
||||
which now checks for an object defined by the
|
||||
:attr:`~bidict.BidictBase.__delegate__` attribute.
|
||||
The :attr:`~bidict.BidictBase.__delegate__` object
|
||||
``BidictBase.__delegate__`` attribute.
|
||||
The ``BidictBase.__delegate__`` object
|
||||
will be delegated to if the method is available on it,
|
||||
otherwise a default implementation
|
||||
(e.g. inherited from :class:`~collections.abc.Mapping`)
|
||||
|
@ -50,8 +70,8 @@ Minor code, interop, and (semi-)private API improvements.
|
|||
Consolidate ``_MutableBidict`` into :class:`bidict.bidict`
|
||||
now that the dropped mixin classes make it unnecessary.
|
||||
|
||||
- Change :attr:`~bidict.BidictBase.__repr_delegate__`
|
||||
to take simply a type like :class:`dict` or :class:`list`.
|
||||
- Change ``__repr_delegate__``
|
||||
to simply take a type like :class:`dict` or :class:`list`.
|
||||
|
||||
- Upgrade to latest major
|
||||
`sortedcontainers <https://github.com/grantjenks/python-sortedcontainers>`__
|
||||
|
@ -64,7 +84,7 @@ Minor code, interop, and (semi-)private API improvements.
|
|||
:class:`~collections.abc.Mapping` interface,
|
||||
and provides fallback implementations when the methods are unavailable.
|
||||
This allows the :ref:`extending:Sorted Bidict Recipes`
|
||||
to continue to work with sortedcontainers v2 on Python2.
|
||||
to continue to work with sortedcontainers v2 on Python 2.
|
||||
|
||||
|
||||
0.17.3 (2018-09-18)
|
||||
|
@ -221,7 +241,7 @@ Miscellaneous
|
|||
|
||||
- :func:`~bidict.BidictBase.__repr__` no longer checks for a ``__reversed__``
|
||||
method to determine whether to use an ordered or unordered-style repr.
|
||||
It now calls the new :attr:`~bidict.BidictBase.__repr_delegate__` instead
|
||||
It now calls the new ``__repr_delegate__`` instead
|
||||
(which may be overridden if needed), for better composability.
|
||||
|
||||
Minor Breaking API Changes
|
||||
|
|
|
@ -41,6 +41,10 @@ Status
|
|||
:target: https://www.codacy.com/app/jab/bidict
|
||||
:alt: Codacy grade
|
||||
|
||||
.. image:: https://bestpractices.coreinfrastructure.org/projects/2354/badge
|
||||
:target: https://bestpractices.coreinfrastructure.org/en/projects/2354
|
||||
:alt: CII best practices badge
|
||||
|
||||
.. image:: https://tidelift.com/badges/github/jab/bidict
|
||||
:target: https://tidelift.com/subscription/pkg/pypi-bidict?utm_source=pypi-bidict&utm_medium=referral&utm_campaign=docs
|
||||
:alt: Tidelift dependency badge
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
// Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
* Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -44,6 +44,7 @@ Efficient, Pythonic bidirectional map implementation and related functionality.
|
|||
|
||||
from ._abc import BidirectionalMapping
|
||||
from ._base import BidictBase
|
||||
from ._mut import MutableBidict
|
||||
from ._bidict import bidict
|
||||
from ._dup import DuplicationPolicy, IGNORE, OVERWRITE, RAISE
|
||||
from ._exc import (
|
||||
|
@ -84,6 +85,7 @@ __all__ = (
|
|||
'ValueDuplicationError',
|
||||
'KeyAndValueDuplicationError',
|
||||
'BidictBase',
|
||||
'MutableBidict',
|
||||
'frozenbidict',
|
||||
'bidict',
|
||||
'namedbidict',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -22,7 +22,7 @@
|
|||
|
||||
# * Code review nav *
|
||||
#==============================================================================
|
||||
# ← Prev: _abc.py Current: _base.py Next: _frozenbidict.py →
|
||||
# ← Prev: _abc.py Current: _base.py Next: _delegating_mixins.py →
|
||||
#==============================================================================
|
||||
|
||||
|
||||
|
@ -38,7 +38,7 @@ from ._exc import (
|
|||
from ._miss import _MISS
|
||||
from ._noop import _NOOP
|
||||
from ._util import _iteritems_args_kw
|
||||
from .compat import PY2, KeysView, ItemsView, iteritems, Mapping
|
||||
from .compat import PY2, KeysView, ItemsView, Mapping, iteritems
|
||||
|
||||
|
||||
# Since BidirectionalMapping implements __subclasshook__, and BidictBase
|
||||
|
@ -52,10 +52,7 @@ from .compat import PY2, KeysView, ItemsView, iteritems, Mapping
|
|||
class BidictBase(BidirectionalMapping):
|
||||
"""Base class implementing :class:`BidirectionalMapping`."""
|
||||
|
||||
__slots__ = ['_fwdm', '_invm', '_inv', '_invweak', '_hash']
|
||||
|
||||
if not PY2:
|
||||
__slots__.append('__weakref__')
|
||||
__slots__ = ('_fwdm', '_invm', '_inv', '_invweak', '_hash') + (() if PY2 else ('__weakref__',))
|
||||
|
||||
#: The default :class:`DuplicationPolicy`
|
||||
#: (in effect during e.g. :meth:`~bidict.bidict.__init__` calls)
|
||||
|
@ -94,7 +91,7 @@ class BidictBase(BidirectionalMapping):
|
|||
_invm_cls = dict
|
||||
|
||||
#: The object used by :meth:`__repr__` for printing the contained items.
|
||||
__repr_delegate__ = dict
|
||||
_repr_delegate = dict
|
||||
|
||||
def __init__(self, *args, **kw): # pylint: disable=super-init-not-called
|
||||
"""Make a new bidirectional dictionary.
|
||||
|
@ -194,7 +191,7 @@ class BidictBase(BidirectionalMapping):
|
|||
clsname = self.__class__.__name__
|
||||
if not self:
|
||||
return '%s()' % clsname
|
||||
return '%s(%r)' % (clsname, self.__repr_delegate__(iteritems(self)))
|
||||
return '%s(%r)' % (clsname, self._repr_delegate(iteritems(self)))
|
||||
|
||||
# The inherited Mapping.__eq__ implementation would work, but it's implemented in terms of an
|
||||
# inefficient ``dict(self.items()) == dict(other.items())`` comparison, so override it with a
|
||||
|
@ -400,28 +397,16 @@ class BidictBase(BidirectionalMapping):
|
|||
"""The number of contained items."""
|
||||
return len(self._fwdm)
|
||||
|
||||
@property
|
||||
def __delegate__(self):
|
||||
"""An object to delegate to for optimized implementations
|
||||
of various operations (e.g. :meth:`keys`) if available.
|
||||
Override or set e.g. ``__delegate__ = None`` in a subclass to disable.
|
||||
"""
|
||||
return self._fwdm
|
||||
|
||||
def __iter__(self): # lgtm [py/inheritance/incorrect-overridden-signature]
|
||||
"""Iterator over the contained items."""
|
||||
delegate = getattr(self.__delegate__, '__iter__', lambda: iter(self.keys()))
|
||||
return delegate()
|
||||
# No default implementation for __iter__ inherited from Mapping ->
|
||||
# always delegate to _fwdm.
|
||||
return iter(self._fwdm)
|
||||
|
||||
def __getitem__(self, key):
|
||||
u"""*x.__getitem__(key) ⟺ x[key]*"""
|
||||
return self._fwdm[key]
|
||||
|
||||
def keys(self):
|
||||
"""A set-like object providing a view on the contained keys."""
|
||||
delegate = getattr(self.__delegate__, 'keys', super(BidictBase, self).keys)
|
||||
return delegate()
|
||||
|
||||
def values(self):
|
||||
"""A set-like object providing a view on the contained values.
|
||||
|
||||
|
@ -434,18 +419,16 @@ class BidictBase(BidirectionalMapping):
|
|||
"""
|
||||
return self.inv.keys()
|
||||
|
||||
def items(self):
|
||||
"""A set-like object providing a view on the contained items."""
|
||||
delegate = getattr(self.__delegate__, 'items', super(BidictBase, self).items)
|
||||
return delegate()
|
||||
|
||||
if PY2:
|
||||
def viewkeys(self): # noqa: D102; pylint: disable=missing-docstring
|
||||
delegate = getattr(self.__delegate__, 'viewkeys', lambda: KeysView(self))
|
||||
return delegate()
|
||||
# For iterkeys and iteritems, inheriting from Mapping already provides
|
||||
# the best default implementations so no need to define here.
|
||||
|
||||
viewkeys.__doc__ = keys.__doc__
|
||||
keys.__doc__ = 'A list of the contained keys.'
|
||||
def itervalues(self):
|
||||
"""An iterator over the contained values."""
|
||||
return self.inv.iterkeys()
|
||||
|
||||
def viewkeys(self): # noqa: D102; pylint: disable=missing-docstring
|
||||
return KeysView(self)
|
||||
|
||||
def viewvalues(self): # noqa: D102; pylint: disable=missing-docstring
|
||||
return self.inv.viewkeys()
|
||||
|
@ -454,25 +437,7 @@ class BidictBase(BidirectionalMapping):
|
|||
values.__doc__ = 'A list of the contained values.'
|
||||
|
||||
def viewitems(self): # noqa: D102; pylint: disable=missing-docstring
|
||||
delegate = getattr(self.__delegate__, 'viewitems', lambda: ItemsView(self))
|
||||
return delegate()
|
||||
|
||||
viewitems.__doc__ = items.__doc__
|
||||
items.__doc__ = 'A list of the contained items.'
|
||||
|
||||
def iterkeys(self):
|
||||
"""An iterator over the contained keys."""
|
||||
delegate = getattr(self.__delegate__, 'iterkeys', super(BidictBase, self).iterkeys)
|
||||
return delegate()
|
||||
|
||||
def itervalues(self):
|
||||
"""An iterator over the contained values."""
|
||||
return self.inv.iterkeys()
|
||||
|
||||
def iteritems(self):
|
||||
"""An iterator over the contained items."""
|
||||
delegate = getattr(self.__delegate__, 'iteritems', super(BidictBase, self).iteritems)
|
||||
return delegate()
|
||||
return ItemsView(self)
|
||||
|
||||
# __ne__ added automatically in Python 3 when you implement __eq__, but not in Python 2.
|
||||
def __ne__(self, other): # noqa: N802
|
||||
|
@ -487,5 +452,5 @@ _NODUP = _DedupResult(False, False, _MISS, _MISS)
|
|||
|
||||
# * Code review nav *
|
||||
#==============================================================================
|
||||
# ← Prev: _abc.py Current: _base.py Next: _frozenbidict.py →
|
||||
# ← Prev: _abc.py Current: _base.py Next: _delegating_mixins.py →
|
||||
#==============================================================================
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -22,156 +22,25 @@
|
|||
|
||||
# * Code review nav *
|
||||
#==============================================================================
|
||||
# ← Prev: _frozenbidict.py Current: _bidict.py Next: _orderedbase.py →
|
||||
# ← Prev: _mut.py Current: _bidict.py Next: _orderedbase.py →
|
||||
#==============================================================================
|
||||
|
||||
|
||||
"""Provides :class:`bidict`."""
|
||||
|
||||
from ._base import BidictBase
|
||||
from ._dup import OVERWRITE, RAISE, _OnDup
|
||||
from ._miss import _MISS
|
||||
from .compat import MutableMapping
|
||||
from ._mut import MutableBidict
|
||||
from ._delegating_mixins import _DelegateKeysAndItemsToFwdm
|
||||
|
||||
|
||||
# Extend MutableMapping explicitly because it doesn't implement __subclasshook__, as well as to
|
||||
# inherit method implementations it provides that bidict can reuse (namely `setdefault`).
|
||||
class bidict(BidictBase, MutableMapping): # noqa: N801; pylint: disable=invalid-name
|
||||
class bidict(_DelegateKeysAndItemsToFwdm, MutableBidict): # noqa: N801,E501; pylint: disable=invalid-name
|
||||
"""Base class for mutable bidirectional mappings."""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
__hash__ = None # since this class is mutable; explicit > implicit.
|
||||
|
||||
_ON_DUP_OVERWRITE = _OnDup(key=OVERWRITE, val=OVERWRITE, kv=OVERWRITE)
|
||||
|
||||
def __delitem__(self, key):
|
||||
u"""*x.__delitem__(y) ⟺ del x[y]*"""
|
||||
self._pop(key)
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
"""
|
||||
Set the value for *key* to *val*.
|
||||
|
||||
If *key* is already associated with *val*, this is a no-op.
|
||||
|
||||
If *key* is already associated with a different value,
|
||||
the old value will be replaced with *val*,
|
||||
as with dict's :meth:`__setitem__`.
|
||||
|
||||
If *val* is already associated with a different key,
|
||||
an exception is raised
|
||||
to protect against accidental removal of the key
|
||||
that's currently associated with *val*.
|
||||
|
||||
Use :meth:`put` instead if you want to specify different policy in
|
||||
the case that the provided key or value duplicates an existing one.
|
||||
Or use :meth:`forceput` to unconditionally associate *key* with *val*,
|
||||
replacing any existing items as necessary to preserve uniqueness.
|
||||
|
||||
:raises bidict.ValueDuplicationError: if *val* duplicates that of an
|
||||
existing item.
|
||||
|
||||
:raises bidict.KeyAndValueDuplicationError: if *key* duplicates the key of an
|
||||
existing item and *val* duplicates the value of a different
|
||||
existing item.
|
||||
"""
|
||||
on_dup = self._get_on_dup()
|
||||
self._put(key, val, on_dup)
|
||||
|
||||
def put(self, key, val, on_dup_key=RAISE, on_dup_val=RAISE, on_dup_kv=None):
|
||||
"""
|
||||
Associate *key* with *val* with the specified duplication policies.
|
||||
|
||||
If *on_dup_kv* is ``None``, the *on_dup_val* policy will be used for it.
|
||||
|
||||
For example, if all given duplication policies are :attr:`~bidict.RAISE`,
|
||||
then *key* will be associated with *val* if and only if
|
||||
*key* is not already associated with an existing value and
|
||||
*val* is not already associated with an existing key,
|
||||
otherwise an exception will be raised.
|
||||
|
||||
If *key* is already associated with *val*, this is a no-op.
|
||||
|
||||
:raises bidict.KeyDuplicationError: if attempting to insert an item
|
||||
whose key only duplicates an existing item's, and *on_dup_key* is
|
||||
:attr:`~bidict.RAISE`.
|
||||
|
||||
:raises bidict.ValueDuplicationError: if attempting to insert an item
|
||||
whose value only duplicates an existing item's, and *on_dup_val* is
|
||||
:attr:`~bidict.RAISE`.
|
||||
|
||||
:raises bidict.KeyAndValueDuplicationError: if attempting to insert an
|
||||
item whose key duplicates one existing item's, and whose value
|
||||
duplicates another existing item's, and *on_dup_kv* is
|
||||
:attr:`~bidict.RAISE`.
|
||||
"""
|
||||
on_dup = self._get_on_dup((on_dup_key, on_dup_val, on_dup_kv))
|
||||
self._put(key, val, on_dup)
|
||||
|
||||
def forceput(self, key, val):
|
||||
"""
|
||||
Associate *key* with *val* unconditionally.
|
||||
|
||||
Replace any existing mappings containing key *key* or value *val*
|
||||
as necessary to preserve uniqueness.
|
||||
"""
|
||||
self._put(key, val, self._ON_DUP_OVERWRITE)
|
||||
|
||||
def clear(self):
|
||||
"""Remove all items."""
|
||||
self._fwdm.clear()
|
||||
self._invm.clear()
|
||||
|
||||
def pop(self, key, default=_MISS):
|
||||
u"""*x.pop(k[, d]) → v*
|
||||
|
||||
Remove specified key and return the corresponding value.
|
||||
|
||||
:raises KeyError: if *key* is not found and no *default* is provided.
|
||||
"""
|
||||
try:
|
||||
return self._pop(key)
|
||||
except KeyError:
|
||||
if default is _MISS:
|
||||
raise
|
||||
return default
|
||||
|
||||
def popitem(self):
|
||||
u"""*x.popitem() → (k, v)*
|
||||
|
||||
Remove and return some item as a (key, value) pair.
|
||||
|
||||
:raises KeyError: if *x* is empty.
|
||||
"""
|
||||
if not self:
|
||||
raise KeyError('mapping is empty')
|
||||
key, val = self._fwdm.popitem()
|
||||
del self._invm[val]
|
||||
return key, val
|
||||
|
||||
def update(self, *args, **kw):
|
||||
"""Like :meth:`putall` with default duplication policies."""
|
||||
if args or kw:
|
||||
self._update(False, None, *args, **kw)
|
||||
|
||||
def forceupdate(self, *args, **kw):
|
||||
"""Like a bulk :meth:`forceput`."""
|
||||
self._update(False, self._ON_DUP_OVERWRITE, *args, **kw)
|
||||
|
||||
def putall(self, items, on_dup_key=RAISE, on_dup_val=RAISE, on_dup_kv=None):
|
||||
"""
|
||||
Like a bulk :meth:`put`.
|
||||
|
||||
If one of the given items causes an exception to be raised,
|
||||
none of the items is inserted.
|
||||
"""
|
||||
if items:
|
||||
on_dup = self._get_on_dup((on_dup_key, on_dup_val, on_dup_kv))
|
||||
self._update(False, on_dup, items)
|
||||
|
||||
|
||||
# * Code review nav *
|
||||
#==============================================================================
|
||||
# ← Prev: _frozenbidict.py Current: _bidict.py Next: _orderedbase.py →
|
||||
# ← Prev: _mut.py Current: _bidict.py Next: _orderedbase.py →
|
||||
#==============================================================================
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
#==============================================================================
|
||||
# * Welcome to the bidict source code *
|
||||
#==============================================================================
|
||||
|
||||
# Doing a code review? You'll find a "Code review nav" comment like the one
|
||||
# below at the top and bottom of the most important source files. This provides
|
||||
# a suggested initial path through the source when reviewing.
|
||||
#
|
||||
# Note: If you aren't reading this on https://github.com/jab/bidict, you may be
|
||||
# viewing an outdated version of the code. Please head to GitHub to review the
|
||||
# latest version, which contains important improvements over older versions.
|
||||
#
|
||||
# Thank you for reading and for any feedback you provide.
|
||||
|
||||
# * Code review nav *
|
||||
#==============================================================================
|
||||
# ← Prev: _base.py Current: _delegating_mixins.py Next: _frozenbidict.py →
|
||||
#==============================================================================
|
||||
|
||||
|
||||
r"""Provides mixin classes that delegate to ``self._fwdm`` for various operations.
|
||||
|
||||
This allows methods such as :meth:`bidict.bidict.items`
|
||||
to be implemented in terms of a ``self._fwdm.items()`` call,
|
||||
which is potentially much more efficient (e.g. in CPython 2)
|
||||
compared to the implementation inherited from :class:`~collections.abc.Mapping`
|
||||
(which returns ``[(key, self[key]) for key in self]`` in Python 2).
|
||||
|
||||
Because this depends on implementation details that aren't necessarily true
|
||||
(such as the bidict's values being the same as its ``self._fwdm.values()``,
|
||||
which is not true for e.g. ordered bidicts where ``_fwdm``\'s values are nodes),
|
||||
these should always be mixed in at a layer below a more general layer,
|
||||
as they are in e.g. :class:`~bidict.frozenbidict`
|
||||
which extends :class:`~bidict.BidictBase`.
|
||||
|
||||
See the :ref:`extending:Sorted Bidict Recipes`
|
||||
for another example of where this comes into play.
|
||||
``SortedBidict`` extends :class:`bidict.MutableBidict`
|
||||
rather than :class:`bidict.bidict`
|
||||
to avoid inheriting these mixins,
|
||||
which are incompatible with the backing
|
||||
:class:`sortedcontainers.SortedDict`s.
|
||||
"""
|
||||
|
||||
from .compat import PY2
|
||||
|
||||
|
||||
_KEYS_METHODS = ('keys',) + (('viewkeys', 'iterkeys') if PY2 else ())
|
||||
_ITEMS_METHODS = ('items',) + (('viewitems', 'iteritems') if PY2 else ())
|
||||
_DOCSTRING_BY_METHOD = {
|
||||
'keys': 'A set-like object providing a view on the contained keys.',
|
||||
'items': 'A set-like object providing a view on the contained items.',
|
||||
}
|
||||
if PY2:
|
||||
_DOCSTRING_BY_METHOD['viewkeys'] = _DOCSTRING_BY_METHOD['keys']
|
||||
_DOCSTRING_BY_METHOD['viewitems'] = _DOCSTRING_BY_METHOD['items']
|
||||
_DOCSTRING_BY_METHOD['keys'] = 'A list of the contained keys.'
|
||||
_DOCSTRING_BY_METHOD['items'] = 'A list of the contained items.'
|
||||
|
||||
|
||||
def _make_method(methodname):
|
||||
def method(self):
|
||||
return getattr(self._fwdm, methodname)() # pylint: disable=protected-access
|
||||
method.__name__ = methodname
|
||||
method.__doc__ = _DOCSTRING_BY_METHOD.get(methodname, '')
|
||||
return method
|
||||
|
||||
|
||||
def _make_fwdm_delegating_mixin(clsname, methodnames):
|
||||
clsdict = dict({name: _make_method(name) for name in methodnames}, __slots__=())
|
||||
return type(clsname, (object,), clsdict)
|
||||
|
||||
|
||||
_DelegateKeysToFwdm = _make_fwdm_delegating_mixin('_DelegateKeysToFwdm', _KEYS_METHODS)
|
||||
_DelegateItemsToFwdm = _make_fwdm_delegating_mixin('_DelegateItemsToFwdm', _ITEMS_METHODS)
|
||||
_DelegateKeysAndItemsToFwdm = type(
|
||||
'_DelegateKeysAndItemsToFwdm',
|
||||
(_DelegateKeysToFwdm, _DelegateItemsToFwdm),
|
||||
{'__slots__': ()})
|
||||
|
||||
# * Code review nav *
|
||||
#==============================================================================
|
||||
# ← Prev: _base.py Current: _delegating_mixins.py Next: _frozenbidict.py →
|
||||
#==============================================================================
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -22,16 +22,17 @@
|
|||
|
||||
# * Code review nav *
|
||||
#==============================================================================
|
||||
# ← Prev: _base.py Current: _frozenbidict.py Next: _bidict.py →
|
||||
# ← Prev: _delegating_mixins.py Current: _frozenbidict.py Next: _mut.py →
|
||||
#==============================================================================
|
||||
|
||||
"""Provides :class:`frozenbidict`, an immutable, hashable bidirectional mapping type."""
|
||||
|
||||
from ._base import BidictBase
|
||||
from ._delegating_mixins import _DelegateKeysAndItemsToFwdm
|
||||
from .compat import ItemsView
|
||||
|
||||
|
||||
class frozenbidict(BidictBase): # noqa: N801; pylint: disable=invalid-name
|
||||
class frozenbidict(_DelegateKeysAndItemsToFwdm, BidictBase): # noqa: N801,E501; pylint: disable=invalid-name
|
||||
"""Immutable, hashable bidict type."""
|
||||
|
||||
__slots__ = ()
|
||||
|
@ -46,5 +47,5 @@ class frozenbidict(BidictBase): # noqa: N801; pylint: disable=invalid-name
|
|||
|
||||
# * Code review nav *
|
||||
#==============================================================================
|
||||
# ← Prev: _base.py Current: _frozenbidict.py Next: _bidict.py →
|
||||
# ← Prev: _delegating_mixins.py Current: _frozenbidict.py Next: _mut.py →
|
||||
#==============================================================================
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -27,57 +27,36 @@
|
|||
|
||||
"""Provides :class:`FrozenOrderedBidict`, an immutable, hashable, ordered bidict."""
|
||||
|
||||
from ._base import BidictBase
|
||||
from ._delegating_mixins import _DelegateKeysToFwdm
|
||||
from ._frozenbidict import frozenbidict
|
||||
from ._orderedbase import OrderedBidictBase
|
||||
from .compat import DICTS_ORDERED, ItemsView, PY2, izip
|
||||
from .compat import DICTS_ORDERED, PY2, izip
|
||||
|
||||
|
||||
# FrozenOrderedBidict intentionally does not subclass frozenbidict because it only complicates the
|
||||
# inheritance hierarchy without providing any actual code reuse: The only thing from frozenbidict
|
||||
# that FrozenOrderedBidict uses is frozenbidict.__hash__(), but Python specifically prevents
|
||||
# __hash__ from being inherited; it must instead always be defined explicitly as below. Users who
|
||||
# need an `is_frozenbidict(..)` test that succeeds for both frozenbidicts and FrozenOrderedBidicts
|
||||
# should therefore not use isinstance(foo, frozenbidict), but should instead use the appropriate
|
||||
# ABCs, e.g. `isinstance(foo, BidirectionalMapping) and not isinstance(foo, MutableMapping)`.
|
||||
class FrozenOrderedBidict(OrderedBidictBase):
|
||||
"""Hashable, immutable, ordered bidict type."""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
# frozenbidict.__hash__ can be resued for FrozenOrderedBidict:
|
||||
# If the Python implementation's dict type is ordered (e.g. PyPy or CPython >= 3.6), then
|
||||
# `FrozenOrderedBidict` can delegate to `_fwdm` for keys: Both `_fwdm` and `_invm` will always
|
||||
# be initialized with the provided items in the correct order, and since `FrozenOrderedBidict`
|
||||
# is immutable, their respective orders can't get out of sync after a mutation. (Can't delegate
|
||||
# to `_fwdm` for items though because values in `_fwdm` are nodes.)
|
||||
_BASES = ((_DelegateKeysToFwdm,) if DICTS_ORDERED else ()) + (OrderedBidictBase,)
|
||||
_CLSDICT = dict(
|
||||
__slots__=(),
|
||||
# Must set __hash__ explicitly, Python prevents inheriting it.
|
||||
# frozenbidict.__hash__ can be reused for FrozenOrderedBidict:
|
||||
# FrozenOrderedBidict inherits BidictBase.__eq__ which is order-insensitive,
|
||||
# and frozenbidict.__hash__ is consistent with BidictBase.__eq__.
|
||||
__hash__ = frozenbidict.__hash__ # Must define __hash__ explicitly, Python prevents inheriting
|
||||
if PY2:
|
||||
# Must grab the __func__ attribute off the method in Python 2, or else get "TypeError:
|
||||
# unbound method __hash__() must be called with frozenbidict instance as first argument"
|
||||
__hash__ = __hash__.__func__
|
||||
__hash__=frozenbidict.__hash__.__func__ if PY2 else frozenbidict.__hash__,
|
||||
__doc__='Hashable, immutable, ordered bidict type.',
|
||||
__module__=__name__, # Otherwise unpickling fails in Python 2.
|
||||
)
|
||||
|
||||
# When PY2 (so we provide iteritems) and DICTS_ORDERED, e.g. on PyPy, the following implementation
|
||||
# of iteritems may be more efficient than that inherited from `Mapping`. This exploits the property
|
||||
# that the keys in `_fwdm` and `_invm` are already in the right order:
|
||||
if PY2 and DICTS_ORDERED:
|
||||
_CLSDICT['iteritems'] = lambda self: izip(self._fwdm, self._invm) # noqa: E501; pylint: disable=protected-access
|
||||
|
||||
if DICTS_ORDERED:
|
||||
# If the Python implementation's dict type is ordered (e.g. PyPy or CPython >= 3.6), then
|
||||
# `FrozenOrderedBidict` can set `__delegate__` to `_fwdm`, allowing the more efficient
|
||||
# implementations of `keys` and `values`, rather than the less efficient implementations
|
||||
# inherited from `Mapping.keys` and `OrderedBidictBase.values`.
|
||||
# Both the `_fwdm` and `_invm` backing dicts will always be initialized with the provided
|
||||
# items in the correct order, and since `FrozenOrderedBidict` is immutable, their respective
|
||||
# orders can't get out of sync after a mutation, like they can with a mutable `OrderedBidict`.
|
||||
FrozenOrderedBidict.__delegate__ = property(lambda self: self._fwdm) # noqa: E501; pylint: disable=protected-access
|
||||
|
||||
# (`FrozenOrderedBidict` can't use the more efficient `_fwdm.items` and `_fwdm.viewitems`
|
||||
# implementations because the values in `_fwdm` are nodes.
|
||||
FrozenOrderedBidict.items = super(BidictBase, FrozenOrderedBidict).items
|
||||
FrozenOrderedBidict.viewitems = lambda s: ItemsView(s) # pylint: disable=unnecessary-lambda
|
||||
|
||||
if PY2:
|
||||
# We can do better than the `iteritems` implementation inherited from `Mapping`;
|
||||
# zipping together the keys from `_fwdm` and `_invm` runs faster (e.g. C speed on CPython).
|
||||
def iteritems(self):
|
||||
"""An iterator over the contained items."""
|
||||
return izip(self._fwdm, self._invm) # pylint: disable=protected-access
|
||||
|
||||
FrozenOrderedBidict.iteritems = iteritems
|
||||
FrozenOrderedBidict = type('FrozenOrderedBidict', _BASES, _CLSDICT) # pylint: disable=invalid-name
|
||||
|
||||
|
||||
# * Code review nav *
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
#==============================================================================
|
||||
# * Welcome to the bidict source code *
|
||||
#==============================================================================
|
||||
|
||||
# Doing a code review? You'll find a "Code review nav" comment like the one
|
||||
# below at the top and bottom of the most important source files. This provides
|
||||
# a suggested initial path through the source when reviewing.
|
||||
#
|
||||
# Note: If you aren't reading this on https://github.com/jab/bidict, you may be
|
||||
# viewing an outdated version of the code. Please head to GitHub to review the
|
||||
# latest version, which contains important improvements over older versions.
|
||||
#
|
||||
# Thank you for reading and for any feedback you provide.
|
||||
|
||||
# * Code review nav *
|
||||
#==============================================================================
|
||||
# ← Prev: _frozenbidict.py Current: _mut.py Next: _bidict.py →
|
||||
#==============================================================================
|
||||
|
||||
|
||||
"""Provides :class:`bidict`."""
|
||||
|
||||
from ._base import BidictBase
|
||||
from ._dup import OVERWRITE, RAISE, _OnDup
|
||||
from ._miss import _MISS
|
||||
from .compat import MutableMapping
|
||||
|
||||
|
||||
# Extend MutableMapping explicitly because it doesn't implement __subclasshook__, as well as to
|
||||
# inherit method implementations it provides that we can reuse (namely `setdefault`).
|
||||
class MutableBidict(BidictBase, MutableMapping):
|
||||
"""Base class for mutable bidirectional mappings."""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
__hash__ = None # since this class is mutable; explicit > implicit.
|
||||
|
||||
_ON_DUP_OVERWRITE = _OnDup(key=OVERWRITE, val=OVERWRITE, kv=OVERWRITE)
|
||||
|
||||
def __delitem__(self, key):
|
||||
u"""*x.__delitem__(y) ⟺ del x[y]*"""
|
||||
self._pop(key)
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
"""
|
||||
Set the value for *key* to *val*.
|
||||
|
||||
If *key* is already associated with *val*, this is a no-op.
|
||||
|
||||
If *key* is already associated with a different value,
|
||||
the old value will be replaced with *val*,
|
||||
as with dict's :meth:`__setitem__`.
|
||||
|
||||
If *val* is already associated with a different key,
|
||||
an exception is raised
|
||||
to protect against accidental removal of the key
|
||||
that's currently associated with *val*.
|
||||
|
||||
Use :meth:`put` instead if you want to specify different policy in
|
||||
the case that the provided key or value duplicates an existing one.
|
||||
Or use :meth:`forceput` to unconditionally associate *key* with *val*,
|
||||
replacing any existing items as necessary to preserve uniqueness.
|
||||
|
||||
:raises bidict.ValueDuplicationError: if *val* duplicates that of an
|
||||
existing item.
|
||||
|
||||
:raises bidict.KeyAndValueDuplicationError: if *key* duplicates the key of an
|
||||
existing item and *val* duplicates the value of a different
|
||||
existing item.
|
||||
"""
|
||||
on_dup = self._get_on_dup()
|
||||
self._put(key, val, on_dup)
|
||||
|
||||
def put(self, key, val, on_dup_key=RAISE, on_dup_val=RAISE, on_dup_kv=None):
|
||||
"""
|
||||
Associate *key* with *val* with the specified duplication policies.
|
||||
|
||||
If *on_dup_kv* is ``None``, the *on_dup_val* policy will be used for it.
|
||||
|
||||
For example, if all given duplication policies are :attr:`~bidict.RAISE`,
|
||||
then *key* will be associated with *val* if and only if
|
||||
*key* is not already associated with an existing value and
|
||||
*val* is not already associated with an existing key,
|
||||
otherwise an exception will be raised.
|
||||
|
||||
If *key* is already associated with *val*, this is a no-op.
|
||||
|
||||
:raises bidict.KeyDuplicationError: if attempting to insert an item
|
||||
whose key only duplicates an existing item's, and *on_dup_key* is
|
||||
:attr:`~bidict.RAISE`.
|
||||
|
||||
:raises bidict.ValueDuplicationError: if attempting to insert an item
|
||||
whose value only duplicates an existing item's, and *on_dup_val* is
|
||||
:attr:`~bidict.RAISE`.
|
||||
|
||||
:raises bidict.KeyAndValueDuplicationError: if attempting to insert an
|
||||
item whose key duplicates one existing item's, and whose value
|
||||
duplicates another existing item's, and *on_dup_kv* is
|
||||
:attr:`~bidict.RAISE`.
|
||||
"""
|
||||
on_dup = self._get_on_dup((on_dup_key, on_dup_val, on_dup_kv))
|
||||
self._put(key, val, on_dup)
|
||||
|
||||
def forceput(self, key, val):
|
||||
"""
|
||||
Associate *key* with *val* unconditionally.
|
||||
|
||||
Replace any existing mappings containing key *key* or value *val*
|
||||
as necessary to preserve uniqueness.
|
||||
"""
|
||||
self._put(key, val, self._ON_DUP_OVERWRITE)
|
||||
|
||||
def clear(self):
|
||||
"""Remove all items."""
|
||||
self._fwdm.clear()
|
||||
self._invm.clear()
|
||||
|
||||
def pop(self, key, default=_MISS):
|
||||
u"""*x.pop(k[, d]) → v*
|
||||
|
||||
Remove specified key and return the corresponding value.
|
||||
|
||||
:raises KeyError: if *key* is not found and no *default* is provided.
|
||||
"""
|
||||
try:
|
||||
return self._pop(key)
|
||||
except KeyError:
|
||||
if default is _MISS:
|
||||
raise
|
||||
return default
|
||||
|
||||
def popitem(self):
|
||||
u"""*x.popitem() → (k, v)*
|
||||
|
||||
Remove and return some item as a (key, value) pair.
|
||||
|
||||
:raises KeyError: if *x* is empty.
|
||||
"""
|
||||
if not self:
|
||||
raise KeyError('mapping is empty')
|
||||
key, val = self._fwdm.popitem()
|
||||
del self._invm[val]
|
||||
return key, val
|
||||
|
||||
def update(self, *args, **kw):
|
||||
"""Like :meth:`putall` with default duplication policies."""
|
||||
if args or kw:
|
||||
self._update(False, None, *args, **kw)
|
||||
|
||||
def forceupdate(self, *args, **kw):
|
||||
"""Like a bulk :meth:`forceput`."""
|
||||
self._update(False, self._ON_DUP_OVERWRITE, *args, **kw)
|
||||
|
||||
def putall(self, items, on_dup_key=RAISE, on_dup_val=RAISE, on_dup_kv=None):
|
||||
"""
|
||||
Like a bulk :meth:`put`.
|
||||
|
||||
If one of the given items causes an exception to be raised,
|
||||
none of the items is inserted.
|
||||
"""
|
||||
if items:
|
||||
on_dup = self._get_on_dup((on_dup_key, on_dup_val, on_dup_kv))
|
||||
self._update(False, on_dup, items)
|
||||
|
||||
|
||||
# * Code review nav *
|
||||
#==============================================================================
|
||||
# ← Prev: _frozenbidict.py Current: _mut.py Next: _bidict.py →
|
||||
#==============================================================================
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -55,7 +55,7 @@ def namedbidict(typename, keyname, valname, base_type=bidict):
|
|||
if not all(map(_VALID_NAME.match, names)) or keyname == valname:
|
||||
raise ValueError(names)
|
||||
|
||||
class _Named(base_type):
|
||||
class _Named(base_type): # pylint: disable=too-many-ancestors
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -22,7 +22,7 @@
|
|||
|
||||
# * Code review nav *
|
||||
#==============================================================================
|
||||
# ← Prev: _bidict.py Current: _orderedbase.py Next: _frozenordered.py →
|
||||
# ← Prev: _bidict.py Current: _orderedbase.py Next: _frozenordered.py →
|
||||
#==============================================================================
|
||||
|
||||
|
||||
|
@ -138,14 +138,8 @@ class OrderedBidictBase(BidictBase): # lgtm [py/missing-equals]
|
|||
_fwdm_cls = bidict
|
||||
_invm_cls = bidict
|
||||
|
||||
#: Can't delegate to e.g. :attr:`_fwdm` for more efficient implementations
|
||||
#: of methods like :meth:`keys` because it isn't ordered.
|
||||
#: Set to None to opt out of optimized-method delegation and
|
||||
#: use default implementations from :class:`Mapping` instead.
|
||||
__delegate__ = None
|
||||
|
||||
#: The object used by :meth:`__repr__` for printing the contained items.
|
||||
__repr_delegate__ = list
|
||||
_repr_delegate = list
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
"""Make a new ordered bidirectional mapping.
|
||||
|
@ -303,5 +297,5 @@ class OrderedBidictBase(BidictBase): # lgtm [py/missing-equals]
|
|||
|
||||
# * Code review nav *
|
||||
#==============================================================================
|
||||
# ← Prev: _bidict.py Current: _orderedbase.py Next: _frozenordered.py →
|
||||
# ← Prev: _bidict.py Current: _orderedbase.py Next: _frozenordered.py →
|
||||
#==============================================================================
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -28,16 +28,14 @@
|
|||
|
||||
"""Provides :class:`OrderedBidict`."""
|
||||
|
||||
from ._bidict import bidict
|
||||
from ._mut import MutableBidict
|
||||
from ._orderedbase import OrderedBidictBase
|
||||
|
||||
|
||||
# Inherit ordered behavior from OrderedBidict and mutable behavior from bidict.
|
||||
class OrderedBidict(OrderedBidictBase, bidict):
|
||||
class OrderedBidict(OrderedBidictBase, MutableBidict):
|
||||
"""Mutable bidict type that maintains items in insertion order."""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
__hash__ = None # since this class is mutable; explicit > implicit.
|
||||
|
||||
def clear(self):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -87,16 +87,23 @@ creating a sorted bidict type is dead simple:
|
|||
|
||||
.. doctest::
|
||||
|
||||
>>> # As an optimization, bidict.bidict includes a mixin class that
|
||||
>>> # we can't use here (namely bidict._delegating_mixins._DelegateKeysAndItemsToFwdm),
|
||||
>>> # so extend the parent class, bidict.MutableBidict, instead.
|
||||
>>> from bidict import MutableBidict
|
||||
|
||||
>>> import sortedcontainers
|
||||
|
||||
>>> # a sorted bidict whose forward items stay sorted by their keys,
|
||||
>>> # and whose inverse items stay sorted by *their* keys (i.e. it and
|
||||
>>> # its inverse iterate over their items in different orders):
|
||||
|
||||
>>> class SortedBidict(bidict):
|
||||
>>> class SortedBidict(MutableBidict):
|
||||
... """A sorted bidict whose forward items stay sorted by their keys,
|
||||
... and whose inverse items stay sorted by *their* keys.
|
||||
... Note: As a result, an instance and its inverse yield their items
|
||||
... in different orders.
|
||||
... """
|
||||
...
|
||||
... _fwdm_cls = sortedcontainers.SortedDict
|
||||
... _invm_cls = sortedcontainers.SortedDict
|
||||
... __repr_delegate__ = list
|
||||
... _repr_delegate = list
|
||||
|
||||
>>> b = SortedBidict({'Tokyo': 'Japan', 'Cairo': 'Egypt'})
|
||||
>>> b
|
||||
|
@ -108,21 +115,23 @@ creating a sorted bidict type is dead simple:
|
|||
>>> list(b.items())
|
||||
[('Cairo', 'Egypt'), ('Lima', 'Peru'), ('Tokyo', 'Japan')]
|
||||
|
||||
>>> # b.inv stays sorted by *its* keys (b's values!)
|
||||
>>> # b.inv stays sorted by *its* keys (b's values)
|
||||
>>> list(b.inv.items())
|
||||
[('Egypt', 'Cairo'), ('Japan', 'Tokyo'), ('Peru', 'Lima')]
|
||||
|
||||
|
||||
>>> # a sorted bidict whose forward items stay sorted by their keys,
|
||||
>>> # and whose inverse items stay sorted by their values (i.e. it and
|
||||
>>> # its inverse iterate over their items in the same order):
|
||||
Here's a recipe for a sorted bidict whose forward items stay sorted by their keys,
|
||||
and whose inverse items stay sorted by their values. i.e. An instance and its inverse
|
||||
will yield their items in *the same* order:
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> import sortedcollections
|
||||
|
||||
>>> class KeySortedBidict(bidict):
|
||||
>>> class KeySortedBidict(MutableBidict):
|
||||
... _fwdm_cls = sortedcontainers.SortedDict
|
||||
... _invm_cls = sortedcollections.ValueSortedDict
|
||||
... __repr_delegate__ = list
|
||||
... _repr_delegate = list
|
||||
|
||||
>>> element_by_atomic_number = KeySortedBidict({
|
||||
... 3: 'lithium', 1: 'hydrogen', 2: 'helium'})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
4
setup.py
4
setup.py
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -52,7 +52,7 @@ TEST_REQS = [
|
|||
'hypothesis < 4',
|
||||
'hypothesis-pytest < 1',
|
||||
'py < 2',
|
||||
'pytest < 4',
|
||||
'pytest < 5',
|
||||
'pytest-benchmark < 4',
|
||||
'sortedcollections < 2',
|
||||
'sortedcontainers < 3',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -111,16 +111,21 @@ def test_issubclass_internal():
|
|||
The relationships tested here are not guaranteed to hold in the future,
|
||||
but are still tested so that any unintentional changes won't go unnoticed.
|
||||
"""
|
||||
assert issubclass(OrderedBidict, bidict)
|
||||
|
||||
assert not issubclass(frozenbidict, bidict)
|
||||
assert not issubclass(FrozenOrderedBidict, bidict)
|
||||
|
||||
assert not issubclass(bidict, FrozenOrderedBidict)
|
||||
assert not issubclass(bidict, OrderedBidict)
|
||||
assert not issubclass(bidict, frozenbidict)
|
||||
assert not issubclass(OrderedBidict, FrozenOrderedBidict)
|
||||
|
||||
assert not issubclass(FrozenOrderedBidict, frozenbidict)
|
||||
assert not issubclass(FrozenOrderedBidict, OrderedBidict)
|
||||
assert not issubclass(FrozenOrderedBidict, bidict)
|
||||
assert not issubclass(FrozenOrderedBidict, frozenbidict)
|
||||
|
||||
assert not issubclass(OrderedBidict, FrozenOrderedBidict)
|
||||
assert not issubclass(OrderedBidict, bidict)
|
||||
assert not issubclass(OrderedBidict, frozenbidict)
|
||||
|
||||
assert not issubclass(frozenbidict, FrozenOrderedBidict)
|
||||
assert not issubclass(frozenbidict, OrderedBidict)
|
||||
assert not issubclass(frozenbidict, bidict)
|
||||
|
||||
|
||||
def test_abstract_bimap_init_fails():
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2018 Joshua Bronson. All Rights Reserved.
|
||||
# Copyright 2009-2018 Joshua Bronson. All Rights Reserved.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
|
Loading…
Reference in New Issue