Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement stricter 1-to-1 checking #26

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 6 additions & 1 deletion .pre-commit-config.yaml
@@ -1,7 +1,12 @@
- repo: git://github.com/pre-commit/pre-commit-hooks
sha: 0ff8620e03bf7acfb4dfdc86de14baf032c604f5
sha: v0.4.2
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: debug-statements
- id: double-quote-string-fixer

#- repo: git://github.com/FalconSocial/pre-commit-mirrors-pep257
# sha: v0.3.2
# hooks:
# - id: pep257
5 changes: 3 additions & 2 deletions .travis.yml
@@ -1,6 +1,7 @@
language: python
python:
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "pypy"
Expand All @@ -12,8 +13,8 @@ install:
- pip install tox-travis

script:
- tox
- py.test --cov bidict tests
- python setup.py test
- pep257 bidict

after_success:
coveralls
Expand Down
7 changes: 4 additions & 3 deletions CONTRIBUTING.rst
Expand Up @@ -107,12 +107,13 @@ Besides creating issues and pull requests, there are other ways to contribute.
If you read the code and learned something new, let us know and it'll give us the warm fuzzies.
If you're using bidict in a project you work on, blog about your experience.
If you come across other people who could find it useful, spread the word.
If bidict has helped you accomplish work you've been paid for,
please `support bidict <https://gumroad.com/l/XGXTp>`_
If bidict has helped you accomplish your work,
especially work you've been paid for,
please `support bidict <https://gumroad.com/l/bidict>`_
and/or ask your organization to do the same.

.. image:: ./docs/_static/support-on-gumroad.png
:target: https://gumroad.com/l/XGXTp
:target: https://gumroad.com/l/bidict
:alt: Support bidict

You can also use `Bountysource <https://www.bountysource.com/teams/jab>`_
Expand Down
7 changes: 4 additions & 3 deletions README.rst
Expand Up @@ -4,6 +4,7 @@ bidict
Efficient, Pythonic bidirectional map implementation and related functionality.

.. image:: ./docs/_static/logo.png
:target: https://bidict.readthedocs.org/
:alt: bidict logo


Expand Down Expand Up @@ -47,7 +48,7 @@ Status
:alt: License

.. image:: https://img.shields.io/badge/gumroad-support%20bidict-brightgreen.svg
:target: https://gum.co/XGXTp
:target: https://gumroad.com/l/bidict
:alt: Support bidict

.. image:: https://img.shields.io/badge/Paypal-Buy%20a%20Drink-blue.svg
Expand Down Expand Up @@ -86,12 +87,12 @@ talk about your use case.

bidict is the result of hundreds of hours of voluntary, unpaid work.
If bidict has helped you accomplish work you've been paid for,
please `support bidict <https://gumroad.com/l/XGXTp>`_
please `support bidict <https://gumroad.com/l/bidict>`_
and/or ask your organization to do the same.
Your support directly contributes to bidict's sustainability.

.. image:: ./docs/_static/support-on-gumroad.png
:target: https://gumroad.com/l/XGXTp
:target: https://gumroad.com/l/bidict
:alt: Support bidict

Check out
Expand Down
2 changes: 1 addition & 1 deletion bidict/VERSION
@@ -1 +1 @@
0.10.0.dev
0.10.0.dev0
15 changes: 9 additions & 6 deletions bidict/__init__.py
@@ -1,26 +1,29 @@
# -*- coding: utf-8 -*-

"""
Efficient, Pythonic bidirectional map implementation
and related functionality.
Efficient, Pythonic bidirectional map implementation and related functionality.

See https://bidict.readthedocs.org/ for comprehensive documentation.

.. :copyright: (c) 2015 Joshua Bronson.
.. :license: ISCL. See LICENSE for details.

"""

from ._common import BidirectionalMapping, CollapseException
from ._common import BidirectionalMapping, BidictException, KeyExistsException, ValueExistsException
from ._bidict import bidict
from ._collapsing import collapsingbidict
from ._loose import loosebidict
from ._frozen import frozenbidict
from ._named import namedbidict
from .util import pairs, inverted

__all__ = (
'BidirectionalMapping',
'CollapseException',
'BidictException',
'KeyExistsException',
'ValueExistsException',
'bidict',
'collapsingbidict',
'loosebidict',
'frozenbidict',
'namedbidict',
'pairs',
Expand Down
109 changes: 75 additions & 34 deletions bidict/_bidict.py
@@ -1,75 +1,116 @@
from ._common import BidirectionalMapping, _missing
"""Implements :class:`bidict.bidict`, the mutable bidirectional map type."""

from ._common import BidirectionalMapping
from .util import pairs
from collections import MutableMapping


class bidict(BidirectionalMapping, MutableMapping):
"""
Mutable bidirectional map type.
"""
"""Mutable bidirectional map type."""

def _del(self, key):
val = self._fwd[key]
del self._fwd[key]
del self._bwd[val]
return val

def __delitem__(self, key):
"""Like :py:meth:`dict.__delitem__`, keeping bidirectionality intact."""
self._del(key)

def __setitem__(self, key, val):
self._put(key, val)
"""
Set the value for *key* to *val*.

If *key* is already associated with a different value,
the old value will be replaced with *val*.
Use :attr:`put` to raise an exception in this case instead.

If *val* is already associated with a different key,
an exception is raised
to protect against accidentally replacing the existing mapping.

If *key* is already associated with *val*, this is a no-op.

Use :attr:`forceput` to unconditionally associate *key* with *val*,
replacing any existing mappings as necessary to preserve uniqueness.

:raises bidict.ValueExistsException: if attempting to set a mapping
with a non-unique value.
"""
self._put(key, val, overwrite_key=False, overwrite_val=True)

def put(self, key, val):
"""
Alternative to using the setitem syntax to insert a mapping.
Associate *key* with *val* iff *key* and *val* are both unique.

That is, insert the given mapping iff
*key* is not already associated with an existing value and
*val* is not already associated with an existing key.

If *key* is already associated with *val*, this is a no-op.

:raises bidict.KeyExistsException: if attempting to insert a mapping
with the same key as an existing mapping.

:raises bidict.ValueExistsException: if attempting to insert a mapping
with the same value as an existing mapping.
"""
self._put(key, val)
self._put(key, val, overwrite_key=False, overwrite_val=False)

def forceput(self, key, val):
"""
Like :attr:`bidict.bidict.put` but silently removes any existing
mapping that would otherwise cause a :class:`bidict.CollapseException`
before inserting the given mapping::

>>> b = bidict({0: 'zero', 1: 'one'})
>>> b.put(0, 'one') # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
CollapseException: ((0, 'zero'), (1, 'one'))
>>> b.forceput(0, 'one')
>>> b
bidict({0: 'one'})
Associate *key* with *val* unconditionally.

Replace any existing mappings containing key *key* or value *val*
as necessary to preserve uniqueness.
"""
oldval = self._fwd.get(key, _missing)
oldkey = self._bwd.get(val, _missing)
if oldval is not _missing:
del self._bwd[oldval]
if oldkey is not _missing:
del self._fwd[oldkey]
self._fwd[key] = val
self._bwd[val] = key
self._put(key, val, overwrite_key=True, overwrite_val=True)

def clear(self):
"""Remove all items."""
self._fwd.clear()
self._bwd.clear()

def pop(self, key, *args):
val = self._fwd.pop(key, *args)
del self._bwd[val]
"""Like :py:meth:`dict.pop`, keeping bidirectionality intact."""
ln = len(args) + 1
if ln > 2:
raise TypeError('pop expected at most 2 arguments, got %d' % ln)
try:
val = self._del(key)
except KeyError:
if args:
return args[0]
raise
return val

def popitem(self):
if not self._fwd:
"""Like :py:meth:`dict.popitem`, keeping bidirectionality intact."""
if not self:
raise KeyError('popitem(): %s is empty' % self.__class__.__name__)
key, val = self._fwd.popitem()
del self._bwd[val]
return key, val

def setdefault(self, key, default=None):
val = self._fwd.setdefault(key, default)
self._bwd[val] = key
return val
"""Like :py:meth:`dict.setdefault`, keeping bidirectionality intact."""
if key not in self:
self[key] = default
return self[key]

def update(self, *args, **kw):
"""
Like :py:meth:`dict.update`, keeping bidirectionality intact.

Similar to calling :attr:`__setitem__` for each mapping given.

:raises bidict.ValueExistsException: if attempting to insert a mapping
with a non-unique value.
"""
return self._update(*args, **kw)

def forceupdate(self, *args, **kw):
"""Call :attr:`forceput` for each mapping given."""
for k, v in pairs(*args, **kw):
self._put(k, v)
self.forceput(k, v)
9 changes: 0 additions & 9 deletions bidict/_collapsing.py

This file was deleted.