Skip to content

Comparison operators as implemented by ArvidJB in #291 #294

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

Merged
merged 2 commits into from
Nov 27, 2016
Merged
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
156 changes: 98 additions & 58 deletions src/runtime/classbase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,68 +67,108 @@ public virtual IntPtr type_subscript(IntPtr idx)
//====================================================================
// Standard comparison implementation for instances of reflected types.
//====================================================================
#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35)
public static IntPtr tp_richcompare(IntPtr ob, IntPtr other, int op) {
if (op != Runtime.Py_EQ && op != Runtime.Py_NE)
{
Runtime.XIncref(Runtime.PyNotImplemented);
return Runtime.PyNotImplemented;
}

IntPtr pytrue = Runtime.PyTrue;
IntPtr pyfalse = Runtime.PyFalse;

// swap true and false for NE
if (op != Runtime.Py_EQ)
{
pytrue = Runtime.PyFalse;
pyfalse = Runtime.PyTrue;
}

if (ob == other) {
Runtime.XIncref(pytrue);
return pytrue;
}

CLRObject co1 = GetManagedObject(ob) as CLRObject;
CLRObject co2 = GetManagedObject(other) as CLRObject;
if (null == co2) {
Runtime.XIncref(pyfalse);
return pyfalse;
}

Object o1 = co1.inst;
Object o2 = co2.inst;

if (Object.Equals(o1, o2)) {
Runtime.XIncref(pytrue);
return pytrue;
}

Runtime.XIncref(pyfalse);
return pyfalse;
}
#else
public static int tp_compare(IntPtr ob, IntPtr other)
{
if (ob == other)
{
return 0;
}

CLRObject co1 = GetManagedObject(ob) as CLRObject;
CLRObject co2 = GetManagedObject(other) as CLRObject;
Object o1 = co1.inst;
Object o2 = co2.inst;

if (Object.Equals(o1, o2))
public static IntPtr tp_richcompare(IntPtr ob, IntPtr other, int op) {
CLRObject co1;
CLRObject co2;
switch (op)
{
return 0;
case Runtime.Py_EQ:
case Runtime.Py_NE:
IntPtr pytrue = Runtime.PyTrue;
IntPtr pyfalse = Runtime.PyFalse;

// swap true and false for NE
if (op != Runtime.Py_EQ)
{
pytrue = Runtime.PyFalse;
pyfalse = Runtime.PyTrue;
}

if (ob == other)
{
Runtime.XIncref(pytrue);
return pytrue;
}

co1 = GetManagedObject(ob) as CLRObject;
co2 = GetManagedObject(other) as CLRObject;
if (null == co2)
{
Runtime.XIncref(pyfalse);
return pyfalse;
}

Object o1 = co1.inst;
Object o2 = co2.inst;

if (Object.Equals(o1, o2))
{
Runtime.XIncref(pytrue);
return pytrue;
}

Runtime.XIncref(pyfalse);
return pyfalse;
case Runtime.Py_LT:
case Runtime.Py_LE:
case Runtime.Py_GT:
case Runtime.Py_GE:
co1 = GetManagedObject(ob) as CLRObject;
co2 = GetManagedObject(other) as CLRObject;
if(co1 == null || co2 == null)
return Exceptions.RaiseTypeError("Cannot get managed object");
var co1Comp = co1.inst as IComparable;
if (co1Comp == null)
return Exceptions.RaiseTypeError("Cannot convert object of type " + co1.GetType() + " to IComparable");
try
{
var cmp = co1Comp.CompareTo(co2.inst);

IntPtr pyCmp;
if (cmp < 0)
{
if (op == Runtime.Py_LT || op == Runtime.Py_LE)
{
pyCmp = Runtime.PyTrue;
}
else
{
pyCmp = Runtime.PyFalse;
}
}
else if (cmp == 0)
{
if (op == Runtime.Py_LE || op == Runtime.Py_GE)
{
pyCmp = Runtime.PyTrue;
}
else
{
pyCmp = Runtime.PyFalse;
}
}
else
{
if (op == Runtime.Py_GE || op == Runtime.Py_GT) {
pyCmp = Runtime.PyTrue;
}
else {
pyCmp = Runtime.PyFalse;
}
}
Runtime.XIncref(pyCmp);
return pyCmp;
}
catch (ArgumentException e)
{
return Exceptions.RaiseTypeError(e.Message);
}
default:
Runtime.XIncref(Runtime.PyNotImplemented);
return Runtime.PyNotImplemented;
}
return -1;
}
#endif


//====================================================================
// Standard iteration support for instances of reflected types. This
Expand Down
32 changes: 17 additions & 15 deletions src/runtime/runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,13 @@ internal static void Initialize()
}

#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35)
IntPtr op = Runtime.PyImport_ImportModule("builtins");
IntPtr dict = Runtime.PyObject_GetAttrString(op, "__dict__");
PyNotImplemented = Runtime.PyObject_GetAttrString(op, "NotImplemented");
IntPtr op = Runtime.PyImport_ImportModule("builtins");
IntPtr dict = Runtime.PyObject_GetAttrString(op, "__dict__");
#else
IntPtr dict = Runtime.PyImport_GetModuleDict();
IntPtr op = Runtime.PyDict_GetItemString(dict, "__builtin__");
#endif
PyNotImplemented = Runtime.PyObject_GetAttrString(op, "NotImplemented");
PyBaseObjectType = Runtime.PyObject_GetAttrString(op, "object");

PyModuleType = Runtime.PyObject_Type(op);
Expand All @@ -263,7 +263,7 @@ internal static void Initialize()
Runtime.XDecref(op);

#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35)
Runtime.XDecref(dict);
Runtime.XDecref(dict);
#endif

op = Runtime.PyString_FromString("string");
Expand All @@ -275,9 +275,9 @@ internal static void Initialize()
Runtime.XDecref(op);

#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35)
op = Runtime.PyBytes_FromString("bytes");
PyBytesType = Runtime.PyObject_Type(op);
Runtime.XDecref(op);
op = Runtime.PyBytes_FromString("bytes");
PyBytesType = Runtime.PyObject_Type(op);
Runtime.XDecref(op);
#endif

op = Runtime.PyTuple_New(0);
Expand Down Expand Up @@ -397,16 +397,18 @@ internal static int AtExit()
internal static IntPtr PyTypeType;

#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35)
internal static IntPtr PyBytesType;
internal static IntPtr PyNotImplemented;
internal const int Py_LT = 0;
internal const int Py_LE = 1;
internal const int Py_EQ = 2;
internal const int Py_NE = 3;
internal const int Py_GT = 4;
internal static IntPtr _PyObject_NextNotImplemented;
internal static IntPtr PyBytesType;
internal static IntPtr _PyObject_NextNotImplemented;
#endif

internal static IntPtr PyNotImplemented;
internal const int Py_LT = 0;
internal const int Py_LE = 1;
internal const int Py_EQ = 2;
internal const int Py_NE = 3;
internal const int Py_GT = 4;
internal const int Py_GE = 5;

internal static IntPtr PyTrue;
internal static IntPtr PyFalse;
internal static IntPtr PyNone;
Expand Down
47 changes: 44 additions & 3 deletions src/tests/test_class.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from System.Collections import Hashtable
from Python.Test import ClassTest
import sys, os, string, unittest, types
import clr
import types
import unittest

import Python.Test as Test
import System
import six
from Python.Test import ClassTest
from System.Collections import Hashtable

if six.PY3:
DictProxyType = type(object.__dict__)
Expand Down Expand Up @@ -209,6 +212,44 @@ def testAddAndRemoveClassAttribute(self):
del TimeSpan.new_method
self.assertFalse(hasattr(ts, "new_method"))

def testComparisons(self):
from System import DateTimeOffset

d1 = DateTimeOffset.Parse("2016-11-14")
d2 = DateTimeOffset.Parse("2016-11-15")

self.assertEqual(d1 == d2, False)
self.assertEqual(d1 != d2, True)

self.assertEqual(d1 < d2, True)
self.assertEqual(d1 <= d2, True)
self.assertEqual(d1 >= d2, False)
self.assertEqual(d1 > d2, False)

self.assertEqual(d1 == d1, True)
self.assertEqual(d1 != d1, False)

self.assertEqual(d1 < d1, False)
self.assertEqual(d1 <= d1, True)
self.assertEqual(d1 >= d1, True)
self.assertEqual(d1 > d1, False)

self.assertEqual(d2 == d1, False)
self.assertEqual(d2 != d1, True)

self.assertEqual(d2 < d1, False)
self.assertEqual(d2 <= d1, False)
self.assertEqual(d2 >= d1, True)
self.assertEqual(d2 > d1, True)

self.assertRaises(TypeError, lambda: d1 < None)
self.assertRaises(TypeError, lambda: d1 < System.Guid())

# ClassTest does not implement IComparable
c1 = ClassTest()
c2 = ClassTest()
self.assertRaises(TypeError, lambda: c1 < c2)


class ClassicClass:
def kind(self):
Expand Down