|
2 | 2 | using System.Collections.Generic; |
3 | 3 | using System.Diagnostics; |
4 | 4 | using System.Dynamic; |
5 | | -using System.Linq.Expressions; |
| 5 | +using System.Globalization; |
| 6 | +using System.Reflection; |
6 | 7 |
|
7 | 8 | using NUnit.Framework; |
8 | 9 |
|
@@ -926,6 +927,190 @@ def SetValue(self): |
926 | 927 | } |
927 | 928 | } |
928 | 929 |
|
| 930 | + private static TestCaseData[] DynamicPropertiesGetterTestCases() => new[] |
| 931 | + { |
| 932 | + new TestCaseData(true), |
| 933 | + new TestCaseData(10), |
| 934 | + new TestCaseData(10.1), |
| 935 | + new TestCaseData(10.2m), |
| 936 | + new TestCaseData("Some string"), |
| 937 | + new TestCaseData(new DateTime(2023, 6, 22)), |
| 938 | + new TestCaseData(new List<int> { 1, 2, 3, 4, 5 }), |
| 939 | + new TestCaseData(new Dictionary<string, int> { { "first", 1 }, { "second", 2 }, { "third", 3 } }), |
| 940 | + new TestCaseData(new Fixture()), |
| 941 | + }; |
| 942 | + |
| 943 | + [TestCaseSource(nameof(DynamicPropertiesGetterTestCases))] |
| 944 | + public void TestGetPublicDynamicObjectPropertyWorks(object property) |
| 945 | + { |
| 946 | + dynamic model = PyModule.FromString("module", @" |
| 947 | +from clr import AddReference |
| 948 | +AddReference(""Python.EmbeddingTest"") |
| 949 | +AddReference(""System"") |
| 950 | +
|
| 951 | +from Python.EmbeddingTest import * |
| 952 | +
|
| 953 | +class TestGetPublicDynamicObjectPropertyWorks: |
| 954 | + def GetValue(self, fixture): |
| 955 | + return fixture.SomeProperty |
| 956 | +").GetAttr("TestGetPublicDynamicObjectPropertyWorks").Invoke(); |
| 957 | + |
| 958 | + dynamic fixture = new DynamicFixture(); |
| 959 | + fixture.SomeProperty = property; |
| 960 | + |
| 961 | + using (Py.GIL()) |
| 962 | + { |
| 963 | + Assert.AreEqual(property, (model.GetValue(fixture) as PyObject).AsManagedObject(property.GetType())); |
| 964 | + } |
| 965 | + } |
| 966 | + |
| 967 | + [Test] |
| 968 | + public void TestGetNonExistingPublicDynamicObjectPropertyThrows() |
| 969 | + { |
| 970 | + dynamic model = PyModule.FromString("module", @" |
| 971 | +from clr import AddReference |
| 972 | +AddReference(""Python.EmbeddingTest"") |
| 973 | +AddReference(""System"") |
| 974 | +
|
| 975 | +from Python.EmbeddingTest import * |
| 976 | +
|
| 977 | +class TestGetNonExistingPublicDynamicObjectPropertyThrows: |
| 978 | + def GetValue(self, fixture): |
| 979 | + try: |
| 980 | + prop = fixture.AnotherProperty |
| 981 | + except AttributeError as e: |
| 982 | + return e |
| 983 | +
|
| 984 | + return None |
| 985 | +").GetAttr("TestGetNonExistingPublicDynamicObjectPropertyThrows").Invoke(); |
| 986 | + |
| 987 | + dynamic fixture = new DynamicFixture(); |
| 988 | + fixture.SomeProperty = "Some property"; |
| 989 | + |
| 990 | + using (Py.GIL()) |
| 991 | + { |
| 992 | + var result = model.GetValue(fixture) as PyObject; |
| 993 | + Assert.IsFalse(result.IsNone()); |
| 994 | + Assert.AreEqual(result.PyType, Exceptions.AttributeError); |
| 995 | + Assert.AreEqual("'Python.EmbeddingTest.TestPropertyAccess+DynamicFixture' object has no attribute 'AnotherProperty'", |
| 996 | + result.ToString()); |
| 997 | + } |
| 998 | + } |
| 999 | + |
| 1000 | + public class DynamicFixture : DynamicObject |
| 1001 | + { |
| 1002 | + private Dictionary<string, object> _properties = new Dictionary<string, object>(); |
| 1003 | + |
| 1004 | + public override bool TryGetMember(GetMemberBinder binder, out object result) |
| 1005 | + { |
| 1006 | + return _properties.TryGetValue(binder.Name, out result); |
| 1007 | + } |
| 1008 | + |
| 1009 | + public override bool TrySetMember(SetMemberBinder binder, object value) |
| 1010 | + { |
| 1011 | + _properties[binder.Name] = value; |
| 1012 | + return true; |
| 1013 | + } |
| 1014 | + |
| 1015 | + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) |
| 1016 | + { |
| 1017 | + try |
| 1018 | + { |
| 1019 | + result = _properties.GetType().InvokeMember(binder.Name, BindingFlags.InvokeMethod, null, _properties, args, |
| 1020 | + CultureInfo.InvariantCulture); |
| 1021 | + return true; |
| 1022 | + } |
| 1023 | + catch |
| 1024 | + { |
| 1025 | + result = null; |
| 1026 | + return false; |
| 1027 | + } |
| 1028 | + } |
| 1029 | + } |
| 1030 | + |
| 1031 | + public class TestPerson : IComparable, IComparable<TestPerson> |
| 1032 | + { |
| 1033 | + public int Id { get; private set; } |
| 1034 | + public string Name { get; private set; } |
| 1035 | + |
| 1036 | + public TestPerson(int id, string name) |
| 1037 | + { |
| 1038 | + Id = id; |
| 1039 | + Name = name; |
| 1040 | + } |
| 1041 | + |
| 1042 | + public int CompareTo(object obj) |
| 1043 | + { |
| 1044 | + return CompareTo(obj as TestPerson); |
| 1045 | + } |
| 1046 | + |
| 1047 | + public int CompareTo(TestPerson other) |
| 1048 | + { |
| 1049 | + if (ReferenceEquals(this, other)) return 0; |
| 1050 | + if (other == null) return 1; |
| 1051 | + if (Id < other.Id) return -1; |
| 1052 | + if (Id > other.Id) return 1; |
| 1053 | + return 0; |
| 1054 | + } |
| 1055 | + |
| 1056 | + public override bool Equals(object obj) |
| 1057 | + { |
| 1058 | + return Equals(obj as TestPerson); |
| 1059 | + } |
| 1060 | + |
| 1061 | + public bool Equals(TestPerson other) |
| 1062 | + { |
| 1063 | + return CompareTo(other) == 0; |
| 1064 | + } |
| 1065 | + } |
| 1066 | + |
| 1067 | + private static TestCaseData[] DynamicPropertiesSetterTestCases() => new[] |
| 1068 | + { |
| 1069 | + new TestCaseData("True", null), |
| 1070 | + new TestCaseData("10", null), |
| 1071 | + new TestCaseData("10.1", null), |
| 1072 | + new TestCaseData("'Some string'", null), |
| 1073 | + new TestCaseData("datetime(2023, 6, 22)", null), |
| 1074 | + new TestCaseData("[1, 2, 3, 4, 5]", null), |
| 1075 | + new TestCaseData("System.DateTime(2023, 6, 22)", typeof(DateTime)), |
| 1076 | + new TestCaseData("TestPropertyAccess.TestPerson(123, 'John doe')", typeof(TestPerson)), |
| 1077 | + new TestCaseData("System.Collections.Generic.List[str]()", typeof(List<string>)), |
| 1078 | + }; |
| 1079 | + |
| 1080 | + [TestCaseSource(nameof(DynamicPropertiesSetterTestCases))] |
| 1081 | + public void TestSetPublicDynamicObjectPropertyWorks(string valueCode, Type expectedType) |
| 1082 | + { |
| 1083 | + dynamic model = PyModule.FromString("module", $@" |
| 1084 | +from clr import AddReference |
| 1085 | +AddReference(""Python.EmbeddingTest"") |
| 1086 | +AddReference(""System"") |
| 1087 | +
|
| 1088 | +from datetime import datetime |
| 1089 | +import System |
| 1090 | +from Python.EmbeddingTest import * |
| 1091 | +
|
| 1092 | +value = {valueCode} |
| 1093 | +
|
| 1094 | +class TestGetPublicDynamicObjectPropertyWorks: |
| 1095 | + def SetValue(self, fixture): |
| 1096 | + fixture.SomeProperty = value |
| 1097 | +
|
| 1098 | + def GetPythonValue(self): |
| 1099 | + return value |
| 1100 | +").GetAttr("TestGetPublicDynamicObjectPropertyWorks").Invoke(); |
| 1101 | + |
| 1102 | + dynamic fixture = new DynamicFixture(); |
| 1103 | + |
| 1104 | + using (Py.GIL()) |
| 1105 | + { |
| 1106 | + model.SetValue(fixture); |
| 1107 | + var expectedAsPyObject = model.GetPythonValue() as PyObject; |
| 1108 | + var expected = expectedType != null ? expectedAsPyObject.AsManagedObject(expectedType) : expectedAsPyObject; |
| 1109 | + |
| 1110 | + Assert.AreEqual(expected, fixture.SomeProperty); |
| 1111 | + } |
| 1112 | + } |
| 1113 | + |
929 | 1114 | [Explicit] |
930 | 1115 | [TestCase(true, TestName = "CSharpGetPropertyPerformance")] |
931 | 1116 | [TestCase(false, TestName = "PythonGetPropertyPerformance")] |
|
0 commit comments