IronLanguages / ironpython2

Implementation of the Python programming language for .NET Framework; built on top of the Dynamic Language Runtime (DLR).
http://ironpython.net
Apache License 2.0
1.08k stars 229 forks source link

Misleading ArgumentTypeException message when calling C# method #15

Open slide opened 7 years ago

slide commented 7 years ago

From @walkindude on May 26, 2017 13:2

Hello, this is not a bug per se, but rather something I just noticed. I'm including a minimal example console app that will throw with the unexpected message Expected Char, got str, further discussion below the code.

By the way, this is IronPython v2.7.7 on .NET 4.

    class Program
    {
        static void Main(string[] args)
        {
            var engine = IronPython.Hosting.Python.CreateEngine();

            var foo = new Foo();
            var locals = new Dictionary<string, object> {{"foo", foo}};

            var scope = engine.CreateScope(locals);

            var result = engine.Execute("foo.Bar('code', 'a', 7.2)",scope);

            Console.ReadLine();
        }
    }

    public class Foo
    {
        public string Bar(string x, char y, float z = 0)
        {
            return string.Format("{0}, {1}, {2}", x, y, z);
        }
    }

Now, the signature of method Bar of class Foo is of course wrong, in that the last parameter should be double rather than float (or we would need to pass in a System.Single to begin with).

The exception message is a bit misleading, though: Expected Char, got str is not really the hint I would expect. I scratched my head for a minute before realizing I had made a dumb mistake. It seems like it should say something along the lines of Expected Single, got Double.

Just thought I would let you guys know.

Copied from original issue: IronLanguages/main#1660

slide commented 7 years ago

From @slozier on May 26, 2017 14:30

Are you sure your minimal example is correct? Seems to me like Expected Char, got str is the correct message in that Bar expects a char but you're passing it the string a.

slide commented 7 years ago

From @walkindude on May 26, 2017 14:33

I'm pretty confident it is correct, yes. If you change Bar to take a double instead of a float, the call will succeed, with 'a' being correctly cast to char.

Of course, passing 'aaa' instead of 'a' will throw as intended.

EDIT: furthermore, if you change the Python part to leave out the third parameter, again everything works flawlessly.

slide commented 7 years ago

From @slozier on May 26, 2017 14:57

Ah I see. I ran a few more tests and it seems to be an issue only when using char and float together in the definition. Seems like a bug. Here's the amended test code:

    class Program
    {
        static void Main(string[] args)
        {
            var engine = IronPython.Hosting.Python.CreateEngine();
            var foo = new Foo();
            var locals = new Dictionary<string, object> { { "foo", foo } };
            var scope = engine.CreateScope(locals);
            var result = engine.Execute(@"
print foo.Bar(x='a')
print foo.Bar(y=7.2)
print foo.Bar(x='a', y=7.2)
", scope);
            Console.ReadLine();
        }
    }

    public class Foo
    {
        public string Bar(char x='0', float y=0) => string.Format("{0}, {1}", x, y);
    }
slide commented 7 years ago

From @walkindude on May 26, 2017 15:0

Yes, to my untrained eye it really does look like some weird interaction taking place when a char parameters enters the picture.

The first parameter to Bar was there just because it was taken from the actual code I needed to call from IronPython, I forgot to leave it out. Sorry about that.

slide commented 7 years ago

From @slozier on May 30, 2017 20:15

After doing a bit of digging, it appears that the DLR uses different "narrowing levels" to figure out what type conversions are allowed. Looking at IronPython.Runtime.Converter.HasNarrowingConversion the string to char conversion is at level "IndexOperator" while the double to single conversion is at level "All". It looks like if the narrowing conversions are not all on the same level the DLR will fail to find the appropriate overload.