Commit 394aa9a8 authored by Robert Bradshaw's avatar Robert Bradshaw

Merge branch 'master' into static

Conflicts:
	CHANGES.rst
parents 306b3a2b 7f36b856
...@@ -6,8 +6,11 @@ python: ...@@ -6,8 +6,11 @@ python:
- 3.2 - 3.2
- 3.3 - 3.3
- 3.4 - 3.4
- pypy
# - pypy env:
- BACKEND=c
- BACKEND=cpp
branches: branches:
only: only:
...@@ -21,9 +24,14 @@ before_install: ...@@ -21,9 +24,14 @@ before_install:
install: CFLAGS="-O2 -ggdb" pip install . install: CFLAGS="-O2 -ggdb" pip install .
script: script:
- PYTHON="python$( python -c 'import sys; print("%d.%d" % sys.version_info[:2])' )-dbg" - PYTHON_DBG="python$( python -c 'import sys; print("%d.%d" % sys.version_info[:2])' )-dbg"
- CFLAGS="-O0 -ggdb" $( $PYTHON -V >&2 && echo $PYTHON || echo python ) runtests.py -vv - if $PYTHON_DBG -V >&2; then CFLAGS="-O0 -ggdb" $PYTHON_DBG runtests.py -vv Debugger --backends=$BACKEND; fi
- CFLAGS="-O0 -ggdb" python runtests.py -vv -x Debugger --backends=$BACKEND
matrix: matrix:
allow_failures: allow_failures:
- python: pypy - python: pypy
exclude:
- python: pypy
env: BACKEND=cpp
fast_finish: true
...@@ -9,10 +9,20 @@ Latest ...@@ -9,10 +9,20 @@ Latest
Features added Features added
-------------- --------------
* Enums can now be declared as cpdef to export their values to
the module's Python namespace. Cpdef enums in pxd files export
their values to their own module, iff it exists.
* Allow @staticmethod decorator to declare static cdef methods. * Allow @staticmethod decorator to declare static cdef methods.
This is especially useful for declaring "constructors" for This is especially useful for declaring "constructors" for
cdef classes that can take non-Python arguments. cdef classes that can take non-Python arguments.
* Taking a ``char*`` from a temporary Python string object is safer
in more cases and can be done inside of non-trivial expressions,
including arguments of a function call. A compile time error
is raised only when such a pointer is assigned to a variable and
would thus exceed the lifetime of the string itself.
* Generators have new properties ``__name__`` and ``__qualname__`` * Generators have new properties ``__name__`` and ``__qualname__``
that provide the plain/qualified name of the generator function that provide the plain/qualified name of the generator function
(following CPython 3.5). See http://bugs.python.org/issue21205 (following CPython 3.5). See http://bugs.python.org/issue21205
...@@ -28,9 +38,6 @@ Features added ...@@ -28,9 +38,6 @@ Features added
* HTML output of annotated code uses Pygments for code highlighting * HTML output of annotated code uses Pygments for code highlighting
and generally received a major overhaul by Matthias Bussonier. and generally received a major overhaul by Matthias Bussonier.
* The Python expression "2 ** N" is optimised into bit shifting.
See http://bugs.python.org/issue21420
* Simple support for declaring Python object types in Python signature * Simple support for declaring Python object types in Python signature
annotations. Currently requires setting the compiler directive annotations. Currently requires setting the compiler directive
``annotation_typing=True``. ``annotation_typing=True``.
...@@ -38,9 +45,23 @@ Features added ...@@ -38,9 +45,23 @@ Features added
* New directive ``use_switch`` (defaults to True) to optionally disable * New directive ``use_switch`` (defaults to True) to optionally disable
the optimization of chained if statement to C switch statements. the optimization of chained if statement to C switch statements.
Optimizations
-------------
* The Python expression "2 ** N" is optimised into bit shifting.
See http://bugs.python.org/issue21420
* Cascaded assignments (a = b = ...) try to minimise the number of
type coercions.
* Calls to ``slice()`` are translated to a straight C-API call.
Bugs fixed Bugs fixed
---------- ----------
* Taking a ``char*`` from an indexed Python string generated unsafe
reference counting code.
* Set literals now create all of their items before trying to add them * Set literals now create all of their items before trying to add them
to the set, following the behaviour in CPython. This makes a to the set, following the behaviour in CPython. This makes a
difference in the rare case that the item creation has side effects difference in the rare case that the item creation has side effects
...@@ -68,7 +89,7 @@ Bugs fixed ...@@ -68,7 +89,7 @@ Bugs fixed
* Syntax highlighting in ``cython-mode.el`` for Emacs no longer * Syntax highlighting in ``cython-mode.el`` for Emacs no longer
incorrectly highlights keywords found as part of longer names. incorrectly highlights keywords found as part of longer names.
* Correctly handle `from cython.submodule comport name``. * Correctly handle ``from cython.submodule cimport name``.
Other changes Other changes
------------- -------------
......
...@@ -792,6 +792,8 @@ def cythonize(module_list, exclude=[], nthreads=0, aliases=None, quiet=False, fo ...@@ -792,6 +792,8 @@ def cythonize(module_list, exclude=[], nthreads=0, aliases=None, quiet=False, fo
if not os.path.exists(options.cache): if not os.path.exists(options.cache):
os.makedirs(options.cache) os.makedirs(options.cache)
to_compile.sort() to_compile.sort()
if len(to_compile) <= 1:
nthreads = 0
if nthreads: if nthreads:
# Requires multiprocessing (or Python >= 2.6) # Requires multiprocessing (or Python >= 2.6)
try: try:
......
...@@ -596,7 +596,7 @@ class ExprNode(Node): ...@@ -596,7 +596,7 @@ class ExprNode(Node):
self.allocate_temp_result(code) self.allocate_temp_result(code)
self.generate_result_code(code) self.generate_result_code(code)
if self.is_temp: if self.is_temp and not (self.type.is_string or self.type.is_pyunicode_ptr):
# If we are temp we do not need to wait until this node is disposed # If we are temp we do not need to wait until this node is disposed
# before disposing children. # before disposing children.
self.generate_subexpr_disposal_code(code) self.generate_subexpr_disposal_code(code)
...@@ -611,6 +611,10 @@ class ExprNode(Node): ...@@ -611,6 +611,10 @@ class ExprNode(Node):
def generate_disposal_code(self, code): def generate_disposal_code(self, code):
if self.is_temp: if self.is_temp:
if self.type.is_string or self.type.is_pyunicode_ptr:
# postponed from self.generate_evaluation_code()
self.generate_subexpr_disposal_code(code)
self.free_subexpr_temps(code)
if self.result(): if self.result():
if self.type.is_pyobject: if self.type.is_pyobject:
code.put_decref_clear(self.result(), self.ctype()) code.put_decref_clear(self.result(), self.ctype())
...@@ -629,7 +633,11 @@ class ExprNode(Node): ...@@ -629,7 +633,11 @@ class ExprNode(Node):
def generate_post_assignment_code(self, code): def generate_post_assignment_code(self, code):
if self.is_temp: if self.is_temp:
if self.type.is_pyobject: if self.type.is_string or self.type.is_pyunicode_ptr:
# postponed from self.generate_evaluation_code()
self.generate_subexpr_disposal_code(code)
self.free_subexpr_temps(code)
elif self.type.is_pyobject:
code.putln("%s = 0;" % self.result()) code.putln("%s = 0;" % self.result())
elif self.type.is_memoryviewslice: elif self.type.is_memoryviewslice:
code.putln("%s.memview = NULL;" % self.result()) code.putln("%s.memview = NULL;" % self.result())
...@@ -2796,7 +2804,10 @@ class IndexNode(ExprNode): ...@@ -2796,7 +2804,10 @@ class IndexNode(ExprNode):
self.compile_time_value_error(e) self.compile_time_value_error(e)
def is_ephemeral(self): def is_ephemeral(self):
return self.base.is_ephemeral() # in most cases, indexing will return a safe reference to an object in a container,
# so we consider the result safe if the base object is
return self.base.is_ephemeral() or self.base.type in (
basestring_type, str_type, bytes_type, unicode_type)
def is_simple(self): def is_simple(self):
if self.is_buffer_access or self.memslice_index: if self.is_buffer_access or self.memslice_index:
...@@ -8455,6 +8466,10 @@ class TypecastNode(ExprNode): ...@@ -8455,6 +8466,10 @@ class TypecastNode(ExprNode):
# either temp or a C cast => no side effects other than the operand's # either temp or a C cast => no side effects other than the operand's
return self.operand.is_simple() return self.operand.is_simple()
def is_ephemeral(self):
# either temp or a C cast => no side effects other than the operand's
return self.operand.is_ephemeral()
def nonlocally_immutable(self): def nonlocally_immutable(self):
return self.is_temp or self.operand.nonlocally_immutable() return self.is_temp or self.operand.nonlocally_immutable()
...@@ -9014,6 +9029,10 @@ class BinopNode(ExprNode): ...@@ -9014,6 +9029,10 @@ class BinopNode(ExprNode):
def check_const(self): def check_const(self):
return self.operand1.check_const() and self.operand2.check_const() return self.operand1.check_const() and self.operand2.check_const()
def is_ephemeral(self):
return (super(BinopNode, self).is_ephemeral() or
self.operand1.is_ephemeral() or self.operand2.is_ephemeral())
def generate_result_code(self, code): def generate_result_code(self, code):
#print "BinopNode.generate_result_code:", self.operand1, self.operand2 ### #print "BinopNode.generate_result_code:", self.operand1, self.operand2 ###
if self.operand1.type.is_pyobject: if self.operand1.type.is_pyobject:
...@@ -9648,18 +9667,24 @@ class BoolBinopNode(ExprNode): ...@@ -9648,18 +9667,24 @@ class BoolBinopNode(ExprNode):
or self.operand2.compile_time_value(denv) or self.operand2.compile_time_value(denv)
def coerce_to_boolean(self, env): def coerce_to_boolean(self, env):
return BoolBinopNode( return BoolBinopNode.from_node(
self.pos, self,
operator = self.operator, operator=self.operator,
operand1 = self.operand1.coerce_to_boolean(env), operand1=self.operand1.coerce_to_boolean(env),
operand2 = self.operand2.coerce_to_boolean(env), operand2=self.operand2.coerce_to_boolean(env),
type = PyrexTypes.c_bint_type, type=PyrexTypes.c_bint_type,
is_temp = self.is_temp) is_temp=self.is_temp)
def analyse_types(self, env): def analyse_types(self, env):
self.operand1 = self.operand1.analyse_types(env) self.operand1 = self.operand1.analyse_types(env)
self.operand2 = self.operand2.analyse_types(env) self.operand2 = self.operand2.analyse_types(env)
self.type = PyrexTypes.independent_spanning_type(self.operand1.type, self.operand2.type) self.type = PyrexTypes.independent_spanning_type(
self.operand1.type, self.operand2.type)
# note: self.type might be ErrorType, but we allow this here
# in order to support eventual coercion to boolean
if not self.type.is_pyobject and not self.type.is_error:
if self.operand1.is_ephemeral() or self.operand2.is_ephemeral():
error(self.pos, "Unsafe C derivative of temporary Python reference used in and/or expression")
self.operand1 = self.operand1.coerce_to(self.type, env) self.operand1 = self.operand1.coerce_to(self.type, env)
self.operand2 = self.operand2.coerce_to(self.type, env) self.operand2 = self.operand2.coerce_to(self.type, env)
...@@ -9676,6 +9701,9 @@ class BoolBinopNode(ExprNode): ...@@ -9676,6 +9701,9 @@ class BoolBinopNode(ExprNode):
return self.operand1.check_const() and self.operand2.check_const() return self.operand1.check_const() and self.operand2.check_const()
def generate_evaluation_code(self, code): def generate_evaluation_code(self, code):
if self.type is error_type:
# quite clearly, we did *not* coerce to boolean, but both operand types mismatch
error(self.pos, "incompatible types in short-circuiting boolean expression not resolved")
code.mark_pos(self.pos) code.mark_pos(self.pos)
self.operand1.generate_evaluation_code(code) self.operand1.generate_evaluation_code(code)
test_result, uses_temp = self.generate_operand1_test(code) test_result, uses_temp = self.generate_operand1_test(code)
...@@ -9703,6 +9731,12 @@ class BoolBinopNode(ExprNode): ...@@ -9703,6 +9731,12 @@ class BoolBinopNode(ExprNode):
self.operand1.free_temps(code) self.operand1.free_temps(code)
code.putln("}") code.putln("}")
def generate_subexpr_disposal_code(self, code):
pass # nothing to do here, all done in generate_evaluation_code()
def free_subexpr_temps(self, code):
pass # nothing to do here, all done in generate_evaluation_code()
def generate_operand1_test(self, code): def generate_operand1_test(self, code):
# Generate code to test the truth of the first operand. # Generate code to test the truth of the first operand.
if self.type.is_pyobject: if self.type.is_pyobject:
...@@ -9744,6 +9778,9 @@ class CondExprNode(ExprNode): ...@@ -9744,6 +9778,9 @@ class CondExprNode(ExprNode):
else: else:
self.constant_result = self.false_val.constant_result self.constant_result = self.false_val.constant_result
def is_ephemeral(self):
return self.true_val.is_ephemeral() or self.false_val.is_ephemeral()
def analyse_types(self, env): def analyse_types(self, env):
self.test = self.test.analyse_types(env).coerce_to_boolean(env) self.test = self.test.analyse_types(env).coerce_to_boolean(env)
self.true_val = self.true_val.analyse_types(env) self.true_val = self.true_val.analyse_types(env)
...@@ -9756,6 +9793,8 @@ class CondExprNode(ExprNode): ...@@ -9756,6 +9793,8 @@ class CondExprNode(ExprNode):
self.true_val.type, self.false_val.type) self.true_val.type, self.false_val.type)
if self.type.is_pyobject: if self.type.is_pyobject:
self.result_ctype = py_object_type self.result_ctype = py_object_type
elif self.true_val.is_ephemeral() or self.false_val.is_ephemeral():
error(self.pos, "Unsafe C derivative of temporary Python reference used in conditional expression")
if self.true_val.type.is_pyobject or self.false_val.type.is_pyobject: if self.true_val.type.is_pyobject or self.false_val.type.is_pyobject:
self.true_val = self.true_val.coerce_to(self.type, env) self.true_val = self.true_val.coerce_to(self.type, env)
self.false_val = self.false_val.coerce_to(self.type, env) self.false_val = self.false_val.coerce_to(self.type, env)
...@@ -9787,7 +9826,7 @@ class CondExprNode(ExprNode): ...@@ -9787,7 +9826,7 @@ class CondExprNode(ExprNode):
code.mark_pos(self.pos) code.mark_pos(self.pos)
self.allocate_temp_result(code) self.allocate_temp_result(code)
self.test.generate_evaluation_code(code) self.test.generate_evaluation_code(code)
code.putln("if (%s) {" % self.test.result() ) code.putln("if (%s) {" % self.test.result())
self.eval_and_get(code, self.true_val) self.eval_and_get(code, self.true_val)
code.putln("} else {") code.putln("} else {")
self.eval_and_get(code, self.false_val) self.eval_and_get(code, self.false_val)
...@@ -9802,6 +9841,13 @@ class CondExprNode(ExprNode): ...@@ -9802,6 +9841,13 @@ class CondExprNode(ExprNode):
expr.generate_post_assignment_code(code) expr.generate_post_assignment_code(code)
expr.free_temps(code) expr.free_temps(code)
def generate_subexpr_disposal_code(self, code):
pass # done explicitly above (cleanup must separately happen within the if/else blocks)
def free_subexpr_temps(self, code):
pass # done explicitly above (cleanup must separately happen within the if/else blocks)
richcmp_constants = { richcmp_constants = {
"<" : "Py_LT", "<" : "Py_LT",
"<=": "Py_LE", "<=": "Py_LE",
...@@ -10871,10 +10917,7 @@ class CoerceFromPyTypeNode(CoercionNode): ...@@ -10871,10 +10917,7 @@ class CoerceFromPyTypeNode(CoercionNode):
error(arg.pos, error(arg.pos,
"Cannot convert Python object to '%s'" % result_type) "Cannot convert Python object to '%s'" % result_type)
if self.type.is_string or self.type.is_pyunicode_ptr: if self.type.is_string or self.type.is_pyunicode_ptr:
if self.arg.is_ephemeral(): if self.arg.is_name and self.arg.entry and self.arg.entry.is_pyglobal:
error(arg.pos,
"Obtaining '%s' from temporary Python value" % result_type)
elif self.arg.is_name and self.arg.entry and self.arg.entry.is_pyglobal:
warning(arg.pos, warning(arg.pos,
"Obtaining '%s' from externally modifiable global Python value" % result_type, "Obtaining '%s' from externally modifiable global Python value" % result_type,
level=1) level=1)
...@@ -10883,6 +10926,9 @@ class CoerceFromPyTypeNode(CoercionNode): ...@@ -10883,6 +10926,9 @@ class CoerceFromPyTypeNode(CoercionNode):
# The arg is always already analysed # The arg is always already analysed
return self return self
def is_ephemeral(self):
return self.type.is_ptr and self.arg.is_ephemeral()
def generate_result_code(self, code): def generate_result_code(self, code):
function = self.type.from_py_function function = self.type.from_py_function
operand = self.arg.py_result() operand = self.arg.py_result()
...@@ -11105,6 +11151,11 @@ class CloneNode(CoercionNode): ...@@ -11105,6 +11151,11 @@ class CloneNode(CoercionNode):
self.entry = self.arg.entry self.entry = self.arg.entry
return self return self
def coerce_to(self, dest_type, env):
if self.arg.is_literal:
return self.arg.coerce_to(dest_type, env)
return super(CloneNode, self).coerce_to(dest_type, env)
def is_simple(self): def is_simple(self):
return True # result is always in a temp (or a name) return True # result is always in a temp (or a name)
......
...@@ -2112,6 +2112,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -2112,6 +2112,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.body.generate_execution_code(code) self.body.generate_execution_code(code)
code.putln()
code.putln("/*--- Wrapped vars code ---*/")
self.generate_wrapped_entries_code(env, code)
code.putln()
if Options.generate_cleanup_code: if Options.generate_cleanup_code:
code.globalstate.use_utility_code( code.globalstate.use_utility_code(
UtilityCode.load_cached("RegisterModuleCleanup", "ModuleSetupCode.c")) UtilityCode.load_cached("RegisterModuleCleanup", "ModuleSetupCode.c"))
...@@ -2370,6 +2375,25 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -2370,6 +2375,25 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if entry.used: if entry.used:
entry.type.global_init_code(entry, code) entry.type.global_init_code(entry, code)
def generate_wrapped_entries_code(self, env, code):
for name, entry in env.entries.items():
if (entry.create_wrapper
and not entry.is_type
and entry.scope is env):
if not entry.type.create_to_py_utility_code(env):
error(entry.pos, "Cannot convert '%s' to Python object" % entry.type)
code.putln("{")
code.putln("PyObject* wrapped = %s(%s);" % (
entry.type.to_py_function,
entry.cname))
code.putln(code.error_goto_if_null("wrapped", entry.pos))
code.putln(
'if (__Pyx_SetAttrString(%s, "%s", wrapped) < 0) %s;' % (
env.module_cname,
name,
code.error_goto(entry.pos)))
code.putln("}")
def generate_c_variable_export_code(self, env, code): def generate_c_variable_export_code(self, env, code):
# Generate code to create PyCFunction wrappers for exported C functions. # Generate code to create PyCFunction wrappers for exported C functions.
entries = [] entries = []
......
...@@ -1424,9 +1424,10 @@ class CEnumDefNode(StatNode): ...@@ -1424,9 +1424,10 @@ class CEnumDefNode(StatNode):
# cname string or None # cname string or None
# items [CEnumDefItemNode] # items [CEnumDefItemNode]
# typedef_flag boolean # typedef_flag boolean
# visibility "public" or "private" # visibility "public" or "private" or "extern"
# api boolean # api boolean
# in_pxd boolean # in_pxd boolean
# create_wrapper boolean
# entry Entry # entry Entry
child_attrs = ["items"] child_attrs = ["items"]
...@@ -1434,7 +1435,8 @@ class CEnumDefNode(StatNode): ...@@ -1434,7 +1435,8 @@ class CEnumDefNode(StatNode):
def declare(self, env): def declare(self, env):
self.entry = env.declare_enum(self.name, self.pos, self.entry = env.declare_enum(self.name, self.pos,
cname = self.cname, typedef_flag = self.typedef_flag, cname = self.cname, typedef_flag = self.typedef_flag,
visibility = self.visibility, api = self.api) visibility = self.visibility, api = self.api,
create_wrapper = self.create_wrapper)
def analyse_declarations(self, env): def analyse_declarations(self, env):
if self.items is not None: if self.items is not None:
...@@ -1479,7 +1481,8 @@ class CEnumDefItemNode(StatNode): ...@@ -1479,7 +1481,8 @@ class CEnumDefItemNode(StatNode):
self.value = self.value.analyse_const_expression(env) self.value = self.value.analyse_const_expression(env)
entry = env.declare_const(self.name, enum_entry.type, entry = env.declare_const(self.name, enum_entry.type,
self.value, self.pos, cname = self.cname, self.value, self.pos, cname = self.cname,
visibility = enum_entry.visibility, api = enum_entry.api) visibility = enum_entry.visibility, api = enum_entry.api,
create_wrapper = enum_entry.create_wrapper)
enum_entry.enum_values.append(entry) enum_entry.enum_values.append(entry)
...@@ -4590,7 +4593,11 @@ class AssignmentNode(StatNode): ...@@ -4590,7 +4593,11 @@ class AssignmentNode(StatNode):
# to any of the left hand sides. # to any of the left hand sides.
def analyse_expressions(self, env): def analyse_expressions(self, env):
return self.analyse_types(env) node = self.analyse_types(env)
if isinstance(node, AssignmentNode):
if node.rhs.type.is_ptr and node.rhs.is_ephemeral():
error(self.pos, "Storing unsafe C derivative of temporary Python reference")
return node
# def analyse_expressions(self, env): # def analyse_expressions(self, env):
# self.analyse_expressions_1(env) # self.analyse_expressions_1(env)
...@@ -4751,41 +4758,56 @@ class CascadedAssignmentNode(AssignmentNode): ...@@ -4751,41 +4758,56 @@ class CascadedAssignmentNode(AssignmentNode):
# #
# coerced_rhs_list [ExprNode] RHS coerced to type of each LHS # coerced_rhs_list [ExprNode] RHS coerced to type of each LHS
child_attrs = ["lhs_list", "rhs", "coerced_rhs_list"] child_attrs = ["lhs_list", "rhs", "coerced_values", "coerced_rhs_list"]
coerced_rhs_list = None coerced_rhs_list = None
coerced_values = None
def analyse_declarations(self, env): def analyse_declarations(self, env):
for lhs in self.lhs_list: for lhs in self.lhs_list:
lhs.analyse_target_declaration(env) lhs.analyse_target_declaration(env)
def analyse_types(self, env, use_temp = 0): def analyse_types(self, env, use_temp=0):
from .ExprNodes import CloneNode, ProxyNode from .ExprNodes import CloneNode, ProxyNode
lhs_types = set()
for lhs in self.lhs_list:
lhs.analyse_target_types(env)
lhs.gil_assignment_check(env)
lhs_types.add(lhs.type)
rhs = self.rhs.analyse_types(env) rhs = self.rhs.analyse_types(env)
if use_temp or rhs.is_attribute or ( if len(lhs_types) == 1:
not rhs.is_name and not rhs.is_literal and # common special case: only one type needed on the LHS => coerce only once
rhs.type.is_pyobject): rhs = rhs.coerce_to(lhs_types.pop(), env)
if not rhs.is_name and not rhs.is_literal and (
use_temp or rhs.is_attribute or rhs.type.is_pyobject):
rhs = rhs.coerce_to_temp(env) rhs = rhs.coerce_to_temp(env)
else: else:
rhs = rhs.coerce_to_simple(env) rhs = rhs.coerce_to_simple(env)
self.rhs = ProxyNode(rhs) self.rhs = ProxyNode(rhs) if rhs.is_temp else rhs
self.coerced_values = []
coerced_values = {}
for lhs in self.lhs_list:
if lhs.type not in coerced_values and lhs.type != rhs.type:
rhs = CloneNode(self.rhs).coerce_to(lhs.type, env)
self.coerced_values.append(rhs)
coerced_values[lhs.type] = rhs
self.coerced_rhs_list = [] self.coerced_rhs_list = []
for lhs in self.lhs_list: for lhs in self.lhs_list:
lhs.analyse_target_types(env) rhs = coerced_values.get(lhs.type, self.rhs)
lhs.gil_assignment_check(env) self.coerced_rhs_list.append(CloneNode(rhs))
rhs = CloneNode(self.rhs)
rhs = rhs.coerce_to(lhs.type, env)
self.coerced_rhs_list.append(rhs)
return self return self
def generate_rhs_evaluation_code(self, code): def generate_rhs_evaluation_code(self, code):
self.rhs.generate_evaluation_code(code) self.rhs.generate_evaluation_code(code)
def generate_assignment_code(self, code): def generate_assignment_code(self, code):
for i in range(len(self.lhs_list)): for rhs in self.coerced_values:
lhs = self.lhs_list[i] rhs.generate_evaluation_code(code)
rhs = self.coerced_rhs_list[i] for lhs, rhs in zip(self.lhs_list, self.coerced_rhs_list):
rhs.generate_evaluation_code(code) rhs.generate_evaluation_code(code)
lhs.generate_assignment_code(rhs, code) lhs.generate_assignment_code(rhs, code)
# Assignment has disposed of the cloned RHS # Assignment has disposed of the cloned RHS
...@@ -4796,9 +4818,11 @@ class CascadedAssignmentNode(AssignmentNode): ...@@ -4796,9 +4818,11 @@ class CascadedAssignmentNode(AssignmentNode):
self.rhs.generate_function_definitions(env, code) self.rhs.generate_function_definitions(env, code)
def annotate(self, code): def annotate(self, code):
for i in range(len(self.lhs_list)): for rhs in self.coerced_values:
self.lhs_list[i].annotate(code) rhs.annotate(code)
self.coerced_rhs_list[i].annotate(code) for lhs, rhs in zip(self.lhs_list, self.coerced_rhs_list):
lhs.annotate(code)
rhs.annotate(code)
self.rhs.annotate(code) self.rhs.annotate(code)
......
...@@ -1327,6 +1327,24 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform): ...@@ -1327,6 +1327,24 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
return pos_args[0] return pos_args[0]
return node return node
def _handle_simple_function_slice(self, node, pos_args):
arg_count = len(pos_args)
start = step = None
if arg_count == 1:
stop, = pos_args
elif arg_count == 2:
start, stop = pos_args
elif arg_count == 3:
start, stop, step = pos_args
else:
self._error_wrong_arg_count('slice', node, pos_args)
return node
return ExprNodes.SliceNode(
node.pos,
start=start or ExprNodes.NoneNode(node.pos),
stop=stop,
step=step or ExprNodes.NoneNode(node.pos))
class YieldNodeCollector(Visitor.TreeVisitor): class YieldNodeCollector(Visitor.TreeVisitor):
def __init__(self): def __init__(self):
Visitor.TreeVisitor.__init__(self) Visitor.TreeVisitor.__init__(self)
......
...@@ -2622,7 +2622,8 @@ def p_cdef_statement(s, ctx): ...@@ -2622,7 +2622,8 @@ def p_cdef_statement(s, ctx):
if ctx.level not in ('module', 'module_pxd'): if ctx.level not in ('module', 'module_pxd'):
error(pos, "C struct/union/enum definition not allowed here") error(pos, "C struct/union/enum definition not allowed here")
if ctx.overridable: if ctx.overridable:
error(pos, "C struct/union/enum cannot be declared cpdef") if s.systring != 'enum':
error(pos, "C struct/union cannot be declared cpdef")
return p_struct_enum(s, pos, ctx) return p_struct_enum(s, pos, ctx)
elif s.sy == 'IDENT' and s.systring == 'fused': elif s.sy == 'IDENT' and s.systring == 'fused':
return p_fused_definition(s, pos, ctx) return p_fused_definition(s, pos, ctx)
...@@ -2679,6 +2680,7 @@ def p_c_enum_definition(s, pos, ctx): ...@@ -2679,6 +2680,7 @@ def p_c_enum_definition(s, pos, ctx):
return Nodes.CEnumDefNode( return Nodes.CEnumDefNode(
pos, name = name, cname = cname, items = items, pos, name = name, cname = cname, items = items,
typedef_flag = ctx.typedef_flag, visibility = ctx.visibility, typedef_flag = ctx.typedef_flag, visibility = ctx.visibility,
create_wrapper = ctx.overridable,
api = ctx.api, in_pxd = ctx.level == 'module_pxd') api = ctx.api, in_pxd = ctx.level == 'module_pxd')
def p_c_enum_line(s, ctx, items): def p_c_enum_line(s, ctx, items):
......
...@@ -3715,6 +3715,9 @@ def independent_spanning_type(type1, type2): ...@@ -3715,6 +3715,9 @@ def independent_spanning_type(type1, type2):
return py_object_type return py_object_type
span_type = _spanning_type(type1, type2) span_type = _spanning_type(type1, type2)
if span_type is None: if span_type is None:
if type1.is_ptr and type2.is_ptr:
# incompatible pointers, void* will do as a result
return c_void_ptr_type
return error_type return error_type
return span_type return span_type
......
...@@ -407,7 +407,7 @@ class Scope(object): ...@@ -407,7 +407,7 @@ class Scope(object):
""" Return the module-level scope containing this scope. """ """ Return the module-level scope containing this scope. """
return self.outer_scope.builtin_scope() return self.outer_scope.builtin_scope()
def declare(self, name, cname, type, pos, visibility, shadow = 0, is_type = 0): def declare(self, name, cname, type, pos, visibility, shadow = 0, is_type = 0, create_wrapper = 0):
# Create new entry, and add to dictionary if # Create new entry, and add to dictionary if
# name is not None. Reports a warning if already # name is not None. Reports a warning if already
# declared. # declared.
...@@ -424,6 +424,7 @@ class Scope(object): ...@@ -424,6 +424,7 @@ class Scope(object):
error(pos, "'%s' redeclared " % name) error(pos, "'%s' redeclared " % name)
entry = Entry(name, cname, type, pos = pos) entry = Entry(name, cname, type, pos = pos)
entry.in_cinclude = self.in_cinclude entry.in_cinclude = self.in_cinclude
entry.create_wrapper = create_wrapper
if name: if name:
entry.qualified_name = self.qualify_name(name) entry.qualified_name = self.qualify_name(name)
# if name in entries and self.is_cpp(): # if name in entries and self.is_cpp():
...@@ -444,14 +445,14 @@ class Scope(object): ...@@ -444,14 +445,14 @@ class Scope(object):
def qualify_name(self, name): def qualify_name(self, name):
return EncodedString("%s.%s" % (self.qualified_name, name)) return EncodedString("%s.%s" % (self.qualified_name, name))
def declare_const(self, name, type, value, pos, cname = None, visibility = 'private', api = 0): def declare_const(self, name, type, value, pos, cname = None, visibility = 'private', api = 0, create_wrapper = 0):
# Add an entry for a named constant. # Add an entry for a named constant.
if not cname: if not cname:
if self.in_cinclude or (visibility == 'public' or api): if self.in_cinclude or (visibility == 'public' or api):
cname = name cname = name
else: else:
cname = self.mangle(Naming.enum_prefix, name) cname = self.mangle(Naming.enum_prefix, name)
entry = self.declare(name, cname, type, pos, visibility) entry = self.declare(name, cname, type, pos, visibility, create_wrapper = create_wrapper)
entry.is_const = 1 entry.is_const = 1
entry.value_node = value entry.value_node = value
return entry return entry
...@@ -588,7 +589,7 @@ class Scope(object): ...@@ -588,7 +589,7 @@ class Scope(object):
entry.name, entry.visibility)) entry.name, entry.visibility))
def declare_enum(self, name, pos, cname, typedef_flag, def declare_enum(self, name, pos, cname, typedef_flag,
visibility = 'private', api = 0): visibility = 'private', api = 0, create_wrapper = 0):
if name: if name:
if not cname: if not cname:
if self.in_cinclude or (visibility == 'public' or api): if self.in_cinclude or (visibility == 'public' or api):
...@@ -600,6 +601,7 @@ class Scope(object): ...@@ -600,6 +601,7 @@ class Scope(object):
type = PyrexTypes.c_anon_enum_type type = PyrexTypes.c_anon_enum_type
entry = self.declare_type(name, type, pos, cname = cname, entry = self.declare_type(name, type, pos, cname = cname,
visibility = visibility, api = api) visibility = visibility, api = api)
entry.create_wrapper = create_wrapper
entry.enum_values = [] entry.enum_values = []
self.sue_entries.append(entry) self.sue_entries.append(entry)
return entry return entry
......
...@@ -4,8 +4,19 @@ from posix.types cimport (blkcnt_t, blksize_t, dev_t, gid_t, ino_t, mode_t, ...@@ -4,8 +4,19 @@ from posix.types cimport (blkcnt_t, blksize_t, dev_t, gid_t, ino_t, mode_t,
cdef extern from "sys/stat.h" nogil: cdef extern from "sys/stat.h" nogil:
cdef struct struct_stat "stat": cdef struct struct_stat "stat":
dev_t st_dev dev_t st_dev
ino_t st_ino ino_t st_ino
mode_t st_mode
nlink_t st_nlink
uid_t st_uid
gid_t st_gid
dev_t st_rdev
off_t st_size
blksize_t st_blksize
blkcnt_t st_blocks
time_t st_atime
time_t st_mtime
time_t st_ctime
# POSIX prescribes including both <sys/stat.h> and <unistd.h> for these # POSIX prescribes including both <sys/stat.h> and <unistd.h> for these
cdef extern from "unistd.h" nogil: cdef extern from "unistd.h" nogil:
......
...@@ -46,23 +46,53 @@ ...@@ -46,23 +46,53 @@
"Keymap used in `cython-mode'.") "Keymap used in `cython-mode'.")
(defvar cython-font-lock-keywords (defvar cython-font-lock-keywords
`(;; new keywords in Cython language `(;; ctypedef statement: "ctypedef (...type... alias)?"
(,(rx
;; keyword itself
symbol-start (group "ctypedef")
;; type specifier: at least 1 non-identifier symbol + 1 identifier
;; symbol and anything but a comment-starter after that.
(opt (regexp "[^a-zA-z0-9_\n]+[a-zA-Z0-9_][^#\n]*")
;; type alias: an identifier
symbol-start (group (regexp "[a-zA-Z_]+[a-zA-Z0-9_]*"))
;; space-or-comments till the end of the line
(* space) (opt "#" (* nonl)) line-end))
(1 font-lock-keyword-face)
(2 font-lock-type-face nil 'noerror))
;; new keywords in Cython language
(,(rx symbol-start (,(rx symbol-start
(or "by" "cdef" "cimport" "cpdef" "ctypedef" "enum" "except?" (or "by" "cdef" "cimport" "cpdef"
"extern" "gil" "include" "nogil" "property" "public" "extern" "gil" "include" "nogil" "property" "public"
"readonly" "struct" "union" "DEF" "IF" "ELIF" "ELSE") "readonly" "DEF" "IF" "ELIF" "ELSE"
"new" "del" "cppclass" "namespace" "const"
"__stdcall" "__cdecl" "__fastcall" "inline" "api")
symbol-end) symbol-end)
1 font-lock-keyword-face) . font-lock-keyword-face)
;; Question mark won't match at a symbol-end, so 'except?' must be
;; special-cased. It's simpler to handle it separately than weaving it
;; into the lengthy list of other keywords.
(,(rx symbol-start "except?") . font-lock-keyword-face)
;; C and Python types (highlight as builtins) ;; C and Python types (highlight as builtins)
(,(rx symbol-start (,(rx symbol-start
(or "NULL" "bint" "char" "dict" "double" "float" "int" "list" (or
"long" "object" "Py_ssize_t" "short" "size_t" "void") "object" "dict" "list"
;; basic c type names
"void" "char" "int" "float" "double" "bint"
;; longness/signed/constness
"signed" "unsigned" "long" "short"
;; special basic c types
"size_t" "Py_ssize_t" "Py_UNICODE" "Py_UCS4" "ssize_t" "ptrdiff_t")
symbol-end) symbol-end)
1 font-lock-builtin-face) . font-lock-builtin-face)
(,(rx symbol-start "NULL" symbol-end)
. font-lock-constant-face)
;; cdef is used for more than functions, so simply highlighting the next ;; cdef is used for more than functions, so simply highlighting the next
;; word is problematic. struct, enum and property work though. ;; word is problematic. struct, enum and property work though.
("\\_<\\(?:struct\\|enum\\)[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)" (,(rx symbol-start
1 py-class-name-face) (group (or "struct" "enum" "union"
(seq "ctypedef" (+ space "fused"))))
(+ space) (group (regexp "[a-zA-Z_]+[a-zA-Z0-9_]*")))
(1 font-lock-keyword-face prepend) (2 font-lock-type-face))
("\\_<property[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)" ("\\_<property[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)"
1 font-lock-function-name-face)) 1 font-lock-function-name-face))
"Additional font lock keywords for Cython mode.") "Additional font lock keywords for Cython mode.")
...@@ -249,8 +279,7 @@ Finds end of innermost nested class or method definition." ...@@ -249,8 +279,7 @@ Finds end of innermost nested class or method definition."
"Major mode for Cython development, derived from Python mode. "Major mode for Cython development, derived from Python mode.
\\{cython-mode-map}" \\{cython-mode-map}"
(setcar font-lock-defaults (font-lock-add-keywords nil cython-font-lock-keywords)
(append python-font-lock-keywords cython-font-lock-keywords))
(set (make-local-variable 'outline-regexp) (set (make-local-variable 'outline-regexp)
(rx (* space) (or "class" "def" "cdef" "cpdef" "elif" "else" "except" "finally" (rx (* space) (or "class" "def" "cdef" "cpdef" "elif" "else" "except" "finally"
"for" "if" "try" "while" "with") "for" "if" "try" "while" "with")
......
...@@ -1929,13 +1929,18 @@ def runtests(options, cmd_args, coverage=None): ...@@ -1929,13 +1929,18 @@ def runtests(options, cmd_args, coverage=None):
coverage.stop() coverage.stop()
ignored_modules = set( ignored_modules = set(
'Cython.Compiler.' + name 'Cython.Compiler.' + name
for name in ('Version', 'DebugFlags', 'CmdLine')) for name in ('Version', 'DebugFlags', 'CmdLine')) | set(
modules = [module for name, module in sys.modules.items() 'Cython.' + name
if module is not None and for name in ('Debugging',))
name.startswith('Cython.') and ignored_packages = ['Cython.Runtime', 'Cython.Tempita']
'.Tests' not in name and modules = [
name not in ignored_modules and module for name, module in sys.modules.items()
not name.startswith('Cython.Runtime')] if module is not None and
name.startswith('Cython.') and
'.Tests' not in name and
name not in ignored_modules and
not any(name.startswith(package) for package in ignored_packages)
]
if options.coverage: if options.coverage:
coverage.report(modules, show_missing=0) coverage.report(modules, show_missing=0)
if options.coverage_xml: if options.coverage_xml:
......
...@@ -18,6 +18,13 @@ cptr = s ...@@ -18,6 +18,13 @@ cptr = s
# temp => error # temp => error
cptr = s + b"cba" cptr = s + b"cba"
# indexing => error (but not clear enough to make it a compiler error)
cptr = s[0]
cdef char* x = <char*>s[0]
# slicing => error
cptr = s[:2]
cdef unicode c_u = u"abc" cdef unicode c_u = u"abc"
u = u"abc" u = u"abc"
...@@ -37,9 +44,23 @@ cuptr = u ...@@ -37,9 +44,23 @@ cuptr = u
cuptr = u + u"cba" cuptr = u + u"cba"
# coercion in conditional expression => ok
boolval = list(u)
cptr = c_s if boolval else c_s
# temp in conditional expression => error
cptr = s + b'x' if boolval else s + b'y'
_ERRORS = """ _ERRORS = """
16:8: Obtaining 'char *' from externally modifiable global Python value 16:8: Obtaining 'char *' from externally modifiable global Python value
19:9: Obtaining 'char *' from temporary Python value 19:9: Storing unsafe C derivative of temporary Python reference
34:9: Obtaining 'Py_UNICODE *' from externally modifiable global Python value #22:8: Storing unsafe C derivative of temporary Python reference
37:10: Obtaining 'Py_UNICODE *' from temporary Python value #23:5: Storing unsafe C derivative of temporary Python reference
#23:15: Casting temporary Python object to non-numeric non-Python type
26:8: Storing unsafe C derivative of temporary Python reference
41:9: Obtaining 'Py_UNICODE *' from externally modifiable global Python value
44:10: Storing unsafe C derivative of temporary Python reference
52:7: Storing unsafe C derivative of temporary Python reference
52:7: Unsafe C derivative of temporary Python reference used in conditional expression
""" """
...@@ -9,4 +9,5 @@ def foo(obj): ...@@ -9,4 +9,5 @@ def foo(obj):
_ERRORS = u""" _ERRORS = u"""
8:5: Casting temporary Python object to non-numeric non-Python type 8:5: Casting temporary Python object to non-numeric non-Python type
8:5: Storing unsafe C derivative of temporary Python reference
""" """
...@@ -275,8 +275,6 @@ def cascaded_buffer_assignment(obj): ...@@ -275,8 +275,6 @@ def cascaded_buffer_assignment(obj):
>>> A = IntMockBuffer("A", range(6)) >>> A = IntMockBuffer("A", range(6))
>>> cascaded_buffer_assignment(A) >>> cascaded_buffer_assignment(A)
acquired A acquired A
acquired A
released A
released A released A
""" """
cdef int[:] a, b cdef int[:] a, b
......
# mode: run
def slice1(stop):
"""
>>> list(range(8))
[0, 1, 2, 3, 4, 5, 6, 7]
>>> list(range(10))[slice1(8)]
[0, 1, 2, 3, 4, 5, 6, 7]
>>> slice1(1)
slice(None, 1, None)
>>> slice1(10)
slice(None, 10, None)
>>> slice1(None)
slice(None, None, None)
>>> slice1(1) == slice(1)
True
>>> slice1(None) == slice(None)
True
"""
return slice(stop)
def slice1_const():
"""
>>> slice1_const() == slice(12)
True
"""
return slice(12)
def slice2(start, stop):
"""
>>> list(range(2, 8))
[2, 3, 4, 5, 6, 7]
>>> list(range(10))[slice2(2, 8)]
[2, 3, 4, 5, 6, 7]
>>> slice2(1, 10)
slice(1, 10, None)
>>> slice2(None, 10)
slice(None, 10, None)
>>> slice2(4, None)
slice(4, None, None)
"""
return slice(start, stop)
def slice2_const():
"""
>>> slice2_const() == slice(None, 12)
True
"""
return slice(None, 12)
def slice3(start, stop, step):
"""
>>> list(range(2, 8, 3))
[2, 5]
>>> list(range(10))[slice3(2, 8, 3)]
[2, 5]
>>> slice3(2, None, 3)
slice(2, None, 3)
>>> slice3(None, 3, 2)
slice(None, 3, 2)
"""
return slice(start, stop, step)
def slice3_const():
"""
>>> slice3_const() == slice(12, None, 34)
True
"""
return slice(12, None, 34)
...@@ -24,6 +24,18 @@ def simple_parallel_int_mix(): ...@@ -24,6 +24,18 @@ def simple_parallel_int_mix():
ai, bi = al, bl = ao, bo = c = d = [1,2] ai, bi = al, bl = ao, bo = c = d = [1,2]
return ao, bo, ai, bi, al, bl, c, d return ao, bo, ai, bi, al, bl, c, d
def simple_parallel_int_mix_recursive():
"""
>>> simple_parallel_int_mix_recursive()
(1, 2, 3, 1, [2, 3], 1, 2, 3, 1, 2, 3, [1, [2, 3]], [1, [2, 3]])
"""
cdef int ai, bi, ci
cdef long al, bl, cl
cdef object ao, bo, co
cdef object xo, yo
ai, [bi, ci] = al, [bl, cl] = xo, yo = ao, [bo, co] = c = d = [1, [2, 3]]
return ao, bo, co, xo, yo, ai, bi, ci, al, bl, cl, c, d
cdef int called = 0 cdef int called = 0
cdef char* get_string(): cdef char* get_string():
......
import cython import cython
@cython.test_fail_if_path_exists(
'//CascadedAssignmentNode//CoerceFromPyTypeNode',
'//CascadedAssignmentNode//CoerceToPyTypeNode',
)
@cython.test_assert_path_exists('//CascadedAssignmentNode')
def test_cascaded_assignment_simple(): def test_cascaded_assignment_simple():
""" """
>>> test_cascaded_assignment_simple() >>> test_cascaded_assignment_simple()
...@@ -8,6 +13,11 @@ def test_cascaded_assignment_simple(): ...@@ -8,6 +13,11 @@ def test_cascaded_assignment_simple():
a = b = c = 5 a = b = c = 5
return a return a
@cython.test_fail_if_path_exists(
'//CascadedAssignmentNode//CoerceFromPyTypeNode',
'//CascadedAssignmentNode//CoerceToPyTypeNode',
)
@cython.test_assert_path_exists('//CascadedAssignmentNode')
def test_cascaded_assignment_typed(): def test_cascaded_assignment_typed():
""" """
>>> test_cascaded_assignment_typed() >>> test_cascaded_assignment_typed()
...@@ -46,4 +56,3 @@ def test_cascaded_assignment_evaluate_expr(): ...@@ -46,4 +56,3 @@ def test_cascaded_assignment_evaluate_expr():
""" """
a = b = c = float(expr()) a = b = c = float(expr())
return a, b, c return a, b, c
# mode: run
from cpython.version cimport PY_MAJOR_VERSION
cdef bint IS_PY2 = PY_MAJOR_VERSION == 2
cdef cfunc1(char* s):
if IS_PY2:
return s
else:
return s.decode('ASCII')
cdef cfunc3(int x, char* s, object y):
return cfunc1(s)
def test_one_arg_indexing(s):
"""
>>> test_one_arg_indexing(b'xyz')
'y'
"""
cfunc1(s[0]) if IS_PY2 else cfunc1(s[:1])
z = cfunc1(s[2]) if IS_PY2 else cfunc1(s[2:])
assert z == 'z', repr(z)
return cfunc1(s[1]) if IS_PY2 else cfunc1(s[1:2])
def test_more_args_indexing(s):
"""
>>> test_more_args_indexing(b'xyz')
'y'
"""
cfunc3(1, s[0 if IS_PY2 else slice(0,1)], 6.5)
z = cfunc3(2, s[2 if IS_PY2 else slice(2,None)], 'abc' * 2)
assert z == 'z', repr(z)
return cfunc3(3, s[1 if IS_PY2 else slice(1,2)], 1)
def test_one_arg_slicing(s):
"""
>>> test_one_arg_slicing(b'xyz')
'y'
"""
cfunc1(s[:2])
z = cfunc1(s[2:])
assert z == 'z', repr(z)
return cfunc1(s[1:2])
def test_more_args_slicing(s):
"""
>>> test_more_args_slicing(b'xyz')
'y'
"""
cfunc3(1, s[:2], 'abc')
z = cfunc3(123, s[2:], 5)
assert z == 'z', repr(z)
return cfunc3(2, s[1:2], 1.4)
def test_one_arg_adding(s):
"""
>>> test_one_arg_adding(b'xyz')
'abxyzqr'
"""
return cfunc1(b"a" + b"b" + s + b"q" + b"r")
def test_more_args_adding(s):
"""
>>> test_more_args_adding(b'xyz')
'abxyzqr'
"""
return cfunc3(1, b"a" + b"b" + s + b"q" + b"r", 'xyz%d' % 3)
cdef char* ret_charptr(char* s):
return s
def test_charptr_and_charptr_func(char* s):
"""
>>> test_charptr_and_charptr_func(b'abc') == b'abc'
True
"""
return s and ret_charptr(s)
def test_charptr_and_ucharptr(char* s):
"""
>>> test_charptr_and_ucharptr(b'abc') == b'abc'
True
"""
return s and <unsigned char*>s
cdef extern from *:
cpdef enum ExternPxdEnum:
FOUR "4"
EIGHT "8"
cdef enum ExternSecretPxdEnum:
SIXTEEN "16"
cpdef enum PxdEnum:
RANK_0 = 11
RANK_1 = 37
RANK_2 = 389
cdef enum PxdSecretEnum:
RANK_3 = 5077
"""
>>> ONE, TEN, HUNDRED
(1, 10, 100)
>>> THOUSAND # doctest: +ELLIPSIS
Traceback (most recent call last):
NameError: ...name 'THOUSAND' is not defined
>>> TWO, THREE, FIVE
(2, 3, 5)
>>> SEVEN # doctest: +ELLIPSIS
Traceback (most recent call last):
NameError: ...name 'SEVEN' is not defined
>>> FOUR, EIGHT
(4, 8)
>>> SIXTEEN # doctest: +ELLIPSIS
Traceback (most recent call last):
NameError: ...name 'SIXTEEN' is not defined
>>> RANK_0, RANK_1, RANK_2
(11, 37, 389)
>>> RANK_3 # doctest: +ELLIPSIS
Traceback (most recent call last):
NameError: ...name 'RANK_3' is not defined
"""
cdef extern from *:
cpdef enum ExternPyxEnum:
ONE "1"
TEN "10"
HUNDRED "100"
cdef enum ExternSecretPyxEnum:
THOUSAND "1000"
cpdef enum PyxEnum:
TWO = 2
THREE = 3
FIVE = 5
cdef enum SecretPyxEnum:
SEVEN = 7
PYTHON setup.py build_ext --inplace
PYTHON -c "import import_enums_test"
######## setup.py ########
from Cython.Build.Dependencies import cythonize
from distutils.core import setup
setup(
ext_modules = cythonize(["enums.pyx", "no_enums.pyx"]),
)
######## enums.pyx ########
cpdef enum:
BAR
cpdef foo(): pass
######## enums.pxd ########
cpdef enum:
FOO
cpdef foo()
######## no_enums.pyx ########
from enums cimport *
######## import_enums_test.py ########
# We can import enums with a star import.
from enums import *
print(dir())
assert 'BAR' in dir() and 'FOO' in dir()
# enums not generated in the wrong module
import no_enums
print(dir(no_enums))
assert 'FOO' not in dir(no_enums)
assert 'foo' not in dir(no_enums)
...@@ -40,5 +40,5 @@ def test_charptr_coercion(x): ...@@ -40,5 +40,5 @@ def test_charptr_coercion(x):
>>> print(test_charptr_coercion(False)) >>> print(test_charptr_coercion(False))
def def
""" """
cdef char* s = 'abc' if x else 'def' cdef char* s = b'abc' if x else b'def'
return s.decode('ascii') return s.decode('ascii')
cimport cython
cdef char* c_string = b'abcdefg' cdef char* c_string = b'abcdefg'
cdef void* void_ptr = c_string cdef void* void_ptr = c_string
...@@ -60,3 +61,29 @@ def bool_binop_truth(int x): ...@@ -60,3 +61,29 @@ def bool_binop_truth(int x):
print True print True
if c_string and x or not (void_ptr or int_ptr and float_ptr) or x: if c_string and x or not (void_ptr or int_ptr and float_ptr) or x:
print True print True
def binop_voidptr(int x, long y, char* z):
"""
>>> binop_voidptr(1, 3, b'abc')
'void *'
"""
result = &x and &y and z
return cython.typeof(result)
def cond_expr_voidptr(int x, long y, char* z):
"""
>>> cond_expr_voidptr(0, -1, b'abc')
('void *', 0)
>>> cond_expr_voidptr(-1, 0, b'abc')
('void *', -1)
>>> cond_expr_voidptr(-1, 0, b'')
('void *', 0)
>>> cond_expr_voidptr(0, -1, b'')
('void *', -1)
"""
result = &x if len(z) else &y
assert sizeof(long) >= sizeof(int)
assert -1 == <int>(-1L)
return cython.typeof(result), (<int*>result)[0]
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