Skip to content

Commit 659ad56

Browse files
committed
Address review by @lostmsu; Add forward operator test.
1 parent 6df5a1f commit 659ad56

File tree

5 files changed

+56
-71
lines changed

5 files changed

+56
-71
lines changed

src/embed_tests/TestPyMethod.cs renamed to src/embed_tests/TestOperator.cs

Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace Python.EmbeddingTest
99
{
10-
public class TestPyMethod
10+
public class TestOperator
1111
{
1212
[OneTimeSetUp]
1313
public void SetUp()
@@ -28,38 +28,6 @@ public class SampleClass
2828
public int Foo(int a, int b = 10) => a + b;
2929
}
3030

31-
[Test]
32-
public void TestVoidCall()
33-
{
34-
string name = string.Format("{0}.{1}",
35-
typeof(SampleClass).DeclaringType.Name,
36-
typeof(SampleClass).Name);
37-
string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace;
38-
PythonEngine.Exec($@"
39-
from {module} import *
40-
SampleClass = {name}
41-
obj = SampleClass()
42-
assert obj.VoidCall() == 10
43-
");
44-
}
45-
46-
[Test]
47-
public void TestDefaultParameter()
48-
{
49-
string name = string.Format("{0}.{1}",
50-
typeof(SampleClass).DeclaringType.Name,
51-
typeof(SampleClass).Name);
52-
string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace;
53-
54-
PythonEngine.Exec($@"
55-
from {module} import *
56-
SampleClass = {name}
57-
obj = SampleClass()
58-
assert obj.Foo(10) == 20
59-
assert obj.Foo(10, 1) == 11
60-
");
61-
}
62-
6331
public class OperableObject
6432
{
6533
public int Num { get; set; }
@@ -73,6 +41,10 @@ public OperableObject(int num)
7341
{
7442
return new OperableObject(a.Num + b.Num);
7543
}
44+
public static OperableObject operator +(OperableObject a, int b)
45+
{
46+
return new OperableObject(a.Num + b);
47+
}
7648

7749
public static OperableObject operator -(OperableObject a, OperableObject b)
7850
{
@@ -151,7 +123,25 @@ public void OperatorOverloads()
151123
");
152124
}
153125
[Test]
154-
public void BitOperatorOverloads()
126+
public void ForwardOperatorOverloads()
127+
{
128+
string name = string.Format("{0}.{1}",
129+
typeof(OperableObject).DeclaringType.Name,
130+
typeof(OperableObject).Name);
131+
string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace;
132+
133+
PythonEngine.Exec($@"
134+
from {module} import *
135+
cls = {name}
136+
a = cls(2)
137+
b = cls(10)
138+
139+
c = a + b.Num
140+
assert c.Num == a.Num + b.Num
141+
");
142+
}
143+
[Test]
144+
public void ShiftOperatorOverloads()
155145
{
156146
string name = string.Format("{0}.{1}",
157147
typeof(OperableObject).DeclaringType.Name,

src/runtime/classderived.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,7 @@ internal ClassDerivedObject(Type tp) : base(tp)
6666
public new static void tp_dealloc(IntPtr ob)
6767
{
6868
var self = (CLRObject)GetManagedObject(ob);
69-
// Amos's fix https://github.com/pythonnet/pythonnet/pull/1260#issuecomment-726719595
70-
if (self.tpHandle == IntPtr.Zero)
71-
{
72-
ClassBase.tp_dealloc(ob);
73-
return;
74-
}
69+
7570
// don't let the python GC destroy this object
7671
Runtime.PyObject_GC_UnTrack(self.pyHandle);
7772

src/runtime/classmanager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ private static ClassInfo GetClassInfo(Type type)
470470

471471
ob = new MethodObject(type, name, mlist);
472472
ci.members[name] = ob;
473-
if (OperatorMethod.IsOperatorMethod(name))
473+
if (OperatorMethod.IsOperatorMethod(mlist[0]))
474474
{
475475
ci.members[OperatorMethod.GetPyMethodName(name)] = ob;
476476
}

src/runtime/methodbinder.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,10 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
354354
needsResolution: _methods.Length > 1, // If there's more than one possible match.
355355
isOperator: isOperator,
356356
outs: out outs);
357+
if (margs == null)
358+
{
359+
continue;
360+
}
357361
if (isOperator)
358362
{
359363
if (inst != IntPtr.Zero)
@@ -367,10 +371,6 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
367371
}
368372
}
369373

370-
if (margs == null)
371-
{
372-
continue;
373-
}
374374

375375
var matchedMethod = new MatchedMethod(kwargsMatched, defaultsNeeded, margs, outs, mi);
376376
argMatchedMethods.Add(matchedMethod);

src/runtime/operatormethod.cs

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,35 @@ internal static class OperatorMethod
1212
// Maps the compiled method name in .NET CIL (e.g. op_Addition) to
1313
// the equivalent Python operator (e.g. __add__) as well as the offset
1414
// that identifies that operator's slot (e.g. nb_add) in heap space.
15-
public static Dictionary<string, Tuple<string, int>> OpMethodMap { get; private set; }
16-
15+
public static Dictionary<string, SlotDefinition> OpMethodMap { get; private set; }
16+
public readonly struct SlotDefinition
17+
{
18+
public SlotDefinition(string methodName, int typeOffset)
19+
{
20+
MethodName = methodName;
21+
TypeOffset = typeOffset;
22+
}
23+
public string MethodName { get; }
24+
public int TypeOffset { get; }
25+
}
1726
private static PyObject _opType;
1827

1928
static OperatorMethod()
2029
{
2130
// TODO: Rich compare, inplace operator support
22-
OpMethodMap = new Dictionary<string, Tuple<string, int>>
31+
OpMethodMap = new Dictionary<string, SlotDefinition>
2332
{
24-
["op_Addition"] = Tuple.Create("__add__", TypeOffset.nb_add),
25-
["op_Subtraction"] = Tuple.Create("__sub__", TypeOffset.nb_subtract),
26-
["op_Multiply"] = Tuple.Create("__mul__", TypeOffset.nb_multiply),
27-
#if PYTHON2
28-
["op_Division"] = Tuple.Create("__div__", TypeOffset.nb_divide),
29-
#else
30-
["op_Division"] = Tuple.Create("__truediv__", TypeOffset.nb_true_divide),
31-
#endif
32-
["op_BitwiseAnd"] = Tuple.Create("__and__", TypeOffset.nb_and),
33-
["op_BitwiseOr"] = Tuple.Create("__or__", TypeOffset.nb_or),
34-
["op_ExclusiveOr"] = Tuple.Create("__xor__", TypeOffset.nb_xor),
35-
["op_LeftShift"] = Tuple.Create("__lshift__", TypeOffset.nb_lshift),
36-
["op_RightShift"] = Tuple.Create("__rshift__", TypeOffset.nb_rshift),
37-
["op_Modulus"] = Tuple.Create("__mod__", TypeOffset.nb_remainder),
38-
["op_OneComplement"] = Tuple.Create("__invert__", TypeOffset.nb_invert)
33+
["op_Addition"] = new SlotDefinition("__add__", TypeOffset.nb_add),
34+
["op_Subtraction"] = new SlotDefinition("__sub__", TypeOffset.nb_subtract),
35+
["op_Multiply"] = new SlotDefinition("__mul__", TypeOffset.nb_multiply),
36+
["op_Division"] = new SlotDefinition("__truediv__", TypeOffset.nb_true_divide),
37+
["op_BitwiseAnd"] = new SlotDefinition("__and__", TypeOffset.nb_and),
38+
["op_BitwiseOr"] = new SlotDefinition("__or__", TypeOffset.nb_or),
39+
["op_ExclusiveOr"] = new SlotDefinition("__xor__", TypeOffset.nb_xor),
40+
["op_LeftShift"] = new SlotDefinition("__lshift__", TypeOffset.nb_lshift),
41+
["op_RightShift"] = new SlotDefinition("__rshift__", TypeOffset.nb_rshift),
42+
["op_Modulus"] = new SlotDefinition("__mod__", TypeOffset.nb_remainder),
43+
["op_OneComplement"] = new SlotDefinition("__invert__", TypeOffset.nb_invert)
3944
};
4045
}
4146

@@ -53,11 +58,6 @@ public static void Shutdown()
5358
}
5459
}
5560

56-
public static bool IsOperatorMethod(string methodName)
57-
{
58-
return OpMethodMap.ContainsKey(methodName);
59-
}
60-
6161
public static bool IsOperatorMethod(MethodBase method)
6262
{
6363
if (!method.IsSpecialName)
@@ -82,7 +82,7 @@ public static void FixupSlots(IntPtr pyType, Type clrType)
8282
{
8383
continue;
8484
}
85-
int offset = OpMethodMap[method.Name].Item2;
85+
int offset = OpMethodMap[method.Name].TypeOffset;
8686
// Copy the default implementation of e.g. the nb_add slot,
8787
// which simply calls __add__ on the type.
8888
IntPtr func = Marshal.ReadIntPtr(_opType.Handle, offset);
@@ -97,7 +97,7 @@ public static void FixupSlots(IntPtr pyType, Type clrType)
9797

9898
public static string GetPyMethodName(string clrName)
9999
{
100-
return OpMethodMap[clrName].Item1;
100+
return OpMethodMap[clrName].MethodName;
101101
}
102102

103103
private static string GenerateDummyCode()
@@ -106,7 +106,7 @@ private static string GenerateDummyCode()
106106
sb.AppendLine("class OperatorMethod(object):");
107107
foreach (var item in OpMethodMap.Values)
108108
{
109-
string def = string.Format(" def {0}(self, other): pass", item.Item1);
109+
string def = string.Format(" def {0}(self, other): pass", item.MethodName);
110110
sb.AppendLine(def);
111111
}
112112
return sb.ToString();

0 commit comments

Comments
 (0)