FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Error when exposing a python method to .Net in python 2. · Issue #492 · pythonnet/pythonnet · GitHub

Repository navigation

Error when exposing a python method to .Net in python 2. #492

Closed

Description

Environment

  • Pythonnet version: 2.3.0
  • Python version: 2.7.13
  • Operating System: Windows 10

Details

We are trying to expose a python method in .Net, in a class that inherits from System.Object.

We are following the example code in clr.py in the pythonnet repository except that we are inheriting from System.Object and that we are using a namespace. When we run

class X(System.Object):
    __namespace__ = "PyTest"
    @clrmethod(int, [str])
    def test(self, x):
        return len(x)

we get the error message

Traceback (most recent call last):
File "test.py", line 80, in
class X(System.Object):
TypeError: Error when calling the metaclass bases
Unable to find an entry point named 'PyIter_Check' in DLL 'python27'.

The error seems to be due to the fact that PyIter_Check is a #define statement and not a function in python. Therefore the import of the function fails in runtime.cs. The error does not occur in python 3 because PyIter_Check is not imported when python 3 is used.

Activity

  1. klasma commented on Jun 14, 2017

    Author

    The definition of PyIterCheck in runtime.cs is

    #if PYTHON2
            [DllImport(PythonDll, CallingConvention = CallingConvention.Cdecl)]
            internal static extern bool PyIter_Check(IntPtr pointer);
    #elif PYTHON3
            internal static bool PyIter_Check(IntPtr pointer)
            {
                var ob_type = (IntPtr)Marshal.PtrToStructure(pointer + ObjectOffset.ob_type, typeof(IntPtr));
                IntPtr tp_iternext = ob_type + TypeOffset.tp_iternext;
                return tp_iternext != null && tp_iternext != _PyObject_NextNotImplemented;
            }
    #endif
    

    We have tried to implement PyIter_Check for python 2, similarly to the way it is done for python 3, but we have not gotten it to work. Here is what we tried

    internal static bool PyIter_Check(IntPtr pointer)
    {
        IntPtr pyType = Marshal.ReadIntPtr(pointer, ObjectOffset.ob_type);
        long flags = Marshal.ReadInt64(pyType, TypeOffset.tp_flags);
        IntPtr iternext = Marshal.ReadIntPtr(pyType, TypeOffset.tp_iternext);
        return (flags & Py_TPFLAGS_HAVE_ITER) != 0 && (Int64)iternext != 0;
    }
    

    Maybe you can tell us what we are doing wrong.

  2. rickardraysearch commented on Jun 14, 2017

    Contributor

    I got a bit further, and I believe that I have understood what is happening. ClassDerivedObject.AddPythonMethod calls PyObject.IsIterable() to find out whether it makes sense to call GetEnumerator on that pyobject. PyObject.IsIterable does a PyIter_Check to find out whether the PyObject implements the iterator interface. However, e.g. list is not an iterator, and PyObject.GetEnumerator does not expect it to be - only that there is a valid iter method on it.

    So, the test in ClassDerivedObject.AddPythonMethod should probably not be IsIterable, but rather something like HasEnumerator, which would check for the presence of a iter method, not an iternext method as PyIter_Check does.

  3. den-run-ai commented on Jun 14, 2017

    Contributor

    Here is comparison of macros from CPython C-API:

    https://github.com/python/cpython/blob/2.7/Include/abstract.h#L637

    #define PyIter_Check(obj) \
        (PyType_HasFeature((obj)->ob_type, Py_TPFLAGS_HAVE_ITER) && \
         (obj)->ob_type->tp_iternext != NULL && \
         (obj)->ob_type->tp_iternext != &_PyObject_NextNotImplemented)
    

    https://github.com/python/cpython/blob/3.6/Include/abstract.h#L711

    #define PyIter_Check(obj) \
        ((obj)->ob_type->tp_iternext != NULL && \
         (obj)->ob_type->tp_iternext != &_PyObject_NextNotImplemented)
    
  4. rickardraysearch commented on Jun 14, 2017

    Contributor

    Yes, and the only difference there is that in python 3.6, the iter and iternext fields are always present.

  5. rickardraysearch commented on Jun 14, 2017

    Contributor

    Update: Calling IsIterable() in ClassDerivedObject.AddPythonMethod is probably correct - it's the implementation which returns wheter the object is an iterator, not an iterable...

  6. rickardraysearch commented on Jun 14, 2017

    Contributor

    Perhaps it's easier to discuss in code. I put up a prospective fix at rickardraysearch@5d052ca

  7. den-run-ai commented on Jun 14, 2017

    Contributor

    @rickardraysearch feel free to submit a pull request!

  8. rickardraysearch commented on Jun 15, 2017

    Contributor

    Hi @denfromufa , now there's a PR that passes all tests and might be possible to merge.

  9. den-run-ai commented on Jun 17, 2017

    Contributor

    Thanks for finding this issue and fixing it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL