[pypy-commit] pypy cffi-complex: Implement complexes, enough to make the test_c part pass
arigo
pypy.commits at gmail.com
Wed May 31 12:16:07 EDT 2017
Author: Armin Rigo <arigo at tunes.org>
Branch: cffi-complex
Changeset: r91466:593f66be444b
Date: 2017-05-31 18:15 +0200
http://bitbucket.org/pypy/pypy/changeset/593f66be444b/
Log: Implement complexes, enough to make the test_c part pass
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/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/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
@@ -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")
More information about the pypy-commit
mailing list