bidict/tests/test_bidict.txt

314 lines
7.7 KiB
Plaintext

# Copyright 2009-2022 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/.
Test script for bidict.bidict:
>>> from bidict import bidict
>>> bi = bidict({1: 'one', 2: 'two', 3: 'three'})
>>> bi
bidict({1: 'one', 2: 'two', 3: 'three'})
Works like dict for getting and changing forward mappings:
>>> bi[2]
'two'
>>> bi[2] = 'twain'
>>> bi[2]
'twain'
>>> bi[4]
Traceback (most recent call last):
...
KeyError: 4
>>> del bi[2]
>>> bi.pop(3)
'three'
>>> bi.update({4: 'four'})
>>> bi.popitem()
(4, 'four')
>>> bi
bidict({1: 'one'})
``put`` can also be used to insert a mapping as long as its key and value
don't already exist:
>>> bi.put(0, 'zero')
>>> bi[0]
'zero'
>>> bi.put(1, 'aught')
Traceback (most recent call last):
...
bidict.KeyDuplicationError: 1
>>> del bi[1]
>>> bi.put(1, 'aught')
>>> bi[1]
'aught'
>>> del bi[0]
>>> bi
bidict({1: 'aught'})
bidicts maintain references to their inverses via the ``inverse`` property,
which can also be used to access or modify them:
>>> bi.inverse
bidict({'aught': 1})
>>> bi.inverse['aught']
1
>>> bi.inverse['aught'] = 'one'
>>> bi
bidict({'one': 'aught'})
>>> bi.inverse.pop('aught')
'one'
>>> bi == bi.inverse == bidict()
True
>>> bi.inverse.update(one=1)
>>> bi
bidict({1: 'one'})
>>> bi is bi.inverse.inverse
True
>>> bi.inverse is bi.inverse.inverse.inverse
True
bidicts work with ``inverted`` as expected:
>>> from bidict import inverted
>>> biinv = bidict(inverted(bi))
>>> biinv
bidict({'one': 1})
This created a new object (equivalent but not identical):
>>> biinv == bi.inverse
True
>>> biinv is bi.inverse
False
Inverting the inverse should round-trip:
>>> bi == bidict(inverted(inverted(bi)))
True
>>> bi = bi.inverse
>>> bi == bidict(inverted(inverted(bi)))
True
The rest of the ``MutableMapping`` interface is supported:
>>> bi.get('one')
1
>>> bi.get('zero')
>>> bi.get('zero', 'default')
'default'
>>> list(bi.keys())
['one']
>>> list(bi.values())
[1]
>>> list(bi.items())
[('one', 1)]
>>> bi.setdefault('one', 2)
1
>>> bi.setdefault('two', 2)
2
>>> bi.pop('one')
1
>>> bi
bidict({'two': 2})
>>> bi.inverse
bidict({2: 'two'})
>>> bi.pop('no-such-key')
Traceback (most recent call last):
...
KeyError: 'no-such-key'
>>> bi.pop('no-such-key', 'default')
'default'
>>> bi.pop('wrong', 'number', 'of', 'args')
Traceback (most recent call last):
...
TypeError: ...pop() takes from 2 to 3 positional arguments but 5 were given
>>> bi.popitem()
('two', 2)
>>> bi.popitem()
Traceback (most recent call last):
...
KeyError: 'popitem(): dictionary is empty'
>>> bi.inverse.setdefault(3, 'three')
'three'
>>> bi
bidict({'three': 3})
>>> len(bi) # calls __len__
1
>>> [key for key in bi] # calls __iter__, returns keys like dict
['three']
>>> 'three' in bi # calls __contains__
True
>>> list(bi.keys())
['three']
>>> list(bi.values())
[3]
>>> bi.update([('four', 4)])
>>> bi.update({'five': 5}, six=6, seven=7)
>>> sorted(bi.items(), key=lambda x: x[1])
[('three', 3), ('four', 4), ('five', 5), ('six', 6), ('seven', 7)]
>>> bi.clear()
>>> bi
bidict()
Empty updates are a no-op:
>>> bi.update()
>>> bi
bidict()
>>> bi.forceupdate()
>>> bi
bidict()
Initializing with wrong number of positional args is a TypeError:
>>> bidict('wrong', 'number', 'of', 'args')
Traceback (most recent call last):
...
TypeError: Expected at most 1 positional argument, got 4
Initializing with different keys mapping to the same value fails:
>>> bidict([(1, 1), (2, 1)])
Traceback (most recent call last):
...
bidict.ValueDuplicationError: 1
Adding a new key associated with an existing value fails:
>>> b = bidict({1: 1})
>>> b[2] = 1
Traceback (most recent call last):
...
bidict.ValueDuplicationError: 1
>>> b.update({2: 1})
Traceback (most recent call last):
...
bidict.ValueDuplicationError: 1
``forceput`` and ``forceupdate`` can be used instead:
>>> b.forceput(2, 1)
>>> b
bidict({2: 1})
>>> b.forceupdate({1: 1})
>>> b
bidict({1: 1})
Trying to insert an existing mapping does not raise, and is a no-op:
>>> b = bidict({1: 'one'})
>>> b[1] = 'one'
>>> b[1]
'one'
>>> b.inverse['one'] = 1
>>> b.inverse['one']
1
The following case does not half-succeed,
i.e. the bidict is not in an inconsistent state after:
>>> b = bidict(one=1, two=2)
>>> b['one'] = 2
Traceback (most recent call last):
...
bidict.KeyAndValueDuplicationError: ('one', 2)
>>> len(b) == len(b.inverse)
True
``put`` and ``putall`` allow you to have
per-call control over duplication behavior
(see doctests in ``../docs/unique-values.rst.inc``).
Even with RAISE duplication behavior,
inserting existing items is a no-op (i.e. it doesn't raise):
>>> from bidict import RAISE, OnDup
>>> b.putall(
... [('three', 3), ('one', 1)],
... OnDup(key=RAISE, val=RAISE)
... ) # does not raise an error because these items were already contained
>>> b0 = b.copy()
>>> b.putall([]) # no-op
>>> b == b0
True
Make sure copy.copy and copy.deepcopy create shallow and deep copies, respectively:
>>> from copy import copy, deepcopy
>>> from bidict import frozenbidict
>>> b = frozenbidict({1: frozenbidict()})
>>> c = copy(b)
>>> d = deepcopy(b)
>>> b == c == d
True
>>> b[1] is c[1]
True
>>> b[1] is d[1]
False
Bidicts support PEP 584-style dict merge operators:
>>> b = bidict({'one': 1})
>>> b | {}
bidict({'one': 1})
>>> b | {'one': 1}
bidict({'one': 1})
>>> b | {'two': 2}
bidict({'one': 1, 'two': 2})
>>> b | {'two': 2} | {'three': 3}
bidict({'one': 1, 'two': 2, 'three': 3})
>>> b | {'one': 111} # duplicate key -> succeeds, last one wins (as with dict.__or__)
bidict({'one': 111})
>>> {'one': 111} | b # ditto
bidict({'one': 1})
>>> b | {'uno': 1} # duplicate value -> ValueDuplicationError
Traceback (most recent call last):
...
bidict.ValueDuplicationError: 1
>>> {'uno': 1} | b # ditto
Traceback (most recent call last):
...
bidict.ValueDuplicationError: 1
>>> b = bidict({'one': 1, 'two': 2})
>>> b | {'one': 2}
Traceback (most recent call last):
...
bidict.KeyAndValueDuplicationError: ('one', 2)
>>> b |= {'three': 3} # in-place merge
>>> b
bidict({'one': 1, 'two': 2, 'three': 3})
>>> b |= {'one': 111} # in-place merge, duplicate key -> succeeds, last one wins
>>> b
bidict({'one': 111, 'two': 2, 'three': 3})
>>> b |= {'dos': 2} # duplicate value -> ValueDuplicationError
Traceback (most recent call last):
...
bidict.ValueDuplicationError: 2
>>> b
bidict({'one': 111, 'two': 2, 'three': 3})
>>> b |= {'one': 3}
Traceback (most recent call last):
...
bidict.KeyAndValueDuplicationError: ('one', 3)
>>> b | 1
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for |: 'bidict' and 'int'
>>> 1 | b
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for |: 'int' and 'bidict'