Reputation:
I'm trying to make a function in Python that does the equivalent of compile(), but also lets me get the original string back. Let's call those two functions comp() and decomp(), for disambiguation purposes. That is,
a = comp("2 * (3 + x)", "", "eval")
eval(a, dict(x=3)) # => 12
decomp(a) # => "2 * (3 + x)"
The returned string does not have to be identical ("2*(3+x)" would be acceptable), but it needs to be basically the same ("2 * x + 6" would not be).
Here's what I've tried that doesn't work:
Here's what does work, with issues:
Upvotes: 3
Views: 336
Reputation: 32478
This is kind of a weird problem, and my initial reaction is that you might be better off doing something else entirely to accomplish whatever it is you're trying to do. But it's still an interesting question, so here's my crack at it: I make the original code source an unused constant of the code object.
import types
def comp(source, *args, **kwargs):
"""Compile the source string; takes the same arguments as builtin compile().
Modifies the resulting code object so that the original source can be
recovered with decomp()."""
c = compile(source, *args, **kwargs)
return types.CodeType(c.co_argcount, c.co_nlocals, c.co_stacksize,
c.co_flags, c.co_code, c.co_consts + (source,), c.co_names,
c.co_varnames, c.co_filename, c.co_name, c.co_firstlineno,
c.co_lnotab, c.co_freevars, c.co_cellvars)
def decomp(code_object):
return code_object.co_consts[-1]
>>> a = comp('2 * (3 + x)', '', 'eval')
>>> eval(a, dict(x=3))
12
>>> decomp(a)
'2 * (3 + x)'
Upvotes: 6
Reputation: 1843
My approach would be to wrap the code object in another object. Something like this:
class CodeObjectEnhanced(object):
def __init__(self, *args):
self.compiled = compile(*args)
self.original = args[0]
def comp(*args):
return CodeObjectEnhanced(*args)
Then whenever you need the code object itself, you use a.compiled, and whenever you need the original, you use a.original. There may be a way to get eval to treat the new class as though it were an ordinary code object, redirecting the function to call eval(self.compiled) instead.
One advantage of this is the original string is deleted at the same time as the code object. However you do this, I think storing the original string is probably the best approach, as you end up with the exact string you used, not just an approximation.
Upvotes: 4