Cython Changelog
================
+0.29.1 (2018-11-24)
+===================
+
+Bugs fixed
+----------
+
+* Extensions compiled with MinGW-64 under Windows could misinterpret integer
+ objects larger than 15 bit and return incorrect results.
+ (Github issue #2670)
+
+* Cython no longer requires the source to be writable when copying its data
+ into a memory view slice.
+ Patch by Andrey Paramonov. (Github issue #2644)
+
+* Line tracing of ``try``-statements generated invalid C code.
+ (Github issue #2274)
+
+* When using the ``warn.undeclared`` directive, Cython's own code generated
+ warnings that are now fixed.
+ Patch by Nicolas Pauss. (Github issue #2685)
+
+* Cython's memoryviews no longer require strides for setting the shape field
+ but only the ``PyBUF_ND`` flag to be set.
+ Patch by John Kirkham. (Github issue #2716)
+
+* Some C compiler warnings about unused memoryview code were fixed.
+ Patch by Ho Cheuk Ting. (Github issue #2588)
+
+* A C compiler warning about implicit signed/unsigned conversion was fixed.
+ (Github issue #2729)
+
+* Assignments to C++ references returned by ``operator[]`` could fail to compile.
+ (Github issue #2671)
+
+* The power operator and the support for NumPy math functions were fixed
+ in Pythran expressions.
+ Patch by Serge Guelton. (Github issues #2702, #2709)
+
+* Signatures with memory view arguments now show the expected type
+ when embedded in docstrings.
+ Patch by Matthew Chan and Benjamin Weigel. (Github issue #2634)
+
+* Some ``from ... cimport ...`` constructs were not correctly considered
+ when searching modified dependencies in ``cythonize()`` to decide
+ whether to recompile a module.
+ Patch by Kryštof Pilnáček. (Github issue #2638)
+
+* A struct field type in the ``cpython.array`` declarations was corrected.
+ Patch by John Kirkham. (Github issue #2712)
+
+
0.29 (2018-10-14)
=================
slightly slower, by about 5-7%.
+0.28.6 (2018-11-01)
+===================
+
+Bugs fixed
+----------
+
+* Extensions compiled with MinGW-64 under Windows could misinterpret integer
+ objects larger than 15 bit and return incorrect results.
+ (Github issue #2670)
+
+* Multiplied string literals lost their factor when they are part of another
+ constant expression (e.g. 'x' * 10 + 'y' => 'xy').
+
+
0.28.5 (2018-08-03)
===================
+Bugs fixed
+----------
+
* The discouraged usage of GCC's attribute ``optimize("Os")`` was replaced by the
similar attribute ``cold`` to reduce the code impact of the module init functions.
(Github issue #2494)
r"(?:^\s*cimport +([0-9a-zA-Z_.]+(?: *, *[0-9a-zA-Z_.]+)*))|"
r"(?:^\s*cdef +extern +from +['\"]([^'\"]+)['\"])|"
r"(?:^\s*include +['\"]([^'\"]+)['\"])", re.M)
+dependency_after_from_regex = re.compile(
+ r"(?:^\s+\(([0-9a-zA-Z_., ]*)\)[#\n])|"
+ r"(?:^\s+([0-9a-zA-Z_., ]*)[#\n])",
+ re.M)
def normalize_existing(base_path, rel_paths):
cimport_from, cimport_list, extern, include = m.groups()
if cimport_from:
cimports.append(cimport_from)
+ m_after_from = dependency_after_from_regex.search(source, pos=m.end())
+ if m_after_from:
+ multiline, one_line = m_after_from.groups()
+ subimports = multiline or one_line
+ cimports.extend("{0}.{1}".format(cimport_from, s.strip())
+ for s in subimports.split(','))
+
elif cimport_list:
cimports.extend(x.strip() for x in cimport_list.split(","))
elif extern:
pxd_list = [filename[:-4] + '.pxd']
else:
pxd_list = []
+ # Cimports generates all possible combinations package.module
+ # when imported as from package cimport module.
for module in self.cimports(filename):
if module[:7] == 'cython.' or module == 'cython':
continue
pxd_file = self.find_pxd(module, filename)
if pxd_file is not None:
pxd_list.append(pxd_file)
- elif not self.quiet:
- print("%s: cannot find cimported module '%s'" % (filename, module))
return tuple(pxd_list)
@cached_method
# both exception handlers are the same.
translate_cpp_exception(code, self.pos,
"%s = %s;" % (self.result(), rhs.result()),
- self.result() if self.lhs.is_pyobject else None,
+ self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context)
else:
code.putln(
if self.type.is_pythran_expr:
code.putln("// Pythran binop")
code.putln("__Pyx_call_destructor(%s);" % self.result())
- code.putln("new (&%s) decltype(%s){%s %s %s};" % (
- self.result(),
- self.result(),
- self.operand1.pythran_result(),
- self.operator,
- self.operand2.pythran_result()))
+ if self.operator == '**':
+ code.putln("new (&%s) decltype(%s){pythonic::numpy::functor::power{}(%s, %s)};" % (
+ self.result(),
+ self.result(),
+ self.operand1.pythran_result(),
+ self.operand2.pythran_result()))
+ else:
+ code.putln("new (&%s) decltype(%s){%s %s %s};" % (
+ self.result(),
+ self.result(),
+ self.operand1.pythran_result(),
+ self.operator,
+ self.operand2.pythran_result()))
elif self.operand1.type.is_pyobject:
function = self.py_operation_function(code)
if self.operator == '**':
h_code.put_generated_by()
api_guard = Naming.api_guard_prefix + self.api_name(env)
h_code.put_h_guard(api_guard)
+ # Work around https://bugs.python.org/issue4709
+ h_code.putln('#ifdef __MINGW64__')
+ h_code.putln('#define MS_WIN64')
+ h_code.putln('#endif')
+
h_code.putln('#include "Python.h"')
if result.h_file:
h_code.putln('#include "%s"' % os.path.basename(result.h_file))
gil_message = "Try-except statement"
def generate_execution_code(self, code):
+ code.mark_pos(self.pos) # before changing the error label, in case of tracing errors
+ code.putln("{")
+
old_return_label = code.return_label
old_break_label = code.break_label
old_continue_label = code.continue_label
exc_save_vars = [code.funcstate.allocate_temp(py_object_type, False)
for _ in range(3)]
- code.mark_pos(self.pos)
- code.putln("{")
save_exc = code.insertion_point()
code.putln(
"/*try:*/ {")
gil_message = "Try-finally statement"
def generate_execution_code(self, code):
- code.mark_pos(self.pos)
+ code.mark_pos(self.pos) # before changing the error label, in case of tracing errors
+ code.putln("/*try:*/ {")
+
old_error_label = code.error_label
old_labels = code.all_new_labels()
new_labels = code.get_all_labels()
code.error_label = old_error_label
catch_label = code.new_label()
- code.putln("/*try:*/ {")
was_in_try_finally = code.funcstate.in_try_finally
code.funcstate.in_try_finally = 1
# so it can be pickled *after* self is memoized.
unpickle_func = TreeFragment(u"""
def %(unpickle_func_name)s(__pyx_type, long __pyx_checksum, __pyx_state):
+ cdef object __pyx_PickleError
+ cdef object __pyx_result
if __pyx_checksum != %(checksum)s:
from pickle import PickleError as __pyx_PickleError
raise __pyx_PickleError("Incompatible checksums (%%s vs %(checksum)s = (%(members)s))" %% __pyx_checksum)
pickle_func = TreeFragment(u"""
def __reduce_cython__(self):
+ cdef tuple state
+ cdef object _dict
cdef bint use_setstate
state = (%(members)s)
_dict = getattr(self, '__dict__', None)
assert not pyrex
assert not dll_linkage
from . import MemoryView
+ base_code = str(self) if for_display else MemoryView.memviewslice_cname
return self.base_declaration_code(
- MemoryView.memviewslice_cname,
+ base_code,
entity_code)
def attributes_known(self):
def pythran_binop_type(op, tA, tB):
- return "decltype(std::declval<%s>() %s std::declval<%s>())" % (
- pythran_type(tA), op, pythran_type(tB))
+ if op == '**':
+ return 'decltype(pythonic::numpy::functor::power{}(std::declval<%s>(), std::declval<%s>()))' % (
+ pythran_type(tA), pythran_type(tB))
+ else:
+ return "decltype(std::declval<%s>() %s std::declval<%s>())" % (
+ pythran_type(tA), op, pythran_type(tB))
def pythran_unaryop_type(op, type_):
def pythran_get_func_include_file(func):
func = np_func_to_list(func)
- return "pythonic/include/numpy/%s.hpp" % "/".join(func)
+ return "pythonic/numpy/%s.hpp" % "/".join(func)
def include_pythran_generic(env):
# Generic files
env.add_include_file("pythonic/python/core.hpp")
env.add_include_file("pythonic/types/bool.hpp")
env.add_include_file("pythonic/types/ndarray.hpp")
+ env.add_include_file("pythonic/numpy/power.hpp")
env.add_include_file("<new>") # for placement new
for i in (8, 16, 32, 64):
ctypedef object GETF(array a, Py_ssize_t ix)
ctypedef object SETF(array a, Py_ssize_t ix, object o)
ctypedef struct arraydescr: # [object arraydescr]:
- int typecode
+ char typecode
int itemsize
GETF getitem # PyObject * (*getitem)(struct arrayobject *, Py_ssize_t);
SETF setitem # int (*setitem)(struct arrayobject *, Py_ssize_t, PyObject *);
# cython.* namespace for pure mode.
from __future__ import absolute_import
-__version__ = "0.29"
+__version__ = "0.29.1"
try:
from __builtin__ import basestring
#define __Pyx_Coroutine_AlreadyRunningError(gen) (__Pyx__Coroutine_AlreadyRunningError(gen), (PyObject*)NULL)
static void __Pyx__Coroutine_AlreadyRunningError(CYTHON_UNUSED __pyx_CoroutineObject *gen) {
const char *msg;
- if (0) {
+ if ((0)) {
#ifdef __Pyx_Coroutine_USED
} else if (__Pyx_Coroutine_Check((PyObject*)gen)) {
msg = "coroutine already executing";
#define __Pyx_Coroutine_NotStartedError(gen) (__Pyx__Coroutine_NotStartedError(gen), (PyObject*)NULL)
static void __Pyx__Coroutine_NotStartedError(CYTHON_UNUSED PyObject *gen) {
const char *msg;
- if (0) {
+ if ((0)) {
#ifdef __Pyx_Coroutine_USED
} else if (__Pyx_Coroutine_Check(gen)) {
msg = "can't send non-None value to a just-started coroutine";
PyBUF_WRITABLE
PyBUF_STRIDES
PyBUF_INDIRECT
+ PyBUF_ND
PyBUF_RECORDS
PyBUF_RECORDS_RO
cdef is_slice(self, obj):
if not isinstance(obj, memoryview):
try:
- obj = memoryview(obj, self.flags|PyBUF_ANY_CONTIGUOUS,
+ obj = memoryview(obj, self.flags & ~PyBUF_WRITABLE | PyBUF_ANY_CONTIGUOUS,
self.dtype_is_object)
except TypeError:
return None
if flags & PyBUF_WRITABLE and self.view.readonly:
raise ValueError("Cannot create writable memory view from read-only memoryview")
- if flags & PyBUF_STRIDES:
+ if flags & PyBUF_ND:
info.shape = self.view.shape
else:
info.shape = NULL
{
Py_ssize_t __pyx_tmp_idx = {{idx}};
- Py_ssize_t __pyx_tmp_shape = {{src}}.shape[{{dim}}];
+
+ {{if wraparound or boundscheck}}
+ Py_ssize_t __pyx_tmp_shape = {{src}}.shape[{{dim}}];
+ {{endif}}
+
Py_ssize_t __pyx_tmp_stride = {{src}}.strides[{{dim}}];
- if ({{wraparound}} && (__pyx_tmp_idx < 0))
- __pyx_tmp_idx += __pyx_tmp_shape;
+ {{if wraparound}}
+ if (__pyx_tmp_idx < 0)
+ __pyx_tmp_idx += __pyx_tmp_shape;
+ {{endif}}
- if ({{boundscheck}} && !__Pyx_is_valid_index(__pyx_tmp_idx, __pyx_tmp_shape)) {
- {{if not have_gil}}
- #ifdef WITH_THREAD
- PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();
- #endif
- {{endif}}
+ {{if boundscheck}}
+ if (!__Pyx_is_valid_index(__pyx_tmp_idx, __pyx_tmp_shape)) {
+ {{if not have_gil}}
+ #ifdef WITH_THREAD
+ PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();
+ #endif
+ {{endif}}
- PyErr_SetString(PyExc_IndexError, "Index out of bounds (axis {{dim}})");
+ PyErr_SetString(PyExc_IndexError,
+ "Index out of bounds (axis {{dim}})");
- {{if not have_gil}}
- #ifdef WITH_THREAD
- PyGILState_Release(__pyx_gilstate_save);
- #endif
- {{endif}}
+ {{if not have_gil}}
+ #ifdef WITH_THREAD
+ PyGILState_Release(__pyx_gilstate_save);
+ #endif
+ {{endif}}
- {{error_goto}}
- }
+ {{error_goto}}
+ }
+ {{else}}
+ // make sure label is not un-used
+ if ((0)) {{error_goto}}
+ {{endif}}
{{if all_dimensions_direct}}
{{dst}}.data += __pyx_tmp_idx * __pyx_tmp_stride;
#undef SHIFT
#undef BASE
#undef MASK
+ /* Compile-time sanity check that these are indeed equal. Github issue #2670. */
+ #ifdef SIZEOF_VOID_P
+ enum { __pyx_check_sizeof_voidp = 1/(SIZEOF_VOID_P == sizeof(void*)) };
+ #endif
#endif
#ifndef __has_attribute
return 0;
}
-static CYTHON_SMALL_CODE int __Pyx_copy_spec_to_module(PyObject *spec, PyObject *moddict, const char* from_name, const char* to_name) {
+static CYTHON_SMALL_CODE int __Pyx_copy_spec_to_module(PyObject *spec, PyObject *moddict, const char* from_name, const char* to_name, int allow_none) {
PyObject *value = PyObject_GetAttrString(spec, from_name);
int result = 0;
if (likely(value)) {
- result = PyDict_SetItemString(moddict, to_name, value);
+ if (allow_none || value != Py_None) {
+ result = PyDict_SetItemString(moddict, to_name, value);
+ }
Py_DECREF(value);
} else if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
if (unlikely(!moddict)) goto bad;
// moddict is a borrowed reference
- if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "loader", "__loader__") < 0)) goto bad;
- if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "origin", "__file__") < 0)) goto bad;
- if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "parent", "__package__") < 0)) goto bad;
- if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "submodule_search_locations", "__path__") < 0)) goto bad;
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "loader", "__loader__", 1) < 0)) goto bad;
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "origin", "__file__", 1) < 0)) goto bad;
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "parent", "__package__", 1) < 0)) goto bad;
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "submodule_search_locations", "__path__", 0) < 0)) goto bad;
return module;
bad:
// because f_localsplus is the last field of PyFrameObject (checked by Py_BUILD_ASSERT_EXPR below).
#define __Pxy_PyFrame_Initialize_Offsets() \
((void)__Pyx_BUILD_ASSERT_EXPR(sizeof(PyFrameObject) == offsetof(PyFrameObject, f_localsplus) + Py_MEMBER_SIZE(PyFrameObject, f_localsplus)), \
- (void)(__pyx_pyframe_localsplus_offset = PyFrame_Type.tp_basicsize - Py_MEMBER_SIZE(PyFrameObject, f_localsplus)))
+ (void)(__pyx_pyframe_localsplus_offset = ((size_t)PyFrame_Type.tp_basicsize) - Py_MEMBER_SIZE(PyFrameObject, f_localsplus)))
#define __Pyx_PyFrame_GetLocalsplus(frame) \
(assert(__pyx_pyframe_localsplus_offset), (PyObject **)(((char *)(frame)) + __pyx_pyframe_localsplus_offset))
#endif
static double __Pyx__PyObject_AsDouble(PyObject* obj) {
PyObject* float_value;
#if !CYTHON_USE_TYPE_SLOTS
- float_value = PyNumber_Float(obj); if (0) goto bad;
+ float_value = PyNumber_Float(obj); if ((0)) goto bad;
#else
PyNumberMethods *nb = Py_TYPE(obj)->tp_as_number;
if (likely(nb) && likely(nb->nb_float)) {
test_script:
- "%PYTHON%\\Scripts\\pip.exe install -r test-requirements.txt"
- "set CFLAGS=/Od"
- - "%WITH_ENV% %PYTHON%\\python.exe runtests.py -vv --no-cpp -j5"
+ - "%WITH_ENV% %PYTHON%\\python.exe runtests.py -vv --no-cpp --no-code-style -j5"
artifacts:
- path: dist\*
self.x1 = x1\r
self.y1 = y1\r
\r
- cpdef int _area(self):\r
+ cpdef int area(self):\r
area = (self.x1 - self.x0) * (self.y1 - self.y0)\r
if area < 0:\r
area = -area\r
Using multiple threads
======================
-Cython have support for OpenMP. It have also some nice wrappers around it,
+Cython has support for OpenMP. It also has some nice wrappers around it,
like the function :func:`prange`. You can see more information about Cython and
-parralelism in :ref:`parallel`. Since we do elementwise operations, we can easily
+parallelism in :ref:`parallel`. Since we do elementwise operations, we can easily
distribute the work among multiple threads. It's important not to forget to pass the
correct arguments to the compiler to enable OpenMP. When using the Jupyter notebook,
you should use the cell magic like this::
Globally
:::::::::
-One can set compiler directives through a special header comment at the top of the file, like this::
+One can set compiler directives through a special header comment near the top of the file, like this::
- #!python
- #cython: language_level=3, boundscheck=False
+ # cython: language_level=3, boundscheck=False
The comment must appear before any code (but can appear after other
comments or whitespace).
def runTest(self):
import pycodestyle
config_file = os.path.join(self.cython_dir, "tox.ini")
+ if not os.path.exists(config_file):
+ config_file=os.path.join(os.path.dirname(__file__), "tox.ini")
paths = glob.glob(os.path.join(self.cython_dir, "**/*.py"), recursive=True)
style = pycodestyle.StyleGuide(config_file=config_file)
print("") # Fix the first line of the report.
.replace("PYTHON", sys.executable))
old_path = os.environ.get('PYTHONPATH')
env = dict(os.environ)
- env['PYTHONPATH'] = self.cython_syspath + os.pathsep + (old_path or '')
+ new_path = self.cython_syspath
+ if old_path:
+ new_path = new_path + os.pathsep + old_path
+ env['PYTHONPATH'] = new_path
cmd = []
out = []
err = []
test_suite.addTest(filetests.handle_directory(sys_pyregr_dir, 'pyregr'))
if options.code_style and options.shard_num <= 0:
- test_suite.addTest(TestCodeFormat(options.cython_dir))
+ try:
+ import pycodestyle
+ except ImportError:
+ # Hack to make the exclusion visible.
+ missing_dep_excluder.tests_missing_deps.append('TestCodeFormat')
+ else:
+ test_suite.addTest(TestCodeFormat(options.cython_dir))
if xml_output_dir:
from Cython.Tests.xmlrunner import XMLTestRunner
CYTHON_USE_TP_FINALIZE {CYTHON_USE_TP_FINALIZE}
PyLong_BASE 0x{PyLong_BASE:X}
-PyLong_MASK {PyLong_MASK:X}
+PyLong_MASK 0x{PyLong_MASK:X}
PyLong_SHIFT {PyLong_SHIFT}
-sizeof(digit) {sizeof(digit)}
+sizeof(digit) {sizeof(digit)}
sizeof(sdigit) {sizeof(sdigit)}
+sys.int_info {getattr(sys, 'int_info', '-')}
+sys.getsizeof(1, 2**14, 2**15, 2**29, 2**30, 2**59, 2**60, 2**64) {tuple(sys.getsizeof(n) for n in (1, 2**14, 2**15, 2**29, 2**30, 2**59, 2**60, 2**64))}
SIZEOF_INT {SIZEOF_INT} ({sizeof(int)})
SIZEOF_LONG {SIZEOF_LONG} ({sizeof(long)})
--- /dev/null
+# cython: warn.undeclared=True
+# mode: error
+# tag: werror
+
+def foo():
+ a = 1
+ return a
+
+cdef class Bar:
+ cdef int baz(self, a):
+ res = 0
+ for i in range(3):
+ res += i
+ return res
+
+_ERRORS = """
+6:4: implicit declaration of 'a'
+11:8: implicit declaration of 'res'
+12:12: implicit declaration of 'i'
+"""
from cpython.ref cimport Py_INCREF, Py_DECREF
cimport cython
+import array as pyarray
+from libc.stdlib cimport malloc, free
+
cdef extern from "Python.h":
cdef int PyBUF_C_CONTIGUOUS
print(arr[x, y, z], arr[x][:][:][y][:][:][z])
print(arr[x, y, z], arr[:][x][:][y][:][:][z])
print(arr[x, y, z], arr[:, :][x][:, :][y][:][z])
+
+
+def test_assign_from_byteslike(byteslike):
+ # Once http://python3statement.org is accepted, should be just
+ # >>> test_assign_from_byteslike(bytes(b'hello'))
+ # b'hello'
+ # ...
+ """
+ >>> print(test_assign_from_byteslike(bytes(b'hello')).decode())
+ hello
+ >>> print(test_assign_from_byteslike(bytearray(b'howdy')).decode())
+ howdy
+ """
+ # fails on Python 2.7- with
+ # TypeError: an integer is required
+ # >>> print(test_assign_from_byteslike(pyarray.array('B', b'aloha')).decode())
+ # aloha
+ # fails on Python 2.6- with
+ # NameError: name 'memoryview' is not defined
+ # >>> print(test_assign_from_byteslike(memoryview(b'bye!!')).decode())
+ # bye!!
+
+ def assign(m):
+ m[:] = byteslike
+
+ cdef void *buf
+ cdef unsigned char[:] mview
+ buf = malloc(5)
+ try:
+ mview = <unsigned char[:5]>(buf)
+ assign(mview)
+ return (<unsigned char*>buf)[:5]
+ finally:
+ free(buf)
ints[idx] = 222
longlongs[idx] = 333
externs[idx] = 444
+ assert externs[idx] == 444 # avoid "set but not used" C compiler warning
floats[idx] = 11.1
doubles[idx] = 12.2
a = np.arange(12).reshape([3, 4])
cdef np.int_t[:,:] a_view = a
cdef np.int_t[:,:] b = a_view[1:2,:].T
+
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+def test_boundscheck_and_wraparound(double[:, :] x):
+ """
+ >>> import numpy as np
+ >>> array = np.ones((2,2)) * 3.5
+ >>> test_boundscheck_and_wraparound(array)
+ """
+ # Make sure we don't generate C compiler warnings for unused code here.
+ cdef Py_ssize_t numrow = x.shape[0]
+ cdef Py_ssize_t i
+ for i in range(numrow):
+ x[i, 0]
+ x[i]
+ x[i, ...]
+ x[i, :]
ctypedef int my_int
+######## pkg/subpkg/__init__.py ########
+
+######## pkg/subpkg/submod.pxd ########
+
+ctypedef int my_int
+
######## a.pyx ########
-from other cimport A, foo
+from other cimport (
+ A,
+ foo,
+)
print A, foo(10)
cimport other
from pkg cimport sub
cdef sub.my_int a = 100
+
+from pkg.subpkg cimport submod
\ No newline at end of file
+# mode: run
+
__doc__ = u"""
>>> int2 = 42
>>> int3 = 7
int1 ^= int2 << int3 | int2 >> int3
long1 = char1 | int1
return int1, long1
+
+
+def long_int_shift():
+ """
+ >>> long_int_shift()
+ 80082
+ 10010
+ 10010
+ 10010
+ 10010
+ """
+ value = 80082 # int using more than 2 bytes == long
+ print(value)
+ shiftedby3 = value >> 3
+ dividedby8 = value // 8
+ print(shiftedby3)
+ print(dividedby8)
+ shiftedby3 = 80082 >> 3
+ dividedby8 = 80082 // 8
+ print(shiftedby3)
+ print(dividedby8)
from libc.string cimport const_char
from libcpp cimport bool
+
cdef out(s, result_type=None):
print '%s [%s]' % (s.decode('ascii'), result_type)
+
+cdef iout(int s, result_type=None):
+ print '%s [%s]' % (s, result_type)
+
+
cdef extern from "cpp_operators_helper.h" nogil:
cdef cppclass TestOps:
- const_char* operator+()
- const_char* operator-()
- const_char* operator*()
- const_char* operator~()
- const_char* operator!()
+ const_char* operator+() except +
+ const_char* operator-() except +
+ const_char* operator*() except +
+ const_char* operator~() except +
+ const_char* operator!() except +
+ # FIXME: using 'except +' here leads to wrong calls ???
const_char* operator++()
const_char* operator--()
const_char* operator++(int)
const_char* operator--(int)
- const_char* operator+(int)
- const_char* operator+(int,const TestOps&)
- const_char* operator-(int)
- const_char* operator-(int,const TestOps&)
- const_char* operator*(int)
+ const_char* operator+(int) except +
+ const_char* operator+(int,const TestOps&) except +
+ const_char* operator-(int) except +
+ const_char* operator-(int,const TestOps&) except +
+ const_char* operator*(int) except +
# deliberately omitted operator* to test case where only defined outside class
- const_char* operator/(int)
- const_char* operator/(int,const TestOps&)
- const_char* operator%(int)
- const_char* operator%(int,const TestOps&)
-
- const_char* operator|(int)
- const_char* operator|(int,const TestOps&)
- const_char* operator&(int)
- const_char* operator&(int,const TestOps&)
- const_char* operator^(int)
- const_char* operator^(int,const TestOps&)
- const_char* operator,(int)
- const_char* operator,(int,const TestOps&)
-
- const_char* operator<<(int)
- const_char* operator<<(int,const TestOps&)
- const_char* operator>>(int)
- const_char* operator>>(int,const TestOps&)
-
+ const_char* operator/(int) except +
+ const_char* operator/(int,const TestOps&) except +
+ const_char* operator%(int) except +
+ const_char* operator%(int,const TestOps&) except +
+
+ const_char* operator|(int) except +
+ const_char* operator|(int,const TestOps&) except +
+ const_char* operator&(int) except +
+ const_char* operator&(int,const TestOps&) except +
+ const_char* operator^(int) except +
+ const_char* operator^(int,const TestOps&) except +
+ const_char* operator,(int) except +
+ const_char* operator,(int,const TestOps&) except +
+
+ const_char* operator<<(int) except +
+ const_char* operator<<(int,const TestOps&) except +
+ const_char* operator>>(int) except +
+ const_char* operator>>(int,const TestOps&) except +
+
+ # FIXME: using 'except +' here leads to invalid C++ code ???
const_char* operator==(int)
const_char* operator!=(int)
const_char* operator>=(int)
const_char* operator>(int)
const_char* operator<(int)
- const_char* operator[](int)
- const_char* operator()(int)
-
+ const_char* operator[](int) except +
+ const_char* operator()(int) except +
+
# Defining the operator outside the class does work
# but doesn't help when importing from pxd files
# (they don't get imported)
- const_char* operator+(float,const TestOps&)
+ const_char* operator+(float,const TestOps&) except +
# deliberately omitted operator- to test case where only defined in class
- const_char* operator*(float,const TestOps&)
- const_char* operator/(float,const TestOps&)
- const_char* operator%(float,const TestOps&)
-
- const_char* operator|(float,const TestOps&)
- const_char* operator&(float,const TestOps&)
- const_char* operator^(float,const TestOps&)
- const_char* operator,(float,const TestOps&)
-
- const_char* operator<<(float,const TestOps&)
- const_char* operator>>(float,const TestOps&)
+ const_char* operator*(float,const TestOps&) except +
+ const_char* operator/(float,const TestOps&) except +
+ const_char* operator%(float,const TestOps&) except +
+
+ const_char* operator|(float,const TestOps&) except +
+ const_char* operator&(float,const TestOps&) except +
+ const_char* operator^(float,const TestOps&) except +
+ const_char* operator,(float,const TestOps&) except +
+
+ const_char* operator<<(float,const TestOps&) except +
+ const_char* operator>>(float,const TestOps&) except +
+
+ cdef cppclass RefTestOps:
+
+ int& operator+() except +
+ int& operator-() except +
+ int& operator*() except +
+ int& operator~() except +
+ int& operator!() except +
+
+ int& operator++() except +
+ int& operator--() except +
+ int& operator++(int) except +
+ int& operator--(int) except +
+
+ int& operator+(int) except +
+ int& operator+(int,const TestOps&) except +
+ int& operator-(int) except +
+ int& operator-(int,const TestOps&) except +
+ int& operator*(int) except +
+ # deliberately omitted operator* to test case where only defined outside class
+ int& operator/(int) except +
+ int& operator/(int,const TestOps&) except +
+ int& operator%(int) except +
+ int& operator%(int,const TestOps&) except +
+
+ int& operator|(int) except +
+ int& operator|(int,const TestOps&) except +
+ int& operator&(int) except +
+ int& operator&(int,const TestOps&) except +
+ int& operator^(int) except +
+ int& operator^(int,const TestOps&) except +
+ int& operator,(int) except +
+ int& operator,(int,const TestOps&) except +
+
+ int& operator<<(int) except +
+ int& operator<<(int,const TestOps&) except +
+ int& operator>>(int) except +
+ int& operator>>(int,const TestOps&) except +
+
+ int& operator==(int) except +
+ int& operator!=(int) except +
+ int& operator>=(int) except +
+ int& operator<=(int) except +
+ int& operator>(int) except +
+ int& operator<(int) except +
+
+ int& operator[](int) except +
+ int& operator()(int) except +
cdef cppclass TruthClass:
TruthClass()
bool operator bool()
bool value
+
cdef cppclass TruthSubClass(TruthClass):
pass
+
def test_unops():
"""
>>> test_unops()
out(t[0] < 1, typeof(t[0] < 1))
del t
+
def test_index_call():
"""
>>> test_index_call()
out(t[0](100), typeof(t[0](100)))
del t
+
+def test_index_assignment():
+ """
+ >>> test_index_assignment()
+ 0 [int &]
+ 123 [int [&]]
+ """
+ cdef RefTestOps* t = new RefTestOps()
+ iout(t[0][100], typeof(t[0][100]))
+ t[0][99] = 123
+ iout(t[0](100), typeof(t[0](100)))
+ del t
+
+
def test_bool_op():
"""
>>> test_bool_op()
NONMEMBER_BIN_OP2(^)
NONMEMBER_BIN_OP2(COMMA)
+
+/* RefTestOps */
+
+#define REF_UN_OP(op) int& operator op () { return value; }
+#define REF_POST_UN_OP(op) int& operator op (int x) { x++; return value; }
+#define REF_BIN_OP(op) int& operator op (int x) { x++; return value; }
+
+class RefTestOps {
+ int value = 0;
+
+public:
+
+ REF_UN_OP(-);
+ REF_UN_OP(+);
+ REF_UN_OP(*);
+ REF_UN_OP(~);
+ REF_UN_OP(!);
+ REF_UN_OP(&);
+
+ REF_UN_OP(++);
+ REF_UN_OP(--);
+ REF_POST_UN_OP(++);
+ REF_POST_UN_OP(--);
+
+ REF_BIN_OP(+);
+ REF_BIN_OP(-);
+ REF_BIN_OP(*);
+ REF_BIN_OP(/);
+ REF_BIN_OP(%);
+
+ REF_BIN_OP(<<);
+ REF_BIN_OP(>>);
+
+ REF_BIN_OP(|);
+ REF_BIN_OP(&);
+ REF_BIN_OP(^);
+ REF_BIN_OP(COMMA);
+
+ REF_BIN_OP(==);
+ REF_BIN_OP(!=);
+ REF_BIN_OP(<=);
+ REF_BIN_OP(<);
+ REF_BIN_OP(>=);
+ REF_BIN_OP(>);
+
+ REF_BIN_OP([]);
+ REF_BIN_OP(());
+};
+
+
+/* TruthClass */
+
class TruthClass {
public:
TruthClass() : value(false) {}
def m28(self, a: list(range(3))[::1]): pass
def m29(self, a: list(range(3))[0:1:1]): pass
def m30(self, a: list(range(3))[7, 3:2:1, ...]): pass
+ def m31(self, double[::1] a): pass
__doc__ += ur"""
>>> print(Foo.m00.__doc__)
>>> print(Foo.m30.__doc__)
Foo.m30(self, a: list(range(3))[7, 3:2:1, ...])
+
+>>> print(Foo.m31.__doc__)
+Foo.m31(self, double[::1] a)
"""
import sys
-from cpython.ref cimport PyObject, Py_INCREF, Py_XINCREF, Py_XDECREF
+from cpython.ref cimport PyObject, Py_INCREF, Py_XDECREF
cdef extern from "frameobject.h":
ctypedef struct PyFrameObject:
map_trace_types = {
PyTrace_CALL: 'call',
- PyTrace_EXCEPTION: 'exc',
+ PyTrace_EXCEPTION: 'exception',
PyTrace_LINE: 'line',
PyTrace_RETURN: 'return',
PyTrace_C_CALL: 'ccall',
local_names = {}
def _trace_func(frame, event, arg):
+ if sys.version_info < (3,) and 'line_trace' not in frame.f_code.co_filename:
+ # Prevent tracing into Py2 doctest functions.
+ return None
+
trace.append((map_trace_types(event, event), frame.f_lineno - frame.f_code.co_firstlineno))
lnames = frame.f_code.co_varnames
# Currently, the locals dict is empty for Cython code, but not for Python code.
if frame.f_code.co_name.startswith('py_'):
# Change this when we start providing proper access to locals.
- assert frame.f_locals
+ assert frame.f_locals, frame.f_code.co_name
else:
- assert not frame.f_locals
+ assert not frame.f_locals, frame.f_code.co_name
return _trace_func
return _trace_func
return x # 2
+def cy_try_except(func):
+ try:
+ return func()
+ except KeyError as exc:
+ raise AttributeError(exc.args[0])
+
+
def run_trace(func, *args, bint with_sys=False):
"""
>>> def py_add(a,b):
return trace
+def run_trace_with_exception(func, bint with_sys=False, bint fail=False):
+ """
+ >>> def py_return(retval=123): return retval
+ >>> run_trace_with_exception(py_return)
+ OK: 123
+ [('call', 0), ('line', 1), ('line', 2), ('call', 0), ('line', 0), ('return', 0), ('return', 2)]
+ >>> run_trace_with_exception(py_return, with_sys=True)
+ OK: 123
+ [('call', 0), ('line', 1), ('line', 2), ('call', 0), ('line', 0), ('return', 0), ('return', 2)]
+
+ >>> run_trace_with_exception(py_return, fail=True)
+ ValueError('failing line trace!')
+ [('call', 0)]
+
+ #>>> run_trace_with_exception(lambda: 123, with_sys=True, fail=True)
+ #ValueError('huhu')
+ #[('call', 0), ('line', 1), ('line', 2), ('call', 0), ('line', 0), ('return', 0), ('return', 2)]
+
+ >>> def py_raise_exc(exc=KeyError('huhu')): raise exc
+ >>> run_trace_with_exception(py_raise_exc)
+ AttributeError('huhu')
+ [('call', 0), ('line', 1), ('line', 2), ('call', 0), ('line', 0), ('exception', 0), ('return', 0), ('line', 3), ('line', 4), ('return', 4)]
+ >>> run_trace_with_exception(py_raise_exc, with_sys=True)
+ AttributeError('huhu')
+ [('call', 0), ('line', 1), ('line', 2), ('call', 0), ('line', 0), ('exception', 0), ('return', 0), ('line', 3), ('line', 4), ('return', 4)]
+ >>> run_trace_with_exception(py_raise_exc, fail=True)
+ ValueError('failing line trace!')
+ [('call', 0)]
+
+ #>>> run_trace_with_exception(raise_exc, with_sys=True, fail=True)
+ #ValueError('huhu')
+ #[('call', 0), ('line', 1), ('line', 2), ('call', 0), ('line', 0), ('exception', 0), ('return', 0), ('line', 3), ('line', 4), ('return', 4)]
+ """
+ trace = ['cy_try_except' if fail else 'NO ERROR']
+ trace_func = _create__failing_line_trace_func(trace) if fail else _create_trace_func(trace)
+ if with_sys:
+ sys.settrace(trace_func)
+ else:
+ PyEval_SetTrace(<Py_tracefunc>trace_trampoline, <PyObject*>trace_func)
+ try:
+ try:
+ retval = cy_try_except(func)
+ except ValueError as exc:
+ print("%s(%r)" % (type(exc).__name__, str(exc)))
+ except AttributeError as exc:
+ print("%s(%r)" % (type(exc).__name__, str(exc)))
+ else:
+ print('OK: %r' % retval)
+ finally:
+ if with_sys:
+ sys.settrace(None)
+ else:
+ PyEval_SetTrace(NULL, NULL)
+ return trace[1:]
+
+
def fail_on_call_trace(func, *args):
"""
>>> def py_add(a,b):
--- /dev/null
+# mode: run
+# tag: pythran, numpy, cpp
+# cython: np_pythran=True
+
+import numpy as np
+cimport numpy as np
+
+def trigo(np.ndarray[double, ndim=1] angles):
+
+ """
+ >>> a = np.array([0., np.pi, np.pi *2])
+ >>> trigo(a)
+ array([ 1., -1., 1.])
+ """
+ return np.cos(angles)
+
+def power(np.ndarray[double, ndim=1] values):
+
+ """
+ >>> a = np.array([0., 1., 2.])
+ >>> res = power(a)
+ >>> res[0], res[1], res[2]
+ (0.0, 1.0, 8.0)
+ """
+ return values ** 3
assert 0 == run_in_subinterpreter(b'1+1')
assert 0 == run_in_subinterpreter(b'2+2')
- assert 0 == run_in_subinterpreter(b'import package')
- assert 0 == run_in_subinterpreter(b'import package')
+ # The subinterpreter does not add the current working directory to
+ # sys.path, so we need to add it manually.
+ pre = b'import sys; sys.path.insert(0, "."); '
+ assert 0 == run_in_subinterpreter(pre + b'import package')
+ assert 0 == run_in_subinterpreter(pre + b'import package')
import sys
- result = run_in_subinterpreter(b'import package.subtest')
+ result = run_in_subinterpreter(pre + b'import package.subtest')
if not MAIN_HAS_IMPORTED:
assert result == 0, result # imports only in subinterpreters are ok
elif sys.version_info >= (3, 5):