Commit 3171ec18 authored by Stefan Behnel's avatar Stefan Behnel

support casting '<bytes>int_val' and coercing integers to bytes on assignment

parent 31f2903e
...@@ -554,7 +554,10 @@ class ExprNode(Node): ...@@ -554,7 +554,10 @@ class ExprNode(Node):
if dst_type.is_pyobject: if dst_type.is_pyobject:
if not src.type.is_pyobject: if not src.type.is_pyobject:
src = CoerceToPyTypeNode(src, env) if dst_type is bytes_type and src.type.is_int:
src = CoerceIntToBytesNode(src, env)
else:
src = CoerceToPyTypeNode(src, env)
if not src.type.subtype_of(dst_type): if not src.type.subtype_of(dst_type):
if not isinstance(src, NoneNode): if not isinstance(src, NoneNode):
src = PyTypeTestNode(src, dst_type, env) src = PyTypeTestNode(src, dst_type, env)
...@@ -4555,7 +4558,12 @@ class TypecastNode(ExprNode): ...@@ -4555,7 +4558,12 @@ class TypecastNode(ExprNode):
if from_py and not to_py and self.operand.is_ephemeral() and not self.type.is_numeric: if from_py and not to_py and self.operand.is_ephemeral() and not self.type.is_numeric:
error(self.pos, "Casting temporary Python object to non-numeric non-Python type") error(self.pos, "Casting temporary Python object to non-numeric non-Python type")
if to_py and not from_py: if to_py and not from_py:
if self.operand.type.can_coerce_to_pyobject(env): if self.type is bytes_type and self.operand.type.is_int:
# FIXME: the type cast node isn't needed in this case
# and can be dropped once analyse_types() can return a
# different node
self.operand = CoerceIntToBytesNode(self.operand, env)
elif self.operand.type.can_coerce_to_pyobject(env):
self.result_ctype = py_object_type self.result_ctype = py_object_type
self.operand = self.operand.coerce_to_pyobject(env) self.operand = self.operand.coerce_to_pyobject(env)
else: else:
...@@ -5614,10 +5622,8 @@ class CmpNode(object): ...@@ -5614,10 +5622,8 @@ class CmpNode(object):
def is_c_string_contains(self): def is_c_string_contains(self):
return self.operator in ('in', 'not_in') and \ return self.operator in ('in', 'not_in') and \
((self.operand1.type in (PyrexTypes.c_char_type, PyrexTypes.c_uchar_type) ((self.operand1.type.is_int
and self.operand2.type in (PyrexTypes.c_char_ptr_type, and (self.operand2.type.is_string or self.operand2.type is bytes_type)) or
PyrexTypes.c_uchar_ptr_type,
bytes_type)) or
(self.operand1.type is PyrexTypes.c_py_unicode_type (self.operand1.type is PyrexTypes.c_py_unicode_type
and self.operand2.type is unicode_type)) and self.operand2.type is unicode_type))
...@@ -6181,7 +6187,7 @@ class CoerceToPyTypeNode(CoercionNode): ...@@ -6181,7 +6187,7 @@ class CoerceToPyTypeNode(CoercionNode):
CoercionNode.__init__(self, arg) CoercionNode.__init__(self, arg)
if not arg.type.create_to_py_utility_code(env): if not arg.type.create_to_py_utility_code(env):
error(arg.pos, error(arg.pos,
"Cannot convert '%s' to Python object" % arg.type) "Cannot convert '%s' to Python object" % arg.type)
if type is not py_object_type: if type is not py_object_type:
self.type = py_object_type self.type = py_object_type
elif arg.type.is_string: elif arg.type.is_string:
...@@ -6217,6 +6223,46 @@ class CoerceToPyTypeNode(CoercionNode): ...@@ -6217,6 +6223,46 @@ class CoerceToPyTypeNode(CoercionNode):
code.put_gotref(self.py_result()) code.put_gotref(self.py_result())
class CoerceIntToBytesNode(CoerceToPyTypeNode):
# This node is used to convert a C int type to a Python bytes
# object.
is_temp = 1
def __init__(self, arg, env):
arg = arg.coerce_to_simple(env)
CoercionNode.__init__(self, arg)
self.type = Builtin.bytes_type
def generate_result_code(self, code):
arg = self.arg
arg_result = arg.result()
if arg.type not in (PyrexTypes.c_char_type,
PyrexTypes.c_uchar_type,
PyrexTypes.c_schar_type):
if arg.type.signed:
code.putln("if ((%s < 0) || (%s > 255)) {" % (
arg_result, arg_result))
else:
code.putln("if (%s > 255) {" % arg_result)
code.putln('PyErr_Format(PyExc_OverflowError, '
'"value too large to pack into a byte"); %s' % (
code.error_goto(self.pos)))
code.putln('}')
temp = None
if arg.type is not PyrexTypes.c_char_type:
temp = code.funcstate.allocate_temp(PyrexTypes.c_char_type, manage_ref=False)
code.putln("%s = (char)%s;" % (temp, arg_result))
arg_result = temp
code.putln('%s = PyBytes_FromStringAndSize(&%s, 1); %s' % (
self.result(),
arg_result,
code.error_goto_if_null(self.result(), self.pos)))
if temp is not None:
code.funcstate.release_temp(temp)
code.put_gotref(self.py_result())
class CoerceFromPyTypeNode(CoercionNode): class CoerceFromPyTypeNode(CoercionNode):
# This node is used to convert a Python object # This node is used to convert a Python object
# to a C data type. # to a C data type.
......
cimport cython
def coerce_char_default(char c):
"""
Default char -> int coercion
>>> coerce_char_default(ord('A')) == ord('A')
True
"""
return c
def coerce_uchar_default(unsigned char c):
"""
Default char -> int coercion
>>> coerce_uchar_default(ord('A')) == ord('A')
True
"""
return c
@cython.test_assert_path_exists("//CoerceIntToBytesNode")
@cython.test_fail_if_path_exists("//CoerceToPyTypeNode")
def coerce_char_bytes_cast(char c):
"""
Explicit char -> bytes coercion
>>> coerce_char_bytes_cast(ord('A')) == 'A'.encode('ASCII')
True
"""
return <bytes>c
@cython.test_assert_path_exists("//CoerceIntToBytesNode")
@cython.test_fail_if_path_exists("//CoerceToPyTypeNode")
def coerce_uchar_bytes_cast(unsigned char c):
"""
Explicit uchar -> bytes coercion
>>> coerce_uchar_bytes_cast(ord('A')) == 'A'.encode('ASCII')
True
>>> b = coerce_uchar_bytes_cast(ord('\\xff'))
>>> b == '\\xff' or b == '\\xff'.encode('ISO-8859-1') # Py2 or Py3
True
"""
return <bytes>c
@cython.test_assert_path_exists("//CoerceIntToBytesNode")
@cython.test_fail_if_path_exists("//CoerceToPyTypeNode")
def coerce_int_bytes_cast(int c):
"""
Explicit int -> bytes coercion
>>> coerce_int_bytes_cast(ord('A')) == 'A'.encode('ASCII')
True
>>> coerce_int_bytes_cast(ord('A') + 0x100)
Traceback (most recent call last):
OverflowError: value too large to pack into a byte
>>> coerce_int_bytes_cast(ord('A') - 0x100)
Traceback (most recent call last):
OverflowError: value too large to pack into a byte
"""
return <bytes>c
@cython.test_assert_path_exists("//CoerceIntToBytesNode")
@cython.test_fail_if_path_exists("//CoerceToPyTypeNode")
def coerce_uint_bytes_cast(unsigned int c):
"""
Explicit uint -> bytes coercion
>>> coerce_uint_bytes_cast(ord('A')) == 'A'.encode('ASCII')
True
>>> b = coerce_uint_bytes_cast(ord('\\xff'))
>>> b == '\\xff' or b == '\\xff'.encode('ISO-8859-1') # Py2 or Py3
True
>>> coerce_uint_bytes_cast(ord('A') + 0x100)
Traceback (most recent call last):
OverflowError: value too large to pack into a byte
"""
return <bytes>c
@cython.test_assert_path_exists("//CoerceIntToBytesNode")
@cython.test_fail_if_path_exists("//CoerceToPyTypeNode")
def coerce_char_bytes_assign(char c):
"""
Implicit char -> bytes coercion in assignments
>>> coerce_char_bytes_assign(ord('A')) == 'A'.encode('ASCII')
True
"""
cdef bytes s = c
return s
@cython.test_assert_path_exists("//CoerceIntToBytesNode")
@cython.test_fail_if_path_exists("//CoerceToPyTypeNode")
def coerce_uchar_bytes_assign(unsigned char c):
"""
Implicit uchar -> bytes coercion in assignments
>>> coerce_uchar_bytes_assign(ord('A')) == 'A'.encode('ASCII')
True
>>> b = coerce_uchar_bytes_assign(ord('\\xff'))
>>> b == '\\xff' or b == '\\xff'.encode('ISO-8859-1') # Py2 or Py3
True
"""
cdef bytes s = c
return s
@cython.test_assert_path_exists("//CoerceIntToBytesNode")
@cython.test_fail_if_path_exists("//CoerceToPyTypeNode")
def coerce_int_bytes_assign(int c):
"""
Implicit int -> bytes coercion in assignments
>>> coerce_int_bytes_assign(ord('A')) == 'A'.encode('ASCII')
True
>>> coerce_int_bytes_assign(ord('A') + 0x100)
Traceback (most recent call last):
OverflowError: value too large to pack into a byte
>>> coerce_int_bytes_assign(ord('A') - 0x100)
Traceback (most recent call last):
OverflowError: value too large to pack into a byte
"""
cdef bytes s = c
return s
@cython.test_assert_path_exists("//CoerceIntToBytesNode")
@cython.test_fail_if_path_exists("//CoerceToPyTypeNode")
def coerce_uint_bytes_assign(unsigned int c):
"""
Implicit uint -> bytes coercion in assignments
>>> coerce_uint_bytes_assign(ord('A')) == 'A'.encode('ASCII')
True
>>> b = coerce_uint_bytes_assign(ord('\\xff'))
>>> b == '\\xff' or b == '\\xff'.encode('ISO-8859-1') # Py2 or Py3
True
>>> coerce_uint_bytes_assign(ord('A') + 0x100)
Traceback (most recent call last):
OverflowError: value too large to pack into a byte
"""
cdef bytes s = c
return s
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment