Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for conversions in Python method calls #10840

Merged
merged 1 commit into from
Jul 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions test/Engine/FFITarget/DummyCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,10 @@ public static IDictionary AcceptTypedDictionary(Dictionary<string, DummyCollecti
{
return dictionary;
}

public static int[] MakeArray(params int[] values)
{
return values;
}
}
}
160 changes: 160 additions & 0 deletions test/Libraries/DynamoPythonTests/PythonEvalTestsWithLibraries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class PythonEvalTestsWithLibraries : DynamoModelTestBase
protected override void GetLibrariesToPreload(List<string> libraries)
{
libraries.Add("FFITarget.dll");
libraries.Add("DSCoreNodes.dll");
}

public IEnumerable<PythonEvaluatorDelegate> Evaluators = new List<PythonEvaluatorDelegate> {
Expand Down Expand Up @@ -43,5 +44,164 @@ from FFITarget import DummyMath
Assert.AreEqual(expected, result);
}
}

[Test]
[Category("Failure")]
public void TestListEncoding()
{
string code = @"
import sys
import clr
clr.AddReference('DSCoreNodes')
from DSCore import List

l = ['a']
# Python list => .NET IList - Does not work in CPython
l = List.AddItemToEnd('b',l)
# .NET IList => Python list - Does not work in IronPython. Couldn't test CPython because of previous bug
# l.append('c')
l.Add(c)

OUT = l
";
var empty = new ArrayList();
var expected = new ArrayList { "a", "b", "c" };
foreach (var pythonEvaluator in Evaluators)
{
var result = pythonEvaluator(code, empty, empty);
Assert.IsTrue(result is IEnumerable);
CollectionAssert.AreEqual(expected, result as IEnumerable);
}
}

[Test]
public void TestArrayEncoding()
{
string code = @"
import sys
import clr
clr.AddReference('FFITarget')
clr.AddReference('DSCoreNodes')
from FFITarget import DummyCollection
from DSCore import List
from array import array

# Python array => .NET IList - array is in a builtin library. This does not work in either engine
# native = array('l', [1,2])
# native = List.AddItemToEnd(3, native)

# .NET array => Python list - Works in both engines
a = DummyCollection.MakeArray(1,2)
a[0] = a[1] + 1
b = len(a)
a[1] = b
a = List.AddItemToEnd(1, a)

OUT = a
";
var empty = new ArrayList();
var expected = new ArrayList { 3, 2, 1 };
foreach (var pythonEvaluator in Evaluators)
{
var result = pythonEvaluator(code, empty, empty);
Assert.IsTrue(result is IEnumerable);
CollectionAssert.AreEqual(expected, result as IEnumerable);
}
}

[Test]
[Category("Failure")]
public void TestTupleEncoding()
{
string code = @"
import sys
import clr
clr.AddReference('FFITarget')
clr.AddReference('DSCoreNodes')
from FFITarget import DummyCollection
from DSCore import List

t = (1,2,3)
# Python tuple => .NET array - Works in both
a = DummyCollection.MakeArray(t)
# Python tuple => .NET IList - Does not work in CPython
l = List.AddItemToEnd(4, t)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are Python tuples mutable?


OUT = a, l
";
var empty = new ArrayList();
var expected = new ArrayList { new ArrayList { 1, 2, 3 }, new ArrayList { 1, 2, 3, 4 } };
foreach (var pythonEvaluator in Evaluators)
{
var result = pythonEvaluator(code, empty, empty);
Assert.IsTrue(result is IEnumerable);
CollectionAssert.AreEqual(expected, result as IEnumerable);
}
}

[Test]
[Category("Failure")]
public void TestRangeEncoding()
{
string code = @"
import sys
import clr
clr.AddReference('FFITarget')
clr.AddReference('DSCoreNodes')
from FFITarget import DummyCollection
from DSCore import List

r = range(0, 10, 2)
# Python range => .NET array - Works in both
a = DummyCollection.MakeArray(r)
# Python range => .NET IList - Does not work in CPython
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are Python ranges mutable? Do they result in lists or arrays? Please bear with the ignorant questions - I'm not a Python expert. Could there by differences in IronPython vs. CPython in the way that tuples, ranges, etc. behave in terms of mutability?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not problem at all. These are very good questions @aparajit-pratap .

Are Python ranges mutable?

What happens when a Python primitive gets passed to a .NET function call, is that the primitive gets converted to a .NET object. This means that in practice they are not mutable because what we get is not even in the Python world.

Do they result in lists or arrays?

This will depend on the declared type in the function call. Support for arrays was already built into Python.NET. For lists, a few minor changes were needed, but most of the logic can be reused.

Could there by differences in IronPython vs. CPython in the way that tuples, ranges, etc. behave in terms of mutability?

I don't think I have tested that. That's a very good point. I'll take a look at IronPython and see if I'm able to mutate a list for instance.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just checked and mutability can be achieved in IronPython for a List. You need to use a Zero Touch node to test it though, because List methods always make a copy.

l = List.AddItemToEnd(10, r)

OUT = a, l
";
var empty = new ArrayList();
var expected = new ArrayList { new ArrayList { 0, 2, 4, 6, 8 }, new ArrayList { 0, 2, 4, 6, 8, 10 } };
foreach (var pythonEvaluator in Evaluators)
{
var result = pythonEvaluator(code, empty, empty);
Assert.IsTrue(result is IEnumerable);
CollectionAssert.AreEqual(expected, result as IEnumerable);
}
}

[Test]
[Category("Failure")]
public void TestDictionaryEncoding()
{
string code = @"
import sys
import clr
clr.AddReference('FFITarget')
clr.AddReference('DesignScriptBuiltin')
from DesignScript.Builtin import Dictionary
from FFITarget import DummyCollection

d = {'one': 1, 'two': 2, 'three': 3}
# Python dict => DS Dictionary - Does not work in either engine
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is intended. It would be good to have but if it were to be supported, we would most likely need to make custom changes in pythonnet for interop with DS that would not be accepted by the main repo for back-porting and we would need to continue to maintain our own custom fork.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We didn't do it for IronPython either.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback. I thought of a way to do this without referencing DS Dictionary in Python.NET. What we would do is a two step conversion:

  1. Convert it to a .NET Dictionary, using primitives from Python.NET
  2. Convert the .NET Dictionary to a DS Dictionary, as part of a custom encoder in Dynamo

# d2 = Dictionary.SetValueAtKeys(d, ['four'], [4])

# Python dict => .NET IDictionary - Does not work in CPython
d = DummyCollection.AcceptIDictionary(d)
# .NET IDictionary => Python dict - Works in IronPython. Could not test in CPython due to previous bug
d['five'] = 5

OUT = d
";
var empty = new ArrayList();
var expected = new Dictionary<string, int> {
{ "one", 1 }, { "two", 2 }, { "three", 3 }, { "five", 5 }
};
foreach (var pythonEvaluator in Evaluators)
{
var result = pythonEvaluator(code, empty, empty);
Assert.IsTrue(result is IDictionary);
CollectionAssert.AreEquivalent(expected, result as IDictionary);
}
}
}
}