[Chandler-dev] Getting rid of all __init__ methods (for Item subclasses)

Phillip J. Eby pje at telecommunity.com
Thu Mar 22 10:51:28 PST 2007


In discussion with Morgen, Bear, and John, we've determined that the 
optimum way to support fast dump and reload with proper support for 
multiple inheritance and type changes due to sharing, is to get rid of 
custom __init__ methods for Item subclasses.

Of the 30-some such methods, we found that about 1/4th were not actually 
doing anything and could be removed altogether.  Another 1/4th were there 
to set computed initial attribute values, like timestamps (e.g. createdOn).

The rest were either updating global data structures or setting up 
listeners of some kind, or providing special constructor features.

In order to replace this functionality, we are adding two new schema features.

First, you will be able to define a '__setup__(self)' method, that will be 
called when the item is created, OR when its class changes.  So for 
example, if you have a ContentItem, and it gets changed to a WebDAVAccount, 
and WebDAVAccount has a __setup__, it will be called when the item 
"becomes" a WebDAVAccount.  __setup__ methods must NOT call 
super().__setup__, because any superclass __setup__ methods have *already* 
been called whenever the subclass __setup__ is called.

So, the __setup__ method allows you to do the sort of global housekeeping 
and listener setup, etc. that were taking place in some __init__ methods 
before.

The second feature is a new 'schema.initialValues()' function that you can 
call in the body of a class to define how the class' attributes should be 
initialized.  For example:

     class ContentItem(Item):
         ...
         schema.initialValues(
             createdOn = lambda self: datetime.now()
         )

In other words, keyword arguments are used for the attribute names, and the 
values are functions taking the item as an argument and returning the 
desired attribute value.  The attributes are *not* limited to schema 
attributes; they can be transient attributes or even properties, and they 
override any values defined in a superclass or in the attribute's 
"initialValue" aspect.  This also eliminates the need for __init__ to 
supply default values for keyword arguments or to change default attribute 
values established by base classes.

The order that individual attributes will be set in is not guaranteeed, but 
they are *all* set before your __setup__ method is called.  Any keyword 
arguments passed to __init__ will prevent that attribute value from being 
calculated - it'll just be set from the keyword before __setup__ is called.

With these two features, and some help from Morgen, I was able to eliminate 
all but two __init__ methods in my checkout, and successfully run all but 
one of the unit tests -- which turned out to be a timezone-specific unit 
test.  :)  I haven't run the functional tests yet, as those have to be done 
on the XP machine which is in another room at the moment.  I'll be doing 
that soon, after I go back and add some more error checking and 
documentation to the new schema features.

The two __init__ methods I couldn't quite figure out were 
osaf.pim.mail.IMAPAccount (which has a special 'addInbox' keyword argument) 
and osaf.pim.mail.EMailAddress (which has a special 'clone' keyword argument.

As far as I can tell, the 'addInbox' argument is never used, so simply 
removing that custom __init__ method seems to work.  Likewise, I was unable 
to find any code using the 'clone' argument of the other method, so 
removing the __init__ seems to work as well.  (It sems to me that, should 
either of these features be needed again, adding classmethod constructors 
would work just as well.)

But, if you know some reason why these methods (or any other __init__ 
methods, for that matter) shouldn't be replaced with __setup__, 
initialValues(), or some other combination, please speak now.

Anyway, assuming that the functional tests are also clean, I should be 
checking this in tomorrow, and the schema API will stop allowing you to 
define custom __init__ methods in Item subclasses.  If you have any 
questions, concerns, or objections, now is the time to bring 'em.  :)

(Again, the purpose of this is to allow the EIM system to change existing 
items' types in a safe way, by setting up initial attribute values and 
calling subclass initialization code *without* reinitializing the base 
classes.  The only way to do this was to make it so that the schema API 
knows what attributes to initialize, how, and have a separate method that 
can be called on *only* the added class(es), without any super() calls 
reinitializing the base classes.  As a side effect, it also ensures that 
all Item constructors have a uniform signature, and avoids some of the 
hassles of writing a correct __init__ with super() in the right place, etc.)



More information about the chandler-dev mailing list