On 03/30/2015 04:54 PM, Andrew Barnert wrote:
[snip]
And the reason for bringing this idea up here was I think it could be used to implement the lite macro behaviour that was suggested with a bit of added syntax.
On the other hand it appears to me, that Python is going in the direction of making it easier to compile to C code. More dynamic features may not be helpful in the long run.
I don't think this is true. There definitely isn't any momentum in that direction--shedskin and its two competitors are dead, while PyPy and numba are alive and kicking ass. And I don't think it's desirable, or that the core developers think it's desirable.
Good to know. BTW... here is a first try. Works barely. Don't use the decorators inside a function yet. I'm sure there's quite a few byte code changes that need to be done to make this dependable, but it does show it's possible and is something interesting to play with. ;-) This is closely related to continuations, but rather than try to capture a frame and then continue the code, it just applies code objects to a name space. That in it self ins't new, but getting the parts from decorated functions is a nice way to do it. import inspect def fix_code(c): """ Fix code object. This attempts to make a code object from a function be more like what you would get if you gave exec a string. * There is more that needs to be done to make this work dependably. But it's a start. """ varnames = c.co_varnames names = c.co_names xchg = [(124, 0x65), #LOAD_FAST to LOAD_NAME (125, 0x5a)] #STORE_FAST to STORE_NAME bcode = [] bgen = iter(c.co_code) for b in bgen: for bx1, bx2 in xchg: if b == bx1: i1 = next(bgen) i2 = next(bgen) index = i1 + i2 * 256 if b in [124, 125]: b = bx2 char = varnames[index] names = names + (char,) index = names.index(char) i2 = index // 256 i1 = index - i2 bcode.append(b) bcode.append(i1) bcode.append(i2) break else: bcode.append(b) co_code = bytes(bcode) Code = type(c) co = Code( 0, #co_argcount, 0, #co_kwonlyargcount, 0, #co_nlocals, c.co_stacksize, 64, #co_flags, co_code, c.co_consts, names, #co_names (), #co_varnames, c.co_filename, c.co_name, c.co_firstlineno, c.co_lnotab ) return co class fn_signature: """ Hold a signature from a function. When called with argumetns it will bind them and return a mapping. """ def __init__(self, fn): self.sig = inspect.signature(fn) def __call__(self, *args, **kwds): return dict(self.sig.bind(*args, **kwds).arguments) class fn_code: """ Create a relocatable code object that can be applied to a name space. """ def __init__(self, fn): self.co = fix_code(fn.__code__) def __call__(self, ns): return eval(self.co, ns) def get_sig(fn): """ Decorator to get a signature. """ return fn_signature(fn) def get_code(fn): """ Decorator to get a code object. """ return fn_code(fn) # Example 1 # Applying code to a namespace created by a signature. @get_sig def foo(x): pass @get_code def inc_x(): x += 1 @get_code def dec_x(): x -= 1 @get_code def get_x(): return x ns = foo(3) # creates a namespace inc_x(ns) # Apply code to namespace inc_x(ns) print(get_x(ns)) # --> 5 dec_x(ns) dec_x(ns) print(get_x(ns)) # --> 3 # Example 2 # Code + signature <---> function def add_xy(x, y): return x + y sig = get_sig(add_xy) co = get_code(add_xy) print(co(sig(3, 5))) # --> 8