Skip to content

bool value conversion malfunctions with Iron python libraries #83

Closed
sriharshav opened this Issue Sep 29, 2012 · 6 comments

4 participants

@sriharshav

I am finding an issue with iron python libraries while passing a boolean value

Say I have python code "booltest.py" like below.

   if ip_bool is not True and ip_bool is not False :
         print "invalid ip_bool"

And when I call it using Iron Python libraries

 ScriptEngine engine = Python.CreateEngine();
 ScriptScope scope = engine.CreateScope();

 scope.SetVariable("ip_bool", false);
 scope = engine.ExecuteFile("booltest.py", scope);

this results in printing 'invalid ip_bool' though its value is set to false

Further I updated "booltest.py" to test type and print more info

 print "Before conversion"
 print "class :" + ip_bool.__class__.__name__
 print "value " + str(ip_bool)

 print "ip_bool is not True # " + str(ip_bool is not True)
 print "ip_bool is not False # " + str(ip_bool is not False)

 ip_bool1 = bool(ip_bool)
 print "\nAfter conversion"
 print "class :" + ip_bool1.__class__.__name__
 print "value " + str(ip_bool1)

 print "ip_bool is not True # " + str(ip_bool1 is not True)
 print "ip_bool is not False # " + str(ip_bool1 is not False)

and it yields

+++++++++++++++++++++++
Before conversion
class :bool
value False
ip_bool is not True # True
ip_bool is not False # True

After conversion
class :bool
value False
ip_bool is not True # True
ip_bool is not False # False
+++++++++++++++++++++++

Thus passing a bool value from scripting libraries malfunctions

@bhadra
bhadra commented Oct 3, 2012

Here is a an approach where there is apparently no conversion malfunction between C# and IronPython for C# bool variable type.

It will be interesting if you can share the differences between the two approaches.

Filename: test_ipy1.py

def test_func1(string_list, var_bool):
    if var_bool is True:
        return string_list[0][0] + ";" + string_list[0][1]
    elif var_bool is False:
        return string_list[1][0] + "!" + string_list[1][1]
    else:
        return None

if __name__ == '__main__':

    def main():

        string_list = [["string1", "string2"], ["string3", "string4"]]
        print test_func1(string_list, False)

    main()

The C# code accessing the above mentioned Python code is as follows:

using System;
using IronPython.Hosting;
using IronPython.Runtime;
using Microsoft.Scripting.Hosting;
using System.Collections.Generic;

namespace TestAppIPy1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> sublist1 = new List<string>();
            sublist1.Add("string1");
            sublist1.Add("string2");
            List<string> sublist2 = new List<string>();
            sublist2.Add("string3");
            sublist2.Add("string4");
            List<List<string>> list1 = new List<List<string>>();
            list1.Add(sublist1);
            list1.Add(sublist2);
            ScriptRuntime runtime = Python.CreateRuntime();
            ScriptEngine engine = runtime.GetEngine("IronPython");
            var paths = engine.GetSearchPaths();
            var ironpythonpath = Environment.GetEnvironmentVariable("IRONPYTHONPATH", EnvironmentVariableTarget.Process);
            var path_elements = ironpythonpath.Split(';');
            foreach (string path_element in path_elements)
            {
                paths.Add(path_element);
            }
            paths.Add(Environment.CurrentDirectory);
            engine.SetSearchPaths(paths);
            dynamic scope = runtime.UseFile("test_ipy1.py");
            bool var_bool = false;
            var str1 = scope.test_func1(list1, var_bool);
            Console.WriteLine(str1);
        }
    }
}

The environment variable ironpythonpath is defined as follows:

SET IRONPYTHONPATH=C:\Program Files\IronPython 2.7\Lib;C:\Program Files\IronPython 2.7\DLLs;C:\Progam Files\IronPython 2.7;C:\Program Files\IronPython 2.7\lib\site-packages
@sriharshav

Your approach is calling a method in script. Whereas I am executing the script in a scope.

When I execute script I set variables with their vales in scope and then just execute it.

The reason I could think of is if ip_bool is set to None in the context of script.

 ip_bool = None
 print(ip_bool  is not True)
 print(ip_bool  is not False)

Value of None in python is neither True nor False.

But that is not the case also. If it is None then the __class__.__name__ should have been NoneType

Contrary in this case ip_bool is recognized as bool

So it is malfunctioning.

@bhadra
bhadra commented Oct 17, 2012

is compares identity. None is not identical to True or False. So "ip_bool is not True" will evaluate to True and "ip_bool is not False" will evaluate to True. the standard Python function bool evaluates a Boolean expression.

[Reference: http://stackoverflow.com/questions/5119709/python-true-false]

So bool value conversion does not malfunction in IronPython.

I suggest that you close the issue.

@sriharshav

If ip_bool is none then I would expect ip_bool.__class__.__name__ not to return the type as class :bool

@slide
IronLanguages member
slide commented Dec 16, 2015

This has to do with the fact that True/False are actually objects in IronPython, so when you do an is comparison of a variable with the object True, they are not the same object (when the variable is set from C#). When you set the value from Python, then the True object is assigned to the Python object and thus the instance comparison is True. I'm not sure there is a good fix for this.

See https://github.com/IronLanguages/main/blob/ipy-2.7-maint/Runtime/Microsoft.Dynamic/Runtime/ScriptingRuntimeHelpers.cs#L46

@jdhardy
IronLanguages member
jdhardy commented Dec 16, 2015

In @bhadra's example, the call site binder likely converts C# System.Boolean into the appropriate Python True/False object, while in @sriharshav's original example ScriptEngine.SetVariable has no such conversion.

I see two solutions and a cop-out: adding code in SetVariable to convert booleans on the way in (but should we do that to Long as well? Decimal? DateTime? Nullable types?), or adding a special case to is handling for Boolean (and possible Nullable types/None, I don't know what it does).

The cop-out is to simply document it, and recommend against using is checks on True/False, which is already strongly discouraged by PEP8 (search for "is greeting") anyway.

I don't like special cases in the other code paths so I think we'll leave it as-is.

@jdhardy jdhardy closed this Dec 16, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.