Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implement a C# Interface in Python for .NET

I have been given a library written in C# that I'm trying to call using Python for .NET.

The primary class I need an instance of has a constructor like:

GDhuClient(IGDhuSettings)

There are no (exposed) classes that implement the IGDhuSettings interface. When I create a Python class to implement it, e.g.,

class PyGDhuSettings(IGDhuSettings):
    ...

I get TypeError: interface takes exactly one argument if I don't have a __new__ method or if I define one the normal way:

def __new__(cls):
    return super().__new__(cls)

If I try to instantiate the interface as if it were a class, I either get the same error (with no or >1 arguments) or <whatever> does not implement IGDhuSettings if I pass it a single argument.

Looking at the Python for .NET source,

using System;
using System.Reflection;
using System.Runtime.InteropServices;

namespace Python.Runtime
{
    /// <summary>
    /// Provides the implementation for reflected interface types. Managed
    /// interfaces are represented in Python by actual Python type objects.
    /// Each of those type objects is associated with an instance of this
    /// class, which provides the implementation for the Python type.
    /// </summary>
    internal class InterfaceObject : ClassBase
    {
        internal ConstructorInfo ctor;

        internal InterfaceObject(Type tp) : base(tp)
        {
            var coclass = (CoClassAttribute)Attribute.GetCustomAttribute(tp, cc_attr);
            if (coclass != null)
            {
                ctor = coclass.CoClass.GetConstructor(Type.EmptyTypes);
            }
        }

        private static Type cc_attr;

        static InterfaceObject()
        {
            cc_attr = typeof(CoClassAttribute);
        }

        /// <summary>
        /// Implements __new__ for reflected interface types.
        /// </summary>
        public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
        {
            var self = (InterfaceObject)GetManagedObject(tp);
            int nargs = Runtime.PyTuple_Size(args);
            Type type = self.type;
            object obj;

            if (nargs == 1)
            {
                IntPtr inst = Runtime.PyTuple_GetItem(args, 0);
                var co = GetManagedObject(inst) as CLRObject;

                if (co == null || !type.IsInstanceOfType(co.inst))
                {
                    Exceptions.SetError(Exceptions.TypeError, $"object does not implement {type.Name}");
                    return IntPtr.Zero;
                }

                obj = co.inst;
            }

            else if (nargs == 0 && self.ctor != null)
            {
                obj = self.ctor.Invoke(null);

                if (obj == null || !type.IsInstanceOfType(obj))
                {
                    Exceptions.SetError(Exceptions.TypeError, "CoClass default constructor failed");
                    return IntPtr.Zero;
                }
            }

            else
            {
                Exceptions.SetError(Exceptions.TypeError, "interface takes exactly one argument");
                return IntPtr.Zero;
            }

            return CLRObject.GetInstHandle(obj, self.pyHandle);
        }
    }
}

I don't see a means of implementing a C# interface in Python without either a CoClass (there isn't one defined) or already having a class that implements it.

Is there some nuance that I'm missing here, or is this a limitation of Python for .NET?

Discussion on GitHub: https://github.com/pythonnet/pythonnet/issues/674

like image 545
Ghillie Dhu Avatar asked Apr 09 '18 15:04

Ghillie Dhu


People also ask

What is AC implementation?

A standard conforming C implementation consists of a compiler that translates compilation units as mandated by the standard, an implementation of the standard library for all functions required by the standard and something (normally a linker) that puts everything together to build an executable file.

What does it mean to implement a class C++?

An implementation file is used in C++ programming when creating a class definition to split the interface from the implementation. The header file would declare all the member functions (methods) and data methods (fields) that the class has.

What is ac language?

The C programming language is a general-purpose, operating system-agnostic, and procedural language that supports structured programming and provides low-level access to the system memory.


1 Answers

I found that I needed to add the namespace field to the Python class implementing the interface. As far as I understand, it this field with represent the .NET namespace for your Python class that implements the interface.

As an example. I have made a C# library with the following interface:

public interface ITestInterface
{
    string StringAdd(string text, int number);
}

and a trivial static function that consumes the interface:

public static string InvokeStringAdd(ITestInterface testInterface, string text)
{
    return "*** " + testInterface.StringAdd(text, 7) + " ***";
}

Then on the Python side I define the following class implementing the interface:

class TestInterfaceSubClass(ITestInterface):
    __namespace__ = "MyNameSpace"

    def StringAdd(self,text,x):
        return 'My text <{}> with number <{}>'.format(text, x)

Note that I added the namespace field and give it the "MyNameSpace" value. I think any non-colliding .NET namespace name would do.

To test the implementation in Python

test_interface_subclass = TestInterfaceSubClass()
print(TestClass.InvokeStringAdd(test_interface_subclass,'My Text'))

which returns

*** My text <My Text> with number <7> ***

Finally note, that the python class TestInterfaceSubClass is given the namespace "MyNameSpace". This means that reevaluation the class definition code in e.g a Jupyter notebook will result in a namepsace clash on the .NET side, so you either have to give it a different namespace value or restart the kernel.

like image 67
kebo Avatar answered Oct 18 '22 16:10

kebo