[Python-Dev] defmacro (was: Anonymous blocks)

Robert Brewer fumanchu at amor.org
Mon Apr 25 23:02:55 CEST 2005


Guido van Rossum wrote:
> > Why not just introduce macros?
> 
> Because I've been using Python for 15 years without needing them?
> Sorry, but "why not add feature X" is exactly what we're trying to
> AVOID here. You've got to come up with some really good use cases
> before we add new features. "I want macros" just doesn't cut it.

I had a use-case recently which could be done using macros. I'll let you
all decide whether it would be "better" with macros or not. ;)

My poor-man's ORM uses descriptors to handle the properties of domain
objects, a lot of which need custom triggers, constraint-checking,
notifications, etc. The base class has:

    def __set__(self, unit, value):
        if self.coerce:
            value = self.coerce(unit, value)
        oldvalue = unit._properties[self.key]
        if oldvalue != value:
            unit._properties[self.key] = value

At one time, I had something like:

    def __set__(self, unit, value):
        if self.coerce:
            value = self.coerce(unit, value)
        oldvalue = unit._properties[self.key]
        if oldvalue != value:
            if self.pre:
                self.pre(unit, value)
            unit._properties[self.key] = value
            if self.post:
                self.post(unit, value)

...to run pre- and post-triggers. But that became unwieldy recently when
one of my post functions depended upon calculations inside the
corresponding pre function.

So currently, all subclasses just override __set__, which leads to a
*lot* of duplication of code. If I could write the base class' __set__
to call "macros" like this:

    def __set__(self, unit, value):
        self.begin()
        if self.coerce:
            value = self.coerce(unit, value)
        oldvalue = unit._properties[self.key]
        if oldvalue != value:
            self.pre()
            unit._properties[self.key] = value
            self.post()
        self.end()

    defmacro begin:
        pass
    
    defmacro pre:
        pass
    
    defmacro post:
        pass
    
    defmacro end:
        pass


...(which would require macro-blocks which were decidedly *not*
anonymous) then I could more cleanly write a subclass with additional
"macro" methods:

    defmacro pre:
        old_children = self.children()
    
    defmacro post:
        for child in self.children:
            if child not in old_children:
                notify_somebody("New child %s" % child)


Notice that the "old_children" local gets injected into the namespace of
__set__ (the caller) when "pre" is executed, and is available inside of
"post". The "self" name doesn't need to be rebound, either, since it is
also available in __set__'s local scope. We also avoid all of the
overhead of separate frames.

The above is quite ugly written with callbacks (due to excessive
argument passing), and is currently fragile when overriding __set__ (due
to duplicated code).

I'm sure there are other cases with both 1) a relatively invariant
series of statements and 2) complicated extensions of that series. Of
course, you can do the above with compile() and exec. Maybe I'm just
averse to code within strings.

Some ideas. Now tear 'em apart. :)


Robert Brewer
MIS
Amor Ministries
fumanchu at amor.org


More information about the Python-Dev mailing list