1.8 bytecodehacks.fleshy

This module is used in the implementation of the bytecodehack library; if you're actually writing bytecodehacks you should be able to ignore it's presence.

This less-elegant-than-it-should-probably-be module allows me to seem to give the code attribute to CodeStrings and the function attribute to the EditableCode object, i.e. the attributes that reach back up the containment heirachy, without creating circular references. Circular references are the bane of large object systems in Python as its refcounting scheme means that circular references almost always lead to memory leaks.

The rationale behind this module is that when an attribute is being stored in the body of its parent, it has no need to refer to its parent. This reference can be added in whenever a reference to the attribute is made, and a new object with this attribute added returned. Confused? A diagram might help, and courtesy of the rather remarkable dia program, here are some.

If the constructor of EditableCode behaved like this:

class EditableCode:
    def __init__(self):
        self.co_code = CodeString()
        self.co_code.code = self

then executing

code = EditableCode()

results in a data structure looking roughly like this:

The cyclic reference is obvious, so when the reference to codegoes out of scope, code and code.co_code refer mutually to each other, and so the storage they occupy will not be reclaimed.

In this simple scheme, a reference to "code.co_code" just points into another part of the structure:

What the EditableCode contructor actually does is more like:

from bytecodehacks.fleshy import Flesher

class EditableCode(Flesher):
    def __init__(self):
        self.co_code = CodeString()
        self.make_fleshed("co_code",code=Flesher.MagicSelfValue)

Now executing

code = EditableCode()

results in a structure that looks like this:

and executing "code.co_code" now triggers the magic Flesher.__getattr__ method which clones the co_code attribute, adds a code attribute to the clone (I refer to this process as ``fleshing'', for no particular reason) and returns it. This leads to a data structure looking like this:

No cyclic references! Magic! This last diagram also indicates the most serious problem with this approach - a new object is created with each attribute access.

Flesher ()
This mixin class allows a derived class to have Fleshable attributes that are automagically fleshed on access.

You must arrange for __init__ to be called. If you get tracebacks like:

Traceback (innermost last):
  File "<stdin>", line 1, in ?
  File "/home/mwh21/src/python/bytecodehacks/fleshy.py", line 19, in __getattr__
    dict = self.__dict__[fleshable_dict_name]
KeyError: $ forgot to call __init__ did we? $

then you haven't.

It is in my opinion one of the suckiest areas of Python, that it is possible by carelessness to fail to initialise a base class part of a derived object. I'd be upset if it wasn't possible to do this deliberately (this module wouldn't work, for starters) but I think it should have to be an act of will.

make_fleshed (attr_name,attr1=value1,...)
Arrange for "self.attr_name" (which must already exist, and be an instance of some class derived from Fleshable below) to be automatically ``fleshed'' with the attributes and values passed as keyword arguments on access.
__getattr__ (attr_name)
Check to see if attr_name is a fleshable attribute - if it is flesh it, and return it.
MagicSelfValue
There is a subtlety in using make_fleshed when you want one of the fleshed attributes to hold the value of "self". As the Flesher machinery needs to store a reference to the values it will flesh an attribute with, if it stored a reference to "self" then there would be a cyclic reference internal to the object! That's not at all what we want, so this class variable (actually it's an empty list - the point is it has a unique id) can be used in the argument list to make_fleshed to indicate an attribute should be fleshed out with the value of "self".

Fleshable ()
This mixin class allows a class to be used as a fleshable attribute to a Flesher instance. Its methods shouldn't really imapct on the code used by code using fleshy if the Fleshable is only accessed as an attribute. However, code_editor.CodeString has fleshable children stored in a list (the opcodes), and so it needs to involve itself in the inner workings to some extent.
flesh (attr1=value1,...)
``Fleshes'' the object with the passed attributes (i.e. clones it, adds the attributes to the clone, and returns it).
unflesh ()
If the object is fleshed, return the object the fleshed object was cloned from. If the object is not fleshed, just returns self. Call this method if an object is being inserted into a container and you suspect it might contain references to the container.

After all the sweat and toil writing this (and considering the unpleasant mess this makes of performance), I think I'd like to see some form of garbage collection in Python. This won't happen any time soon, but never mind.

Send comments to mwh@python.net