[TransWarp] Infinite recursion computing a binded attribute

Phillip J. Eby pje at telecommunity.com
Tue May 20 12:05:50 EDT 2003


At 05:49 PM 5/20/03 +0400, Oleg Broytmann wrote:
>Hello! I've got a problem. Infinite recursion. I think I am doing something
>wrong, but what?
>
>    Let me briefly describe my hierarchy of classes:
>
>class Component(binding.Component):
>     appname = binding.bindTo("appname")
>     appver = binding.bindTo("appver")
>     log = binding.bindTo("log")

Maybe I'm confused, but based on your config file, shouldn't this be

     log = binding.bindToProperty("MedapE.log")

or:

     log = binding.bindTo("config:MedapE.log/")

or:

     log = binding.bindTo(PropertyName("MedapE.log"))

???



>    Components are loaded from config file:
>
>[Provide Utilities]
>ank.MedapE.interfaces.IUiFrontend = 
>config.provideInstance('ank.MedapE.UiFrontEnd.BaseHTTPD.BaseHTTPD')
>ank.MedapE.interfaces.IUiServer = 
>config.provideInstance('ank.MedapE.UiServer.HtmlServer.HtmlServer')
>
>[MedapE]
>log = naming.lookup(targetObj, 'logfile:./MedapE.log?level=DEBUG')

By the way, if you use the "config:MedapE.log/" approach, the above can be:

     log = naming.LinkRef('logfile:./MedapE.log?level=DEBUG')



>components = "ui", "httpd"
>component.ui = "ank.MedapE.UiServer.HtmlServer.HtmlServer"
>component.httpd = "ank.MedapE.UiFrontend.BaseHTTPD.BaseHTTPD"

I don't understand what this part above does.


>def run():
>     _root = config.makeRoot(iniFiles=(("peak", "peak.ini"), "MedapE.cfg"))
>     app = Application(parentComponent=_root)
>     app.start()
>
>    Boom!
>
>STARTDIR=./ank/MedapE PEAK_CONFIG=`pwd`/MedapE.cfg PYTHONPATH=. python 
>-t  ./ank/MedapE/app.py
>error: uncaptured python exception, closing channel 
><ank.MedapE.UiFrontend.BaseHTTPD.BaseHTTPDServer listening localhost:8084 
>at 0x869a534> (exceptions.RuntimeError:maximum recursion depth exceeded
>[/usr/local/lib/python2.2/asyncore.py|poll3|183]
>[/usr/local/lib/python2.2/asyncore.py|handle_read_event|390]
>[BaseHTTPD.py|handle_accept|73]
>[BaseHTTPD.py|__init__|42]
>[/usr/local/lib/python2.2/SocketServer.py|__init__|514]
>[/usr/local/lib/python2.2/BaseHTTPServer.py|handle|266]
>[BaseHTTPD.py|process_request|47]
>[BaseHTTPD.py|process_request|161]
>[HtmlServer.py|handle|118]
>
>    There is just self.log.debug() in the line 118, but it seems peak cannot
>compute self.log. Very strange, because in BaseHTTPD self.log was accessed
>and used, no problems.
>
>[C:\cygwin\home\pje\PEAK\src/peak/binding/_once.pyx|_once.OnceDescriptor.__get__|119] 
>
>[C:\cygwin\home\pje\PEAK\src/peak/binding/_once.pyx|_once.__get__|106]
>[/usr/local/lib/python2.2/site-packages/peak/binding/components.py|computeValue|438] 
>
>[/usr/local/lib/python2.2/site-packages/peak/binding/components.py|lookupComponent|319] 
>
>[/usr/local/lib/python2.2/site-packages/peak/binding/components.py|lookup|399] 
>
>[/usr/local/lib/python2.2/site-packages/peak/binding/components.py|lookup|258] 
>
>[/usr/local/lib/python2.2/site-packages/peak/binding/components.py|acquireComponent|189] 
>
>[/usr/local/lib/python2.2/site-packages/peak/config/config_components.py|nameNotFound|407] 
>

Right here, what we see is that the lookup for "log" passed off the top of 
the component hierarchy.  It was attempting to acquire a "log" attribute (I 
presume) and none of that component's parents had such an attribute.  It 
then tried to do a lookup in the default naming context, and that's what 
went into an infinite loop.

I just tried duplicating this effect with the following code:

 >>> from peak.api import *
 >>> r=config.makeRoot()
 >>> r.lookupComponent('log')

And received the same infinite loop.  So now I will go find out what causes 
the infinite loop.  Meanwhile, I think you can see what you need to change 
to make your app work: the infinite recursion is merely a different error 
than the error message you should be getting: something you're looking for 
hasn't been found.  I've now found and fixed the recursion in PEAK, so you 
will just get 'NameNotFound' errors instead.

(Note that 'provideInstance()' effectively sets the parent component of the 
provided instance, to be the object whose configuration the instance is 
loaded from.  In the case of your app, this is the root object.  So, even 
if your App object had a 'log' attribute, the instance wouldn't find it.

If you want to have things configured in your app object, call 
'config.loadConfigFile()' on your app object, rather than configuring those 
items in the root.  Or, of course if the configuration is relatively 
static, then wiring up the configuration in the app class is also doable, e.g.:

class App(binding.Component):

     __uiServer = binding.New(
         'ank.MedapE.UiServer.HtmlServer.HtmlServer',
         provides = ank.MedapE.interfaces.IUiServer
     )

This would make the class default to providing its '_App__uiServer' 
attribute as the implementer of that interface.  However, any configuration 
loaded from the config file directly into the App instance, will override 
that.  So if you define a provider in the config file, it will be used 
instead, and the '__uiServer' attribute will never be activated unless you 
refer to it in code.  However, if the config file *doesn't* define a 
provider, then the class-defined default will apply as shown.




More information about the PEAK mailing list