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?