Commit e2d55c56 authored by Stefan Behnel's avatar Stefan Behnel

Merge branch 'master' into release

parents c10046b9 4308e1e7
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
max_line_length = 120
# 4 space indentation
[*.{py,pyx,pxi,pxd,c,cpp,h,hpp}]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
# Tab indentation (no size specified)
[Makefile]
indent_style = tab
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2
......@@ -71,6 +71,8 @@ Features added
* New C macro ``CYTHON_HEX_VERSION`` to access Cython's version in the same style as
``PY_HEX_VERSION``.
* Constants in ``libc.math`` are now declared as ``const`` to simplify their handling.
Bugs fixed
----------
......@@ -102,6 +104,13 @@ Bugs fixed
* Fix declarations of builtin or C types using strings in pure python mode.
(Github issue #2046)
* Generator expressions and lambdas failed to compile in ``@cfunc`` functions.
(Github issue #459)
* Global names with ``const`` types were not excluded from star-import assignments
which could lead to invalid C code.
(Github issue #2621)
* Several internal function signatures were fixed that lead to warnings in gcc-8.
(Github issue #2363)
......@@ -123,6 +132,10 @@ Bugs fixed
module state. It now raises an exception instead, given that reloading is
not actually supported.
* Object-returning, C++ exception throwing functions were not checking that
the return value was non-null.
Original patch by Matt Wozniski (Github Issue #2603)
Other changes
-------------
......
......@@ -197,10 +197,12 @@ def get_exception_handler(exception_value):
else:
return '%s(); if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError , "Error converting c++ exception.");' % exception_value.entry.cname
def translate_cpp_exception(code, pos, inside, exception_value, nogil):
def translate_cpp_exception(code, pos, inside, py_result, exception_value, nogil):
raise_py_exception = get_exception_handler(exception_value)
code.putln("try {")
code.putln("%s" % inside)
if py_result:
code.putln(code.error_goto_if_null(py_result, pos))
code.putln("} catch(...) {")
if nogil:
code.put_ensure_gil(declare_gilstate=True)
......@@ -2306,7 +2308,11 @@ class NameNode(AtomicExprNode):
if overloaded_assignment:
result = rhs.result()
if exception_check == '+':
translate_cpp_exception(code, self.pos, '%s = %s;' % (self.result(), result), exception_value, self.in_nogil_context)
translate_cpp_exception(
code, self.pos,
'%s = %s;' % (self.result(), result),
self.result() if self.type.is_pyobject else None,
exception_value, self.in_nogil_context)
else:
code.putln('%s = %s;' % (self.result(), result))
else:
......@@ -3978,6 +3984,7 @@ class IndexNode(_IndexingBaseNode):
translate_cpp_exception(code, self.pos,
"%s = %s[%s];" % (self.result(), self.base.result(),
self.index.result()),
self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context)
else:
error_check = '!%s' if error_value == 'NULL' else '%%s == %s' % error_value
......@@ -4048,6 +4055,7 @@ class IndexNode(_IndexingBaseNode):
# 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.exception_value, self.in_nogil_context)
else:
code.putln(
......@@ -5838,7 +5846,7 @@ class SimpleCallNode(CallNode):
elif self.type.is_memoryviewslice:
assert self.is_temp
exc_checks.append(self.type.error_condition(self.result()))
else:
elif func_type.exception_check != '+':
exc_val = func_type.exception_value
exc_check = func_type.exception_check
if exc_val is not None:
......@@ -5861,6 +5869,7 @@ class SimpleCallNode(CallNode):
lhs = ""
if func_type.exception_check == '+':
translate_cpp_exception(code, self.pos, '%s%s;' % (lhs, rhs),
self.result() if self.type.is_pyobject else None,
func_type.exception_value, self.nogil)
else:
if (self.overflowcheck
......@@ -10038,6 +10047,7 @@ class UnopNode(ExprNode):
if self.is_cpp_operation() and self.exception_check == '+':
translate_cpp_exception(code, self.pos,
"%s = %s %s;" % (self.result(), self.operator, self.operand.result()),
self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context)
else:
code.putln("%s = %s %s;" % (self.result(), self.operator, self.operand.result()))
......@@ -10277,6 +10287,7 @@ class AmpersandNode(CUnopNode):
if (self.operand.type.is_cpp_class and self.exception_check == '+'):
translate_cpp_exception(code, self.pos,
"%s = %s %s;" % (self.result(), self.operator, self.operand.result()),
self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context)
......@@ -10821,7 +10832,7 @@ class TypeidNode(ExprNode):
arg_code = self.arg_type.result()
translate_cpp_exception(code, self.pos,
"%s = typeid(%s);" % (self.temp_code, arg_code),
None, self.in_nogil_context)
None, None, self.in_nogil_context)
class TypeofNode(ExprNode):
# Compile-time type of an expression, as a string.
......@@ -11080,6 +11091,7 @@ class BinopNode(ExprNode):
if self.is_cpp_operation() and self.exception_check == '+':
translate_cpp_exception(code, self.pos,
"%s = %s;" % (self.result(), self.calculate_result_code()),
self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context)
else:
code.putln("%s = %s;" % (self.result(), self.calculate_result_code()))
......@@ -12393,7 +12405,13 @@ class CmpNode(object):
self.c_operator(op),
code2)
if self.is_cpp_comparison() and self.exception_check == '+':
translate_cpp_exception(code, self.pos, statement, self.exception_value, self.in_nogil_context)
translate_cpp_exception(
code,
self.pos,
statement,
result_code if self.type.is_pyobject else None,
self.exception_value,
self.in_nogil_context)
code.putln(statement)
def c_operator(self, op):
......
......@@ -2238,7 +2238,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("if (0);") # so the first one can be "else if"
msvc_count = 0
for name, entry in sorted(env.entries.items()):
if entry.is_cglobal and entry.used:
if entry.is_cglobal and entry.used and not entry.type.is_const:
msvc_count += 1
if msvc_count % 100 == 0:
code.putln("#ifdef _MSC_VER")
......
......@@ -207,6 +207,9 @@ class Node(object):
# can either contain a single node or a list of nodes. See Visitor.py.
child_attrs = None
# Subset of attributes that are evaluated in the outer scope (e.g. function default arguments).
outer_attrs = None
cf_state = None
# This may be an additional (or 'actual') type that will be checked when
......@@ -222,6 +225,7 @@ class Node(object):
gil_message = "Operation"
nogil_check = None
in_nogil_context = False # For use only during code generation.
def gil_error(self, env=None):
error(self.pos, "%s not allowed without gil" % self.gil_message)
......@@ -848,6 +852,7 @@ class CArgDeclNode(Node):
# is_dynamic boolean Non-literal arg stored inside CyFunction
child_attrs = ["base_type", "declarator", "default", "annotation"]
outer_attrs = ["default", "annotation"]
is_self_arg = 0
is_type_arg = 0
......@@ -2335,7 +2340,7 @@ class CFuncDefNode(FuncDefNode):
self.is_c_class_method = env.is_c_class_scope
if self.directive_locals is None:
self.directive_locals = {}
self.directive_locals.update(env.directives['locals'])
self.directive_locals.update(env.directives.get('locals', {}))
if self.directive_returns is not None:
base_type = self.directive_returns.analyse_as_type(env)
if base_type is None:
......@@ -2737,6 +2742,7 @@ class DefNode(FuncDefNode):
# decorator_indirection IndirectionNode Used to remove __Pyx_Method_ClassMethod for fused functions
child_attrs = ["args", "star_arg", "starstar_arg", "body", "decorators", "return_type_annotation"]
outer_attrs = ["decorators", "return_type_annotation"]
is_staticmethod = False
is_classmethod = False
......@@ -2901,7 +2907,7 @@ class DefNode(FuncDefNode):
self.py_wrapper.analyse_declarations(env)
def analyse_argument_types(self, env):
self.directive_locals = env.directives['locals']
self.directive_locals = env.directives.get('locals', {})
allow_none_for_extension_args = env.directives['allow_none_for_extension_args']
f2s = env.fused_to_specific
......@@ -4953,7 +4959,7 @@ class CClassDefNode(ClassDefNode):
code.putln("if (__Pyx_MergeVtables(&%s) < 0) %s" % (
typeobj_cname,
code.error_goto(entry.pos)))
if not type.scope.is_internal and not type.scope.directives['internal']:
if not type.scope.is_internal and not type.scope.directives.get('internal'):
# scope.is_internal is set for types defined by
# Cython (such as closures), the 'internal'
# directive is set by users
......
......@@ -170,8 +170,6 @@ _directive_defaults = {
'nonecheck' : False,
'initializedcheck' : True,
'embedsignature' : False,
'locals' : {},
'exceptval' : None, # (except value=None, check=True)
'auto_cpdef': False,
'auto_pickle': None,
'cdivision': False, # was True before 0.12
......@@ -183,12 +181,8 @@ _directive_defaults = {
'wraparound' : True,
'ccomplex' : False, # use C99/C++ for complex types and arith
'callspec' : "",
'final' : False,
'nogil' : False,
'internal' : False,
'profile': False,
'no_gc_clear': False,
'no_gc': False,
'linetrace': False,
'emit_code_comments': True, # copy original source code into C code comments
'annotation_typing': True, # read type declarations from Python function annotations
......@@ -241,7 +235,6 @@ _directive_defaults = {
# experimental, subject to change
'binding': None,
'freelist': 0,
'formal_grammar': False,
}
......@@ -301,7 +294,9 @@ def normalise_encoding_name(option_name, encoding):
directive_types = {
'language_level': int, # values can be None/2/3, where None == 2+warning
'auto_pickle': bool,
'locals': dict,
'final' : bool, # final cdef classes and methods
'nogil' : bool,
'internal' : bool, # cdef class visibility in the module dict
'infer_types' : bool, # values can be True/None/False
'binding' : bool,
......@@ -310,7 +305,10 @@ directive_types = {
'inline' : None,
'staticmethod' : None,
'cclass' : None,
'no_gc_clear' : bool,
'no_gc' : bool,
'returns' : type,
'exceptval': type, # actually (type, check=True/False), but has its own parser
'set_initial_path': str,
'freelist': int,
'c_string_type': one_of('bytes', 'bytearray', 'str', 'unicode'),
......@@ -325,8 +323,10 @@ directive_scopes = { # defaults to available everywhere
# 'module', 'function', 'class', 'with statement'
'auto_pickle': ('module', 'cclass'),
'final' : ('cclass', 'function'),
'nogil' : ('function',),
'nogil' : ('function', 'with statement'),
'inline' : ('function',),
'cfunc' : ('function', 'with statement'),
'ccall' : ('function', 'with statement'),
'returns' : ('function',),
'exceptval' : ('function',),
'locals' : ('function',),
......@@ -334,6 +334,7 @@ directive_scopes = { # defaults to available everywhere
'no_gc_clear' : ('cclass',),
'no_gc' : ('cclass',),
'internal' : ('cclass',),
'cclass' : ('class', 'cclass', 'with statement'),
'autotestdict' : ('module',),
'autotestdict.all' : ('module',),
'autotestdict.cdef' : ('module',),
......
......@@ -958,30 +958,33 @@ class InterpretCompilerDirectives(CythonTransform):
else:
assert False
def visit_with_directives(self, body, directives):
olddirectives = self.directives
newdirectives = copy.copy(olddirectives)
newdirectives.update(directives)
self.directives = newdirectives
assert isinstance(body, Nodes.StatListNode), body
retbody = self.visit_Node(body)
directive = Nodes.CompilerDirectivesNode(pos=retbody.pos, body=retbody,
directives=newdirectives)
self.directives = olddirectives
return directive
def visit_with_directives(self, node, directives):
if not directives:
return self.visit_Node(node)
old_directives = self.directives
new_directives = dict(old_directives)
new_directives.update(directives)
if new_directives == old_directives:
return self.visit_Node(node)
self.directives = new_directives
retbody = self.visit_Node(node)
self.directives = old_directives
if not isinstance(retbody, Nodes.StatListNode):
retbody = Nodes.StatListNode(node.pos, stats=[retbody])
return Nodes.CompilerDirectivesNode(
pos=retbody.pos, body=retbody, directives=new_directives)
# Handle decorators
def visit_FuncDefNode(self, node):
directives = self._extract_directives(node, 'function')
if not directives:
return self.visit_Node(node)
body = Nodes.StatListNode(node.pos, stats=[node])
return self.visit_with_directives(body, directives)
return self.visit_with_directives(node, directives)
def visit_CVarDefNode(self, node):
directives = self._extract_directives(node, 'function')
if not directives:
return self.visit_Node(node)
for name, value in directives.items():
if name == 'locals':
node.directive_locals = value
......@@ -990,29 +993,19 @@ class InterpretCompilerDirectives(CythonTransform):
node.pos,
"Cdef functions can only take cython.locals(), "
"staticmethod, or final decorators, got %s." % name))
body = Nodes.StatListNode(node.pos, stats=[node])
return self.visit_with_directives(body, directives)
return self.visit_with_directives(node, directives)
def visit_CClassDefNode(self, node):
directives = self._extract_directives(node, 'cclass')
if not directives:
return self.visit_Node(node)
body = Nodes.StatListNode(node.pos, stats=[node])
return self.visit_with_directives(body, directives)
return self.visit_with_directives(node, directives)
def visit_CppClassNode(self, node):
directives = self._extract_directives(node, 'cppclass')
if not directives:
return self.visit_Node(node)
body = Nodes.StatListNode(node.pos, stats=[node])
return self.visit_with_directives(body, directives)
return self.visit_with_directives(node, directives)
def visit_PyClassDefNode(self, node):
directives = self._extract_directives(node, 'class')
if not directives:
return self.visit_Node(node)
body = Nodes.StatListNode(node.pos, stats=[node])
return self.visit_with_directives(body, directives)
return self.visit_with_directives(node, directives)
def _extract_directives(self, node, scope_name):
if not node.decorators:
......@@ -1059,7 +1052,7 @@ class InterpretCompilerDirectives(CythonTransform):
optdict[name] = value
return optdict
# Handle with statements
# Handle with-statements
def visit_WithStatNode(self, node):
directive_dict = {}
for directive in self.try_to_parse_directives(node.manager) or []:
......@@ -2385,6 +2378,10 @@ class AdjustDefByDirectives(CythonTransform, SkipDeclarations):
self.visitchildren(node)
return node
def visit_LambdaNode(self, node):
# No directives should modify lambdas or generator expressions (and also nothing in them).
return node
def visit_PyClassDefNode(self, node):
if 'cclass' in self.directives:
node = node.as_cclass()
......@@ -2680,7 +2677,7 @@ class CreateClosureClasses(CythonTransform):
if node.is_generator:
for scope in node.local_scope.iter_local_scopes():
for entry in scope.entries.values():
if not entry.from_closure:
if not (entry.from_closure or entry.is_pyglobal or entry.is_cglobal):
entry.in_closure = True
from_closure, in_closure = self.find_entries_used_in_closures(node)
......@@ -2861,24 +2858,33 @@ class GilCheck(VisitorTransform):
self.nogil_declarator_only = False
return super(GilCheck, self).__call__(root)
def _visit_scoped_children(self, node, gil_state):
was_nogil = self.nogil
outer_attrs = node.outer_attrs
if outer_attrs and len(self.env_stack) > 1:
self.nogil = self.env_stack[-2].nogil
self.visitchildren(node, outer_attrs)
self.nogil = gil_state
self.visitchildren(node, exclude=outer_attrs)
self.nogil = was_nogil
def visit_FuncDefNode(self, node):
self.env_stack.append(node.local_scope)
was_nogil = self.nogil
self.nogil = node.local_scope.nogil
inner_nogil = node.local_scope.nogil
if self.nogil:
if inner_nogil:
self.nogil_declarator_only = True
if self.nogil and node.nogil_check:
if inner_nogil and node.nogil_check:
node.nogil_check(node.local_scope)
self.visitchildren(node)
self._visit_scoped_children(node, inner_nogil)
# This cannot be nested, so it doesn't need backup/restore
self.nogil_declarator_only = False
self.env_stack.pop()
self.nogil = was_nogil
return node
def visit_GILStatNode(self, node):
......@@ -2886,9 +2892,9 @@ class GilCheck(VisitorTransform):
node.nogil_check()
was_nogil = self.nogil
self.nogil = (node.state == 'nogil')
is_nogil = (node.state == 'nogil')
if was_nogil == self.nogil and not self.nogil_declarator_only:
if was_nogil == is_nogil and not self.nogil_declarator_only:
if not was_nogil:
error(node.pos, "Trying to acquire the GIL while it is "
"already held.")
......@@ -2901,8 +2907,7 @@ class GilCheck(VisitorTransform):
# which is wrapped in a StatListNode. Just unpack that.
node.finally_clause, = node.finally_clause.stats
self.visitchildren(node)
self.nogil = was_nogil
self._visit_scoped_children(node, is_nogil)
return node
def visit_ParallelRangeNode(self, node):
......@@ -2949,8 +2954,12 @@ class GilCheck(VisitorTransform):
def visit_Node(self, node):
if self.env_stack and self.nogil and node.nogil_check:
node.nogil_check(self.env_stack[-1])
self.visitchildren(node)
node.in_nogil_context = self.nogil
if node.outer_attrs:
self._visit_scoped_children(node, self.nogil)
else:
self.visitchildren(node)
if self.nogil:
node.in_nogil_context = True
return node
......
......@@ -16,8 +16,9 @@ cdef class TreeVisitor:
cdef class VisitorTransform(TreeVisitor):
cdef dict _process_children(self, parent, attrs=*)
cpdef visitchildren(self, parent, attrs=*)
cpdef visitchildren(self, parent, attrs=*, exclude=*)
cdef list _flatten_list(self, list orig_list)
cdef list _select_attrs(self, attrs, exclude)
cdef class CythonTransform(VisitorTransform):
cdef public context
......
......@@ -244,10 +244,16 @@ class VisitorTransform(TreeVisitor):
was not, an exception will be raised. (Typically you want to ensure that you
are within a StatListNode or similar before doing this.)
"""
def visitchildren(self, parent, attrs=None):
def visitchildren(self, parent, attrs=None, exclude=None):
# generic def entry point for calls from Python subclasses
if exclude is not None:
attrs = self._select_attrs(parent.child_attrs if attrs is None else attrs, exclude)
return self._process_children(parent, attrs)
@cython.final
def _select_attrs(self, attrs, exclude):
return [name for name in attrs if name not in exclude]
@cython.final
def _process_children(self, parent, attrs=None):
# fast cdef entry point for calls from Cython subclasses
......
cdef extern from "<math.h>" nogil:
double M_E
double e "M_E" # as in Python's math module
double M_LOG2E
double M_LOG10E
double M_LN2
double M_LN10
double M_PI
double pi "M_PI" # as in Python's math module
double M_PI_2
double M_PI_4
double M_1_PI
double M_2_PI
double M_2_SQRTPI
double M_SQRT2
double M_SQRT1_2
const double M_E
const double e "M_E" # as in Python's math module
const double M_LOG2E
const double M_LOG10E
const double M_LN2
const double M_LN10
const double M_PI
const double pi "M_PI" # as in Python's math module
const double M_PI_2
const double M_PI_4
const double M_1_PI
const double M_2_PI
const double M_2_SQRTPI
const double M_SQRT2
const double M_SQRT1_2
# C99 constants
float INFINITY
float NAN
const float INFINITY
const float NAN
# note: not providing "nan" and "inf" aliases here as nan() is a function in C
double HUGE_VAL
float HUGE_VALF
long double HUGE_VALL
const double HUGE_VAL
const float HUGE_VALF
const long double HUGE_VALL
double acos(double x)
double asin(double x)
......
......@@ -455,7 +455,7 @@ class CythonDotParallel(object):
def parallel(self, num_threads=None):
return nogil
def prange(self, start=0, stop=None, step=1, schedule=None, nogil=False):
def prange(self, start=0, stop=None, step=1, nogil=False, schedule=None, chunksize=None, num_threads=None):
if stop is None:
stop = start
start = 0
......
......@@ -376,7 +376,7 @@ static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {
if (sizeof(Py_ssize_t) >= sizeof(long))
return PyInt_AS_LONG(b);
else
return PyInt_AsSsize_t(x);
return PyInt_AsSsize_t(b);
}
#endif
if (likely(PyLong_CheckExact(b))) {
......
......@@ -34,3 +34,21 @@ def two_dim(a: cython.double[:,:]):
"""
a[0,0] *= 3
return a[0,0], a[0,1], a.ndim
@cython.nogil
@cython.cfunc
def _one_dim_nogil_cfunc(a: cython.double[:]) -> cython.double:
a[0] *= 2
return a[0]
def one_dim_nogil_cfunc(a: cython.double[:]):
"""
>>> a = numpy.ones((10,), numpy.double)
>>> one_dim_nogil_cfunc(a)
2.0
"""
with cython.nogil:
result = _one_dim_nogil_cfunc(a)
return result
# mode: run
# tag: pep492, asyncfor, await, gh2613
# Using C-globals in coroutines.
cdef object py_retval
async def test():
"""
>>> t = test()
>>> try: t.send(None)
... except StopIteration as ex:
... print(ex.args[0] if ex.args else None)
... else: print("NOT STOPPED!")
None
"""
global py_retval
py_retval = {'foo': 42}
......@@ -21,6 +21,8 @@ cdef extern from "cpp_exceptions_helper.h":
cdef void raise_typeerror() except +
cdef void raise_underflow() except +
cdef raise_or_throw(bint py) except +
cdef cppclass Foo:
int bar_raw "bar"(bint fire) except +
int bar_value "bar"(bint fire) except +ValueError
......@@ -98,6 +100,19 @@ def test_underflow():
"""
raise_underflow()
def test_func_that_can_raise_or_throw(bint py):
"""
>>> test_func_that_can_raise_or_throw(0)
Traceback (most recent call last):
...
RuntimeError: oopsie
>>> test_func_that_can_raise_or_throw(1)
Traceback (most recent call last):
...
ValueError: oopsie
"""
raise_or_throw(py)
def test_int_raw(bint fire):
"""
>>> test_int_raw(False)
......
#include <Python.h>
#include <ios>
#include <new>
#include <stdexcept>
......@@ -61,3 +62,11 @@ void raise_typeerror() {
void raise_underflow() {
throw std::underflow_error("underflow_error");
}
PyObject *raise_or_throw(int py) {
if (!py) {
throw std::runtime_error("oopsie");
}
PyErr_SetString(PyExc_ValueError, "oopsie");
return NULL;
}
......@@ -451,3 +451,19 @@ cdef class ExtTypeWithRefCycle:
def __init__(self, obj=None):
self.attribute = obj
@cython.freelist(3)
@cython.cclass
class DecoratedPyClass(object):
"""
>>> obj1 = DecoratedPyClass()
>>> obj2 = DecoratedPyClass()
>>> obj3 = DecoratedPyClass()
>>> obj4 = DecoratedPyClass()
>>> obj1 = DecoratedPyClass()
>>> obj2 = DecoratedPyClass()
>>> obj3 = DecoratedPyClass()
>>> obj4 = DecoratedPyClass()
"""
......@@ -10,7 +10,9 @@ ctypedef struct MyStruct:
# (there used to be a problem getting Cython conversion code generated here)
cdef MyStruct _no_such_name_ = MyStruct(1, 2, 3)
from libc.math cimport NAN
# Danger ahead!
from sys import *
......@@ -39,3 +41,13 @@ def test_non_cdefed_names():
>>> assert pth is not None
"""
return modules, path
def test_cimported_NAN():
"""
>>> from math import isnan
>>> nan = test_cimported_NAN()
>>> isnan(nan)
True
"""
return NAN
# mode: run
# tag: openmp, pure3.6
import cython
from cython.parallel import prange, parallel
def prange_regression(n: cython.int, data: list):
"""
>>> prange_regression(10, list(range(1, 4)))
19
"""
s: cython.int = 0
i: cython.int
d: cython.int[3] = data
for i in prange(n, num_threads=3, nogil=True):
s += d[i % 3]
return s
def prange_with_gil(n: cython.int, x):
"""
>>> sum(3*i for i in range(10))
135
>>> prange_with_gil(10, 3)
135
"""
i: cython.int
s: cython.int = 0
for i in prange(n, num_threads=3, nogil=True):
with cython.gil:
s += x * i
return s
@cython.cfunc
def use_nogil(x, i: cython.int) -> cython.int:
cx: cython.int = x
with cython.nogil:
return cx * i
def prange_with_gil_call_nogil(n: cython.int, x):
"""
>>> sum(3*i for i in range(10))
135
>>> prange_with_gil(10, 3)
135
"""
i: cython.int
s: cython.int = 0
for i in prange(n, num_threads=3, nogil=True):
with cython.gil:
s += use_nogil(x, i)
return s
......@@ -28,6 +28,17 @@ with cfunc:
def fwith2(a):
return a*4
@cython.test_assert_path_exists(
'//CFuncDefNode',
'//LambdaNode',
'//GeneratorDefNode',
'//GeneratorBodyDefNode',
)
def f_with_genexpr(a):
f = lambda x: x+1
return (f(x) for x in a)
with cclass:
@cython.test_assert_path_exists('//CClassDefNode')
class Egg(object):
......@@ -123,3 +134,14 @@ def test_typed_return():
"""
x = cython.declare(int, 5)
assert typed_return(cython.address(x))[0] is x
def test_genexpr_in_cdef(l):
"""
>>> gen = test_genexpr_in_cdef([1, 2, 3])
>>> list(gen)
[2, 3, 4]
>>> list(gen)
[]
"""
return f_with_genexpr(l)
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