[Zope-dev] Re: Python Base Classes, ZObjectManager, and Subobjects

Ross Patterson rossp@ppc.ucsc.edu
Tue, 29 Jan 2002 11:28:47 -0800 (PST)


I figured it out and I'm posting this so that the next hapless victim
might stumble upon this in a google search where I did not.  Long,
with suggestions that have probably already been made for the
improvements and clarification of ZClasses.

The problem is that I was trying to subclass in python a class that
was meant to be subclassed in a ZClass, namely
ZClasses.ObjectManager.ZObjectManager.  ZObjectManager is a mix-in
class that sets the ObjectManager class as its _zclass_ attribute, a
ZObjectManagerPropertySheets() class instance as its propertysheets
attribute, and sets the appropriate manage_options attribute.
ZClasses.createZClassForBase, however overrides all of this so
calling it on a mix-in class yeilds two layers of meta-classes and
everything goes #$*&i%@!!

ZObjectManager gets registered using
App.ProductContext.registerZClass(), which is supposedly deprecated.
So I made a nearly exact duplicate of the ZObjectManager mix-in (which
violates DRY) that sets _zclass_ to a class that subclasses
ZCatalog.CatalogAwareness.CatalogAware and
ZClasses.ObjectManager.ObjectManager, and handles the propertysheets
and manage_options attributes.  I then register my mix-in with
registerZClass and the rest of my python base classes with
createZClassForBase.  It works, the subobjects management tab shows up
in any ZClass that directly or indirectly subclasses my python mix-in.

There are several glaring problems here, however.  One is the DRY
violation in duplicating the ZObjectManager mix-in.  The problem here
is that inheritance only applies to instances, so a class object that
subclasses a python ZClass doesn't inherit IN ITSELF the _zclass_,
propertysheets, manage_options, or any other attributes used by the
ZClass machinery at module import time, and thus it must be
duplicated.  Is there any way around this?

Second is the use of a deprecated function.  Of course, I'm not alone
on that one.  The ZClass module itself still depends on that function
for the registration of ZObjectManager.  So maybe it shouldn't be
deprecated, maybe it should be moved into ZClasses, maybe
createZClassForBase should be more intelligent?

Thirdly, its just all not very OO.  Of course, now that I've struggled
with all of this, I'm impressed as hell with ZClasses, I'm still
pissed at them, but I'm also impressed.  Its just difficult.  All the
same, however, there may well be room for more elegant ways of doing
all this.  For example, if I ever want my python base classes to
subclass another class like ZObjectManager that implements changes to
the ZClass management interface, I'm going to have to manually
reconcile the demands of the two subclasses on the ZClass machinery.
Ick.

Of course, having finished complaining, I'm going to do nothing about
all of this.  My excuse, figuring this out without so much as a
mailing list post to aid has put my project several days in arrears.
Of course, this is how I choose to learn is with a project to push me.
So there we have it.  My working *.py files are quoted below the
quoted history.

On Mon, 28 Jan 2002, Ross Patterson wrote:

> Nobody's really answering and I keep getting both closer and further
> looking at the ZClasses code.  As I understand it now,
> ZClasses.ZClass.createZClassForBase is supposed to address the ZClass
> "meta class" issues such as creating the _zclass_ in addition to the
> actual class and handling propertysheets.  So I would assume that my
> error lies somewhere in there, but I can't figure it out.
>
> A few more notes, when I create a ZClass that subclasses my python base
> class which in turn subclasses ZClasses.ObjectManager.ZObjectManager,
> it creates a ZClass with a default "View" for "Subobjects".  This
> seems to indicate that somewhere the subobjects propertysheet is being
> treated as an instance propertysheet rather than a propertysheet of
> the ZClass meta class.
>
> Also, when I create an instance from such a ZClass, trying to access
> the instance in the ZMI yields an authorization error.  Following is
> the traceback:
>
> Traceback (innermost last):
>   File /usr/Zope/lib/python/ZPublisher/Publish.py, line 150, in
> publish_module
>   File /usr/Zope/lib/python/ZPublisher/Publish.py, line 114, in
> publish
>   File /usr/Zope/lib/python/Zope/__init__.py, line 158, in
> zpublisher_exception_hook
>     (Object: blah1)
>   File /usr/Zope/lib/python/ZPublisher/Publish.py, line 98, in publish
>   File /usr/Zope/lib/python/ZPublisher/mapply.py, line 88, in mapply
>     (Object: manage_workspace)
>   File /usr/Zope/lib/python/ZPublisher/Publish.py, line 39, in
> call_object
>     (Object: manage_workspace)
>   File /usr/Zope/lib/python/App/Management.py, line 76, in
> manage_workspace
>     (Object: blah1)
> Unauthorized: (see above)
>
> I would assume this happens because the
> propertysheets/subobjects/manage method listed in the view tab is not
> valid for the instance, but trying to call that method on the ZClass
> directly returns:
>
> Traceback (innermost last):
>   File /usr/Zope/lib/python/ZPublisher/Publish.py, line 150, in
> publish_module
>   File /usr/Zope/lib/python/ZPublisher/Publish.py, line 114, in
> publish
>   File /usr/Zope/lib/python/Zope/__init__.py, line 158, in
> zpublisher_exception_hook
>   File /usr/Zope/lib/python/ZPublisher/Publish.py, line 89, in publish
>   File /usr/Zope/lib/python/ZPublisher/BaseRequest.py, line 278, in
> traverse
>   File /usr/Zope/lib/python/OFS/PropertySheets.py, line 601, in
> __bobo_traverse__
> AttributeError: subobjects
>
> I have also tried the two variations in the quoted message below.
> Then I tried the two variations below except substituting
> ZClasses.ObjectManager.ZObjectManager for
> ZClasses.ObjectManager.ObjectManager.  All with the same results
>
> Again, my goal is to be able create a ZClass that subclasses a python
> base class that in turn subclasses
> ZClasses.ObjectManager.ZObjectManager in such a way that the final
> ZClass and any other ZClasses that subclass it have a subobjects tab
> in the management view of the ZClass.  Thanks.
>
> On Sun, 27 Jan 2002, Ross Patterson wrote:
>
> > I'm transitioning a product from pure ZClasses to a hybrid product python base
> > classes and ZClasses.  So this is my first time working with python
> > products.
> >
> > I'm trying to setup a python base class to provide a central place to
> > subclass ZObjectManager and CatalogAware that I can rebase later
> > without the ZClass hassle.  But when I try to create a ZClass in the
> > product and subclass to one of my python base classes, I don't get the
> > subobjects tab in my ZClass.
> >
> > Clearly I'm missing somthing basic here, but I have scoured the docs
> > and mailing lists for a clue before coming here.  BTW, I did find this
> > post earlier that no one seemed to reply to, and below that are my
> > actual python files.
> >
> > ---quoted message---
> >
> > I want to create a Python base class that inherits from the
> > ObjectManager and has the "Subobjects" tab in the ZClass management
> > screen.  Inheriting from OFS.ObjectManager.ObjectManager does not do
> > this - I need to inherit from ZClasses.ObjectManager.ObjectManager.
> >
> > I still don't know how to have the Subobjects propertysheet thing
> > work.  I've tried the two ways I listed in my last email of doing
> > this, but I can't get it to work.
> >
> > One way was:
> >
> > class MyObjectManager(ZClasses.ObjectManager.ObjectManager):
> >     " .... "
> >
> > class ZMyObjectManager:
> >     """Mix-in for Object Management
> >     """
> >
> >     _zclass_=myObjectManager
> >
> >     propertysheets=ZClasses.ObjectManager.ZObjectManagerPropertySheets()
> >
> >     manage_options=(
> >         {'label': 'Subobjects', 'action'
> > :'propertysheets/subobjects/manage'},
> > )
> >
> > and then register ZMyObjectManager as a base class in __init__.py.
> > The other way was:
> >
> > class MyObjectManager(ZClasses.ObjectManager.ObjectManager):
> >     propertysheets=ZClasses.ObjectManager.ZObjectManagerPropertySheets()
> >
> >     manage_options=(
> >         {'label': 'Subobjects', 'action'
> > :'propertysheets/subobjects/manage'},
> > )
> >
> > and register that as the base class.  Neither of them work.
> >
> > --
> > Itamar S.T.  itamars@ibm.net
> >
> > ---end quoted message---

#!/usr/bin/python
# Products/ZCapitalProjects/__init__.py

from ZClasses import createZClassForBase

from PartZClass import PartZClass
from PartBase import PartBase
from SitePartBase import SitePartBase
from ProjectPartBase import ProjectPartBase

createZClassForBase( PartBase, globals(), 'ZCPPartBase' )
createZClassForBase( SitePartBase, globals(), 'ZCPSitePartBase' )
createZClassForBase( ProjectPartBase, globals(), 'ZCPProjectPartBase' )

def initialize( context ) :

	context.registerZClass( PartZClass )

#!/usr/bin/python
# Products/ZCapitalProjects/PartZClass.py

from ZClasses.ObjectManager import ObjectManager, ZObjectManagerPropertySheets
from Products.ZCatalog.CatalogAwareness import CatalogAware

class ZPartBase ( CatalogAware, ObjectManager ) :
	"""
	Base Classes for all ZCP Parts
	"""

	pass

class PartZClass :
    """Mix-in for Object Management
    """

    _zclass_=ZPartBase

    propertysheets=ZObjectManagerPropertySheets()

    manage_options=(
        {'label': 'Subobjects', 'action' :'propertysheets/subobjects/manage'},
        )

#!/usr/bin/python
# Products/ZCapitalProjects/PartBase.py

class PartBase :
	"""
	Base Class for ZCP Parts
	"""

	pass

#!/usr/bin/python
# Products/ZCapitalProjects/SitePartBases.py

class SitePartBase :
	"""
	Base Class for ZCP Site Parts
	"""

	pass

#!/usr/bin/python
# Products/ZCapitalProjects/ProjectPartBases.py

class ProjectPartBase :
	"""
	Base Class for ZCP Project Parts
	"""

	pass

-----------------------------------------------------------------
| Ross Patterson			rossp@cats.ucsc.edu	|
| Programmer/Analyst			(831)459-2792		|
| Physical Planning & Construction	Fax:(831)423-7436	|
| UC Santa Cruz				http:www2.ucsc.edu/ppc	|
-----------------------------------------------------------------