.netironpythondynamic-language-runtime

Querying IronPython scripts for interfaces in a hosted environment


I'm developing an application written in C# which hosts IronPython. What I'm planning is that the users will write python scripts, which contain classes implementing a pre-defined interface.

Something like:

Define interface in C# code:

public interface IPlugin
{
    void DoSomething();
}

Implement the interface in python code.

class Plugin(IPlugin):

      def DoSomething():
          print "Doing stuff."

In my application, I load the python script and execute methods implemented by the classes in those scripts.

I want to know: how do I get a handle to all the classes which implement the specified interface in the python script?

For example: if I were to do the whole thing in C#, I have reflection to help me and I can jut load the assembly and go something like:

    Assembly assembly = Assembly.LoadFrom("plugin.dll");
    Type[] types = assembly.GetTypes();
    foreach(Type type in types)
    {
        foreach(Type interfaceType in type.GetInterface())
        {
            if(interfaceType == typeof(IPlugin))
            {
                // gotcha, invoke interface specific methods
            }
        }
     }

How do I do the same, when instead of an assembly, I'm dealing with an IronPython script? I know that using ObjectOperations and the ScriptScope, I can get a handle to the class, if I know its name -- as described here -- but I'm looking for something more robust.


Solution

  • The problem you have is that IronPython classes are not .NET classes. Python classes are much more dynamic than C# classes so IronPython classes are typically .NET objects.

    When you subclass a .NET interface in IronPython it does create a new .NET class, but multiple Python classes will actually share a backing .NET class (only one .NET class will be created for every .NET type subclassed in IronPython).

    The correct way to do this is to use Python introspection to collect the IronPython class objects and use a factory function (which you can use as a delegate - casting the returned instance to the interface) where you need to instantiate them.

    For example, you could try executing in the Python scope:

    list_of_classes = IPlugin.\_\_subclasses_\_