Search code examples
mefironpython

Using MEF to Import components Exported by IronPython or other DLR languages?


Is it possible to declare IronPython classes as "Export" and thus add them to a MEF catalog that a host C# application can Import?

I cannot really find any concrete examples of this, just speculation.

Here is how I have manually loaded a Python class that implements a .NET interface:

https://github.com/versionone/VersionOne.SDK.Experimental

I would like to be able to put attributes on the python classes similar to how you do it in C#. (Or something equivalent)

Has anyone tried this?

Thanks, Josh


Solution

  • For those interested, I found a project on GitHub that had done this, but was a little bit coupled into the project. With the author's approval I've created a new repo, IronPythonMef, and a NuGet package for it.

    There's additional discussion in this thread at GitHub.

    Here's an example of how it works:

    First, an interface declared in C#:

    namespace IronPythonMef.Tests.Example.Operations
    {
        public interface IOperation
        {
            object Execute(params object[] args);
            string Name { get; }
            string Usage { get; }
        }
    }
    

    An implementation that Exports that interface in C#:

    [Export(typeof(IOperation))]
    public class Power : IOperation
    {
        public object Execute(params object[] args)
        {
            if (args.Length < 2)
            {
                throw new ArgumentException(Usage, "args");
            }
    
            var x = Convert.ToDouble(args[0]);
            var y = Convert.ToDouble(args[1]);
    
            return Math.Pow(x, y);
        }
    
        public string Name
        {
            get { return "pow"; }
        }
    
        public string Usage
        {
            get { return "pow n, y -- calculates n to the y power"; }
        }
    }
    

    And, an implementation of IOperation in IronPython:

    @export(IOperation)
    class Fibonacci(IOperation):
        def Execute(self, n):
            n = int(n)
            if n == 0:
                return 0
            elif n == 1:
                return 1
            else:
                return self.Execute(n-1) + self.Execute(n-2)
    
        @property
        def Name(self):
            return "fib"
    
        @property
        def Usage(self):
            return "fib n -- calculates the nth Fibonacci number"
    

    And here's a test case of a class that imports these operations from both C# and IronPython and executes them:

    [TestFixture]
    public class MathWizardTests
    {
        [Test]
        public void runs_script_with_operations_from_both_csharp_and_python()
        {
            var mathWiz = new MathWizard();
    
            new CompositionHelper().ComposeWithTypesExportedFromPythonAndCSharp(
                mathWiz,
                "Operations.Python.py",
                typeof(IOperation));
    
            const string mathScript =
    @"fib 6
    fac 6
    abs -99
    pow 2 4
    ";
            var results = mathWiz.ExecuteScript(mathScript).ToList();
    
            Assert.AreEqual(8, results[0]);
            Assert.AreEqual(720, results[1]);
            Assert.AreEqual(99f, results[2]);
            Assert.AreEqual(16m, results[3]);
        }
    }