overload __getattr__ in brains-class?
Situation: I'm developing a product where some attributes come from one bunch of SQL-tables and some additional info is obtained by other means. The idea was to create a brain-class and overwrite __getattr__ to return the correct values from the different sources according to what key the application asked for. The objects are created (just like in DA.py) via objects=Results.Results(result,brain,parent,None) Now whatever I try, either some other __getattr__ seems to be called since I can get none of my "simulated" attributes to show up. Is it at all possible to do what I want to? Did I take a wrong turn somewhere way back? Any hints are appreciated. Jo. -- Internetmanufaktur Jo Meder ---------------------- Berlin, Germany http://www.meder.de/ ------------------- fon: ++49-30-417 17 63 33 Kollwitzstr. 75 ------------------------ fax: ++49-30-417 17 63 45 10435 Berlin --------------------------- mob: ++49-170- 2 98 89 97 Public GnuPG-Key ---------- http://www.meder.de/keys/jo-pubkey.txt
Jo Meder writes:
Situation: I'm developing a product where some attributes come from one bunch of SQL-tables and some additional info is obtained by other means.
The idea was to create a brain-class and overwrite __getattr__ to return the correct values from the different sources according to what key the application asked for.
The objects are created (just like in DA.py) via
objects=Results.Results(result,brain,parent,None)
Now whatever I try, either some other __getattr__ seems to be called since I can get none of my "simulated" attributes to show up. It should work with modern Zope versions.
I once tried and failed with an older version (Zope 2.1.6). My "__getattr__" led to an infinite loop. Dieter
Am 18.07.2002, 21:54 Uhr schrub Dieter Maurer <dieter@handshake.de>:
objects=Results.Results(result,brain,parent,None)
Now whatever I try, either some other __getattr__ seems to be called since I can get none of my "simulated" attributes to show up. It should work with modern Zope versions.
I once tried and failed with an older version (Zope 2.1.6). My "__getattr__" led to an infinite loop.
I'm using 2.5.1 with python 2.1.3. AFAIK this is fairly recent. So either I'm making a some tragically stupid mistake or something really strange is going on... Let me elaborate a bit (sorry for what follows, it's rather longish): The standard procedure for simulating class attributes in Python I normally use is (set discussions of efficiency aside for a moment): class a: def __init__(self): self.realattrib_a="this is real" def __getattr__(self,key): if key in self.__dict__.keys(): return self.__dict__[key] else: return "simulated attribute" where "realattrib_a" is the only genuine attribute of class a and everything else should have a value of "simulated attribute" This - of course - works perfectly when called with Python from the command line. Now I take this definition and use it as my brain-class. In my parent class (which is a CMF class derived from PortalContent and DefaultDublinCoreImpl and essentially represents an arbitrary list of results of a RDBMS-Query) I do the following: def calculate_results(self,selectionparams): <some logic to construct a query string from params> result=query_the_database(query_string) objects=Results.Results(result,a,self,None) return objects Next I have a DTML-Method that essentially contains: <dtml-let result="calculate_results(selectionparams)"> <dtml-if expr="result"> <dtml-with expr="result[0]"> <dtml-var expr="realattrib_a"> <dtml-var expr="doesntexist_b"> </dtml-with> </dtml-if> </dtml-let> And to my astonishment this already barfs on '<dtml-with expr="result[0]">' with a traceback telling me that the __init__ of class a is flawed since there is no attribute "realattrib_a". ??? How can an __init__-method complain about nonexisting attributes when all I'm trying to do is define some? Ok, I tell myself, if Zope wants me to live without sensible __init__-methods on brain-classes, I can cope with that. I completely remove the __init__-method and the next traceback I get is: File /usr/lib/zope/lib/python/DocumentTemplate/DT_With.py, line 76, in render (Object: result[0]) File /usr/lib/zope/lib/python/DocumentTemplate/DT_Util.py, line 169, in eval (Object: realattrib_a) (Info: realattrib_a) File <string>, line 2, in f NameError: global name 'realattrib_a' is not defined I really don't know what to try next. I know there has to be a really simple solution to this problem, since it is such a simple thing to come up with. But I have to admit, I'm completely lost. No ideas left to try, no hope, no sunshine... Perhaps I just didn't follow standard procedures for this situation, but then I'm guilty of not having tried the appropriate search term with zope.org or google. I'm stumped. Jo. -- Internetmanufaktur Jo Meder ---------------------- Berlin, Germany http://www.meder.de/ ------------------- fon: ++49-30-417 17 63 33 Kollwitzstr. 75 ------------------------ fax: ++49-30-417 17 63 45 10435 Berlin --------------------------- mob: ++49-170- 2 98 89 97 Public GnuPG-Key ---------- http://www.meder.de/keys/jo-pubkey.txt
Jo Meder writes:
... The standard procedure for simulating class attributes in Python I normally use is (set discussions of efficiency aside for a moment):
class a:
def __init__(self): self.realattrib_a="this is real"
def __getattr__(self,key): if key in self.__dict__.keys(): return self.__dict__[key] else: return "simulated attribute" You do not need the "then" part!
"__getattr__" is only called when the normal lookup fails. This includes the lookup in "self.__dict__", any subclass and (in Zope) the acquisition context.
... Now I take this definition and use it as my brain-class. ... And to my astonishment this already barfs on '<dtml-with expr="result[0]">' with a traceback telling me that the __init__ of class a is flawed since there is no attribute "realattrib_a".
??? How can an __init__-method complain about nonexisting attributes when all I'm trying to do is define some?
That's a restriction (bug) of the "Result" class. It is implemented in "C" as an ExtensionClass without "__dict__". Therefore, you cannot assign any (new) attributes to its instances: the set of attributes is fixed to the set of result columns. All derived classes inherit this property (they do not have a "__dict__"). I think, it is a bug. I reported it in the mailing list and in the (old) collector. It could probably easily be changed (whether or not an Extension Class has a __dict__ or not is controlled by a flag in the corresponding type). But it's implemented in "C" and in order to use the modification, one needs a "C" development systems. Many Zope users do not have one. That's why I more hesitant to provide patches for "C" parts than for Python parts. Dieter
Am 19.07.2002, 21:14 Uhr schrub Dieter Maurer <dieter@handshake.de>:
"__getattr__" is only called when the normal lookup fails. This includes the lookup in "self.__dict__", any subclass and (in Zope) the acquisition context.
Ah yes, you're right of course. Sometimes it pays to revisit the documentation :-O.
It is implemented in "C" as an ExtensionClass without "__dict__". Therefore, you cannot assign any (new) attributes to its instances: the set of attributes is fixed to the set of result columns. All derived classes inherit this property (they do not have a "__dict__").
I see, that explains the behaviour I observed very well.
I think, it is a bug. I reported it in the mailing list and in the (old) collector.
At least it is very inconvenient in my current situation. Do you have an url where I can look up your bug report and follow further development?
It could probably easily be changed (whether or not an Extension Class has a __dict__ or not is controlled by a flag in the corresponding type).
You mean I just have to change "EXTENSIONCLASS_NOINSTDICT_FLAG" to "EXTENSIONCLASS_INSTDICT_FLAG", recompile and I'm set? It can't be that simple, can it?
But it's implemented in "C" and in order to use the modification, one needs a "C" development systems. Many Zope users do not have one. That's why I more hesitant to provide patches for "C" parts than for Python parts.
I'm hesitating to deploy my own patches to C code as well. But with you confirming that this is a problem with the Record-class and not my own inability to produce correct code I'm more than happy to apply this patch. So now I have a semi-solution where I can insert my own attributes into __dict__ but my own __getattr__ and __setattr__ won't get called, no matter what I do. And I had no success in trying to install these methods after the __init__ finished. But thanks anyway for the insights. Jo. -- Internetmanufaktur Jo Meder ---------------------- Berlin, Germany http://www.meder.de/ ------------------- fon: ++49-30-417 17 63 33 Kollwitzstr. 75 ------------------------ fax: ++49-30-417 17 63 45 10435 Berlin --------------------------- mob: ++49-170- 2 98 89 97 Public GnuPG-Key ---------- http://www.meder.de/keys/jo-pubkey.txt
Jo Meder writes:
... "Result" instances do not have __dict__ ...
I think, it is a bug. I reported it in the mailing list and in the (old) collector.
At least it is very inconvenient in my current situation. Do you have an url where I can look up your bug report and follow further development? My bug report went into the old collector. And this collector (and its content) is lost.
But, I think, there is already a new bug report (from someone else). Search <http://collector.zope.org>.
It could probably easily be changed (whether or not an Extension Class has a __dict__ or not is controlled by a flag in the corresponding type).
You mean I just have to change "EXTENSIONCLASS_NOINSTDICT_FLAG" to "EXTENSIONCLASS_INSTDICT_FLAG", recompile and I'm set? It can't be that simple, can it? I am not sure, but I think that this is at least necessary. It might also be necessary to modify the "getattr" method (it probably has not this exact name) such that it looks also in the dict (better, calls the standard function to look up an attribute, (something like "find_attr").
Dieter
Am 21.07.2002, 22:19 Uhr schrub Dieter Maurer <dieter@handshake.de>:
But, I think, there is already a new bug report (from someone else). Search <http://collector.zope.org>.
That's the first thing I searched but obviously I haven't found the successfull search term yet.
change "EXTENSIONCLASS_NOINSTDICT_FLAG" to "EXTENSIONCLASS_INSTDICT_FLAG" [in Record.c]
Well, after changing this flag I had access to __dict__. But that's about all I gained. __setattr__ and __getattr__ just remained glued to the definitions in Record.c and no matter what I tried on the Python-level, I couldn't get them redefined.
I am not sure, but I think that this is at least necessary. It might also be necessary to modify the "getattr" method (it probably has not this exact name)
It is "Record_getattr" if I parse the code correctly. And it does call "Py_FindAttr". But this doesn't seem to do me any good since my getattr won't get called no matter what I try. I'm really reluctant to start messing with Record.c in earnest since I really can't claim to understand all side-effects this might produce. But perhaps this is something more suitable for zope-dev anyway. There might actually be a good reason for not adding attributes to Records programatically. Jo. -- Internetmanufaktur Jo Meder ---------------------- Berlin, Germany http://www.meder.de/ ------------------- fon: ++49-30-417 17 63 33 Kollwitzstr. 75 ------------------------ fax: ++49-30-417 17 63 45 10435 Berlin --------------------------- mob: ++49-170- 2 98 89 97 Public GnuPG-Key ---------- http://www.meder.de/keys/jo-pubkey.txt
If you look at the brains code in Results.py you see that on the fly a class called 'r' is created that inherits from (Record,Implicit,brains,zbrains) where brains is your brain class. see ./lib/python/Shared/DC/ZRDB/Resuts.py line 67 (Zope 2.5.1) you can overload __getattr__ in your brain class, but since this class is on the fly subclassed with the class 'r', that inherits first from 'Record' that is an extension class that has its own __getattr__ your __getattr__ never gets called. regards phil Dieter Maurer wrote:
Jo Meder writes:
Situation: I'm developing a product where some attributes come from one bunch of SQL-tables and some additional info is obtained by other means.
The idea was to create a brain-class and overwrite __getattr__ to return the correct values from the different sources according to what key the application asked for.
The objects are created (just like in DA.py) via
objects=Results.Results(result,brain,parent,None)
Now whatever I try, either some other __getattr__ seems to be called since I can get none of my "simulated" attributes to show up. It should work with modern Zope versions.
I once tried and failed with an older version (Zope 2.1.6). My "__getattr__" led to an infinite loop.
Dieter
_______________________________________________ Zope maillist - Zope@zope.org http://lists.zope.org/mailman/listinfo/zope ** No cross posts or HTML encoding! ** (Related lists - http://lists.zope.org/mailman/listinfo/zope-announce http://lists.zope.org/mailman/listinfo/zope-dev )
Am 22.07.2002, 04:43 Uhr schrub Philipp Auersperg <phil@bluedynamics.com>:
you can overload __getattr__ in your brain class, but since this class is on the fly subclassed with the class 'r', that inherits first from 'Record' that is an extension class that has its own __getattr__ your __getattr__ never gets called.
Yes, sure. But I wanted it to get called because my getattr was to add some attributes from another source. Jo. -- Internetmanufaktur Jo Meder ---------------------- Berlin, Germany http://www.meder.de/ ------------------- fon: ++49-30-417 17 63 33 Kollwitzstr. 75 ------------------------ fax: ++49-30-417 17 63 45 10435 Berlin --------------------------- mob: ++49-170- 2 98 89 97 Public GnuPG-Key ---------- http://www.meder.de/keys/jo-pubkey.txt
--On Monday, July 22, 2002 07:57:36 AM +0200 Jo Meder <jo@meder.de> wrote:
Yes, sure. But I wanted it to get called because my getattr was to add some attributes from another source.
If what you want to do is add a small fixed number of other attributes to the result of a ZSQL query (sorry, I missed your initial question), Jim Fulton gave me the simplest solution at the last IPC: just return the additional attributes as null values, e.g.: SELECT name, NULL as address, NULL as phone FROM ...; It's a kludge, but it beats the convolutions I was going through trying to hack around the Record class limits. Dan Pierson
Am 22.07.2002, 19:13 Uhr schrub Dan L. Pierson <dan@control.com>:
If what you want to do is add a small fixed number of other attributes to the result of a ZSQL query (sorry, I missed your initial question), Jim Fulton gave me the simplest solution at the last IPC: just return the additional attributes as null values
This might really work, but it I'm afraid it won't get me where I want to go. The idea behind all this is to create a common base class for my content-objects (in CMF) which are stored in mySQL-tables. I'm trying to separate the CMF/Zope-specific data (mostly DublinCore, owner, workflow and permissions) from the "real" content. That way I could keep the table definitions clean and wouldn't have to bloat each table with all the attributes Zope needs in addition to the content. All the metadata would be stored in one table for all derived classes (keyed by tablename and recordid of the content object). So my common base class would be there to handle all the zopish stuff and derived classes would be concentrating on the specifics of the data at hand. I knew I was inviting trouble when I started to think about storing Zope content in a RDBMS but I'd never have imagined the toughest problem would be the limitations of the Record object implementation. Jo. -- Internetmanufaktur Jo Meder ---------------------- Berlin, Germany http://www.meder.de/ ------------------- fon: ++49-30-417 17 63 33 Kollwitzstr. 75 ------------------------ fax: ++49-30-417 17 63 45 10435 Berlin --------------------------- mob: ++49-170- 2 98 89 97 Public GnuPG-Key ---------- http://www.meder.de/keys/jo-pubkey.txt
participants (4)
-
Dan L. Pierson -
Dieter Maurer -
Jo Meder -
Philipp Auersperg