Question

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.

Was it helpful?

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_\_

OTHER TIPS

Dynamic Languages don't typically use interfaces, you're thinking in a very statically-typed way :)

Instead of worrying whether it matches some predefined interface, you should probably just call the member function and just deal with any exception thrown - you'll have to deal with errors anyways, just make "doesn't fit the method call we expect" to be one more. In Objective-C this would be called an "informal protocol".

You can always get the backing .NET type for a Python Type by calling clr.GetClrType

Python.GetClrModule(engine) returns the clr module and then call the GetClrType method on it passing in the Python class you obtained using objectoperations. That should give you back a System.Type. After that use the GetInterfaces method as you normally would.

According to the IronPython FAQ it doesn't support compilation of *.py files into an assembly that could then be linked to:

http://ironpython.codeplex.com/Wiki/View.aspx?title=FAQ&referringTitle=Home

But Paul is correct, if you are going to use a dynamic language for this kind of extensibility you kind of want to make it as easy as just editing a file rather than having to understand interfaces from a runtime.

If you are exposing this extensibility point to system-administrator types you might like to consider hosting PowerShell. I wrote a post a little while ago on how to do this:

http://notgartner.wordpress.com/2008/02/23/how-to-host-the-powershell-runtime/

PowerShell is good for this because it already has some adoption in environments where Exchange is used. Mind you - I am a PowerShell fan so take that with a grain of salt :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top