Created
January 12, 2017 03:49
-
-
Save MSeifert04/acec8515090a4217d1b87d4ff092f459 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Helper to warn about deprecated tp_compare return values. Return: | |
-2 for an exception; | |
-1 if v < w; | |
0 if v == w; | |
1 if v > w. | |
(This function cannot return 2.) | |
*/ | |
static int | |
adjust_tp_compare(int c) | |
{ | |
printf("adjust_tp_compare\n"); | |
if (PyErr_Occurred()) { | |
if (c != -1 && c != -2) { | |
PyObject *t, *v, *tb; | |
PyErr_Fetch(&t, &v, &tb); | |
if (PyErr_Warn(PyExc_RuntimeWarning, | |
"tp_compare didn't return -1 or -2 " | |
"for exception") < 0) { | |
Py_XDECREF(t); | |
Py_XDECREF(v); | |
Py_XDECREF(tb); | |
} | |
else | |
PyErr_Restore(t, v, tb); | |
} | |
return -2; | |
} | |
else if (c < -1 || c > 1) { | |
if (PyErr_Warn(PyExc_RuntimeWarning, | |
"tp_compare didn't return -1, 0 or 1") < 0) | |
return -2; | |
else | |
return c < -1 ? -1 : 1; | |
} | |
else { | |
assert(c >= -1 && c <= 1); | |
return c; | |
} | |
} | |
/* Macro to get the tp_richcompare field of a type if defined */ | |
#define RICHCOMPARE(t) (PyType_HasFeature((t), Py_TPFLAGS_HAVE_RICHCOMPARE) \ | |
? (t)->tp_richcompare : NULL) | |
/* Try a genuine rich comparison, returning an object. Return: | |
NULL for exception; | |
NotImplemented if this particular rich comparison is not implemented or | |
undefined; | |
some object not equal to NotImplemented if it is implemented | |
(this latter object may not be a Boolean). | |
*/ | |
static PyObject * | |
try_rich_compare(PyObject *v, PyObject *w, int op) | |
{ | |
printf("try_rich_compare\n"); | |
richcmpfunc f; | |
PyObject *res; | |
if (v->ob_type != w->ob_type && | |
PyType_IsSubtype(w->ob_type, v->ob_type) && | |
(f = RICHCOMPARE(w->ob_type)) != NULL) { | |
printf("try_rich_compare - 1\n"); | |
res = (*f)(w, v, _Py_SwappedOp[op]); | |
if (res != Py_NotImplemented) | |
return res; | |
Py_DECREF(res); | |
} | |
if ((f = RICHCOMPARE(v->ob_type)) != NULL) { | |
printf("try_rich_compare - 2\n"); | |
res = (*f)(v, w, op); | |
if (res != Py_NotImplemented) | |
return res; | |
Py_DECREF(res); | |
} | |
if ((f = RICHCOMPARE(w->ob_type)) != NULL) { | |
printf("try_rich_compare - 3\n"); | |
return (*f)(w, v, _Py_SwappedOp[op]); | |
} | |
res = Py_NotImplemented; | |
Py_INCREF(res); | |
return res; | |
} | |
/* Try a genuine rich comparison, returning an int. Return: | |
-1 for exception (including the case where try_rich_compare() returns an | |
object that's not a Boolean); | |
0 if the outcome is false; | |
1 if the outcome is true; | |
2 if this particular rich comparison is not implemented or undefined. | |
*/ | |
static int | |
try_rich_compare_bool(PyObject *v, PyObject *w, int op) | |
{ | |
printf("try_rich_compare_bool\n"); | |
PyObject *res; | |
int ok; | |
if (RICHCOMPARE(v->ob_type) == NULL && RICHCOMPARE(w->ob_type) == NULL) | |
return 2; /* Shortcut, avoid INCREF+DECREF */ | |
res = try_rich_compare(v, w, op); | |
if (res == NULL) | |
return -1; | |
if (res == Py_NotImplemented) { | |
Py_DECREF(res); | |
return 2; | |
} | |
ok = PyObject_IsTrue(res); | |
Py_DECREF(res); | |
return ok; | |
} | |
/* Try rich comparisons to determine a 3-way comparison. Return: | |
-2 for an exception; | |
-1 if v < w; | |
0 if v == w; | |
1 if v > w; | |
2 if this particular rich comparison is not implemented or undefined. | |
*/ | |
static int | |
try_rich_to_3way_compare(PyObject *v, PyObject *w) | |
{ | |
printf("try_rich_to_3way_compare\n"); | |
static struct { int op; int outcome; } tries[3] = { | |
/* Try this operator, and if it is true, use this outcome: */ | |
{Py_EQ, 0}, | |
{Py_LT, -1}, | |
{Py_GT, 1}, | |
}; | |
int i; | |
if (RICHCOMPARE(v->ob_type) == NULL && RICHCOMPARE(w->ob_type) == NULL) | |
return 2; /* Shortcut */ | |
for (i = 0; i < 3; i++) { | |
switch (try_rich_compare_bool(v, w, tries[i].op)) { | |
case -1: | |
return -2; | |
case 1: | |
return tries[i].outcome; | |
} | |
} | |
return 2; | |
} | |
/* Try a 3-way comparison, returning an int. Return: | |
-2 for an exception; | |
-1 if v < w; | |
0 if v == w; | |
1 if v > w; | |
2 if this particular 3-way comparison is not implemented or undefined. | |
*/ | |
static int | |
try_3way_compare(PyObject *v, PyObject *w) | |
{ | |
printf("try_3way_compare\n"); | |
int c; | |
cmpfunc f; | |
/* Comparisons involving instances are given to instance_compare, | |
which has the same return conventions as this function. */ | |
f = v->ob_type->tp_compare; | |
if (PyInstance_Check(v)) | |
return (*f)(v, w); | |
if (PyInstance_Check(w)) | |
return (*w->ob_type->tp_compare)(v, w); | |
/* If both have the same (non-NULL) tp_compare, use it. */ | |
if (f != NULL && f == w->ob_type->tp_compare) { | |
c = (*f)(v, w); | |
return adjust_tp_compare(c); | |
} | |
/* If we're here, v and w, | |
a) are not instances; | |
b) have different types or a type without tp_compare; and | |
c) don't have a user-defined tp_compare. | |
tp_compare implementations in C assume that both arguments | |
have their type, so we give up if the coercion fails or if | |
it yields types which are still incompatible (which can | |
happen with a user-defined nb_coerce). | |
*/ | |
c = PyNumber_CoerceEx(&v, &w); | |
if (c < 0) | |
return -2; | |
if (c > 0) | |
return 2; | |
f = v->ob_type->tp_compare; | |
if (f != NULL && f == w->ob_type->tp_compare) { | |
c = (*f)(v, w); | |
Py_DECREF(v); | |
Py_DECREF(w); | |
return adjust_tp_compare(c); | |
} | |
/* No comparison defined */ | |
Py_DECREF(v); | |
Py_DECREF(w); | |
return 2; | |
} | |
/* Final fallback 3-way comparison, returning an int. Return: | |
-2 if an error occurred; | |
-1 if v < w; | |
0 if v == w; | |
1 if v > w. | |
*/ | |
static int | |
default_3way_compare(PyObject *v, PyObject *w) | |
{ | |
printf("default_3way_compare\n"); | |
int c; | |
const char *vname, *wname; | |
if (v->ob_type == w->ob_type) { | |
/* When comparing these pointers, they must be cast to | |
* integer types (i.e. Py_uintptr_t, our spelling of C9X's | |
* uintptr_t). ANSI specifies that pointer compares other | |
* than == and != to non-related structures are undefined. | |
*/ | |
Py_uintptr_t vv = (Py_uintptr_t)v; | |
Py_uintptr_t ww = (Py_uintptr_t)w; | |
return (vv < ww) ? -1 : (vv > ww) ? 1 : 0; | |
} | |
/* None is smaller than anything */ | |
if (v == Py_None) | |
return -1; | |
if (w == Py_None) | |
return 1; | |
/* different type: compare type names; numbers are smaller */ | |
if (PyNumber_Check(v)) | |
vname = ""; | |
else | |
vname = v->ob_type->tp_name; | |
if (PyNumber_Check(w)) | |
wname = ""; | |
else | |
wname = w->ob_type->tp_name; | |
c = strcmp(vname, wname); | |
if (c < 0) | |
return -1; | |
if (c > 0) | |
return 1; | |
/* Same type name, or (more likely) incomparable numeric types */ | |
return ((Py_uintptr_t)(v->ob_type) < ( | |
Py_uintptr_t)(w->ob_type)) ? -1 : 1; | |
} | |
/* Do a 3-way comparison, by hook or by crook. Return: | |
-2 for an exception (but see below); | |
-1 if v < w; | |
0 if v == w; | |
1 if v > w; | |
BUT: if the object implements a tp_compare function, it returns | |
whatever this function returns (whether with an exception or not). | |
*/ | |
static int | |
do_cmp(PyObject *v, PyObject *w) | |
{ | |
printf("do_cmp\n"); | |
int c; | |
cmpfunc f; | |
if (v->ob_type == w->ob_type | |
&& (f = v->ob_type->tp_compare) != NULL) { | |
c = (*f)(v, w); | |
if (PyInstance_Check(v)) { | |
/* Instance tp_compare has a different signature. | |
But if it returns undefined we fall through. */ | |
if (c != 2) | |
return c; | |
/* Else fall through to try_rich_to_3way_compare() */ | |
} | |
else | |
return adjust_tp_compare(c); | |
} | |
/* We only get here if one of the following is true: | |
a) v and w have different types | |
b) v and w have the same type, which doesn't have tp_compare | |
c) v and w are instances, and either __cmp__ is not defined or | |
__cmp__ returns NotImplemented | |
*/ | |
c = try_rich_to_3way_compare(v, w); | |
if (c < 2) | |
return c; | |
c = try_3way_compare(v, w); | |
if (c < 2) | |
return c; | |
return default_3way_compare(v, w); | |
} | |
/* Return (new reference to) Py_True or Py_False. */ | |
static PyObject * | |
convert_3way_to_object(int op, int c) | |
{ | |
printf("convert_3way_to_object\n"); | |
PyObject *result; | |
switch (op) { | |
case Py_LT: c = c < 0; break; | |
case Py_LE: c = c <= 0; break; | |
case Py_EQ: c = c == 0; break; | |
case Py_NE: c = c != 0; break; | |
case Py_GT: c = c > 0; break; | |
case Py_GE: c = c >= 0; break; | |
} | |
result = c ? Py_True : Py_False; | |
Py_INCREF(result); | |
return result; | |
} | |
/* We want a rich comparison but don't have one. Try a 3-way cmp instead. | |
Return | |
NULL if error | |
Py_True if v op w | |
Py_False if not (v op w) | |
*/ | |
static PyObject * | |
try_3way_to_rich_compare(PyObject *v, PyObject *w, int op) | |
{ | |
printf("try_3way_to_rich_compare\n"); | |
int c; | |
c = try_3way_compare(v, w); | |
if (c >= 2) { | |
/* Py3K warning if types are not equal and comparison isn't == or != */ | |
if (Py_Py3kWarningFlag && | |
v->ob_type != w->ob_type && op != Py_EQ && op != Py_NE && | |
PyErr_WarnEx(PyExc_DeprecationWarning, | |
"comparing unequal types not supported " | |
"in 3.x", 1) < 0) { | |
return NULL; | |
} | |
c = default_3way_compare(v, w); | |
} | |
if (c <= -2) | |
return NULL; | |
return convert_3way_to_object(op, c); | |
} | |
/* Do rich comparison on v and w. Return | |
NULL if error | |
Else a new reference to an object other than Py_NotImplemented, usually(?): | |
Py_True if v op w | |
Py_False if not (v op w) | |
*/ | |
static PyObject * | |
do_richcmp(PyObject *v, PyObject *w, int op) | |
{ | |
printf("do_richcmp\n"); | |
PyObject *res; | |
res = try_rich_compare(v, w, op); | |
if (res != Py_NotImplemented) | |
return res; | |
Py_DECREF(res); | |
return try_3way_to_rich_compare(v, w, op); | |
} | |
/* Return: | |
NULL for exception; | |
some object not equal to NotImplemented if it is implemented | |
(this latter object may not be a Boolean). | |
*/ | |
PyObject * | |
otherrichcompare(PyObject *v, PyObject *w, int op) | |
{ | |
printf("otherrichcompare\n"); | |
PyObject *res; | |
assert(Py_LT <= op && op <= Py_GE); | |
if (Py_EnterRecursiveCall(" in cmp")) | |
return NULL; | |
/* If the types are equal, and not old-style instances, try to | |
get out cheap (don't bother with coercions etc.). */ | |
if (v->ob_type == w->ob_type && !PyInstance_Check(v)) { | |
cmpfunc fcmp; | |
richcmpfunc frich = RICHCOMPARE(v->ob_type); | |
/* If the type has richcmp, try it first. try_rich_compare | |
tries it two-sided, which is not needed since we've a | |
single type only. */ | |
if (frich != NULL) { | |
printf("otherrichcompare - 1\n"); | |
res = (*frich)(v, w, op); | |
if (res != Py_NotImplemented) | |
goto Done; | |
Py_DECREF(res); | |
} | |
/* No richcmp, or this particular richmp not implemented. | |
Try 3-way cmp. */ | |
fcmp = v->ob_type->tp_compare; | |
if (fcmp != NULL) { | |
printf("otherrichcompare - 2\n"); | |
int c = (*fcmp)(v, w); | |
c = adjust_tp_compare(c); | |
if (c == -2) { | |
res = NULL; | |
goto Done; | |
} | |
res = convert_3way_to_object(op, c); | |
goto Done; | |
} | |
} | |
/* Fast path not taken, or couldn't deliver a useful result. */ | |
res = do_richcmp(v, w, op); | |
Done: | |
Py_LeaveRecursiveCall(); | |
return res; | |
} | |
static PyObject * | |
myrichcmp(PyObject *m, PyObject *args) | |
{ | |
PyObject *op1, *op2; | |
if (PyArg_UnpackTuple(args, "richcomp\n", 2, 2, &op1, &op2)) { | |
return otherrichcompare(op1, op2, Py_EQ); | |
} else { | |
return NULL; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment