omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    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?

    Pythonista
    3
    9
    3987
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • JonB
      JonB last edited by

      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.

      1 Reply Last reply Reply Quote 0
      • JonB
        JonB last edited by

        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
        
        1 Reply Last reply Reply Quote 0
        • shaun-h
          shaun-h last edited by

          @JonB I have a question about how you determined the encoding for this, after I got help yesterday with my map view issues, @omz used it to help solve one of the issues, so I was trying to read up on it and I assume this is a objective c compiler encoding or am I not on the right track?

          1 Reply Last reply Reply Quote 0
          • JonB
            JonB last edited by

            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.html

            For 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.

            1 Reply Last reply Reply Quote 1
            • JonB
              JonB last edited by ccc

              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.

              1 Reply Last reply Reply Quote 0
              • dgelessus
                dgelessus last edited by

                @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.

                1 Reply Last reply Reply Quote 0
                • JonB
                  JonB last edited by JonB

                  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....

                  1 Reply Last reply Reply Quote 0
                  • dgelessus
                    dgelessus last edited by

                    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 is ob_refcnt, which has type Py_ssize_t, which is the signed version of size_t, which has the same size as an int on 32-bit devices, so it is encoded as i. The next field after that is ob_type, which points to a struct _typeobject.

                    The encoding for struct _typeobject isn't included anymore, even though struct _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 a PyObject * (a pointer to a PyObject, or at least something similar), so the py_object type is actually a pointer already. You could of course define a Structure for PyObject and use POINTER(PyObjectStruct) to access an object's internal data, but that's not very useful in most cases.

                    1 Reply Last reply Reply Quote 0
                    • JonB
                      JonB last edited by

                      thanks for the explanation. I naively assumed that ctypes.py_object == PyObject instead of PyObject *.

                      1 Reply Last reply Reply Quote 0
                      • First post
                        Last post
                      Powered by NodeBB Forums | Contributors