Skip to content

Constructor argument matching supports simple int to (float|double) types #239

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

Closed
wants to merge 16 commits into from
Closed
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
29 changes: 27 additions & 2 deletions src/runtime/methodbinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down
55 changes: 54 additions & 1 deletion src/testing/constructortests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
18 changes: 18 additions & 0 deletions src/testing/methodtest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
85 changes: 84 additions & 1 deletion src/tests/test_constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down Expand Up @@ -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)
Expand Down
37 changes: 37 additions & 0 deletions src/tests/test_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down