[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Fedora-suds-list] Re: suds.options.Options.update(...) infinite recursion



Alexander,

Thanks for the report.

I rewrote the Options class in 0.3.7. There were a number of problems with the 0.3.6 (and earlier) version. The intention of the update() method was to provide a seamless integration between Options contained with the Transport and the main Options instance within the Client. For cases where users create a Transport, set transport related options, then pass the Transport to the Client in the constructor. Then a user changes a Transport option using Client.set_options() and it has no effect because the Transport has it's own Options object. So, the update() method was designed to find the Options attribute in the Transport and set it to itself. I know, kind of hacky :/

The main thing that is broken here is that the Options object contains a Transport _and_ the Transport contains a Options.

So, in 0.3.7, I refactored the Options class. The main suds.options.Options class contains general Suds client options and the suds.transport.Options class contains Transport related options. To support setting Transport related options via Client.set_options() (and visa versa actually), the refactored Options classes provides a mechanism to associate many options instances together creating one virtual Options domain. When accessing an option, though any of the Options instances, the request is forwarded to the "owner" of that option. This way, a user can do something like this and it will work as expected:

>
> transport = HttpTransport(timeout=10)
> client = Client(url, transport=transport)
> #
> # got the wsdl, now I need to set a proxy for invoking methods
> #
> proxy = dict(http='http://abc.com'))
> client.set_options(proxy=proxy)
>

OR

>
> transport = HttpTransport(timeout=10)
> client = Client(url, transport=transport)
> #
> # got the wsdl, now I need to set a proxy for invoking methods
> #
> proxy = dict(http='http://abc.com'))
> transport.options.proxy=proxy
>

Also, most users run into the infinite recursion problem when trying to copy the client to be used for concurrent requests. 0.3.7 provides a Client.clone() method that, in conjuction with some tweaks in the Transport, provides a lightweight copy of the Cleint. It is lightweight because the clients share the WSDL/XSD objects but have their own transports and options.

I'm getting the release notes together now for 0.3.7 to be released soon.

Hope this helps.

Regards,

Jeff





On 10/11/2009 05:03 PM, Alexander Gattin wrote:
Hello,

Options class has a controversial update(x)
method, which finds all Options objects
from x's children and tries to perform
a merge (AFAIU) using Options.__setattr__(...).
The latter method in turn calls update(...),
which can cause recursion.

     def update(self, x):
         for attr, options in self.optattrs(x):
             if options == self:
                 continue
             for k in options.__defined__:
                 v = options.get(k)
                 setattr(self, k, v)
             setattr(x, attr, self)

     def __setattr__(self, name, value):
         builtin = name.startswith('__') and name.endswith('__')
         if not builtin:
             self.__constraint__.validate(name, value)
             value = self.default(name, value)
             self.__defined__.add(name)
             self.update(value)
         self.__dict__[name] = value

I'm sending a POC code and exception trace.
The code creates 2 Client objects referring
to the same Transport instance (t).

1st Client object (foowscl) gets created OK, but
the second Client (barwscl) triggers infinite
recursion by calling barwscloptions.set(**kwargs),
and then barwscl.options.__setattr__('transport', t)

The barwscl.options.__setattr__('transport', t)
calls barwscl.options.update(t), which finds
t.options object of class Options. The interesting
thing here is that the t.options refers to the
foowscl.options instance, foowscl.options !=
barwscl.options, and the foowscl.options already
contains "defined" ('transport' =>  t) item...

So the barwscl.options.update(t) calls
setattr(barwscl.options, 'transport', t) =>
barwscl.options.__setattr__('transport', t)
Loop.

P.S.

I don't understand why the merge is necessary
in the first place. Why not just let the
parent/child objects to have their own sets
of options?


Attachment: smime.p7s
Description: S/MIME Cryptographic Signature


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]