Created
December 4, 2013 20:29
-
-
Save dplepage/7794951 to your computer and use it in GitHub Desktop.
Fun with inspect and ast. I do not recommend using this code in production.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import inspect, ast | |
from itertools import islice, chain, cycle | |
def iter_n(iterator, n, default=None): | |
return islice(chain(iterator, cycle([default])), n) | |
def unpack(sequence, default=None): | |
stack = inspect.stack() | |
try: | |
frame = stack[1][0] | |
source = inspect.getsource(inspect.getmodule(frame)).splitlines() | |
line = source[frame.f_lineno-1].strip() | |
try: | |
tree = ast.parse(line, 'whatever', 'exec') | |
except SyntaxError: | |
return tuple(sequence) | |
exp = tree.body[0] | |
if not isinstance(exp, ast.Assign): | |
return tuple(sequence) | |
exp = exp.targets[0] | |
if not isinstance(exp, ast.Tuple): | |
return tuple(sequence) | |
n_items = len(exp.elts) | |
return tuple(iter_n(sequence, n_items, default)) | |
finally: | |
del stack |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Extra items are discarded | |
x, y = unpack([1,2,3,4,5]) | |
assert (x,y) == (1,2) | |
# Missing items become None | |
x, y, z = unpack([9]) | |
assert (x, y, z) == (9, None, None) | |
# Or the default you provide | |
x, y, z = unpack([1], 'foo') | |
assert (x, y, z) == (1, 'foo', 'foo') | |
# unpack() is equivalent to tuple() if it's not part of an assignment | |
assert unpack('abc') == ('a', 'b', 'c') | |
# Or if it's part of an assignment that isn't sequence-unpacking | |
x = unpack([1,2,3]) | |
assert x == (1,2,3) | |
# Add a comma to force tuple assignment: | |
x, = unpack([1,2,3]) | |
assert x == 1 | |
# unpack only uses the first assignment target | |
# So in this case, unpack('foobar') returns tuple('foo') | |
(x, y, z) = t = unpack('foobar') | |
assert (x, y, z) == t == ('f', 'o', 'o') | |
# But in this case, it returns tuple('foobar') | |
try: | |
t = (x, y, z) = unpack('foobar') | |
except ValueError as e: | |
assert str(e) == 'too many values to unpack' | |
else: | |
raise Exception("That should have failed.") | |
# Also, it won't work if the call spans multiple lines, because it only | |
# inspects the actual line where the call happens: | |
try: | |
(x, y, z) = unpack([ | |
1, 2, 3, 4]) | |
except ValueError as e: | |
assert str(e) == 'too many values to unpack' | |
else: | |
raise Exception("That should have failed.") | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment