Commit faace560 authored by Jeremy Hylton's avatar Jeremy Hylton

Another fix for the coercion bug.

I'm hesitant to see this fix really works, except that I wrote a very
minimal test suite that covers all the cases I'm aware of.  The
previous checkin caused numberic operations when the Missing object
was on the right-hand side.
parent 640a36ca
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
static char Missing_module_documentation[] = static char Missing_module_documentation[] =
"" ""
"\n$Id: Missing.c,v 1.19 2003/05/09 20:57:10 jeremy Exp $" "\n$Id: Missing.c,v 1.20 2003/05/09 21:50:36 jeremy Exp $"
; ;
#include "ExtensionClass.h" #include "ExtensionClass.h"
...@@ -26,7 +26,7 @@ typedef struct { ...@@ -26,7 +26,7 @@ typedef struct {
} Missing; } Missing;
static PyObject *vname=0, *Missing_dot_Value=0, *empty_string=0, *reduce=0; static PyObject *vname=0, *Missing_dot_Value=0, *empty_string=0, *reduce=0;
static PyObject *theValue; static PyObject *theValue, *notMissing;
static void static void
Missing_dealloc(Missing *self) Missing_dealloc(Missing *self)
...@@ -49,11 +49,20 @@ Missing_str(Missing *self) ...@@ -49,11 +49,20 @@ Missing_str(Missing *self)
return empty_string; return empty_string;
} }
/* Code to access Missing objects as numbers */ /* Code to access Missing objects as numbers.
We must guarantee that notMissing is never returned to Python code,
because it would violate the guarantee that all Python-accessible
Missing values are equal to each other.
*/
static PyObject * static PyObject *
Missing_bin(PyObject *v, PyObject *w) Missing_bin(PyObject *v, PyObject *w)
{ {
if (v == notMissing)
v = w;
assert(v != notMissing);
Py_INCREF(v); Py_INCREF(v);
return v; return v;
} }
...@@ -61,6 +70,9 @@ Missing_bin(PyObject *v, PyObject *w) ...@@ -61,6 +70,9 @@ Missing_bin(PyObject *v, PyObject *w)
static PyObject * static PyObject *
Missing_pow(PyObject *v, PyObject *w, PyObject *z) Missing_pow(PyObject *v, PyObject *w, PyObject *z)
{ {
if (v == notMissing)
v = w;
assert(v != notMissing);
Py_INCREF(v); Py_INCREF(v);
return v; return v;
} }
...@@ -78,62 +90,20 @@ Missing_nonzero(PyObject *v) ...@@ -78,62 +90,20 @@ Missing_nonzero(PyObject *v)
return 0; return 0;
} }
/* coerce should return two values of the same type. /* Always return the distinguished notMissing object as the result
of the coercion. The notMissing object does not compare equal
We violate that contract in a controlled way, by always coercing to other Missing objects.
the other object to Py_None. We are guaranteed that our tp_compare
will be called because Py_None does not define a tp_compare.
*/ */
static int static int
Missing_coerce(PyObject **pv, PyObject **pw) Missing_coerce(PyObject **pv, PyObject **pw)
{ {
Py_INCREF(*pv); Py_INCREF(*pv);
Py_INCREF(Py_None); Py_INCREF(notMissing);
*pw = Py_None; *pw = notMissing;
return 0; return 0;
} }
static PyObject *
Missing_int(Missing *v)
{
PyErr_SetString(PyExc_TypeError,
"Missing objects do not support conversion to integer");
return NULL;
}
static PyObject *
Missing_long(Missing *v)
{
PyErr_SetString(PyExc_TypeError,
"Missing objects do not support conversion to long");
return NULL;
}
static PyObject *
Missing_float(Missing *v)
{
PyErr_SetString(PyExc_TypeError,
"Missing objects do not support conversion to float");
return NULL;
}
static PyObject *
Missing_oct(Missing *v)
{
PyErr_SetString(PyExc_TypeError,
"Missing objects do not support conversion to an octal string");
return NULL;
}
static PyObject *
Missing_hex(Missing *v)
{
PyErr_SetString(PyExc_TypeError,
"Missing objects do not support conversion to hexadecimal strings");
return NULL;
}
static PyNumberMethods Missing_as_number = { static PyNumberMethods Missing_as_number = {
(binaryfunc)Missing_bin, /*nb_add*/ (binaryfunc)Missing_bin, /*nb_add*/
(binaryfunc)Missing_bin, /*nb_subtract*/ (binaryfunc)Missing_bin, /*nb_subtract*/
...@@ -153,11 +123,28 @@ static PyNumberMethods Missing_as_number = { ...@@ -153,11 +123,28 @@ static PyNumberMethods Missing_as_number = {
(binaryfunc)Missing_bin, /*nb_xor*/ (binaryfunc)Missing_bin, /*nb_xor*/
(binaryfunc)Missing_bin, /*nb_or*/ (binaryfunc)Missing_bin, /*nb_or*/
(coercion)Missing_coerce, /*nb_coerce*/ (coercion)Missing_coerce, /*nb_coerce*/
(unaryfunc)Missing_int, /*nb_int*/ 0, /*nb_int*/
(unaryfunc)Missing_long, /*nb_long*/ 0, /*nb_long*/
(unaryfunc)Missing_float, /*nb_float*/ 0, /*nb_float*/
(unaryfunc)Missing_oct, /*nb_oct*/ 0, /*nb_oct*/
(unaryfunc)Missing_hex, /*nb_hex*/ 0, /*nb_hex*/
#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION != 1
0, /* nb_inplace_add */
0, /* nb_inplace_subtract */
0, /* nb_inplace_multiply */
0, /* nb_inplace_divide */
0, /* nb_inplace_remainder */
0, /* nb_inplace_power */
0, /* nb_inplace_lshift */
0, /* nb_inplace_rshift */
0, /* nb_inplace_and */
0, /* nb_inplace_xor */
0, /* nb_inplace_or */
Missing_bin, /* nb_floor_divide */
Missing_bin, /* nb_true_divide */
0, /* nb_inplace_floor_divide */
0, /* nb_inplace_true_divide */
#endif
}; };
/* ------------------------------------------------------- */ /* ------------------------------------------------------- */
...@@ -226,16 +213,17 @@ Missing_call(PyObject *self, PyObject *args, PyObject *kw) ...@@ -226,16 +213,17 @@ Missing_call(PyObject *self, PyObject *args, PyObject *kw)
return self; return self;
} }
/* All Missing objects are equal to each other, but we have /* All Missing objects are equal to each other, except for the
specially arranged for Py_None to be passed as m2 via special notMissing object. It is returned by coerce to
Missing_coerce(). So be prepared for Py_None where indicate that Missing is being compare to something else.
a Missing is expected.
*/ */
static int static int
Missing_cmp(Missing *m1, Missing *m2) Missing_cmp(PyObject *m1, PyObject *m2)
{ {
return Py_None == (PyObject *)m2; if (m1 == notMissing)
return -1;
return (m2 == notMissing);
} }
static PyExtensionClass MissingType = { static PyExtensionClass MissingType = {
...@@ -249,7 +237,7 @@ static PyExtensionClass MissingType = { ...@@ -249,7 +237,7 @@ static PyExtensionClass MissingType = {
(printfunc)0, /*tp_print*/ (printfunc)0, /*tp_print*/
(getattrfunc)0, /*obsolete tp_getattr*/ (getattrfunc)0, /*obsolete tp_getattr*/
(setattrfunc)0, /*obsolete tp_setattr*/ (setattrfunc)0, /*obsolete tp_setattr*/
(cmpfunc)Missing_cmp, /*tp_compare*/ Missing_cmp, /*tp_compare*/
(reprfunc)Missing_repr, /*tp_repr*/ (reprfunc)Missing_repr, /*tp_repr*/
&Missing_as_number, /*tp_as_number*/ &Missing_as_number, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
...@@ -300,7 +288,8 @@ initMissing(void) ...@@ -300,7 +288,8 @@ initMissing(void)
PyExtensionClass_Export(d,"Missing",MissingType); PyExtensionClass_Export(d,"Missing",MissingType);
theValue=PyObject_CallObject((PyObject*)&MissingType, NULL); theValue = PyObject_CallObject((PyObject*)&MissingType, NULL);
notMissing = PyObject_CallObject((PyObject*)&MissingType, NULL);
reduce=PyCFunction_New(reduce_ml, theValue); reduce=PyCFunction_New(reduce_ml, theValue);
PyDict_SetItemString(d, "Value", theValue); PyDict_SetItemString(d, "Value", theValue);
......
from Missing import Value
assert Value != 12
assert 12 != Value
assert u"abc" != Value
assert Value != u"abc"
assert 1 + Value == Value
assert Value + 1 == Value
assert Value == 1 + Value
assert Value == Value + 1
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment