dictmatching/dictmatching.py
import inspect
import itertools
from decorator import contextmanager
from ._dis import dis
@contextmanager
def unpack(dictionary):
"""
Unpacks a dictionary (or any mapping type) in a nice and clean form.
Usage example:
>>> d1 = dict(a=1, b=2)
>>> d2 = dict(x=3, y=4)
>>> with unpack(d1) as b, unpack(d2) as (x, y):
... assert b == 2
... assert x == 3
... assert y == 4
"""
# +1 for unpack and +1 that @contextmanager adds
unpacker_frame = inspect.currentframe().f_back.f_back
dest = _get_with_destination(unpacker_frame)
if isinstance(dest, tuple):
yield tuple(dictionary[key] for key in dest)
else:
yield dictionary[dest]
def _get_with_destination(unpacker_frame):
after_unpack = (
op for op in dis(unpacker_frame.f_code)
if op.offset >= unpacker_frame.f_lasti and op.name != 'EXTENDED_ARG'
)
# Dispose SETUP_WITH
after_unpack = list(after_unpack)[1:]
if after_unpack[0].name == 'STORE_FAST':
# A single variable unpacking
return after_unpack[0].desc
else:
# Multi variables unpacking, dispose of UNPACK_SEQUENCE
store_ops = itertools.takewhile(
lambda op: op.name == 'STORE_FAST',
after_unpack[1:]
)
return tuple(store_op.desc for store_op in store_ops)