Links
All about Python metaclasses (see below)
Notes
Addressing API
I've been working on the API to be used for ref-collection based addressing. There are two places where changes to addressing impact the API:
- The construction of items
- The equivalent of today's repository find method
Katie and I were looking at the calendar code in the content model as a way of working out the details,
The relevant files are:
parcels/osaf/contentmodel/ContentModel.py
parcels/osaf/contentmodel/calendar/Calendar.py
parcels/osaf/contentmodel/tests/TestCalendar.py
Item Construction APIs
In both ContentModel.py and Calendar.py, there is a boilerplate pattern that appears:
the use of a
_setUUIDs method to grab the UUID's of Kind items which are addressed using repository paths (to be replaced with an API that uses ref-collection based addresses)
_setUUIDs is called by the parcel loader when the parcel is loaded. The UUID's that it defines are used to get access to the Kinds for the various content item defined kinds.
The constructors for content item Kinds are basically boiler plate which use a pair of method calls to get the appropriate repository path parent and the right Kind item (done by "dereferencing" the UUID).
This seems kind of odd because
_setUUIDs obtained the Kind items, but then only grabbed the UUID's and then released the references. Apparently this is due to the fact that references to the Kinds might become stale, since the methods that get the Kind items could be called at any time, and subsequent activity could cause them to become invalid. So there is a very large amount of boilerplate code dedicated to working around this problem. Every ContentItem class writer pays the penalty for this. This is something that we're going to need to fix. Fixing it would reduce but probably not eliminate the amount of boilerplate. I'd like to see removal of
_setUUIDs and all the
getXXXKind() methods. I think that Morgen's new extension manager will probably take over the function of the
getXXXKind() methods, either that or use of "new" find.
I think that it's probably possible to eliminate all of the boilerplate by judicious use of metaclasses (see Links section).
class ContentItem:
def __init__(self, name=None, parent=None, kind=None):
if not parent:
parent = ContentModel.getContentItemParent()
if not kind:
kind = ContentModel.getContentItemKind()
Item.Item.__init__(self, name, parent, kind)
becomes
class ContentItem:
def __init__(self, name):
parent, kind = em.metaDataFor(type(self))
Item.Item.__init__(self, name, parent, kind)
em.metaDataFor:
gets the right info for the type - uses type name as dict hash to get to address of kind - yielding kind. then retrieves parent info (but why do we need this?)
but we can't do this when the items are already present, because we need to address them forward (using address) not backward (via classname as in this case). This means we have to pass an address somewhere. Currently this happens in
_setUUIDs. This appears to be unavoidable without a persistent reverse mapping of class names to item addresses in the parcel manager or other layer
Things get a little more verbose if we cannot map backwards from python classes to kinds:
class ContentItem:
def __init__(self, name, address):
kind = rep.find(CONTENTMODEL_ADDR % address)
content_parent = kind.content_parent
Item.Item.__init__(self, name, content_parent, kind, "contentitem_axis_attribute_name")
Actually we can do better, because we don't actually need to have a hierarchical repository path. Instead, we just put all items at the top level, and
rely on ref-collection paths for real naming:
class ContentItem:
def __init__(self, name, address):
LayerItem.__init__(self, name, address)
for the moment assume the existence of some other shim layer betweeen the repository and ContentItems. This may turn out to be bogus and we fold this functionality into
ContentItem?.
class LayerItem:
def __init__(self, name, address):
# figure out the right kind
kind = rep.find(CONTENT_MODEL_ADDR % address, ContentItem.DEFAULT_PATH_ATTRIBUTE) # note ref-collection based path
# instantiate
Item.Item.__init__(self, name, rep.root, kind)
# hook up any ref-collection based addresses (see below)
Since we are using ref=collection based paths, we don't (necessarily) have to do any parent/child setup. In many cases this will be taken care of by the ref-collections that the
ContentItem? ends up getting put in.
We may have to specify any system wide default ref-collections such as Kind.items. This will be clear from the address space map.
So applying this to Calendar.py...
we then get rid of
_setUUIDs and
getXXXKind()
class Location(Item.Item):
def __init__(self, name=None, parent=None, kind=None):
if not parent:
parent = ContentModel.ContentModel.getContentItemParent()
if not kind:
kind = CalendarParcel.getLocationKind()
Item.Item.__init__(self, name, parent, kind)
becomes
class Location(Item.Item):
def __init__(self, name):
LayerItem.__init__(self, name, '//ContentItems/Location')
depending on how the addresses of ContentItems are assigned, we may be able to yank this out entirely via metaclass or some other Python magic. One question I have here is whether or note Location's are ContentItems.
Impact on parcel.xml
Along with all of this, I am assuming that when you define a Kind in parcel.xml, you will supply an attribute which tells how that kind is to be addressed.
--
TedLeung - 28 May 2004