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.)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
Open Source Applications Foundation "chandler-dev" mailing list
http://lists.osafoundation.org/mailman/listinfo/chandler-dev