Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I'm trying to do the following:

  • Get an instance of an object defined in C#
    • It implements a certain interface
    • It is a field on another C# class instance
  • Wrap it with an IronPython proxy object that logs method calls
  • Put the wrapper back in place of the original object

Here's an example showing what I am trying to do. In reality, the IWoof interface has many more methods on it.

The C# classes and interface:

namespace Foo
{
    public interface IWoof { string DoSomething(); }

    public class Bar
    {
        public Bar() { Woof = new WoofImp(); }
        public IWoof Woof { get; set; }
    }

    public class WoofImp : IWoof
    {
        public string DoSomething()
        {
            return "DoSomething executing in C#";
        }
    }

}

I want to take the bar.Woof and replace it with an IronPython proxy. I want to do this from an IronPython Script. I have tried various things.

First attempt: Overriding getattr

I defined a Wrapper like this:

class Wrapper(object):

    def __init__(self, obj):
        self._obj = obj

    def __getattr__(self, name):
        print '__getattr__ called'
        if name == "DoSomething":
           return self.__DoSomething
        else:
            return object.__getattr__(self, name)

    def __DoSomething(self):
        result = self._obj.DoSomething()
        return result + " AND DoSomething executing in Python"

This works fine, as you would expect:

import Foo
bar = Foo.Bar()
woof = Wrapper(bar.Woof)
print woof.DoSomething()

> DoSomething executing in C# AND DoSomething executing in Python

However, when I try to replace bar.Woof with the wrapped object, it doesn't work:

bar.Woof = woof_wrapper

> TypeError: expected IWoof, got Object_1$1
> Microsoft.Scripting.ArgumentTypeException: expected IWoof, got Object_1$1
   at CallSite.Target(Closure , CallSite , Object , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
   at Microsoft.Scripting.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)

Second attempt: Implementing the C# interface

So obviously my wrapper needs to implement the IWoof interface, so I tried that.

class Wrapper_IWoof(object, Foo.IWoof): # Inherits from IWoof
... etc

Now I can add the wrapper instance and try to call it like this:

woof = Wrapper_IWoof(bar.Woof)
bar.Woof = woof
print bar.Woof.DoSomething()

But I get a new problem:

> AttributeError: 'Wrapper_IWoof' object has no attribute 'DoSomething'
> System.MissingMemberException: 'Wrapper_IWoof' object has no attribute 'DoSomething'
   at IronPython.Runtime.Operations.PythonOps.MissingInvokeMethodException(Object o, String name)
   at IronPython.NewTypes.System.Object#IWoof_4$4.DoSomething()
   at Microsoft.Scripting.Interpreter.FuncCallInstruction`2.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.LightLambda.Run3[T0,T1,T2,TRet](T0 arg0, T1 arg1, T2 arg2)
   at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
   at Microsoft.Scripting.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)

Third and fourth attempts: Dynamic Proxy

I also tried following this approach described here (and again implementing IWoof): http://www.ronnie-midnight-oil.net/2008/11/checking-type-contract-from-ironpython.html However, they resulted in the same problems found in the two attempts above.

Alternate solution

The other way to solve the same problem is to generate a python class via reflection and use that. I have done this and it works. This gives me the explicit function declaration as described by Jeff Hardy below.

But, I would really like to know why the attempts above aren't working. Am I doing something wrong or missing something?

share|improve this question

1 Answer 1

I think you made things too complicated with __getattr__. Try implementing the interface directly:

class Wrapper_IWoof(object, Foo.IWoof):

    def __init__(self, obj):
        self._obj = obj

    def DoSomething(self):
        result = self._obj.DoSomething()
        return result + " AND DoSomething executing in Python"
share|improve this answer
    
I should have made it clear that this is a toy example to illustrate the problem. The interface I am dealing with has many more methods on it, so I was looking for a way to do this without having to explicitly declare each function. Like I said, I can solve this with code generation, which actually does what you suggest for all the methods. I'll edit the question to clarify. –  Captain Whippet Mar 13 at 12:53

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.