[pypy-commit] pypy unrecursive-opt: Merge default.
jerith
noreply at buildbot.pypy.org
Tue Oct 13 10:45:36 CEST 2015
Author: Jeremy Thurgood <firxen at gmail.com>
Branch: unrecursive-opt
Changeset: r80154:c3e4bdc5e479
Date: 2015-10-13 10:45 +0200
http://bitbucket.org/pypy/pypy/changeset/c3e4bdc5e479/
Log: Merge default.
diff too long, truncating to 2000 out of 8776 lines
diff --git a/dotviewer/graphclient.py b/dotviewer/graphclient.py
--- a/dotviewer/graphclient.py
+++ b/dotviewer/graphclient.py
@@ -127,16 +127,8 @@
return spawn_graphserver_handler((host, port))
def spawn_local_handler():
- if hasattr(sys, 'pypy_objspaceclass'):
- # if 'python' is actually PyPy, e.g. in a virtualenv, then
- # try hard to find a real CPython
- try:
- python = subprocess.check_output(
- 'env -i $SHELL -l -c "which python"', shell=True).strip()
- except subprocess.CalledProcessError:
- # did not work, fall back to 'python'
- python = 'python'
- else:
+ python = os.getenv('PYPY_PYGAME_PYTHON')
+ if not python:
python = sys.executable
args = [python, '-u', GRAPHSERVER, '--stdio']
p = subprocess.Popen(args,
diff --git a/lib-python/conftest.py b/lib-python/conftest.py
--- a/lib-python/conftest.py
+++ b/lib-python/conftest.py
@@ -158,7 +158,7 @@
RegrTest('test_codecs.py', core=True, usemodules='_multibytecodec'),
RegrTest('test_codeop.py', core=True),
RegrTest('test_coding.py', core=True),
- RegrTest('test_coercion.py', core=True),
+ RegrTest('test_coercion.py', core=True, usemodules='struct'),
RegrTest('test_collections.py', usemodules='binascii struct'),
RegrTest('test_colorsys.py'),
RegrTest('test_commands.py'),
diff --git a/lib_pypy/cffi/api.py b/lib_pypy/cffi/api.py
--- a/lib_pypy/cffi/api.py
+++ b/lib_pypy/cffi/api.py
@@ -310,6 +310,22 @@
"""
return self._backend.from_buffer(self.BCharA, python_buffer)
+ def memmove(self, dest, src, n):
+ """ffi.memmove(dest, src, n) copies n bytes of memory from src to dest.
+
+ Like the C function memmove(), the memory areas may overlap;
+ apart from that it behaves like the C function memcpy().
+
+ 'src' can be any cdata ptr or array, or any Python buffer object.
+ 'dest' can be any cdata ptr or array, or a writable Python buffer
+ object. The size to copy, 'n', is always measured in bytes.
+
+ Unlike other methods, this one supports all Python buffer including
+ byte strings and bytearrays---but it still does not support
+ non-contiguous buffers.
+ """
+ return self._backend.memmove(dest, src, n)
+
def callback(self, cdecl, python_callable=None, error=None, onerror=None):
"""Return a callback object or a decorator making such a
callback object. 'cdecl' must name a C function pointer type.
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
@@ -26,6 +26,9 @@
_r_words = re.compile(r"\w+|\S")
_parser_cache = None
_r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE)
+_r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b")
+_r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b")
+_r_cdecl = re.compile(r"\b__cdecl\b")
def _get_parser():
global _parser_cache
@@ -44,6 +47,14 @@
macrovalue = macrovalue.replace('\\\n', '').strip()
macros[macroname] = macrovalue
csource = _r_define.sub('', csource)
+ # BIG HACK: replace WINAPI or __stdcall with "volatile const".
+ # It doesn't make sense for the return type of a function to be
+ # "volatile volatile const", so we abuse it to detect __stdcall...
+ # Hack number 2 is that "int(volatile *fptr)();" is not valid C
+ # syntax, so we place the "volatile" before the opening parenthesis.
+ csource = _r_stdcall2.sub(' volatile volatile const(', csource)
+ csource = _r_stdcall1.sub(' volatile volatile const ', csource)
+ csource = _r_cdecl.sub(' ', csource)
# Replace "[...]" with "[__dotdotdotarray__]"
csource = _r_partial_array.sub('[__dotdotdotarray__]', csource)
# Replace "...}" with "__dotdotdotNUM__}". This construction should
@@ -449,7 +460,14 @@
if not ellipsis and args == [model.void_type]:
args = []
result, quals = self._get_type_and_quals(typenode.type)
- return model.RawFunctionType(tuple(args), result, ellipsis)
+ # the 'quals' on the result type are ignored. HACK: we absure them
+ # to detect __stdcall functions: we textually replace "__stdcall"
+ # with "volatile volatile const" above.
+ abi = None
+ if hasattr(typenode.type, 'quals'): # else, probable syntax error anyway
+ if typenode.type.quals[-3:] == ['volatile', 'volatile', 'const']:
+ abi = '__stdcall'
+ return model.RawFunctionType(tuple(args), result, ellipsis, abi)
def _as_func_arg(self, type, quals):
if isinstance(type, model.ArrayType):
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
@@ -1,4 +1,4 @@
-import types
+import types, sys
import weakref
from .lock import allocate_lock
@@ -193,18 +193,21 @@
class BaseFunctionType(BaseType):
- _attrs_ = ('args', 'result', 'ellipsis')
+ _attrs_ = ('args', 'result', 'ellipsis', 'abi')
- def __init__(self, args, result, ellipsis):
+ def __init__(self, args, result, ellipsis, abi=None):
self.args = args
self.result = result
self.ellipsis = ellipsis
+ self.abi = abi
#
reprargs = [arg._get_c_name() for arg in self.args]
if self.ellipsis:
reprargs.append('...')
reprargs = reprargs or ['void']
replace_with = self._base_pattern % (', '.join(reprargs),)
+ if abi is not None:
+ replace_with = replace_with[:1] + abi + ' ' + replace_with[1:]
self.c_name_with_marker = (
self.result.c_name_with_marker.replace('&', replace_with))
@@ -222,7 +225,7 @@
"type, not a pointer-to-function type" % (self,))
def as_function_pointer(self):
- return FunctionPtrType(self.args, self.result, self.ellipsis)
+ return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi)
class FunctionPtrType(BaseFunctionType):
@@ -233,11 +236,18 @@
args = []
for tp in self.args:
args.append(tp.get_cached_btype(ffi, finishlist))
+ abi_args = ()
+ if self.abi == "__stdcall":
+ if not self.ellipsis: # __stdcall ignored for variadic funcs
+ try:
+ abi_args = (ffi._backend.FFI_STDCALL,)
+ except AttributeError:
+ pass
return global_cache(self, ffi, 'new_function_type',
- tuple(args), result, self.ellipsis)
+ tuple(args), result, self.ellipsis, *abi_args)
def as_raw_function(self):
- return RawFunctionType(self.args, self.result, self.ellipsis)
+ return RawFunctionType(self.args, self.result, self.ellipsis, self.abi)
class PointerType(BaseType):
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
@@ -5,7 +5,7 @@
#define _CFFI_OP(opcode, arg) (_cffi_opcode_t)(opcode | (((uintptr_t)(arg)) << 8))
#define _CFFI_GETOP(cffi_opcode) ((unsigned char)(uintptr_t)cffi_opcode)
-#define _CFFI_GETARG(cffi_opcode) (((uintptr_t)cffi_opcode) >> 8)
+#define _CFFI_GETARG(cffi_opcode) (((intptr_t)cffi_opcode) >> 8)
#define _CFFI_OP_PRIMITIVE 1
#define _CFFI_OP_POINTER 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
@@ -607,7 +607,11 @@
call_arguments.append('x%d' % i)
repr_arguments = ', '.join(arguments)
repr_arguments = repr_arguments or 'void'
- name_and_arguments = '_cffi_d_%s(%s)' % (name, repr_arguments)
+ if tp.abi:
+ abi = tp.abi + ' '
+ else:
+ abi = ''
+ name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments)
prnt('static %s' % (tp.result.get_c_name(name_and_arguments),))
prnt('{')
call_arguments = ', '.join(call_arguments)
@@ -710,7 +714,8 @@
if difference:
repr_arguments = ', '.join(arguments)
repr_arguments = repr_arguments or 'void'
- name_and_arguments = '_cffi_f_%s(%s)' % (name, repr_arguments)
+ name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name,
+ repr_arguments)
prnt('static %s' % (tp_result.get_c_name(name_and_arguments),))
prnt('{')
if result_decl:
@@ -1135,7 +1140,13 @@
else:
self.cffi_types[index] = CffiOp(OP_NOOP, realindex)
index += 1
- self.cffi_types[index] = CffiOp(OP_FUNCTION_END, int(tp.ellipsis))
+ flags = int(tp.ellipsis)
+ if tp.abi is not None:
+ if tp.abi == '__stdcall':
+ flags |= 2
+ else:
+ raise NotImplementedError("abi=%r" % (tp.abi,))
+ self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags)
def _emit_bytecode_PointerType(self, tp, index):
self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype])
diff --git a/lib_pypy/cffi/vengine_gen.py b/lib_pypy/cffi/vengine_gen.py
--- a/lib_pypy/cffi/vengine_gen.py
+++ b/lib_pypy/cffi/vengine_gen.py
@@ -159,7 +159,11 @@
arglist = ', '.join(arglist) or 'void'
wrappername = '_cffi_f_%s' % name
self.export_symbols.append(wrappername)
- funcdecl = ' %s(%s)' % (wrappername, arglist)
+ if tp.abi:
+ abi = tp.abi + ' '
+ else:
+ abi = ''
+ funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist)
context = 'result of %s' % name
prnt(tpresult.get_c_name(funcdecl, context))
prnt('{')
diff --git a/pypy/doc/extending.rst b/pypy/doc/extending.rst
--- a/pypy/doc/extending.rst
+++ b/pypy/doc/extending.rst
@@ -83,7 +83,7 @@
RPython Mixed Modules
-=====================
+---------------------
This is the internal way to write built-in extension modules in PyPy.
It cannot be used by any 3rd-party module: the extension modules are
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
@@ -43,3 +43,34 @@
.. branch: numpy-ctypes
Add support for ndarray.ctypes property.
+
+.. branch: share-guard-info
+
+Share guard resume data between consecutive guards that have only
+pure operations and guards in between.
+
+.. branch: issue-2148
+
+Fix performance regression on operations mixing numpy scalars and Python
+floats, cf. issue #2148.
+
+.. branch: cffi-stdcall
+Win32: support '__stdcall' in CFFI.
+
+.. branch: callfamily
+
+Refactorings of annotation and rtyping of function calls.
+
+.. branch: fortran-order
+
+Allow creation of fortran-ordered ndarrays
+
+.. branch: type_system-cleanup
+
+Remove some remnants of the old ootypesystem vs lltypesystem dichotomy.
+
+.. branch: cffi-handle-lifetime
+
+ffi.new_handle() returns handles that work more like CPython's: they
+remain valid as long as the target exists (unlike the previous
+version, where handles become invalid *before* the __del__ is called).
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -1058,6 +1058,14 @@
args = Arguments.frompacked(self, w_args, w_kwds)
return self.call_args(w_callable, args)
+ def _try_fetch_pycode(self, w_func):
+ from pypy.interpreter.function import Function, Method
+ if isinstance(w_func, Method):
+ w_func = w_func.w_function
+ if isinstance(w_func, Function):
+ return w_func.code
+ return None
+
def call_function(self, w_func, *args_w):
nargs = len(args_w) # used for pruning funccall versions
if not self.config.objspace.disable_call_speedhacks and nargs < 5:
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
@@ -1,9 +1,16 @@
import sys
from pypy.interpreter.mixedmodule import MixedModule
-from rpython.rlib import rdynload
+from rpython.rlib import rdynload, clibffi
VERSION = "1.3.0"
+FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI
+try:
+ FFI_STDCALL = clibffi.FFI_STDCALL
+ has_stdcall = True
+except AttributeError:
+ has_stdcall = False
+
class Module(MixedModule):
@@ -40,12 +47,13 @@
'string': 'func.string',
'buffer': 'cbuffer.buffer',
+ 'memmove': 'func.memmove',
'get_errno': 'cerrno.get_errno',
'set_errno': 'cerrno.set_errno',
- 'FFI_DEFAULT_ABI': 'ctypefunc._get_abi(space, "FFI_DEFAULT_ABI")',
- 'FFI_CDECL': 'ctypefunc._get_abi(space,"FFI_DEFAULT_ABI")',#win32 name
+ 'FFI_DEFAULT_ABI': 'space.wrap(%d)' % FFI_DEFAULT_ABI,
+ 'FFI_CDECL': 'space.wrap(%d)' % FFI_DEFAULT_ABI, # win32 name
# CFFI 1.0
'FFI': 'ffi_obj.W_FFIObject',
@@ -53,6 +61,9 @@
if sys.platform == 'win32':
interpleveldefs['getwinerror'] = 'cerrno.getwinerror'
+ if has_stdcall:
+ interpleveldefs['FFI_STDCALL'] = 'space.wrap(%d)' % FFI_STDCALL
+
def get_dict_rtld_constants():
found = {}
diff --git a/pypy/module/_cffi_backend/ccallback.py b/pypy/module/_cffi_backend/ccallback.py
--- a/pypy/module/_cffi_backend/ccallback.py
+++ b/pypy/module/_cffi_backend/ccallback.py
@@ -1,11 +1,11 @@
"""
Callbacks.
"""
-import sys, os
+import sys, os, py
-from rpython.rlib import clibffi, rweakref, jit, jit_libffi
-from rpython.rlib.objectmodel import compute_unique_id, keepalive_until_here
-from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rlib import clibffi, jit, jit_libffi, rgc, objectmodel
+from rpython.rlib.objectmodel import keepalive_until_here
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
from pypy.interpreter.error import OperationError, oefmt
from pypy.module._cffi_backend import cerrno, misc
@@ -19,6 +19,23 @@
# ____________________________________________________________
+ at jit.dont_look_inside
+def make_callback(space, ctype, w_callable, w_error, w_onerror):
+ # Allocate a callback as a nonmovable W_CDataCallback instance, which
+ # we can cast to a plain VOIDP. As long as the object is not freed,
+ # we can cast the VOIDP back to a W_CDataCallback in reveal_callback().
+ cdata = objectmodel.instantiate(W_CDataCallback, nonmovable=True)
+ gcref = rgc.cast_instance_to_gcref(cdata)
+ raw_cdata = rgc.hide_nonmovable_gcref(gcref)
+ cdata.__init__(space, ctype, w_callable, w_error, w_onerror, raw_cdata)
+ return cdata
+
+def reveal_callback(raw_ptr):
+ addr = rffi.cast(llmemory.Address, raw_ptr)
+ gcref = rgc.reveal_gcref(addr)
+ return rgc.try_cast_gcref_to_instance(W_CDataCallback, gcref)
+
+
class Closure(object):
"""This small class is here to have a __del__ outside any cycle."""
@@ -34,10 +51,11 @@
class W_CDataCallback(W_CData):
- #_immutable_fields_ = ...
+ _immutable_fields_ = ['key_pycode']
w_onerror = None
- def __init__(self, space, ctype, w_callable, w_error, w_onerror):
+ def __init__(self, space, ctype, w_callable, w_error, w_onerror,
+ raw_cdata):
raw_closure = rffi.cast(rffi.CCHARP, clibffi.closureHeap.alloc())
self._closure = Closure(raw_closure)
W_CData.__init__(self, space, raw_closure, ctype)
@@ -46,6 +64,7 @@
raise oefmt(space.w_TypeError,
"expected a callable object, not %T", w_callable)
self.w_callable = w_callable
+ self.key_pycode = space._try_fetch_pycode(w_callable)
if not space.is_none(w_onerror):
if not space.is_true(space.callable(w_onerror)):
raise oefmt(space.w_TypeError,
@@ -64,8 +83,12 @@
convert_from_object_fficallback(fresult, self._closure.ll_error,
w_error)
#
- self.unique_id = compute_unique_id(self)
- global_callback_mapping.set(self.unique_id, self)
+ # We must setup the GIL here, in case the callback is invoked in
+ # some other non-Pythonic thread. This is the same as cffi on
+ # CPython.
+ if space.config.translation.thread:
+ from pypy.module.thread.os_thread import setup_threads
+ setup_threads(space)
#
cif_descr = self.getfunctype().cif_descr
if not cif_descr:
@@ -74,20 +97,13 @@
"return type or with '...'", self.getfunctype().name)
with self as ptr:
closure_ptr = rffi.cast(clibffi.FFI_CLOSUREP, ptr)
- unique_id = rffi.cast(rffi.VOIDP, self.unique_id)
+ unique_id = rffi.cast(rffi.VOIDP, raw_cdata)
res = clibffi.c_ffi_prep_closure(closure_ptr, cif_descr.cif,
invoke_callback,
unique_id)
if rffi.cast(lltype.Signed, res) != clibffi.FFI_OK:
raise OperationError(space.w_SystemError,
space.wrap("libffi failed to build this callback"))
- #
- # We must setup the GIL here, in case the callback is invoked in
- # some other non-Pythonic thread. This is the same as cffi on
- # CPython.
- if space.config.translation.thread:
- from pypy.module.thread.os_thread import setup_threads
- setup_threads(space)
def _repr_extra(self):
space = self.space
@@ -105,6 +121,7 @@
def invoke(self, ll_args):
space = self.space
ctype = self.getfunctype()
+ ctype = jit.promote(ctype)
args_w = []
for i, farg in enumerate(ctype.fargs):
ll_arg = rffi.cast(rffi.CCHARP, ll_args[i])
@@ -127,9 +144,6 @@
keepalive_until_here(self) # to keep self._closure.ll_error alive
-global_callback_mapping = rweakref.RWeakValueDictionary(int, W_CDataCallback)
-
-
def convert_from_object_fficallback(fresult, ll_res, w_res):
space = fresult.space
small_result = fresult.size < SIZE_OF_FFI_ARG
@@ -178,7 +192,8 @@
@jit.dont_look_inside
-def _handle_applevel_exception(space, callback, e, ll_res, extra_line):
+def _handle_applevel_exception(callback, e, ll_res, extra_line):
+ space = callback.space
callback.write_error_return_value(ll_res)
if callback.w_onerror is None:
callback.print_error(e, extra_line)
@@ -199,19 +214,36 @@
extra_line="\nDuring the call to 'onerror', "
"another exception occurred:\n\n")
+def get_printable_location(key_pycode):
+ if key_pycode is None:
+ return 'cffi_callback <?>'
+ return 'cffi_callback ' + key_pycode.get_repr()
- at jit.jit_callback("CFFI")
+jitdriver = jit.JitDriver(name='cffi_callback',
+ greens=['callback.key_pycode'],
+ reds=['ll_res', 'll_args', 'callback'],
+ get_printable_location=get_printable_location)
+
+def py_invoke_callback(callback, ll_res, ll_args):
+ jitdriver.jit_merge_point(callback=callback, ll_res=ll_res, ll_args=ll_args)
+ extra_line = ''
+ try:
+ w_res = callback.invoke(ll_args)
+ extra_line = "Trying to convert the result back to C:\n"
+ callback.convert_result(ll_res, w_res)
+ except OperationError, e:
+ _handle_applevel_exception(callback, e, ll_res, extra_line)
+
def _invoke_callback(ffi_cif, ll_res, ll_args, ll_userdata):
""" Callback specification.
ffi_cif - something ffi specific, don't care
ll_args - rffi.VOIDPP - pointer to array of pointers to args
- ll_restype - rffi.VOIDP - pointer to result
+ ll_res - rffi.VOIDP - pointer to result
ll_userdata - a special structure which holds necessary information
(what the real callback is for example), casted to VOIDP
"""
ll_res = rffi.cast(rffi.CCHARP, ll_res)
- unique_id = rffi.cast(lltype.Signed, ll_userdata)
- callback = global_callback_mapping.get(unique_id)
+ callback = reveal_callback(ll_userdata)
if callback is None:
# oups!
try:
@@ -224,17 +256,11 @@
misc._raw_memclear(ll_res, SIZE_OF_FFI_ARG)
return
#
+ space = callback.space
must_leave = False
- space = callback.space
try:
must_leave = space.threadlocals.try_enter_thread(space)
- extra_line = ''
- try:
- w_res = callback.invoke(ll_args)
- extra_line = "Trying to convert the result back to C:\n"
- callback.convert_result(ll_res, w_res)
- except OperationError, e:
- _handle_applevel_exception(space, callback, e, ll_res, extra_line)
+ py_invoke_callback(callback, ll_res, ll_args)
#
except Exception, e:
# oups! last-level attempt to recover.
diff --git a/pypy/module/_cffi_backend/ctypearray.py b/pypy/module/_cffi_backend/ctypearray.py
--- a/pypy/module/_cffi_backend/ctypearray.py
+++ b/pypy/module/_cffi_backend/ctypearray.py
@@ -18,6 +18,7 @@
_attrs_ = ['ctptr']
_immutable_fields_ = ['ctptr']
kind = "array"
+ is_nonfunc_pointer_or_array = True
def __init__(self, space, ctptr, length, arraysize, extra):
W_CTypePtrOrArray.__init__(self, space, arraysize, extra, 0,
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
@@ -12,6 +12,7 @@
from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
from pypy.interpreter.error import OperationError, oefmt
+from pypy.module import _cffi_backend
from pypy.module._cffi_backend import ctypearray, cdataobj, cerrno
from pypy.module._cffi_backend.ctypeobj import W_CType
from pypy.module._cffi_backend.ctypeptr import W_CTypePtrBase, W_CTypePointer
@@ -23,20 +24,22 @@
class W_CTypeFunc(W_CTypePtrBase):
- _attrs_ = ['fargs', 'ellipsis', 'cif_descr']
- _immutable_fields_ = ['fargs[*]', 'ellipsis', 'cif_descr']
+ _attrs_ = ['fargs', 'ellipsis', 'abi', 'cif_descr']
+ _immutable_fields_ = ['fargs[*]', 'ellipsis', 'abi', 'cif_descr']
kind = "function"
cif_descr = lltype.nullptr(CIF_DESCRIPTION)
- def __init__(self, space, fargs, fresult, ellipsis):
+ def __init__(self, space, fargs, fresult, ellipsis,
+ abi=_cffi_backend.FFI_DEFAULT_ABI):
assert isinstance(ellipsis, bool)
- extra = self._compute_extra_text(fargs, fresult, ellipsis)
+ extra, xpos = self._compute_extra_text(fargs, fresult, ellipsis, abi)
size = rffi.sizeof(rffi.VOIDP)
- W_CTypePtrBase.__init__(self, space, size, extra, 2, fresult,
+ W_CTypePtrBase.__init__(self, space, size, extra, xpos, fresult,
could_cast_anything=False)
self.fargs = fargs
self.ellipsis = ellipsis
+ self.abi = abi
# fresult is stored in self.ctitem
if not ellipsis:
@@ -44,7 +47,7 @@
# at all. The cif is computed on every call from the actual
# types passed in. For all other functions, the cif_descr
# is computed here.
- builder = CifDescrBuilder(fargs, fresult)
+ builder = CifDescrBuilder(fargs, fresult, abi)
try:
builder.rawallocate(self)
except OperationError, e:
@@ -76,7 +79,7 @@
ctypefunc.fargs = fvarargs
ctypefunc.ctitem = self.ctitem
#ctypefunc.cif_descr = NULL --- already provided as the default
- CifDescrBuilder(fvarargs, self.ctitem).rawallocate(ctypefunc)
+ CifDescrBuilder(fvarargs, self.ctitem, self.abi).rawallocate(ctypefunc)
return ctypefunc
@rgc.must_be_light_finalizer
@@ -84,8 +87,13 @@
if self.cif_descr:
lltype.free(self.cif_descr, flavor='raw')
- def _compute_extra_text(self, fargs, fresult, ellipsis):
+ def _compute_extra_text(self, fargs, fresult, ellipsis, abi):
+ from pypy.module._cffi_backend import newtype
argnames = ['(*)(']
+ xpos = 2
+ if _cffi_backend.has_stdcall and abi == _cffi_backend.FFI_STDCALL:
+ argnames[0] = '(__stdcall *)('
+ xpos += len('__stdcall ')
for i, farg in enumerate(fargs):
if i > 0:
argnames.append(', ')
@@ -95,7 +103,7 @@
argnames.append(', ')
argnames.append('...')
argnames.append(')')
- return ''.join(argnames)
+ return ''.join(argnames), xpos
def _fget(self, attrchar):
if attrchar == 'a': # args
@@ -106,7 +114,7 @@
if attrchar == 'E': # ellipsis
return self.space.wrap(self.ellipsis)
if attrchar == 'A': # abi
- return self.space.wrap(clibffi.FFI_DEFAULT_ABI) # XXX
+ return self.space.wrap(self.abi)
return W_CTypePtrBase._fget(self, attrchar)
def call(self, funcaddr, args_w):
@@ -181,11 +189,6 @@
def set_mustfree_flag(data, flag):
rffi.ptradd(data, -1)[0] = chr(flag)
-def _get_abi(space, name):
- abi = getattr(clibffi, name)
- assert isinstance(abi, int)
- return space.wrap(abi)
-
# ____________________________________________________________
@@ -260,9 +263,10 @@
class CifDescrBuilder(object):
rawmem = lltype.nullptr(rffi.CCHARP.TO)
- def __init__(self, fargs, fresult):
+ def __init__(self, fargs, fresult, fabi):
self.fargs = fargs
self.fresult = fresult
+ self.fabi = fabi
def fb_alloc(self, size):
size = llmemory.raw_malloc_usage(size)
@@ -421,7 +425,7 @@
cif_descr.exchange_size = exchange_offset
def fb_extra_fields(self, cif_descr):
- cif_descr.abi = clibffi.FFI_DEFAULT_ABI # XXX
+ cif_descr.abi = self.fabi
cif_descr.nargs = len(self.fargs)
cif_descr.rtype = self.rtype
cif_descr.atypes = self.atypes
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
@@ -21,6 +21,7 @@
cast_anything = False
is_primitive_integer = False
+ is_nonfunc_pointer_or_array = False
kind = "?"
def __init__(self, space, size, name, name_position):
@@ -143,7 +144,7 @@
# obscure hack when untranslated, maybe, approximate, don't use
if isinstance(align, llmemory.FieldOffset):
align = rffi.sizeof(align.TYPE.y)
- if (1 << (8*align-2)) > sys.maxint:
+ if sys.platform != 'win32' and (1 << (8*align-2)) > sys.maxint:
align /= 2
else:
# a different hack when translated, to avoid seeing constants
diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py
--- a/pypy/module/_cffi_backend/ctypeptr.py
+++ b/pypy/module/_cffi_backend/ctypeptr.py
@@ -172,6 +172,7 @@
_immutable_fields_ = ['is_file', 'cache_array_type?', 'is_void_ptr']
kind = "pointer"
cache_array_type = None
+ is_nonfunc_pointer_or_array = True
def __init__(self, space, ctitem):
from pypy.module._cffi_backend import ctypearray
diff --git a/pypy/module/_cffi_backend/ffi_obj.py b/pypy/module/_cffi_backend/ffi_obj.py
--- a/pypy/module/_cffi_backend/ffi_obj.py
+++ b/pypy/module/_cffi_backend/ffi_obj.py
@@ -294,9 +294,9 @@
CONSIDER_FN_AS_FNPTR)
space = self.space
if not space.is_none(w_python_callable):
- return ccallback.W_CDataCallback(space, w_ctype,
- w_python_callable, w_error,
- w_onerror)
+ return ccallback.make_callback(space, w_ctype,
+ w_python_callable, w_error,
+ w_onerror)
else:
# decorator mode: returns a single-argument function
return space.appexec([w_ctype, w_error, w_onerror],
@@ -391,6 +391,25 @@
return cerrno.getwinerror(self.space, code)
+ @unwrap_spec(n=int)
+ def descr_memmove(self, w_dest, w_src, n):
+ """\
+ffi.memmove(dest, src, n) copies n bytes of memory from src to dest.
+
+Like the C function memmove(), the memory areas may overlap;
+apart from that it behaves like the C function memcpy().
+
+'src' can be any cdata ptr or array, or any Python buffer object.
+'dest' can be any cdata ptr or array, or a writable Python buffer
+object. The size to copy, 'n', is always measured in bytes.
+
+Unlike other methods, this one supports all Python buffer including
+byte strings and bytearrays---but it still does not support
+non-contiguous buffers."""
+ #
+ return func.memmove(self.space, w_dest, w_src, n)
+
+
@unwrap_spec(w_init=WrappedDefault(None))
def descr_new(self, w_arg, w_init):
"""\
@@ -623,6 +642,7 @@
gc = interp2app(W_FFIObject.descr_gc),
getctype = interp2app(W_FFIObject.descr_getctype),
integer_const = interp2app(W_FFIObject.descr_integer_const),
+ memmove = interp2app(W_FFIObject.descr_memmove),
new = interp2app(W_FFIObject.descr_new),
new_allocator = interp2app(W_FFIObject.descr_new_allocator),
new_handle = interp2app(W_FFIObject.descr_new_handle),
diff --git a/pypy/module/_cffi_backend/func.py b/pypy/module/_cffi_backend/func.py
--- a/pypy/module/_cffi_backend/func.py
+++ b/pypy/module/_cffi_backend/func.py
@@ -1,3 +1,8 @@
+from rpython.rtyper.annlowlevel import llstr
+from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rtyper.lltypesystem.rstr import copy_string_to_raw
+from rpython.rlib.objectmodel import keepalive_until_here
+
from pypy.interpreter.error import OperationError, oefmt
from pypy.interpreter.gateway import unwrap_spec, WrappedDefault
from pypy.module._cffi_backend import ctypeobj, cdataobj, allocator
@@ -19,8 +24,8 @@
@unwrap_spec(w_ctype=ctypeobj.W_CType)
def callback(space, w_ctype, w_callable, w_error=None, w_onerror=None):
- from pypy.module._cffi_backend.ccallback import W_CDataCallback
- return W_CDataCallback(space, w_ctype, w_callable, w_error, w_onerror)
+ from pypy.module._cffi_backend.ccallback import make_callback
+ return make_callback(space, w_ctype, w_callable, w_error, w_onerror)
# ____________________________________________________________
@@ -79,6 +84,26 @@
# ____________________________________________________________
+def _fetch_as_read_buffer(space, w_x):
+ # xxx do we really need to implement the same mess as in CPython 2.7
+ # w.r.t. buffers and memoryviews??
+ try:
+ buf = space.readbuf_w(w_x)
+ except OperationError, e:
+ if not e.match(space, space.w_TypeError):
+ raise
+ buf = space.buffer_w(w_x, space.BUF_SIMPLE)
+ return buf
+
+def _fetch_as_write_buffer(space, w_x):
+ try:
+ buf = space.writebuf_w(w_x)
+ except OperationError, e:
+ if not e.match(space, space.w_TypeError):
+ raise
+ buf = space.buffer_w(w_x, space.BUF_WRITABLE)
+ return buf
+
@unwrap_spec(w_ctype=ctypeobj.W_CType)
def from_buffer(space, w_ctype, w_x):
from pypy.module._cffi_backend import ctypearray, ctypeprim
@@ -88,14 +113,7 @@
raise oefmt(space.w_TypeError,
"needs 'char[]', got '%s'", w_ctype.name)
#
- # xxx do we really need to implement the same mess as in CPython 2.7
- # w.r.t. buffers and memoryviews??
- try:
- buf = space.readbuf_w(w_x)
- except OperationError, e:
- if not e.match(space, space.w_TypeError):
- raise
- buf = space.buffer_w(w_x, space.BUF_SIMPLE)
+ buf = _fetch_as_read_buffer(space, w_x)
try:
_cdata = buf.get_raw_address()
except ValueError:
@@ -106,6 +124,76 @@
#
return cdataobj.W_CDataFromBuffer(space, _cdata, w_ctype, buf, w_x)
+
+def unsafe_escaping_ptr_for_ptr_or_array(w_cdata):
+ if not w_cdata.ctype.is_nonfunc_pointer_or_array:
+ raise oefmt(w_cdata.space.w_TypeError,
+ "expected a pointer or array ctype, got '%s'",
+ w_cdata.ctype.name)
+ return w_cdata.unsafe_escaping_ptr()
+
+c_memmove = rffi.llexternal('memmove', [rffi.CCHARP, rffi.CCHARP,
+ rffi.SIZE_T], lltype.Void,
+ _nowrapper=True)
+
+ at unwrap_spec(n=int)
+def memmove(space, w_dest, w_src, n):
+ if n < 0:
+ raise oefmt(space.w_ValueError, "negative size")
+
+ # cases...
+ src_buf = None
+ src_data = lltype.nullptr(rffi.CCHARP.TO)
+ if isinstance(w_src, cdataobj.W_CData):
+ src_data = unsafe_escaping_ptr_for_ptr_or_array(w_src)
+ src_is_ptr = True
+ else:
+ src_buf = _fetch_as_read_buffer(space, w_src)
+ try:
+ src_data = src_buf.get_raw_address()
+ src_is_ptr = True
+ except ValueError:
+ src_is_ptr = False
+
+ if src_is_ptr:
+ src_string = None
+ else:
+ if n == src_buf.getlength():
+ src_string = src_buf.as_str()
+ else:
+ src_string = src_buf.getslice(0, n, 1, n)
+
+ dest_buf = None
+ dest_data = lltype.nullptr(rffi.CCHARP.TO)
+ if isinstance(w_dest, cdataobj.W_CData):
+ dest_data = unsafe_escaping_ptr_for_ptr_or_array(w_dest)
+ dest_is_ptr = True
+ else:
+ dest_buf = _fetch_as_write_buffer(space, w_dest)
+ try:
+ dest_data = dest_buf.get_raw_address()
+ dest_is_ptr = True
+ except ValueError:
+ dest_is_ptr = False
+
+ if dest_is_ptr:
+ if src_is_ptr:
+ c_memmove(dest_data, src_data, rffi.cast(rffi.SIZE_T, n))
+ else:
+ copy_string_to_raw(llstr(src_string), dest_data, 0, n)
+ else:
+ if src_is_ptr:
+ for i in range(n):
+ dest_buf.setitem(i, src_data[i])
+ else:
+ for i in range(n):
+ dest_buf.setitem(i, src_string[i])
+
+ keepalive_until_here(src_buf)
+ keepalive_until_here(dest_buf)
+ keepalive_until_here(w_src)
+ keepalive_until_here(w_dest)
+
# ____________________________________________________________
@unwrap_spec(w_cdata=cdataobj.W_CData)
diff --git a/pypy/module/_cffi_backend/handle.py b/pypy/module/_cffi_backend/handle.py
--- a/pypy/module/_cffi_backend/handle.py
+++ b/pypy/module/_cffi_backend/handle.py
@@ -1,24 +1,24 @@
+import py
from pypy.interpreter.error import OperationError, oefmt
from pypy.interpreter.gateway import unwrap_spec
+from pypy.interpreter.baseobjspace import W_Root
from pypy.module._cffi_backend import ctypeobj, ctypeptr, cdataobj
-from rpython.rtyper.lltypesystem import lltype, rffi
-from rpython.rlib import rweaklist
-
-
-class CffiHandles(rweaklist.RWeakListMixin):
- def __init__(self, space):
- self.initialize()
-
-def get(space):
- return space.fromcache(CffiHandles)
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
+from rpython.rlib import rgc, objectmodel, jit
# ____________________________________________________________
+ at jit.dont_look_inside
def _newp_handle(space, w_ctype, w_x):
- index = get(space).reserve_next_handle_index()
- _cdata = rffi.cast(rffi.CCHARP, index + 1)
- new_cdataobj = cdataobj.W_CDataHandle(space, _cdata, w_ctype, w_x)
- get(space).store_handle(index, new_cdataobj)
+ # Allocate a handle as a nonmovable W_CDataHandle instance, which
+ # we can cast to a plain CCHARP. As long as the object is not freed,
+ # we can cast the CCHARP back to a W_CDataHandle with reveal_gcref().
+ new_cdataobj = objectmodel.instantiate(cdataobj.W_CDataHandle,
+ nonmovable=True)
+ gcref = rgc.cast_instance_to_gcref(new_cdataobj)
+ _cdata = rgc.hide_nonmovable_gcref(gcref)
+ _cdata = rffi.cast(rffi.CCHARP, _cdata)
+ cdataobj.W_CDataHandle.__init__(new_cdataobj, space, _cdata, w_ctype, w_x)
return new_cdataobj
@unwrap_spec(w_ctype=ctypeobj.W_CType)
@@ -38,14 +38,17 @@
"expected a 'cdata' object with a 'void *' out of "
"new_handle(), got '%s'", ctype.name)
with w_cdata as ptr:
- index = rffi.cast(lltype.Signed, ptr)
- original_cdataobj = get(space).fetch_handle(index - 1)
- #
- if isinstance(original_cdataobj, cdataobj.W_CDataHandle):
- return original_cdataobj.w_keepalive
- else:
- if index == 0:
- msg = "cannot use from_handle() on NULL pointer"
- else:
- msg = "'void *' value does not correspond to any object"
- raise OperationError(space.w_RuntimeError, space.wrap(msg))
+ return _reveal(space, ptr)
+
+ at jit.dont_look_inside
+def _reveal(space, ptr):
+ addr = rffi.cast(llmemory.Address, ptr)
+ gcref = rgc.reveal_gcref(addr)
+ if not gcref:
+ raise oefmt(space.w_RuntimeError,
+ "cannot use from_handle() on NULL pointer")
+ cd = rgc.try_cast_gcref_to_instance(cdataobj.W_CDataHandle, gcref)
+ if cd is None:
+ raise oefmt(space.w_SystemError,
+ "ffi.from_handle(): dead or bogus object handle")
+ return cd.w_keepalive
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
@@ -4,10 +4,11 @@
from rpython.rlib.objectmodel import specialize, r_dict, compute_identity_hash
from rpython.rlib.rarithmetic import ovfcheck, intmask
-from rpython.rlib import jit, rweakref
+from rpython.rlib import jit, rweakref, clibffi
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.rtyper.tool import rffi_platform
+from pypy.module import _cffi_backend
from pypy.module._cffi_backend import (ctypeobj, ctypeprim, ctypeptr,
ctypearray, ctypestruct, ctypevoid, ctypeenum)
@@ -592,8 +593,9 @@
# ____________________________________________________________
- at unwrap_spec(w_fresult=ctypeobj.W_CType, ellipsis=int)
-def new_function_type(space, w_fargs, w_fresult, ellipsis=0):
+ at unwrap_spec(w_fresult=ctypeobj.W_CType, ellipsis=int, abi=int)
+def new_function_type(space, w_fargs, w_fresult, ellipsis=0,
+ abi=_cffi_backend.FFI_DEFAULT_ABI):
fargs = []
for w_farg in space.fixedview(w_fargs):
if not isinstance(w_farg, ctypeobj.W_CType):
@@ -602,28 +604,28 @@
if isinstance(w_farg, ctypearray.W_CTypeArray):
w_farg = w_farg.ctptr
fargs.append(w_farg)
- return _new_function_type(space, fargs, w_fresult, bool(ellipsis))
+ return _new_function_type(space, fargs, w_fresult, bool(ellipsis), abi)
-def _func_key_hash(unique_cache, fargs, fresult, ellipsis):
+def _func_key_hash(unique_cache, fargs, fresult, ellipsis, abi):
x = compute_identity_hash(fresult)
for w_arg in fargs:
y = compute_identity_hash(w_arg)
x = intmask((1000003 * x) ^ y)
- x ^= ellipsis
+ x ^= (ellipsis - abi)
if unique_cache.for_testing: # constant-folded to False in translation;
x &= 3 # but for test, keep only 2 bits of hash
return x
# can't use @jit.elidable here, because it might call back to random
# space functions via force_lazy_struct()
-def _new_function_type(space, fargs, fresult, ellipsis=False):
+def _new_function_type(space, fargs, fresult, ellipsis, abi):
try:
- return _get_function_type(space, fargs, fresult, ellipsis)
+ return _get_function_type(space, fargs, fresult, ellipsis, abi)
except KeyError:
- return _build_function_type(space, fargs, fresult, ellipsis)
+ return _build_function_type(space, fargs, fresult, ellipsis, abi)
@jit.elidable
-def _get_function_type(space, fargs, fresult, ellipsis):
+def _get_function_type(space, fargs, fresult, ellipsis, abi):
# This function is elidable because if called again with exactly the
# same arguments (and if it didn't raise KeyError), it would give
# the same result, at least as long as this result is still live.
@@ -633,18 +635,19 @@
# one such dict, but in case of hash collision, there might be
# more.
unique_cache = space.fromcache(UniqueCache)
- func_hash = _func_key_hash(unique_cache, fargs, fresult, ellipsis)
+ func_hash = _func_key_hash(unique_cache, fargs, fresult, ellipsis, abi)
for weakdict in unique_cache.functions:
ctype = weakdict.get(func_hash)
if (ctype is not None and
ctype.ctitem is fresult and
ctype.fargs == fargs and
- ctype.ellipsis == ellipsis):
+ ctype.ellipsis == ellipsis and
+ ctype.abi == abi):
return ctype
raise KeyError
@jit.dont_look_inside
-def _build_function_type(space, fargs, fresult, ellipsis):
+def _build_function_type(space, fargs, fresult, ellipsis, abi):
from pypy.module._cffi_backend import ctypefunc
#
if ((fresult.size < 0 and
@@ -658,9 +661,9 @@
raise oefmt(space.w_TypeError,
"invalid result type: '%s'", fresult.name)
#
- fct = ctypefunc.W_CTypeFunc(space, fargs, fresult, ellipsis)
+ fct = ctypefunc.W_CTypeFunc(space, fargs, fresult, ellipsis, abi)
unique_cache = space.fromcache(UniqueCache)
- func_hash = _func_key_hash(unique_cache, fargs, fresult, ellipsis)
+ func_hash = _func_key_hash(unique_cache, fargs, fresult, ellipsis, abi)
for weakdict in unique_cache.functions:
if weakdict.get(func_hash) is None:
weakdict.set(func_hash, fct)
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
@@ -5,6 +5,7 @@
from rpython.rtyper.lltypesystem import lltype, rffi
from pypy.interpreter.error import oefmt
from pypy.interpreter.baseobjspace import W_Root
+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 parse_c_type
@@ -164,16 +165,28 @@
OP_FUNCTION_END = cffi_opcode.OP_FUNCTION_END
while getop(opcodes[base_index + num_args]) != OP_FUNCTION_END:
num_args += 1
- ellipsis = (getarg(opcodes[base_index + num_args]) & 1) != 0
+ #
+ ellipsis = (getarg(opcodes[base_index + num_args]) & 0x01) != 0
+ abi = (getarg(opcodes[base_index + num_args]) & 0xFE)
+ if abi == 0:
+ abi = _cffi_backend.FFI_DEFAULT_ABI
+ elif abi == 2:
+ if _cffi_backend.has_stdcall:
+ abi = _cffi_backend.FFI_STDCALL
+ else:
+ abi = _cffi_backend.FFI_DEFAULT_ABI
+ else:
+ raise oefmt(ffi.w_FFIError, "abi number %d not supported", abi)
+ #
fargs = [realize_c_type(ffi, opcodes, base_index + i)
for i in range(num_args)]
- return fargs, fret, ellipsis
+ return fargs, fret, ellipsis, abi
def unwrap_as_fnptr(self, ffi):
if self._ctfuncptr is None:
- fargs, fret, ellipsis = self._unpack(ffi)
+ fargs, fret, ellipsis, abi = self._unpack(ffi)
self._ctfuncptr = newtype._new_function_type(
- ffi.space, fargs, fret, ellipsis)
+ ffi.space, fargs, fret, ellipsis, abi)
return self._ctfuncptr
def unwrap_as_fnptr_in_elidable(self):
@@ -190,7 +203,7 @@
# type ptr-to-struct. This is how recompiler.py produces
# trampoline functions for PyPy.
if self.nostruct_ctype is None:
- fargs, fret, ellipsis = self._unpack(ffi)
+ fargs, fret, ellipsis, abi = self._unpack(ffi)
# 'locs' will be a string of the same length as the final fargs,
# containing 'A' where a struct argument was detected, and 'R'
# in first position if a struct return value was detected
@@ -207,7 +220,7 @@
locs = ['R'] + locs
fret = newtype.new_void_type(ffi.space)
ctfuncptr = newtype._new_function_type(
- ffi.space, fargs, fret, ellipsis)
+ ffi.space, fargs, fret, ellipsis, abi)
if locs == ['\x00'] * len(locs):
locs = None
else:
@@ -218,7 +231,7 @@
locs[0] == 'R')
def unexpected_fn_type(self, ffi):
- fargs, fret, ellipsis = self._unpack(ffi)
+ fargs, fret, ellipsis, abi = self._unpack(ffi)
argnames = [farg.name for farg in fargs]
if ellipsis:
argnames.append('...')
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
@@ -51,6 +51,9 @@
TOK_UNSIGNED,
TOK_VOID,
TOK_VOLATILE,
+
+ TOK_CDECL,
+ TOK_STDCALL,
};
typedef struct {
@@ -165,6 +168,8 @@
switch (*p) {
case '_':
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;
break;
case 'c':
if (tok->size == 4 && !memcmp(p, "char", 4)) tok->kind = TOK_CHAR;
@@ -236,7 +241,7 @@
type). The 'outer' argument is the index of the opcode outside
this "sequel".
*/
- int check_for_grouping;
+ int check_for_grouping, abi=0;
_cffi_opcode_t result, *p_current;
header:
@@ -253,6 +258,12 @@
/* ignored for now */
next_token(tok);
goto header;
+ case TOK_CDECL:
+ case TOK_STDCALL:
+ /* must be in a function; checked below */
+ abi = tok->kind;
+ next_token(tok);
+ goto header;
default:
break;
}
@@ -269,6 +280,11 @@
while (tok->kind == TOK_OPEN_PAREN) {
next_token(tok);
+ if (tok->kind == TOK_CDECL || tok->kind == TOK_STDCALL) {
+ abi = tok->kind;
+ next_token(tok);
+ }
+
if ((check_for_grouping--) == 1 && (tok->kind == TOK_STAR ||
tok->kind == TOK_CONST ||
tok->kind == TOK_VOLATILE ||
@@ -286,7 +302,14 @@
}
else {
/* function type */
- int arg_total, base_index, arg_next, has_ellipsis=0;
+ int arg_total, base_index, arg_next, flags=0;
+
+ if (abi == TOK_STDCALL) {
+ flags = 2;
+ /* note that an ellipsis below will overwrite this flags,
+ which is the goal: variadic functions are always cdecl */
+ }
+ abi = 0;
if (tok->kind == TOK_VOID && get_following_char(tok) == ')') {
next_token(tok);
@@ -315,7 +338,7 @@
_cffi_opcode_t oarg;
if (tok->kind == TOK_DOTDOTDOT) {
- has_ellipsis = 1;
+ flags = 1; /* ellipsis */
next_token(tok);
break;
}
@@ -339,8 +362,7 @@
next_token(tok);
}
}
- tok->output[arg_next] = _CFFI_OP(_CFFI_OP_FUNCTION_END,
- has_ellipsis);
+ tok->output[arg_next] = _CFFI_OP(_CFFI_OP_FUNCTION_END, flags);
}
if (tok->kind != TOK_CLOSE_PAREN)
@@ -348,6 +370,9 @@
next_token(tok);
}
+ if (abi != 0)
+ return parse_error(tok, "expected '('");
+
while (tok->kind == TOK_OPEN_BRACKET) {
*p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index);
p_current = tok->output + tok->output_index;
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
@@ -2316,9 +2316,6 @@
f(); f()
assert get_errno() == 77
-def test_abi():
- assert isinstance(FFI_DEFAULT_ABI, int)
-
def test_cast_to_array():
# not valid in C! extension to get a non-owning <cdata 'int[3]'>
BInt = new_primitive_type("int")
@@ -3396,6 +3393,78 @@
check(4 | 8, "CHB", "GTB")
check(4 | 16, "CHB", "ROB")
+def test_memmove():
+ Short = new_primitive_type("short")
+ ShortA = new_array_type(new_pointer_type(Short), None)
+ Char = new_primitive_type("char")
+ CharA = new_array_type(new_pointer_type(Char), None)
+ p = newp(ShortA, [-1234, -2345, -3456, -4567, -5678])
+ memmove(p, p + 1, 4)
+ assert list(p) == [-2345, -3456, -3456, -4567, -5678]
+ p[2] = 999
+ memmove(p + 2, p, 6)
+ assert list(p) == [-2345, -3456, -2345, -3456, 999]
+ memmove(p + 4, newp(CharA, b"\x71\x72"), 2)
+ if sys.byteorder == 'little':
+ assert list(p) == [-2345, -3456, -2345, -3456, 0x7271]
+ else:
+ assert list(p) == [-2345, -3456, -2345, -3456, 0x7172]
+
+def test_memmove_buffer():
+ import array
+ Short = new_primitive_type("short")
+ ShortA = new_array_type(new_pointer_type(Short), None)
+ a = array.array('H', [10000, 20000, 30000])
+ p = newp(ShortA, 5)
+ memmove(p, a, 6)
+ assert list(p) == [10000, 20000, 30000, 0, 0]
+ memmove(p + 1, a, 6)
+ assert list(p) == [10000, 10000, 20000, 30000, 0]
+ b = array.array('h', [-1000, -2000, -3000])
+ memmove(b, a, 4)
+ assert b.tolist() == [10000, 20000, -3000]
+ assert a.tolist() == [10000, 20000, 30000]
+ p[0] = 999
+ p[1] = 998
+ p[2] = 997
+ p[3] = 996
+ p[4] = 995
+ memmove(b, p, 2)
+ assert b.tolist() == [999, 20000, -3000]
+ memmove(b, p + 2, 4)
+ assert b.tolist() == [997, 996, -3000]
+ p[2] = -p[2]
+ p[3] = -p[3]
+ memmove(b, p + 2, 6)
+ assert b.tolist() == [-997, -996, 995]
+
+def test_memmove_readonly_readwrite():
+ SignedChar = new_primitive_type("signed char")
+ SignedCharA = new_array_type(new_pointer_type(SignedChar), None)
+ p = newp(SignedCharA, 5)
+ memmove(p, b"abcde", 3)
+ assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0]
+ memmove(p, bytearray(b"ABCDE"), 2)
+ assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0]
+ py.test.raises((TypeError, BufferError), memmove, b"abcde", p, 3)
+ ba = bytearray(b"xxxxx")
+ memmove(dest=ba, src=p, n=3)
+ assert ba == bytearray(b"ABcxx")
+ memmove(ba, b"EFGH", 4)
+ assert ba == bytearray(b"EFGHx")
+
+def test_memmove_sign_check():
+ SignedChar = new_primitive_type("signed char")
+ SignedCharA = new_array_type(new_pointer_type(SignedChar), None)
+ p = newp(SignedCharA, 5)
+ py.test.raises(ValueError, memmove, p, p + 1, -1) # not segfault
+
+def test_memmove_bad_cdata():
+ BInt = new_primitive_type("int")
+ p = cast(BInt, 42)
+ py.test.raises(TypeError, memmove, p, bytearray(b'a'), 1)
+ py.test.raises(TypeError, memmove, bytearray(b'a'), p, 1)
+
def test_dereference_null_ptr():
BInt = new_primitive_type("int")
BIntPtr = new_pointer_type(BInt)
@@ -3427,3 +3496,16 @@
"be 'foo *', but the types are different (check "
"that you are not e.g. mixing up different ffi "
"instances)")
+
+def test_stdcall_function_type():
+ assert FFI_CDECL == FFI_DEFAULT_ABI
+ try:
+ stdcall = FFI_STDCALL
+ except NameError:
+ stdcall = FFI_DEFAULT_ABI
+ BInt = new_primitive_type("int")
+ BFunc = new_function_type((BInt, BInt), BInt, False, stdcall)
+ if stdcall != FFI_DEFAULT_ABI:
+ assert repr(BFunc) == "<ctype 'int(__stdcall *)(int, int)'>"
+ else:
+ assert repr(BFunc) == "<ctype 'int(*)(int, int)'>"
diff --git a/pypy/module/_cffi_backend/test/test_ffi_obj.py b/pypy/module/_cffi_backend/test/test_ffi_obj.py
--- a/pypy/module/_cffi_backend/test/test_ffi_obj.py
+++ b/pypy/module/_cffi_backend/test/test_ffi_obj.py
@@ -247,6 +247,63 @@
ffi.cast("unsigned short *", c)[1] += 500
assert list(a) == [10000, 20500, 30000]
+ def test_memmove(self):
+ import sys
+ import _cffi_backend as _cffi1_backend
+ ffi = _cffi1_backend.FFI()
+ p = ffi.new("short[]", [-1234, -2345, -3456, -4567, -5678])
+ ffi.memmove(p, p + 1, 4)
+ assert list(p) == [-2345, -3456, -3456, -4567, -5678]
+ p[2] = 999
+ ffi.memmove(p + 2, p, 6)
+ assert list(p) == [-2345, -3456, -2345, -3456, 999]
+ ffi.memmove(p + 4, ffi.new("char[]", b"\x71\x72"), 2)
+ if sys.byteorder == 'little':
+ assert list(p) == [-2345, -3456, -2345, -3456, 0x7271]
+ else:
+ assert list(p) == [-2345, -3456, -2345, -3456, 0x7172]
+
+ def test_memmove_buffer(self):
+ import _cffi_backend as _cffi1_backend
+ import array
+ ffi = _cffi1_backend.FFI()
+ a = array.array('H', [10000, 20000, 30000])
+ p = ffi.new("short[]", 5)
+ ffi.memmove(p, a, 6)
+ assert list(p) == [10000, 20000, 30000, 0, 0]
+ ffi.memmove(p + 1, a, 6)
+ assert list(p) == [10000, 10000, 20000, 30000, 0]
+ b = array.array('h', [-1000, -2000, -3000])
+ ffi.memmove(b, a, 4)
+ assert b.tolist() == [10000, 20000, -3000]
+ assert a.tolist() == [10000, 20000, 30000]
+ p[0] = 999
+ p[1] = 998
+ p[2] = 997
+ p[3] = 996
+ p[4] = 995
+ ffi.memmove(b, p, 2)
+ assert b.tolist() == [999, 20000, -3000]
+ ffi.memmove(b, p + 2, 4)
+ assert b.tolist() == [997, 996, -3000]
+ p[2] = -p[2]
+ p[3] = -p[3]
+ ffi.memmove(b, p + 2, 6)
+ assert b.tolist() == [-997, -996, 995]
+
+ def test_memmove_readonly_readwrite(self):
+ import _cffi_backend as _cffi1_backend
+ ffi = _cffi1_backend.FFI()
+ p = ffi.new("signed char[]", 5)
+ ffi.memmove(p, b"abcde", 3)
+ assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0]
+ ffi.memmove(p, bytearray(b"ABCDE"), 2)
+ assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0]
+ raises((TypeError, BufferError), ffi.memmove, b"abcde", p, 3)
+ ba = bytearray(b"xxxxx")
+ ffi.memmove(dest=ba, src=p, n=3)
+ assert ba == bytearray(b"ABcxx")
+
def test_ffi_types(self):
import _cffi_backend as _cffi1_backend
CData = _cffi1_backend.FFI.CData
diff --git a/pypy/module/_cffi_backend/test/test_handle.py b/pypy/module/_cffi_backend/test/test_handle.py
deleted file mode 100644
--- a/pypy/module/_cffi_backend/test/test_handle.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import random
-from pypy.module._cffi_backend.handle import CffiHandles
-
-
-class PseudoWeakRef(object):
- _content = 42
-
- def __call__(self):
- return self._content
-
-
-def test_cffi_handles_1():
- ch = CffiHandles(None)
- expected_content = {}
- for i in range(10000):
- index = ch.reserve_next_handle_index()
- assert 0 <= index < len(ch.handles)
- assert ch.handles[index]() is None
- pwr = PseudoWeakRef()
- expected_content[index] = pwr
- ch.handles[index] = pwr
- assert len(ch.handles) <= 16384
- for index, pwr in expected_content.items():
- assert ch.handles[index] is pwr
-
-def test_cffi_handles_2():
- ch = CffiHandles(None)
- expected_content = {}
- for i in range(10000):
- index = ch.reserve_next_handle_index()
- assert 0 <= index < len(ch.handles)
- assert ch.handles[index]() is None
- pwr = PseudoWeakRef()
- expected_content[index] = pwr
- ch.handles[index] = pwr
- #
- if len(expected_content) > 20:
- r = random.choice(list(expected_content))
- pwr = expected_content.pop(r)
- pwr._content = None
- #
- assert len(ch.handles) < 100
- for index, pwr in expected_content.items():
- assert ch.handles[index] is pwr
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
@@ -338,3 +338,17 @@
# not supported (really obscure):
# "char[+5]"
# "char['A']"
+
+def test_stdcall_cdecl():
+ assert parse("int __stdcall(int)") == [Prim(cffi_opcode.PRIM_INT),
+ '->', Func(0), NoOp(4), FuncEnd(2),
+ Prim(cffi_opcode.PRIM_INT)]
+ assert parse("int __stdcall func(int)") == parse("int __stdcall(int)")
+ assert parse("int (__stdcall *)()") == [Prim(cffi_opcode.PRIM_INT),
+ NoOp(3), '->', Pointer(1),
+ Func(0), FuncEnd(2), 0]
+ assert parse("int (__stdcall *p)()") == parse("int (__stdcall*)()")
+ parse_error("__stdcall int", "identifier expected", 0)
+ parse_error("__cdecl int", "identifier expected", 0)
+ parse_error("int __stdcall", "expected '('", 13)
+ parse_error("int __cdecl", "expected '('", 11)
diff --git a/pypy/module/_io/interp_io.py b/pypy/module/_io/interp_io.py
--- a/pypy/module/_io/interp_io.py
+++ b/pypy/module/_io/interp_io.py
@@ -118,7 +118,7 @@
if buffering < 0:
buffering = DEFAULT_BUFFER_SIZE
- if space.config.translation.type_system == 'lltype' and 'st_blksize' in STAT_FIELD_TYPES:
+ if 'st_blksize' in STAT_FIELD_TYPES:
fileno = space.c_int_w(space.call_method(w_raw, "fileno"))
try:
st = os.fstat(fileno)
diff --git a/pypy/module/cpyext/ndarrayobject.py b/pypy/module/cpyext/ndarrayobject.py
--- a/pypy/module/cpyext/ndarrayobject.py
+++ b/pypy/module/cpyext/ndarrayobject.py
@@ -12,6 +12,7 @@
from pypy.module.micronumpy.descriptor import get_dtype_cache, W_Dtype
from pypy.module.micronumpy.concrete import ConcreteArray
from pypy.module.micronumpy import ufuncs
+import pypy.module.micronumpy.constants as NPY
from rpython.rlib.rawstorage import RAW_STORAGE_PTR
from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.baseobjspace import W_Root
@@ -203,12 +204,12 @@
return shape, dtype
def simple_new(space, nd, dims, typenum,
- order='C', owning=False, w_subtype=None):
+ order=NPY.CORDER, owning=False, w_subtype=None):
shape, dtype = get_shape_and_dtype(space, nd, dims, typenum)
return W_NDimArray.from_shape(space, shape, dtype)
def simple_new_from_data(space, nd, dims, typenum, data,
- order='C', owning=False, w_subtype=None):
+ order=NPY.CORDER, owning=False, w_subtype=None):
shape, dtype = get_shape_and_dtype(space, nd, dims, typenum)
storage = rffi.cast(RAW_STORAGE_PTR, data)
return W_NDimArray.from_shape_and_storage(space, shape, storage, dtype,
@@ -238,7 +239,7 @@
raise OperationError(space.w_NotImplementedError,
space.wrap("strides must be NULL"))
- order = 'C' if flags & NPY_C_CONTIGUOUS else 'F'
+ order = NPY.CORDER if flags & NPY_C_CONTIGUOUS else NPY.FORTRANORDER
owning = True if flags & NPY_OWNDATA else False
w_subtype = None
diff --git a/pypy/module/cpyext/test/test_ndarrayobject.py b/pypy/module/cpyext/test/test_ndarrayobject.py
--- a/pypy/module/cpyext/test/test_ndarrayobject.py
+++ b/pypy/module/cpyext/test/test_ndarrayobject.py
@@ -4,16 +4,17 @@
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.module.micronumpy.ndarray import W_NDimArray
from pypy.module.micronumpy.descriptor import get_dtype_cache
+import pypy.module.micronumpy.constants as NPY
def scalar(space):
dtype = get_dtype_cache(space).w_float64dtype
return W_NDimArray.new_scalar(space, dtype, space.wrap(10.))
-def array(space, shape, order='C'):
+def array(space, shape, order=NPY.CORDER):
dtype = get_dtype_cache(space).w_float64dtype
return W_NDimArray.from_shape(space, shape, dtype, order=order)
-def iarray(space, shape, order='C'):
+def iarray(space, shape, order=NPY.CORDER):
dtype = get_dtype_cache(space).w_int64dtype
return W_NDimArray.from_shape(space, shape, dtype, order=order)
@@ -32,8 +33,8 @@
def test_FLAGS(self, space, api):
s = array(space, [10])
- c = array(space, [10, 5, 3], order='C')
- f = array(space, [10, 5, 3], order='F')
+ c = array(space, [10, 5, 3], order=NPY.CORDER)
+ f = array(space, [10, 5, 3], order=NPY.FORTRANORDER)
assert api._PyArray_FLAGS(s) & 0x0001
assert api._PyArray_FLAGS(s) & 0x0002
assert api._PyArray_FLAGS(c) & 0x0001
diff --git a/pypy/module/itertools/__init__.py b/pypy/module/itertools/__init__.py
--- a/pypy/module/itertools/__init__.py
+++ b/pypy/module/itertools/__init__.py
@@ -10,7 +10,6 @@
repeat(elem [,n]) --> elem, elem, elem, ... endlessly or up to n times
Iterators terminating on the shortest input sequence:
- izip(p, q, ...) --> (p[0], q[0]), (p[1], q[1]), ...
ifilter(pred, seq) --> elements of seq where pred(elem) is True
ifilterfalse(pred, seq) --> elements of seq where pred(elem) is False
islice(seq, [start,] stop [, step]) --> elements from
@@ -22,6 +21,14 @@
takewhile(pred, seq) --> seq[0], seq[1], until pred fails
dropwhile(pred, seq) --> seq[n], seq[n+1], starting when pred fails
groupby(iterable[, keyfunc]) --> sub-iterators grouped by value of keyfunc(v)
+ izip(p, q, ...) --> (p[0], q[0]), (p[1], q[1]), ...
+ izip_longest(p, q, ...) --> (p[0], q[0]), (p[1], q[1]), ...
+
+ Combinatoric generators:
+ product(p, q, ... [repeat=1]) --> cartesian product
+ permutations(p[, r])
+ combinations(p, r)
+ combinations_with_replacement(p, r)
"""
interpleveldefs = {
diff --git a/pypy/module/itertools/interp_itertools.py b/pypy/module/itertools/interp_itertools.py
--- a/pypy/module/itertools/interp_itertools.py
+++ b/pypy/module/itertools/interp_itertools.py
@@ -649,33 +649,38 @@
class W_IZipLongest(W_IMap):
_error_name = "izip_longest"
+ _immutable_fields_ = ["w_fillvalue"]
+
+ def _fetch(self, index):
+ w_iter = self.iterators_w[index]
+ if w_iter is not None:
+ space = self.space
+ try:
+ return space.next(w_iter)
+ except OperationError, e:
+ if not e.match(space, space.w_StopIteration):
+ raise
+ self.active -= 1
+ if self.active <= 0:
+ # It was the last active iterator
+ raise
+ self.iterators_w[index] = None
+ return self.w_fillvalue
def next_w(self):
- space = self.space
+ # common case: 2 arguments
+ if len(self.iterators_w) == 2:
+ objects = [self._fetch(0), self._fetch(1)]
+ else:
+ objects = self._get_objects()
+ return self.space.newtuple(objects)
+
+ def _get_objects(self):
+ # the loop is out of the way of the JIT
nb = len(self.iterators_w)
-
if nb == 0:
- raise OperationError(space.w_StopIteration, space.w_None)
-
- objects_w = [None] * nb
- for index in range(nb):
- w_value = self.w_fillvalue
- w_it = self.iterators_w[index]
- if w_it is not None:
- try:
- w_value = space.next(w_it)
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
-
- self.active -= 1
- if self.active == 0:
- # It was the last active iterator
- raise
- self.iterators_w[index] = None
-
- objects_w[index] = w_value
- return space.newtuple(objects_w)
+ raise OperationError(self.space.w_StopIteration, self.space.w_None)
+ return [self._fetch(index) for index in range(nb)]
def W_IZipLongest___new__(space, w_subtype, __args__):
arguments_w, kwds_w = __args__.unpack()
diff --git a/pypy/module/micronumpy/arrayops.py b/pypy/module/micronumpy/arrayops.py
--- a/pypy/module/micronumpy/arrayops.py
+++ b/pypy/module/micronumpy/arrayops.py
@@ -108,7 +108,8 @@
w_axis = space.wrap(0)
if space.is_none(w_axis):
args_w = [w_arg.reshape(space,
- space.newlist([w_arg.descr_get_size(space)]))
+ space.newlist([w_arg.descr_get_size(space)]),
+ w_arg.get_order())
for w_arg in args_w]
w_axis = space.wrap(0)
dtype = args_w[0].get_dtype()
@@ -140,7 +141,7 @@
dtype = find_result_type(space, args_w, [])
# concatenate does not handle ndarray subtypes, it always returns a ndarray
- res = W_NDimArray.from_shape(space, shape, dtype, 'C')
+ res = W_NDimArray.from_shape(space, shape, dtype, NPY.CORDER)
chunks = [Chunk(0, i, 1, i) for i in shape]
axis_start = 0
for arr in args_w:
diff --git a/pypy/module/micronumpy/base.py b/pypy/module/micronumpy/base.py
--- a/pypy/module/micronumpy/base.py
+++ b/pypy/module/micronumpy/base.py
@@ -38,7 +38,8 @@
self.implementation = implementation
@staticmethod
- def from_shape(space, shape, dtype, order='C', w_instance=None, zero=True):
+ def from_shape(space, shape, dtype, order=NPY.CORDER,
+ w_instance=None, zero=True):
from pypy.module.micronumpy import concrete, descriptor, boxes
from pypy.module.micronumpy.strides import calc_strides
if len(shape) > NPY.MAXDIMS:
@@ -59,8 +60,9 @@
@staticmethod
def from_shape_and_storage(space, shape, storage, dtype, storage_bytes=-1,
- order='C', owning=False, w_subtype=None,
- w_base=None, writable=True, strides=None, start=0):
+ order=NPY.CORDER, owning=False, w_subtype=None,
+ w_base=None, writable=True, strides=None,
+ start=0):
from pypy.module.micronumpy import concrete
from pypy.module.micronumpy.strides import (calc_strides,
calc_backstrides)
diff --git a/pypy/module/micronumpy/concrete.py b/pypy/module/micronumpy/concrete.py
--- a/pypy/module/micronumpy/concrete.py
+++ b/pypy/module/micronumpy/concrete.py
@@ -56,6 +56,9 @@
jit.hint(len(backstrides), promote=True)
return backstrides
+ def get_flags(self):
+ return self.flags
+
def getitem(self, index):
return self.dtype.read(self, index, 0)
@@ -89,17 +92,18 @@
def get_storage_size(self):
return self.size
- def reshape(self, orig_array, new_shape):
+ def reshape(self, orig_array, new_shape, order=NPY.ANYORDER):
# Since we got to here, prod(new_shape) == self.size
+ order = support.get_order_as_CF(self.order, order)
new_strides = None
if self.size == 0:
- new_strides, _ = calc_strides(new_shape, self.dtype, self.order)
+ new_strides, _ = calc_strides(new_shape, self.dtype, order)
else:
if len(self.get_shape()) == 0:
new_strides = [self.dtype.elsize] * len(new_shape)
else:
new_strides = calc_new_strides(new_shape, self.get_shape(),
- self.get_strides(), self.order)
+ self.get_strides(), order)
if new_strides is None or len(new_strides) != len(new_shape):
return None
if new_strides is not None:
@@ -303,10 +307,11 @@
return SliceArray(self.start, strides,
backstrides, shape, self, orig_array)
- def copy(self, space):
+ def copy(self, space, order=NPY.ANYORDER):
+ order = support.get_order_as_CF(self.order, order)
strides, backstrides = calc_strides(self.get_shape(), self.dtype,
- self.order)
- impl = ConcreteArray(self.get_shape(), self.dtype, self.order, strides,
+ order)
+ impl = ConcreteArray(self.get_shape(), self.dtype, order, strides,
backstrides)
return loop.setslice(space, self.get_shape(), impl, self)
@@ -360,12 +365,12 @@
# but make the array storage contiguous in memory
shape = self.get_shape()
strides = self.get_strides()
- if order not in ('C', 'F'):
- raise oefmt(space.w_ValueError, "Unknown order %s in astype", order)
+ if order not in (NPY.KEEPORDER, NPY.FORTRANORDER, NPY.CORDER):
+ raise oefmt(space.w_ValueError, "Unknown order %d in astype", order)
if len(strides) == 0:
t_strides = []
backstrides = []
- elif order != self.order:
+ elif order in (NPY.FORTRANORDER, NPY.CORDER):
t_strides, backstrides = calc_strides(shape, dtype, order)
else:
indx_array = range(len(strides))
@@ -378,6 +383,7 @@
t_strides[i] = base
base *= shape[i]
backstrides = calc_backstrides(t_strides, shape)
+ order = support.get_order_as_CF(self.order, order)
impl = ConcreteArray(shape, dtype, order, t_strides, backstrides)
loop.setslice(space, impl.get_shape(), impl, self)
return impl
@@ -429,6 +435,8 @@
self.shape = shape
# already tested for overflow in from_shape_and_storage
self.size = support.product(shape) * dtype.elsize
+ if order not in (NPY.CORDER, NPY.FORTRANORDER):
+ raise oefmt(dtype.itemtype.space.w_ValueError, "ConcreteArrayNotOwning but order is not 0,1 rather %d", order)
self.order = order
self.dtype = dtype
self.strides = strides
@@ -562,6 +570,8 @@
self.parent = parent
self.storage = parent.storage
self.gcstruct = parent.gcstruct
+ if parent.order not in (NPY.CORDER, NPY.FORTRANORDER):
+ raise oefmt(dtype.itemtype.space.w_ValueError, "SliceArray but parent order is not 0,1 rather %d", parent.order)
self.order = parent.order
self.dtype = dtype
try:
@@ -602,13 +612,13 @@
s = self.get_strides()[0] // dtype.elsize
except IndexError:
s = 1
- if self.order == 'C':
+ if self.order != NPY.FORTRANORDER:
new_shape.reverse()
for sh in new_shape:
strides.append(s * dtype.elsize)
backstrides.append(s * (sh - 1) * dtype.elsize)
s *= max(1, sh)
- if self.order == 'C':
+ if self.order != NPY.FORTRANORDER:
strides.reverse()
backstrides.reverse()
new_shape.reverse()
diff --git a/pypy/module/micronumpy/converters.py b/pypy/module/micronumpy/converters.py
--- a/pypy/module/micronumpy/converters.py
+++ b/pypy/module/micronumpy/converters.py
@@ -77,9 +77,8 @@
elif order.startswith('K') or order.startswith('k'):
return NPY.KEEPORDER
else:
- raise OperationError(space.w_TypeError, space.wrap(
- "order not understood"))
-
+ raise oefmt(space.w_TypeError, "Unknown order: '%s'", order)
+ return -1
def multi_axis_converter(space, w_axis, ndim):
if space.is_none(w_axis):
diff --git a/pypy/module/micronumpy/ctors.py b/pypy/module/micronumpy/ctors.py
--- a/pypy/module/micronumpy/ctors.py
+++ b/pypy/module/micronumpy/ctors.py
@@ -3,10 +3,13 @@
from rpython.rlib.buffer import SubBuffer
from rpython.rlib.rstring import strip_spaces
from rpython.rtyper.lltypesystem import lltype, rffi
+
from pypy.module.micronumpy import descriptor, loop, support
-from pypy.module.micronumpy.base import (
+from pypy.module.micronumpy.base import (wrap_impl,
W_NDimArray, convert_to_array, W_NumpyObject)
-from pypy.module.micronumpy.converters import shape_converter
+from pypy.module.micronumpy.converters import shape_converter, order_converter
+import pypy.module.micronumpy.constants as NPY
+from .casting import scalar2dtype
def build_scalar(space, w_dtype, w_state):
@@ -82,7 +85,6 @@
return w_res
def _array(space, w_object, w_dtype=None, copy=True, w_order=None, subok=False):
- from pypy.module.micronumpy import strides
# for anything that isn't already an array, try __array__ method first
if not isinstance(w_object, W_NDimArray):
@@ -99,13 +101,8 @@
dtype = descriptor.decode_w_dtype(space, w_dtype)
if space.is_none(w_order):
- order = 'C'
- else:
- order = space.str_w(w_order)
- if order == 'K':
- order = 'C'
- if order != 'C': # or order != 'F':
- raise oefmt(space.w_ValueError, "Unknown order: %s", order)
+ w_order = space.wrap('C')
+ npy_order = order_converter(space, w_order, NPY.CORDER)
if isinstance(w_object, W_NDimArray):
if (dtype is None or w_object.get_dtype() is dtype):
@@ -124,7 +121,7 @@
copy = True
if copy:
shape = w_object.get_shape()
- w_arr = W_NDimArray.from_shape(space, shape, dtype, order=order)
+ w_arr = W_NDimArray.from_shape(space, shape, dtype, order=npy_order)
if support.product(shape) == 1:
w_arr.set_scalar_value(dtype.coerce(space,
w_object.implementation.getitem(0)))
@@ -143,18 +140,13 @@
w_base=w_base, start=imp.start)
else:
# not an array
- shape, elems_w = strides.find_shape_and_elems(space, w_object, dtype)
+ shape, elems_w = find_shape_and_elems(space, w_object, dtype)
if dtype is None and space.isinstance_w(w_object, space.w_buffer):
dtype = descriptor.get_dtype_cache(space).w_uint8dtype
if dtype is None or (dtype.is_str_or_unicode() and dtype.elsize < 1):
dtype = find_dtype_for_seq(space, elems_w, dtype)
- if dtype is None:
- dtype = descriptor.get_dtype_cache(space).w_float64dtype
- elif dtype.is_str_or_unicode() and dtype.elsize < 1:
- # promote S0 -> S1, U0 -> U1
- dtype = descriptor.variable_dtype(space, dtype.char + '1')
- w_arr = W_NDimArray.from_shape(space, shape, dtype, order=order)
+ w_arr = W_NDimArray.from_shape(space, shape, dtype, order=npy_order)
if support.product(shape) == 1: # safe from overflow since from_shape checks
w_arr.set_scalar_value(dtype.coerce(space, elems_w[0]))
else:
@@ -165,7 +157,6 @@
def numpify(space, w_object):
"""Convert the object to a W_NumpyObject"""
# XXX: code duplication with _array()
- from pypy.module.micronumpy import strides
if isinstance(w_object, W_NumpyObject):
return w_object
# for anything that isn't already an array, try __array__ method first
@@ -173,20 +164,82 @@
if w_array is not None:
return w_array
- shape, elems_w = strides.find_shape_and_elems(space, w_object, None)
+ if is_scalar_like(space, w_object, dtype=None):
+ dtype = scalar2dtype(space, w_object)
+ if dtype.is_str_or_unicode() and dtype.elsize < 1:
+ # promote S0 -> S1, U0 -> U1
+ dtype = descriptor.variable_dtype(space, dtype.char + '1')
+ return dtype.coerce(space, w_object)
+
+ shape, elems_w = _find_shape_and_elems(space, w_object)
dtype = find_dtype_for_seq(space, elems_w, None)
- if dtype is None:
- dtype = descriptor.get_dtype_cache(space).w_float64dtype
- elif dtype.is_str_or_unicode() and dtype.elsize < 1:
- # promote S0 -> S1, U0 -> U1
- dtype = descriptor.variable_dtype(space, dtype.char + '1')
+ w_arr = W_NDimArray.from_shape(space, shape, dtype)
+ loop.assign(space, w_arr, elems_w)
+ return w_arr
- if len(elems_w) == 1:
- return dtype.coerce(space, elems_w[0])
+
+def find_shape_and_elems(space, w_iterable, dtype):
+ if is_scalar_like(space, w_iterable, dtype):
+ return [], [w_iterable]
+ is_rec_type = dtype is not None and dtype.is_record()
+ return _find_shape_and_elems(space, w_iterable, is_rec_type)
+
+def is_scalar_like(space, w_obj, dtype):
+ isstr = space.isinstance_w(w_obj, space.w_str)
+ if not support.issequence_w(space, w_obj) or isstr:
+ if dtype is None or dtype.char != NPY.CHARLTR:
+ return True
+ is_rec_type = dtype is not None and dtype.is_record()
+ if is_rec_type and is_single_elem(space, w_obj, is_rec_type):
+ return True
+ if isinstance(w_obj, W_NDimArray) and w_obj.is_scalar():
+ return True
+ return False
+
+def _find_shape_and_elems(space, w_iterable, is_rec_type=False):
+ from pypy.objspace.std.bufferobject import W_Buffer
+ shape = [space.len_w(w_iterable)]
+ if space.isinstance_w(w_iterable, space.w_buffer):
+ batch = [space.wrap(0)] * shape[0]
+ for i in range(shape[0]):
+ batch[i] = space.ord(space.getitem(w_iterable, space.wrap(i)))
else:
- w_arr = W_NDimArray.from_shape(space, shape, dtype)
- loop.assign(space, w_arr, elems_w)
- return w_arr
+ batch = space.listview(w_iterable)
+ while True:
+ if not batch:
+ return shape[:], []
+ if is_single_elem(space, batch[0], is_rec_type):
+ for w_elem in batch:
+ if not is_single_elem(space, w_elem, is_rec_type):
+ raise OperationError(space.w_ValueError, space.wrap(
+ "setting an array element with a sequence"))
+ return shape[:], batch
+ new_batch = []
+ size = space.len_w(batch[0])
+ for w_elem in batch:
+ if (is_single_elem(space, w_elem, is_rec_type) or
+ space.len_w(w_elem) != size):
+ raise OperationError(space.w_ValueError, space.wrap(
+ "setting an array element with a sequence"))
+ w_array = space.lookup(w_elem, '__array__')
+ if w_array is not None:
+ # Make sure we call the array implementation of listview,
+ # since for some ndarray subclasses (matrix, for instance)
+ # listview does not reduce but rather returns the same class
+ w_elem = space.get_and_call_function(w_array, w_elem, space.w_None)
+ new_batch += space.listview(w_elem)
+ shape.append(size)
+ batch = new_batch
+
+def is_single_elem(space, w_elem, is_rec_type):
+ if (is_rec_type and space.isinstance_w(w_elem, space.w_tuple)):
+ return True
+ if (space.isinstance_w(w_elem, space.w_tuple) or
+ space.isinstance_w(w_elem, space.w_list)):
+ return False
+ if isinstance(w_elem, W_NDimArray) and not w_elem.is_scalar():
More information about the pypy-commit
mailing list