[IronPython] IronPyton fails to query IDynamicMetaObjectProvider when looking for IEnumerable

Dino Viehland dinov at microsoft.com
Mon Nov 30 20:21:22 CET 2009


You're right that we should be letting the object bind at some point.  We should be using our CompatConversionBinder for this but it looks like we only use that for COM.

I've opened this bug: http://ironpython.codeplex.com/WorkItem/View.aspx?WorkItemId=25477


From: users-bounces at lists.ironpython.com [mailto:users-bounces at lists.ironpython.com] On Behalf Of Albert Szilvasy
Sent: Thursday, November 26, 2009 10:40 AM
To: Discussion of IronPython
Subject: Re: [IronPython] IronPyton fails to query IDynamicMetaObjectProvider when looking for IEnumerable

Ok.  I got the latest source from codeplex to investigate this a bit. I understand why it doesn't work and I suspect this is a "bug" but I would love to hear what others  say.
So, things go south in PythonConversionBinder.Bind method. It has code like this:

            IPythonConvertible convertible = target as IPythonConvertible;
            if (convertible != null) {
                res = convertible.BindConvert(this);
            } else if (res == null) {
                res = FallbackConvert(self);
            }

Of course, my DynamicMetaObject does not implement IPythonConvertible and this code falls back immediately instead of calling DynamicMetaObject.BindConvert.

I quickly fixed this on my machine by adding a ConvertBinder like this:

    class PythonConvertBinder : ConvertBinder
    {
        PythonConversionBinder m_real;
        public PythonConvertBinder(PythonConversionBinder real) : base(real.Type, true)
        {
            m_real = real;
        }
        public override DynamicMetaObject FallbackConvert(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
        {
            return m_real.FallbackConvert(target);
        }
    }

And modifying the above code like so:

            IPythonConvertible convertible = target as IPythonConvertible;
            if (convertible != null) {
                res = convertible.BindConvert(this);
            } else if (res == null) {
                res = target.BindConvert(new PythonConvertBinder(this));
            }

I doubt that this is the "right fix" but it demonstrates the nature of the problem.

Albert

From: users-bounces at lists.ironpython.com [mailto:users-bounces at lists.ironpython.com] On Behalf Of Albert Szilvasy
Sent: Wednesday, November 25, 2009 11:13 PM
To: 'users at lists.ironpython.com'
Subject: [IronPython] IronPyton fails to query IDynamicMetaObjectProvider when looking for IEnumerable

Hi there,

I'm experimenting with the DLR and IronPython and I've run into a puzzling issue.


1.       I have a type (ObjectId) that implements IDynamicMetaObjectProvider (written in C#).

2.       My derived DynamicMetaObjectProvider implements all the plumbing so that ObjectId can be converted to IEnumerable.



Given the above the following C# code works:

dynamic btr = ObjectId.GetBlockTableRecord();
foreach (dynamic o in btr)
    o.Prop = 1;

HOWEVER, the following IronPython code fails:
-----
import clr
clr.AddReference("Dyn.exe")
import Dyn
for o in Dyn.ObjectId.GetBlockTableRecord() :
    o.Prop = 1
-----

The error I'm getting is this:
TypeError: iteration over non-sequence of type ObjectId

Note that IDynamicMetaObjectProvider::GetMetaObject isn't even called by the IronPython runtime in this situation.

Is this a bug? How can make IronPython query IDynamicMetaObjectProvider in this case?

For you reference here's all the C# code that implements Dyn.exe:

----------------------
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq.Expressions;
using System.Reflection;

namespace Dyn
{
    class EnumerableProxy : System.Collections.IEnumerable
    {
        ObjectId m_id;
        public EnumerableProxy(ObjectId id)
        {
            m_id = id;
        }
        public System.Collections.IEnumerator GetEnumerator()
        {
            DBObject o = m_id.Open(OpenMode.ForRead);
            System.Collections.IEnumerator e = ((System.Collections.IEnumerable)o).GetEnumerator();
            o.Close();
            return e;
        }
    }
    class DbObjectProxy : DynamicMetaObject
    {
        public DbObjectProxy(Expression expression, ObjectId value):base(expression,BindingRestrictions.Empty, value)
        {
        }
        public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder)
        {
            Console.WriteLine("BindUnaryOperation");
            return base.BindUnaryOperation(binder);
        }
        public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg)
        {
            Console.WriteLine("BindBinaryOperation");
            return base.BindBinaryOperation(binder, arg);
        }
        public override DynamicMetaObject BindConvert(ConvertBinder binder)
        {
            Console.WriteLine("BindConvert");
            var id = (ObjectId)base.Value;
            Type enumerable = id.Type.GetInterface("System.Collections.IEnumerable");
            if (enumerable==null)
                return base.BindConvert(binder);

            //new EnumarableProxy(id);
            var exp = Expression.New(typeof(EnumerableProxy).GetConstructor(new Type[] { typeof(ObjectId) }), new Expression[] { Expression.Convert(base.Expression, typeof(ObjectId)) });
            var restr = BindingRestrictions.GetTypeRestriction(base.Expression, typeof(ObjectId));
            return new DynamicMetaObject(exp, restr);
        }
        public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args)
        {
            Console.WriteLine("BindCreateInstance");
            return base.BindCreateInstance(binder, args);
        }
        public override DynamicMetaObject BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes)
        {
            Console.WriteLine("BindDeleteIndex");
            return base.BindDeleteIndex(binder, indexes);
        }
        public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder)
        {
            Console.WriteLine("BindDeleteMember");
            return base.BindDeleteMember(binder);
        }
        public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes)
        {
            Console.WriteLine("BindGetIndex");
            var id = (ObjectId)base.Value;
            return CreateWrapper(
                Expression.MakeIndex(Expression.Convert(obj, id.Type), id.Type.GetProperty("Item"), new Expression[] { indexes[0].Expression }), OpenMode.ForRead);

        }
        public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args)
        {
            Console.WriteLine("BindInvoke");
            return base.BindInvoke(binder, args);
        }
        public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
        {
            Console.WriteLine("BindInvokeMember");
            return base.BindInvokeMember(binder, args);
        }
        public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value)
        {
            Console.WriteLine("BindSetIndex");
            var id = (ObjectId)base.Value;
            return CreateWrapper(Expression.Assign(
                Expression.MakeIndex(Expression.Convert(obj, id.Type), id.Type.GetProperty("Item"), new Expression[] { indexes[0].Expression }),
                value.Expression),OpenMode.ForWrite);
        }
        static ParameterExpression obj = Expression.Variable(typeof(DBObject), "obj");
        DynamicMetaObject  CreateWrapper(Expression body, OpenMode openMode)
        {
            Console.WriteLine("CreateWrapper {0}", body);
            var self = Expression.Convert(this.Expression, typeof(ObjectId));
            /*
             DBObject obj = id.Open(<openMode>);
             bool commit = false;
             try
             {
                retValue = <body>
                commit = true;
             }
             finally
             {
                if (commit)
                    obj.Close();
                else
                    obj.Cancel();

             }
             (object)retValue;
             */
            var commit = Expression.Variable(typeof(bool), "commit");
            var retValue = Expression.Variable(body.Type, "retValue");
            var target = Expression.Block(
                new ParameterExpression[] {obj, commit, retValue},
                Expression.Assign(
                    obj,
                    Expression.Call(self,
                           typeof(ObjectId).GetMethod("Open",BindingFlags.Instance | BindingFlags.NonPublic), Expression.Constant(openMode))
                           ),
                Expression.Assign(commit,Expression.Constant(false)),
                Expression.TryFinally(
                    Expression.Block(
                               Expression.Assign(retValue,body),
                               Expression.Assign(commit,Expression.Constant(true))
                    ),
                    Expression.IfThenElse(commit,
                        Expression.Call(obj, typeof(DBObject).GetMethod("Close")),
                        Expression.Call(obj, typeof(DBObject).GetMethod("Cancel")))
                )
                ,
                Expression.Convert(retValue, typeof(object))
            );
            var id = (ObjectId)base.Value;
            var restrictions =
                BindingRestrictions.GetExpressionRestriction(
                Expression.Equal( Expression.MakeMemberAccess(self, typeof(ObjectId).GetProperty("Type")), Expression.Constant(id.Type))
                );
            return new DynamicMetaObject(target, restrictions);
        }
        public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
        {
            Console.WriteLine("BindGetMember");
            var id = (ObjectId)base.Value;
            return CreateWrapper(
                Expression.MakeMemberAccess(Expression.Convert(obj, id.Type),
                    id.Type.GetProperty(binder.Name)),
                OpenMode.ForRead);
        }
        public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
        {
            Console.WriteLine("BindSetMember");
            var id = (ObjectId)base.Value;
            return CreateWrapper(Expression.Assign(
                            Expression.MakeMemberAccess(Expression.Convert(obj, id.Type), id.Type.GetProperty(binder.Name)),
                            value.Expression),OpenMode.ForWrite);
        }
        public override IEnumerable<string> GetDynamicMemberNames()
        {
            Console.WriteLine("GetDynamicMemberNames");
            return base.GetDynamicMemberNames();
        }
    }
    enum OpenMode { ForWrite, ForRead }
    class DBObject
    {
        public void Close()
        {
            Console.WriteLine("DBObject.Close()");
        }
        public void Cancel()
        {
            Console.WriteLine("DBObject.Cancel()");
        }
    }
    class Line : DBObject
    {
        int m_prop2;
        public int Prop
        {
            get
            {
                Console.WriteLine("Line.Prop.get()");
                return m_prop2;
            }
            set
            {
                Console.WriteLine("Line.Prop.set()");
                m_prop2 = value;
            }
        }

    }

    class BlockTableRecord : DBObject, System.Collections.IEnumerable
    {
        public System.Collections.IEnumerator GetEnumerator()
        {
            for (int i = 0; i < 5; i++)
            {
                yield return new ObjectId(typeof(Line));
            }
        }
        int m_indexer;
        public int this[string val]
        {
            get { return m_indexer; }
            set { m_indexer = value; }
        }
    }
    public struct ObjectId : IDynamicMetaObjectProvider
    {
        int m_id;
        static Dictionary<int, DBObject> handleTable = new Dictionary<int, DBObject>();
        public ObjectId(Type type)
        {
            DBObject obj = (DBObject)Activator.CreateInstance(type);
            m_id = obj.GetHashCode();
            handleTable.Add(m_id, obj);
        }

        internal DBObject Open(OpenMode mode)
        {
            Console.WriteLine("ObjectId.Open({0})", mode);
            return handleTable[m_id];
        }
        public Type Type
        {
            get { return handleTable[m_id].GetType(); }
        }

        public DynamicMetaObject GetMetaObject(Expression parameter)
        {
            Console.WriteLine("ObjectId.GetMetaObject({0})",parameter);
            return new DbObjectProxy(parameter, this);
        }

        public static ObjectId GetBlockTableRecord()
        {
            return new ObjectId(typeof(BlockTableRecord));
        }

        public static ObjectId GetLine()
        {
            return new ObjectId(typeof(Line));
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            dynamic btr = ObjectId.GetBlockTableRecord();
            foreach (dynamic o in btr)
            {
                o.Prop = 1;
            }
        }
    }
}
---------------------------------




-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/ironpython-users/attachments/20091130/373ffaa0/attachment.html>


More information about the Ironpython-users mailing list