Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
PyPy
cffi
Commits
d05a37bd9b1f
Commit
ea197e78
authored
Jan 23, 2017
by
Armin Rigo
Browse files
issue300: return _Bool as Python booleans; related fixes
parent
b0204d9d8b96
Changes
4
Hide whitespace changes
Inline
Side-by-side
c/_cffi_backend.c
View file @
d05a37bd
...
...
@@ -970,8 +970,23 @@ convert_to_object(char *data, CTypeDescrObject *ct)
/*READ(data, ct->ct_size)*/
value
=
read_raw_unsigned_data
(
data
,
ct
->
ct_size
);
if
(
ct
->
ct_flags
&
CT_PRIMITIVE_FITS_LONG
)
if
(
ct
->
ct_flags
&
CT_PRIMITIVE_FITS_LONG
)
{
if
(
ct
->
ct_flags
&
CT_IS_BOOL
)
{
PyObject
*
x
;
switch
((
int
)
value
)
{
case
0
:
x
=
Py_False
;
break
;
case
1
:
x
=
Py_True
;
break
;
default:
PyErr_Format
(
PyExc_ValueError
,
"got a _Bool of value %d, expected 0 or 1"
,
(
int
)
value
);
return
NULL
;
}
Py_INCREF
(
x
);
return
x
;
}
return
PyInt_FromLong
((
long
)
value
);
}
else
return
PyLong_FromUnsignedLongLong
(
value
);
}
...
...
@@ -1207,6 +1222,20 @@ convert_vfield_from_object(char *data, CFieldObject *cf, PyObject *value,
return
0
;
}
static
int
must_be_array_of_zero_or_one
(
const
char
*
data
,
Py_ssize_t
n
)
{
Py_ssize_t
i
;
for
(
i
=
0
;
i
<
n
;
i
++
)
{
if
(((
unsigned
char
)
data
[
i
])
>
1
)
{
PyErr_SetString
(
PyExc_ValueError
,
"an array of _Bool can only contain
\\
x00 or
\\
x01"
);
return
-
1
;
}
}
return
0
;
}
static
int
convert_array_from_object
(
char
*
data
,
CTypeDescrObject
*
ct
,
PyObject
*
init
)
{
...
...
@@ -1254,6 +1283,9 @@ convert_array_from_object(char *data, CTypeDescrObject *ct, PyObject *init)
if
(
n
!=
ct
->
ct_length
)
n
++
;
srcdata
=
PyBytes_AS_STRING
(
init
);
if
(
ctitem
->
ct_flags
&
CT_IS_BOOL
)
if
(
must_be_array_of_zero_or_one
(
srcdata
,
n
)
<
0
)
return
-
1
;
memcpy
(
data
,
srcdata
,
n
);
return
0
;
}
...
...
@@ -1424,12 +1456,15 @@ convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init)
unsigned
PY_LONG_LONG
value
=
_my_PyLong_AsUnsignedLongLong
(
init
,
1
);
if
(
value
==
(
unsigned
PY_LONG_LONG
)
-
1
&&
PyErr_Occurred
())
return
-
1
;
if
(
ct
->
ct_flags
&
CT_IS_BOOL
)
if
(
value
&
~
1
)
/* value != 0 && value != 1 */
if
(
ct
->
ct_flags
&
CT_IS_BOOL
)
{
if
(
value
>
1ULL
)
/* value != 0 && value != 1 */
goto
overflow
;
write_raw_integer_data
(
buf
,
value
,
ct
->
ct_size
);
if
(
value
!=
read_raw_unsigned_data
(
buf
,
ct
->
ct_size
))
goto
overflow
;
}
else
{
write_raw_integer_data
(
buf
,
value
,
ct
->
ct_size
);
if
(
value
!=
read_raw_unsigned_data
(
buf
,
ct
->
ct_size
))
goto
overflow
;
}
write_raw_integer_data
(
data
,
value
,
ct
->
ct_size
);
return
0
;
}
...
...
@@ -2544,6 +2579,10 @@ _prepare_pointer_call_argument(CTypeDescrObject *ctptr, PyObject *init,
length
=
PyBytes_GET_SIZE
(
init
)
+
1
;
#else
*
output_data
=
PyBytes_AS_STRING
(
init
);
if
(
ctitem
->
ct_flags
&
CT_IS_BOOL
)
if
(
must_be_array_of_zero_or_one
(
*
output_data
,
PyBytes_GET_SIZE
(
init
))
<
0
)
return
-
1
;
return
0
;
#endif
}
...
...
@@ -5740,7 +5779,8 @@ static PyObject *b_string(PyObject *self, PyObject *args, PyObject *kwds)
if
(
cd
->
c_type
->
ct_itemdescr
!=
NULL
&&
cd
->
c_type
->
ct_itemdescr
->
ct_flags
&
(
CT_PRIMITIVE_CHAR
|
CT_PRIMITIVE_SIGNED
|
CT_PRIMITIVE_UNSIGNED
))
{
CT_PRIMITIVE_UNSIGNED
)
&&
!
(
cd
->
c_type
->
ct_itemdescr
->
ct_flags
&
CT_IS_BOOL
))
{
Py_ssize_t
length
=
maxlen
;
if
(
cd
->
c_data
==
NULL
)
{
PyObject
*
s
=
cdata_repr
(
cd
);
...
...
@@ -5904,7 +5944,8 @@ static PyObject *b_unpack(PyObject *self, PyObject *args, PyObject *kwds)
else
if
(
itemsize
==
sizeof
(
short
))
casenum
=
1
;
else
if
(
itemsize
==
sizeof
(
signed
char
))
casenum
=
0
;
}
else
if
(
ctitem
->
ct_flags
&
CT_PRIMITIVE_UNSIGNED
)
{
else
if
((
ctitem
->
ct_flags
&
(
CT_PRIMITIVE_UNSIGNED
|
CT_IS_BOOL
))
==
CT_PRIMITIVE_UNSIGNED
)
{
/* Note: we never pick case 6 if sizeof(int) == sizeof(long),
so that case 6 below can assume that the 'unsigned int' result
would always fit in a 'signed long'. */
...
...
c/test_c.py
View file @
d05a37bd
...
...
@@ -896,6 +896,15 @@ def test_call_function_0():
py
.
test
.
raises
(
OverflowError
,
f
,
128
,
0
)
py
.
test
.
raises
(
OverflowError
,
f
,
0
,
128
)
def
test_call_function_0_pretend_bool_result
():
BSignedChar
=
new_primitive_type
(
"signed char"
)
BBool
=
new_primitive_type
(
"_Bool"
)
BFunc0
=
new_function_type
((
BSignedChar
,
BSignedChar
),
BBool
,
False
)
f
=
cast
(
BFunc0
,
_testfunc
(
0
))
assert
f
(
40
,
-
39
)
is
True
assert
f
(
40
,
-
40
)
is
False
py
.
test
.
raises
(
ValueError
,
f
,
40
,
2
)
def
test_call_function_1
():
BInt
=
new_primitive_type
(
"int"
)
BLong
=
new_primitive_type
(
"long"
)
...
...
@@ -1058,6 +1067,17 @@ def test_call_function_23_bis():
res
=
f
(
b
"foo"
)
assert
res
==
1000
*
ord
(
b
'f'
)
def
test_call_function_23_bool_array
():
# declaring the function as int(_Bool*)
BBool
=
new_primitive_type
(
"_Bool"
)
BBoolP
=
new_pointer_type
(
BBool
)
BInt
=
new_primitive_type
(
"int"
)
BFunc23
=
new_function_type
((
BBoolP
,),
BInt
,
False
)
f
=
cast
(
BFunc23
,
_testfunc
(
23
))
res
=
f
(
b
"
\x01\x01
"
)
assert
res
==
1000
py
.
test
.
raises
(
ValueError
,
f
,
b
"
\x02\x02
"
)
def
test_cannot_pass_struct_with_array_of_length_0
():
BInt
=
new_primitive_type
(
"int"
)
BArray0
=
new_array_type
(
new_pointer_type
(
BInt
),
0
)
...
...
@@ -2628,13 +2648,38 @@ def test_bool():
py
.
test
.
raises
(
OverflowError
,
newp
,
BBoolP
,
2
)
py
.
test
.
raises
(
OverflowError
,
newp
,
BBoolP
,
-
1
)
BCharP
=
new_pointer_type
(
new_primitive_type
(
"char"
))
p
=
newp
(
BCharP
,
b
'
X
'
)
p
=
newp
(
BCharP
,
b
'
\x01
'
)
q
=
cast
(
BBoolP
,
p
)
assert
q
[
0
]
==
ord
(
b
'X'
)
assert
q
[
0
]
is
True
p
=
newp
(
BCharP
,
b
'
\x00
'
)
q
=
cast
(
BBoolP
,
p
)
assert
q
[
0
]
is
False
py
.
test
.
raises
(
TypeError
,
string
,
cast
(
BBool
,
False
))
BDouble
=
new_primitive_type
(
"double"
)
assert
int
(
cast
(
BBool
,
cast
(
BDouble
,
0.1
)))
==
1
assert
int
(
cast
(
BBool
,
cast
(
BDouble
,
0.0
)))
==
0
BBoolA
=
new_array_type
(
BBoolP
,
None
)
p
=
newp
(
BBoolA
,
b
'
\x01\x00
'
)
assert
p
[
0
]
is
True
assert
p
[
1
]
is
False
def
test_bool_forbidden_cases
():
BBool
=
new_primitive_type
(
"_Bool"
)
BBoolP
=
new_pointer_type
(
BBool
)
BBoolA
=
new_array_type
(
BBoolP
,
None
)
BCharP
=
new_pointer_type
(
new_primitive_type
(
"char"
))
p
=
newp
(
BCharP
,
b
'X'
)
q
=
cast
(
BBoolP
,
p
)
py
.
test
.
raises
(
ValueError
,
"q[0]"
)
py
.
test
.
raises
(
TypeError
,
newp
,
BBoolP
,
b
'
\x00
'
)
assert
newp
(
BBoolP
,
0
)[
0
]
is
False
assert
newp
(
BBoolP
,
1
)[
0
]
is
True
py
.
test
.
raises
(
OverflowError
,
newp
,
BBoolP
,
2
)
py
.
test
.
raises
(
OverflowError
,
newp
,
BBoolP
,
-
1
)
py
.
test
.
raises
(
ValueError
,
newp
,
BBoolA
,
b
'
\x00\x01\x02
'
)
py
.
test
.
raises
(
OverflowError
,
newp
,
BBoolA
,
[
0
,
1
,
2
])
py
.
test
.
raises
(
TypeError
,
string
,
newp
(
BBoolP
,
1
))
py
.
test
.
raises
(
TypeError
,
string
,
newp
(
BBoolA
,
[
1
]))
def
test_typeoffsetof
():
BChar
=
new_primitive_type
(
"char"
)
...
...
@@ -3674,7 +3719,7 @@ def test_unpack():
(
"int16_t"
,
[
-
2
**
15
,
2
**
15
-
1
]),
(
"int32_t"
,
[
-
2
**
31
,
2
**
31
-
1
]),
(
"int64_t"
,
[
-
2
**
63
,
2
**
63
-
1
]),
(
"_Bool"
,
[
0
,
1
]),
(
"_Bool"
,
[
False
,
True
]),
(
"float"
,
[
0.0
,
10.5
]),
(
"double"
,
[
12.34
,
56.78
]),
]:
...
...
doc/source/ref.rst
View file @
d05a37bd
...
...
@@ -597,7 +597,8 @@ allowed.
| integers | an integer or anything | a Python int or | int(), bool() |
| and enums | on which int() works | long, depending | `(******)` |
| `(*****)` | (but not a float!). | on the type | |
| | Must be within range. | | |
| | Must be within range. | (ver. 1.10: or a | |
| | | bool) | |
+---------------+------------------------+------------------+----------------+
| ``char`` | a string of length 1 | a string of | int(), bool() |
| | or another <cdata char>| length 1 | |
...
...
@@ -637,12 +638,13 @@ allowed.
| | items | |``[]`` `(****)`,|
| | | |``+``, ``-`` |
+---------------+------------------------+ +----------------+
| ``char[]`` | same as arrays, or a | | len(), iter(), |
| | Python string | | ``[]``, ``+``, |
| | | | ``-`` |
| ``char[]``, | same as arrays, or a | | len(), iter(), |
| ``un/signed`` | Python byte string | | ``[]``, ``+``, |
| ``char[]``, | | | ``-`` |
| ``_Bool[]`` | | | |
+---------------+------------------------+ +----------------+
| ``wchar_t[]`` | same as arrays, or a | | len(), iter(), |
| | Python unicode
| | ``[]``, |
| | Python unicode
string
| | ``[]``, |
| | | | ``+``, ``-`` |
| | | | |
+---------------+------------------------+------------------+----------------+
...
...
@@ -727,6 +729,14 @@ allowed.
*New in version 1.7.* In previous versions, it only worked on
pointers; for primitives it always returned True.
*New in version 1.10:* The C type ``_Bool`` or ``bool`` converts to
Python booleans now. You get an exception if a C ``_Bool`` happens
to contain a value different from 0 and 1 (this case triggers
undefined behavior in C; if you really have to interface with a
library relying on this, don't use ``_Bool`` in the CFFI side).
Also, when converting from a byte string to a ``_Bool[]``, only the
bytes ``\x00`` and ``\x01`` are accepted.
.. _file:
Support for FILE
...
...
doc/source/whatsnew.rst
View file @
d05a37bd
...
...
@@ -31,6 +31,19 @@ v1.10
been fixed and the unicode strings don't support the memoryview
interface any more.)
* The C type ``_Bool`` or ``bool`` now converts to a Python boolean
when reading, instead of the content of the byte as an integer. The
change here is mostly what occurs if the byte happens to contain a
value different from 0 and 1. Previously, it would just return it;
with this change, CFFI raises an exception in this case. But this
case means "undefined behavior" in C; if you really have to interface
with a library relying on this, don't use ``_Bool`` in the CFFI side.
Also, it is still valid to use a byte string as initializer for a
``_Bool[]``, but now it must only contain ``\x00`` or ``\x01``. As an
aside, ``ffi.string()`` no longer works on ``_Bool[]`` (but it never
made much sense, as this function stops on the first zero).
v1.9
====
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment