Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
more API and code improvements
including:
- simplify type hierarchy, merging the frozen types into the base types
- upgrade to latest hypothesis (3.38.0)
- use Reversible for order-sensitive FrozenOrderedBidict.__eq__
- fix bug where bidict._on_dup_kv was not set to ON_DUP_VAL as documented
- drop leading underscore from attrs that should be public
- improve setup.py/metadata
- rename DuplicationBehavior -> DuplicationPolicy
- drop ON_DUP_VAL duplication policy in preference to just using None
- reduce ordered bidict memory usage and speed up copy
  • Loading branch information
jab committed Nov 20, 2017
1 parent 4b86b57 commit 64ed727
Show file tree
Hide file tree
Showing 51 changed files with 1,363 additions and 1,089 deletions.
8 changes: 4 additions & 4 deletions .travis.yml
Expand Up @@ -18,17 +18,17 @@ matrix:
script: travis_retry ./build-docs.sh linkcheck
- env: TASK=pylint
before_script: travis_retry pip install -U pylint
script: pylint bidict tests/*.py
script: pylint bidict tests setup.py
- env: TASK=flake8
before_script: travis_retry pip install -U flake8
script: flake8 bidict tests/*.py
- env: TASK=pydocstyle
before_script: travis_retry pip install -U pydocstyle
script: pydocstyle bidict
- env: TASK=test-with-optimization-flag
- env: TASK=test-with-optimization-flag # make sure there are no relied-on side effects in assert statements
script: python3 -O -m doctest -o IGNORE_EXCEPTION_DETAIL -o ELLIPSIS tests/*.txt

# Test with all supported Python versions on Linux.
# Run the test suite with all supported Python versions on Linux.
# Only run these tests for master since they're so slow on Travis.
- env: TASK=pytest PYVER=2.7.14
if: branch = master
Expand All @@ -52,7 +52,7 @@ matrix:
- env: TASK=pytest PYVER=pypy3.5-5.9.0
if: branch = master

# Test with all supported Python versions on macOS.
# Run the test suite with all supported Python versions on macOS.
- env: TASK=pytest PYVER=2.7.14
if: branch = master
os: osx
Expand Down
232 changes: 136 additions & 96 deletions CHANGELOG.rst
Expand Up @@ -6,40 +6,37 @@ Changelog
.. include:: release-notifications.rst.inc


0.14.0.dev0 (not yet released)
------------------------------
0.14.0 (not yet released)
-------------------------

- Add the :attr:`bidict.BidictBase.ordered` class attribute.
- Fix a bug where :class:`bidict <bidict.bidict>`\'s
default *on_dup_kv* policy was not
to match whatever *on_dup_val* policy was in effect,
as :ref:`documented <key-and-value-duplication>`,
but rather was set to :class:`RAISE <bidict.DuplicationPolicy.RAISE>`.

- Add the :attr:`OrderedBidictBase.should_compare_order_sensitive_to
<bidict.OrderedBidictBase.should_compare_order_sensitive_to>`
public static method, replacing the previous
``OrderedBidictBase._should_compare_order_sensitive()``.
- Fix a bug that could happen when using Python's optimization (``-O``) flags
that could leave an ordered bidict in an inconsistent state
when dealing with duplicated, overwritten keys or values.

:attr:`OrderedBidictBase.should_compare_order_sensitive_to
<bidict.OrderedBidictBase.should_compare_order_sensitive_to>`
returns its argument's ``ordered`` attribute if present.
Otherwise it returns True iff the argument has a ``__reversed__`` method
and is not a :py:class:`dict`.
(On PyPy, :py:class:`dict`\s have a ``__reversed__`` method,
but should nonetheless be compared order-insensitively.)
- Fix a bug caused by the optimizations in 0.13.0 that made
:class:`FrozenOrderedBidict <bidict.FrozenOrderedBidict>`
instances containing the same items in different order
hash to different values,
which results in a violation of Python's object model:
Since a :class:`FrozenOrderedBidict <bidict.FrozenOrderedBidict>`
and a :class:`frozenbidict <bidict.frozenbidict>` with the same items
compare equal, they must also hash to the same value.

The previous ``OrderedBidictBase._should_compare_order_sensitive()``
method only returned True for other
:class:`OrderedBidictBase <bidict.OrderedBidictBase>` subclasses.
- Reduce the memory usage of ordered bidicts.
(Store node data as a (*k*, *v*) pair rather than a {*k*: *v*, *v*: *k*} dict.)

- Revert the optimizations in v0.13.0 to make
:class:`FrozenOrderedBidict <bidict.FrozenOrderedBidict>`
instances that have the same items in different order
have different hash values,
as this can result in a violation of Python's object model:
With those changes, a :class:`FrozenOrderedBidict <bidict.FrozenOrderedBidict>`
and a :class:`FrozenBidict <bidict.FrozenBidict>` with the same items
would hash to different values despite comparing equal, which is a violation.

- Fix a rare bug that could happen when using Python's ``-O`` flag
that could leave an ordered bidict in an inconsistent state
if dealing with duplicated keys or values.
- Make copying of ordered bidicts faster.
(Skip unnecessary duplication checking.)

- Add :class:`bidict.compat.Reversible`.
This is an alias of :class:`collections.abc.Reversible` on Python ≥ 3.6
and a reimplementation elsewhere.

- Improvements to tests and CI, including:

Expand All @@ -49,60 +46,108 @@ Changelog
- Test with optimization flags
- Require pylint to pass


Breaking API Changes
++++++++++++++++++++

- ``FrozenBidictBase`` has been removed.
Use :class:`FrozenBidict <bidict.FrozenBidict>` instead.

- :meth:`OrderedBidictBase.__eq__ <bidict.OrderedBidictBase.__eq__>`
comparison is now order-sensitive when the other mapping is inferred to be
a mapping with a guaranteed order. Previously it would only compare
order-sensitively with other
:class:`OrderedBidictBase <bidict.OrderedBidictBase>` instances.

For example, the last line in the following example no longer returns True::

>>> from bidict import OrderedBidict
>>> from collections import OrderedDict
>>> o1 = OrderedBidict([('one', 1), ('two', 2)])
>>> o2 = OrderedDict([('two', 2), ('one', 1)])
>>> o1 == o2
False

- ``OrderedBidictBase._should_compare_order_sensitive`` was removed
in preference to :attr:`BidictBase.ordered <bidict.BidictBase.ordered>` and
:attr:`OrderedBidictBase.should_compare_order_sensitive_to
<bidict.OrderedBidictBase.should_compare_order_sensitive_to>`.

- ``frozenorderedbidict._HASH_NITEMS_MAX`` was removed.
This release includes multiple API simplifications and improvements.

- Renamed:

- ``orderedbidict`` → :class:`OrderedBidict <bidict.OrderedBidict>`
- ``frozenorderedbidict`` → :class:`FrozenOrderedBidict <bidict.FrozenOrderedBidict>`

so that these now match the case of :class:`collections.OrderedDict`.

The names of the
:class:`bidict <bidict.bidict>`,
:class:`namedbidict <bidict.namedbidict>`, and
:class:`frozenbidict <bidict.frozenbidict>` classes
have been retained as all-lowercase
so that they continue to match the case of
:class:`dict`, :func:`namedtuple <collections.namedtuple>`, and
:class:`frozenset`, respectively.

- ``bidict.DuplicationPolicy.ON_DUP_VAL`` has been removed.
Use ``None`` instead.

- Merge :class:`frozenbidict <bidict.frozenbidict>` and ``BidictBase``
together and remove ``BidictBase``.
:class:`frozenbidict <bidict.frozenbidict>`
is now the concrete base class that all other bidict types derive from.
See the updated :ref:`bidict-type-hierarchy`.

- Merge :class:`frozenbidict <bidict.frozenbidict>` and ``FrozenBidictBase``
together and remove ``FrozenBidictBase``.
See the updated :ref:`bidict-type-hierarchy`.

- Merge ``frozenorderedbidict`` and ``OrderedBidictBase`` together
and remove ``OrderedBidictBase``.
See the updated :ref:`bidict-type-hierarchy`.

- ``orderedbidict._should_compare_order_sensitive()`` has been removed.
This logic is now covered by checking ``isinstance(other,``
:attr:`self.ordered_cls <bidict.FrozenOrderedBidict.ordered_cls>` ``)``
(see below).

- Change the behavior of
:meth:`FrozenOrderedBidict.__eq__ <bidict.FrozenOrderedBidict.__eq__>`
as follows:

Add
:attr:`FrozenOrderedBidict.ordered_cls <bidict.FrozenOrderedBidict.ordered_cls>`
and set it to :class:`Reversible <bidict.compat.Reversible>`.
Check ``isinstance(other, self.ordered_cls)`` in
:meth:`FrozenOrderedBidict.__eq__ <bidict.FrozenOrderedBidict.__eq__>`
to detect whether an equality check against another mapping should be
order-sensitive.

Previously,
:meth:`FrozenOrderedBidict.__eq__ <bidict.FrozenOrderedBidict.__eq__>`
was only order-sensitive for other ``OrderedBidictBase`` subclasses.
Now equality tests are order-sensitive for any
:class:`Reversible <collections.abc.Reversible>` mapping,
:class:`collections.OrderedDict` being a notable example.

- Renamed ``FrozenBidictBase._compute_hash`` →
:attr:`frozenbidict.compute_hash <bidict.frozenbidict.compute_hash>`

- ``frozenorderedbidict._HASH_NITEMS_MAX`` has been removed.
Since its hash value must be computed from all contained items
(so that hash results are consistent with equality comparisons with
unordered mappings containing the same items), the number of items
that influence the hash value should not be limitable.
(so that hash results are consistent with
equality comparisons against unordered mappings),
the number of items that influence the hash value should not be limitable.

- ``frozenbidict._USE_ITEMSVIEW_HASH`` was removed, and
:meth:`FrozenBidict.compute_hash <bidict.FrozenBidict.compute_hash>`
now uses ``ItemsView._hash()`` to compute the hash always,
- ``frozenbidict._USE_ITEMSVIEW_HASH`` has been removed, and
:meth:`frozenbidict.compute_hash <bidict.frozenbidict.compute_hash>`
now uses ``collections.ItemsView._hash()`` to compute the hash always,
not just when running on PyPy.

Override :meth:`FrozenBidict.compute_hash <bidict.FrozenBidict.compute_hash>`
Override :meth:`frozenbidict.compute_hash <bidict.frozenbidict.compute_hash>`
to return ``hash(frozenset(iteritems(self)))``
if you prefer the old default behavior on CPython,
which takes linear rather than constant space,
but which uses the ``frozenset_hash`` routine
(implemented in ``setobject.c``)
rather than the pure Python ``ItemsView._hash()`` routine.

- The following were renamed for better code style:
- ``loosebidict`` and ``looseorderedbidict`` have been removed.
Simple recipes to implement them yourself are now given in
:ref:`overwritingbidict`.

- Renamed ``FrozenBidictBase._compute_hash`` →
:attr:`frozenbidict.compute_hash <bidict.frozenbidict.compute_hash>`

- Renamed ``DuplicationBehavior`` →
:class:`DuplicationPolicy <bidict.DuplicationPolicy>`.

- Renamed:

- ``FrozenBidictBase._compute_hash`` → :attr:`FrozenBidict.compute_hash
<bidict.FrozenBidict.compute_hash>`
- ``frozenbidict`` → :class:`FrozenBidict <bidict.FrozenBidict>`
- ``loosebidict`` → :class:`LooseBidict <bidict.LooseBidict>`
- ``orderedbidict`` → :class:`OrderedBidict <bidict.OrderedBidict>`
- ``frozenorderedbidict`` → :class:`FrozenOrderedBidict <bidict.FrozenOrderedBidict>`
- ``looseorderedbidict`` → :class:`LooseOrderedBidict <bidict.LooseOrderedBidict>`
- ``bidict.BidictBase._fwd_class`` → :attr:`bidict.frozenbidict.fwd_cls`
- ``bidict.BidictBase._inv_class`` → :attr:`bidict.frozenbidict.inv_cls`
- ``bidict.BidictBase._on_dup_key`` → :attr:`bidict.frozenbidict.on_dup_key`
- ``bidict.BidictBase._on_dup_val`` → :attr:`bidict.frozenbidict.on_dup_val`
- ``bidict.BidictBase._on_dup_kv`` → :attr:`bidict.frozenbidict.on_dup_kv`


0.13.1 (2017-03-15)
Expand All @@ -114,7 +159,7 @@ Breaking API Changes
``issubclass(OldStyleClass, BidirectionalMapping)`` once again
works with old-style classes,
returning ``False`` rather than raising :class:`AttributeError`
(`thanks, @knaperek <https://github.com/jab/bidict/pull/41>`_!).
(`thanks, @knaperek <https://github.com/jab/bidict/pull/41>`_).


0.13.0 (2017-01-19)
Expand All @@ -129,7 +174,7 @@ Breaking API Changes
has been refactored into an abstract base class,
following the way :class:`collections.abc.Mapping` works.
The concrete method implementations it used to provide have been moved
into a new :class:`BidictBase <bidict.BidictBase>` subclass.
into a new ``BidictBase`` subclass.

:class:`BidirectionalMapping <bidict.BidirectionalMapping>`
now also implements
Expand All @@ -140,10 +185,8 @@ Breaking API Changes
:class:`BidirectionalMapping <bidict.BidirectionalMapping>`
subclass automatically.

- ``OrderedBidirectionalMapping`` has been renamed to
:class:`OrderedBidictBase <bidict.OrderedBidictBase>`,
to better reflect its function.
(It is not an ABC.)
- ``OrderedBidirectionalMapping`` has been renamed to ``OrderedBidictBase``,
to better reflect its function. (It is not an ABC.)

- A new ``FrozenBidictBase`` class has been factored out of
:class:`frozenbidict <bidict.FrozenBidict>` and
Expand Down Expand Up @@ -175,8 +218,8 @@ Breaking API Changes
or suggestions for an alternative implementation,
please `share your feedback <https://gitter.im/jab/bidict>`_.

- Add :attr:`_fwd_class <bidict.BidictBase._fwd_class>` and
:attr:`_inv_class <bidict.BidictBase._inv_class>` attributes
- Add :attr:`_fwd_class <bidict.frozenbidict._fwd_class>` and
:attr:`_inv_class <bidict.frozenbidict._inv_class>` attributes
representing the backing :class:`Mapping <collections.abc.Mapping>` types
used internally to store the forward and inverse dictionaries, respectively.

Expand Down Expand Up @@ -208,24 +251,24 @@ Breaking API Changes

- :func:`put() <bidict.bidict.put>`
now accepts ``on_dup_key``, ``on_dup_val``, and ``on_dup_kv`` keyword args
which allow you to override the default behavior
which allow you to override the default policy
when the key or value of a given item
duplicates that (those) of any existing item(s).
duplicates any existing item's.
These can take the following values:

- :attr:`bidict.DuplicationBehavior.RAISE`
- :attr:`bidict.DuplicationBehavior.OVERWRITE`
- :attr:`bidict.DuplicationBehavior.IGNORE`
- :attr:`bidict.DuplicationPolicy.RAISE`
- :attr:`bidict.DuplicationPolicy.OVERWRITE`
- :attr:`bidict.DuplicationPolicy.IGNORE`

``on_dup_kv`` can also take :attr:`bidict.DuplicationBehavior.ON_DUP_VAL`.
``on_dup_kv`` can also take ``ON_DUP_VAL``.

If not provided,
:func:`put() <bidict.bidict.put>` uses
:attr:`RAISE <bidict.DuplicationBehavior.RAISE>` behavior by default.
:func:`put() <bidict.bidict.put>` uses the
:attr:`RAISE <bidict.DuplicationPolicy.RAISE>` policy by default.

- New :func:`putall() <bidict.bidict.putall>` method
provides a bulk :func:`put() <bidict.bidict.put>` API,
allowing you to override the default duplication handling behavior
allowing you to override the default duplication handling policy
that :func:`update() <bidict.bidict.update>` uses.

- :func:`bidict.update() <bidict.bidict.update>` now fails clean,
Expand Down Expand Up @@ -273,13 +316,12 @@ Breaking API Changes
- More efficient implementations of
:func:`pairs() <bidict.util.pairs>`,
:func:`inverted() <bidict.util.inverted>`, and
:func:`bidict.copy() <bidict.BidictBase.copy>`.
:func:`bidict.copy() <bidict.frozenbidict.copy>`.

- Implement :func:`bidict.__copy__() <bidict.BidictBase.__copy__>`
- Implement :func:`bidict.__copy__() <bidict.frozenbidict.__copy__>`
for use with the :mod:`copy` module.

- Fix issue preventing a client class from inheriting from
:class:`loosebidict <bidict.LooseBidict>`
- Fix issue preventing a client class from inheriting from ``loosebidict``
(see `#34 <https://github.com/jab/bidict/issues/34>`_).

- Add benchmarking to tests.
Expand Down Expand Up @@ -327,8 +369,7 @@ Breaking API Changes

- Add
:class:`orderedbidict <bidict.OrderedBidict>`,
:class:`looseorderedbidict <bidict.LooseOrderedBidict>`,
and
``looseorderedbidict``, and
:class:`frozenorderedbidict <bidict.FrozenOrderedBidict>`.

- Add :doc:`Code of Conduct <code-of-conduct>`
Expand Down Expand Up @@ -368,23 +409,23 @@ Breaking API Changes
++++++++++++++++++++

- Remove ``bidict.__invert__``, and with it, support for the ``~b`` syntax.
Use :attr:`b.inv <bidict.BidictBase.inv>` instead.
Use :attr:`b.inv <bidict.frozenbidict.inv>` instead.
`#19 <https://github.com/jab/bidict/issues/19>`_

- Remove support for the slice syntax.
Use ``b.inv[val]`` rather than ``b[:val]``.
`#19 <https://github.com/jab/bidict/issues/19>`_

- Remove ``bidict.invert``.
Use :attr:`b.inv <bidict.BidictBase.inv>`
Use :attr:`b.inv <bidict.frozenbidict.inv>`
rather than inverting a bidict in place.
`#20 <https://github.com/jab/bidict/issues/20>`_

- Raise ``ValueExistsException``
when attempting to insert a mapping with a non-unique key.
`#21 <https://github.com/jab/bidict/issues/21>`_

- Rename ``collapsingbidict`` to :class:`loosebidict <bidict.LooseBidict>`
- Rename ``collapsingbidict`` to ``loosebidict``
now that it suppresses
``ValueExistsException``
rather than the less general ``CollapseException``.
Expand All @@ -409,8 +450,7 @@ Breaking API Changes
0.9.0rc0 (2015-05-30)
---------------------

- Add a Changelog!
Also a
- Add this changelog,
`Contributors' Guide <https://github.com/jab/bidict/blob/master/CONTRIBUTING.rst>`_,
`Gitter chat room <https://gitter.im/jab/bidict>`_,
and other community-oriented improvements.
Expand Down
4 changes: 2 additions & 2 deletions CODE_OF_CONDUCT.rst
Expand Up @@ -78,5 +78,5 @@ Attribution
-----------

This Code of Conduct is adapted from the `Contributor
Covenant <https://www.contributor-covenant.org>`__, version 1.4, available at
`https://www.contributor-covenant.org/version/1/4 <https://www.contributor-covenant.org/version/1/4/>`__
Covenant <https://www.contributor-covenant.org>`_, version 1.4, available at
`https://www.contributor-covenant.org/version/1/4 <https://www.contributor-covenant.org/version/1/4/>`_.

0 comments on commit 64ed727

Please sign in to comment.