[Python-3000] PyLong_Check() behaviour clarification (test_getargs2 failing on Windows x64)

Neal Norwitz nnorwitz at gmail.com
Fri Mar 21 05:51:06 CET 2008


On Thu, Mar 20, 2008 at 2:13 AM, Trent Nelson <tnelson at onresolve.com> wrote:
> test_getargs2 is failing on Windows x64:
> test test_getargs2 failed -- Traceback (most recent call last):
> File "S:\buildbots\python.x643円.0.nelson-win64\build\lib\test\test_getargs2.py", line 190, in test_n
> self.failUnlessEqual(99, getargs_n(Long()))
> TypeError: 'Long' object cannot be interpreted as an integer
>> Drilling down into the source, this code arrives at Python/getargs.c:convertsimple(). On Windows x64, SIZEOF_SIZE_T != SIZEOF_LONG, so there's a case 'n' statement that is defined that does the following:
>> iobj = PyNumber_Index(arg);
>> I'm a little confused. At this point, arg is a PyObject *, and arg->obj_type->tp_as_number->nb_int has a non-zero value. However, PyNumber_Index(arg) performs two checks, and this object fails both, which results in the TypeError being raised.

PyNumber_Index() works on an object that is an int or long or has an
__index__ method. The "Long" class has only an __int__ method. It is
otherwise a regular user-defined class that has no super class.
> The first check is PyLong_Check(arg), which is handled via PyType_FastSubclass, which ends up doing the following:
>> ((arg->obj_type->tp_flags & Py_TPFLAGS_LONG_SUBCLASS) != 0)
>> tp_flags is 284160 at this point. Py_TPFLAGS_LONG_SUBCLASS is 16777216 and Py_TPFLAGS_INT_SUBCLASS is 8388608, so this check fails. Why doesn't tp_flags have either one of these values here? obj_type->nb_int has a value, so shouldn't flags reflect the type?

Despite the class name of "Long", the object doesn't subclass int or
long. That's why the tp_flags checks are failing.
nb_int has a value because of the __int__ method defined on Long.
> I've just checked a 32-bit build and PyNumber_Index for a PyObject * representing a literal 99 also exhibits the same behaviour as above.
>> Also, if we're a 32-bit build, the 'n' code path visits PyLong_AsLong(), which calls PyLong_AsLongAndOverflow() -- the test cases calling getargs_n() test for the overflow being raised correctly. Notionally, on x64 Windows where ((size_t == long long) > long), shouldn't we have a PyLong_AsLongLongAndOverflow() method?

The APIs seem very confused. I'm not sure that is the best way to go
either. Conceptually from what the test is trying to do, we need to
convert from a user-defined type (Long) to an PyObject (Python long or
PyLongObject) and then convert that to a Py_ssize_t. I'm not sure the
test makes the most sense though. There should be a test added that
uses a value between 2**32 and 2**64 to ensure we cover the win64
case.
I think the best thing to do for now (we need to clean up all the APIs
and have them make sense) is change the code that handles 'n' as
follows:
 {
 PyObject *iobj;
 Py_ssize_t *p = va_arg(*p_va, Py_ssize_t *);
 Py_ssize_t ival = -1;
 if (float_argument_error(arg))
 return converterr("integer<n>", arg, msgbuf, bufsize);
 iobj = PyNumber_Index(arg);
 /* Code added here to handle __int__ methods on
classes which don't subclass int. */
 if (iobj == NULL) {
 PyErr_Clear();
 /* Using PyNumber_Long() here is probably too
permissive. It converts strings and objects with __trunc__. Probably
we should just check the nb_int (or is it nb_long in 3k?) slot
specifically and call that? */
 iobj = PyNumber_Long(arg);
 }
 if (iobj != NULL)
 ival = PyLong_AsSsize_t(arg);
 if (ival == -1 && PyErr_Occurred())
 return converterr("integer<n>", arg, msgbuf, bufsize);
 *p = ival;
 break;
 }
I'm not sure if we are abusing the 'n' format here or not. The doc
(Doc/c-api/arg.rst) just says it turns the arg into a Py_ssize_t. I
don't know if __index__ should really be (ab)used or not. PEP 357
added __index__.
n


More information about the Python-3000 mailing list

AltStyle によって変換されたページ (->オリジナル) /