staticmethod and namespaces

darnzen darnzen at gmail.com
Sat Feb 27 08:40:02 CET 2010


On Feb 26, 10:20 am, "Diez B. Roggisch" <de... at nospam.web.de> wrote:
> Am 26.02.10 17:08, schrieb Diez B. Roggisch:
>
>
>
>
>
> > Am 26.02.10 16:57, schrieb darnzen:
> >> On Feb 26, 9:41 am, "Diez B. Roggisch"<de... at nospam.web.de> wrote:
> >>> Am 26.02.10 16:32, schrieb darnzen:
>
> >>>> On Feb 26, 3:15 am, "Diez B. Roggisch"<de... at nospam.web.de> wrote:
> >>>>> Am 26.02.10 06:07, schrieb darnzen:
>
> >>>>>> Having an odd problem that I solved, but wondering if its the best
> >>>>>> solution (seems like a bit of a hack).
>
> >>>>>> First off, I'm using an external DLL that requires static callbacks,
> >>>>>> but because of this, I'm losing instance info. It could be import
> >>>>>> related? It will make more sense after I diagram it:
>
> >>>>>> #Module main.py
> >>>>>> from A import *
>
> >>>>>> class App:
> >>>>>> def sperg(self):
> >>>>>> self.a = A()
>
> >>>>>> app = App()
> >>>>>> [main loop and such]
> >>>>>> -----------------------------
> >>>>>> # Module A.py
> >>>>>> import main
> >>>>>> class Foo:
> >>>>>> Selves=[]
> >>>>>> def __init__(self):
> >>>>>> Foo.Selves.append(self)
> >>>>>> @staticmethod
> >>>>>> def chum_callback(nType, nP):
> >>>>>> # Need to access function / data in app instance
> >>>>>> app.sperg(nP)
> >>>>>> # Need to access func data in Foo
> >>>>>> # I'm pulling 'self' ouf of list made in constructor
> >>>>>> self = Foo.getSelf(nP)
>
> >>>>>> def getSelf(nP):
> >>>>>> return self.Selves[nP]
>
> >>>>>> ---------------------------------------------------------------------
> >>>>>> So basically I added a list of instances to the base class so I can
> >>>>>> get at them from the staticmethod.
> >>>>>> What's bothering me the most is I can't use the global app
> >>>>>> instance in
> >>>>>> the A.py module.
>
> >>>>>> How can I get at the app instance (currently I'm storing that along
> >>>>>> with the class instance in the constructor)?
> >>>>>> Is there another way to do this that's not such a hack?
>
> >>>>>> Sorry for the double / partial post :(
>
> >>>>> Can you show how you pass the staticmethod to the C-function? Is
> >>>>> the DLL
> >>>>> utilized by ctypes?
>
> >>>>> I don't see any reason you couldn't use a bound method, which would
> >>>>> give
> >>>>> you your self, instead relying on global state.
>
> >>>>> Diez
>
> >>>> __main__.K<< *facepalm* should of tried that!
>
> >>>> Yeah I'm using ctypes. The DLL callback set ups are as follows. The
> >>>> local callback is in the App namespace (in this case, some callbacks
> >>>> are in different modules as noted in OP), but still no access to self:
>
> >>>> #Function wrapper
> >>>> A.expCallback = WINFUNCTYPE(None, c_int, c_int, \
> >>>> POINTER(Data_s))(A.Callback)
>
> >>>> #DLL call to register the local callback function
> >>>> DLLSetCallback(self.hID, A.SubID, EVENTID, A.expCallback)
>
> >>>> class A:
> >>>> #Local callback function
> >>>> @staticmethod
> >>>> def Callback(hID, SubID, Data):
> >>>> print 'I DON'T KNOW WHO I AM OR WHERE I CAME FROM!!'
> >>>> print 'BUT WITH hID, and SubID, I CAN FIGURE IT OUT'
> >>>> print 'IF I STORE A REFERENCE TO MYSELF IN A DICT'
> >>>> print 'USING KEY GENERATED FROM hID, SubID'
> >>>> pass
>
> >>>> I'm not sure why they need to be static callbacks, but the DLL doc's
> >>>> say "when using object based languages, such as c++, callback
> >>>> functions must be declared as static functions and not instance
> >>>> methods", and I couldn't get it to work without setting it up that
> >>>> way. I could probably have them all be "classless" functions, but with
> >>>> 100's of these, my namespace would be polluted up the wazoo, and I'd
> >>>> still have the problem that they wouldn't have access to instance
> >>>> methods / properties.
>
> >>> The above code can't work with self, because you use
>
> >>> A.expCallback
>
> >>> which at best can of course be a classmethod.
>
> >>> You need to instead invoke DLLSetCallback with a bound method, like this
>
> >>> a = A()
> >>> DLLSetCallback(self.hID, A.SubID, EVENTID, a.expCallback)
>
> >>> Also, the DLL-docs seem to refer to *C* or *C++*, where the concept of
> >>> static functions is differently. If ctypes manages to get *some*
> >>> callback passed, I'm 100% positive that it can pass *any* callable you
> >>> like, including bound methods.
>
> >>> Diez
>
> >> Thinking about it some more, I believe I understand why it has to be
> >> staticfunction. To use an bound method would require the wrapper to
> >> include a reference to the instance as follows:
>
> >> A.expCallback = WINFUNCTYPE(None, POINTER(A), c_int, c_int, \
> >> POINTER(Data_s))(a.Callback)
>
> >> Since a = A(); a.foo() is really A.foo(self). The problem here is that
> >> A is not a ctypes object and I can't change what arguments the DLL
> >> uses in the callback in any case. Rewording my thoughts: a bound
> >> method callback would require 'self' to be the first argument. I can
> >> not make the DLL include 'self' as it doesn't know anything about the
> >> objects in my program. Since I can't pass 'self', it has to be a
> >> staticmethod.
>
> > No, that's not true. A bound method implictly knows about it self, and
> > it's a callable.
>
> > What I guess is that you did the same mistake I did when I created that
> > example - namely, not keeping a refernce to the bound method around.
> > Ctypes will then garbage-collect the callback, which of course leads to
> > all kinds of troubles.
>
> > Try this:
>
> > a = A()
> > # keep this around
> > bound_m = a.expCallback
> > DLLSetCallback(self.hID, A.SubID, EVENTID, a.expCallback)
>
> AAAAHHRG, same error again.
>
> Of course, use
>
> DLLSetCallback(self.hID, A.SubID, EVENTID, bound_m)
>
> because the bound method changes with each time you create it.
>
> Sorry for the confusion.
>
> Diez

Well, I got around this mess by putting all those static callbacks
into a separate module in a new class. I set them up to call a bound
method of that class which passes the arguments to the appropriate
bound methods of other class instances. I just have to keep a little
dict of the bound method callbacks when I register them with the
class.

I also found a simple trick for sharing globals between modules.
Pretty simple, create an empty module, say shared.py, with only the
pass statement in it. Then dynamically add properties to it such as
shared.app = App(). Import shared into the modules that need access to
app and done!

Thanks for the help on this. Always learning!



More information about the Python-list mailing list