views:

365

answers:

4

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.

A: 

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".

Paul Betts
Agreed, except that Rohit may want to use the interface to differentiate between classes he wants to call and helper classes.
Greg
Yep, Greg spoke my mind.
Rohit
But he wants to use these Python object *from* a statically typed language where he *does* need to be able to differentiate on type.
fuzzyman
A: 

Hi there,

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 :)

Mitch Denny
The answer in the FAQ which you point me also says "you can define interfaces in C#, build those into a DLL, and then implement those interfaces in Python code as well as pass the python objects that implement the interfaces to C# code." How to do that is what I'm looking for.
Rohit
That is easy. Once you have your C# code compiled into a DLL then all you need to do is clr.AddReference("YourAssembly.dll") and then you can instantiate the types in it.
Mitch Denny
+1  A: 

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.

srivatsn
So, I should be doing a scope.GetItems() and call the GetClrType method on each of the values returned? Is that the correct way?
Rohit
+3  A: 

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__

fuzzyman