Skip to content

Instantly share code, notes, and snippets.

@obriencj obriencj/bindings.py
Last active Sep 14, 2017

Embed
What would you like to do?
local bindings from a map
#! /usr/bin/env python3
# This is a Proof-Of-Concept, and only works in Python3.
# A Python2 port is almost certainly possible, haven't even tried.
from inspect import currentframe
from dis import get_instructions
def bindings(source_map):
"""
Find just the values for the bindings you were going to ask for.
"""
# find our calling frame, and the index of the op that called us
caller = currentframe().f_back
code = caller.f_code
index = caller.f_lasti
# disassemble the instructions for the calling frame, and advance
# past our calling op's index
iterins = get_instructions(code)
for instr in iterins:
if instr.offset > index:
break
if instr.opname != "UNPACK_SEQUENCE":
# someone invoked us without being the right-hand side of an
# unpack assignment, do let's be a noop
return source_map
# this is the number of assignments being unpacked, we'll get that
# many STORE_ ops from the bytecode
count = instr.argval
# each STORE_ op has an argval which is the name it would assign
# to. This is just a convenience that the dis module fills in!
dest_keys = (next(iterins).argval for _ in range(0, count))
# finally, just return a generator that'll provide the values from
# the source_map that match to the bindings
return (source_map[dest] for dest in dest_keys)
def test():
data = {"tacos": 900, "beer": 700, "a": 100, "b": 200, "c": 300, }
a, b, c = bindings(data)
print("a:", a)
print("b:", b)
print("c:", c)
if __name__ == "__main__":
test()
@obriencj

This comment has been minimized.

Copy link
Owner Author

obriencj commented Sep 14, 2017

Summarizing things you'd need to do to make this more robust:

  • check that the instructions after UNPACK_SEQUENCE are all STORE_FAST, STORE_DEREF, STORE_GLOBAL, or STORE_NAME. If they aren't, it would indicate some nested unpacking or a *arg function application. In both of those cases, just return the original map
  • this is targeted at Python3. The Python2 dis module kinda sucks, and there's no Instruction data type. You'd have to copy/paste most of dis.dis and have it accumulate and yield the ops and their arguments. Still well within reason!
  • collapse it all into one function
  • write a bunch of unittests and check across a wide range of pythons (2.5+, 3.3+)
@obriencj

This comment has been minimized.

Copy link
Owner Author

obriencj commented Sep 14, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.