Commit 8d727b67 authored by scoder's avatar scoder Committed by GitHub

Merge branch 'master' into feature/pythran

parents 8705f0fa 4f857124
...@@ -390,14 +390,30 @@ def normalize_existing(base_path, rel_paths): ...@@ -390,14 +390,30 @@ def normalize_existing(base_path, rel_paths):
@cached_function @cached_function
def normalize_existing0(base_dir, rel_paths): def normalize_existing0(base_dir, rel_paths):
"""
Given some base directory ``base_dir`` and a list of path names
``rel_paths``, normalize each relative path name ``rel`` by
replacing it by ``os.path.join(base, rel)`` if that file exists.
Return a couple ``(normalized, needed_base)`` where ``normalized``
if the list of normalized file names and ``needed_base`` is
``base_dir`` if we actually needed ``base_dir``. If no paths were
changed (for example, if all paths were already absolute), then
``needed_base`` is ``None``.
"""
normalized = [] normalized = []
needed_base = None
for rel in rel_paths: for rel in rel_paths:
if os.path.isabs(rel):
normalized.append(rel)
continue
path = join_path(base_dir, rel) path = join_path(base_dir, rel)
if path_exists(path): if path_exists(path):
normalized.append(os.path.normpath(path)) normalized.append(os.path.normpath(path))
needed_base = base_dir
else: else:
normalized.append(rel) normalized.append(rel)
return normalized return (normalized, needed_base)
def resolve_depends(depends, include_dirs): def resolve_depends(depends, include_dirs):
...@@ -498,20 +514,25 @@ class DependencyTree(object): ...@@ -498,20 +514,25 @@ class DependencyTree(object):
return all return all
@cached_method @cached_method
def cimports_and_externs(self, filename): def cimports_externs_incdirs(self, filename):
# This is really ugly. Nested cimports are resolved with respect to the # This is really ugly. Nested cimports are resolved with respect to the
# includer, but includes are resolved with respect to the includee. # includer, but includes are resolved with respect to the includee.
cimports, includes, externs = self.parse_dependencies(filename)[:3] cimports, includes, externs = self.parse_dependencies(filename)[:3]
cimports = set(cimports) cimports = set(cimports)
externs = set(externs) externs = set(externs)
incdirs = set()
for include in self.included_files(filename): for include in self.included_files(filename):
included_cimports, included_externs = self.cimports_and_externs(include) included_cimports, included_externs, included_incdirs = self.cimports_externs_incdirs(include)
cimports.update(included_cimports) cimports.update(included_cimports)
externs.update(included_externs) externs.update(included_externs)
return tuple(cimports), normalize_existing(filename, externs) incdirs.update(included_incdirs)
externs, incdir = normalize_existing(filename, externs)
if incdir:
incdirs.add(incdir)
return tuple(cimports), externs, incdirs
def cimports(self, filename): def cimports(self, filename):
return self.cimports_and_externs(filename)[0] return self.cimports_externs_incdirs(filename)[0]
def package(self, filename): def package(self, filename):
return package(filename) return package(filename)
...@@ -594,12 +615,22 @@ class DependencyTree(object): ...@@ -594,12 +615,22 @@ class DependencyTree(object):
def distutils_info0(self, filename): def distutils_info0(self, filename):
info = self.parse_dependencies(filename)[3] info = self.parse_dependencies(filename)[3]
externs = self.cimports_and_externs(filename)[1] kwds = info.values
cimports, externs, incdirs = self.cimports_externs_incdirs(filename)
# Add dependencies on "cdef extern from ..." files
if externs: if externs:
if 'depends' in info.values: if 'depends' in kwds:
info.values['depends'] = list(set(info.values['depends']).union(externs)) kwds['depends'] = list(set(kwds['depends']).union(externs))
else: else:
info.values['depends'] = list(externs) kwds['depends'] = list(externs)
# Add include_dirs to ensure that the C compiler will find the
# "cdef extern from ..." files
if incdirs:
include_dirs = list(kwds.get('include_dirs', []))
for inc in incdirs:
if inc not in include_dirs:
include_dirs.append(inc)
kwds['include_dirs'] = include_dirs
return info return info
def distutils_info(self, filename, aliases=None, base=None): def distutils_info(self, filename, aliases=None, base=None):
......
...@@ -477,11 +477,7 @@ class FusedCFuncDefNode(StatListNode): ...@@ -477,11 +477,7 @@ class FusedCFuncDefNode(StatListNode):
pyx_code.imports.put_chunk( pyx_code.imports.put_chunk(
u""" u"""
cdef type ndarray cdef type ndarray
try: ndarray = __Pyx_ImportNumPyArrayTypeIfAvailable()
import numpy
ndarray = numpy.ndarray
except (ImportError, AttributeError, TypeError):
ndarray = None
""") """)
seen_int_dtypes = set() seen_int_dtypes = set()
...@@ -540,13 +536,18 @@ class FusedCFuncDefNode(StatListNode): ...@@ -540,13 +536,18 @@ class FusedCFuncDefNode(StatListNode):
# PROCESSING ARGUMENT {{arg_tuple_idx}} # PROCESSING ARGUMENT {{arg_tuple_idx}}
if {{arg_tuple_idx}} < len(<tuple>args): if {{arg_tuple_idx}} < len(<tuple>args):
arg = (<tuple>args)[{{arg_tuple_idx}}] arg = (<tuple>args)[{{arg_tuple_idx}}]
elif '{{arg.name}}' in <dict>kwargs: elif kwargs is not None and '{{arg.name}}' in <dict>kwargs:
arg = (<dict>kwargs)['{{arg.name}}'] arg = (<dict>kwargs)['{{arg.name}}']
else: else:
{{if arg.default}} {{if arg.default}}
arg = (<tuple>defaults)[{{default_idx}}] arg = (<tuple>defaults)[{{default_idx}}]
{{else}} {{else}}
raise TypeError("Expected at least %d arguments" % len(<tuple>args)) {{if arg_tuple_idx < min_positional_args}}
raise TypeError("Expected at least %d argument%s, got %d" % (
{{min_positional_args}}, {{'"s"' if min_positional_args != 1 else '""'}}, len(<tuple>args)))
{{else}}
raise TypeError("Missing keyword-only argument: '%s'" % "{{arg.default}}")
{{endif}}
{{endif}} {{endif}}
""") """)
...@@ -568,6 +569,10 @@ class FusedCFuncDefNode(StatListNode): ...@@ -568,6 +569,10 @@ class FusedCFuncDefNode(StatListNode):
'memviewslice_cname': MemoryView.memviewslice_cname, 'memviewslice_cname': MemoryView.memviewslice_cname,
'func_args': self.node.args, 'func_args': self.node.args,
'n_fused': len(fused_types), 'n_fused': len(fused_types),
'min_positional_args':
self.node.num_required_args - self.node.num_required_kw_args
if is_def else
sum(1 for arg in self.node.args if arg.default is None),
'name': orig_py_func.entry.name, 'name': orig_py_func.entry.name,
} }
...@@ -577,14 +582,11 @@ class FusedCFuncDefNode(StatListNode): ...@@ -577,14 +582,11 @@ class FusedCFuncDefNode(StatListNode):
u""" u"""
cdef extern from *: cdef extern from *:
void __pyx_PyErr_Clear "PyErr_Clear" () void __pyx_PyErr_Clear "PyErr_Clear" ()
type __Pyx_ImportNumPyArrayTypeIfAvailable()
int __Pyx_Is_Little_Endian() int __Pyx_Is_Little_Endian()
""") """)
decl_code.indent() decl_code.indent()
pyx_code.put_chunk(
u"""
from __future__ import absolute_import # for later numpy import
""")
pyx_code.put_chunk( pyx_code.put_chunk(
u""" u"""
def __pyx_fused_cpdef(signatures, args, kwargs, defaults): def __pyx_fused_cpdef(signatures, args, kwargs, defaults):
...@@ -593,8 +595,8 @@ class FusedCFuncDefNode(StatListNode): ...@@ -593,8 +595,8 @@ class FusedCFuncDefNode(StatListNode):
dest_sig = [None] * {{n_fused}} dest_sig = [None] * {{n_fused}}
if kwargs is None: if kwargs is not None and not kwargs:
kwargs = {} kwargs = None
cdef Py_ssize_t i cdef Py_ssize_t i
...@@ -655,15 +657,18 @@ class FusedCFuncDefNode(StatListNode): ...@@ -655,15 +657,18 @@ class FusedCFuncDefNode(StatListNode):
if all_buffer_types: if all_buffer_types:
self._buffer_declarations(pyx_code, decl_code, all_buffer_types) self._buffer_declarations(pyx_code, decl_code, all_buffer_types)
env.use_utility_code(Code.UtilityCode.load_cached("Import", "ImportExport.c")) env.use_utility_code(Code.UtilityCode.load_cached("Import", "ImportExport.c"))
env.use_utility_code(Code.UtilityCode.load_cached("ImportNumPyArray", "ImportExport.c"))
pyx_code.put_chunk( pyx_code.put_chunk(
u""" u"""
candidates = [] candidates = []
for sig in <dict>signatures: for sig in <dict>signatures:
match_found = False match_found = False
for src_type, dst_type in zip(sig.strip('()').split('|'), dest_sig): src_sig = sig.strip('()').split('|')
for i in range(len(dest_sig)):
dst_type = dest_sig[i]
if dst_type is not None: if dst_type is not None:
if src_type == dst_type: if src_sig[i] == dst_type:
match_found = True match_found = True
else: else:
match_found = False match_found = False
......
...@@ -2649,6 +2649,9 @@ class DefNode(FuncDefNode): ...@@ -2649,6 +2649,9 @@ class DefNode(FuncDefNode):
child_attrs = ["args", "star_arg", "starstar_arg", "body", "decorators", "return_type_annotation"] child_attrs = ["args", "star_arg", "starstar_arg", "body", "decorators", "return_type_annotation"]
is_staticmethod = False
is_classmethod = False
lambda_name = None lambda_name = None
reqd_kw_flags_cname = "0" reqd_kw_flags_cname = "0"
is_wrapper = 0 is_wrapper = 0
...@@ -2761,7 +2764,6 @@ class DefNode(FuncDefNode): ...@@ -2761,7 +2764,6 @@ class DefNode(FuncDefNode):
return True return True
def analyse_declarations(self, env): def analyse_declarations(self, env):
self.is_classmethod = self.is_staticmethod = False
if self.decorators: if self.decorators:
for decorator in self.decorators: for decorator in self.decorators:
func = decorator.decorator func = decorator.decorator
......
...@@ -1355,8 +1355,18 @@ class DecoratorTransform(ScopeTrackingTransform, SkipDeclarations): ...@@ -1355,8 +1355,18 @@ class DecoratorTransform(ScopeTrackingTransform, SkipDeclarations):
return self._reject_decorated_property(node, decorator_node) return self._reject_decorated_property(node, decorator_node)
return self._add_to_property(properties, node, handler_name, decorator_node) return self._add_to_property(properties, node, handler_name, decorator_node)
# we clear node.decorators, so we need to set the
# is_staticmethod/is_classmethod attributes now
for decorator in node.decorators:
func = decorator.decorator
if func.is_name:
node.is_classmethod |= func.name == 'classmethod'
node.is_staticmethod |= func.name == 'staticmethod'
# transform normal decorators # transform normal decorators
return self.chain_decorators(node, node.decorators, node.name) decs = node.decorators
node.decorators = None
return self.chain_decorators(node, decs, node.name)
@staticmethod @staticmethod
def _reject_decorated_property(node, decorator_node): def _reject_decorated_property(node, decorator_node):
......
...@@ -2208,15 +2208,17 @@ class CPointerBaseType(CType): ...@@ -2208,15 +2208,17 @@ class CPointerBaseType(CType):
if base_type.signed == 2: if base_type.signed == 2:
self.to_py_function = "__Pyx_PyObject_FromCString" self.to_py_function = "__Pyx_PyObject_FromCString"
if self.is_ptr: if self.is_ptr:
self.from_py_function = "__Pyx_PyObject_AsSString" self.from_py_function = "__Pyx_PyObject_As%sSString"
elif base_type.signed: elif base_type.signed:
self.to_py_function = "__Pyx_PyObject_FromString" self.to_py_function = "__Pyx_PyObject_FromString"
if self.is_ptr: if self.is_ptr:
self.from_py_function = "__Pyx_PyObject_AsString" self.from_py_function = "__Pyx_PyObject_As%sString"
else: else:
self.to_py_function = "__Pyx_PyObject_FromCString" self.to_py_function = "__Pyx_PyObject_FromCString"
if self.is_ptr: if self.is_ptr:
self.from_py_function = "__Pyx_PyObject_AsUString" self.from_py_function = "__Pyx_PyObject_As%sUString"
if self.is_ptr:
self.from_py_function %= '' if self.base_type.is_const else 'Writable'
self.exception_value = "NULL" self.exception_value = "NULL"
elif self.is_pyunicode_ptr and not base_type.is_error: elif self.is_pyunicode_ptr and not base_type.is_error:
self.to_py_function = "__Pyx_PyUnicode_FromUnicode" self.to_py_function = "__Pyx_PyUnicode_FromUnicode"
...@@ -2596,7 +2598,7 @@ class CFuncType(CType): ...@@ -2596,7 +2598,7 @@ class CFuncType(CType):
return self.same_c_signature_as_resolved_type( return self.same_c_signature_as_resolved_type(
other_type.resolve(), as_cmethod) other_type.resolve(), as_cmethod)
def same_c_signature_as_resolved_type(self, other_type, as_cmethod = 0): def same_c_signature_as_resolved_type(self, other_type, as_cmethod = 0, as_pxd_definition = 0):
#print "CFuncType.same_c_signature_as_resolved_type:", \ #print "CFuncType.same_c_signature_as_resolved_type:", \
# self, other_type, "as_cmethod =", as_cmethod ### # self, other_type, "as_cmethod =", as_cmethod ###
if other_type is error_type: if other_type is error_type:
...@@ -2618,8 +2620,13 @@ class CFuncType(CType): ...@@ -2618,8 +2620,13 @@ class CFuncType(CType):
return 0 return 0
if self.optional_arg_count != other_type.optional_arg_count: if self.optional_arg_count != other_type.optional_arg_count:
return 0 return 0
if not self.return_type.same_as(other_type.return_type): if as_pxd_definition:
return 0 # A narrowing of the return type declared in the pxd is allowed.
if not self.return_type.subtype_of_resolved_type(other_type.return_type):
return 0
else:
if not self.return_type.same_as(other_type.return_type):
return 0
if not self.same_calling_convention_as(other_type): if not self.same_calling_convention_as(other_type):
return 0 return 0
if self.exception_check != other_type.exception_check: if self.exception_check != other_type.exception_check:
...@@ -3238,7 +3245,7 @@ class CStructOrUnionType(CType): ...@@ -3238,7 +3245,7 @@ class CStructOrUnionType(CType):
return False return False
context = dict( context = dict(
struct_name=self.name, struct_type=self,
var_entries=self.scope.var_entries, var_entries=self.scope.var_entries,
funcname=self.from_py_function, funcname=self.from_py_function,
) )
......
...@@ -2098,7 +2098,8 @@ class CClassScope(ClassScope): ...@@ -2098,7 +2098,8 @@ class CClassScope(ClassScope):
# Fix with_gil vs nogil. # Fix with_gil vs nogil.
entry.type = entry.type.with_with_gil(type.with_gil) entry.type = entry.type.with_with_gil(type.with_gil)
elif type.compatible_signature_with(entry.type, as_cmethod = 1) and type.nogil == entry.type.nogil: elif type.compatible_signature_with(entry.type, as_cmethod = 1) and type.nogil == entry.type.nogil:
if self.defined and not in_pxd: if (self.defined and not in_pxd
and not type.same_c_signature_as_resolved_type(entry.type, as_cmethod = 1, as_pxd_definition = 1)):
error(pos, error(pos,
"Compatible but non-identical C method '%s' not redeclared " "Compatible but non-identical C method '%s' not redeclared "
"in definition part of extension type '%s'" % (name, self.class_name)) "in definition part of extension type '%s'" % (name, self.class_name))
......
# cython.* namespace for pure mode. # cython.* namespace for pure mode.
from __future__ import absolute_import from __future__ import absolute_import
__version__ = "0.25.2" __version__ = "0.26.alpha0"
try: try:
from __builtin__ import basestring from __builtin__ import basestring
......
...@@ -8,8 +8,8 @@ cdef extern from *: ...@@ -8,8 +8,8 @@ cdef extern from *:
object PyErr_Format(exc, const char *format, ...) object PyErr_Format(exc, const char *format, ...)
@cname("{{funcname}}") @cname("{{funcname}}")
cdef {{struct_name}} {{funcname}}(obj) except *: cdef {{struct_type}} {{funcname}}(obj) except *:
cdef {{struct_name}} result cdef {{struct_type}} result
if not PyMapping_Check(obj): if not PyMapping_Check(obj):
PyErr_Format(TypeError, b"Expected %.16s, got %.200s", b"a mapping", Py_TYPE(obj).tp_name) PyErr_Format(TypeError, b"Expected %.16s, got %.200s", b"a mapping", Py_TYPE(obj).tp_name)
...@@ -33,8 +33,8 @@ cdef extern from *: ...@@ -33,8 +33,8 @@ cdef extern from *:
object PyErr_Format(exc, const char *format, ...) object PyErr_Format(exc, const char *format, ...)
@cname("{{funcname}}") @cname("{{funcname}}")
cdef {{struct_name}} {{funcname}}(obj) except *: cdef {{struct_type}} {{funcname}}(obj) except *:
cdef {{struct_name}} result cdef {{struct_type}} result
cdef Py_ssize_t length cdef Py_ssize_t length
if not PyMapping_Check(obj): if not PyMapping_Check(obj):
PyErr_Format(TypeError, b"Expected %.16s, got %.200s", b"a mapping", Py_TYPE(obj).tp_name) PyErr_Format(TypeError, b"Expected %.16s, got %.200s", b"a mapping", Py_TYPE(obj).tp_name)
......
...@@ -7,12 +7,12 @@ cdef extern from *: ...@@ -7,12 +7,12 @@ cdef extern from *:
cdef cppclass string "{{type}}": cdef cppclass string "{{type}}":
string() string()
string(char* c_str, size_t size) string(char* c_str, size_t size)
cdef char* __Pyx_PyObject_AsStringAndSize(object, Py_ssize_t*) except NULL cdef const char* __Pyx_PyObject_AsStringAndSize(object, Py_ssize_t*) except NULL
@cname("{{cname}}") @cname("{{cname}}")
cdef string {{cname}}(object o) except *: cdef string {{cname}}(object o) except *:
cdef Py_ssize_t length cdef Py_ssize_t length
cdef char* data = __Pyx_PyObject_AsStringAndSize(o, &length) cdef const char* data = __Pyx_PyObject_AsStringAndSize(o, &length)
return string(data, length) return string(data, length)
...@@ -27,7 +27,7 @@ cdef extern from *: ...@@ -27,7 +27,7 @@ cdef extern from *:
{{for py_type in ['PyObject', 'PyUnicode', 'PyStr', 'PyBytes', 'PyByteArray']}} {{for py_type in ['PyObject', 'PyUnicode', 'PyStr', 'PyBytes', 'PyByteArray']}}
cdef extern from *: cdef extern from *:
cdef object __Pyx_{{py_type}}_FromStringAndSize(char*, size_t) cdef object __Pyx_{{py_type}}_FromStringAndSize(const char*, size_t)
@cname("{{cname.replace("PyObject", py_type, 1)}}") @cname("{{cname.replace("PyObject", py_type, 1)}}")
cdef inline object {{cname.replace("PyObject", py_type, 1)}}(const string& s): cdef inline object {{cname.replace("PyObject", py_type, 1)}}(const string& s):
......
...@@ -661,3 +661,41 @@ bad: ...@@ -661,3 +661,41 @@ bad:
Py_XDECREF(ob); Py_XDECREF(ob);
return NULL; return NULL;
} }
/////////////// ImportNumPyArray.proto ///////////////
static PyObject *__pyx_numpy_ndarray = NULL;
static PyObject* __Pyx_ImportNumPyArrayTypeIfAvailable(void); /*proto*/
/////////////// ImportNumPyArray.cleanup ///////////////
Py_CLEAR(__pyx_numpy_ndarray);
/////////////// ImportNumPyArray ///////////////
//@requires: ImportExport.c::Import
static PyObject* __Pyx__ImportNumPyArray(void) {
PyObject *numpy_module, *ndarray_object = NULL;
numpy_module = __Pyx_Import(PYIDENT("numpy"), NULL, 0);
if (likely(numpy_module)) {
ndarray_object = PyObject_GetAttrString(numpy_module, "ndarray");
}
if (unlikely(!ndarray_object)) {
// ImportError, AttributeError, ...
PyErr_Clear();
}
if (unlikely(!ndarray_object || !PyObject_TypeCheck(ndarray_object, &PyType_Type))) {
Py_XDECREF(ndarray_object);
Py_INCREF(Py_None);
ndarray_object = Py_None;
}
return ndarray_object;
}
static CYTHON_INLINE PyObject* __Pyx_ImportNumPyArrayTypeIfAvailable(void) {
if (unlikely(!__pyx_numpy_ndarray)) {
__pyx_numpy_ndarray = __Pyx__ImportNumPyArray();
}
return __pyx_numpy_ndarray;
}
...@@ -37,8 +37,8 @@ ...@@ -37,8 +37,8 @@
#define __Pyx_sst_abs(value) ((value<0) ? -value : value) #define __Pyx_sst_abs(value) ((value<0) ? -value : value)
#endif #endif
static CYTHON_INLINE char* __Pyx_PyObject_AsString(PyObject*); static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject*);
static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject*, Py_ssize_t* length); static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject*, Py_ssize_t* length);
#define __Pyx_PyByteArray_FromString(s) PyByteArray_FromStringAndSize((const char*)s, strlen((const char*)s)) #define __Pyx_PyByteArray_FromString(s) PyByteArray_FromStringAndSize((const char*)s, strlen((const char*)s))
#define __Pyx_PyByteArray_FromStringAndSize(s, l) PyByteArray_FromStringAndSize((const char*)s, l) #define __Pyx_PyByteArray_FromStringAndSize(s, l) PyByteArray_FromStringAndSize((const char*)s, l)
...@@ -54,8 +54,11 @@ static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char*); ...@@ -54,8 +54,11 @@ static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char*);
#define __Pyx_PyStr_FromStringAndSize __Pyx_PyUnicode_FromStringAndSize #define __Pyx_PyStr_FromStringAndSize __Pyx_PyUnicode_FromStringAndSize
#endif #endif
#define __Pyx_PyObject_AsSString(s) ((signed char*) __Pyx_PyObject_AsString(s)) #define __Pyx_PyObject_AsWritableString(s) ((char*) __Pyx_PyObject_AsString(s))
#define __Pyx_PyObject_AsUString(s) ((unsigned char*) __Pyx_PyObject_AsString(s)) #define __Pyx_PyObject_AsWritableSString(s) ((signed char*) __Pyx_PyObject_AsString(s))
#define __Pyx_PyObject_AsWritableUString(s) ((unsigned char*) __Pyx_PyObject_AsString(s))
#define __Pyx_PyObject_AsSString(s) ((const signed char*) __Pyx_PyObject_AsString(s))
#define __Pyx_PyObject_AsUString(s) ((const unsigned char*) __Pyx_PyObject_AsString(s))
#define __Pyx_PyObject_FromCString(s) __Pyx_PyObject_FromString((const char*)s) #define __Pyx_PyObject_FromCString(s) __Pyx_PyObject_FromString((const char*)s)
#define __Pyx_PyBytes_FromCString(s) __Pyx_PyBytes_FromString((const char*)s) #define __Pyx_PyBytes_FromCString(s) __Pyx_PyBytes_FromString((const char*)s)
#define __Pyx_PyByteArray_FromCString(s) __Pyx_PyByteArray_FromString((const char*)s) #define __Pyx_PyByteArray_FromCString(s) __Pyx_PyByteArray_FromString((const char*)s)
...@@ -189,12 +192,14 @@ static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char* c_str) { ...@@ -189,12 +192,14 @@ static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char* c_str) {
return __Pyx_PyUnicode_FromStringAndSize(c_str, (Py_ssize_t)strlen(c_str)); return __Pyx_PyUnicode_FromStringAndSize(c_str, (Py_ssize_t)strlen(c_str));
} }
static CYTHON_INLINE char* __Pyx_PyObject_AsString(PyObject* o) { // Py3.7 returns a "const char*" for unicode strings
static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject* o) {
Py_ssize_t ignore; Py_ssize_t ignore;
return __Pyx_PyObject_AsStringAndSize(o, &ignore); return __Pyx_PyObject_AsStringAndSize(o, &ignore);
} }
static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_t *length) { // Py3.7 returns a "const char*" for unicode strings
static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_t *length) {
#if CYTHON_COMPILING_IN_CPYTHON && (__PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT) #if CYTHON_COMPILING_IN_CPYTHON && (__PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT)
if ( if (
#if PY_MAJOR_VERSION < 3 && __PYX_DEFAULT_STRING_ENCODING_IS_ASCII #if PY_MAJOR_VERSION < 3 && __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
......
...@@ -339,42 +339,57 @@ Public Declarations ...@@ -339,42 +339,57 @@ Public Declarations
--------------------- ---------------------
You can make C types, variables and functions defined in a Cython module You can make C types, variables and functions defined in a Cython module
accessible to C code that is linked with the module, by declaring them with accessible to C code that is linked together with the Cython-generated C file,
the public keyword:: by declaring them with the public keyword::
cdef public struct Bunny: # public type declaration cdef public struct Bunny: # public type declaration
int vorpalness int vorpalness
cdef public int spam # public variable declaration cdef public int spam # public variable declaration
cdef public void grail(Bunny *): # public function declaration cdef public void grail(Bunny *) # public function declaration
print "Ready the holy hand grenade"
If there are any public declarations in a Cython module, a header file called If there are any public declarations in a Cython module, a header file called
:file:`modulename.h` file is generated containing equivalent C declarations for :file:`modulename.h` file is generated containing equivalent C declarations for
inclusion in other C code. inclusion in other C code.
Users who are embedding Python in C with Cython need to make sure to call Py_Initialize() A typical use case for this is building an extension module from multiple
and Py_Finalize(). For example, in the following snippet that includes :file:`modulename.h`:: C sources, one of them being Cython generated (i.e. with something like
``Extension("grail", sources=["grail.pyx", "grail_helper.c"])`` in ``setup.py``.
In this case, the file ``grail_helper.c`` just needs to add
``#include "grail.h"`` in order to access the public Cython variables.
A more advanced use case is embedding Python in C using Cython.
In this case, make sure to call Py_Initialize() and Py_Finalize().
For example, in the following snippet that includes :file:`grail.h`:
.. code-block:: c
#include <Python.h> #include <Python.h>
#include "modulename.h" #include "grail.h"
void grail() { int main() {
Py_Initialize(); Py_Initialize();
initmodulename(); initgrail();
Bunny b; Bunny b;
grail(b); grail(b);
Py_Finalize(); Py_Finalize();
} }
Any C code wanting to make use of these declarations will need to be linked, This C code can then be built together with the Cython-generated C code
either statically or dynamically, with the extension module. in a single program (or library).
If the Cython module resides within a package, then the name of the ``.h`` If the Cython module resides within a package, then the name of the ``.h``
file consists of the full dotted name of the module, e.g. a module called file consists of the full dotted name of the module, e.g. a module called
:mod:`foo.spam` would have a header file called :file:`foo.spam.h`. :mod:`foo.spam` would have a header file called :file:`foo.spam.h`.
.. NOTE::
On some operating systems like Linux, it is also possible to first
build the Cython extension in the usual way and then link against
the resulting ``.so`` file like a dynamic library.
Beware that this is not portable, so it should be avoided.
.. _api: .. _api:
C API Declarations C API Declarations
...@@ -418,10 +433,12 @@ made available when you include :file:`modulename_api.h`.:: ...@@ -418,10 +433,12 @@ made available when you include :file:`modulename_api.h`.::
Vehicle car; Vehicle car;
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
Py_Initialize();
import_delorean(); import_delorean();
car.speed = atoi(argv[1]); car.speed = atoi(argv[1]);
car.power = atof(argv[2]); car.power = atof(argv[2]);
activate(&car); activate(&car);
Py_Finalize();
} }
.. note:: .. note::
...@@ -434,7 +451,10 @@ made available when you include :file:`modulename_api.h`.:: ...@@ -434,7 +451,10 @@ made available when you include :file:`modulename_api.h`.::
Using the :keyword:`api` method does not require the C code using the Using the :keyword:`api` method does not require the C code using the
declarations to be linked with the extension module in any way, as the Python declarations to be linked with the extension module in any way, as the Python
import machinery is used to make the connection dynamically. However, only import machinery is used to make the connection dynamically. However, only
functions can be accessed this way, not variables. functions can be accessed this way, not variables. Note also that for the
module import mechanism to be set up correctly, the user must call
Py_Initialize() and Py_Finalize(); if you experience a segmentation fault in
the call to :func:`import_modulename`, it is likely that this wasn't done.
You can use both :keyword:`public` and :keyword:`api` on the same function to You can use both :keyword:`public` and :keyword:`api` on the same function to
make it available by both methods, e.g.:: make it available by both methods, e.g.::
......
...@@ -6,3 +6,6 @@ cdef class MissingRedeclaration(Base): ...@@ -6,3 +6,6 @@ cdef class MissingRedeclaration(Base):
cdef class BadRedeclaration(Base): cdef class BadRedeclaration(Base):
cdef f(self) cdef f(self)
cdef class NarrowerReturn(Base):
pass
...@@ -28,6 +28,11 @@ cdef class UnneededRedeclaration(Base): ...@@ -28,6 +28,11 @@ cdef class UnneededRedeclaration(Base):
cpdef f(self): cpdef f(self):
pass pass
cdef class NarrowerReturn(Base):
# This does not require a new vtable entry.
cdef Base f(self):
pass
_ERRORS = u""" _ERRORS = u"""
8: 9: Signature not compatible with previous declaration 8: 9: Signature not compatible with previous declaration
......
...@@ -19,8 +19,15 @@ setup( ...@@ -19,8 +19,15 @@ setup(
######## site-packages/b/other.pxd ######## ######## site-packages/b/other.pxd ########
cdef inline foo(int a): cdef extern from "foo.c":
return a**2 int foo(int)
######## site-packages/b/foo.c ########
static int foo(int a)
{
return a * a;
}
######## a.pyx ######## ######## a.pyx ########
......
...@@ -304,3 +304,47 @@ def test_annotations(a: "test", b: "other" = 2, c: 123 = 4) -> "ret": ...@@ -304,3 +304,47 @@ def test_annotations(a: "test", b: "other" = 2, c: 123 = 4) -> "ret":
def inner(x: "banana", y: b()) -> c(): def inner(x: "banana", y: b()) -> c():
return x,y return x,y
return inner return inner
def add_one(func):
"Decorator to add 1 to the last argument of the function call"
def inner(*args):
args = args[:-1] + (args[-1] + 1,)
return func(*args)
return inner
@add_one
def test_decorated(x):
"""
>>> test_decorated(0)
1
"""
return x
@add_one
@add_one
def test_decorated2(x):
"""
>>> test_decorated2(0)
2
"""
return x
cdef class TestDecoratedMethods:
@add_one
def test(self, x):
"""
>>> TestDecoratedMethods().test(0)
1
"""
return x
@add_one
@add_one
def test2(self, x):
"""
>>> TestDecoratedMethods().test2(0)
2
"""
return x
...@@ -94,6 +94,12 @@ def test_multiarg(): ...@@ -94,6 +94,12 @@ def test_multiarg():
x is an int, y is a float: 1 2.0 x is an int, y is a float: 1 2.0
x is an int, y is a float: 1 2.0 x is an int, y is a float: 1 2.0
x is a long, y is a double: 4 5.0 x is a long, y is a double: 4 5.0
>>> multiarg()
Traceback (most recent call last):
TypeError: Expected at least 2 arguments, got 0
>>> multiarg(1, 2.0, 3) # doctest: +ELLIPSIS
Traceback (most recent call last):
TypeError: ...2...arg...3...
""" """
multiarg[int, float](1, 2.0) multiarg[int, float](1, 2.0)
multiarg[cy.int, cy.float](1, 2.0) multiarg[cy.int, cy.float](1, 2.0)
......
...@@ -105,11 +105,15 @@ def opt_func(fused_t obj, cython.floating myf = 1.2, cython.integral myi = 7): ...@@ -105,11 +105,15 @@ def opt_func(fused_t obj, cython.floating myf = 1.2, cython.integral myi = 7):
>>> opt_func(object(), f) >>> opt_func(object(), f)
Traceback (most recent call last): Traceback (most recent call last):
...
TypeError: Function call with ambiguous argument types TypeError: Function call with ambiguous argument types
>>> opt_func()
Traceback (most recent call last):
TypeError: Expected at least 1 argument, got 0
>>> opt_func("abc", f, i, 5) # doctest: +ELLIPSIS
Traceback (most recent call last):
TypeError: ...at most 3...
>>> opt_func[ExtClassA, cy.float, cy.long](object(), f) >>> opt_func[ExtClassA, cy.float, cy.long](object(), f)
Traceback (most recent call last): Traceback (most recent call last):
...
TypeError: Argument 'obj' has incorrect type (expected fused_def.ExtClassA, got object) TypeError: Argument 'obj' has incorrect type (expected fused_def.ExtClassA, got object)
""" """
print cython.typeof(obj), cython.typeof(myf), cython.typeof(myi) print cython.typeof(obj), cython.typeof(myf), cython.typeof(myi)
......
...@@ -181,8 +181,8 @@ try: ...@@ -181,8 +181,8 @@ try:
>>> print(test_nested_dtypes(np.zeros((3,), dtype=np.dtype([\ >>> print(test_nested_dtypes(np.zeros((3,), dtype=np.dtype([\
('a', np.dtype('i,i')),\ ('a', np.dtype('i,i')),\
('b', np.dtype('i,i'))\ ('b', np.dtype('i,i'))\
])))) ])))) # doctest: +NORMALIZE_WHITESPACE
array([((0, 0), (0, 0)), ((1, 2), (1, 4)), ((1, 2), (1, 4))], array([((0, 0), (0, 0)), ((1, 2), (1, 4)), ((1, 2), (1, 4))],
dtype=[('a', [('f0', '!i4'), ('f1', '!i4')]), ('b', [('f0', '!i4'), ('f1', '!i4')])]) dtype=[('a', [('f0', '!i4'), ('f1', '!i4')]), ('b', [('f0', '!i4'), ('f1', '!i4')])])
>>> print(test_nested_dtypes(np.zeros((3,), dtype=np.dtype([\ >>> print(test_nested_dtypes(np.zeros((3,), dtype=np.dtype([\
...@@ -234,8 +234,8 @@ try: ...@@ -234,8 +234,8 @@ try:
1,1 1,1
8,16 8,16
>>> test_point_record() >>> test_point_record() # doctest: +NORMALIZE_WHITESPACE
array([(0., 0.), (1., -1.), (2., -2.)], array([(0., 0.), (1., -1.), (2., -2.)],
dtype=[('x', '!f8'), ('y', '!f8')]) dtype=[('x', '!f8'), ('y', '!f8')])
""" """
......
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