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

Albert Szilvasy albert.szilvasy at autodesk.com
Thu Nov 26 19:39:42 CET 2009


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/20091126/bacf6d76/attachment.html>


More information about the Ironpython-users mailing list