Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
getting PyObject from objc object?
-
Some UI objc components have a Py_Object method, which returns a pointer (if encoding is corrected). Is there a way to turn this back into an actual object? Or, am I misguessing what this method does?
Use case: I have created a View(), added it to the editor tab as a subview with a tag. In another area, I'd like to get the viewWithTag, but retrieve the python object, since then I can access custom attributes. There are other ways I can go about this, but this seemed convienent.
-
okay, cast is the answer....given an ObjCInstance of a view that started in python...
o.pyObject.encoding='^80:4' ctypes.cast(o.pyObject(),ctypes.py_object).value
-
-
If you look at the .encoding property of an ObJCInstanceMethod, it reports what the objc runtime says the type encoding is. Apple has some useful docs on the format of that string.
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.htmlFor type encodings which have embedded named type, these are tricky to parse, and sometimes pythonista gets it wrong. Luckily, it provides a way to override it. So, you can read the encoding, figure out what it is really saying, then translate into a version without named types. In this case the return type was a pointer ^ to a named type, but for pythonista it is sufficient just to return a void pointer.
In general the size string of the type encodings do not matter, and can even be omitted altogether.
-
Came across the need to do this again, but with the new calling convention it is no longer convienent to set encoding directly. So instead:
pyView=ctypes.cast(objcView.pyObject(argtypes=[], restype=ctypes.py_object), ctypes.py_object).value
I don't quite get why the extra cast is required, probably an issue with 32 bit structure returns. But anyway, this works, as long as the object is still alive. it does not seem to work after the object is gc'd which is unfortunate.
-
@JonB What does
objcView.pyObject(...)
without the cast give you in the console? For me, I get the_ui.View
object right away with no casts required, on both 32-bit (iPad mini 1) and 64-bit (iPhone SE), both Python 3. -
Just calling pyObject(), I get .... this
Basically parse_types barfs on the encoding which is
b'^{_object=i^{_typeobject}}8@0:4'
which is not really a valid encoding per the apple docs (since _typeobject is not defined in terms of intrinsic types ).But, Actually,
V.pyObject(restype=py_object,argtypes=[])
does work, which is maybe what you just said.
I got thrown off by the encoding saying it was a pointer, so I think in my earlier attempts I used POINTER(py_object), which crashes when I tried to derefernce the pointer.
Actually, I wonder if the @omz code is properly handling restype encodings that are pointers to structs (at least for 32 bit ), or else if ios is lying about the restype.It would be nice if objc_util just knew how to handle pyObject....
-
The encoding looks good to me, it matches what Include/object.h says:
typedef struct _object { _PyObject_HEAD_EXTRA Py_ssize_t ob_refcnt; struct _typeobject *ob_type; } PyObject;
The
_PyObject_HEAD_EXTRA
part is a macro that is only used in debug builds of Python. In a normal build it expands to nothing and doesn't add anything to the struct. The first actual field isob_refcnt
, which has typePy_ssize_t
, which is the signed version ofsize_t
, which has the same size as anint
on 32-bit devices, so it is encoded asi
. The next field after that isob_type
, which points to astruct _typeobject
.The encoding for
struct _typeobject
isn't included anymore, even thoughstruct _typeobject
is defined later in the header file. I think that is because at that point the pointer is two levels deep (a pointer to a struct containing a pointer), the Objective-C runtime guide says that pointers to pointers to structs don't include the full struct encoding anymore, maybe the same is the case for pointers in pointers to structs.Yes,
POINTER(py_object)
is not going to work. Python objects are always passed around in C code as aPyObject *
(a pointer to aPyObject
, or at least something similar), so thepy_object
type is actually a pointer already. You could of course define aStructure
forPyObject
and usePOINTER(PyObjectStruct)
to access an object's internal data, but that's not very useful in most cases. -
thanks for the explanation. I naively assumed that ctypes.py_object == PyObject instead of PyObject *.