[Python-ideas] History on proposals for Macros?
Ron Adam
ron3200 at gmail.com
Wed Apr 1 07:11:08 CEST 2015
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
More information about the Python-ideas
mailing list