[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