Cython Changelog
================
+0.29.6 (2019-02-27)
+===================
+
+Bugs fixed
+----------
+
+* Fix a crash when accessing the ``__kwdefaults__`` special attribute of
+ fused functions. (Github issue #1470)
+
+* Fix the parsing of buffer format strings that contain numeric sizes, which
+ could lead to incorrect input rejections. (Github issue #2845)
+
+* Avoid a C #pragma in old gcc versions that was only added in GCC 4.6.
+ Patch by Michael Anselmi. (Github issue #2838)
+
+* Auto-encoding of Unicode strings to UTF-8 C/C++ strings failed in Python 3,
+ even though the default encoding there is UTF-8.
+ (Github issue #2819)
+
+
0.29.5 (2019-02-09)
===================
if self.defaults_kwdict:
code.putln('__Pyx_CyFunction_SetDefaultsKwDict(%s, %s);' % (
self.result(), self.defaults_kwdict.py_result()))
- if def_node.defaults_getter:
+ if def_node.defaults_getter and not self.specialized_cpdefs:
+ # Fused functions do not support dynamic defaults, only their specialisations can have them for now.
code.putln('__Pyx_CyFunction_SetDefaultsGetter(%s, %s);' % (
self.result(), def_node.defaults_getter.entry.pyfunc_cname))
if self.annotations_dict:
def generate_evaluation_code(self, code):
if not self.type.is_pyobject and not self.type.is_complex:
if self.cdivision is None:
- self.cdivision = (code.globalstate.directives['cdivision']
- or not self.type.signed
- or self.type.is_float)
+ self.cdivision = (
+ code.globalstate.directives['cdivision']
+ or self.type.is_float
+ or ((self.type.is_numeric or self.type.is_enum) and not self.type.signed)
+ )
if not self.cdivision:
code.globalstate.use_utility_code(
UtilityCode.load_cached("DivInt", "CMath.c").specialize(self.type))
if c_string_type not in ('bytes', 'bytearray') and not c_string_encoding:
error(self.pos, "a default encoding must be provided if c_string_type is not a byte type")
code.putln('#define __PYX_DEFAULT_STRING_ENCODING_IS_ASCII %s' % int(c_string_encoding == 'ascii'))
+ code.putln('#define __PYX_DEFAULT_STRING_ENCODING_IS_UTF8 %s' %
+ int(c_string_encoding.replace('-', '').lower() == 'utf8'))
if c_string_encoding == 'default':
code.putln('#define __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT 1')
else:
- code.putln('#define __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT 0')
+ code.putln('#define __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT '
+ '(PY_MAJOR_VERSION >= 3 && __PYX_DEFAULT_STRING_ENCODING_IS_UTF8)')
code.putln('#define __PYX_DEFAULT_STRING_ENCODING "%s"' % c_string_encoding)
if c_string_type == 'bytearray':
c_string_func_name = 'ByteArray'
if docstr.is_unicode:
docstr = docstr.as_utf8_string()
- code.putln(
- 'static char %s[] = %s;' % (
+ if not (entry.is_special and entry.name in ('__getbuffer__', '__releasebuffer__')):
+ code.putln('static char %s[] = %s;' % (
entry.doc_cname,
docstr.as_c_string_literal()))
" || (Py_TYPE(%s)->tp_flags & (Py_TPFLAGS_IS_ABSTRACT | Py_TPFLAGS_HEAPTYPE)))) {" % (
self_arg, self_arg))
- code.putln("#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_PYTYPE_LOOKUP")
+ code.putln("#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_PYTYPE_LOOKUP && CYTHON_USE_TYPE_SLOTS")
code.globalstate.use_utility_code(
UtilityCode.load_cached("PyDictVersioning", "ObjectHandling.c"))
# TODO: remove the object dict version check by 'inlining' the getattr implementation for methods.
# NOTE: it's not 100% sure that we catch the exact versions here that were used for the lookup,
# but it is very unlikely that the versions change during lookup, and the type dict safe guard
# should increase the chance of detecting such a case.
- code.putln("#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_PYTYPE_LOOKUP")
+ code.putln("#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_PYTYPE_LOOKUP && CYTHON_USE_TYPE_SLOTS")
code.putln("%s = __Pyx_get_tp_dict_version(%s);" % (
Naming.tp_dict_version_temp, self_arg))
code.putln("%s = __Pyx_get_object_dict_version(%s);" % (
code.put_decref_clear(func_node_temp, PyrexTypes.py_object_type)
code.funcstate.release_temp(func_node_temp)
- code.putln("#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_PYTYPE_LOOKUP")
+ code.putln("#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_PYTYPE_LOOKUP && CYTHON_USE_TYPE_SLOTS")
code.putln("}")
code.putln("#endif")
# cython.* namespace for pure mode.
from __future__ import absolute_import
-__version__ = "0.29.5"
+__version__ = "0.29.6"
try:
from __builtin__ import basestring
return -1;
} else {
count = *t++ - '0';
- while (*t >= '0' && *t < '9') {
+ while (*t >= '0' && *t <= '9') {
count *= 10;
count += *t++ - '0';
}
//////////////////// ClassMethod.proto ////////////////////
#include "descrobject.h"
-static PyObject* __Pyx_Method_ClassMethod(PyObject *method); /*proto*/
+static CYTHON_UNUSED PyObject* __Pyx_Method_ClassMethod(PyObject *method); /*proto*/
//////////////////// ClassMethod ////////////////////
/////////////// PyDictVersioning.proto ///////////////
-#if CYTHON_USE_DICT_VERSIONS
+#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_TYPE_SLOTS
#define __PYX_DICT_VERSION_INIT ((PY_UINT64_T) -1)
#define __PYX_GET_DICT_VERSION(dict) (((PyDictObject*)(dict))->ma_version_tag)
#define __PYX_UPDATE_DICT_CACHE(dict, value, cache_var, version_var) \
/////////////// PyDictVersioning ///////////////
-#if CYTHON_USE_DICT_VERSIONS
+#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_TYPE_SLOTS
static CYTHON_INLINE PY_UINT64_T __Pyx_get_tp_dict_version(PyObject *obj) {
PyObject *dict = Py_TYPE(obj)->tp_dict;
- return dict ? __PYX_GET_DICT_VERSION(dict) : 0;
+ return likely(dict) ? __PYX_GET_DICT_VERSION(dict) : 0;
}
static CYTHON_INLINE PY_UINT64_T __Pyx_get_object_dict_version(PyObject *obj) {
PyObject **dictptr = NULL;
Py_ssize_t offset = Py_TYPE(obj)->tp_dictoffset;
if (offset) {
- dictptr = (offset > 0) ? (PyObject **) ((char *)obj + offset) : _PyObject_GetDictPtr(obj);
+#if CYTHON_COMPILING_IN_CPYTHON
+ dictptr = (likely(offset > 0)) ? (PyObject **) ((char *)obj + offset) : _PyObject_GetDictPtr(obj);
+#else
+ dictptr = _PyObject_GetDictPtr(obj);
+#endif
}
return (dictptr && *dictptr) ? __PYX_GET_DICT_VERSION(*dictptr) : 0;
}
static CYTHON_INLINE int __Pyx_object_dict_version_matches(PyObject* obj, PY_UINT64_T tp_dict_version, PY_UINT64_T obj_dict_version) {
PyObject *dict = Py_TYPE(obj)->tp_dict;
- if (!dict || tp_dict_version != __PYX_GET_DICT_VERSION(dict))
+ if (unlikely(!dict) || unlikely(tp_dict_version != __PYX_GET_DICT_VERSION(dict)))
return 0;
return obj_dict_version == __Pyx_get_object_dict_version(obj);
}
#define __Pyx_PyObject_PopIndex(L, py_ix, ix, is_signed, type, to_py_func) ( \
(likely(PyList_CheckExact(L) && __Pyx_fits_Py_ssize_t(ix, type, is_signed))) ? \
__Pyx__PyList_PopIndex(L, py_ix, ix) : ( \
- (unlikely(py_ix == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \
+ (unlikely((py_ix) == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \
__Pyx__PyObject_PopIndex(L, py_ix)))
#define __Pyx_PyList_PopIndex(L, py_ix, ix, is_signed, type, to_py_func) ( \
__Pyx_fits_Py_ssize_t(ix, type, is_signed) ? \
__Pyx__PyList_PopIndex(L, py_ix, ix) : ( \
- (unlikely(py_ix == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \
+ (unlikely((py_ix) == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \
__Pyx__PyObject_PopIndex(L, py_ix)))
#else
__Pyx_PyObject_PopIndex(L, py_ix, ix, is_signed, type, to_py_func)
#define __Pyx_PyObject_PopIndex(L, py_ix, ix, is_signed, type, to_py_func) ( \
- (unlikely(py_ix == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \
+ (unlikely((py_ix) == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \
__Pyx__PyObject_PopIndex(L, py_ix))
#endif
#define __Pyx_div_const_no_overflow(a, b, overflow) ((a) / (b))
/////////////// Common.init ///////////////
+//@substitute: naming
-__Pyx_check_twos_complement();
+// FIXME: Propagate the error here instead of just printing it.
+if (unlikely(__Pyx_check_twos_complement())) {
+ PyErr_WriteUnraisable($module_cname);
+}
/////////////// BaseCaseUnsigned.proto ///////////////
/////////////// SizeCheck.init ///////////////
+//@substitute: naming
-__Pyx_check_sane_{{NAME}}();
+// FIXME: Propagate the error here instead of just printing it.
+if (unlikely(__Pyx_check_sane_{{NAME}}())) {
+ PyErr_WriteUnraisable($module_cname);
+}
/////////////// SizeCheck.proto ///////////////
// NOTE: inlining because most arguments are constant, which collapses lots of code below
+// GCC diagnostic pragmas were introduced in GCC 4.6
+#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#define GCC_DIAGNOSTIC
+#endif
static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value, Py_ssize_t width, char padding_char, char format_char) {
// simple and conservative C string allocation on the stack: each byte gives at most 3 digits, plus sign
char digits[sizeof({{TYPE}})*3+2];
Py_ssize_t length, ulength;
int prepend_sign, last_one_off;
{{TYPE}} remaining;
-#ifdef __GNUC__
+#ifdef GCC_DIAGNOSTIC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
const {{TYPE}} neg_one = ({{TYPE}}) -1, const_zero = ({{TYPE}}) 0;
-#ifdef __GNUC__
+#ifdef GCC_DIAGNOSTIC
#pragma GCC diagnostic pop
#endif
const int is_unsigned = neg_one > const_zero;
-# queue.pyx\r
-\r
-cimport cqueue\r
-\r
-cdef class Queue:\r
- """A queue class for C integer values.\r
-\r
- >>> q = Queue()\r
- >>> q.append(5)\r
- >>> q.peek()\r
- 5\r
- >>> q.pop()\r
- 5\r
- """\r
- cdef cqueue.Queue* _c_queue\r
- def __cinit__(self):\r
- self._c_queue = cqueue.queue_new()\r
- if self._c_queue is NULL:\r
- raise MemoryError()\r
-\r
- def __dealloc__(self):\r
- if self._c_queue is not NULL:\r
- cqueue.queue_free(self._c_queue)\r
-\r
- cpdef append(self, int value):\r
- if not cqueue.queue_push_tail(self._c_queue,\r
- <void*> value):\r
- raise MemoryError()\r
-\r
- # The `cpdef` feature is obviously not available for the original "extend()"\r
- # method, as the method signature is incompatible with Python argument\r
- # types (Python does not have pointers). However, we can rename\r
- # the C-ish "extend()" method to e.g. "extend_ints()", and write\r
- # a new "extend()" method that provides a suitable Python interface by\r
- # accepting an arbitrary Python iterable.\r
- cpdef extend(self, values):\r
- for value in values:\r
- self.append(value)\r
-\r
- cdef extend_ints(self, int* values, size_t count):\r
- cdef int value\r
- for value in values[:count]: # Slicing pointer to limit the iteration boundaries.\r
- self.append(value)\r
-\r
- cpdef int peek(self) except? -1:\r
- cdef int value = <Py_ssize_t> cqueue.queue_peek_head(self._c_queue)\r
-\r
- if value == 0:\r
- # this may mean that the queue is empty,\r
- # or that it happens to contain a 0 value\r
- if cqueue.queue_is_empty(self._c_queue):\r
- raise IndexError("Queue is empty")\r
- return value\r
-\r
- cpdef int pop(self) except? -1:\r
- if cqueue.queue_is_empty(self._c_queue):\r
- raise IndexError("Queue is empty")\r
- return <Py_ssize_t> cqueue.queue_pop_head(self._c_queue)\r
-\r
- def __bool__(self):\r
- return not cqueue.queue_is_empty(self._c_queue)\r
+# queue.pyx
+
+cimport cqueue
+
+cdef class Queue:
+ """A queue class for C integer values.
+
+ >>> q = Queue()
+ >>> q.append(5)
+ >>> q.peek()
+ 5
+ >>> q.pop()
+ 5
+ """
+ cdef cqueue.Queue* _c_queue
+ def __cinit__(self):
+ self._c_queue = cqueue.queue_new()
+ if self._c_queue is NULL:
+ raise MemoryError()
+
+ def __dealloc__(self):
+ if self._c_queue is not NULL:
+ cqueue.queue_free(self._c_queue)
+
+ cpdef append(self, int value):
+ if not cqueue.queue_push_tail(self._c_queue,
+ <void*> <Py_ssize_t> value):
+ raise MemoryError()
+
+ # The `cpdef` feature is obviously not available for the original "extend()"
+ # method, as the method signature is incompatible with Python argument
+ # types (Python does not have pointers). However, we can rename
+ # the C-ish "extend()" method to e.g. "extend_ints()", and write
+ # a new "extend()" method that provides a suitable Python interface by
+ # accepting an arbitrary Python iterable.
+ cpdef extend(self, values):
+ for value in values:
+ self.append(value)
+
+ cdef extend_ints(self, int* values, size_t count):
+ cdef int value
+ for value in values[:count]: # Slicing pointer to limit the iteration boundaries.
+ self.append(value)
+
+ cpdef int peek(self) except? -1:
+ cdef int value = <Py_ssize_t> cqueue.queue_peek_head(self._c_queue)
+
+ if value == 0:
+ # this may mean that the queue is empty,
+ # or that it happens to contain a 0 value
+ if cqueue.queue_is_empty(self._c_queue):
+ raise IndexError("Queue is empty")
+ return value
+
+ cpdef int pop(self) except? -1:
+ if cqueue.queue_is_empty(self._c_queue):
+ raise IndexError("Queue is empty")
+ return <Py_ssize_t> cqueue.queue_pop_head(self._c_queue)
+
+ def __bool__(self):
+ return not cqueue.queue_is_empty(self._c_queue)
-from cython.view cimport array as cvarray\r
-import numpy as np\r
-\r
-# Memoryview on a NumPy array\r
-narr = np.arange(27, dtype=np.dtype("i")).reshape((3, 3, 3))\r
-cdef int [:, :, :] narr_view = narr\r
-\r
-# Memoryview on a C array\r
-cdef int carr[3][3][3]\r
-cdef int [:, :, :] carr_view = carr\r
-\r
-# Memoryview on a Cython array\r
-cyarr = cvarray(shape=(3, 3, 3), itemsize=sizeof(int), format="i")\r
-cdef int [:, :, :] cyarr_view = cyarr\r
-\r
-# Show the sum of all the arrays before altering it\r
-print("NumPy sum of the NumPy array before assignments: %s" % narr.sum())\r
-\r
-# We can copy the values from one memoryview into another using a single\r
-# statement, by either indexing with ... or (NumPy-style) with a colon.\r
-carr_view[...] = narr_view\r
-cyarr_view[:] = narr_view\r
-# NumPy-style syntax for assigning a single value to all elements.\r
-narr_view[:, :, :] = 3\r
-\r
-# Just to distinguish the arrays\r
-carr_view[0, 0, 0] = 100\r
-cyarr_view[0, 0, 0] = 1000\r
-\r
-# Assigning into the memoryview on the NumPy array alters the latter\r
-print("NumPy sum of NumPy array after assignments: %s" % narr.sum())\r
-\r
-# A function using a memoryview does not usually need the GIL\r
-cpdef int sum3d(int[:, :, :] arr) nogil:\r
- cdef size_t i, j, k\r
- cdef int total = 0\r
- I = arr.shape[0]\r
- J = arr.shape[1]\r
- K = arr.shape[2]\r
- for i in range(I):\r
- for j in range(J):\r
- for k in range(K):\r
- total += arr[i, j, k]\r
- return total\r
-\r
-# A function accepting a memoryview knows how to use a NumPy array,\r
-# a C array, a Cython array...\r
-print("Memoryview sum of NumPy array is %s" % sum3d(narr))\r
-print("Memoryview sum of C array is %s" % sum3d(carr))\r
-print("Memoryview sum of Cython array is %s" % sum3d(cyarr))\r
-# ... and of course, a memoryview.\r
-print("Memoryview sum of C memoryview is %s" % sum3d(carr_view))\r
+from cython.view cimport array as cvarray
+import numpy as np
+
+# Memoryview on a NumPy array
+narr = np.arange(27, dtype=np.dtype("i")).reshape((3, 3, 3))
+cdef int [:, :, :] narr_view = narr
+
+# Memoryview on a C array
+cdef int carr[3][3][3]
+cdef int [:, :, :] carr_view = carr
+
+# Memoryview on a Cython array
+cyarr = cvarray(shape=(3, 3, 3), itemsize=sizeof(int), format="i")
+cdef int [:, :, :] cyarr_view = cyarr
+
+# Show the sum of all the arrays before altering it
+print("NumPy sum of the NumPy array before assignments: %s" % narr.sum())
+
+# We can copy the values from one memoryview into another using a single
+# statement, by either indexing with ... or (NumPy-style) with a colon.
+carr_view[...] = narr_view
+cyarr_view[:] = narr_view
+# NumPy-style syntax for assigning a single value to all elements.
+narr_view[:, :, :] = 3
+
+# Just to distinguish the arrays
+carr_view[0, 0, 0] = 100
+cyarr_view[0, 0, 0] = 1000
+
+# Assigning into the memoryview on the NumPy array alters the latter
+print("NumPy sum of NumPy array after assignments: %s" % narr.sum())
+
+# A function using a memoryview does not usually need the GIL
+cpdef int sum3d(int[:, :, :] arr) nogil:
+ cdef size_t i, j, k, I, J, K
+ cdef int total = 0
+ I = arr.shape[0]
+ J = arr.shape[1]
+ K = arr.shape[2]
+ for i in range(I):
+ for j in range(J):
+ for k in range(K):
+ total += arr[i, j, k]
+ return total
+
+# A function accepting a memoryview knows how to use a NumPy array,
+# a C array, a Cython array...
+print("Memoryview sum of NumPy array is %s" % sum3d(narr))
+print("Memoryview sum of C array is %s" % sum3d(carr))
+print("Memoryview sum of Cython array is %s" % sum3d(cyarr))
+# ... and of course, a memoryview.
+print("Memoryview sum of C memoryview is %s" % sum3d(carr_view))
If within Cython code, we have a variable already 'early-bound' (ie, declared
explicitly as type Rectangle, (or cast to type Rectangle), then invoking its
area method will use the efficient C code path and skip the Python overhead.
-But if in Pyrex or regular Python code we have a regular object variable
+But if in Cython or regular Python code we have a regular object variable
storing a Rectangle object, then invoking the area method will require:
* an attribute lookup for the area method
jupyter
line_profiler
+# transitive dependency of jupyter (17.0+ lacks wheels for Py3.4)
+pyzmq<17
cdef class MockBuffer:
cdef Py_ssize_t zero
cdef Py_ssize_t minusone
- cdef object format
+ cdef bytes format
cdef object itemsize
def __init__(self, format, itemsize):
int c
int d
+ctypedef struct LongString:
+ char[90198] c
+
cdef struct CharIntCFloat:
char a
int b
cdef object obj = MockBuffer(fmt, sizeof(Char3Int))
cdef object[Char3Int, ndim=1] buf = obj
+
+@testcase
+def long_string(fmt):
+ """
+ >>> long_string("90198s")
+ """
+ cdef object obj = MockBuffer(fmt, sizeof(LongString))
+ cdef object[LongString, ndim=1] buf = obj
+
+
@testcase
def unpacked_struct(fmt):
"""
BaseType.meth
BaseType.meth
"""
- def callmeth(self):
+ cpdef callmeth(self):
+ return self.callmeth2()
+ cpdef callmeth2(self):
+ # not overridden by subclasses
return self.meth()
cpdef meth(self):
+ # overridden by subclasses
print("BaseType.meth")
+class NonOverride(BaseType):
+ """
+ >>> NonOverride().callmeth()
+ BaseType.meth
+ >>> obj = NonOverride()
+ >>> obj.callmeth()
+ BaseType.meth
+ >>> obj.callmeth()
+ BaseType.meth
+ >>> _call_method(NonOverride)
+ BaseType.meth
+ BaseType.meth
+ BaseType.meth
+ BaseType.meth
+ BaseType.meth
+ BaseType.meth
+ """
+
+
class PyClass(BaseType):
"""
>>> PyClass().callmeth()
def meth(self):
print("PySlotsClass.meth")
+
+
+class DynamicOverride(BaseType):
+ """
+ >>> DynamicOverride().callmeth()
+ meth1
+ >>> obj = DynamicOverride()
+ >>> obj.callmeth()
+ meth1
+ >>> obj.callmeth()
+ meth2
+ >>> obj.callmeth()
+ BaseType.meth
+ >>> obj.callmeth()
+ BaseType.meth
+ >>> _call_method(DynamicOverride)
+ meth1
+ meth1
+ meth2
+ meth1
+ meth2
+ BaseType.meth
+ """
+ def __init__(self):
+ self.meth = self.meth1
+ def meth1(self):
+ self.meth = self.meth2
+ print("meth1")
+ def meth2(self):
+ del self.meth
+ print("meth2")
--- /dev/null
+# mode: run
+# tag: cpp, werror
+# cython: c_string_encoding=utf-8, c_string_type=unicode
+
+cimport cython
+
+from libcpp.string cimport string
+
+b_asdf = b'asdf'
+s_asdf = 'asdf'
+u_asdf = u'asdf'
+u_s = u's'
+
+
+def test_conversion(py_obj):
+ """
+ >>> test_conversion(b_asdf) == u_asdf or test_conversion(b_asdf)
+ True
+ >>> test_conversion(u_asdf) == u_asdf or test_conversion(u_asdf)
+ True
+ >>> test_conversion(123) # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ TypeError: expected ..., int found
+ """
+ cdef string s = py_obj
+ assert <size_t>len(py_obj) == s.length(), '%d != %d' % (len(py_obj), s.length())
+ return s
+
+
+def test_empty(py_obj):
+ """
+ >>> test_empty('')
+ True
+ >>> test_empty('abc')
+ False
+ >>> test_empty(u_asdf[:0])
+ True
+ >>> test_empty(u_asdf)
+ False
+ """
+ cdef string a = py_obj
+ return a.empty()
+
+
+def test_push_back(a):
+ """
+ >>> test_push_back(b_asdf) == u_asdf + u_s
+ True
+ >>> test_push_back(u_asdf) == u_asdf + u_s
+ True
+ """
+ cdef string s = a
+ s.push_back(<char>ord('s'))
+ return s
+
+
+def test_clear(a):
+ """
+ >>> test_clear(u_asdf) == u_s[:0]
+ True
+ >>> test_clear(b_asdf) == u_s[:0]
+ True
+ """
+ cdef string s = a
+ s.clear()
+ return s
+
+
+def test_assign(char *a):
+ """
+ >>> test_assign(b_asdf) == 'ggg'
+ True
+ """
+ cdef string s = string(a)
+ s.assign(<char *>"ggg")
+ return s.c_str()
+
+
+def test_bytes_cast(a):
+ """
+ >>> b = test_bytes_cast(b'abc')
+ >>> isinstance(b, bytes)
+ True
+ >>> print(b.decode('ascii'))
+ abc
+ >>> b = test_bytes_cast(b'abc\\xe4\\xfc')
+ >>> isinstance(b, bytes)
+ True
+ >>> len(b)
+ 5
+ >>> print(b[:3].decode('ascii'))
+ abc
+ >>> print(ord(b[3:4]))
+ 228
+ >>> print(ord(b[4:5]))
+ 252
+ """
+ cdef string s = a
+ assert s.length() == <size_t>len(a), "%d != %d" % (s.length(), len(a))
+ return <bytes>s
+
+
+def test_bytearray_cast(a):
+ """
+ >>> b = test_bytearray_cast(b'abc')
+ >>> isinstance(b, bytearray)
+ True
+ >>> print(b.decode('ascii'))
+ abc
+ >>> b = test_bytearray_cast(b'abc\\xe4\\xfc')
+ >>> isinstance(b, bytearray)
+ True
+ >>> len(b)
+ 5
+ >>> print(b[:3].decode('ascii'))
+ abc
+ >>> print(ord(b[3:4]))
+ 228
+ >>> print(ord(b[4:5]))
+ 252
+ """
+ cdef string s = a
+ assert s.length() == <size_t>len(a), "%d != %d" % (s.length(), len(a))
+ return <bytearray>s
+
+
+def test_unicode_cast(a):
+ """
+ >>> u = test_unicode_cast(b'abc')
+ >>> type(u) is type(u_asdf) or type(u)
+ True
+ >>> print(u)
+ abc
+ """
+ cdef string s = a
+ assert s.length() == <size_t>len(a), "%d != %d" % (s.length(), len(a))
+ return <unicode>s
+
+
+def test_str_cast(a):
+ """
+ >>> s = test_str_cast(b'abc')
+ >>> type(s) is type(s_asdf) or type(s)
+ True
+ >>> print(s)
+ abc
+ """
+ cdef string s = a
+ assert s.length() == <size_t>len(a), "%d != %d" % (s.length(), len(a))
+ return <str>s
>>> opt_func("spam", f, i)
str object double long
spam 5.60 9 5.60 9
+ >>> opt_func("spam", f, myi=i)
+ str object double long
+ spam 5.60 9 5.60 9
+ >>> opt_func("spam", myf=f, myi=i)
+ str object double long
+ spam 5.60 9 5.60 9
>>> opt_func[str, float, int]("spam", f, i)
str object float int
spam 5.60 9 5.60 9
>>> opt_func[str, cy.double, cy.long]("spam", f, i)
str object double long
spam 5.60 9 5.60 9
+ >>> opt_func[str, cy.double, cy.long]("spam", f, myi=i)
+ str object double long
+ spam 5.60 9 5.60 9
>>> opt_func[str, float, cy.int]("spam", f, i)
str object float int
spam 5.60 9 5.60 9
opt_func("ham", f, entry4)
+def test_opt_func_introspection():
+ """
+ >>> opt_func.__defaults__
+ (1.2, 7)
+ >>> opt_func.__kwdefaults__
+ >>> opt_func.__annotations__
+ {}
+
+ >>> opt_func[str, float, int].__defaults__
+ (1.2, 7)
+ >>> opt_func[str, float, int].__kwdefaults__
+ >>> opt_func[str, float, int].__annotations__
+ {}
+
+ >>> opt_func[str, cy.double, cy.long].__defaults__
+ (1.2, 7)
+ >>> opt_func[str, cy.double, cy.long].__kwdefaults__
+ >>> opt_func[str, cy.double, cy.long].__annotations__
+ {}
+ """
+
+
def func_with_object(fused_with_object obj, cython.integral myi = 7):
"""
>>> func_with_object(1)