Author: Richard Plangger <planri...@gmail.com> Branch: new-jit-log Changeset: r85755:3c9d6acbc144 Date: 2016-07-18 16:29 +0200 http://bitbucket.org/pypy/pypy/changeset/3c9d6acbc144/
Log: merge default diff too long, truncating to 2000 out of 3103 lines diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py --- a/lib_pypy/_ctypes/basics.py +++ b/lib_pypy/_ctypes/basics.py @@ -199,10 +199,13 @@ return tp._alignmentofinstances() @builtinify -def byref(cdata): +def byref(cdata, offset=0): # "pointer" is imported at the end of this module to avoid circular # imports - return pointer(cdata) + ptr = pointer(cdata) + if offset != 0: + ptr._buffer[0] += offset + return ptr def cdata_from_address(self, address): # fix the address: turn it into as unsigned, in case it's a negative number diff --git a/lib_pypy/_pypy_winbase_build.py b/lib_pypy/_pypy_winbase_build.py new file mode 100644 --- /dev/null +++ b/lib_pypy/_pypy_winbase_build.py @@ -0,0 +1,91 @@ +# Note: uses the CFFI out-of-line ABI mode. We can't use the API +# mode because ffi.compile() needs to run the compiler, which +# needs 'subprocess', which needs 'msvcrt' and '_subprocess', +# which depend on '_pypy_winbase_cffi' already. +# +# Note that if you need to regenerate _pypy_winbase_cffi and +# can't use a preexisting PyPy to do that, then running this +# file should work as long as 'subprocess' is not imported +# by cffi. I had to hack in 'cffi._pycparser' to move an +#'import subprocess' to the inside of a function. (Also, +# CPython+CFFI should work as well.) +# +# This module supports both msvcrt.py and _subprocess.py. + +from cffi import FFI + +ffi = FFI() + +ffi.set_source("_pypy_winbase_cffi", None) + +# ---------- MSVCRT ---------- + +ffi.cdef(""" +typedef unsigned short wint_t; + +int _open_osfhandle(intptr_t osfhandle, int flags); +intptr_t _get_osfhandle(int fd); +int _setmode(int fd, int mode); +int _locking(int fd, int mode, long nbytes); + +int _kbhit(void); +int _getch(void); +wint_t _getwch(void); +int _getche(void); +wint_t _getwche(void); +int _putch(int); +wint_t _putwch(wchar_t); +int _ungetch(int); +wint_t _ungetwch(wint_t); +""") + +# ---------- SUBPROCESS ---------- + +ffi.cdef(""" +typedef struct { + DWORD cb; + char * lpReserved; + char * lpDesktop; + char * lpTitle; + DWORD dwX; + DWORD dwY; + DWORD dwXSize; + DWORD dwYSize; + DWORD dwXCountChars; + DWORD dwYCountChars; + DWORD dwFillAttribute; + DWORD dwFlags; + WORD wShowWindow; + WORD cbReserved2; + LPBYTE lpReserved2; + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; +} STARTUPINFO, *LPSTARTUPINFO; + +typedef struct { + HANDLE hProcess; + HANDLE hThread; + DWORD dwProcessId; + DWORD dwThreadId; +} PROCESS_INFORMATION, *LPPROCESS_INFORMATION; + +DWORD WINAPI GetVersion(void); +BOOL WINAPI CreatePipe(PHANDLE, PHANDLE, void *, DWORD); +BOOL WINAPI CloseHandle(HANDLE); +HANDLE WINAPI GetCurrentProcess(void); +BOOL WINAPI DuplicateHandle(HANDLE, HANDLE, HANDLE, LPHANDLE, + DWORD, BOOL, DWORD); +BOOL WINAPI CreateProcessA(char *, char *, void *, + void *, BOOL, DWORD, char *, + char *, LPSTARTUPINFO, LPPROCESS_INFORMATION); +DWORD WINAPI WaitForSingleObject(HANDLE, DWORD); +BOOL WINAPI GetExitCodeProcess(HANDLE, LPDWORD); +BOOL WINAPI TerminateProcess(HANDLE, UINT); +HANDLE WINAPI GetStdHandle(DWORD); +""") + +# -------------------- + +if __name__ == "__main__": + ffi.compile() diff --git a/lib_pypy/_pypy_winbase_cffi.py b/lib_pypy/_pypy_winbase_cffi.py new file mode 100644 --- /dev/null +++ b/lib_pypy/_pypy_winbase_cffi.py @@ -0,0 +1,10 @@ +# auto-generated file +import _cffi_backend + +ffi = _cffi_backend.FFI('_pypy_winbase_cffi', + _version = 0x2601, + _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x50\x03\x00\x00\x13\x11\x00\x00\x53\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\x4F\x03\x00\x00\x4E\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x18\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x 00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x42\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x42\x0D\x00\x00\x00\x0F\x00\x00\x42\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x00\x09\x00\x00\x01\x09\x00\x00\x02\x01\x00\x00\x52\x03\x00\x00\x04\x01\x00\x00\x00\x01', + _globals = (b'\x00\x00\x24\x23CloseHandle',0,b'\x00\x00\x1E\x23CreatePipe',0,b'\x00\x00\x12\x23CreateProcessA',0,b'\x00\x00\x2F\x23DuplicateHandle',0,b'\x00\x00\x4C\x23GetCurrentProcess',0,b'\x00\x00\x2B\x23GetExitCodeProcess',0,b'\x00\x00\x49\x23GetStdHandle',0,b'\x00\x00\x3F\x23GetVersion',0,b'\x00\x00\x27\x23TerminateProcess',0,b'\x00\x00\x3B\x23WaitForSingleObject',0,b'\x00\x00\x38\x23_get_osfhandle',0,b'\x00\x00\x10\x23_getch',0,b'\x00\x00\x10\x23_getche',0,b'\x00\x00\x44\x23_getwch',0,b'\x00\x00\x44\x23_getwche',0,b'\x00\x00\x10\x23_kbhit',0,b'\x00\x00\x07\x23_locking',0,b'\x00\x00\x0C\x23_open_osfhandle',0,b'\x00\x00\x00\x23_putch',0,b'\x00\x00\x46\x23_putwch',0,b'\x00\x00\x03\x23_setmode',0,b'\x00\x00\x00\x23_ungetch',0,b'\x00\x00\x41\x23_ungetwch',0), + _struct_unions = ((b'\x00\x00\x00\x4E\x00\x00\x00\x02$PROCESS_INFORMATION',b'\x00\x00\x15\x11hProcess',b'\x00\x00\x15\x11hThread',b'\x00\x00\x18\x11dwProcessId',b'\x00\x00\x18\x11dwThreadId'),(b'\x00\x00\x00\x4F\x00\x00\x00\x02$STARTUPINFO',b'\x00\x00\x18\x11cb',b'\x00\x00\x13\x11lpReserved',b'\x00\x00\x13\x11lpDesktop',b'\x00\x00\x13\x11lpTitle',b'\x00\x00\x18\x11dwX',b'\x00\x00\x18\x11dwY',b'\x00\x00\x18\x11dwXSize',b'\x00\x00\x18\x11dwYSize',b'\x00\x00\x18\x11dwXCountChars',b'\x00\x00\x18\x11dwYCountChars',b'\x00\x00\x18\x11dwFillAttribute',b'\x00\x00\x18\x11dwFlags',b'\x00\x00\x42\x11wShowWindow',b'\x00\x00\x42\x11cbReserved2',b'\x00\x00\x51\x11lpReserved2',b'\x00\x00\x15\x11hStdInput',b'\x00\x00\x15\x11hStdOutput',b'\x00\x00\x15\x11hStdError')), + _typenames = (b'\x00\x00\x00\x1CLPPROCESS_INFORMATION',b'\x00\x00\x00\x1BLPSTARTUPINFO',b'\x00\x00\x00\x4EPROCESS_INFORMATION',b'\x00\x00\x00\x4FSTARTUPINFO',b'\x00\x00\x00\x42wint_t'), +) diff --git a/lib_pypy/_subprocess.py b/lib_pypy/_subprocess.py --- a/lib_pypy/_subprocess.py +++ b/lib_pypy/_subprocess.py @@ -10,148 +10,99 @@ # Declare external Win32 functions -import ctypes - -_kernel32 = ctypes.WinDLL('kernel32') - -_CloseHandle = _kernel32.CloseHandle -_CloseHandle.argtypes = [ctypes.c_int] -_CloseHandle.restype = ctypes.c_int - -_CreatePipe = _kernel32.CreatePipe -_CreatePipe.argtypes = [ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), - ctypes.c_void_p, ctypes.c_int] -_CreatePipe.restype = ctypes.c_int - -_GetCurrentProcess = _kernel32.GetCurrentProcess -_GetCurrentProcess.argtypes = [] -_GetCurrentProcess.restype = ctypes.c_int +from _pypy_winbase_cffi import ffi as _ffi +_kernel32 = _ffi.dlopen('kernel32') GetVersion = _kernel32.GetVersion -GetVersion.argtypes = [] -GetVersion.restype = ctypes.c_int -_DuplicateHandle = _kernel32.DuplicateHandle -_DuplicateHandle.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int, - ctypes.POINTER(ctypes.c_int), - ctypes.c_int, ctypes.c_int, ctypes.c_int] -_DuplicateHandle.restype = ctypes.c_int - -_WaitForSingleObject = _kernel32.WaitForSingleObject -_WaitForSingleObject.argtypes = [ctypes.c_int, ctypes.c_uint] -_WaitForSingleObject.restype = ctypes.c_int - -_GetExitCodeProcess = _kernel32.GetExitCodeProcess -_GetExitCodeProcess.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_int)] -_GetExitCodeProcess.restype = ctypes.c_int - -_TerminateProcess = _kernel32.TerminateProcess -_TerminateProcess.argtypes = [ctypes.c_int, ctypes.c_int] -_TerminateProcess.restype = ctypes.c_int - -_GetStdHandle = _kernel32.GetStdHandle -_GetStdHandle.argtypes = [ctypes.c_int] -_GetStdHandle.restype = ctypes.c_int - -class _STARTUPINFO(ctypes.Structure): - _fields_ = [('cb', ctypes.c_int), - ('lpReserved', ctypes.c_void_p), - ('lpDesktop', ctypes.c_char_p), - ('lpTitle', ctypes.c_char_p), - ('dwX', ctypes.c_int), - ('dwY', ctypes.c_int), - ('dwXSize', ctypes.c_int), - ('dwYSize', ctypes.c_int), - ('dwXCountChars', ctypes.c_int), - ('dwYCountChars', ctypes.c_int), - ("dwFillAttribute", ctypes.c_int), - ("dwFlags", ctypes.c_int), - ("wShowWindow", ctypes.c_short), - ("cbReserved2", ctypes.c_short), - ("lpReserved2", ctypes.c_void_p), - ("hStdInput", ctypes.c_int), - ("hStdOutput", ctypes.c_int), - ("hStdError", ctypes.c_int) - ] - -class _PROCESS_INFORMATION(ctypes.Structure): - _fields_ = [("hProcess", ctypes.c_int), - ("hThread", ctypes.c_int), - ("dwProcessID", ctypes.c_int), - ("dwThreadID", ctypes.c_int)] - -_CreateProcess = _kernel32.CreateProcessA -_CreateProcess.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p, ctypes.c_void_p, - ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p, - ctypes.POINTER(_STARTUPINFO), ctypes.POINTER(_PROCESS_INFORMATION)] -_CreateProcess.restype = ctypes.c_int - -del ctypes # Now the _subprocess module implementation -from ctypes import c_int as _c_int, byref as _byref, WinError as _WinError +def _WinError(): + code, message = _ffi.getwinerror() + raise WindowsError(code, message) -class _handle: - def __init__(self, handle): - self.handle = handle +_INVALID_HANDLE_VALUE = _ffi.cast("HANDLE", -1) + +class _handle(object): + def __init__(self, c_handle): + # 'c_handle' is a cffi cdata of type HANDLE, which is basically 'void *' + self.c_handle = c_handle + if int(self) != -1: + self.c_handle = _ffi.gc(self.c_handle, _kernel32.CloseHandle) def __int__(self): - return self.handle + return int(_ffi.cast("intptr_t", self.c_handle)) - def __del__(self): - if self.handle is not None: - _CloseHandle(self.handle) + def __repr__(self): + return '<_subprocess.handle %d at 0x%x>' % (int(self), id(self)) def Detach(self): - handle, self.handle = self.handle, None - return handle + h = int(self) + if h != -1: + c_handle = self.c_handle + self.c_handle = _INVALID_HANDLE_VALUE + _ffi.gc(c_handle, None) + return h def Close(self): - if self.handle not in (-1, None): - _CloseHandle(self.handle) - self.handle = None + if int(self) != -1: + c_handle = self.c_handle + self.c_handle = _INVALID_HANDLE_VALUE + _ffi.gc(c_handle, None) + _kernel32.CloseHandle(c_handle) def CreatePipe(attributes, size): - read = _c_int() - write = _c_int() + handles = _ffi.new("HANDLE[2]") - res = _CreatePipe(_byref(read), _byref(write), None, size) + res = _kernel32.CreatePipe(handles, handles + 1, _ffi.NULL, size) if not res: raise _WinError() - return _handle(read.value), _handle(write.value) + return _handle(handles[0]), _handle(handles[1]) def GetCurrentProcess(): - return _handle(_GetCurrentProcess()) + return _handle(_kernel32.GetCurrentProcess()) def DuplicateHandle(source_process, source, target_process, access, inherit, options=0): - target = _c_int() + # CPython: the first three arguments are expected to be integers + target = _ffi.new("HANDLE[1]") - res = _DuplicateHandle(int(source_process), int(source), int(target_process), - _byref(target), - access, inherit, options) + res = _kernel32.DuplicateHandle( + _ffi.cast("HANDLE", source_process), + _ffi.cast("HANDLE", source), + _ffi.cast("HANDLE", target_process), + target, access, inherit, options) if not res: raise _WinError() - return _handle(target.value) + return _handle(target[0]) + +def _z(input): + if input is None: + return _ffi.NULL + if isinstance(input, basestring): + return str(input) + raise TypeError("string/unicode/None expected, got %r" % ( + type(input).__name__,)) def CreateProcess(name, command_line, process_attr, thread_attr, inherit, flags, env, start_dir, startup_info): - si = _STARTUPINFO() + si = _ffi.new("STARTUPINFO *") if startup_info is not None: si.dwFlags = startup_info.dwFlags si.wShowWindow = startup_info.wShowWindow + # CPython: these three handles are expected to be _handle objects if startup_info.hStdInput: - si.hStdInput = int(startup_info.hStdInput) + si.hStdInput = startup_info.hStdInput.c_handle if startup_info.hStdOutput: - si.hStdOutput = int(startup_info.hStdOutput) + si.hStdOutput = startup_info.hStdOutput.c_handle if startup_info.hStdError: - si.hStdError = int(startup_info.hStdError) + si.hStdError = startup_info.hStdError.c_handle - pi = _PROCESS_INFORMATION() + pi = _ffi.new("PROCESS_INFORMATION *") if env is not None: envbuf = "" @@ -159,47 +110,55 @@ envbuf += "%s=%s\0" % (k, v) envbuf += '\0' else: - envbuf = None + envbuf = _ffi.NULL - res = _CreateProcess(name, command_line, None, None, inherit, flags, envbuf, - start_dir, _byref(si), _byref(pi)) + res = _kernel32.CreateProcessA(_z(name), _z(command_line), _ffi.NULL, + _ffi.NULL, inherit, flags, envbuf, + _z(start_dir), si, pi) if not res: raise _WinError() - return _handle(pi.hProcess), _handle(pi.hThread), pi.dwProcessID, pi.dwThreadID + return _handle(pi.hProcess), _handle(pi.hThread), pi.dwProcessId, pi.dwThreadId def WaitForSingleObject(handle, milliseconds): - res = _WaitForSingleObject(int(handle), milliseconds) - + # CPython: the first argument is expected to be an integer. + res = _kernel32.WaitForSingleObject(_ffi.cast("HANDLE", handle), + milliseconds) if res < 0: raise _WinError() return res def GetExitCodeProcess(handle): - code = _c_int() + # CPython: the first argument is expected to be an integer. + code = _ffi.new("DWORD[1]") - res = _GetExitCodeProcess(int(handle), _byref(code)) + res = _kernel32.GetExitCodeProcess(_ffi.cast("HANDLE", handle), code) if not res: raise _WinError() - return code.value + return code[0] def TerminateProcess(handle, exitcode): - res = _TerminateProcess(int(handle), exitcode) + # CPython: the first argument is expected to be an integer. + # The second argument is silently wrapped in a UINT. + res = _kernel32.TerminateProcess(_ffi.cast("HANDLE", handle), + _ffi.cast("UINT", exitcode)) if not res: raise _WinError() def GetStdHandle(stdhandle): - res = _GetStdHandle(stdhandle) + stdhandle = _ffi.cast("DWORD", stdhandle) + res = _kernel32.GetStdHandle(stdhandle) if not res: return None else: - return res + # note: returns integer, not handle object + return int(_ffi.cast("intptr_t", res)) STD_INPUT_HANDLE = -10 STD_OUTPUT_HANDLE = -11 diff --git a/lib_pypy/cffi/_pycparser/__init__.py b/lib_pypy/cffi/_pycparser/__init__.py --- a/lib_pypy/cffi/_pycparser/__init__.py +++ b/lib_pypy/cffi/_pycparser/__init__.py @@ -10,7 +10,6 @@ __all__ = ['c_lexer', 'c_parser', 'c_ast'] __version__ = '2.14' -from subprocess import Popen, PIPE from .c_parser import CParser @@ -28,6 +27,7 @@ When successful, returns the preprocessed file's contents. Errors from cpp will be printed out. """ + from subprocess import Popen, PIPE path_list = [cpp_path] if isinstance(cpp_args, list): path_list += cpp_args diff --git a/lib_pypy/msvcrt.py b/lib_pypy/msvcrt.py --- a/lib_pypy/msvcrt.py +++ b/lib_pypy/msvcrt.py @@ -7,26 +7,39 @@ # XXX incomplete: implemented only functions needed by subprocess.py # PAC: 2010/08 added MS locking for Whoosh -import ctypes +# 07/2016: rewrote in CFFI + +import sys +if sys.platform != 'win32': + raise ImportError("The 'msvcrt' module is only available on Windows") + +import _rawffi +from _pypy_winbase_cffi import ffi as _ffi +_lib = _ffi.dlopen(_rawffi.get_libc().name) + import errno -from ctypes_support import standard_c_lib as _c -from ctypes_support import get_errno - -try: - open_osfhandle = _c._open_osfhandle -except AttributeError: # we are not on windows - raise ImportError try: from __pypy__ import builtinify, validate_fd except ImportError: builtinify = validate_fd = lambda f: f -open_osfhandle.argtypes = [ctypes.c_int, ctypes.c_int] -open_osfhandle.restype = ctypes.c_int +def _ioerr(): + e = _ffi.errno + raise IOError(e, errno.errorcode[e]) -_get_osfhandle = _c._get_osfhandle -_get_osfhandle.argtypes = [ctypes.c_int] -_get_osfhandle.restype = ctypes.c_int + +@builtinify +def open_osfhandle(fd, flags): + """"open_osfhandle(handle, flags) -> file descriptor + + Create a C runtime file descriptor from the file handle handle. The + flags parameter should be a bitwise OR of os.O_APPEND, os.O_RDONLY, + and os.O_TEXT. The returned file descriptor may be used as a parameter + to os.fdopen() to create a file object.""" + fd = _lib._open_osfhandle(fd, flags) + if fd == -1: + _ioerr() + return fd @builtinify def get_osfhandle(fd): @@ -38,62 +51,74 @@ validate_fd(fd) except OSError as e: raise IOError(*e.args) - return _get_osfhandle(fd) + result = _lib._get_osfhandle(fd) + if result == -1: + _ioerr() + return result -setmode = _c._setmode -setmode.argtypes = [ctypes.c_int, ctypes.c_int] -setmode.restype = ctypes.c_int +@builtinify +def setmode(fd, flags): + """setmode(fd, mode) -> Previous mode + + Set the line-end translation mode for the file descriptor fd. To set + it to text mode, flags should be os.O_TEXT; for binary, it should be + os.O_BINARY.""" + flags = _lib._setmode(fd, flags) + if flags == -1: + _ioerr() + return flags LK_UNLCK, LK_LOCK, LK_NBLCK, LK_RLCK, LK_NBRLCK = range(5) -_locking = _c._locking -_locking.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int] -_locking.restype = ctypes.c_int - @builtinify def locking(fd, mode, nbytes): - '''lock or unlock a number of bytes in a file.''' - rv = _locking(fd, mode, nbytes) + """"locking(fd, mode, nbytes) -> None + + Lock part of a file based on file descriptor fd from the C runtime. + Raises IOError on failure. The locked region of the file extends from + the current file position for nbytes bytes, and may continue beyond + the end of the file. mode must be one of the LK_* constants listed + below. Multiple regions in a file may be locked at the same time, but + may not overlap. Adjacent regions are not merged; they must be unlocked + individually.""" + rv = _lib._locking(fd, mode, nbytes) if rv != 0: - e = get_errno() - raise IOError(e, errno.errorcode[e]) + _ioerr() # Console I/O routines -kbhit = _c._kbhit -kbhit.argtypes = [] -kbhit.restype = ctypes.c_int +kbhit = _lib._kbhit -getch = _c._getch -getch.argtypes = [] -getch.restype = ctypes.c_char +@builtinify +def getch(): + return chr(_lib._getch()) -getwch = _c._getwch -getwch.argtypes = [] -getwch.restype = ctypes.c_wchar +@builtinify +def getwch(): + return unichr(_lib._getwch()) -getche = _c._getche -getche.argtypes = [] -getche.restype = ctypes.c_char +@builtinify +def getche(): + return chr(_lib._getche()) -getwche = _c._getwche -getwche.argtypes = [] -getwche.restype = ctypes.c_wchar +@builtinify +def getwche(): + return unichr(_lib._getwche()) -putch = _c._putch -putch.argtypes = [ctypes.c_char] -putch.restype = None +@builtinify +def putch(ch): + _lib._putch(ord(ch)) -putwch = _c._putwch -putwch.argtypes = [ctypes.c_wchar] -putwch.restype = None +@builtinify +def putwch(ch): + _lib._putwch(ord(ch)) -ungetch = _c._ungetch -ungetch.argtypes = [ctypes.c_char] -ungetch.restype = None +@builtinify +def ungetch(ch): + if _lib._ungetch(ord(ch)) == -1: # EOF + _ioerr() -ungetwch = _c._ungetwch -ungetwch.argtypes = [ctypes.c_wchar] -ungetwch.restype = None - -del ctypes +@builtinify +def ungetwch(ch): + if _lib._ungetwch(ord(ch)) == -1: # EOF + _ioerr() diff --git a/pypy/doc/faq.rst b/pypy/doc/faq.rst --- a/pypy/doc/faq.rst +++ b/pypy/doc/faq.rst @@ -335,3 +335,60 @@ This will disable SELinux's protection and allow PyPy to configure correctly. Be sure to enable it again if you need it! + + +How should I report a bug? +-------------------------- + +Our bug tracker is here: https://bitbucket.org/pypy/pypy/issues/ + +Missing features or incompatibilities with CPython are considered +bugs, and they are welcome. (See also our list of `known +incompatibilities`__.) + +.. __: http://pypy.org/compat.html + +For bugs of the kind "I'm getting a PyPy crash or a strange +exception", please note that: **We can't do anything without +reproducing the bug ourselves**. We cannot do anything with +tracebacks from gdb, or core dumps. This is not only because the +standard PyPy is compiled without debug symbols. The real reason is +that a C-level traceback is usually of no help at all in PyPy. +Debugging PyPy can be annoying. + +In more details: + +* First, please give the exact PyPy version, and the OS. + +* It might help focus our search if we know if the bug can be + reproduced on a "``pypy --jit off``" or not. If "``pypy --jit + off``" always works, then the problem might be in the JIT. + Otherwise, we know we can ignore that part. + +* If you got the bug using only Open Source components, please give a + step-by-step guide that we can follow to reproduce the problem + ourselves. Don't assume we know anything about any program other + than PyPy. We would like a guide that we can follow point by point + (without guessing or having to figure things out) + on a machine similar to yours, starting from a bare PyPy, until we + see the same problem. (If you can, you can try to reduce the number + of steps and the time it needs to run, but that is not mandatory.) + +* If the bug involves Closed Source components, or just too many Open + Source components to install them all ourselves, then maybe you can + give us some temporary ssh access to a machine where the bug can be + reproduced. Or, maybe we can download a VirtualBox or VMWare + virtual machine where the problem occurs. + +* If giving us access would require us to use tools other than ssh, + make appointments, or sign a NDA, then we can consider a commerical + support contract for a small sum of money. + +* If even that is not possible for you, then sorry, we can't help. + +Of course, you can try to debug the problem yourself, and we can help +you get started if you ask on the #pypy IRC channel, but be prepared: +debugging an annoying PyPy problem usually involves quite a lot of gdb +in auto-generated C code, and at least some knowledge about the +various components involved, from PyPy's own RPython source code to +the GC and possibly the JIT. 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 @@ -53,3 +53,43 @@ Refactor PyTupleObject to look like cpython's and allow subclassing PyTuple_Type + +.. branch: call-via-pyobj + +Use offsets from PyTypeObject to find actual c function to call rather than +fixed functions, allows function override after PyType_Ready is called + +.. branch: issue2335 + +Avoid exhausting the stack in the JIT due to successive guard +failures in the same Python function ending up as successive levels of +RPython functions, while at app-level the traceback is very short + +.. branch: use-madv-free + +Try harder to memory to the OS. See e.g. issue #2336. Note that it does +not show up as a reduction of the VIRT column in ``top``, and the RES +column might also not show the reduction, particularly on Linux >= 4.5 or +on OS/X: it uses MADV_FREE, which only marks the pages as returnable to +the OS if the memory is low. + +.. branch: cpyext-slotdefs2 + +Fill in more slots when creating a PyTypeObject from a W_TypeObject +More slots are still TBD, like tp_print and richcmp + +.. branch: json-surrogates + +Align json module decode with the cpython's impl, fixes issue 2345 + +.. branch: issue2343 + +Copy CPython's logic more closely for handling of ``__instancecheck__()`` +and ``__subclasscheck__()``. Fixes issue 2343. + +.. branch: msvcrt-cffi + +Rewrite the Win32 dependencies of 'subprocess' to use cffi instead +of ctypes. This avoids importing ctypes in many small programs and +scripts, which in turn avoids enabling threads (because ctypes +creates callbacks at import time, and callbacks need threads). diff --git a/pypy/module/__builtin__/abstractinst.py b/pypy/module/__builtin__/abstractinst.py --- a/pypy/module/__builtin__/abstractinst.py +++ b/pypy/module/__builtin__/abstractinst.py @@ -46,9 +46,65 @@ raise # propagate other errors return space.type(w_obj) + +# ---------- isinstance ---------- + + +def p_recursive_isinstance_w(space, w_inst, w_cls): + # Copied straight from CPython 2.7. Does not handle 'cls' being a tuple. + if (isinstance(w_cls, W_ClassObject) and + isinstance(w_inst, W_InstanceObject)): + return w_inst.w_class.is_subclass_of(w_cls) + + if space.isinstance_w(w_cls, space.w_type): + return p_recursive_isinstance_type_w(space, w_inst, w_cls) + + check_class(space, w_cls, "isinstance() arg 2 must be a class, type," + " or tuple of classes and types") + try: + w_abstractclass = space.getattr(w_inst, space.wrap('__class__')) + except OperationError as e: + if e.async(space): # ignore most exceptions + raise + return False + else: + return p_abstract_issubclass_w(space, w_abstractclass, w_cls) + + +def p_recursive_isinstance_type_w(space, w_inst, w_type): + # subfunctionality of p_recursive_isinstance_w(): assumes that w_type is + # a type object. Copied straight from CPython 2.7. + if space.isinstance_w(w_inst, w_type): + return True + try: + w_abstractclass = space.getattr(w_inst, space.wrap('__class__')) + except OperationError as e: + if e.async(space): # ignore most exceptions + raise + else: + if w_abstractclass is not space.type(w_inst): + if space.isinstance_w(w_abstractclass, space.w_type): + return space.issubtype_w(w_abstractclass, w_type) + return False + + @jit.unroll_safe def abstract_isinstance_w(space, w_obj, w_klass_or_tuple, allow_override=False): """Implementation for the full 'isinstance(obj, klass_or_tuple)'.""" + # Copied from CPython 2.7's PyObject_Isinstance(). Additionally, + # if 'allow_override' is False (the default), then don't try to + # use a custom __instancecheck__ method. + + # WARNING: backward compatibility function name here. CPython + # uses the name "abstract" to refer to the logic of handling + # class-like objects, with a "__bases__" attribute. This function + # here is not related to that and implements the full + # PyObject_IsInstance() logic. + + # Quick test for an exact match + if space.type(w_obj) is w_klass_or_tuple: + return True + # -- case (anything, tuple) # XXX it might be risky that the JIT sees this if space.isinstance_w(w_klass_or_tuple, space.w_tuple): @@ -58,68 +114,59 @@ return False # -- case (anything, type) - try: - if allow_override: - w_result = space.isinstance_allow_override(w_obj, w_klass_or_tuple) - else: - w_result = space.isinstance(w_obj, w_klass_or_tuple) - except OperationError as e: # if w_klass_or_tuple was not a type, ignore it - if not e.match(space, space.w_TypeError): - raise # propagate other errors - else: - if space.is_true(w_result): - return True - # From now on we know that w_klass_or_tuple is indeed a type. - # Try also to compare it with obj.__class__, if this is not - # the same as type(obj). - try: - w_pretendtype = space.getattr(w_obj, space.wrap('__class__')) - if space.is_w(w_pretendtype, space.type(w_obj)): - return False # common case: obj.__class__ is type(obj) - if not allow_override: - return space.issubtype_w(w_pretendtype, w_klass_or_tuple) - w_result = space.issubtype_allow_override(w_pretendtype, - w_klass_or_tuple) - except OperationError as e: - if e.async(space): - raise - return False # ignore most exceptions - else: - return space.is_true(w_result) + if allow_override: + w_check = space.lookup(w_klass_or_tuple, "__instancecheck__") + if w_check is not None: + # this is the common case: all type objects have a method + # __instancecheck__. The one in the base 'type' type calls + # back p_recursive_isinstance_type_w() from the present module. + return space.is_true(space.get_and_call_function( + w_check, w_klass_or_tuple, w_obj)) - # -- case (old-style instance, old-style class) - if isinstance(w_klass_or_tuple, W_ClassObject): - if isinstance(w_obj, W_InstanceObject): - return w_obj.w_class.is_subclass_of(w_klass_or_tuple) - return _abstract_isinstance_w_helper(space, w_obj, w_klass_or_tuple) + return p_recursive_isinstance_w(space, w_obj, w_klass_or_tuple) -def _abstract_isinstance_w_helper(space, w_obj, w_klass_or_tuple): - # -- case (anything, abstract-class) - check_class(space, w_klass_or_tuple, - "isinstance() arg 2 must be a class, type," - " or tuple of classes and types") - try: - w_abstractclass = space.getattr(w_obj, space.wrap('__class__')) - except OperationError as e: - if e.async(space): # ignore most exceptions - raise - return False - else: - return _issubclass_recurse(space, w_abstractclass, w_klass_or_tuple) +# ---------- issubclass ---------- @jit.unroll_safe -def _issubclass_recurse(space, w_derived, w_top): - """Internal helper for abstract cases. Here, w_top cannot be a tuple.""" - if space.is_w(w_derived, w_top): - return True - w_bases = _get_bases(space, w_derived) - if w_bases is not None: - for w_base in space.fixedview(w_bases): - if _issubclass_recurse(space, w_base, w_top): +def p_abstract_issubclass_w(space, w_derived, w_cls): + # Copied straight from CPython 2.7, function abstract_issubclass(). + # Don't confuse this with the function abstract_issubclass_w() below. + # Here, w_cls cannot be a tuple. + while True: + if space.is_w(w_derived, w_cls): + return True + w_bases = _get_bases(space, w_derived) + if w_bases is None: + return False + bases_w = space.fixedview(w_bases) + last_index = len(bases_w) - 1 + if last_index < 0: + return False + # Avoid recursivity in the single inheritance case; in general, + # don't recurse on the last item in the tuple (loop instead). + for i in range(last_index): + if p_abstract_issubclass_w(space, bases_w[i], w_cls): return True - return False + w_derived = bases_w[last_index] + + +def p_recursive_issubclass_w(space, w_derived, w_cls): + # From CPython's function of the same name (which as far as I can tell + # is not recursive). Copied straight from CPython 2.7. + if (space.isinstance_w(w_cls, space.w_type) and + space.isinstance_w(w_derived, space.w_type)): + return space.issubtype_w(w_derived, w_cls) + # + if (isinstance(w_derived, W_ClassObject) and + isinstance(w_cls, W_ClassObject)): + return w_derived.is_subclass_of(w_cls) + # + check_class(space, w_derived, "issubclass() arg 1 must be a class") + check_class(space, w_cls, "issubclass() arg 2 must be a class" + " or tuple of classes") + return p_abstract_issubclass_w(space, w_derived, w_cls) @jit.unroll_safe @@ -127,37 +174,31 @@ allow_override=False): """Implementation for the full 'issubclass(derived, klass_or_tuple)'.""" - # -- case (class-like-object, tuple-of-classes) + # WARNING: backward compatibility function name here. CPython + # uses the name "abstract" to refer to the logic of handling + # class-like objects, with a "__bases__" attribute. This function + # here is not related to that and implements the full + # PyObject_IsSubclass() logic. There is also p_abstract_issubclass_w(). + + # -- case (anything, tuple-of-classes) if space.isinstance_w(w_klass_or_tuple, space.w_tuple): for w_klass in space.fixedview(w_klass_or_tuple): if abstract_issubclass_w(space, w_derived, w_klass, allow_override): return True return False - # -- case (type, type) - try: - if not allow_override: - return space.issubtype_w(w_derived, w_klass_or_tuple) - w_result = space.issubtype_allow_override(w_derived, w_klass_or_tuple) - except OperationError as e: # if one of the args was not a type, ignore it - if not e.match(space, space.w_TypeError): - raise # propagate other errors - else: - return space.is_true(w_result) + # -- case (anything, type) + if allow_override: + w_check = space.lookup(w_klass_or_tuple, "__subclasscheck__") + if w_check is not None: + # this is the common case: all type objects have a method + # __subclasscheck__. The one in the base 'type' type calls + # back p_recursive_issubclass_w() from the present module. + return space.is_true(space.get_and_call_function( + w_check, w_klass_or_tuple, w_derived)) - # -- case (old-style class, old-style class) - if isinstance(w_derived, W_ClassObject): - if isinstance(w_klass_or_tuple, W_ClassObject): - return w_derived.is_subclass_of(w_klass_or_tuple) - else: - check_class(space, w_derived, "issubclass() arg 1 must be a class") - # from here on, we are sure that w_derived is a class-like object + return p_recursive_issubclass_w(space, w_derived, w_klass_or_tuple) - # -- case (class-like-object, abstract-class) - check_class(space, w_klass_or_tuple, - "issubclass() arg 2 must be a class, type," - " or tuple of classes and types") - return _issubclass_recurse(space, w_derived, w_klass_or_tuple) # ------------------------------------------------------------ # Exception helpers diff --git a/pypy/module/__builtin__/test/test_abstractinst.py b/pypy/module/__builtin__/test/test_abstractinst.py --- a/pypy/module/__builtin__/test/test_abstractinst.py +++ b/pypy/module/__builtin__/test/test_abstractinst.py @@ -216,3 +216,26 @@ c = C() assert isinstance(c, C) assert not called + + def test_instancecheck_exception_not_eaten(self): + class M(object): + def __instancecheck__(self, obj): + raise TypeError("foobar") + + e = raises(TypeError, isinstance, 42, M()) + assert str(e.value) == "foobar" + + def test_issubclass_exception_not_eaten(self): + class M(object): + def __subclasscheck__(self, subcls): + raise TypeError("foobar") + + e = raises(TypeError, issubclass, 42, M()) + assert str(e.value) == "foobar" + + def test_issubclass_no_fallback(self): + class M(object): + def __subclasscheck__(self, subcls): + return False + + assert issubclass(42, M()) is False diff --git a/pypy/module/_cffi_backend/ctypestruct.py b/pypy/module/_cffi_backend/ctypestruct.py --- a/pypy/module/_cffi_backend/ctypestruct.py +++ b/pypy/module/_cffi_backend/ctypestruct.py @@ -90,7 +90,7 @@ self.force_lazy_struct() space = self.space try: - cfield = self._fields_dict[fieldname] + cfield = self._getcfield_const(fieldname) except KeyError: raise OperationError(space.w_KeyError, space.wrap(fieldname)) if cfield.bitshift >= 0: diff --git a/pypy/module/_pypyjson/interp_decoder.py b/pypy/module/_pypyjson/interp_decoder.py --- a/pypy/module/_pypyjson/interp_decoder.py +++ b/pypy/module/_pypyjson/interp_decoder.py @@ -360,10 +360,11 @@ hexdigits = self.getslice(start, i) try: val = int(hexdigits, 16) - if val & 0xfc00 == 0xd800: + if sys.maxunicode > 65535 and 0xd800 <= val <= 0xdfff: # surrogate pair - val = self.decode_surrogate_pair(i, val) - i += 6 + if self.ll_chars[i] == '\\' and self.ll_chars[i+1] == 'u': + val = self.decode_surrogate_pair(i, val) + i += 6 except ValueError: self._raise("Invalid \uXXXX escape (char %d)", i-1) return # help the annotator to know that we'll never go beyond @@ -375,8 +376,9 @@ return i def decode_surrogate_pair(self, i, highsurr): - if self.ll_chars[i] != '\\' or self.ll_chars[i+1] != 'u': - self._raise("Unpaired high surrogate at char %d", i) + """ uppon enter the following must hold: + chars[i] == "\\" and chars[i+1] == "u" + """ i += 2 hexdigits = self.getslice(i, i+4) lowsurr = int(hexdigits, 16) # the possible ValueError is caugth by the caller diff --git a/pypy/module/_pypyjson/test/test__pypyjson.py b/pypy/module/_pypyjson/test/test__pypyjson.py --- a/pypy/module/_pypyjson/test/test__pypyjson.py +++ b/pypy/module/_pypyjson/test/test__pypyjson.py @@ -184,6 +184,12 @@ res = _pypyjson.loads('"z\\ud834\\udd20x"') assert res == expected + def test_surrogate_pair(self): + import _pypyjson + json = '{"a":"\\uD83D"}' + res = _pypyjson.loads(json) + assert res == {u'a': u'\ud83d'} + def test_tab_in_string_should_fail(self): import _pypyjson # http://json.org/JSON_checker/test/fail25.json diff --git a/pypy/module/_rawffi/interp_rawffi.py b/pypy/module/_rawffi/interp_rawffi.py --- a/pypy/module/_rawffi/interp_rawffi.py +++ b/pypy/module/_rawffi/interp_rawffi.py @@ -2,6 +2,7 @@ from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt, wrap_oserror from pypy.interpreter.gateway import interp2app, unwrap_spec +from pypy.interpreter.typedef import interp_attrproperty from pypy.interpreter.typedef import TypeDef, GetSetProperty from rpython.rlib.clibffi import * @@ -237,6 +238,7 @@ __new__ = interp2app(descr_new_cdll), ptr = interp2app(W_CDLL.ptr), getaddressindll = interp2app(W_CDLL.getaddressindll), + name = interp_attrproperty('name', W_CDLL), __doc__ = """ C Dynamically loaded library use CDLL(libname) to create a handle to a C library (the argument is processed the same way as dlopen processes it). On such a library you can call: diff --git a/pypy/module/_rawffi/test/test__rawffi.py b/pypy/module/_rawffi/test/test__rawffi.py --- a/pypy/module/_rawffi/test/test__rawffi.py +++ b/pypy/module/_rawffi/test/test__rawffi.py @@ -1223,6 +1223,11 @@ assert z == 43 arg.free() + def test_cdll_name(self): + import _rawffi + lib = _rawffi.CDLL(self.lib_name) + assert lib.name == self.lib_name + class AppTestAutoFree: spaceconfig = dict(usemodules=['_rawffi', 'struct']) diff --git a/pypy/module/cpyext/include/object.h b/pypy/module/cpyext/include/object.h --- a/pypy/module/cpyext/include/object.h +++ b/pypy/module/cpyext/include/object.h @@ -561,8 +561,10 @@ #define PyObject_TypeCheck(ob, tp) \ ((ob)->ob_type == (tp) || PyType_IsSubtype((ob)->ob_type, (tp))) -#define Py_TRASHCAN_SAFE_BEGIN(pyObj) -#define Py_TRASHCAN_SAFE_END(pyObj) +#define Py_TRASHCAN_SAFE_BEGIN(pyObj) do { +#define Py_TRASHCAN_SAFE_END(pyObj) ; } while(0); +/* note: the ";" at the start of Py_TRASHCAN_SAFE_END is needed + if the code has a label in front of the macro call */ /* Copied from CPython ----------------------------- */ PyAPI_FUNC(int) PyObject_AsReadBuffer(PyObject *, const void **, Py_ssize_t *); diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py --- a/pypy/module/cpyext/methodobject.py +++ b/pypy/module/cpyext/methodobject.py @@ -1,4 +1,4 @@ -from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rtyper.lltypesystem import lltype, rffi, llmemory from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt @@ -10,9 +10,10 @@ from pypy.module.cpyext.api import ( CONST_STRING, METH_CLASS, METH_COEXIST, METH_KEYWORDS, METH_NOARGS, METH_O, METH_STATIC, METH_VARARGS, PyObject, PyObjectFields, bootstrap_function, - build_type_checkers, cpython_api, cpython_struct, generic_cpy_call) + build_type_checkers, cpython_api, cpython_struct, generic_cpy_call, + PyTypeObjectPtr) from pypy.module.cpyext.pyobject import ( - Py_DecRef, from_ref, make_ref, make_typedescr) + Py_DecRef, from_ref, make_ref, as_pyobj, make_typedescr) PyCFunction_typedef = rffi.COpaquePtr(typedef='PyCFunction') PyCFunction = lltype.Ptr(lltype.FuncType([PyObject, PyObject], PyObject)) @@ -151,28 +152,45 @@ class W_PyCWrapperObject(W_Root): def __init__(self, space, pto, method_name, wrapper_func, - wrapper_func_kwds, doc, func): + wrapper_func_kwds, doc, func, offset=None): self.space = space self.method_name = method_name self.wrapper_func = wrapper_func self.wrapper_func_kwds = wrapper_func_kwds self.doc = doc self.func = func + self.offset = offset pyo = rffi.cast(PyObject, pto) w_type = from_ref(space, pyo) assert isinstance(w_type, W_TypeObject) self.w_objclass = w_type def call(self, space, w_self, w_args, w_kw): + func_to_call = self.func + if self.offset: + pto = as_pyobj(space, self.w_objclass) + # make ptr the equivalent of this, using the offsets + #func_to_call = rffi.cast(rffi.VOIDP, ptr.c_tp_as_number.c_nb_multiply) + if pto: + cptr = rffi.cast(rffi.CCHARP, pto) + for o in self.offset: + ptr = rffi.cast(rffi.VOIDPP, rffi.ptradd(cptr, o))[0] + cptr = rffi.cast(rffi.CCHARP, ptr) + func_to_call = rffi.cast(rffi.VOIDP, cptr) + else: + # Should never happen, assert to get a traceback + assert False, "failed to convert w_type %s to PyObject" % str( + self.w_objclass) + assert func_to_call if self.wrapper_func is None: assert self.wrapper_func_kwds is not None - return self.wrapper_func_kwds(space, w_self, w_args, self.func, + return self.wrapper_func_kwds(space, w_self, w_args, func_to_call, w_kw) if space.is_true(w_kw): raise oefmt(space.w_TypeError, "wrapper %s doesn't take any keyword arguments", self.method_name) - return self.wrapper_func(space, w_self, w_args, self.func) + return self.wrapper_func(space, w_self, w_args, func_to_call) def descr_method_repr(self): return self.space.wrap("<slot wrapper '%s' of '%s' objects>" % @@ -301,12 +319,6 @@ def PyDescr_NewClassMethod(space, w_type, method): return space.wrap(W_PyCClassMethodObject(space, method, w_type)) -def PyDescr_NewWrapper(space, pto, method_name, wrapper_func, - wrapper_func_kwds, doc, func): - # not exactly the API sig - return space.wrap(W_PyCWrapperObject(space, pto, method_name, - wrapper_func, wrapper_func_kwds, doc, func)) - @cpython_api([lltype.Ptr(PyMethodDef), PyObject, CONST_STRING], PyObject) def Py_FindMethod(space, table, w_obj, name_ptr): """Return a bound method object for an extension type implemented in diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py --- a/pypy/module/cpyext/slotdefs.py +++ b/pypy/module/cpyext/slotdefs.py @@ -380,6 +380,7 @@ ('tp_as_number.c_nb_absolute', '__abs__'), ('tp_as_number.c_nb_invert', '__invert__'), ('tp_as_number.c_nb_index', '__index__'), + ('tp_as_number.c_nb_hex', '__hex__'), ('tp_str', '__str__'), ('tp_repr', '__repr__'), ('tp_iter', '__iter__'), @@ -398,7 +399,7 @@ # binary functions for tp_name, attr in [('tp_as_number.c_nb_add', '__add__'), - ('tp_as_number.c_nb_subtract', '__subtract__'), + ('tp_as_number.c_nb_subtract', '__sub__'), ('tp_as_number.c_nb_multiply', '__mul__'), ('tp_as_number.c_nb_divide', '__div__'), ('tp_as_number.c_nb_remainder', '__mod__'), @@ -408,6 +409,8 @@ ('tp_as_number.c_nb_and', '__and__'), ('tp_as_number.c_nb_xor', '__xor__'), ('tp_as_number.c_nb_or', '__or__'), + ('tp_as_sequence.c_sq_concat', '__add__'), + ('tp_as_sequence.c_sq_inplace_concat', '__iadd__') ]: if name == tp_name: slot_fn = w_type.getdictvalue(space, attr) @@ -421,8 +424,26 @@ api_func = slot_func.api_func handled = True + # binary-with-Py_ssize_t-type + for tp_name, attr in [('tp_as_sequence.c_sq_item', '__getitem'), + ('tp_as_sequence.c_sq_repeat', '__mul__'), + ('tp_as_sequence.c_sq_repeat', '__mul__'), + ('tp_as_sequence.c_sq_inplace_repeat', '__imul__'), + ]: + if name == tp_name: + slot_fn = w_type.getdictvalue(space, attr) + if slot_fn is None: + return + + @cpython_api([PyObject, Py_ssize_t], PyObject, header=header) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_func(space, w_self, arg): + return space.call_function(slot_fn, w_self, space.wrap(arg)) + api_func = slot_func.api_func + handled = True + # ternary functions - for tp_name, attr in [('tp_as_number.c_nb_power', ''), + for tp_name, attr in [('tp_as_number.c_nb_power', '__pow__'), ]: if name == tp_name: slot_fn = w_type.getdictvalue(space, attr) @@ -522,6 +543,8 @@ api_func = slot_tp_new.api_func else: # missing: tp_as_number.nb_nonzero, tp_as_number.nb_coerce + # tp_as_sequence.c_sq_contains, tp_as_sequence.c_sq_length + # richcmpfunc(s) return return lambda: llhelper(api_func.functype, api_func.get_wrapper(space)) diff --git a/pypy/module/cpyext/test/array.c b/pypy/module/cpyext/test/array.c --- a/pypy/module/cpyext/test/array.c +++ b/pypy/module/cpyext/test/array.c @@ -2144,6 +2144,13 @@ return array_new(type, args, NULL); } +static PyObject * +switch_multiply(void) +{ + Arraytype.tp_as_number->nb_multiply = array_base_multiply; + Py_RETURN_NONE; +}; + PyDoc_STRVAR(module_doc, "This module defines an object type which can efficiently represent\n\ an array of basic values: characters, integers, floating point\n\ @@ -2394,6 +2401,7 @@ /* No functions in array module. */ static PyMethodDef a_methods[] = { {"_reconstruct", (PyCFunction)_reconstruct, METH_VARARGS, NULL}, + {"switch_multiply", (PyCFunction)switch_multiply, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/pypy/module/cpyext/test/foo.c b/pypy/module/cpyext/test/foo.c --- a/pypy/module/cpyext/test/foo.c +++ b/pypy/module/cpyext/test/foo.c @@ -747,6 +747,7 @@ if (PyType_Ready(&UnicodeSubtype3) < 0) INITERROR; + TupleLike.tp_flags = Py_TPFLAGS_DEFAULT; TupleLike.tp_base = &PyTuple_Type; if (PyType_Ready(&TupleLike) < 0) INITERROR; diff --git a/pypy/module/cpyext/test/test_arraymodule.py b/pypy/module/cpyext/test/test_arraymodule.py --- a/pypy/module/cpyext/test/test_arraymodule.py +++ b/pypy/module/cpyext/test/test_arraymodule.py @@ -84,3 +84,7 @@ arr = module.array('i', [2]) res = [1, 2, 3] * arr assert res == [1, 2, 3, 1, 2, 3] + module.switch_multiply() + res = [1, 2, 3] * arr + assert res == [2, 4, 6] + diff --git a/pypy/module/cpyext/test/test_bytearrayobject.py b/pypy/module/cpyext/test/test_bytearrayobject.py --- a/pypy/module/cpyext/test/test_bytearrayobject.py +++ b/pypy/module/cpyext/test/test_bytearrayobject.py @@ -87,11 +87,12 @@ module = self.import_extension('foo', [ ("getbytearray", "METH_NOARGS", """ - PyObject* s1 = PyByteArray_FromStringAndSize("test", 4); + const char *c; + PyObject *s2, *s1 = PyByteArray_FromStringAndSize("test", 4); if (s1 == NULL) return NULL; - const char* c = PyByteArray_AsString(s1); - PyObject* s2 = PyByteArray_FromStringAndSize(c, 4); + c = PyByteArray_AsString(s1); + s2 = PyByteArray_FromStringAndSize(c, 4); Py_DECREF(s1); return s2; """), diff --git a/pypy/module/cpyext/test/test_bytesobject.py b/pypy/module/cpyext/test/test_bytesobject.py --- a/pypy/module/cpyext/test/test_bytesobject.py +++ b/pypy/module/cpyext/test/test_bytesobject.py @@ -113,9 +113,10 @@ module = self.import_extension('foo', [ ("getbytes", "METH_NOARGS", """ - PyObject* s1 = PyBytes_FromStringAndSize("test", 4); - char* c = PyBytes_AsString(s1); - PyObject* s2 = PyBytes_FromStringAndSize(c, 4); + char *c; + PyObject* s2, *s1 = PyBytes_FromStringAndSize("test", 4); + c = PyBytes_AsString(s1); + s2 = PyBytes_FromStringAndSize(c, 4); Py_DECREF(s1); return s2; """), diff --git a/pypy/module/cpyext/test/test_datetime.py b/pypy/module/cpyext/test/test_datetime.py --- a/pypy/module/cpyext/test/test_datetime.py +++ b/pypy/module/cpyext/test/test_datetime.py @@ -176,13 +176,15 @@ """), ("test_datetime_macros", "METH_NOARGS", """ + PyObject* obj; + PyDateTime_DateTime *dt; PyDateTime_IMPORT; if (!PyDateTimeAPI) { PyErr_SetString(PyExc_RuntimeError, "No PyDateTimeAPI"); return NULL; } - PyObject* obj = PyDateTime_FromDateAndTime(2000, 6, 6, 6, 6, 6, 6); - PyDateTime_DateTime* dt = (PyDateTime_DateTime*)obj; + obj = PyDateTime_FromDateAndTime(2000, 6, 6, 6, 6, 6, 6); + dt = (PyDateTime_DateTime*)obj; PyDateTime_GET_YEAR(obj); PyDateTime_GET_YEAR(dt); @@ -209,13 +211,15 @@ """), ("test_time_macros", "METH_NOARGS", """ + PyObject* obj; + PyDateTime_Time* t; PyDateTime_IMPORT; if (!PyDateTimeAPI) { PyErr_SetString(PyExc_RuntimeError, "No PyDateTimeAPI"); return NULL; } - PyObject* obj = PyTime_FromTime(6, 6, 6, 6); - PyDateTime_Time* t = (PyDateTime_Time*)obj; + obj = PyTime_FromTime(6, 6, 6, 6); + t = (PyDateTime_Time*)obj; PyDateTime_TIME_GET_HOUR(obj); PyDateTime_TIME_GET_HOUR(t); @@ -233,13 +237,15 @@ """), ("test_delta_macros", "METH_NOARGS", """ + PyObject* obj; + PyDateTime_Delta* delta; PyDateTime_IMPORT; if (!PyDateTimeAPI) { PyErr_SetString(PyExc_RuntimeError, "No PyDateTimeAPI"); return NULL; } - PyObject* obj = PyDelta_FromDSU(6, 6, 6); - PyDateTime_Delta* delta = (PyDateTime_Delta*)obj; + obj = PyDelta_FromDSU(6, 6, 6); + delta = (PyDateTime_Delta*)obj; #if defined(PYPY_VERSION) || PY_VERSION_HEX >= 0x03030000 // These macros are only defined in CPython 3.x and PyPy. diff --git a/pypy/module/cpyext/test/test_listobject.py b/pypy/module/cpyext/test/test_listobject.py --- a/pypy/module/cpyext/test/test_listobject.py +++ b/pypy/module/cpyext/test/test_listobject.py @@ -64,7 +64,7 @@ assert space.unwrap(w_s) == [2, 1] class AppTestListObject(AppTestCpythonExtensionBase): - def test_listobject(self): + def test_basic_listobject(self): import sys module = self.import_extension('foo', [ ("newlist", "METH_NOARGS", @@ -104,6 +104,15 @@ Py_RETURN_NONE; """ ), + ('test_tp_as_', "METH_NOARGS", + ''' + PyObject *l = PyList_New(3); + int ok = l->ob_type->tp_as_sequence != NULL; /* 1 */ + ok += 2 * (l->ob_type->tp_as_number == NULL); /* 2 */ + Py_DECREF(l); + return PyLong_FromLong(ok); /* should be 3 */ + ''' + ), ]) l = module.newlist() assert l == [3, -5, 1000] @@ -137,6 +146,9 @@ module.setlistitem(l,0) assert l == [None, 2, 3] + # tp_as_sequence should be filled, but tp_as_number should be NULL + assert module.test_tp_as_() == 3 + def test_list_macros(self): """The PyList_* macros cast, and calls expecting that build.""" module = self.import_extension('foo', [ diff --git a/pypy/module/cpyext/test/test_longobject.py b/pypy/module/cpyext/test/test_longobject.py --- a/pypy/module/cpyext/test/test_longobject.py +++ b/pypy/module/cpyext/test/test_longobject.py @@ -227,4 +227,40 @@ """)]) assert module.from_str() == 0 - + def test_slots(self): + module = self.import_extension('foo', [ + ("has_sub", "METH_NOARGS", + """ + PyObject *ret, *obj = PyLong_FromLong(42); + if (obj->ob_type->tp_as_number->nb_subtract) + ret = obj->ob_type->tp_as_number->nb_subtract(obj, obj); + else + ret = PyLong_FromLong(-1); + Py_DECREF(obj); + return ret; + """), + ("has_pow", "METH_NOARGS", + """ + PyObject *ret, *obj = PyLong_FromLong(42); + PyObject *one = PyLong_FromLong(1); + if (obj->ob_type->tp_as_number->nb_power) + ret = obj->ob_type->tp_as_number->nb_power(obj, one, one); + else + ret = PyLong_FromLong(-1); + Py_DECREF(obj); + return ret; + """), + ("has_hex", "METH_NOARGS", + """ + PyObject *ret, *obj = PyLong_FromLong(42); + if (obj->ob_type->tp_as_number->nb_hex) + ret = obj->ob_type->tp_as_number->nb_hex(obj); + else + ret = PyLong_FromLong(-1); + Py_DECREF(obj); + return ret; + """)]) + assert module.has_sub() == 0 + assert module.has_pow() == 0 + assert module.has_hex() == '0x2aL' + diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py --- a/pypy/module/cpyext/typeobject.py +++ b/pypy/module/cpyext/typeobject.py @@ -17,9 +17,9 @@ generic_cpy_call, Py_TPFLAGS_READY, Py_TPFLAGS_READYING, Py_TPFLAGS_HEAPTYPE, METH_VARARGS, METH_KEYWORDS, CANNOT_FAIL, Py_TPFLAGS_HAVE_GETCHARBUFFER, build_type_checkers, StaticObjectBuilder, - PyObjectFields, Py_TPFLAGS_BASETYPE) + PyObjectFields, Py_TPFLAGS_BASETYPE, PyTypeObject, PyTypeObjectPtr) from pypy.module.cpyext.methodobject import (W_PyCClassMethodObject, - PyDescr_NewWrapper, PyCFunction_NewEx, PyCFunction_typedef, PyMethodDef, + W_PyCWrapperObject, PyCFunction_NewEx, PyCFunction_typedef, PyMethodDef, W_PyCMethodObject, W_PyCFunctionObject) from pypy.module.cpyext.modsupport import convert_method_defs from pypy.module.cpyext.pyobject import ( @@ -30,7 +30,7 @@ from pypy.module.cpyext.state import State from pypy.module.cpyext.structmember import PyMember_GetOne, PyMember_SetOne from pypy.module.cpyext.typeobjectdefs import ( - PyTypeObjectPtr, PyTypeObject, PyGetSetDef, PyMemberDef, newfunc, + PyGetSetDef, PyMemberDef, newfunc, PyNumberMethods, PyMappingMethods, PySequenceMethods, PyBufferProcs) from pypy.objspace.std.typeobject import W_TypeObject, find_best_base @@ -238,7 +238,7 @@ i += 1 def update_all_slots(space, w_type, pto): - # XXX fill slots in pto + # fill slots in pto # Not very sure about it, but according to # test_call_tp_dealloc_when_created_from_python, we should not # overwrite slots that are already set: these ones are probably @@ -272,6 +272,15 @@ if len(slot_names) == 1: if not getattr(pto, slot_names[0]): setattr(pto, slot_names[0], slot_func_helper) + elif (w_type.getname(space) in ('list', 'tuple') and + slot_names[0] == 'c_tp_as_number'): + # XXX hack - hwo can we generalize this? The problem is method + # names like __mul__ map to more than one slot, and we have no + # convenient way to indicate which slots CPython have filled + # + # We need at least this special case since Numpy checks that + # (list, tuple) do __not__ fill tp_as_number + pass else: assert len(slot_names) == 2 struct = getattr(pto, slot_names[0]) @@ -296,6 +305,7 @@ for method_name, slot_names, wrapper_func, wrapper_func_kwds, doc in slotdefs_for_wrappers: if method_name in dict_w: continue + offset = [rffi.offsetof(lltype.typeOf(pto).TO, slot_names[0])] if len(slot_names) == 1: func = getattr(pto, slot_names[0]) else: @@ -303,14 +313,16 @@ struct = getattr(pto, slot_names[0]) if not struct: continue + offset.append(rffi.offsetof(lltype.typeOf(struct).TO, slot_names[1])) func = getattr(struct, slot_names[1]) func_voidp = rffi.cast(rffi.VOIDP, func) if not func: continue if wrapper_func is None and wrapper_func_kwds is None: continue - dict_w[method_name] = PyDescr_NewWrapper(space, pto, method_name, wrapper_func, - wrapper_func_kwds, doc, func_voidp) + w_obj = W_PyCWrapperObject(space, pto, method_name, wrapper_func, + wrapper_func_kwds, doc, func_voidp, offset=offset) + dict_w[method_name] = space.wrap(w_obj) if pto.c_tp_new: add_tp_new_wrapper(space, dict_w, pto) @@ -730,6 +742,7 @@ try: w_obj = _type_realize(space, py_obj) finally: + name = rffi.charp2str(pto.c_tp_name) pto.c_tp_flags &= ~Py_TPFLAGS_READYING pto.c_tp_flags |= Py_TPFLAGS_READY return w_obj @@ -811,7 +824,8 @@ base = pto.c_tp_base base_pyo = rffi.cast(PyObject, pto.c_tp_base) if base and not base.c_tp_flags & Py_TPFLAGS_READY: - type_realize(space, rffi.cast(PyObject, base_pyo)) + name = rffi.charp2str(base.c_tp_name) + type_realize(space, base_pyo) if base and not pto.c_ob_type: # will be filled later pto.c_ob_type = base.c_ob_type if not pto.c_tp_bases: 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 @@ -835,7 +835,7 @@ data before the other iterator, it is faster to use list() instead of tee() - Equivalent to : + If iter(iterable) has no method __copy__(), this is equivalent to: def tee(iterable, n=2): def gen(next, data={}, cnt=[0]): @@ -848,17 +848,22 @@ yield item it = iter(iterable) return tuple([gen(it.next) for i in range(n)]) + + If iter(iterable) has a __copy__ method, though, we just return + a tuple t = (iterable, t[0].__copy__(), t[1].__copy__(), ...). """ if n < 0: raise oefmt(space.w_ValueError, "n must be >= 0") - if isinstance(w_iterable, W_TeeIterable): # optimization only - chained_list = w_iterable.chained_list - w_iterator = w_iterable.w_iterator + if space.findattr(w_iterable, space.wrap("__copy__")) is not None: + # In this case, we don't instantiate any W_TeeIterable. + # We just rely on doing repeated __copy__(). This case + # includes the situation where w_iterable is already + # a W_TeeIterable itself. iterators_w = [w_iterable] * n for i in range(1, n): - iterators_w[i] = space.wrap(W_TeeIterable(space, w_iterator, - chained_list)) + w_iterable = space.call_method(w_iterable, "__copy__") + iterators_w[i] = w_iterable else: w_iterator = space.iter(w_iterable) chained_list = TeeChainedListNode() @@ -891,6 +896,11 @@ self.chained_list = chained_list.next return w_obj + def copy_w(self): + space = self.space + tee_iter = W_TeeIterable(space, self.w_iterator, self.chained_list) + return space.wrap(tee_iter) + def W_TeeIterable___new__(space, w_subtype, w_iterable): # Obscure and undocumented function. PyPy only supports w_iterable # being a W_TeeIterable, because the case where it is a general @@ -906,6 +916,7 @@ __new__ = interp2app(W_TeeIterable___new__), __iter__ = interp2app(W_TeeIterable.iter_w), next = interp2app(W_TeeIterable.next_w), + __copy__ = interp2app(W_TeeIterable.copy_w), __weakref__ = make_weakref_descr(W_TeeIterable), ) W_TeeIterable.typedef.acceptable_as_base_class = False diff --git a/pypy/module/itertools/test/test_itertools.py b/pypy/module/itertools/test/test_itertools.py --- a/pypy/module/itertools/test/test_itertools.py +++ b/pypy/module/itertools/test/test_itertools.py @@ -702,6 +702,48 @@ x = d.next() assert x == 'b' + def test_tee_defines_copy(self): + import itertools + a, b = itertools.tee('abc') + c = b.__copy__() + assert list(a) == ['a', 'b', 'c'] + assert list(b) == ['a', 'b', 'c'] + assert list(c) == ['a', 'b', 'c'] + a, = itertools.tee('abc', 1) + x = a.next() + assert x == 'a' + b = a.__copy__() + x = a.next() + assert x == 'b' + x = b.next() + assert x == 'b' + + def test_tee_function_uses_copy(self): + import itertools + class MyIterator(object): + def __iter__(self): + return self + def next(self): + raise NotImplementedError + def __copy__(self): + return iter('def') + my = MyIterator() + a, = itertools.tee(my, 1) + assert a is my + a, b = itertools.tee(my) + assert a is my + assert b is not my + assert list(b) == ['d', 'e', 'f'] + # this gives AttributeError because it tries to call + # my.__copy__().__copy__() and there isn't one + raises(AttributeError, itertools.tee, my, 3) + + def test_tee_function_empty(self): + import itertools + assert itertools.tee('abc', 0) == () + a, = itertools.tee('abc', 1) + assert itertools.tee(a, 0) == () + class AppTestItertools26: spaceconfig = dict(usemodules=['itertools']) diff --git a/pypy/module/micronumpy/ndarray.py b/pypy/module/micronumpy/ndarray.py --- a/pypy/module/micronumpy/ndarray.py +++ b/pypy/module/micronumpy/ndarray.py @@ -254,7 +254,7 @@ idx = space.str_w(w_idx) return self.getfield(space, idx) if space.is_w(w_idx, space.w_Ellipsis): - return self + return self.descr_view(space, space.type(self)) elif isinstance(w_idx, W_NDimArray) and w_idx.get_dtype().is_bool(): if w_idx.ndims() > 0: w_ret = self.getitem_filter(space, w_idx) diff --git a/pypy/module/micronumpy/test/test_ndarray.py b/pypy/module/micronumpy/test/test_ndarray.py --- a/pypy/module/micronumpy/test/test_ndarray.py +++ b/pypy/module/micronumpy/test/test_ndarray.py @@ -2614,17 +2614,11 @@ import numpy as np import sys a = np.array(1.5) - if '__pypy__' in sys.builtin_module_names: - assert a[...] is a - else: - assert a[...].base is a + assert a[...].base is a a[...] = 2.5 assert a == 2.5 a = np.array([1, 2, 3]) - if '__pypy__' in sys.builtin_module_names: - assert a[...] is a - else: - assert a[...].base is a + assert a[...].base is a a[...] = 4 assert (a == [4, 4, 4]).all() assert a[..., 0] == 4 diff --git a/pypy/module/posix/interp_posix.py b/pypy/module/posix/interp_posix.py --- a/pypy/module/posix/interp_posix.py +++ b/pypy/module/posix/interp_posix.py @@ -544,6 +544,14 @@ raise oefmt(space.w_ValueError, "the environment variable is longer than %d bytes", _MAX_ENV) + if _WIN32 and not objectmodel.we_are_translated() and value == '': + # special case: on Windows, _putenv("NAME=") really means that + # we want to delete NAME. So that's what the os.environ[name]='' + # below will do after translation. But before translation, it + # will cache the environment value '' instead of <missing> and + # then return that. We need to avoid that. + del os.environ[name] + return try: os.environ[name] = value except OSError as e: diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_pointers.py b/pypy/module/test_lib_pypy/ctypes_tests/test_pointers.py --- a/pypy/module/test_lib_pypy/ctypes_tests/test_pointers.py +++ b/pypy/module/test_lib_pypy/ctypes_tests/test_pointers.py @@ -265,3 +265,10 @@ class A(object): _byref = byref A._byref(c_int(5)) + + def test_byref_with_offset(self): + c = c_int() + d = byref(c) + base = cast(d, c_void_p).value + for i in [0, 1, 4, 1444, -10293]: + assert cast(byref(c, i), c_void_p).value == base + i diff --git a/pypy/module/thread/os_lock.py b/pypy/module/thread/os_lock.py --- a/pypy/module/thread/os_lock.py +++ b/pypy/module/thread/os_lock.py @@ -9,12 +9,9 @@ from pypy.interpreter.gateway import interp2app, unwrap_spec from pypy.interpreter.typedef import TypeDef from pypy.interpreter.error import oefmt -from rpython.rlib.rarithmetic import r_longlong +from rpython.rlib.rarithmetic import r_longlong, ovfcheck_float_to_longlong -LONGLONG_MAX = r_longlong(2 ** (r_longlong.BITS-1) - 1) -TIMEOUT_MAX = LONGLONG_MAX - RPY_LOCK_FAILURE, RPY_LOCK_ACQUIRED, RPY_LOCK_INTR = range(3) def parse_acquire_args(space, blocking, timeout): @@ -29,10 +26,12 @@ elif timeout == -1.0: microseconds = -1 else: - timeout *= 1e6 - if timeout > float(TIMEOUT_MAX): + # 0.0 => 0.0, but otherwise tends to round up + timeout = timeout * 1e6 + 0.999 + try: + microseconds = ovfcheck_float_to_longlong(timeout) + except OverflowError: raise oefmt(space.w_OverflowError, "timeout value is too large") - microseconds = r_longlong(timeout) return microseconds @@ -45,7 +44,8 @@ # Run signal handlers if we were interrupted space.getexecutioncontext().checksignals() if microseconds >= 0: - microseconds = r_longlong(endtime - (time.time() * 1e6)) + microseconds = r_longlong((endtime - (time.time() * 1e6)) + + 0.999) # Check for negative values, since those mean block # forever if microseconds <= 0: diff --git a/pypy/module/thread/test/test_lock.py b/pypy/module/thread/test/test_lock.py --- a/pypy/module/thread/test/test_lock.py +++ b/pypy/module/thread/test/test_lock.py @@ -64,6 +64,25 @@ else: assert self.runappdirect, "missing lock._py3k_acquire()" + def test_py3k_acquire_timeout_overflow(self): + import thread + lock = thread.allocate_lock() + if not hasattr(lock, '_py3k_acquire'): + skip("missing lock._py3k_acquire()") + maxint = 2**63 - 1 + boundary = int(maxint * 1e-6) + for i in [-100000, -10000, -1000, -100, -10, -1, 0, + 1, 10, 100, 1000, 10000, 100000]: + timeout = (maxint + i) * 1e-6 + try: + lock._py3k_acquire(True, timeout=timeout) + except OverflowError: + got_ovf = True + else: + got_ovf = False + lock.release() + assert (i, got_ovf) == (i, int(timeout * 1e6 + 0.999) > maxint) + @py.test.mark.xfail(machine()=='s390x', reason='may fail under heavy load') def test_ping_pong(self): # The purpose of this test is that doing a large number of ping-pongs diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py --- a/pypy/objspace/descroperation.py +++ b/pypy/objspace/descroperation.py @@ -509,21 +509,6 @@ def isinstance(space, w_inst, w_type): return space.wrap(space.isinstance_w(w_inst, w_type)) - def issubtype_allow_override(space, w_sub, w_type): - w_check = space.lookup(w_type, "__subclasscheck__") - if w_check is None: - raise oefmt(space.w_TypeError, "issubclass not supported here") - return space.get_and_call_function(w_check, w_type, w_sub) - - def isinstance_allow_override(space, w_inst, w_type): - if space.type(w_inst) is w_type: - return space.w_True # fast path copied from cpython - w_check = space.lookup(w_type, "__instancecheck__") - if w_check is not None: - return space.get_and_call_function(w_check, w_type, w_inst) - else: - return space.isinstance(w_inst, w_type) - # helpers diff --git a/pypy/objspace/std/test/test_typeobject.py b/pypy/objspace/std/test/test_typeobject.py --- a/pypy/objspace/std/test/test_typeobject.py +++ b/pypy/objspace/std/test/test_typeobject.py @@ -1091,6 +1091,29 @@ C() # the lookup of '__new__' succeeds in 'int', # but the lookup of '__init__' fails + def test_instancecheck(self): + assert int.__instancecheck__(42) is True + assert int.__instancecheck__(42.0) is False + class Foo: + __class__ = int + assert int.__instancecheck__(Foo()) is False + class Bar(object): + __class__ = int + assert int.__instancecheck__(Bar()) is True + + def test_subclasscheck(self): + assert int.__subclasscheck__(bool) is True + assert int.__subclasscheck__(float) is False + class Foo: + __class__ = int + assert int.__subclasscheck__(Foo) is False + class Bar(object): + __class__ = int + assert int.__subclasscheck__(Bar) is False + class AbstractClass(object): + __bases__ = (int,) + assert int.__subclasscheck__(AbstractClass()) is True + class AppTestWithMethodCacheCounter: spaceconfig = {"objspace.std.withmethodcachecounter": True} @@ -1290,5 +1313,3 @@ assert not self.compares_by_identity(X) del X.__eq__ assert self.compares_by_identity(X) - - diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py --- a/pypy/objspace/std/typeobject.py +++ b/pypy/objspace/std/typeobject.py @@ -6,6 +6,7 @@ from pypy.interpreter.typedef import weakref_descr, GetSetProperty,\ descr_get_dict, dict_descr, Member, TypeDef from pypy.interpreter.astcompiler.misc import mangle +from pypy.module.__builtin__ import abstractinst from rpython.rlib.jit import (promote, elidable_promote, we_are_jitted, elidable, dont_look_inside, unroll_safe) @@ -899,13 +900,15 @@ # ____________________________________________________________ -@gateway.unwrap_spec(w_obj=W_TypeObject, w_sub=W_TypeObject) +@gateway.unwrap_spec(w_obj=W_TypeObject) def type_issubtype(w_obj, space, w_sub): - return space.newbool(w_sub.issubtype(w_obj)) + return space.newbool( + abstractinst.p_recursive_issubclass_w(space, w_sub, w_obj)) @gateway.unwrap_spec(w_obj=W_TypeObject) def type_isinstance(w_obj, space, w_inst): - return space.newbool(space.type(w_inst).issubtype(w_obj)) + return space.newbool( + abstractinst.p_recursive_isinstance_type_w(space, w_inst, w_obj)) W_TypeObject.typedef = TypeDef("type", __new__ = gateway.interp2app(descr__new__), diff --git a/rpython/jit/backend/llsupport/test/ztranslation_test.py b/rpython/jit/backend/llsupport/test/ztranslation_test.py --- a/rpython/jit/backend/llsupport/test/ztranslation_test.py +++ b/rpython/jit/backend/llsupport/test/ztranslation_test.py @@ -41,7 +41,7 @@ eci = ExternalCompilationInfo(post_include_bits=[''' #define pypy_my_fabs(x) fabs(x) -''']) +'''], includes=['math.h']) myabs1 = rffi.llexternal('pypy_my_fabs', [lltype.Float], lltype.Float, macro=True, releasegil=False, compilation_info=eci) diff --git a/rpython/jit/backend/zarch/assembler.py b/rpython/jit/backend/zarch/assembler.py --- a/rpython/jit/backend/zarch/assembler.py +++ b/rpython/jit/backend/zarch/assembler.py @@ -317,7 +317,7 @@ self._push_fp_regs_to_jitframe(mc) - # First argument is SPP (= r31), which is the jitframe + # First argument is SPP, which is the jitframe mc.LGR(r.r2, r.SPP) # no need to move second argument (frame_depth), @@ -385,14 +385,12 @@ # signature of these cond_call_slowpath functions: # * on entry, r11 contains the function to call # * r2, r3, r4, r5 contain arguments for the call - # * r0 is the gcmap + # * gcmap is pushed # * the old value of these regs must already be stored in the jitframe # * on exit, all registers are restored from the jitframe mc = InstrBuilder() self.mc = mc - ofs2 = self.cpu.get_ofs_of_frame_field('jf_gcmap') - mc.STG(r.SCRATCH2, l.addr(ofs2,r.SPP)) mc.store_link() mc.push_std_frame() @@ -502,7 +500,6 @@ # r.RSZ is loaded from [r1], to make the caller's store a no-op here mc.load(r.RSZ, r.r1, 0) # - self.pop_gcmap(mc) # push_gcmap(store=True) done by the caller mc.restore_link() mc.BCR(c.ANY, r.r14) self.mc = None diff --git a/rpython/jit/backend/zarch/opassembler.py b/rpython/jit/backend/zarch/opassembler.py --- a/rpython/jit/backend/zarch/opassembler.py +++ b/rpython/jit/backend/zarch/opassembler.py @@ -387,8 +387,7 @@ if reg in self._COND_CALL_SAVE_REGS] self._push_core_regs_to_jitframe(self.mc, should_be_saved) - # load gc map into unusual location: r0 - self.load_gcmap(self.mc, r.SCRATCH2, regalloc.get_gcmap()) + self.push_gcmap(self.mc, regalloc.get_gcmap()) # # load the 0-to-4 arguments into these registers, with the address of # the function to call into r11 diff --git a/rpython/jit/backend/zarch/test/test_runner.py b/rpython/jit/backend/zarch/test/test_runner.py --- a/rpython/jit/backend/zarch/test/test_runner.py +++ b/rpython/jit/backend/zarch/test/test_runner.py @@ -26,4 +26,4 @@ add_loop_instructions = "lg; lgr; larl; agr; cgfi; jge; j;$" bridge_loop_instructions = "lg; cgfi; jnl; lghi; " \ - "(lgfi|iilf);( iihf;)? (lgfi|iilf);( iihf;)? basr; larl; (lgfi|iilf);( iihf;)? br;$" + "(lgfi|iilf);( iihf;)? (lgfi|iilf);( iihf;)? stg; basr; larl; (lgfi|iilf);( iihf;)? br;$" diff --git a/rpython/jit/metainterp/blackhole.py b/rpython/jit/metainterp/blackhole.py --- a/rpython/jit/metainterp/blackhole.py +++ b/rpython/jit/metainterp/blackhole.py @@ -1143,45 +1143,35 @@ @arguments("cpu", "i", "R", "d", returns="i") def bhimpl_residual_call_r_i(cpu, func, args_r, calldescr): - workaround2200.active = True return cpu.bh_call_i(func, None, args_r, None, calldescr) @arguments("cpu", "i", "R", "d", returns="r") def bhimpl_residual_call_r_r(cpu, func, args_r, calldescr): - workaround2200.active = True return cpu.bh_call_r(func, None, args_r, None, calldescr) @arguments("cpu", "i", "R", "d") def bhimpl_residual_call_r_v(cpu, func, args_r, calldescr): - workaround2200.active = True return cpu.bh_call_v(func, None, args_r, None, calldescr) @arguments("cpu", "i", "I", "R", "d", returns="i") def bhimpl_residual_call_ir_i(cpu, func, args_i, args_r, calldescr): - workaround2200.active = True return cpu.bh_call_i(func, args_i, args_r, None, calldescr) @arguments("cpu", "i", "I", "R", "d", returns="r") def bhimpl_residual_call_ir_r(cpu, func, args_i, args_r, calldescr): - workaround2200.active = True return cpu.bh_call_r(func, args_i, args_r, None, calldescr) @arguments("cpu", "i", "I", "R", "d") def bhimpl_residual_call_ir_v(cpu, func, args_i, args_r, calldescr): - workaround2200.active = True return cpu.bh_call_v(func, args_i, args_r, None, calldescr) @arguments("cpu", "i", "I", "R", "F", "d", returns="i") def bhimpl_residual_call_irf_i(cpu, func, args_i,args_r,args_f,calldescr): - workaround2200.active = True return cpu.bh_call_i(func, args_i, args_r, args_f, calldescr) @arguments("cpu", "i", "I", "R", "F", "d", returns="r") _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit