[Zope] Please test this Zope 2.2 beta 1 ZSQLMethods fix...
Brian Lloyd
Brian@digicool.com
Wed, 21 Jun 2000 11:25:39 -0400
This message is in MIME format. Since your mail reader does not understand
this format, some or all of this message may not be legible.
------ =_NextPart_000_01BFDB94.F4F26D80
Content-Type: text/plain
Hi all,
Several folks have reported problems in beta 1 with SQLMethods
having authentication problems when trying to call other SQL
Methods. I'd like a few intrepid volunteers to try something
for me to see if it fixes the problem. I'd like to make a 2.2
beta 2 release tomorrow but I want to make sure this issue is
resolved first.
Make a backup of the file:
lib/python/Shared/DC/ZRDB/DA.py
in your current (beta 1) installation and replace it with the
DA.py attached to this email, restart and let me know if the
problem goes away.
Thanks!
Brian Lloyd brian@digicool.com
Software Engineer 540.371.6909
Digital Creations http://www.digicool.com
------ =_NextPart_000_01BFDB94.F4F26D80
Content-Type: application/octet-stream;
name="DA.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="DA.py"
########################################################################=
######=0A=
# =0A=
# Zope Public License (ZPL) Version 1.0=0A=
# -------------------------------------=0A=
# =0A=
# Copyright (c) Digital Creations. All rights reserved.=0A=
# =0A=
# This license has been certified as Open Source(tm).=0A=
# =0A=
# Redistribution and use in source and binary forms, with or without=0A=
# modification, are permitted provided that the following conditions =
are=0A=
# met:=0A=
# =0A=
# 1. Redistributions in source code must retain the above copyright=0A=
# notice, this list of conditions, and the following disclaimer.=0A=
# =0A=
# 2. Redistributions in binary form must reproduce the above =
copyright=0A=
# notice, this list of conditions, and the following disclaimer =
in=0A=
# the documentation and/or other materials provided with the=0A=
# distribution.=0A=
# =0A=
# 3. Digital Creations requests that attribution be given to Zope=0A=
# in any manner possible. Zope includes a "Powered by Zope"=0A=
# button that is installed by default. While it is not a license=0A=
# violation to remove this button, it is requested that the=0A=
# attribution remain. A significant investment has been put=0A=
# into Zope, and this effort will continue if the Zope community=0A=
# continues to grow. This is one way to assure that growth.=0A=
# =0A=
# 4. All advertising materials and documentation mentioning=0A=
# features derived from or use of this software must display=0A=
# the following acknowledgement:=0A=
# =0A=
# "This product includes software developed by Digital =
Creations=0A=
# for use in the Z Object Publishing Environment=0A=
# (http://www.zope.org/)."=0A=
# =0A=
# In the event that the product being advertised includes an=0A=
# intact Zope distribution (with copyright and license included)=0A=
# then this clause is waived.=0A=
# =0A=
# 5. Names associated with Zope or Digital Creations must not be used =
to=0A=
# endorse or promote products derived from this software without=0A=
# prior written permission from Digital Creations.=0A=
# =0A=
# 6. Modified redistributions of any form whatsoever must retain=0A=
# the following acknowledgment:=0A=
# =0A=
# "This product includes software developed by Digital =
Creations=0A=
# for use in the Z Object Publishing Environment=0A=
# (http://www.zope.org/)."=0A=
# =0A=
# Intact (re-)distributions of any official Zope release do not=0A=
# require an external acknowledgement.=0A=
# =0A=
# 7. Modifications are encouraged but must be packaged separately as=0A=
# patches to official Zope releases. Distributions that do not=0A=
# clearly separate the patches from the original work must be =
clearly=0A=
# labeled as unofficial distributions. Modifications which do =
not=0A=
# carry the name Zope may be packaged in any form, as long as =
they=0A=
# conform to all of the clauses above.=0A=
# =0A=
# =0A=
# Disclaimer=0A=
# =0A=
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY=0A=
# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, =
THE=0A=
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A =
PARTICULAR=0A=
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR =
ITS=0A=
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,=0A=
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT=0A=
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF=0A=
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED =
AND=0A=
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT =
LIABILITY,=0A=
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY =
OUT=0A=
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY =
OF=0A=
# SUCH DAMAGE.=0A=
# =0A=
# =0A=
# This software consists of contributions made by Digital Creations =
and=0A=
# many individuals on behalf of Digital Creations. Specific=0A=
# attributions are listed in the accompanying credits file.=0A=
# =0A=
########################################################################=
######=0A=
__doc__=3D'''Generic Database adapter=0A=
=0A=
=0A=
$Id$'''=0A=
__version__=3D'$Revision$'[11:-2]=0A=
=0A=
import OFS.SimpleItem, Aqueduct, RDB=0A=
import DocumentTemplate, marshal, md5, base64, Acquisition, os=0A=
from Aqueduct import decodestring, parse=0A=
from Aqueduct import custom_default_report, default_input_form=0A=
from Globals import HTMLFile, MessageDialog=0A=
from cStringIO import StringIO=0A=
import sys, Globals, OFS.SimpleItem, AccessControl.Role=0A=
from string import atoi, find, join, split=0A=
import DocumentTemplate, sqlvar, sqltest, sqlgroup=0A=
from time import time=0A=
from zlib import compress, decompress=0A=
from DateTime.DateTime import DateTime=0A=
md5new=3Dmd5.new=0A=
import ExtensionClass=0A=
import DocumentTemplate.DT_Util=0A=
from cPickle import dumps, loads=0A=
from Results import Results=0A=
from App.Extensions import getBrain=0A=
from AccessControl import getSecurityManager=0A=
=0A=
try: from IOBTree import Bucket=0A=
except: Bucket=3Dlambda:{}=0A=
=0A=
=0A=
class nvSQL(DocumentTemplate.HTML):=0A=
# Non-validating SQL Template for use by SQLFiles.=0A=
commands=3D{}=0A=
for k, v in DocumentTemplate.HTML.commands.items(): =
commands[k]=3Dv=0A=
commands['sqlvar' ]=3Dsqlvar.SQLVar=0A=
commands['sqltest']=3Dsqltest.SQLTest=0A=
commands['sqlgroup' ]=3Dsqlgroup.SQLGroup=0A=
=0A=
_proxy_roles=3D()=0A=
=0A=
=0A=
class SQL(nvSQL):=0A=
# Validating SQL template for Zope SQL Methods.=0A=
=0A=
def validate(self, inst, parent, name, value, md):=0A=
return getSecurityManager().validate(inst, parent, name, =
value)=0A=
=0A=
class DA(=0A=
Aqueduct.BaseQuery,Acquisition.Implicit,=0A=
Globals.Persistent,=0A=
AccessControl.Role.RoleManager,=0A=
OFS.SimpleItem.Item,=0A=
):=0A=
'Database Adapter'=0A=
=0A=
_col=3DNone=0A=
max_rows_=3D1000=0A=
cache_time_=3D0=0A=
max_cache_=3D100=0A=
class_name_=3Dclass_file_=3D''=0A=
_zclass=3DNone=0A=
allow_simple_one_argument_traversal=3DNone=0A=
template_class=3DSQL=0A=
=0A=
manage_options=3D(=0A=
(=0A=
{'label':'Edit', 'action':'manage_main',=0A=
'help':('ZSQLMethods','Z-SQL-Method_Edit.stx')},=0A=
{'label':'Test', 'action':'manage_testForm',=0A=
'help':('ZSQLMethods','Z-SQL-Method_Test.stx')},=0A=
{'label':'Advanced', 'action':'manage_advancedForm',=0A=
'help':('ZSQLMethods','Z-SQL-Method_Advanced.stx')},=0A=
)=0A=
+OFS.SimpleItem.Item.manage_options=0A=
+AccessControl.Role.RoleManager.manage_options=0A=
)=0A=
=0A=
# Specify how individual operations add up to "permissions":=0A=
__ac_permissions__=3D(=0A=
('View management screens',=0A=
(=0A=
'manage_main', 'index_html',=0A=
'manage_advancedForm', 'PrincipiaSearchSource'=0A=
)),=0A=
('Change Database Methods',=0A=
('manage_edit','manage_advanced', =
'manage_testForm','manage_test',=0A=
'manage_product_zclass_info')),=0A=
('Use Database Methods', ('__call__',''), =
('Anonymous','Manager')),=0A=
)=0A=
=0A=
=0A=
def __init__(self, id, title, connection_id, arguments, =
template):=0A=
self.id=3Dstr(id)=0A=
self.manage_edit(title, connection_id, arguments, template)=0A=
=0A=
manage_advancedForm=3DHTMLFile('advanced', globals())=0A=
=0A=
test_url___roles__=3DNone=0A=
def test_url_(self):=0A=
'Method for testing server connection information'=0A=
return 'PING'=0A=
=0A=
_size_changes=3D{=0A=
'Bigger': (5,5),=0A=
'Smaller': (-5,-5),=0A=
'Narrower': (0,-5),=0A=
'Wider': (0,5),=0A=
'Taller': (5,0),=0A=
'Shorter': (-5,0),=0A=
}=0A=
=0A=
def _er(self,title,connection_id,arguments,template,=0A=
SUBMIT,sql_pref__cols,sql_pref__rows,REQUEST):=0A=
dr,dc =3D self._size_changes[SUBMIT]=0A=
=0A=
rows=3Dmax(1,atoi(sql_pref__rows)+dr)=0A=
cols=3Dmax(40,atoi(sql_pref__cols)+dc)=0A=
e=3D(DateTime('GMT') + 365).rfc822()=0A=
resp=3DREQUEST['RESPONSE']=0A=
=
resp.setCookie('sql_pref__rows',str(rows),path=3D'/',expires=3De)=0A=
=
resp.setCookie('sql_pref__cols',str(cols),path=3D'/',expires=3De)=0A=
return self.manage_main(=0A=
self,REQUEST,=0A=
title=3Dtitle,=0A=
arguments_src=3Darguments,=0A=
connection_id=3Dconnection_id,=0A=
src=3Dtemplate,=0A=
sql_pref__cols=3Dcols,sql_pref__rows=3Drows)=0A=
=0A=
def manage_edit(self,title,connection_id,arguments,template,=0A=
SUBMIT=3D'Change',sql_pref__cols=3D'50', =
sql_pref__rows=3D'20',=0A=
REQUEST=3DNone):=0A=
"""Change database method properties=0A=
=0A=
The 'connection_id' argument is the id of a database =
connection=0A=
that resides in the current folder or in a folder above the=0A=
current folder. The database should understand SQL.=0A=
=0A=
The 'arguments' argument is a string containing an arguments=0A=
specification, as would be given in the SQL method cration =
form.=0A=
=0A=
The 'template' argument is a string containing the source for =
the=0A=
SQL Template.=0A=
"""=0A=
=0A=
if self._size_changes.has_key(SUBMIT):=0A=
return self._er(title,connection_id,arguments,template,=0A=
=
SUBMIT,sql_pref__cols,sql_pref__rows,REQUEST)=0A=
=0A=
self.title=3Dstr(title)=0A=
self.connection_id=3Dstr(connection_id)=0A=
arguments=3Dstr(arguments)=0A=
self.arguments_src=3Darguments=0A=
self._arg=3Dparse(arguments)=0A=
template=3Dstr(template)=0A=
self.src=3Dtemplate=0A=
self.template=3Dt=3Dself.template_class(template)=0A=
t.cook()=0A=
self._v_cache=3D{}, Bucket()=0A=
if REQUEST:=0A=
if SUBMIT=3D=3D'Change and Test':=0A=
return self.manage_testForm(REQUEST)=0A=
message=3D'ZSQL Method content changed'=0A=
return self.manage_main(self, REQUEST, =
manage_tabs_message=3Dmessage)=0A=
return ''=0A=
=0A=
=0A=
def manage_advanced(self, max_rows, max_cache, cache_time,=0A=
class_name, class_file, direct=3DNone,=0A=
REQUEST=3DNone, zclass=3D''):=0A=
"""Change advanced properties=0A=
=0A=
The arguments are:=0A=
=0A=
max_rows -- The maximum number of rows to be returned from a =
query.=0A=
=0A=
max_cache -- The maximum number of results to cache=0A=
=0A=
cache_time -- The maximum amound of time to use a cached =
result.=0A=
=0A=
class_name -- The name of a class that provides additional=0A=
attributes for result record objects. This class will be a=0A=
base class of the result record class.=0A=
=0A=
class_file -- The name of the file containing the class=0A=
definition.=0A=
=0A=
The class file normally resides in the 'Extensions'=0A=
directory, however, the file name may have a prefix of=0A=
'product.', indicating that it should be found in a product=0A=
directory.=0A=
=0A=
For example, if the class file is: 'ACMEWidgets.foo', then =
an=0A=
attempt will first be made to use the file=0A=
'lib/python/Products/ACMEWidgets/Extensions/foo.py'. If this=0A=
failes, then the file 'Extensions/ACMEWidgets.foo.py' will =
be=0A=
used.=0A=
=0A=
"""=0A=
# paranoid type checking=0A=
if type(max_rows) is not type(1):=0A=
max_rows=3Datoi(max_rows)=0A=
if type(max_cache) is not type(1):=0A=
max_cache=3Datoi(max_cache)=0A=
if type(cache_time) is not type(1):=0A=
cache_time=3Datoi(cache_time) =0A=
class_name=3Dstr(class_name)=0A=
class_file=3Dstr(class_file)=0A=
=0A=
self.max_rows_ =3D max_rows=0A=
self.max_cache_, self.cache_time_ =3D max_cache, cache_time=0A=
self._v_cache=3D{}, Bucket()=0A=
self.class_name_, self.class_file_ =3D class_name, =
class_file=0A=
self._v_brain=3DgetBrain(self.class_file_, self.class_name_, =
1)=0A=
self.allow_simple_one_argument_traversal=3Ddirect=0A=
=0A=
if zclass:=0A=
for d in =
self.aq_acquire('_getProductRegistryData')('zclasses'):=0A=
if ("%s/%s" % (d.get('product'),d.get('id'))) =3D=3D =
zclass:=0A=
self._zclass=3Dd['meta_class']=0A=
break=0A=
=0A=
=0A=
if REQUEST is not None:=0A=
m=3D"ZSQL Method advanced settings have been set"=0A=
return =
self.manage_advancedForm(self,REQUEST,manage_tabs_message=3Dm)=0A=
## return self.manage_editedDialog(REQUEST)=0A=
=0A=
#def getFindContent(self):=0A=
# """Return content for use by the Find machinery."""=0A=
# return '%s\n%s' % (self.arguments_src, self.src)=0A=
=0A=
def PrincipiaSearchSource(self):=0A=
"""Return content for use by the Find machinery."""=0A=
return '%s\n%s' % (self.arguments_src, self.src)=0A=
=0A=
def manage_testForm(self, REQUEST):=0A=
" "=0A=
input_src=3Ddefault_input_form(self.title_or_id(),=0A=
self._arg, 'manage_test',=0A=
'<dtml-var manage_tabs>')=0A=
return DocumentTemplate.HTML(input_src)(self, REQUEST, =
HTTP_REFERER=3D'')=0A=
=0A=
def manage_test(self, REQUEST):=0A=
"""Test an SQL method."""=0A=
=0A=
src=3D"Could not render the query template!"=0A=
result=3D()=0A=
t=3Dv=3Dtb=3DNone=0A=
try:=0A=
try:=0A=
src, result=3Dself(REQUEST, test__=3D1)=0A=
if find(src,'\0'):=0A=
src=3Djoin(split(src,'\0'),'\n'+'-'*60+'\n')=0A=
if result._searchable_result_columns():=0A=
r=3Dcustom_default_report(self.id, result)=0A=
else:=0A=
r=3D'This statement returned no results.'=0A=
except:=0A=
t, v, tb =3D sys.exc_info()=0A=
r=3D'<strong>Error, <em>%s</em>:</strong> %s' % (t, =
v)=0A=
=0A=
report=3DDocumentTemplate.HTML(=0A=
'<html>\n'=0A=
'<BODY BGCOLOR=3D"#FFFFFF" LINK=3D"#000099" =
VLINK=3D"#555555">\n'=0A=
'<dtml-var manage_tabs>\n<hr>\n%s\n\n'=0A=
'<hr><strong>SQL =
used:</strong><br>\n<pre>\n%s\n</pre>\n<hr>\n'=0A=
'</body></html>'=0A=
% (r,src))=0A=
=0A=
report=3Dapply(report,(self,REQUEST),{self.id:result})=0A=
=0A=
if tb is not None:=0A=
self.raise_standardErrorMessage(=0A=
None, REQUEST, t, v, tb, None, report)=0A=
=0A=
return report=0A=
=0A=
finally: tb=3DNone=0A=
=0A=
def index_html(self, URL1):=0A=
" "=0A=
raise 'Redirect', ("%s/manage_testForm" % URL1)=0A=
=0A=
def _searchable_arguments(self): return self._arg=0A=
=0A=
def _searchable_result_columns(self): return self._col=0A=
=0A=
def _cached_result(self, DB__, query):=0A=
=0A=
# Try to fetch from cache=0A=
if hasattr(self,'_v_cache'): cache=3Dself._v_cache=0A=
else: cache=3Dself._v_cache=3D{}, Bucket()=0A=
cache, tcache =3D cache=0A=
max_cache=3Dself.max_cache_=0A=
now=3Dtime()=0A=
t=3Dnow-self.cache_time_=0A=
if len(cache) > max_cache / 2:=0A=
keys=3Dtcache.keys()=0A=
keys.reverse()=0A=
while keys and (len(keys) > max_cache or keys[-1] < t):=0A=
key=3Dkeys[-1]=0A=
q=3Dtcache[key]=0A=
del tcache[key]=0A=
del cache[q]=0A=
del keys[-1]=0A=
=0A=
if cache.has_key(query):=0A=
k, r =3D cache[query]=0A=
if k > t: return r=0A=
=0A=
result=3Dapply(DB__.query, query)=0A=
if self.cache_time_ > 0:=0A=
tcache[int(now)]=3Dquery=0A=
cache[query]=3D now, result=0A=
=0A=
return result=0A=
=0A=
def __call__(self, REQUEST=3DNone, __ick__=3DNone, src__=3D0, =
test__=3D0, **kw):=0A=
"""Call the database method=0A=
=0A=
The arguments to the method should be passed via keyword=0A=
arguments, or in a single mapping object. If no arguments =
are=0A=
given, and if the method was invoked through the Web, then =
the=0A=
method will try to acquire and use the Web REQUEST object as=0A=
the argument mapping.=0A=
=0A=
The returned value is a sequence of record objects.=0A=
"""=0A=
=0A=
if REQUEST is None:=0A=
if kw: REQUEST=3Dkw=0A=
else:=0A=
if hasattr(self, 'REQUEST'): REQUEST=3Dself.REQUEST=0A=
else: REQUEST=3D{}=0A=
=0A=
try: dbc=3Dgetattr(self, self.connection_id)=0A=
except AttributeError:=0A=
raise AttributeError, (=0A=
"The database connection <em>%s</em> cannot be found." =
% (=0A=
self.connection_id))=0A=
=0A=
try: DB__=3Ddbc()=0A=
except: raise 'Database Error', (=0A=
'%s is not connected to a database' % self.id)=0A=
=0A=
if hasattr(self, 'aq_parent'):=0A=
p=3Dself.aq_parent=0A=
if self._isBeingAccessedAsZClassDefinedInstanceMethod():=0A=
p=3Dp.aq_parent=0A=
else: p=3DNone=0A=
=0A=
argdata=3Dself._argdata(REQUEST)=0A=
argdata['sql_delimiter']=3D'\0'=0A=
argdata['sql_quote__']=3Ddbc.sql_quote__=0A=
=0A=
security=3DgetSecurityManager()=0A=
security.addContext(self)=0A=
try: query=3Dapply(self.template, (p,), argdata)=0A=
finally: security.removeContext(self)=0A=
=0A=
if src__: return query=0A=
=0A=
if self.cache_time_ > 0 and self.max_cache_ > 0:=0A=
result=3Dself._cached_result(DB__, (query, =
self.max_rows_))=0A=
else: result=3DDB__.query(query, self.max_rows_)=0A=
=0A=
if hasattr(self, '_v_brain'): brain=3Dself._v_brain=0A=
else:=0A=
brain=3Dself._v_brain=3DgetBrain(self.class_file_, =
self.class_name_)=0A=
=0A=
zc=3Dself._zclass=0A=
if zc is not None: zc=3Dzc._zclass_=0A=
=0A=
if type(result) is type(''):=0A=
f=3DStringIO()=0A=
f.write(result)=0A=
f.seek(0)=0A=
result=3DRDB.File(f,brain,p, zc)=0A=
else:=0A=
result=3DResults(result, brain, p, zc)=0A=
columns=3Dresult._searchable_result_columns()=0A=
if test__ and columns !=3D self._col: self._col=3Dcolumns=0A=
=0A=
# If run in test mode, return both the query and results so=0A=
# that the template doesn't have to be rendered twice!=0A=
if test__: return query, result=0A=
=0A=
return result=0A=
=0A=
def da_has_single_argument(self): return len(self._arg)=3D=3D1=0A=
=0A=
def __getitem__(self, key):=0A=
args=3Dself._arg=0A=
if self.allow_simple_one_argument_traversal and =
len(args)=3D=3D1:=0A=
results=3Dself({args.keys()[0]: key})=0A=
if results:=0A=
if len(results) > 1: raise KeyError, key=0A=
else: raise KeyError, key=0A=
r=3Dresults[0]=0A=
# if hasattr(self, 'aq_parent'): =
r=3Dr.__of__(self.aq_parent)=0A=
return r=0A=
=0A=
self._arg[key] # raise KeyError if not an arg=0A=
return Traverse(self,{},key)=0A=
=0A=
def connectionIsValid(self):=0A=
return (hasattr(self, self.connection_id) and=0A=
hasattr(getattr(self, self.connection_id), =
'connected'))=0A=
=0A=
def connected(self):=0A=
return getattr(getattr(self, self.connection_id), =
'connected')()=0A=
=0A=
=0A=
def manage_product_zclass_info(self):=0A=
r=3D[]=0A=
Z=3Dself._zclass=0A=
Z=3Dgetattr(Z, 'aq_self', Z)=0A=
for d in =
self.aq_acquire('_getProductRegistryData')('zclasses'):=0A=
z=3Dd['meta_class']=0A=
if hasattr(z._zclass_,'_p_deactivate'):=0A=
# Eek, persistent=0A=
continue=0A=
x=3D{}=0A=
x.update(d)=0A=
x['selected'] =3D (z is Z) and 'selected' or ''=0A=
del x['meta_class']=0A=
r.append(x)=0A=
=0A=
return r =0A=
=0A=
=0A=
=0A=
Globals.default__class_init__(DA)=0A=
=0A=
=0A=
=0A=
ListType=3Dtype([])=0A=
class Traverse(ExtensionClass.Base):=0A=
"""Helper class for 'traversing' searches during URL traversal=0A=
"""=0A=
_r=3DNone=0A=
_da=3DNone=0A=
=0A=
def __init__(self, da, args, name=3DNone):=0A=
self._da=3Dda=0A=
self._args=3Dargs=0A=
self._name=3Dname=0A=
=0A=
def __bobo_traverse__(self, REQUEST, key):=0A=
name=3Dself._name=0A=
da=3Dself.__dict__['_da']=0A=
args=3Dself._args=0A=
if name:=0A=
if args.has_key(name):=0A=
v=3Dargs[name]=0A=
if type(v) is not ListType: v=3D[v]=0A=
v.append(key)=0A=
key=3Dv=0A=
=0A=
args[name]=3Dkey=0A=
=0A=
if len(args) < len(da._arg): =0A=
return self.__class__(da, args)=0A=
key=3Dself # "consume" key=0A=
=0A=
elif da._arg.has_key(key): return self.__class__(da, args, =
key)=0A=
=0A=
results=3Dda(args)=0A=
if results:=0A=
if len(results) > 1:=0A=
try: return results[atoi(key)].__of__(da)=0A=
except: raise KeyError, key=0A=
else: raise KeyError, key=0A=
r=3Dresults[0]=0A=
# if hasattr(da, 'aq_parent'): r=3Dr.__of__(da.aq_parent)=0A=
self._r=3Dr=0A=
=0A=
if key is self: return r=0A=
=0A=
if hasattr(r,'__bobo_traverse__'):=0A=
try: return r.__bobo_traverse__(REQUEST, key)=0A=
except: pass=0A=
=0A=
try: return getattr(r,key)=0A=
except AttributeError, v:=0A=
if str(v) !=3D key: raise AttributeError, v=0A=
=0A=
return r[key]=0A=
=0A=
def __getattr__(self, name):=0A=
r=3Dself.__dict__['_r']=0A=
if hasattr(r, name): return getattr(r,name)=0A=
return getattr(self.__dict__['_da'], name)=0A=
=0A=
------ =_NextPart_000_01BFDB94.F4F26D80--