Skip to content

Commit

Permalink
Merge c888fe6 into 033083c
Browse files Browse the repository at this point in the history
  • Loading branch information
jab committed Nov 17, 2017
2 parents 033083c + c888fe6 commit 4ce5616
Show file tree
Hide file tree
Showing 17 changed files with 398 additions and 365 deletions.
131 changes: 81 additions & 50 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,89 @@ env:
global:
- PYENV_ROOT="$HOME/.pyenv"
- PYENV="$PYENV_ROOT/bin/pyenv"
# Default Python version. Used for jobs that don't specify one.
- PYVER=3.6.3

matrix:
include:
# One-off jobs. Override "script" to do something other than run pytest.
# Use latest CPython 3 for these. Set a "TASK" var so the task is evident in the build matrix.
- script: ./build-docs.sh linkcheck
env:
- TASK=build-docs
- PYENV_PYTHON_VER=3.6.3
- script: pylint bidict tests/*.py
env:
- TASK=pylint
- PYENV_PYTHON_VER=3.6.3
- script: flake8 bidict tests/*.py
env:
- TASK=flake8
- PYENV_PYTHON_VER=3.6.3
- script: pydocstyle bidict
env:
- TASK=pydocstyle
- PYENV_PYTHON_VER=3.6.3
# Each job performs a single task, and sets a "TASK" var so it's evident in the build matrix.

# Test all supported Python versions on macOS...
- os: osx
env: PYENV_PYTHON_VER=2.7.14
- os: osx
env: PYENV_PYTHON_VER=3.4.7
- os: osx
env: PYENV_PYTHON_VER=3.5.3
- os: osx
env: PYENV_PYTHON_VER=3.6.3
- os: osx
env: PYENV_PYTHON_VER=3.7-dev
- os: osx
env: PYENV_PYTHON_VER=pypy2.7-5.9.0
# not yet working on macOS? works on linux:
#- os: osx
#- env: PYENV_PYTHON_VER=pypy3.5-5.9.0
# One-off jobs. Override "script" to do something other than run the default (pytest).
- env: TASK=build-docs
before_script: travis_retry pip install -U Sphinx
script: ./build-docs.sh linkcheck
- env: TASK=pylint
before_script: travis_retry pip install -U pylint
script: pylint bidict tests/*.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
script: python3 -O -m doctest -o IGNORE_EXCEPTION_DETAIL -o ELLIPSIS tests/*.txt

# ...and Linux.
- env: PYENV_PYTHON_VER=2.7.14
- env: PYENV_PYTHON_VER=3.4.7
- env: PYENV_PYTHON_VER=3.5.3
# Enable coverage just for the latest stable Python 3 version on Linux.
- env: PYENV_PYTHON_VER=3.6.3
# Test 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
- env: TASK=pytest PYVER=3.4.7
if: branch = master
- env: TASK=pytest PYVER=3.5.3
if: branch = master
# Enable coverage for the latest stable Python 3 release on Linux.
- env: TASK=pytest PYVER=3.6.3 COVERAGE=1
if: branch = master
before_script: travis_retry pip install -e .[coverage]
script:
- py.test --cov=bidict --cov-config=.coveragerc
- travis_retry pip install coveralls && coveralls
- env: PYENV_PYTHON_VER=3.7-dev
- env: PYENV_PYTHON_VER=pypy2.7-5.9.0
- env: PYENV_PYTHON_VER=pypy3.5-5.9.0
- |
set -e
py.test --cov=bidict --cov-config=.coveragerc
travis_retry pip install coveralls
coveralls
- env: TASK=pytest PYVER=3.7-dev
if: branch = master
- env: TASK=pytest PYVER=pypy2.7-5.9.0
if: branch = master
- env: TASK=pytest PYVER=pypy3.5-5.9.0
if: branch = master

# Test with all supported Python versions on macOS.
- env: TASK=pytest PYVER=2.7.14
if: branch = master
os: osx
osx_image: xcode9.1
- env: TASK=pytest PYVER=3.4.7
if: branch = master
os: osx
osx_image: xcode9.1
- env: TASK=pytest PYVER=3.5.3
if: branch = master
os: osx
osx_image: xcode9.1
- env: TASK=pytest PYVER=3.6.3
if: branch = master
os: osx
osx_image: xcode9.1
- env: TASK=pytest PYVER=3.7-dev
if: branch = master
os: osx
osx_image: xcode9.1
- env: TASK=pytest PYVER=pypy2.7-5.9.0
if: branch = master
os: osx
osx_image: xcode9.1
- env: TASK=pytest PYVER=pypy3.5-5.9.0
if: branch = master
os: osx
osx_image: xcode9.1
# PyPy3 not available for osx64 via pyenv. Install from homebrew instead:
before_install: brew update # Do not remove: https://github.com/Homebrew/brew/issues/3299
install:
- brew install pypy3
- pip_pypy3 install -e .[test]
script: pypy3 -m pytest

before_install:
- |
Expand All @@ -63,20 +95,19 @@ before_install:
echo "TRAVIS_COMMIT: $TRAVIS_COMMIT"
install:
# based on https://github.com/frol/flask-restplus-server-example/blob/018f48e5/.travis.yml
- |
set -e
if [[ -n "$PYENV_PYTHON_VER" ]]; then
if [[ -n "$PYVER" ]]; then
if [[ -f "$PYENV" ]]; then
pushd "$PYENV_ROOT" && git pull && popd
else
rm -rf "$PYENV_ROOT" && git clone --depth 1 https://github.com/yyuu/pyenv.git "$PYENV_ROOT"
fi
"$PYENV" install --skip-existing "$PYENV_PYTHON_VER"
export PYTHON="$PYENV_ROOT/versions/$PYENV_PYTHON_VER/bin/python"
"$PYENV" install --skip-existing "$PYVER"
export PYTHON="$PYENV_ROOT/versions/$PYVER/bin/python"
$PYTHON -m ensurepip --upgrade
$PYTHON -m pip install --upgrade virtualenv
export VENV="$HOME/virtualenvs/$PYENV_PYTHON_VER"
export VENV="$HOME/virtualenvs/$PYVER"
$PYTHON -m virtualenv --python="$PYTHON" "$VENV"
source "$VENV/bin/activate"
fi
Expand Down
100 changes: 79 additions & 21 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,85 @@ Changelog
0.14.0.dev0 (not yet released)
------------------------------

- Internal code improvements
- Improvements to CI, including:
- Add the :attr:`bidict.BidictBase.ordered` class attribute.

- 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()``.

: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.)

The previous ``OrderedBidictBase._should_compare_order_sensitive()``
method only returned True for other
:class:`OrderedBidictBase <bidict.OrderedBidictBase>` subclasses.

- 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.

- Improvements to tests and CI, including:

- Test on macOS and Windows
- Test with PyPy3
- Test with CPython 3.7-dev
- Add pylint
- Test with optimization flags
- Require pylint to pass

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

The following classes were renamed for better style guide compliance:
- ``FrozenBidictBase`` has been removed.
Use :class:`FrozenBidict <bidict.FrozenBidict>` instead.

- ``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>`
- :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>`.

- The following were renamed for better compliance with proper code style:

- ``FrozenBidictBase._compute_hash`` → :attr:`FrozenBidict.compute_hash
<bidict.FrozenBidict.compute_hash>`
- ``FrozenBidict._USE_ITEMSVIEW_HASH`` → :attr:`FrozenBidict.USE_ITEMSVIEW_HASH
<bidict.FrozenBidict.USE_ITEMSVIEW_HASH>`
- ``FrozenBidict._HASH_NITEMS_MAX`` → :attr:`FrozenBidict.HASH_NITEMS_MAX
<bidict.FrozenBidict.HASH_NITEMS_MAX>`
- ``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>`


0.13.1 (2017-03-15)
Expand Down Expand Up @@ -69,36 +130,33 @@ The following classes were renamed for better style guide compliance:
to better reflect its function.
(It is not an ABC.)

- A new
:class:`FrozenBidictBase <bidict.FrozenBidictBase>` class
has been factored out of
- A new ``FrozenBidictBase`` class has been factored out of
:class:`frozenbidict <bidict.FrozenBidict>` and
:class:`frozenorderedbidict <bidict.FrozenOrderedBidict>`.
This implements common behavior such as caching the result of
:attr:`__hash__ <bidict.FrozenBidictBase.__hash__>`
after the first call.
``__hash__`` after the first call.

- The hash implementations of
:class:`frozenbidict <bidict.FrozenBidict>` and
:class:`frozenorderedbidict <bidict.FrozenOrderedBidict>`.
have been reworked to improve performance and flexibility:

:attr:`frozenorderedbidict's hash implementation
<bidict.FrozenOrderedBidict._compute_hash>` is now order-sensitive.
<bidict.FrozenOrderedBidict.compute_hash>` is now order-sensitive.
Since ``frozenorderedbidict([(k1, v1), (k2, v2)])`` does not equal
``frozenorderedbidict([(k2, v2), (k1, v1)])``,
their hashes shouldn't be equal either. Avoids hash collisions when inserting
such objects into the same set or mapping.

See
:attr:`frozenbidict._compute_hash <bidict.FrozenBidict._compute_hash>` and
:attr:`frozenorderedbidict._compute_hash <bidict.FrozenOrderedBidict._compute_hash>`
:attr:`frozenbidict.compute_hash <bidict.FrozenBidict.compute_hash>` and
:attr:`frozenorderedbidict.compute_hash <bidict.FrozenOrderedBidict.compute_hash>`
for more documentation of the changes,
including the new
:attr:`frozenbidict._USE_ITEMSVIEW_HASH
<bidict.FrozenBidict._USE_ITEMSVIEW_HASH>` and
:attr:`frozenorderedbidict._HASH_NITEMS_MAX
<bidict.FrozenOrderedBidict._HASH_NITEMS_MAX>`
:attr:`frozenbidict.USE_ITEMSVIEW_HASH
<bidict.FrozenBidict.USE_ITEMSVIEW_HASH>` and
:attr:`frozenorderedbidict.HASH_NITEMS_MAX
<bidict.FrozenOrderedBidict.HASH_NITEMS_MAX>`
attributes.
If you have an interesting use case that requires overriding these,
or suggestions for an alternative implementation,
Expand Down
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Status
.. :target: https://pypi.python.org/pypi/bidict
.. :alt: Downloads per month
.. image:: https://img.shields.io/pypi/v/bidict.svg?colorB=65C838
.. image:: https://img.shields.io/pypi/v/bidict.svg
:target: https://pypi.python.org/pypi/bidict
:alt: Latest release

Expand Down Expand Up @@ -108,7 +108,7 @@ Release Notifications


Tip: `Follow bidict on Sibbell <https://sibbell.com/github/jab/bidict/releases/>`_
to be notified via email when a new version of bidict is released.
to be notified when a new version of bidict is released.


Installation
Expand Down
Binary file modified _static/type-hierarchy.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 4 additions & 5 deletions _static/type-hierarchy.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
graph { flow: up; }
node { font: Menlo; }

[ bidict.abc.BidirectionalMapping ] { fontsize: 0.7em; borderstyle: dashed; fill: #eeeeee; } -> [ collections.abc.Mapping ] { fontsize: 0.7em; borderstyle: dashed; fill: #eeeeee; }
[ bidict.BidictBase ] { fontsize: 0.7em; fill: #cccccc; } -> [ bidict.abc.BidirectionalMapping ]
[ bidict.BidirectionalMapping ] { fontsize: 0.7em; borderstyle: dashed; fill: #eeeeee; } -> [ collections.abc.Mapping ] { fontsize: 0.7em; borderstyle: dashed; fill: #eeeeee; }
[ bidict.BidictBase ] { fontsize: 0.7em; fill: #cccccc; } -> [ bidict.BidirectionalMapping ]
[ bidict.bidict ] -> [ bidict.BidictBase ]
[ bidict.LooseBidict ] -> [ bidict.bidict ]
[ bidict.FrozenBidictBase ] { fontsize: 0.7em; fill: #cccccc; } -> [ bidict.BidictBase ]
[ bidict.FrozenBidict ] -> [ bidict.FrozenBidictBase ]
[ bidict.FrozenBidict ] -> [ bidict.BidictBase ]
[ bidict.OrderedBidictBase ] { fontsize: 0.7em; fill: #cccccc; } -> [ bidict.BidictBase ]
[ bidict.OrderedBidict ] -> [ bidict.OrderedBidictBase ]
[ bidict.OrderedBidict ] -> [ bidict.bidict ]
[ bidict.FrozenOrderedBidict ] -> [ bidict.OrderedBidictBase ]
[ bidict.FrozenOrderedBidict ] -> [ bidict.FrozenBidictBase ]
[ bidict.FrozenOrderedBidict ] -> [ bidict.FrozenBidict ]
[ bidict.LooseOrderedBidict ] -> [ bidict.OrderedBidict ]
[ bidict.LooseOrderedBidict ] -> [ bidict.LooseBidict ]

Expand Down
3 changes: 1 addition & 2 deletions bidict/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
DuplicationError, KeyDuplicationError, ValueDuplicationError,
KeyAndValueDuplicationError)
from ._bidict import bidict
from ._frozen import FrozenBidictBase, FrozenBidict, FrozenOrderedBidict
from ._frozen import FrozenBidict, FrozenOrderedBidict
from ._loose import LooseBidict, LooseOrderedBidict
from ._named import namedbidict
from ._ordered import OrderedBidictBase, OrderedBidict
Expand All @@ -36,7 +36,6 @@
'bidict',
'LooseBidict',
'LooseOrderedBidict',
'FrozenBidictBase',
'FrozenBidict',
'FrozenOrderedBidict',
'namedbidict',
Expand Down
11 changes: 11 additions & 0 deletions bidict/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ class BidictBase(BidirectionalMapping):
which implements all the shared logic.
Users will typically only interact with subclasses of this class.
.. py:attribute:: ordered
Whether this bidict's items should be considered ordered
for the purpose of equality comparison with another ordered mapping.
Always returns False, since in this base class we presume no ordering.
Subclasses with ordered items override this as needed.
.. py:attribute:: _fwd
The backing one-way dict for the forward items.
Expand Down Expand Up @@ -148,6 +156,8 @@ class BidictBase(BidirectionalMapping):
:class:`DuplicationBehavior` in the event of key and value duplication.
"""

ordered = False

_on_dup_key = OVERWRITE
_on_dup_val = RAISE
_on_dup_kv = RAISE
Expand Down Expand Up @@ -193,6 +203,7 @@ def __repr__(self):
return tmpl % delegate(iteritems(self))

def __eq__(self, other):
"""Like :py:meth:`dict.__eq__`."""
# This should be faster than using Mapping.__eq__'s implementation.
return self._fwd == other

Expand Down

0 comments on commit 4ce5616

Please sign in to comment.