staticmethod and namespaces

Diez B. Roggisch deets at nospam.web.de
Fri Feb 26 11:08:46 EST 2010


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)

Diez



More information about the Python-list mailing list