From 3171ec18c17762adb38734bf925e1953c5ebbe16 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 27 Apr 2010 07:34:45 +0200 Subject: [PATCH] support casting 'int_val' and coercing integers to bytes on assignment --- Cython/Compiler/ExprNodes.py | 60 ++++++++++-- tests/run/bytes_char_coercion.pyx | 154 ++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+), 7 deletions(-) create mode 100644 tests/run/bytes_char_coercion.pyx diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 27ace483..9631ad6a 100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -554,7 +554,10 @@ class ExprNode(Node): if dst_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 isinstance(src, NoneNode): src = PyTypeTestNode(src, dst_type, env) @@ -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: error(self.pos, "Casting temporary Python object to non-numeric non-Python type") 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.operand = self.operand.coerce_to_pyobject(env) else: @@ -5614,10 +5622,8 @@ class CmpNode(object): def is_c_string_contains(self): return self.operator in ('in', 'not_in') and \ - ((self.operand1.type in (PyrexTypes.c_char_type, PyrexTypes.c_uchar_type) - and self.operand2.type in (PyrexTypes.c_char_ptr_type, - PyrexTypes.c_uchar_ptr_type, - bytes_type)) or + ((self.operand1.type.is_int + and (self.operand2.type.is_string or self.operand2.type is bytes_type)) or (self.operand1.type is PyrexTypes.c_py_unicode_type and self.operand2.type is unicode_type)) @@ -6181,7 +6187,7 @@ class CoerceToPyTypeNode(CoercionNode): CoercionNode.__init__(self, arg) if not arg.type.create_to_py_utility_code(env): 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: self.type = py_object_type elif arg.type.is_string: @@ -6217,6 +6223,46 @@ class CoerceToPyTypeNode(CoercionNode): 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): # This node is used to convert a Python object # to a C data type. diff --git a/tests/run/bytes_char_coercion.pyx b/tests/run/bytes_char_coercion.pyx new file mode 100644 index 00000000..82c63e79 --- /dev/null +++ b/tests/run/bytes_char_coercion.pyx @@ -0,0 +1,154 @@ + +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 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 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 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 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 -- 2.26.2