# 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'