[Python-checkins] python/nondist/sandbox/datetime obj_delta.c,1.17,1.18 test_both.py,1.18,1.19

tim_one@users.sourceforge.net tim_one@users.sourceforge.net
Mon, 02 Dec 2002 13:17:32 -0800


Update of /cvsroot/python/python/nondist/sandbox/datetime
In directory sc8-pr-cvs1:/tmp/cvs-serv20656

Modified Files:
	obj_delta.c test_both.py 
Log Message:
Repair comparison for timedelta objects.  tp_compare is ignored by
try_3way_compare because a timedelta isn't an old-style instance, so
its tp_compare didn't get called at all unless both comparands were
timedelta.  This caused, e.g., comparing a timedelta and a dict to
compare via type string name instead of raising the intended TypeError.
As suggested by Guido, repaired via getting rid of tp_compare and
adding tp_richcompare.

The test suite didn't provoke these problems, so added a new test that
does.

I'm sure that date and datetime objects still suffer a similar problem.


Index: obj_delta.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/obj_delta.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -C2 -d -r1.17 -r1.18
*** obj_delta.c	2 Dec 2002 20:42:53 -0000	1.17
--- obj_delta.c	2 Dec 2002 21:17:14 -0000	1.18
***************
*** 246,278 ****
  }
  
! /* XXX This routine is never entered unless self and other are both
!  * XXX PyDateTime_Delta.  For whatever reason, Python's try_3way_compare
!  * XXX ignores tp_compare unless PyInstance_Check returns true, but
!  * XXX these aren't old-style classes.
   */
! static int
! delta_compare(PyDateTime_Delta *self, PyObject *other)
  {
! 	int result = -1;
  
  	if (! PyObject_TypeCheck(other, &PyDateTime_DeltaType)) {
- 		/* XXX Dead code!  See note above. */
  		PyErr_Format(PyExc_TypeError,
  			     "can't compare %s to %s instance",
  			     self->ob_type->tp_name, other->ob_type->tp_name);
  	}
! 	else {
! 		long diff = GET_TD_DAYS(self) - GET_TD_DAYS(other);
! 		if (diff == 0) {
! 			diff = GET_TD_SECONDS(self) - GET_TD_SECONDS(other);
! 			if (diff == 0)
! 				diff = GET_TD_MICROSECONDS(self) -
! 				       GET_TD_MICROSECONDS(other);
! 		}
  		if (diff == 0)
! 			result = 0;
! 		else if (diff > 0)
! 			result = 1;
  	}
  	return result;
  }
--- 246,285 ----
  }
  
! /* This is more natural as a tp_compare, but doesn't work then:  for whatever
!  * reason, Python's try_3way_compare ignores tp_compare unless
!  * PyInstance_Check returns true, but these aren't old-style classes.
   */
! static PyObject *
! delta_richcompare(PyDateTime_Delta *self, PyObject *other, int op)
  {
! 	PyObject *result;
! 	long diff;
! 	int istrue;
  
  	if (! PyObject_TypeCheck(other, &PyDateTime_DeltaType)) {
  		PyErr_Format(PyExc_TypeError,
  			     "can't compare %s to %s instance",
  			     self->ob_type->tp_name, other->ob_type->tp_name);
+ 		return NULL;
  	}
! 	diff = GET_TD_DAYS(self) - GET_TD_DAYS(other);
! 	if (diff == 0) {
! 		diff = GET_TD_SECONDS(self) - GET_TD_SECONDS(other);
  		if (diff == 0)
! 			diff = GET_TD_MICROSECONDS(self) -
! 			       GET_TD_MICROSECONDS(other);
! 	}
! 	switch (op) {
! 		case Py_EQ: istrue = diff == 0; break;
! 		case Py_NE: istrue = diff != 0; break;
! 		case Py_LE: istrue = diff <= 0; break;
! 		case Py_GE: istrue = diff >= 0; break;
! 		case Py_LT: istrue = diff < 0; break;
! 		case Py_GT: istrue = diff > 0; break;
! 		default:
! 			assert(! "op unknown");
  	}
+ 	result = istrue ? Py_True : Py_False;
+ 	Py_INCREF(result);
  	return result;
  }
***************
*** 668,672 ****
  	0,						/* tp_getattr */
  	0,						/* tp_setattr */
! 	(cmpfunc)delta_compare,				/* tp_compare */
  	(reprfunc)delta_repr,				/* tp_repr */
  	&delta_as_number,				/* tp_as_number */
--- 675,679 ----
  	0,						/* tp_getattr */
  	0,						/* tp_setattr */
! 	0,						/* tp_compare */
  	(reprfunc)delta_repr,				/* tp_repr */
  	&delta_as_number,				/* tp_as_number */
***************
*** 683,687 ****
  	0,						/* tp_traverse */
  	0,						/* tp_clear */
! 	0,						/* tp_richcompare */
  	0,						/* tp_weaklistoffset */
  	0,						/* tp_iter */
--- 690,694 ----
  	0,						/* tp_traverse */
  	0,						/* tp_clear */
! 	delta_richcompare,				/* tp_richcompare */
  	0,						/* tp_weaklistoffset */
  	0,						/* tp_iter */

Index: test_both.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_both.py,v
retrieving revision 1.18
retrieving revision 1.19
diff -C2 -d -r1.18 -r1.19
*** test_both.py	2 Dec 2002 19:40:42 -0000	1.18
--- test_both.py	2 Dec 2002 21:17:18 -0000	1.19
***************
*** 146,149 ****
--- 146,192 ----
                  self.assertEqual(orig, derived)
  
+     def test_compare(self):
+         t1 = timedelta(2, 3, 4)
+         t2 = timedelta(2, 3, 4)
+         self.failUnless(t1 == t2)
+         self.failUnless(t1 <= t2)
+         self.failUnless(t1 >= t2)
+         self.failUnless(not t1 != t2)
+         self.failUnless(not t1 < t2)
+         self.failUnless(not t1 > t2)
+         self.assertEqual(cmp(t1, t2), 0)
+         self.assertEqual(cmp(t2, t1), 0)
+ 
+         for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
+             t2 = timedelta(*args)   # this is larger than t1
+             self.failUnless(t1 < t2)
+             self.failUnless(t2 > t1)
+             self.failUnless(t1 <= t2)
+             self.failUnless(t2 >= t1)
+             self.failUnless(t1 != t2)
+             self.failUnless(t2 != t1)
+             self.failUnless(not t1 == t2)
+             self.failUnless(not t2 == t1)
+             self.failUnless(not t1 > t2)
+             self.failUnless(not t2 < t1)
+             self.failUnless(not t1 >= t2)
+             self.failUnless(not t2 <= t1)
+             self.assertEqual(cmp(t1, t2), -1)
+             self.assertEqual(cmp(t2, t1), 1)
+ 
+         for badarg in 10, 10L, 34.5, "abc", {}, [], ():
+             self.assertRaises(TypeError, lambda: t1 == badarg)
+             self.assertRaises(TypeError, lambda: t1 != badarg)
+             self.assertRaises(TypeError, lambda: t1 <= badarg)
+             self.assertRaises(TypeError, lambda: t1 < badarg)
+             self.assertRaises(TypeError, lambda: t1 > badarg)
+             self.assertRaises(TypeError, lambda: t1 >= badarg)
+             self.assertRaises(TypeError, lambda: badarg == t1)
+             self.assertRaises(TypeError, lambda: badarg != t1)
+             self.assertRaises(TypeError, lambda: badarg <= t1)
+             self.assertRaises(TypeError, lambda: badarg < t1)
+             self.assertRaises(TypeError, lambda: badarg > t1)
+             self.assertRaises(TypeError, lambda: badarg >= t1)
+ 
  #############################################################################
  # date tests