diff -r 9b450b19aa11 Lib/test/test_struct.py --- a/Lib/test/test_struct.py Sat Jul 12 03:20:40 2014 +0200 +++ b/Lib/test/test_struct.py Mon Jul 21 10:07:15 2014 +0200 @@ -487,6 +487,28 @@ value, = struct.unpack('>I', data) self.assertEqual(value, 0x12345678) + def test_native_bool(self): + # See issue 22012 for more information + sz = struct.calcsize('@?') + + buf = b'\x00'*(sz) + self.assertEqual(struct.unpack('@?', buf), (False,)) + + buf = b'\x00'*(sz) + if sys.byteorder == 'little': + buf = (buf + b'\x01')[1:] + else: + buf = (b'\x01' + buf)[1:] + self.assertEqual(struct.unpack('@?', buf), (True,)) + + buf = b'\x00'*(sz) + if sys.byteorder == 'little': + buf = (buf + b'\x02')[1:] + else: + buf = (b'\x02' + buf)[1:] + self.assertEqual(struct.unpack('@?', buf), (True,)) + + def test_bool(self): class ExplodingBool(object): def __bool__(self): diff -r 9b450b19aa11 Modules/_struct.c --- a/Modules/_struct.c Sat Jul 12 03:20:40 2014 +0200 +++ b/Modules/_struct.c Mon Jul 21 10:07:15 2014 +0200 @@ -463,7 +463,40 @@ static PyObject * nu_bool(const char *p, const formatdef *f) { + /* + * The documentation says that any non-zero value + * will be converted to True. The C standard says that + * _Bool values can only be 0 or 1, and some compilers + * (in particular clang) ignore anything but the least + * significant bit of the _Bool value. + * + * The size of _Bool might not be 1 either, hence we cannot + * use bu_bool here. + */ +#ifdef HAVE_C99_BOOL + +#if SIZEOF__BOOL == 1 + unsigned char x; + +#elif SIZEOF__BOOL == SIZEOF_SHORT + unsigned short x; + +#elif SIZEOF__BOOL == SIZEOF_INT + unsigned int x; + +#elif SIZEOF__BOOL == SIZEOF_LONG + unsigned long x; + +#else + +# error "Don't know how to convert _Bool" +#endif + +#else /* !HAVE_C99_BOOL */ BOOL_TYPE x; + +#endif + memcpy((char *)&x, p, sizeof x); return PyBool_FromLong(x != 0); }