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