mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	Check the type of values returned by __int__, __float__, __long__,
__oct__, and __hex__. Raise TypeError if an invalid type is returned. Note that PyNumber_Int and PyNumber_Long can still return ints or longs. Fixes SF bug #966618.
This commit is contained in:
		
							parent
							
								
									66edb6295f
								
							
						
					
					
						commit
						3a313e3655
					
				
					 5 changed files with 138 additions and 41 deletions
				
			
		| 
						 | 
					@ -43,11 +43,6 @@
 | 
				
			||||||
    "neg",
 | 
					    "neg",
 | 
				
			||||||
    "pos",
 | 
					    "pos",
 | 
				
			||||||
    "abs",
 | 
					    "abs",
 | 
				
			||||||
    "int",
 | 
					 | 
				
			||||||
    "long",
 | 
					 | 
				
			||||||
    "float",
 | 
					 | 
				
			||||||
    "oct",
 | 
					 | 
				
			||||||
    "hex",
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# generic operations
 | 
					# generic operations
 | 
				
			||||||
    "init",
 | 
					    "init",
 | 
				
			||||||
| 
						 | 
					@ -58,6 +53,11 @@
 | 
				
			||||||
#    "hash",
 | 
					#    "hash",
 | 
				
			||||||
#    "str",
 | 
					#    "str",
 | 
				
			||||||
#    "repr",
 | 
					#    "repr",
 | 
				
			||||||
 | 
					#    "int",
 | 
				
			||||||
 | 
					#    "long",
 | 
				
			||||||
 | 
					#    "float",
 | 
				
			||||||
 | 
					#    "oct",
 | 
				
			||||||
 | 
					#    "hex",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# These are separate because they can influence the test of other methods.
 | 
					# These are separate because they can influence the test of other methods.
 | 
				
			||||||
#    "getattr",
 | 
					#    "getattr",
 | 
				
			||||||
| 
						 | 
					@ -81,6 +81,26 @@ def __repr__(self, *args):
 | 
				
			||||||
        print "__repr__:", args
 | 
					        print "__repr__:", args
 | 
				
			||||||
        return "AllTests"
 | 
					        return "AllTests"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __int__(self, *args):
 | 
				
			||||||
 | 
					        print "__int__:", args
 | 
				
			||||||
 | 
					        return 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __float__(self, *args):
 | 
				
			||||||
 | 
					        print "__float__:", args
 | 
				
			||||||
 | 
					        return 1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __long__(self, *args):
 | 
				
			||||||
 | 
					        print "__long__:", args
 | 
				
			||||||
 | 
					        return 1L
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __oct__(self, *args):
 | 
				
			||||||
 | 
					        print "__oct__:", args
 | 
				
			||||||
 | 
					        return '01'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __hex__(self, *args):
 | 
				
			||||||
 | 
					        print "__hex__:", args
 | 
				
			||||||
 | 
					        return '0x1'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __cmp__(self, *args):
 | 
					    def __cmp__(self, *args):
 | 
				
			||||||
        print "__cmp__:", args
 | 
					        print "__cmp__:", args
 | 
				
			||||||
        return 0
 | 
					        return 0
 | 
				
			||||||
| 
						 | 
					@ -195,21 +215,11 @@ def __%(method)s__(self, *args):
 | 
				
			||||||
-testme
 | 
					-testme
 | 
				
			||||||
+testme
 | 
					+testme
 | 
				
			||||||
abs(testme)
 | 
					abs(testme)
 | 
				
			||||||
if sys.platform[:4] != 'java':
 | 
					int(testme)
 | 
				
			||||||
    int(testme)
 | 
					long(testme)
 | 
				
			||||||
    long(testme)
 | 
					float(testme)
 | 
				
			||||||
    float(testme)
 | 
					oct(testme)
 | 
				
			||||||
    oct(testme)
 | 
					hex(testme)
 | 
				
			||||||
    hex(testme)
 | 
					 | 
				
			||||||
else:
 | 
					 | 
				
			||||||
    # Jython enforced that these methods return
 | 
					 | 
				
			||||||
    # a value of the expected type.
 | 
					 | 
				
			||||||
    print "__int__: ()"
 | 
					 | 
				
			||||||
    print "__long__: ()"
 | 
					 | 
				
			||||||
    print "__float__: ()"
 | 
					 | 
				
			||||||
    print "__oct__: ()"
 | 
					 | 
				
			||||||
    print "__hex__: ()"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# And the rest...
 | 
					# And the rest...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -254,6 +264,54 @@ def __delattr__(self, *args):
 | 
				
			||||||
del testme.cardinal
 | 
					del testme.cardinal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# return values of some method are type-checked
 | 
				
			||||||
 | 
					class BadTypeClass:
 | 
				
			||||||
 | 
					    def __int__(self):
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					    __float__ = __int__
 | 
				
			||||||
 | 
					    __long__ = __int__
 | 
				
			||||||
 | 
					    __str__ = __int__
 | 
				
			||||||
 | 
					    __repr__ = __int__
 | 
				
			||||||
 | 
					    __oct__ = __int__
 | 
				
			||||||
 | 
					    __hex__ = __int__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def check_exc(stmt, exception):
 | 
				
			||||||
 | 
					    """Raise TestFailed if executing 'stmt' does not raise 'exception'
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        exec stmt
 | 
				
			||||||
 | 
					    except exception:
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        raise TestFailed, "%s should raise %s" % (stmt, exception)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					check_exc("int(BadTypeClass())", TypeError)
 | 
				
			||||||
 | 
					check_exc("float(BadTypeClass())", TypeError)
 | 
				
			||||||
 | 
					check_exc("long(BadTypeClass())", TypeError)
 | 
				
			||||||
 | 
					check_exc("str(BadTypeClass())", TypeError)
 | 
				
			||||||
 | 
					check_exc("repr(BadTypeClass())", TypeError)
 | 
				
			||||||
 | 
					check_exc("oct(BadTypeClass())", TypeError)
 | 
				
			||||||
 | 
					check_exc("hex(BadTypeClass())", TypeError)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# mixing up ints and longs is okay
 | 
				
			||||||
 | 
					class IntLongMixClass:
 | 
				
			||||||
 | 
					    def __int__(self):
 | 
				
			||||||
 | 
					        return 0L
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __long__(self):
 | 
				
			||||||
 | 
					        return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					    int(IntLongMixClass())
 | 
				
			||||||
 | 
					except TypeError:
 | 
				
			||||||
 | 
					    raise TestFailed, "TypeError should not be raised"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					    long(IntLongMixClass())
 | 
				
			||||||
 | 
					except TypeError:
 | 
				
			||||||
 | 
					    raise TestFailed, "TypeError should not be raised"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Test correct errors from hash() on objects with comparisons but no __hash__
 | 
					# Test correct errors from hash() on objects with comparisons but no __hash__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class C0:
 | 
					class C0:
 | 
				
			||||||
| 
						 | 
					@ -264,17 +322,12 @@ class C0:
 | 
				
			||||||
class C1:
 | 
					class C1:
 | 
				
			||||||
    def __cmp__(self, other): return 0
 | 
					    def __cmp__(self, other): return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try: hash(C1())
 | 
					check_exc("hash(C1())", TypeError)
 | 
				
			||||||
except TypeError: pass
 | 
					 | 
				
			||||||
else: raise TestFailed, "hash(C1()) should raise an exception"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class C2:
 | 
					class C2:
 | 
				
			||||||
    def __eq__(self, other): return 1
 | 
					    def __eq__(self, other): return 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try: hash(C2())
 | 
					check_exc("hash(C2())", TypeError)
 | 
				
			||||||
except TypeError: pass
 | 
					 | 
				
			||||||
else: raise TestFailed, "hash(C2()) should raise an exception"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Test for SF bug 532646
 | 
					# Test for SF bug 532646
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,11 @@ Core and builtins
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Compiler now treats None as a constant.
 | 
					- Compiler now treats None as a constant.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- The type of values returned by __int__, __float__, __long__,
 | 
				
			||||||
 | 
					  __oct__, and __hex__ are now checked.  Returning an invalid type
 | 
				
			||||||
 | 
					  will cause a TypeError to be raised.  This matches the behavior of
 | 
				
			||||||
 | 
					  Jython.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Extension modules
 | 
					Extension modules
 | 
				
			||||||
-----------------
 | 
					-----------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -965,8 +965,17 @@ PyNumber_Int(PyObject *o)
 | 
				
			||||||
					 10);
 | 
										 10);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	m = o->ob_type->tp_as_number;
 | 
						m = o->ob_type->tp_as_number;
 | 
				
			||||||
	if (m && m->nb_int)
 | 
						if (m && m->nb_int) {
 | 
				
			||||||
		return m->nb_int(o);
 | 
							PyObject *res = m->nb_int(o);
 | 
				
			||||||
 | 
							if (res && (!PyInt_Check(res) && !PyLong_Check(res))) {
 | 
				
			||||||
 | 
								PyErr_Format(PyExc_TypeError,
 | 
				
			||||||
 | 
									     "__int__ returned non-int (type %.200s)",
 | 
				
			||||||
 | 
									     res->ob_type->tp_name);
 | 
				
			||||||
 | 
								Py_DECREF(res);
 | 
				
			||||||
 | 
								return NULL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return res;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if (!PyObject_AsCharBuffer(o, &buffer, &buffer_len))
 | 
						if (!PyObject_AsCharBuffer(o, &buffer, &buffer_len))
 | 
				
			||||||
		return int_from_string((char*)buffer, buffer_len);
 | 
							return int_from_string((char*)buffer, buffer_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1022,8 +1031,17 @@ PyNumber_Long(PyObject *o)
 | 
				
			||||||
					  10);
 | 
										  10);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	m = o->ob_type->tp_as_number;
 | 
						m = o->ob_type->tp_as_number;
 | 
				
			||||||
	if (m && m->nb_long)
 | 
						if (m && m->nb_long) {
 | 
				
			||||||
		return m->nb_long(o);
 | 
							PyObject *res = m->nb_long(o);
 | 
				
			||||||
 | 
							if (res && (!PyInt_Check(res) && !PyLong_Check(res))) {
 | 
				
			||||||
 | 
								PyErr_Format(PyExc_TypeError,
 | 
				
			||||||
 | 
									     "__long__ returned non-long (type %.200s)",
 | 
				
			||||||
 | 
									     res->ob_type->tp_name);
 | 
				
			||||||
 | 
								Py_DECREF(res);
 | 
				
			||||||
 | 
								return NULL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return res;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if (!PyObject_AsCharBuffer(o, &buffer, &buffer_len))
 | 
						if (!PyObject_AsCharBuffer(o, &buffer, &buffer_len))
 | 
				
			||||||
		return long_from_string(buffer, buffer_len);
 | 
							return long_from_string(buffer, buffer_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1047,8 +1065,17 @@ PyNumber_Float(PyObject *o)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (!PyString_Check(o)) {
 | 
						if (!PyString_Check(o)) {
 | 
				
			||||||
		m = o->ob_type->tp_as_number;
 | 
							m = o->ob_type->tp_as_number;
 | 
				
			||||||
		if (m && m->nb_float)
 | 
							if (m && m->nb_float) {
 | 
				
			||||||
			return m->nb_float(o);
 | 
								PyObject *res = m->nb_float(o);
 | 
				
			||||||
 | 
								if (res && !PyFloat_Check(res)) {
 | 
				
			||||||
 | 
									PyErr_Format(PyExc_TypeError,
 | 
				
			||||||
 | 
								          "__float__ returned non-float (type %.200s)",
 | 
				
			||||||
 | 
								          res->ob_type->tp_name);
 | 
				
			||||||
 | 
									Py_DECREF(res);
 | 
				
			||||||
 | 
									return NULL;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return res;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return PyFloat_FromString(o, NULL);
 | 
						return PyFloat_FromString(o, NULL);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -948,12 +948,6 @@ int_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 | 
				
			||||||
	if (tmp == NULL)
 | 
						if (tmp == NULL)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	if (!PyInt_Check(tmp)) {
 | 
						if (!PyInt_Check(tmp)) {
 | 
				
			||||||
		if (!PyLong_Check(tmp)) {
 | 
					 | 
				
			||||||
			PyErr_SetString(PyExc_ValueError,
 | 
					 | 
				
			||||||
					"value can't be converted to int");
 | 
					 | 
				
			||||||
			Py_DECREF(tmp);
 | 
					 | 
				
			||||||
			return NULL;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		ival = PyLong_AsLong(tmp);
 | 
							ival = PyLong_AsLong(tmp);
 | 
				
			||||||
		if (ival == -1 && PyErr_Occurred()) {
 | 
							if (ival == -1 && PyErr_Occurred()) {
 | 
				
			||||||
			Py_DECREF(tmp);
 | 
								Py_DECREF(tmp);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -962,6 +962,7 @@ static PyObject *
 | 
				
			||||||
builtin_hex(PyObject *self, PyObject *v)
 | 
					builtin_hex(PyObject *self, PyObject *v)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	PyNumberMethods *nb;
 | 
						PyNumberMethods *nb;
 | 
				
			||||||
 | 
						PyObject *res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((nb = v->ob_type->tp_as_number) == NULL ||
 | 
						if ((nb = v->ob_type->tp_as_number) == NULL ||
 | 
				
			||||||
	    nb->nb_hex == NULL) {
 | 
						    nb->nb_hex == NULL) {
 | 
				
			||||||
| 
						 | 
					@ -969,7 +970,15 @@ builtin_hex(PyObject *self, PyObject *v)
 | 
				
			||||||
			   "hex() argument can't be converted to hex");
 | 
								   "hex() argument can't be converted to hex");
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return (*nb->nb_hex)(v);
 | 
						res = (*nb->nb_hex)(v);
 | 
				
			||||||
 | 
						if (res && !PyString_Check(res)) {
 | 
				
			||||||
 | 
							PyErr_Format(PyExc_TypeError,
 | 
				
			||||||
 | 
								     "__hex__ returned non-string (type %.200s)",
 | 
				
			||||||
 | 
								     res->ob_type->tp_name);
 | 
				
			||||||
 | 
							Py_DECREF(res);
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PyDoc_STRVAR(hex_doc,
 | 
					PyDoc_STRVAR(hex_doc,
 | 
				
			||||||
| 
						 | 
					@ -1178,6 +1187,7 @@ static PyObject *
 | 
				
			||||||
builtin_oct(PyObject *self, PyObject *v)
 | 
					builtin_oct(PyObject *self, PyObject *v)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	PyNumberMethods *nb;
 | 
						PyNumberMethods *nb;
 | 
				
			||||||
 | 
						PyObject *res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (v == NULL || (nb = v->ob_type->tp_as_number) == NULL ||
 | 
						if (v == NULL || (nb = v->ob_type->tp_as_number) == NULL ||
 | 
				
			||||||
	    nb->nb_oct == NULL) {
 | 
						    nb->nb_oct == NULL) {
 | 
				
			||||||
| 
						 | 
					@ -1185,7 +1195,15 @@ builtin_oct(PyObject *self, PyObject *v)
 | 
				
			||||||
			   "oct() argument can't be converted to oct");
 | 
								   "oct() argument can't be converted to oct");
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return (*nb->nb_oct)(v);
 | 
						res = (*nb->nb_oct)(v);
 | 
				
			||||||
 | 
						if (res && !PyString_Check(res)) {
 | 
				
			||||||
 | 
							PyErr_Format(PyExc_TypeError,
 | 
				
			||||||
 | 
								     "__oct__ returned non-string (type %.200s)",
 | 
				
			||||||
 | 
								     res->ob_type->tp_name);
 | 
				
			||||||
 | 
							Py_DECREF(res);
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PyDoc_STRVAR(oct_doc,
 | 
					PyDoc_STRVAR(oct_doc,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue