Forum Archive

ctypes.pythonapi not usable?

ywangd

@omz Following code raises an AttributeError in Pythonista:

import ctypes
print ctypes.pythonapi.PyThreadState_SetAsyncExc

In Pythonista, the error is AttributeError: dlsym(RTLD_DEFAULT, PyThreadState_SetAsyncExc): symbol not found.

But it works with PC version Python and the correct output should be something <_FuncPtr object at 0x10223be20>

I looked into __init__.py of the ctypes module and found following code

if _os.name in ("nt", "ce"):
    pythonapi = PyDLL("python dll", None, _sys.dllhandle)
elif _sys.platform == "cygwin":
    pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2])
else:
    pythonapi = PyDLL(None)

Apparently, the else clause should take effect. Since the Pythonista environment is quite special, are there some settings missing in the DLL initialisation? I tried PyDLL('libpythonista.a') but got error image not found.

Any tips?

omz

Strange, this prints something like <_FuncPtr object at 0x106ab31c8> for me:

import ctypes
print ctypes.pythonapi.PyThreadState_SetAsyncExc
ywangd

@omz My device is an iPad Air 2 WiFi 64GB. I do also have an iPhone5 which I believe is a 32-bit device. But you are on 64-bit as well, right?

I am also on the latest Beta (160023).

Here is the full Trackback:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/private/var/mobile/Containers/Bundle/Application/650B1932-78AB-40BF-80BC-9BE928D8090B/Pythonista.app/pylib/ctypes/__init__.py", line 378, in __getattr__
    func = self.__getitem__(name)
  File "/private/var/mobile/Containers/Bundle/Application/650B1932-78AB-40BF-80BC-9BE928D8090B/Pythonista.app/pylib/ctypes/__init__.py", line 383, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
AttributeError: dlsym(RTLD_DEFAULT, PyThreadState_SetAsyncExc): symbol not found
omz

I guess it might have to do with me running a debug build... Could you try a different function in pythonapi, something like PyObject_Str? There's this note in the docs about PyThreadState_SetAsyncExc, so I think it might be "special" somehow:

To prevent naive misuse, you must write your own C extension to call this.

ywangd

No luck. Still the same error with PyObject_Str ...

omz

The most likely explanation seems to be that debug symbols are stripped from release builds (TestFlight betas are built with the "release" configuration), but not from debug builds that I deploy directly from Xcode on my device... (which is probably why it works for me). I'm installing the TestFlight build right now to verify this.

I'm not sure if I want to turn off symbol stripping just to make the Python C API accessible. I honestly don't see a lot of practical use cases for this, and it might have a negative impact on size and performance of the binary.

omz

Okay, I can verify that this doesn't work in the TestFlight build.

ywangd

I am no expert on iOS app building process. But I don't quite understand why you have to enable the entire debug builds just for exposing the Python C API.

The PC version Python is definitely not released as debug builds, yet it still allow access to the C API. I would guess there is something minor that could be changed in build settings to allow the C API becoming available. Maybe it is just a matter of including a DLL file. On OS X, I am able to load the C Library by calling print ctypes.PyDLL('libpython2.7.dylib').PyThreadState_SetAsyncExc.

ywangd

@omz
I wonder whether this could be a **permission ** issue which prevents the library file being loaded by PyDLL?

In 1.5, we can access the Pythonista.app folder alongside with the Documents folder. But it is no longer there in 1.6. Presumably this folder is where the library file (e.g. libpython.dylib) resides. Is this something worth to look at? Again I am no expert and could be very wrong. Please bear with me.

My intention is to use this API for better thread management in StaSh so that a worker thread can be interupted/stopped by user from the UI thread.