Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5130aaa
Overload operators except for bit shifts and default args.
christabella Dec 16, 2020
0d0ca87
Remove params array tests since they are unrelated to operator overlo…
christabella Dec 17, 2020
253f9c7
Fix type bug; rename variables pynargs etc.
christabella Dec 17, 2020
8cdb61c
Remove unused variables and add comments.
christabella Dec 17, 2020
c26e589
Add comments and remove unused in operatormethod.cs
christabella Dec 18, 2020
3222a54
Address review by @lostmsu; Add forward operator test.
christabella Dec 20, 2020
550ff31
Add forward operator tests (OpObj, int).
christabella Dec 21, 2020
eab8edc
Address @amos402's comment on raising branch.
christabella Dec 21, 2020
c2be3f1
Add operator overload and rename pynargs, clrnargs
christabella Dec 21, 2020
581a047
Revert variable renames
christabella Dec 22, 2020
e11327f
Update AUTHORS and CHANGELOG
christabella Dec 22, 2020
35be4bc
Revert rename to pynargs
christabella Dec 23, 2020
f19c281
Address @tminka's comments.
christabella Dec 23, 2020
d7f52d2
Remove whitespace
christabella Dec 23, 2020
5855a1b
Fix nits
christabella Dec 24, 2020
e7da0bc
Support reverse binary operations
christabella Dec 28, 2020
a376838
Use reverse instead of forward (semantics)
christabella Dec 29, 2020
6923a78
Address @tminka's comments
christabella Dec 30, 2020
4c992d8
Support unary neg and pos operators
christabella Dec 30, 2020
8cce61d
Remove isOperator from MatchesArgumentCount (simplify), add test.
christabella Jan 4, 2021
09a2047
Revert "Remove isOperator from MatchesArgumentCount (simplify)"
christabella Jan 4, 2021
41bd07f
Properly remove isOperator from MatchesArgumentCount (@tminka comment)
christabella Jan 4, 2021
10ccf1e
Update changelog.
christabella Jan 4, 2021
5682e0c
Add ones complement and modulo tests (@tminka comment)
christabella Jan 4, 2021
5f45c70
Merge branch 'master' into feat/operator-overloads
lostmsu Jan 5, 2021
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
Next Next commit
Overload operators except for bit shifts and default args.
  • Loading branch information
christabella committed Dec 23, 2020
commit 5130aaa1bbbfa111cf9d828060d11ee35dbc37d7
185 changes: 185 additions & 0 deletions src/embed_tests/TestPyMethod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
using NUnit.Framework;

using Python.Runtime;

using System.Linq;
using System.Reflection;

namespace Python.EmbeddingTest
{
public class TestPyMethod
Comment thread
christabella marked this conversation as resolved.
Outdated
{
[OneTimeSetUp]
public void SetUp()
{
PythonEngine.Initialize();
}

[OneTimeTearDown]
public void Dispose()
{
PythonEngine.Shutdown();
}

public class SampleClass
{
public int VoidCall() => 10;

public int Foo(int a, int b = 10) => a + b;

public int Foo2(int a = 10, params int[] args)
{
return a + args.Sum();
}
}

[Test]
public void TestVoidCall()
{
string name = string.Format("{0}.{1}",
typeof(SampleClass).DeclaringType.Name,
typeof(SampleClass).Name);
string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace;
PythonEngine.Exec($@"
from {module} import *
SampleClass = {name}
obj = SampleClass()
assert obj.VoidCall() == 10
");
}

[Test]
public void TestDefaultParameter()
{
string name = string.Format("{0}.{1}",
typeof(SampleClass).DeclaringType.Name,
typeof(SampleClass).Name);
string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace;

PythonEngine.Exec($@"
from {module} import *
SampleClass = {name}
obj = SampleClass()
assert obj.Foo(10) == 20
assert obj.Foo(10, 1) == 11

assert obj.Foo2() == 10
assert obj.Foo2(20) == 20
assert obj.Foo2(20, 30) == 50
assert obj.Foo2(20, 30, 50) == 100
");
}

public class OperableObject
{
public int Num { get; set; }

public OperableObject(int num)
{
Num = num;
}

public static OperableObject operator +(OperableObject a, OperableObject b)
{
return new OperableObject(a.Num + b.Num);
}

public static OperableObject operator -(OperableObject a, OperableObject b)
{
return new OperableObject(a.Num - b.Num);
}

public static OperableObject operator *(OperableObject a, OperableObject b)
{
return new OperableObject(a.Num * b.Num);
}

public static OperableObject operator /(OperableObject a, OperableObject b)
{
return new OperableObject(a.Num / b.Num);
}

public static OperableObject operator &(OperableObject a, OperableObject b)
{
return new OperableObject(a.Num & b.Num);
}

public static OperableObject operator |(OperableObject a, OperableObject b)
{
return new OperableObject(a.Num | b.Num);
}

public static OperableObject operator ^(OperableObject a, OperableObject b)
{
return new OperableObject(a.Num ^ b.Num);
}

public static OperableObject operator <<(OperableObject a, int offset)
{
return new OperableObject(a.Num << offset);
}

public static OperableObject operator >>(OperableObject a, int offset)
{
return new OperableObject(a.Num >> offset);
}
}

[Test]
public void OperatorOverloads()
{
string name = string.Format("{0}.{1}",
typeof(OperableObject).DeclaringType.Name,
typeof(OperableObject).Name);
string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace;

PythonEngine.Exec($@"
from {module} import *
cls = {name}
a = cls(2)
b = cls(10)
c = a + b
assert c.Num == a.Num + b.Num

c = a - b
assert c.Num == a.Num - b.Num

c = a * b
assert c.Num == a.Num * b.Num

c = a / b
assert c.Num == a.Num // b.Num

c = a & b
assert c.Num == a.Num & b.Num

c = a | b
assert c.Num == a.Num | b.Num

c = a ^ b
assert c.Num == a.Num ^ b.Num
");
}
[Test]
public void BitOperatorOverloads()
{
string name = string.Format("{0}.{1}",
typeof(OperableObject).DeclaringType.Name,
typeof(OperableObject).Name);
string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace;

PythonEngine.Exec($@"
from {module} import *
cls = {name}
a = cls(2)
b = cls(10)

c = a << b.Num
assert c.Num == a.Num << b.Num

c = a >> b.Num
assert c.Num == a.Num >> b.Num
");
}
}
}
4 changes: 4 additions & 0 deletions src/runtime/classmanager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,10 @@ private static ClassInfo GetClassInfo(Type type)

ob = new MethodObject(type, name, mlist);
ci.members[name] = ob;
if (OperatorMethod.IsOperatorMethod(name))
{
ci.members[OperatorMethod.GetPyMethodName(name)] = ob;
}
}

if (ci.indexer == null && type.IsClass)
Expand Down
54 changes: 50 additions & 4 deletions src/runtime/methodbinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -342,15 +342,34 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
bool paramsArray;
int kwargsMatched;
int defaultsNeeded;

bool isOperator = OperatorMethod.IsOperatorMethod(mi); // e.g. op_Addition is defined for OperableObject
Comment thread
christabella marked this conversation as resolved.
Outdated
if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList, out kwargsMatched, out defaultsNeeded))
{
continue;
if (isOperator)
{
defaultArgList = null;
}
else { continue; }
}
var outs = 0;
int clrnargs = pi.Length;
isOperator = isOperator && pynargs == clrnargs - 1; // Handle mismatched arg numbers due to Python operator being bound.
Comment thread
christabella marked this conversation as resolved.
Outdated
var margs = TryConvertArguments(pi, paramsArray, args, pynargs, kwargDict, defaultArgList,
needsResolution: _methods.Length > 1,
needsResolution: _methods.Length > 1, // If there's more than one possible match.
isOperator: isOperator,
outs: out outs);
if (isOperator)
{
if (inst != IntPtr.Zero)
{
var co = ManagedType.GetManagedObject(inst) as CLRObject;
if (co == null)
{
break;
}
margs[0] = co.inst;
}
}

if (margs == null)
{
Expand Down Expand Up @@ -474,13 +493,15 @@ static IntPtr HandleParamsArray(IntPtr args, int arrayStart, int pyArgCount, out
/// <param name="kwargDict">Dictionary of keyword argument name to python object pointer</param>
/// <param name="defaultArgList">A list of default values for omitted parameters</param>
/// <param name="needsResolution"><c>true</c>, if overloading resolution is required</param>
/// <param name="isOperator"><c>true</c>, if is operator method</param>
/// <param name="outs">Returns number of output parameters</param>
/// <returns>An array of .NET arguments, that can be passed to a method.</returns>
static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
IntPtr args, int pyArgCount,
Dictionary<string, IntPtr> kwargDict,
ArrayList defaultArgList,
bool needsResolution,
bool isOperator,
out int outs)
{
outs = 0;
Expand Down Expand Up @@ -519,6 +540,12 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
op = Runtime.PyTuple_GetItem(args, paramIndex);
}
}
if (isOperator && paramIndex == 0)
{
// After we've obtained the first argument from Python, we need to skip the first argument of the CLR
// because operator method is a bound method in Python
paramIndex++; // Leave the first .NET param as null (margs).
}

bool isOut;
if (!TryConvertArgument(op, parameter.ParameterType, needsResolution, out margs[paramIndex], out isOut))
Expand All @@ -543,6 +570,15 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
return margs;
}

/// <summary>
/// Try to convert a Python argument object to a managed CLR type.
/// </summary>
/// <param name="op">Pointer to the object at a particular parameter.</param>
/// <param name="parameterType">That parameter's managed type.</param>
/// <param name="needsResolution">There are multiple overloading methods that need resolution.</param>
/// <param name="arg">Converted argument.</param>
/// <param name="isOut">Whether the CLR type is passed by reference.</param>
/// <returns></returns>
static bool TryConvertArgument(IntPtr op, Type parameterType, bool needsResolution,
out object arg, out bool isOut)
{
Expand Down Expand Up @@ -633,7 +669,17 @@ static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool

return clrtype;
}

/// <summary>
/// Check whether the number of Python and .NET arguments match, and compute additional arg information.
/// </summary>
/// <param name="positionalArgumentCount">Number of positional args passed from Python.</param>
/// <param name="parameters">Parameters of the specified .NET method.</param>
/// <param name="kwargDict">Keyword args passed from Python.</param>
/// <param name="paramsArray">True if the final param of the .NET method is an array (`params` keyword).</param>
/// <param name="defaultArgList">List of default values for arguments.</param>
/// <param name="kwargsMatched">Number of kwargs from Python that are also present in the .NET method.</param>
/// <param name="defaultsNeeded">Number of non-null defaultsArgs.</param>
/// <returns></returns>
static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] parameters,
Dictionary<string, IntPtr> kwargDict,
out bool paramsArray,
Expand Down
10 changes: 10 additions & 0 deletions src/runtime/native/ITypeOffsets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ interface ITypeOffsets
int mp_subscript { get; }
int name { get; }
int nb_add { get; }
int nb_subtract { get; }
int nb_multiply { get; }
int nb_true_divide { get; }
int nb_and { get; }
int nb_or { get; }
int nb_xor { get; }
int nb_lshift { get; }
int nb_rshift { get; }
int nb_remainder { get; }
int nb_invert { get; }
int nb_inplace_add { get; }
int nb_inplace_subtract { get; }
int ob_size { get; }
Expand Down
10 changes: 10 additions & 0 deletions src/runtime/native/TypeOffset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ static partial class TypeOffset
internal static int mp_subscript { get; private set; }
internal static int name { get; private set; }
internal static int nb_add { get; private set; }
internal static int nb_subtract { get; private set; }
internal static int nb_multiply { get; private set; }
internal static int nb_true_divide { get; private set; }
internal static int nb_and { get; private set; }
internal static int nb_or { get; private set; }
internal static int nb_xor { get; private set; }
internal static int nb_lshift { get; private set; }
internal static int nb_rshift { get; private set; }
internal static int nb_remainder { get; private set; }
internal static int nb_invert { get; private set; }
internal static int nb_inplace_add { get; private set; }
internal static int nb_inplace_subtract { get; private set; }
internal static int ob_size { get; private set; }
Expand Down
Loading