[pypy-commit] pypy release-pypy2.7-5.x: merge default into release
mattip
pypy.commits at gmail.com
Thu Jun 1 09:27:02 EDT 2017
Author: Matti Picus <matti.picus at gmail.com>
Branch: release-pypy2.7-5.x
Changeset: r91482:c0b4e6c36ee6
Date: 2017-06-01 16:25 +0300
http://bitbucket.org/pypy/pypy/changeset/c0b4e6c36ee6/
Log: merge default into release
diff --git a/lib_pypy/cffi.egg-info/PKG-INFO b/lib_pypy/cffi.egg-info/PKG-INFO
--- a/lib_pypy/cffi.egg-info/PKG-INFO
+++ b/lib_pypy/cffi.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: cffi
-Version: 1.10.0
+Version: 1.11.0
Summary: Foreign Function Interface for Python calling C code.
Home-page: http://cffi.readthedocs.org
Author: Armin Rigo, Maciej Fijalkowski
diff --git a/lib_pypy/cffi/__init__.py b/lib_pypy/cffi/__init__.py
--- a/lib_pypy/cffi/__init__.py
+++ b/lib_pypy/cffi/__init__.py
@@ -4,8 +4,8 @@
from .api import FFI
from .error import CDefError, FFIError, VerificationError, VerificationMissing
-__version__ = "1.10.0"
-__version_info__ = (1, 10, 0)
+__version__ = "1.11.0"
+__version_info__ = (1, 11, 0)
# The verifier module file names are based on the CRC32 of a string that
# contains the following version number. It may be older than __version__
diff --git a/lib_pypy/cffi/_cffi_include.h b/lib_pypy/cffi/_cffi_include.h
--- a/lib_pypy/cffi/_cffi_include.h
+++ b/lib_pypy/cffi/_cffi_include.h
@@ -8,7 +8,7 @@
the same works for the other two macros. Py_DEBUG implies them,
but not the other way around.
*/
-#ifndef _CFFI_USE_EMBEDDING
+#if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API)
# include <pyconfig.h>
# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG)
# define Py_LIMITED_API
diff --git a/lib_pypy/cffi/_embedding.h b/lib_pypy/cffi/_embedding.h
--- a/lib_pypy/cffi/_embedding.h
+++ b/lib_pypy/cffi/_embedding.h
@@ -233,7 +233,7 @@
f = PySys_GetObject((char *)"stderr");
if (f != NULL && f != Py_None) {
PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME
- "\ncompiled with cffi version: 1.10.0"
+ "\ncompiled with cffi version: 1.11.0"
"\n_cffi_backend module: ", f);
modules = PyImport_GetModuleDict();
mod = PyDict_GetItemString(modules, "_cffi_backend");
diff --git a/lib_pypy/cffi/cffi_opcode.py b/lib_pypy/cffi/cffi_opcode.py
--- a/lib_pypy/cffi/cffi_opcode.py
+++ b/lib_pypy/cffi/cffi_opcode.py
@@ -105,8 +105,11 @@
PRIM_UINT_FAST64 = 45
PRIM_INTMAX = 46
PRIM_UINTMAX = 47
+PRIM_FLOATCOMPLEX = 48
+PRIM_DOUBLECOMPLEX = 49
-_NUM_PRIM = 48
+
+_NUM_PRIM = 50
_UNKNOWN_PRIM = -1
_UNKNOWN_FLOAT_PRIM = -2
_UNKNOWN_LONG_DOUBLE = -3
@@ -128,6 +131,8 @@
'float': PRIM_FLOAT,
'double': PRIM_DOUBLE,
'long double': PRIM_LONGDOUBLE,
+ 'float _Complex': PRIM_FLOATCOMPLEX,
+ 'double _Complex': PRIM_DOUBLECOMPLEX,
'_Bool': PRIM_BOOL,
'wchar_t': PRIM_WCHAR,
'int8_t': PRIM_INT8,
diff --git a/lib_pypy/cffi/cparser.py b/lib_pypy/cffi/cparser.py
--- a/lib_pypy/cffi/cparser.py
+++ b/lib_pypy/cffi/cparser.py
@@ -16,6 +16,7 @@
except ImportError:
lock = None
+CDEF_SOURCE_STRING = "<cdef source string>"
_r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$",
re.DOTALL | re.MULTILINE)
_r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)"
@@ -258,15 +259,21 @@
ctn.discard(name)
typenames += sorted(ctn)
#
- csourcelines = ['typedef int %s;' % typename for typename in typenames]
+ csourcelines = []
+ csourcelines.append('# 1 "<cdef automatic initialization code>"')
+ for typename in typenames:
+ csourcelines.append('typedef int %s;' % typename)
csourcelines.append('typedef int __dotdotdotint__, __dotdotdotfloat__,'
' __dotdotdot__;')
+ # this forces pycparser to consider the following in the file
+ # called <cdef source string> from line 1
+ csourcelines.append('# 1 "%s"' % (CDEF_SOURCE_STRING,))
csourcelines.append(csource)
- csource = '\n'.join(csourcelines)
+ fullcsource = '\n'.join(csourcelines)
if lock is not None:
lock.acquire() # pycparser is not thread-safe...
try:
- ast = _get_parser().parse(csource)
+ ast = _get_parser().parse(fullcsource)
except pycparser.c_parser.ParseError as e:
self.convert_pycparser_error(e, csource)
finally:
@@ -276,17 +283,17 @@
return ast, macros, csource
def _convert_pycparser_error(self, e, csource):
- # xxx look for ":NUM:" at the start of str(e) and try to interpret
- # it as a line number
+ # xxx look for "<cdef source string>:NUM:" at the start of str(e)
+ # and interpret that as a line number. This will not work if
+ # the user gives explicit ``# NUM "FILE"`` directives.
line = None
msg = str(e)
- if msg.startswith(':') and ':' in msg[1:]:
- linenum = msg[1:msg.find(':',1)]
- if linenum.isdigit():
- linenum = int(linenum, 10)
- csourcelines = csource.splitlines()
- if 1 <= linenum <= len(csourcelines):
- line = csourcelines[linenum-1]
+ match = re.match(r"%s:(\d+):" % (CDEF_SOURCE_STRING,), msg)
+ if match:
+ linenum = int(match.group(1), 10)
+ csourcelines = csource.splitlines()
+ if 1 <= linenum <= len(csourcelines):
+ line = csourcelines[linenum-1]
return line
def convert_pycparser_error(self, e, csource):
@@ -321,10 +328,12 @@
break
else:
assert 0
+ current_decl = None
#
try:
self._inside_extern_python = '__cffi_extern_python_stop'
for decl in iterator:
+ current_decl = decl
if isinstance(decl, pycparser.c_ast.Decl):
self._parse_decl(decl)
elif isinstance(decl, pycparser.c_ast.Typedef):
@@ -348,7 +357,13 @@
elif decl.__class__.__name__ == 'Pragma':
pass # skip pragma, only in pycparser 2.15
else:
- raise CDefError("unrecognized construct", decl)
+ raise CDefError("unexpected <%s>: this construct is valid "
+ "C but not valid in cdef()" %
+ decl.__class__.__name__, decl)
+ except CDefError as e:
+ if len(e.args) == 1:
+ e.args = e.args + (current_decl,)
+ raise
except FFIError as e:
msg = self._convert_pycparser_error(e, csource)
if msg:
diff --git a/lib_pypy/cffi/error.py b/lib_pypy/cffi/error.py
--- a/lib_pypy/cffi/error.py
+++ b/lib_pypy/cffi/error.py
@@ -5,10 +5,13 @@
class CDefError(Exception):
def __str__(self):
try:
- line = 'line %d: ' % (self.args[1].coord.line,)
+ current_decl = self.args[1]
+ filename = current_decl.coord.file
+ linenum = current_decl.coord.line
+ prefix = '%s:%d: ' % (filename, linenum)
except (AttributeError, TypeError, IndexError):
- line = ''
- return '%s%s' % (line, self.args[0])
+ prefix = ''
+ return '%s%s' % (prefix, self.args[0])
class VerificationError(Exception):
""" An error raised when verification fails
diff --git a/lib_pypy/cffi/ffiplatform.py b/lib_pypy/cffi/ffiplatform.py
--- a/lib_pypy/cffi/ffiplatform.py
+++ b/lib_pypy/cffi/ffiplatform.py
@@ -6,6 +6,7 @@
'extra_objects', 'depends']
def get_extension(srcfilename, modname, sources=(), **kwds):
+ _hack_at_distutils()
from distutils.core import Extension
allsources = [srcfilename]
for src in sources:
@@ -15,6 +16,7 @@
def compile(tmpdir, ext, compiler_verbose=0, debug=None):
"""Compile a C extension module using distutils."""
+ _hack_at_distutils()
saved_environ = os.environ.copy()
try:
outputfilename = _build(tmpdir, ext, compiler_verbose, debug)
@@ -113,3 +115,13 @@
f = cStringIO.StringIO()
_flatten(x, f)
return f.getvalue()
+
+def _hack_at_distutils():
+ # Windows-only workaround for some configurations: see
+ # https://bugs.python.org/issue23246 (Python 2.7 with
+ # a specific MS compiler suite download)
+ if sys.platform == "win32":
+ try:
+ import setuptools # for side-effects, patches distutils
+ except ImportError:
+ pass
diff --git a/lib_pypy/cffi/model.py b/lib_pypy/cffi/model.py
--- a/lib_pypy/cffi/model.py
+++ b/lib_pypy/cffi/model.py
@@ -95,7 +95,8 @@
class BasePrimitiveType(BaseType):
- pass
+ def is_complex_type(self):
+ return False
class PrimitiveType(BasePrimitiveType):
@@ -116,6 +117,8 @@
'float': 'f',
'double': 'f',
'long double': 'f',
+ 'float _Complex': 'j',
+ 'double _Complex': 'j',
'_Bool': 'i',
# the following types are not primitive in the C sense
'wchar_t': 'c',
@@ -163,6 +166,8 @@
return self.ALL_PRIMITIVE_TYPES[self.name] == 'i'
def is_float_type(self):
return self.ALL_PRIMITIVE_TYPES[self.name] == 'f'
+ def is_complex_type(self):
+ return self.ALL_PRIMITIVE_TYPES[self.name] == 'j'
def build_backend_type(self, ffi, finishlist):
return global_cache(self, ffi, 'new_primitive_type', self.name)
diff --git a/lib_pypy/cffi/parse_c_type.h b/lib_pypy/cffi/parse_c_type.h
--- a/lib_pypy/cffi/parse_c_type.h
+++ b/lib_pypy/cffi/parse_c_type.h
@@ -79,8 +79,10 @@
#define _CFFI_PRIM_UINT_FAST64 45
#define _CFFI_PRIM_INTMAX 46
#define _CFFI_PRIM_UINTMAX 47
+#define _CFFI_PRIM_FLOATCOMPLEX 48
+#define _CFFI_PRIM_DOUBLECOMPLEX 49
-#define _CFFI__NUM_PRIM 48
+#define _CFFI__NUM_PRIM 50
#define _CFFI__UNKNOWN_PRIM (-1)
#define _CFFI__UNKNOWN_FLOAT_PRIM (-2)
#define _CFFI__UNKNOWN_LONG_DOUBLE (-3)
diff --git a/lib_pypy/cffi/recompiler.py b/lib_pypy/cffi/recompiler.py
--- a/lib_pypy/cffi/recompiler.py
+++ b/lib_pypy/cffi/recompiler.py
@@ -506,7 +506,7 @@
def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
extraarg = ''
- if isinstance(tp, model.BasePrimitiveType):
+ if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type():
if tp.is_integer_type() and tp.name != '_Bool':
converter = '_cffi_to_c_int'
extraarg = ', %s' % tp.name
@@ -524,8 +524,10 @@
tovar, errcode)
return
#
- elif isinstance(tp, model.StructOrUnionOrEnum):
- # a struct (not a struct pointer) as a function argument
+ elif (isinstance(tp, model.StructOrUnionOrEnum) or
+ isinstance(tp, model.BasePrimitiveType)):
+ # a struct (not a struct pointer) as a function argument;
+ # or, a complex (the same code works)
self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'
% (tovar, self._gettypenum(tp), fromvar))
self._prnt(' %s;' % errcode)
@@ -570,7 +572,7 @@
return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
elif isinstance(tp, model.UnknownFloatType):
return '_cffi_from_c_double(%s)' % (var,)
- elif tp.name != 'long double':
+ elif tp.name != 'long double' and not tp.is_complex_type():
return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var)
else:
return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
@@ -734,21 +736,26 @@
#
# the PyPy version: need to replace struct/union arguments with
# pointers, and if the result is a struct/union, insert a first
- # arg that is a pointer to the result.
+ # arg that is a pointer to the result. We also do that for
+ # complex args and return type.
+ def need_indirection(type):
+ return (isinstance(type, model.StructOrUnion) or
+ (isinstance(type, model.PrimitiveType) and
+ type.is_complex_type()))
difference = False
arguments = []
call_arguments = []
context = 'argument of %s' % name
for i, type in enumerate(tp.args):
indirection = ''
- if isinstance(type, model.StructOrUnion):
+ if need_indirection(type):
indirection = '*'
difference = True
arg = type.get_c_name(' %sx%d' % (indirection, i), context)
arguments.append(arg)
call_arguments.append('%sx%d' % (indirection, i))
tp_result = tp.result
- if isinstance(tp_result, model.StructOrUnion):
+ if need_indirection(tp_result):
context = 'result of %s' % name
arg = tp_result.get_c_name(' *result', context)
arguments.insert(0, arg)
@@ -1180,7 +1187,7 @@
size_of_result = '(int)sizeof(%s)' % (
tp.result.get_c_name('', context),)
prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name)
- prnt(' { "%s", %s };' % (name, size_of_result))
+ prnt(' { "%s.%s", %s };' % (self.module_name, name, size_of_result))
prnt()
#
arguments = []
@@ -1479,6 +1486,12 @@
_patch_for_embedding(patchlist)
if target != '*':
_patch_for_target(patchlist, target)
+ if compiler_verbose:
+ if tmpdir == '.':
+ msg = 'the current directory is'
+ else:
+ msg = 'setting the current directory to'
+ print('%s %r' % (msg, os.path.abspath(tmpdir)))
os.chdir(tmpdir)
outputfilename = ffiplatform.compile('.', ext,
compiler_verbose, debug)
diff --git a/lib_pypy/cffi/verifier.py b/lib_pypy/cffi/verifier.py
--- a/lib_pypy/cffi/verifier.py
+++ b/lib_pypy/cffi/verifier.py
@@ -26,16 +26,6 @@
s = s.encode('ascii')
super(NativeIO, self).write(s)
-def _hack_at_distutils():
- # Windows-only workaround for some configurations: see
- # https://bugs.python.org/issue23246 (Python 2.7 with
- # a specific MS compiler suite download)
- if sys.platform == "win32":
- try:
- import setuptools # for side-effects, patches distutils
- except ImportError:
- pass
-
class Verifier(object):
@@ -126,7 +116,7 @@
return basename
def get_extension(self):
- _hack_at_distutils() # backward compatibility hack
+ ffiplatform._hack_at_distutils() # backward compatibility hack
if not self._has_source:
with self.ffi._lock:
if not self._has_source:
diff --git a/pypy/doc/release-v5.8.0.rst b/pypy/doc/release-v5.8.0.rst
--- a/pypy/doc/release-v5.8.0.rst
+++ b/pypy/doc/release-v5.8.0.rst
@@ -8,8 +8,7 @@
the dual release. Note that PyPy3.5 supports Linux 64bit only for now.
This new PyPy2.7 release includes the upstream stdlib version 2.7.13, and
-PyPy3.5 (our first in the 3.5 series) includes the upstream stdlib version
-3.5.3.
+PyPy3.5 includes the upstream stdlib version 3.5.3.
We continue to make incremental improvements to our C-API
compatibility layer (cpyext). PyPy2 can now import and run many C-extension
@@ -19,13 +18,13 @@
faster but need real-world examples (not micro-benchmarks) of problematic code.
Work proceeds at a good pace on the PyPy3.5
-version due to a grant_ from the Mozilla Foundation, hence our first 3.5.3 beta
+version due to a grant_ from the Mozilla Foundation, hence our 3.5.3 beta
release. Thanks Mozilla !!! While we do not pass all tests yet, asyncio works and
as `these benchmarks show`_ it already gives a nice speed bump.
We also backported the ``f""`` formatting from 3.6 (as an exception; otherwise
"PyPy3.5" supports the Python 3.5 language).
-CFFI_ has been updated to 1.10, improving an already great package for
+CFFI_ has been updated to 1.11, improving an already great package for
interfacing with C.
As always, this release fixed many issues and bugs raised by the
@@ -81,48 +80,117 @@
See also issues that were resolved_
+Note that these are also merged into PyPy 3.5
+
* New features and cleanups
- *
- *
+ * Implement PyModule_New, Py_GetRecursionLimit, Py_SetRecursionLimit,
+ Py_EnterRecursiveCall, Py_LeaveRecursiveCall, populate tp_descr_get and
+ tp_descr_set slots,
+ add conversions of ``__len__``, ``__setitem__``, ``__delitem__`` to
+ appropriate C-API slots
+ * Fix for multiple inheritance in app-level for C-API defined classes
+ * Revert a change that removed tp_getattr (Part of the 5.7.1 bugfix release)
+ * Document more differences with CPython here_
+ * Add native PyPy support to profile frames in vmprof
+ * Fix an issue with Exception order on failed import
+ * Fix for a corner case of __future__ imports
+ * Update packaged Windows zlib, sqlite, expat and OpenSSL to versions used
+ by CPython
+ * Allow windows builds to use ``jom.exe`` for compiling in parallel
+ * Rewrite ``itertools.groupby()``, following CPython
+ * Backport changes from PyPy 3.5 to minimize the code differences
+ * Improve support for BSD using patches contributed by downstream
+ * Support profile-guided optimization, enabled with --profopt, , and
+ specify training data ``profoptpath``
-* Bug Fixes
+* Bug Fixes
- *
- *
+ * Correctly handle dict.pop where the popping key is not the same type as the
+ dict's and pop is called with a default (Part of the 5.7.1 bugfix release)
+ * Improve our file's universal newline .readline implementation for
+ ``\n``, ``\r`` confusion
+ * Tweak issue where ctype array ``_base`` was set on empty arrays, now it
+ is closer to the implementation in CPython
+ * Fix critical bugs in shadowstack that crashed multithreaded programs and
+ very rarely showed up even in single threaded programs
+ * Remove flaky fastpath function call from ctypes
+ * Support passing a buffersize of 0 to socket.getsockopt
+ * Avoid hash() returning -1 in cpyext
* Performance improvements:
- *
- *
+ * Tweaks made to improve performance by reducing the number of guards
+ inserted in jitted code, based on feedback from users
+ * Add garbage collector memory pressure to some c-level allocations
+ * Speed up struck.pack, struck.pack_into
+ * Performance tweaks to round(x, n) for the case n == 0
+ * Improve zipfile performance by not doing repeated string concatenation
* RPython improvements
- *
- *
+ * Improve the default shadowstack garbage collector, fixing a crash with
+ multithreaded code and other issues
+ * Make sure lstrip consumes the entire string
+ * Support posix_fallocate and posix_fadvise, expose them on PyPy3.5
+ * Test and fix for int_and() propagating wrong bounds
+ * Improve the generated machine code by tracking the (constant) value of
+ r11 across intructions. This lets us avoid reloading r11 with another
+ (apparently slowish) "movabs" instruction, replacing it with either
+ nothing or a cheaper variant.
+ * Performance tweaks in the x86 JIT-generated machine code: rarely taken
+ blocks are moved off-line. Also, the temporary register used to contain
+ large constants is reused across instructions. This helps CPUs branch
+ predictor
+ * Refactor rpython.rtyper.controllerentry to use use ``@specialize`` instead
+ of ``._annspecialcase_``
+ * Refactor handling of buffers and memoryviews. Memoryviews will now be
+ accepted in a few more places, e.g. in compile()
+.. _here: http://rpython.readthedocs.io/en/latest/cpython_differences.html
+
Highlights of the PyPy3.5 release (since 5.7 beta released March 2017)
======================================================================
* New features
- *
- *
+ * Implement main part of PEP 489 (multi-phase extension module initialization)
+ * Add docstrings to various modules and functions
+ * Adapt many CPython bug/feature fixes from CPython 3.5 to PyPy3.5
+ * Translation succeeds on Mac OS X, unfortunately our buildbot slave cannot
+ be updated to the proper development versions of OpenSSL to properly
+ package a release.
+ * Implement `` _SSLSocket.server_side``
+ * Do not silently ignore ``_swappedbytes_`` in ctypes. We now raise a
+ ``NotImplementedError``
+ * Implement and expose ``msvcrt.SetErrorMode``
+ * Implement ``PyModule_GetState``
* Bug Fixes
- *
- *
+ * Fix inconsistencies in the xml.etree.ElementTree.Element class, which on
+ CPython is hidden by the C version from '_elementree'.
+ * OSError(None,None) is different from OSError()
+ * Get closer to supporting 32 bit windows, translation now succeeds and most
+ lib-python/3/test runs
+ * Call ``sys.__interactivehook__`` at startup
* Performance improvements:
- *
+ * Use "<python> -m test" to run the CPython test suite, as documented by CPython,
+ instead of our outdated regrverbose.py script
+ * Change _cffi_src/openssl/callbacks.py to stop relying on the CPython C API.
+ * Avoid importing the full locale module during _io initialization,
+ CPython change fbbf8b160e8d
+ * Avoid freezing many app-level modules at translation, avoid importing many
+ modules at startup
+ * Refactor buffers, which allows an optimization for
+ ``bytearray()[:n].tobytes()``
* The following features of Python 3.5 are not implemented yet in PyPy:
* PEP 442: Safe object finalization
- * PEP 489: Multi-phase extension module initialization
.. _resolved: whatsnew-pypy2-5.8.0.html
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -3,6 +3,8 @@
==========================
.. this is a revision shortly after release-pypy2.7-v5.8.0
-.. startrev: 0c91b5f4f275
+.. startrev: 558bd00b3dd8
+.. branch: cffi-complex
+Part of the upgrade to cffi 1.11
diff --git a/pypy/doc/whatsnew-pypy3-5.8.0.rst b/pypy/doc/whatsnew-pypy3-5.8.0.rst
new file mode 100644
--- /dev/null
+++ b/pypy/doc/whatsnew-pypy3-5.8.0.rst
@@ -0,0 +1,32 @@
+=========================
+What's new in PyPy3 5.7+
+=========================
+
+.. this is the revision after release-pypy3.3-5.7.x was branched
+.. startrev: afbf09453369
+
+.. branch: mtest
+Use "<python> -m test" to run the CPython test suite, as documented by CPython,
+instead of our outdated regrverbose.py script.
+
+.. branch: win32-faulthandler
+
+Enable the 'faulthandler' module on Windows;
+this unblocks the Python test suite.
+
+.. branch: superjumbo
+
+Implement posix.posix_fallocate() and posix.posix_fadvise()
+
+.. branch: py3.5-mac-translate
+
+Fix for different posix primitives on MacOS
+
+.. branch: PyBuffer
+
+Internal refactoring of memoryviews and buffers, fixing some related
+performance issues.
+
+.. branch: jumbojet
+
+Add sched_get min/max to rposix
diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py
--- a/pypy/module/_cffi_backend/__init__.py
+++ b/pypy/module/_cffi_backend/__init__.py
@@ -3,7 +3,7 @@
from rpython.rlib import rdynload, clibffi
from rpython.rtyper.lltypesystem import rffi
-VERSION = "1.10.0"
+VERSION = "1.11.0"
FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI
try:
diff --git a/pypy/module/_cffi_backend/cdataobj.py b/pypy/module/_cffi_backend/cdataobj.py
--- a/pypy/module/_cffi_backend/cdataobj.py
+++ b/pypy/module/_cffi_backend/cdataobj.py
@@ -91,6 +91,11 @@
w_result = self.ctype.float(ptr)
return w_result
+ def complex(self):
+ with self as ptr:
+ w_result = self.ctype.complex(ptr)
+ return w_result
+
def len(self):
from pypy.module._cffi_backend import ctypearray
space = self.space
@@ -405,6 +410,13 @@
with self as ptr:
misc.write_raw_float_data(ptr, source, self.ctype.size)
+ def write_raw_complex_data(self, real, imag):
+ with self as ptr:
+ halfsize = self.ctype.size >> 1
+ ptr2 = rffi.ptradd(ptr, halfsize)
+ misc.write_raw_float_data(ptr, real, halfsize)
+ misc.write_raw_float_data(ptr2, imag, halfsize)
+
def convert_to_object(self):
with self as ptr:
w_obj = self.ctype.convert_to_object(ptr)
@@ -646,6 +658,7 @@
__int__ = interp2app(W_CData.int),
__long__ = interp2app(W_CData.long),
__float__ = interp2app(W_CData.float),
+ __complex__ = interp2app(W_CData.complex),
__len__ = interp2app(W_CData.len),
__lt__ = interp2app(W_CData.lt),
__le__ = interp2app(W_CData.le),
diff --git a/pypy/module/_cffi_backend/cffi_opcode.py b/pypy/module/_cffi_backend/cffi_opcode.py
--- a/pypy/module/_cffi_backend/cffi_opcode.py
+++ b/pypy/module/_cffi_backend/cffi_opcode.py
@@ -105,8 +105,10 @@
PRIM_UINT_FAST64 = 45
PRIM_INTMAX = 46
PRIM_UINTMAX = 47
+PRIM_FLOATCOMPLEX = 48
+PRIM_DOUBLECOMPLEX = 49
-_NUM_PRIM = 48
+_NUM_PRIM = 50
_UNKNOWN_PRIM = -1
_UNKNOWN_FLOAT_PRIM = -2
_UNKNOWN_LONG_DOUBLE = -3
diff --git a/pypy/module/_cffi_backend/ctypefunc.py b/pypy/module/_cffi_backend/ctypefunc.py
--- a/pypy/module/_cffi_backend/ctypefunc.py
+++ b/pypy/module/_cffi_backend/ctypefunc.py
@@ -20,7 +20,7 @@
from pypy.module._cffi_backend.ctypestruct import W_CTypeStruct, W_CTypeUnion
from pypy.module._cffi_backend.ctypeprim import (W_CTypePrimitiveSigned,
W_CTypePrimitiveUnsigned, W_CTypePrimitiveCharOrUniChar,
- W_CTypePrimitiveFloat, W_CTypePrimitiveLongDouble)
+ W_CTypePrimitiveFloat, W_CTypePrimitiveLongDouble, W_CTypePrimitiveComplex)
class W_CTypeFunc(W_CTypePtrBase):
@@ -212,18 +212,21 @@
# ----------
# We attach to the classes small methods that return a 'ffi_type'
-def _missing_ffi_type(self, cifbuilder, is_result_type):
- space = self.space
- if self.size < 0:
- raise oefmt(space.w_TypeError,
- "ctype '%s' has incomplete type", self.name)
+
+def _notimplemented_ffi_type(self, is_result_type, extra=''):
if is_result_type:
place = "return value"
else:
place = "argument"
- raise oefmt(space.w_NotImplementedError,
- "ctype '%s' (size %d) not supported as %s",
- self.name, self.size, place)
+ raise oefmt(self.space.w_NotImplementedError,
+ "ctype '%s' (size %d) not supported as %s%s",
+ self.name, self.size, place, extra)
+
+def _missing_ffi_type(self, cifbuilder, is_result_type):
+ if self.size < 0:
+ raise oefmt(self.space.w_TypeError,
+ "ctype '%s' has incomplete type", self.name)
+ raise _notimplemented_ffi_type(self, is_result_type)
def _struct_ffi_type(self, cifbuilder, is_result_type):
if self.size >= 0:
@@ -260,6 +263,13 @@
def _primlongdouble_ffi_type(self, cifbuilder, is_result_type):
return clibffi.ffi_type_longdouble
+def _primcomplex_ffi_type(self, cifbuilder, is_result_type):
+ raise _notimplemented_ffi_type(self, is_result_type,
+ extra = " (the support for complex types inside libffi "
+ "is mostly missing at this point, so CFFI only "
+ "supports complex types as arguments or return "
+ "value in API-mode functions)")
+
def _ptr_ffi_type(self, cifbuilder, is_result_type):
return clibffi.ffi_type_pointer
@@ -276,6 +286,7 @@
W_CTypePrimitiveUnsigned._get_ffi_type = _primunsigned_ffi_type
W_CTypePrimitiveFloat._get_ffi_type = _primfloat_ffi_type
W_CTypePrimitiveLongDouble._get_ffi_type = _primlongdouble_ffi_type
+W_CTypePrimitiveComplex._get_ffi_type = _primcomplex_ffi_type
W_CTypePtrBase._get_ffi_type = _ptr_ffi_type
W_CTypeVoid._get_ffi_type = _void_ffi_type
# ----------
diff --git a/pypy/module/_cffi_backend/ctypeobj.py b/pypy/module/_cffi_backend/ctypeobj.py
--- a/pypy/module/_cffi_backend/ctypeobj.py
+++ b/pypy/module/_cffi_backend/ctypeobj.py
@@ -73,6 +73,14 @@
raise oefmt(space.w_TypeError, "float() not supported on cdata '%s'",
self.name)
+ def complex(self, cdata):
+ # <cdata 'float'> or <cdata 'int'> cannot be directly converted by
+ # calling complex(), just like <cdata 'int'> cannot be directly
+ # converted by calling float()
+ space = self.space
+ raise oefmt(space.w_TypeError, "complex() not supported on cdata '%s'",
+ self.name)
+
def convert_to_object(self, cdata):
space = self.space
raise oefmt(space.w_TypeError, "cannot return a cdata '%s'", self.name)
diff --git a/pypy/module/_cffi_backend/ctypeprim.py b/pypy/module/_cffi_backend/ctypeprim.py
--- a/pypy/module/_cffi_backend/ctypeprim.py
+++ b/pypy/module/_cffi_backend/ctypeprim.py
@@ -532,3 +532,51 @@
@jit.dont_look_inside
def nonzero(self, cdata):
return misc.is_nonnull_longdouble(cdata)
+
+
+class W_CTypePrimitiveComplex(W_CTypePrimitive):
+ _attrs_ = []
+
+ def cast(self, w_ob):
+ space = self.space
+ if isinstance(w_ob, cdataobj.W_CData):
+ if not isinstance(w_ob.ctype, W_CTypePrimitive):
+ raise oefmt(space.w_TypeError,
+ "cannot cast ctype '%s' to ctype '%s'",
+ w_ob.ctype.name, self.name)
+ w_ob = w_ob.convert_to_object()
+ #
+ imag = 0.0
+ if space.isinstance_w(w_ob, space.w_bytes):
+ real = self.cast_str(w_ob)
+ elif space.isinstance_w(w_ob, space.w_unicode):
+ real = self.cast_unicode(w_ob)
+ else:
+ real, imag = space.unpackcomplex(w_ob)
+ w_cdata = cdataobj.W_CDataMem(space, self)
+ w_cdata.write_raw_complex_data(real, imag)
+ return w_cdata
+
+ def complex(self, cdata):
+ return self.convert_to_object(cdata)
+
+ def convert_to_object(self, cdata):
+ halfsize = self.size >> 1
+ cdata2 = rffi.ptradd(cdata, halfsize)
+ real = misc.read_raw_float_data(cdata, halfsize)
+ imag = misc.read_raw_float_data(cdata2, halfsize)
+ return self.space.newcomplex(real, imag)
+
+ def convert_from_object(self, cdata, w_ob):
+ space = self.space
+ real, imag = space.unpackcomplex(w_ob)
+ halfsize = self.size >> 1
+ cdata2 = rffi.ptradd(cdata, halfsize)
+ misc.write_raw_float_data(cdata, real, halfsize)
+ misc.write_raw_float_data(cdata2, imag, halfsize)
+
+ def nonzero(self, cdata):
+ halfsize = self.size >> 1
+ cdata2 = rffi.ptradd(cdata, halfsize)
+ return (misc.is_nonnull_float(cdata, halfsize) |
+ misc.is_nonnull_float(cdata2, halfsize))
diff --git a/pypy/module/_cffi_backend/newtype.py b/pypy/module/_cffi_backend/newtype.py
--- a/pypy/module/_cffi_backend/newtype.py
+++ b/pypy/module/_cffi_backend/newtype.py
@@ -66,8 +66,8 @@
PRIMITIVE_TYPES = {}
-def eptype(name, TYPE, ctypecls):
- PRIMITIVE_TYPES[name] = ctypecls, rffi.sizeof(TYPE), alignment(TYPE)
+def eptype(name, TYPE, ctypecls, rep=1):
+ PRIMITIVE_TYPES[name] = ctypecls, rffi.sizeof(TYPE) * rep, alignment(TYPE)
def eptypesize(name, size, ctypecls):
for TYPE in [lltype.Signed, lltype.SignedLongLong, rffi.SIGNEDCHAR,
@@ -94,6 +94,9 @@
eptype("long double", rffi.LONGDOUBLE, ctypeprim.W_CTypePrimitiveLongDouble)
eptype("_Bool", lltype.Bool, ctypeprim.W_CTypePrimitiveBool)
+eptype("float _Complex", rffi.FLOAT, ctypeprim.W_CTypePrimitiveComplex, rep=2)
+eptype("double _Complex", rffi.DOUBLE, ctypeprim.W_CTypePrimitiveComplex, rep=2)
+
eptypesize("int8_t", 1, ctypeprim.W_CTypePrimitiveSigned)
eptypesize("uint8_t", 1, ctypeprim.W_CTypePrimitiveUnsigned)
eptypesize("int16_t", 2, ctypeprim.W_CTypePrimitiveSigned)
diff --git a/pypy/module/_cffi_backend/realize_c_type.py b/pypy/module/_cffi_backend/realize_c_type.py
--- a/pypy/module/_cffi_backend/realize_c_type.py
+++ b/pypy/module/_cffi_backend/realize_c_type.py
@@ -8,6 +8,7 @@
from pypy.module import _cffi_backend
from pypy.module._cffi_backend.ctypeobj import W_CType
from pypy.module._cffi_backend import cffi_opcode, newtype, ctypestruct
+from pypy.module._cffi_backend import ctypeprim
from pypy.module._cffi_backend import parse_c_type
@@ -70,6 +71,8 @@
"uint_fast64_t",
"intmax_t",
"uintmax_t",
+ "float _Complex",
+ "double _Complex",
]
assert len(NAMES) == cffi_opcode._NUM_PRIM
@@ -209,7 +212,7 @@
# which the struct args are replaced with ptr-to- struct, and
# a struct return value is replaced with a hidden first arg of
# type ptr-to-struct. This is how recompiler.py produces
- # trampoline functions for PyPy.
+ # trampoline functions for PyPy. (Same with complex numbers.)
if self.nostruct_ctype is None:
fargs, fret, ellipsis, abi = self._unpack(ffi)
# 'locs' will be a string of the same length as the final fargs,
@@ -218,11 +221,13 @@
locs = ['\x00'] * len(fargs)
for i in range(len(fargs)):
farg = fargs[i]
- if isinstance(farg, ctypestruct.W_CTypeStructOrUnion):
+ if (isinstance(farg, ctypestruct.W_CTypeStructOrUnion) or
+ isinstance(farg, ctypeprim.W_CTypePrimitiveComplex)):
farg = newtype.new_pointer_type(ffi.space, farg)
fargs[i] = farg
locs[i] = 'A'
- if isinstance(fret, ctypestruct.W_CTypeStructOrUnion):
+ if (isinstance(fret, ctypestruct.W_CTypeStructOrUnion) or
+ isinstance(fret, ctypeprim.W_CTypePrimitiveComplex)):
fret = newtype.new_pointer_type(ffi.space, fret)
fargs = [fret] + fargs
locs = ['R'] + locs
diff --git a/pypy/module/_cffi_backend/src/parse_c_type.c b/pypy/module/_cffi_backend/src/parse_c_type.c
--- a/pypy/module/_cffi_backend/src/parse_c_type.c
+++ b/pypy/module/_cffi_backend/src/parse_c_type.c
@@ -37,7 +37,7 @@
/* keywords */
TOK__BOOL,
TOK_CHAR,
- //TOK__COMPLEX,
+ TOK__COMPLEX,
TOK_CONST,
TOK_DOUBLE,
TOK_ENUM,
@@ -171,6 +171,7 @@
if (tok->size == 5 && !memcmp(p, "_Bool", 5)) tok->kind = TOK__BOOL;
if (tok->size == 7 && !memcmp(p,"__cdecl",7)) tok->kind = TOK_CDECL;
if (tok->size == 9 && !memcmp(p,"__stdcall",9))tok->kind = TOK_STDCALL;
+ if (tok->size == 8 && !memcmp(p,"_Complex",8)) tok->kind = TOK__COMPLEX;
break;
case 'c':
if (tok->size == 4 && !memcmp(p, "char", 4)) tok->kind = TOK_CHAR;
@@ -613,6 +614,7 @@
{
unsigned int t0;
_cffi_opcode_t t1;
+ _cffi_opcode_t t1complex;
int modifiers_length, modifiers_sign;
qualifiers:
@@ -668,6 +670,8 @@
break;
}
+ t1complex = 0;
+
if (modifiers_length || modifiers_sign) {
switch (tok->kind) {
@@ -678,6 +682,7 @@
case TOK_STRUCT:
case TOK_UNION:
case TOK_ENUM:
+ case TOK__COMPLEX:
return parse_error(tok, "invalid combination of types");
case TOK_DOUBLE:
@@ -731,9 +736,11 @@
break;
case TOK_FLOAT:
t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_FLOAT);
+ t1complex = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_FLOATCOMPLEX);
break;
case TOK_DOUBLE:
t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_DOUBLE);
+ t1complex = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_DOUBLECOMPLEX);
break;
case TOK_IDENTIFIER:
{
@@ -800,6 +807,13 @@
}
next_token(tok);
}
+ if (tok->kind == TOK__COMPLEX)
+ {
+ if (t1complex == 0)
+ return parse_error(tok, "_Complex type combination unsupported");
+ t1 = t1complex;
+ next_token(tok);
+ }
return parse_sequel(tok, write_ds(tok, t1));
}
diff --git a/pypy/module/_cffi_backend/src/parse_c_type.h b/pypy/module/_cffi_backend/src/parse_c_type.h
--- a/pypy/module/_cffi_backend/src/parse_c_type.h
+++ b/pypy/module/_cffi_backend/src/parse_c_type.h
@@ -78,8 +78,10 @@
#define _CFFI_PRIM_UINT_FAST64 45
#define _CFFI_PRIM_INTMAX 46
#define _CFFI_PRIM_UINTMAX 47
+#define _CFFI_PRIM_FLOATCOMPLEX 48
+#define _CFFI_PRIM_DOUBLECOMPLEX 49
-#define _CFFI__NUM_PRIM 48
+#define _CFFI__NUM_PRIM 50
#define _CFFI__UNKNOWN_PRIM (-1)
#define _CFFI__UNKNOWN_FLOAT_PRIM (-2)
#define _CFFI__UNKNOWN_LONG_DOUBLE (-3)
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -1,7 +1,7 @@
# ____________________________________________________________
import sys
-assert __version__ == "1.10.0", ("This test_c.py file is for testing a version"
+assert __version__ == "1.11.0", ("This test_c.py file is for testing a version"
" of cffi that differs from the one that we"
" get from 'import _cffi_backend'")
if sys.version_info < (3,):
@@ -174,37 +174,56 @@
py.test.raises(TypeError, cast, p, None)
def test_complex_types():
- py.test.skip("later")
INF = 1E200 * 1E200
for name in ["float", "double"]:
- p = new_primitive_type("_Complex " + name)
- assert bool(cast(p, 0))
+ p = new_primitive_type(name + " _Complex")
+ assert bool(cast(p, 0)) is False
assert bool(cast(p, INF))
assert bool(cast(p, -INF))
- assert bool(cast(p, 0j))
+ assert bool(cast(p, 0j)) is False
assert bool(cast(p, INF*1j))
assert bool(cast(p, -INF*1j))
+ # "can't convert complex to float", like CPython's "float(0j)"
py.test.raises(TypeError, int, cast(p, -150))
py.test.raises(TypeError, long, cast(p, -150))
py.test.raises(TypeError, float, cast(p, -150))
assert complex(cast(p, 1.25)) == 1.25
assert complex(cast(p, 1.25j)) == 1.25j
- assert float(cast(p, INF*1j)) == INF*1j
- assert float(cast(p, -INF)) == -INF
+ assert complex(cast(p, complex(0,INF))) == complex(0,INF)
+ assert complex(cast(p, -INF)) == -INF
if name == "float":
assert complex(cast(p, 1.1j)) != 1.1j # rounding error
assert complex(cast(p, 1E200+3j)) == INF+3j # limited range
- assert complex(cast(p, 3+1E200j)) == 3+INF*1j # limited range
+ assert complex(cast(p, complex(3,1E200))) == complex(3,INF) # limited range
- assert cast(p, -1.1j) != cast(p, -1.1j)
+ assert cast(p, -1.1j) == cast(p, -1.1j)
assert repr(complex(cast(p, -0.0)).real) == '-0.0'
- assert repr(complex(cast(p, -0j))) == '-0j'
- assert complex(cast(p, '\x09')) == 9.0
- assert complex(cast(p, True)) == 1.0
+ #assert repr(complex(cast(p, -0j))) == '-0j' # http://bugs.python.org/issue29602
+ assert complex(cast(p, b'\x09')) == 9.0 + 0j
+ assert complex(cast(p, u+'\x09')) == 9.0 + 0j
+ assert complex(cast(p, True)) == 1.0 + 0j
py.test.raises(TypeError, cast, p, None)
#
- py.test.raises(cast, new_primitive_type(name), 1+2j)
- py.test.raises(cast, new_primitive_type("int"), 1+2j)
+ py.test.raises(TypeError, cast, new_primitive_type(name), 1+0j)
+ #
+ for basetype in ["char", "int", "uint64_t", "float",
+ "double", "long double"]:
+ baseobj = cast(new_primitive_type(basetype), 65)
+ py.test.raises(TypeError, complex, baseobj)
+ #
+ BArray = new_array_type(new_pointer_type(p), 10)
+ x = newp(BArray, None)
+ x[5] = 12.34 + 56.78j
+ assert type(x[5]) is complex
+ assert abs(x[5] - (12.34 + 56.78j)) < 1e-5
+ assert (x[5] == 12.34 + 56.78j) == (name == "double") # rounding error
+ #
+ class Foo:
+ def __complex__(self):
+ return 2 + 3j
+ assert complex(Foo()) == 2 + 3j
+ assert complex(cast(p, Foo())) == 2 + 3j
+ py.test.raises(TypeError, cast, new_primitive_type("int"), 1+0j)
def test_character_type():
p = new_primitive_type("char")
@@ -1105,6 +1124,34 @@
BSShort = new_primitive_type("short")
assert f(3, cast(BSChar, -3), cast(BUChar, 200), cast(BSShort, -5)) == 192
+def test_call_function_24():
+ BFloat = new_primitive_type("float")
+ BFloatComplex = new_primitive_type("float _Complex")
+ BFunc3 = new_function_type((BFloat, BFloat), BFloatComplex, False)
+ if 0: # libffi returning nonsense silently, so logic disabled for now
+ f = cast(BFunc3, _testfunc(24))
+ result = f(1.25, 5.1)
+ assert type(result) == complex
+ assert result.real == 1.25 # exact
+ assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact
+ else:
+ f = cast(BFunc3, _testfunc(9))
+ py.test.raises(NotImplementedError, f, 12.3, 34.5)
+
+def test_call_function_25():
+ BDouble = new_primitive_type("double")
+ BDoubleComplex = new_primitive_type("double _Complex")
+ BFunc3 = new_function_type((BDouble, BDouble), BDoubleComplex, False)
+ if 0: # libffi returning nonsense silently, so logic disabled for now
+ f = cast(BFunc3, _testfunc(25))
+ result = f(1.25, 5.1)
+ assert type(result) == complex
+ assert result.real == 1.25 # exact
+ assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-10) # inexact
+ else:
+ f = cast(BFunc3, _testfunc(9))
+ py.test.raises(NotImplementedError, f, 12.3, 34.5)
+
def test_cannot_call_with_a_autocompleted_struct():
BSChar = new_primitive_type("signed char")
BDouble = new_primitive_type("double")
@@ -3796,7 +3843,7 @@
def test_char_pointer_conversion():
import warnings
- assert __version__.startswith(("1.8", "1.9", "1.10")), (
+ assert __version__.startswith(("1.8", "1.9", "1.10", "1.11")), (
"consider turning the warning into an error")
BCharP = new_pointer_type(new_primitive_type("char"))
BIntP = new_pointer_type(new_primitive_type("int"))
diff --git a/pypy/module/_cffi_backend/test/test_parse_c_type.py b/pypy/module/_cffi_backend/test/test_parse_c_type.py
--- a/pypy/module/_cffi_backend/test/test_parse_c_type.py
+++ b/pypy/module/_cffi_backend/test/test_parse_c_type.py
@@ -148,6 +148,8 @@
("long int", cffi_opcode.PRIM_LONG),
("unsigned short", cffi_opcode.PRIM_USHORT),
("long double", cffi_opcode.PRIM_LONGDOUBLE),
+ (" float _Complex", cffi_opcode.PRIM_FLOATCOMPLEX),
+ ("double _Complex ", cffi_opcode.PRIM_DOUBLECOMPLEX),
]:
assert parse(simple_type) == ['->', Prim(expected)]
@@ -273,6 +275,11 @@
parse_error("int[5](*)", "unexpected symbol", 6)
parse_error("int a(*)", "identifier expected", 6)
parse_error("int[123456789012345678901234567890]", "number too large", 4)
+ #
+ parse_error("_Complex", "identifier expected", 0)
+ parse_error("int _Complex", "_Complex type combination unsupported", 4)
+ parse_error("long double _Complex", "_Complex type combination unsupported",
+ 12)
def test_number_too_large():
num_max = sys.maxsize
diff --git a/pypy/module/_cffi_backend/test/test_recompiler.py b/pypy/module/_cffi_backend/test/test_recompiler.py
--- a/pypy/module/_cffi_backend/test/test_recompiler.py
+++ b/pypy/module/_cffi_backend/test/test_recompiler.py
@@ -1819,6 +1819,68 @@
assert lib.f.__get__(42) is lib.f
assert lib.f.__get__(42, int) is lib.f
+ def test_function_returns_float_complex(self):
+ import sys
+ if sys.platform == 'win32':
+ skip("MSVC may not support _Complex")
+ ffi, lib = self.prepare(
+ "float _Complex f1(float a, float b);",
+ "test_function_returns_float_complex", """
+ #include <complex.h>
+ static float _Complex f1(float a, float b) { return a + I*2.0*b; }
+ """, min_version=(1, 11, 0))
+ result = lib.f1(1.25, 5.1)
+ assert type(result) == complex
+ assert result.real == 1.25 # exact
+ assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact
+
+ def test_function_returns_double_complex(self):
+ import sys
+ if sys.platform == 'win32':
+ skip("MSVC may not support _Complex")
+ ffi, lib = self.prepare(
+ "double _Complex f1(double a, double b);",
+ "test_function_returns_double_complex", """
+ #include <complex.h>
+ static double _Complex f1(double a, double b) { return a + I*2.0*b; }
+ """, min_version=(1, 11, 0))
+ result = lib.f1(1.25, 5.1)
+ assert type(result) == complex
+ assert result.real == 1.25 # exact
+ assert result.imag == 2*5.1 # exact
+
+ def test_function_argument_float_complex(self):
+ import sys
+ if sys.platform == 'win32':
+ skip("MSVC may not support _Complex")
+ ffi, lib = self.prepare(
+ "float f1(float _Complex x);",
+ "test_function_argument_float_complex", """
+ #include <complex.h>
+ static float f1(float _Complex x) { return cabsf(x); }
+ """, min_version=(1, 11, 0))
+ x = complex(12.34, 56.78)
+ result = lib.f1(x)
+ assert abs(result - abs(x)) < 1e-5
+ result2 = lib.f1(ffi.cast("float _Complex", x))
+ assert result2 == result
+
+ def test_function_argument_double_complex(self):
+ import sys
+ if sys.platform == 'win32':
+ skip("MSVC may not support _Complex")
+ ffi, lib = self.prepare(
+ "double f1(double _Complex);",
+ "test_function_argument_double_complex", """
+ #include <complex.h>
+ static double f1(double _Complex x) { return cabs(x); }
+ """, min_version=(1, 11, 0))
+ x = complex(12.34, 56.78)
+ result = lib.f1(x)
+ assert abs(result - abs(x)) < 1e-11
+ result2 = lib.f1(ffi.cast("double _Complex", x))
+ assert result2 == result
+
def test_typedef_array_dotdotdot(self):
ffi, lib = self.prepare("""
typedef int foo_t[...], bar_t[...];
diff --git a/pypy/module/_cffi_backend/wrapper.py b/pypy/module/_cffi_backend/wrapper.py
--- a/pypy/module/_cffi_backend/wrapper.py
+++ b/pypy/module/_cffi_backend/wrapper.py
@@ -8,6 +8,7 @@
from pypy.module._cffi_backend.cdataobj import W_CData
from pypy.module._cffi_backend.cdataobj import W_CDataPtrToStructOrUnion
from pypy.module._cffi_backend.ctypeptr import W_CTypePtrOrArray
+from pypy.module._cffi_backend.ctypeptr import W_CTypePointer
from pypy.module._cffi_backend.ctypefunc import W_CTypeFunc
from pypy.module._cffi_backend.ctypestruct import W_CTypeStructOrUnion
from pypy.module._cffi_backend import allocator
@@ -83,8 +84,9 @@
#
ctype._call(self.fnptr, args_w) # returns w_None
#
- assert isinstance(w_result_cdata, W_CDataPtrToStructOrUnion)
- return w_result_cdata.structobj
+ ctyperesptr = w_result_cdata.ctype
+ assert isinstance(ctyperesptr, W_CTypePointer)
+ return w_result_cdata._do_getitem(ctyperesptr, 0)
else:
args_w = args_w[:]
prepare_args(space, rawfunctype, args_w, 0)
@@ -109,13 +111,14 @@
@jit.unroll_safe
def prepare_args(space, rawfunctype, args_w, start_index):
# replaces struct/union arguments with ptr-to-struct/union arguments
+ # as well as complex numbers
locs = rawfunctype.nostruct_locs
fargs = rawfunctype.nostruct_ctype.fargs
for i in range(start_index, len(locs)):
if locs[i] != 'A':
continue
w_arg = args_w[i]
- farg = fargs[i] # <ptr to struct/union>
+ farg = fargs[i] # <ptr to struct/union/complex>
assert isinstance(farg, W_CTypePtrOrArray)
if isinstance(w_arg, W_CData) and w_arg.ctype is farg.ctitem:
# fast way: we are given a W_CData "struct", so just make
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py
@@ -229,13 +229,27 @@
# this checks that we get a sensible error if we try "int foo(...);"
ffi = FFI()
e = py.test.raises(CDefError, ffi.cdef, "int foo(...);")
- assert str(e.value) == \
- "foo: a function with only '(...)' as argument is not correct C"
+ assert str(e.value) == (
+ "<cdef source string>:1: foo: a function with only '(...)' "
+ "as argument is not correct C")
def test_parse_error():
ffi = FFI()
e = py.test.raises(CDefError, ffi.cdef, " x y z ")
- assert re.match(r'cannot parse "x y z"\n:\d+:', str(e.value))
+ assert str(e.value).startswith(
+ 'cannot parse "x y z"\n<cdef source string>:1:')
+ e = py.test.raises(CDefError, ffi.cdef, "\n\n\n x y z ")
+ assert str(e.value).startswith(
+ 'cannot parse "x y z"\n<cdef source string>:4:')
+
+def test_error_custom_lineno():
+ ffi = FFI()
+ e = py.test.raises(CDefError, ffi.cdef, """
+# 42 "foobar"
+
+ a b c d
+ """)
+ assert str(e.value).startswith('parse error\nfoobar:43:')
def test_cannot_declare_enum_later():
ffi = FFI()
@@ -279,7 +293,8 @@
def test_unknown_argument_type():
ffi = FFI()
e = py.test.raises(CDefError, ffi.cdef, "void f(foobarbazzz);")
- assert str(e.value) == ("f arg 1: unknown type 'foobarbazzz' (if you meant"
+ assert str(e.value) == ("<cdef source string>:1: f arg 1:"
+ " unknown type 'foobarbazzz' (if you meant"
" to use the old C syntax of giving untyped"
" arguments, it is not supported)")
@@ -437,3 +452,9 @@
ffi._parser._declarations['extern_python foobar'] !=
ffi._parser._declarations['function bok'] ==
ffi._parser._declarations['extern_python bzrrr'])
+
+def test_error_invalid_syntax_for_cdef():
+ ffi = FFI()
+ e = py.test.raises(CDefError, ffi.cdef, 'void foo(void) {}')
+ assert str(e.value) == ('<cdef source string>:1: unexpected <FuncDef>: '
+ 'this construct is valid C but not valid in cdef()')
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py
@@ -240,15 +240,18 @@
tp = model.PrimitiveType(typename)
C = tp.is_char_type()
F = tp.is_float_type()
+ X = tp.is_complex_type()
I = tp.is_integer_type()
assert C == (typename in ('char', 'wchar_t'))
assert F == (typename in ('float', 'double', 'long double'))
- assert I + F + C == 1 # one and only one of them is true
+ assert X == (typename in ('float _Complex', 'double _Complex'))
+ assert I + F + C + X == 1 # one and only one of them is true
def test_all_integer_and_float_types():
typenames = []
for typename in all_primitive_types:
if (all_primitive_types[typename] == 'c' or
+ all_primitive_types[typename] == 'j' or # complex
typename == '_Bool' or typename == 'long double'):
pass
else:
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_new_ffi_1.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_new_ffi_1.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_new_ffi_1.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_new_ffi_1.py
@@ -1705,6 +1705,8 @@
"ptrdiff_t",
"size_t",
"ssize_t",
+ 'float _Complex',
+ 'double _Complex',
])
for name in PRIMITIVE_TO_INDEX:
x = ffi.sizeof(name)
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_parse_c_type.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_parse_c_type.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_parse_c_type.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_parse_c_type.py
@@ -156,6 +156,8 @@
("long int", lib._CFFI_PRIM_LONG),
("unsigned short", lib._CFFI_PRIM_USHORT),
("long double", lib._CFFI_PRIM_LONGDOUBLE),
+ (" float _Complex", lib._CFFI_PRIM_FLOATCOMPLEX),
+ ("double _Complex ", lib._CFFI_PRIM_DOUBLECOMPLEX),
]:
assert parse(simple_type) == ['->', Prim(expected)]
@@ -281,6 +283,11 @@
parse_error("int[5](*)", "unexpected symbol", 6)
parse_error("int a(*)", "identifier expected", 6)
parse_error("int[123456789012345678901234567890]", "number too large", 4)
+ #
+ parse_error("_Complex", "identifier expected", 0)
+ parse_error("int _Complex", "_Complex type combination unsupported", 4)
+ parse_error("long double _Complex", "_Complex type combination unsupported",
+ 12)
def test_number_too_large():
num_max = sys.maxsize
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_realize_c_type.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_realize_c_type.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_realize_c_type.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_realize_c_type.py
@@ -48,7 +48,6 @@
for name in cffi_opcode.PRIMITIVE_TO_INDEX:
check(name, name)
-
def check_func(input, expected_output=None):
import _cffi_backend
ffi = _cffi_backend.FFI()
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
@@ -1553,7 +1553,8 @@
res = lib.bar(4, 5)
assert res == 0
assert f.getvalue() == (
- b"extern \"Python\": function bar() called, but no code was attached "
+ b"extern \"Python\": function _CFFI_test_extern_python_1.bar() called, "
+ b"but no code was attached "
b"to it yet with @ffi.def_extern(). Returning 0.\n")
@ffi.def_extern("bar")
@@ -2001,6 +2002,60 @@
""")
assert lib.f1(52).a == 52
+def test_function_returns_float_complex():
+ if sys.platform == 'win32':
+ py.test.skip("MSVC may not support _Complex")
+ ffi = FFI()
+ ffi.cdef("float _Complex f1(float a, float b);");
+ lib = verify(ffi, "test_function_returns_float_complex", """
+ #include <complex.h>
+ static float _Complex f1(float a, float b) { return a + I*2.0*b; }
+ """)
+ result = lib.f1(1.25, 5.1)
+ assert type(result) == complex
+ assert result.real == 1.25 # exact
+ assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact
+
+def test_function_returns_double_complex():
+ if sys.platform == 'win32':
+ py.test.skip("MSVC may not support _Complex")
+ ffi = FFI()
+ ffi.cdef("double _Complex f1(double a, double b);");
+ lib = verify(ffi, "test_function_returns_double_complex", """
+ #include <complex.h>
+ static double _Complex f1(double a, double b) { return a + I*2.0*b; }
+ """)
+ result = lib.f1(1.25, 5.1)
+ assert type(result) == complex
+ assert result.real == 1.25 # exact
+ assert result.imag == 2*5.1 # exact
+
+def test_function_argument_float_complex():
+ if sys.platform == 'win32':
+ py.test.skip("MSVC may not support _Complex")
+ ffi = FFI()
+ ffi.cdef("float f1(float _Complex x);");
+ lib = verify(ffi, "test_function_argument_float_complex", """
+ #include <complex.h>
+ static float f1(float _Complex x) { return cabsf(x); }
+ """)
+ x = complex(12.34, 56.78)
+ result = lib.f1(x)
+ assert abs(result - abs(x)) < 1e-5
+
+def test_function_argument_double_complex():
+ if sys.platform == 'win32':
+ py.test.skip("MSVC may not support _Complex")
+ ffi = FFI()
+ ffi.cdef("double f1(double _Complex);");
+ lib = verify(ffi, "test_function_argument_double_complex", """
+ #include <complex.h>
+ static double f1(double _Complex x) { return cabs(x); }
+ """)
+ x = complex(12.34, 56.78)
+ result = lib.f1(x)
+ assert abs(result - abs(x)) < 1e-11
+
def test_typedef_array_dotdotdot():
ffi = FFI()
ffi.cdef("""
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py
@@ -220,15 +220,18 @@
tp = model.PrimitiveType(typename)
C = tp.is_char_type()
F = tp.is_float_type()
+ X = tp.is_complex_type()
I = tp.is_integer_type()
assert C == (typename in ('char', 'wchar_t'))
assert F == (typename in ('float', 'double', 'long double'))
- assert I + F + C == 1 # one and only one of them is true
+ assert X == (typename in ('float _Complex', 'double _Complex'))
+ assert I + F + C + X == 1 # one and only one of them is true
def test_all_integer_and_float_types():
typenames = []
for typename in all_primitive_types:
if (all_primitive_types[typename] == 'c' or
+ all_primitive_types[typename] == 'j' or # complex
typename == '_Bool' or typename == 'long double'):
pass
else:
More information about the pypy-commit
mailing list