diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index 22aaf33e4..0d1ddccac 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -375,8 +375,20 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, Runtime.Decref(pyoptype); if (!typematch) { - margs = null; - break; + if (SimplePromotableType(clrtype, pi[n].ParameterType)) + { + if (!Converter.ToManaged(op, pi[n].ParameterType, out arg, false)) + { + margs = null;// basically we are failing this match, but can't throw yet + break; + } + } + else + { + margs = null; + break; + } + } } else @@ -455,6 +467,19 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, } return null; } + /// <summary> + /// Returns true if src type (int -type) is promotable to a higher resolution type (float|double) + /// <note>no sanity check for overflow is done, this is done elsewhere when performing + /// the actual conversion. + /// </note> + /// </summary> + /// <param name="src"></param> + /// <param name="dst"></param> + /// <returns></returns> + private bool SimplePromotableType(Type src, Type dst) + { + return (((src == typeof(int) || src == typeof(short) || src == typeof(long) ) && (dst == typeof(double) || dst == typeof(float)))); + } internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw) { diff --git a/src/testing/constructortests.cs b/src/testing/constructortests.cs index cffcee888..b7d39d1f6 100644 --- a/src/testing/constructortests.cs +++ b/src/testing/constructortests.cs @@ -7,7 +7,28 @@ namespace Python.Test //======================================================================== // These classes support the CLR constructor unit tests. //======================================================================== - + public class AConstructorTest + { + public string name; + public AConstructorTest(string n) { name = n; } + } + public class LinkConstructorTest + { + public LinkConstructorTest() + { + DefaultCtCalled = true; + } + public LinkConstructorTest(AConstructorTest a,double matchMe,AConstructorTest b) + { + MatchMe = matchMe; + a1 = a; + a2 = b; + } + public bool DefaultCtCalled; + public double MatchMe; + public AConstructorTest a1; + public AConstructorTest a2; + } public class EnumConstructorTest { public TypeCode value; @@ -50,4 +71,36 @@ public SubclassConstructorTest(Exception v) this.value = v; } } + public class ToDoubleConstructorTest + { + public ToDoubleConstructorTest() + { + //Just default values + } + public ToDoubleConstructorTest(string a, double b,string c) + { + this.a = a; + this.b = b; + this.c = c; + } + public string a; + public double b; + public string c; + } + public class ToFloatConstructorTest + { + public ToFloatConstructorTest() + { + // just default values. + } + public ToFloatConstructorTest(string a, float b, string c) + { + this.a = a; + this.b = b; + this.c = c; + } + public string a; + public float b; + public string c; + } } \ No newline at end of file diff --git a/src/testing/methodtest.cs b/src/testing/methodtest.cs index 675b1577c..fcdbbd1cc 100644 --- a/src/testing/methodtest.cs +++ b/src/testing/methodtest.cs @@ -73,6 +73,24 @@ public Guid TestStructConversion(Guid v) { return v; } + /// <summary> + /// Method to support testing + /// simple promotion of numeric types: + /// + /// integer types -> double + /// + /// </summary> + /// <param name="a"></param> + /// <param name="b"></param> + /// <returns></returns> + public double TestSimpleIntToDoubleTypePromotion(double a, double b) + { + return a + b; + } + public float TestSimpleIntToFloatTypePromotion(float a, float b) + { + return a + b; + } public Exception TestSubclassConversion(Exception v) { diff --git a/src/tests/test_constructors.py b/src/tests/test_constructors.py index fdc1ef060..d7bb80cab 100644 --- a/src/tests/test_constructors.py +++ b/src/tests/test_constructors.py @@ -5,6 +5,8 @@ import Python.Test as Test import System +constructor_throw_on_arg_match_is_fixed = False # currently, failed match on super() is silently ignored, and tests will fail if set to true +constructor_to_sub_class_accepts_specific_parameters= False # currently, we can't pass arguments to super() class ct. class ConstructorTests(unittest.TestCase): """Test CLR class constructor support.""" @@ -44,9 +46,90 @@ class sub(System.Exception): instance = sub() ob = SubclassConstructorTest(instance) - print(ob) self.assertTrue(isinstance(ob.value, System.Exception)) + def testSubClassWithInternalArgsPassedToSuper(self): + """ + Verify we can sub-class a .NET class, in python, + and pass a working set of arguments to our super class. + """ + from Python.Test import ToDoubleConstructorTest # does the job for this test + + class PySubClass(ToDoubleConstructorTest): + def __init__(self,d): + super(PySubClass, self).__init__('a',2.0,'c') + self.d = d + + o = PySubClass('d') + self.assertEqual( o.d,'d') + if constructor_to_sub_class_accepts_specific_parameters: + self.assertEqual( o.a,'a') + self.assertAlmostEqual(o.b,2.0) + self.assertEqual( o.c,'c') + else: + print("\n\n*** Warning passing parameters to super class is currently not verified\n") + + + def testConstructorArgumentMatching(self): + """ Test that simple type promitions works for int->double """ + from Python.Test import AConstructorTest, LinkConstructorTest + a1=AConstructorTest('a1') + a2=AConstructorTest('a2') + self.assertEqual(a1.name,'a1') + self.assertEqual(a2.name, 'a2') + l1=LinkConstructorTest(a1,3000,a2) + #l2=LinkConstructorTest(a1,3000.0,a2) + self.assertEqual(l1.a1.name, a1.name) + self.assertEqual(l1.a2.name, a2.name) + self.assertAlmostEqual(3000.0,l1.MatchMe) + + def testIntToDoubleConstructorArguments(self): + from Python.Test import ToDoubleConstructorTest + + o = ToDoubleConstructorTest('a',2,'c') + self.assertEqual(o.a,'a') + self.assertAlmostEqual(o.b,2) + self.assertEqual(o.c,'c') + + o = ToDoubleConstructorTest() # just to verify the default constructor is there + + def testIntToFloatConstructorArguments(self): + from Python.Test import ToFloatConstructorTest + + o = ToFloatConstructorTest('a',2,'c') + self.assertEqual(o.a,'a') + self.assertAlmostEqual(o.b,2) + self.assertEqual(o.c,'c') + + o = ToFloatConstructorTest() + + def testConstructorRaiseExceptionIfNoMatch(self): + """ + Notice: Due to the feature of .NET object as super-class, there is a + 'hack' that after calling a constructor with the supplied arguments + (and they fail to match the .NET class constructor for any reason) + then it removes all the arguments, and retry the call. + Now, if this succeeds, it will return an object, with default values. + This *could* make sense, given that the .NET class *IS* subclassed, + however, if the process is *not* a part of a sub-class construction, + then this is at best very unexpected. + + + """ + + from Python.Test import ToDoubleConstructorTest + + + if constructor_throw_on_arg_match_is_fixed: + try: + o = ToDoubleConstructorTest('a','not a number','c') # this should raise exception, because there are no match! + except TypeError: + return + self.fail("exception should be raised for non-matching constructor atempt") + else: + print("\n\n*** Warning: failing arg match on constructors are currently silently accepted if there is a null constructor\n") + + def test_suite(): return unittest.makeSuite(ConstructorTests) diff --git a/src/tests/test_method.py b/src/tests/test_method.py index 4728d13e4..a00b024d1 100644 --- a/src/tests/test_method.py +++ b/src/tests/test_method.py @@ -208,6 +208,43 @@ def testMethodCallFlagsConversion(self): r = object.TestFlagsConversion(flags) self.assertTrue(r == flags) + def testSimpleTypePromotionIntToDouble(self): + object = MethodTest() + sum_of_a_plus_b = object.TestSimpleIntToDoubleTypePromotion(2,2) + self.assertAlmostEqual(sum_of_a_plus_b,2+2) + sum_of_a_plus_b = object.TestSimpleIntToDoubleTypePromotion(2.0,2) + self.assertAlmostEqual(sum_of_a_plus_b,2+2) + sum_of_a_plus_b = object.TestSimpleIntToDoubleTypePromotion(2,2.0) + self.assertAlmostEqual(sum_of_a_plus_b,2+2) + sum_of_a_plus_b = object.TestSimpleIntToDoubleTypePromotion(2.0,2.0) + self.assertAlmostEqual(sum_of_a_plus_b,2.0+2.0) + + #with self.assertRaises(TypeError), wont work on py 2.6, so .. + try: + should_fail = object.TestSimpleIntToDoubleTypePromotion(2,'x2.0') + except TypeError: + return + self.fail("A string (not convertible to number), should throw type error") + # TODO: consider this for consistence: bool True-> 1.0, False -> 0.0 + + def testSimpleTypePromotionIntToFloat(self): + object = MethodTest() + + sum_of_a_plus_b = object.TestSimpleIntToFloatTypePromotion(2,2) + self.assertAlmostEqual(sum_of_a_plus_b,2+2) + sum_of_a_plus_b = object.TestSimpleIntToFloatTypePromotion(2.0,2) + self.assertAlmostEqual(sum_of_a_plus_b,2+2) + sum_of_a_plus_b = object.TestSimpleIntToFloatTypePromotion(2,2.0) + self.assertAlmostEqual(sum_of_a_plus_b,2+2) + sum_of_a_plus_b = object.TestSimpleIntToFloatTypePromotion(2.0,2.0) + self.assertAlmostEqual(sum_of_a_plus_b,2.0+2.0) + try: + should_fail = object.TestSimpleIntToFloatTypePromotion(2,'x2.0') + except TypeError: + return + self.fail("A string (not convertible to number), should throw type error") + # TODO: consider this for consistence: bool True-> 1.0, False -> 0.0 + def testMethodCallStructConversion(self): """Test struct conversion in method call.""" from System import Guid