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
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With