Author: fijal Branch: unicode-utf8 Changeset: r93144:177352fb8cf4 Date: 2017-11-23 16:46 +0100 http://bitbucket.org/pypy/pypy/changeset/177352fb8cf4/
Log: merge default diff too long, truncating to 2000 out of 7577 lines diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -71,6 +71,8 @@ ^lib_pypy/.+.c$ ^lib_pypy/.+.o$ ^lib_pypy/.+.so$ +^lib_pypy/.+.pyd$ +^lib_pypy/Release/ ^pypy/doc/discussion/.+\.html$ ^include/.+\.h$ ^include/.+\.inl$ diff --git a/extra_tests/requirements.txt b/extra_tests/requirements.txt new file mode 100644 --- /dev/null +++ b/extra_tests/requirements.txt @@ -0,0 +1,2 @@ +pytest +hypothesis diff --git a/extra_tests/test_bytes.py b/extra_tests/test_bytes.py new file mode 100644 --- /dev/null +++ b/extra_tests/test_bytes.py @@ -0,0 +1,84 @@ +from hypothesis import strategies as st +from hypothesis import given, example + +st_bytestring = st.binary() | st.binary().map(bytearray) + +@given(st_bytestring, st_bytestring, st_bytestring) +def test_find(u, prefix, suffix): + s = prefix + u + suffix + assert 0 <= s.find(u) <= len(prefix) + assert s.find(u, len(prefix), len(s) - len(suffix)) == len(prefix) + +@given(st_bytestring, st_bytestring, st_bytestring) +def test_index(u, prefix, suffix): + s = prefix + u + suffix + assert 0 <= s.index(u) <= len(prefix) + assert s.index(u, len(prefix), len(s) - len(suffix)) == len(prefix) + +@given(st_bytestring, st_bytestring, st_bytestring) +def test_rfind(u, prefix, suffix): + s = prefix + u + suffix + assert s.rfind(u) >= len(prefix) + assert s.rfind(u, len(prefix), len(s) - len(suffix)) == len(prefix) + +@given(st_bytestring, st_bytestring, st_bytestring) +def test_rindex(u, prefix, suffix): + s = prefix + u + suffix + assert s.rindex(u) >= len(prefix) + assert s.rindex(u, len(prefix), len(s) - len(suffix)) == len(prefix) + +def adjust_indices(u, start, end): + if end < 0: + end = max(end + len(u), 0) + else: + end = min(end, len(u)) + if start < 0: + start = max(start + len(u), 0) + return start, end + +@given(st_bytestring, st_bytestring) +def test_startswith_basic(u, v): + assert u.startswith(v) is (u[:len(v)] == v) + +@example(b'x', b'', 1) +@example(b'x', b'', 2) +@given(st_bytestring, st_bytestring, st.integers()) +def test_startswith_start(u, v, start): + expected = u[start:].startswith(v) if v else (start <= len(u)) + assert u.startswith(v, start) is expected + +@example(b'x', b'', 1, 0) +@example(b'xx', b'', -1, 0) +@given(st_bytestring, st_bytestring, st.integers(), st.integers()) +def test_startswith_3(u, v, start, end): + if v: + expected = u[start:end].startswith(v) + else: # CPython leaks implementation details in this case + start0, end0 = adjust_indices(u, start, end) + expected = start0 <= len(u) and start0 <= end0 + assert u.startswith(v, start, end) is expected + +@given(st_bytestring, st_bytestring) +def test_endswith_basic(u, v): + if len(v) > len(u): + assert u.endswith(v) is False + else: + assert u.endswith(v) is (u[len(u) - len(v):] == v) + +@example(b'x', b'', 1) +@example(b'x', b'', 2) +@given(st_bytestring, st_bytestring, st.integers()) +def test_endswith_2(u, v, start): + expected = u[start:].endswith(v) if v else (start <= len(u)) + assert u.endswith(v, start) is expected + +@example(b'x', b'', 1, 0) +@example(b'xx', b'', -1, 0) +@given(st_bytestring, st_bytestring, st.integers(), st.integers()) +def test_endswith_3(u, v, start, end): + if v: + expected = u[start:end].endswith(v) + else: # CPython leaks implementation details in this case + start0, end0 = adjust_indices(u, start, end) + expected = start0 <= len(u) and start0 <= end0 + assert u.endswith(v, start, end) is expected diff --git a/extra_tests/test_unicode.py b/extra_tests/test_unicode.py --- a/extra_tests/test_unicode.py +++ b/extra_tests/test_unicode.py @@ -1,3 +1,4 @@ +import sys import pytest from hypothesis import strategies as st from hypothesis import given, settings, example @@ -32,3 +33,89 @@ @given(s=st.text()) def test_composition(s, norm1, norm2, norm3): assert normalize(norm2, normalize(norm1, s)) == normalize(norm3, s) + +@given(st.text(), st.text(), st.text()) +def test_find(u, prefix, suffix): + s = prefix + u + suffix + assert 0 <= s.find(u) <= len(prefix) + assert s.find(u, len(prefix), len(s) - len(suffix)) == len(prefix) + +@given(st.text(), st.text(), st.text()) +def test_index(u, prefix, suffix): + s = prefix + u + suffix + assert 0 <= s.index(u) <= len(prefix) + assert s.index(u, len(prefix), len(s) - len(suffix)) == len(prefix) + +@given(st.text(), st.text(), st.text()) +def test_rfind(u, prefix, suffix): + s = prefix + u + suffix + assert s.rfind(u) >= len(prefix) + assert s.rfind(u, len(prefix), len(s) - len(suffix)) == len(prefix) + +@given(st.text(), st.text(), st.text()) +def test_rindex(u, prefix, suffix): + s = prefix + u + suffix + assert s.rindex(u) >= len(prefix) + assert s.rindex(u, len(prefix), len(s) - len(suffix)) == len(prefix) + +def adjust_indices(u, start, end): + if end < 0: + end = max(end + len(u), 0) + else: + end = min(end, len(u)) + if start < 0: + start = max(start + len(u), 0) + return start, end + +@given(st.text(), st.text()) +def test_startswith_basic(u, v): + assert u.startswith(v) is (u[:len(v)] == v) + +@example(u'x', u'', 1) +@example(u'x', u'', 2) +@given(st.text(), st.text(), st.integers()) +def test_startswith_2(u, v, start): + if v or sys.version_info[0] == 2: + expected = u[start:].startswith(v) + else: # CPython leaks implementation details in this case + expected = start <= len(u) + assert u.startswith(v, start) is expected + +@example(u'x', u'', 1, 0) +@example(u'xx', u'', -1, 0) +@given(st.text(), st.text(), st.integers(), st.integers()) +def test_startswith_3(u, v, start, end): + if v or sys.version_info[0] == 2: + expected = u[start:end].startswith(v) + else: # CPython leaks implementation details in this case + start0, end0 = adjust_indices(u, start, end) + expected = start0 <= len(u) and start0 <= end0 + assert u.startswith(v, start, end) is expected + +@given(st.text(), st.text()) +def test_endswith_basic(u, v): + if len(v) > len(u): + assert u.endswith(v) is False + else: + assert u.endswith(v) is (u[len(u) - len(v):] == v) + +@example(u'x', u'', 1) +@example(u'x', u'', 2) +@given(st.text(), st.text(), st.integers()) +def test_endswith_2(u, v, start): + if v or sys.version_info[0] == 2: + expected = u[start:].endswith(v) + else: # CPython leaks implementation details in this case + expected = start <= len(u) + assert u.endswith(v, start) is expected + +@example(u'x', u'', 1, 0) +@example(u'xx', u'', -1, 0) +@given(st.text(), st.text(), st.integers(), st.integers()) +def test_endswith_3(u, v, start, end): + if v or sys.version_info[0] == 2: + expected = u[start:end].endswith(v) + else: # CPython leaks implementation details in this case + start0, end0 = adjust_indices(u, start, end) + expected = start0 <= len(u) and start0 <= end0 + assert u.endswith(v, start, end) is expected diff --git a/lib-python/2.7/ctypes/__init__.py b/lib-python/2.7/ctypes/__init__.py --- a/lib-python/2.7/ctypes/__init__.py +++ b/lib-python/2.7/ctypes/__init__.py @@ -360,14 +360,15 @@ self._FuncPtr = _FuncPtr if handle is None: - if flags & _FUNCFLAG_CDECL: - pypy_dll = _ffi.CDLL(name, mode) - else: - pypy_dll = _ffi.WinDLL(name, mode) - self.__pypy_dll__ = pypy_dll - handle = int(pypy_dll) - if _sys.maxint > 2 ** 32: - handle = int(handle) # long -> int + handle = 0 + if flags & _FUNCFLAG_CDECL: + pypy_dll = _ffi.CDLL(name, mode, handle) + else: + pypy_dll = _ffi.WinDLL(name, mode, handle) + self.__pypy_dll__ = pypy_dll + handle = int(pypy_dll) + if _sys.maxint > 2 ** 32: + handle = int(handle) # long -> int self._handle = handle def __repr__(self): diff --git a/lib-python/2.7/inspect.py b/lib-python/2.7/inspect.py --- a/lib-python/2.7/inspect.py +++ b/lib-python/2.7/inspect.py @@ -40,6 +40,10 @@ import linecache from operator import attrgetter from collections import namedtuple +try: + from cpyext import is_cpyext_function as _is_cpyext_function +except ImportError: + _is_cpyext_function = lambda obj: False # These constants are from Include/code.h. CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS = 0x1, 0x2, 0x4, 0x8 @@ -230,7 +234,7 @@ __doc__ documentation string __name__ original name of this function or method __self__ instance to which a method is bound, or None""" - return isinstance(object, types.BuiltinFunctionType) + return isinstance(object, types.BuiltinFunctionType) or _is_cpyext_function(object) def isroutine(object): """Return true if the object is any kind of function or method.""" diff --git a/lib-python/2.7/test/test_urllib2net.py b/lib-python/2.7/test/test_urllib2net.py --- a/lib-python/2.7/test/test_urllib2net.py +++ b/lib-python/2.7/test/test_urllib2net.py @@ -286,7 +286,7 @@ self.assertEqual(u.fp._sock.fp._sock.gettimeout(), 120) u.close() - FTP_HOST = 'ftp://ftp.debian.org/debian/' + FTP_HOST = 'ftp://www.pythontest.net/' def test_ftp_basic(self): self.assertIsNone(socket.getdefaulttimeout()) diff --git a/lib-python/2.7/warnings.py b/lib-python/2.7/warnings.py --- a/lib-python/2.7/warnings.py +++ b/lib-python/2.7/warnings.py @@ -43,11 +43,12 @@ unicodetype = unicode except NameError: unicodetype = () + template = "%s: %s: %s\n" try: message = str(message) except UnicodeEncodeError: - pass - s = "%s: %s: %s\n" % (lineno, category.__name__, message) + template = unicode(template) + s = template % (lineno, category.__name__, message) line = linecache.getline(filename, lineno) if line is None else line if line: line = line.strip() diff --git a/lib_pypy/_ctypes/array.py b/lib_pypy/_ctypes/array.py --- a/lib_pypy/_ctypes/array.py +++ b/lib_pypy/_ctypes/array.py @@ -8,60 +8,63 @@ class ArrayMeta(_CDataMeta): def __new__(self, name, cls, typedict): res = type.__new__(self, name, cls, typedict) - if '_type_' in typedict: - ffiarray = _rawffi.Array(typedict['_type_']._ffishape_) - res._ffiarray = ffiarray - subletter = getattr(typedict['_type_'], '_type_', None) - if subletter == 'c': - def getvalue(self): - return _rawffi.charp2string(self._buffer.buffer, - self._length_) - def setvalue(self, val): - # we don't want to have buffers here - if len(val) > self._length_: - raise ValueError("%r too long" % (val,)) - if isinstance(val, str): - _rawffi.rawstring2charp(self._buffer.buffer, val) - else: - for i in range(len(val)): - self[i] = val[i] - if len(val) < self._length_: - self._buffer[len(val)] = '\x00' - res.value = property(getvalue, setvalue) - def getraw(self): - return _rawffi.charp2rawstring(self._buffer.buffer, - self._length_) + if cls == (_CData,): # this is the Array class defined below + res._ffiarray = None + return res + if not hasattr(res, '_length_') or not isinstance(res._length_, int): + raise AttributeError( + "class must define a '_length_' attribute, " + "which must be a positive integer") + ffiarray = res._ffiarray = _rawffi.Array(res._type_._ffishape_) + subletter = getattr(res._type_, '_type_', None) + if subletter == 'c': + def getvalue(self): + return _rawffi.charp2string(self._buffer.buffer, + self._length_) + def setvalue(self, val): + # we don't want to have buffers here + if len(val) > self._length_: + raise ValueError("%r too long" % (val,)) + if isinstance(val, str): + _rawffi.rawstring2charp(self._buffer.buffer, val) + else: + for i in range(len(val)): + self[i] = val[i] + if len(val) < self._length_: + self._buffer[len(val)] = b'\x00' + res.value = property(getvalue, setvalue) - def setraw(self, buffer): - if len(buffer) > self._length_: - raise ValueError("%r too long" % (buffer,)) - _rawffi.rawstring2charp(self._buffer.buffer, buffer) - res.raw = property(getraw, setraw) - elif subletter == 'u': - def getvalue(self): - return _rawffi.wcharp2unicode(self._buffer.buffer, - self._length_) + def getraw(self): + return _rawffi.charp2rawstring(self._buffer.buffer, + self._length_) - def setvalue(self, val): - # we don't want to have buffers here - if len(val) > self._length_: - raise ValueError("%r too long" % (val,)) - if isinstance(val, unicode): - target = self._buffer - else: - target = self - for i in range(len(val)): - target[i] = val[i] - if len(val) < self._length_: - target[len(val)] = u'\x00' - res.value = property(getvalue, setvalue) - - if '_length_' in typedict: - res._ffishape_ = (ffiarray, typedict['_length_']) - res._fficompositesize_ = res._sizeofinstances() - else: - res._ffiarray = None + def setraw(self, buffer): + if len(buffer) > self._length_: + raise ValueError("%r too long" % (buffer,)) + _rawffi.rawstring2charp(self._buffer.buffer, buffer) + res.raw = property(getraw, setraw) + elif subletter == 'u': + def getvalue(self): + return _rawffi.wcharp2unicode(self._buffer.buffer, + self._length_) + + def setvalue(self, val): + # we don't want to have buffers here + if len(val) > self._length_: + raise ValueError("%r too long" % (val,)) + if isinstance(val, unicode): + target = self._buffer + else: + target = self + for i in range(len(val)): + target[i] = val[i] + if len(val) < self._length_: + target[len(val)] = u'\x00' + res.value = property(getvalue, setvalue) + + res._ffishape_ = (ffiarray, res._length_) + res._fficompositesize_ = res._sizeofinstances() return res from_address = cdata_from_address @@ -156,7 +159,7 @@ l = [self[i] for i in range(start, stop, step)] letter = getattr(self._type_, '_type_', None) if letter == 'c': - return "".join(l) + return b"".join(l) if letter == 'u': return u"".join(l) return l 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 @@ -176,6 +176,10 @@ def _get_buffer_value(self): return self._buffer[0] + def _copy_to(self, addr): + target = type(self).from_address(addr)._buffer + target[0] = self._get_buffer_value() + def _to_ffi_param(self): if self.__class__._is_pointer_like(): return self._get_buffer_value() diff --git a/lib_pypy/_ctypes/pointer.py b/lib_pypy/_ctypes/pointer.py --- a/lib_pypy/_ctypes/pointer.py +++ b/lib_pypy/_ctypes/pointer.py @@ -114,7 +114,9 @@ cobj = self._type_.from_param(value) if ensure_objects(cobj) is not None: store_reference(self, index, cobj._objects) - self._subarray(index)[0] = cobj._get_buffer_value() + address = self._buffer[0] + address += index * sizeof(self._type_) + cobj._copy_to(address) def __nonzero__(self): return self._buffer[0] != 0 diff --git a/lib_pypy/_ctypes/structure.py b/lib_pypy/_ctypes/structure.py --- a/lib_pypy/_ctypes/structure.py +++ b/lib_pypy/_ctypes/structure.py @@ -291,6 +291,11 @@ def _get_buffer_value(self): return self._buffer.buffer + def _copy_to(self, addr): + from ctypes import memmove + origin = self._get_buffer_value() + memmove(addr, origin, self._fficompositesize_) + def _to_ffi_param(self): return self._buffer diff --git a/lib_pypy/_ctypes_test.py b/lib_pypy/_ctypes_test.py --- a/lib_pypy/_ctypes_test.py +++ b/lib_pypy/_ctypes_test.py @@ -21,5 +21,11 @@ with fp: imp.load_module('_ctypes_test', fp, filename, description) except ImportError: + if os.name == 'nt': + # hack around finding compilers on win32 + try: + import setuptools + except ImportError: + pass print('could not find _ctypes_test in %s' % output_dir) _pypy_testcapi.compile_shared('_ctypes_test.c', '_ctypes_test', output_dir) diff --git a/lib_pypy/_sqlite3.py b/lib_pypy/_sqlite3.py --- a/lib_pypy/_sqlite3.py +++ b/lib_pypy/_sqlite3.py @@ -1027,21 +1027,25 @@ if '\0' in sql: raise ValueError("the query contains a null character") - first_word = sql.lstrip().split(" ")[0].upper() - if first_word == "": + + if sql: + first_word = sql.lstrip().split()[0].upper() + if first_word == '': + self._type = _STMT_TYPE_INVALID + if first_word == "SELECT": + self._type = _STMT_TYPE_SELECT + elif first_word == "INSERT": + self._type = _STMT_TYPE_INSERT + elif first_word == "UPDATE": + self._type = _STMT_TYPE_UPDATE + elif first_word == "DELETE": + self._type = _STMT_TYPE_DELETE + elif first_word == "REPLACE": + self._type = _STMT_TYPE_REPLACE + else: + self._type = _STMT_TYPE_OTHER + else: self._type = _STMT_TYPE_INVALID - elif first_word == "SELECT": - self._type = _STMT_TYPE_SELECT - elif first_word == "INSERT": - self._type = _STMT_TYPE_INSERT - elif first_word == "UPDATE": - self._type = _STMT_TYPE_UPDATE - elif first_word == "DELETE": - self._type = _STMT_TYPE_DELETE - elif first_word == "REPLACE": - self._type = _STMT_TYPE_REPLACE - else: - self._type = _STMT_TYPE_OTHER if isinstance(sql, unicode): sql = sql.encode('utf-8') diff --git a/lib_pypy/_testcapi.py b/lib_pypy/_testcapi.py --- a/lib_pypy/_testcapi.py +++ b/lib_pypy/_testcapi.py @@ -16,4 +16,10 @@ with fp: imp.load_module('_testcapi', fp, filename, description) except ImportError: + if os.name == 'nt': + # hack around finding compilers on win32 + try: + import setuptools + except ImportError: + pass _pypy_testcapi.compile_shared(cfile, '_testcapi', output_dir) diff --git a/lib_pypy/_tkinter/app.py b/lib_pypy/_tkinter/app.py --- a/lib_pypy/_tkinter/app.py +++ b/lib_pypy/_tkinter/app.py @@ -119,7 +119,7 @@ tklib.TCL_GLOBAL_ONLY) # This is used to get the application class for Tk 4.1 and up - argv0 = className.lower() + argv0 = className.lower().encode('ascii') tklib.Tcl_SetVar(self.interp, "argv0", argv0, tklib.TCL_GLOBAL_ONLY) @@ -180,6 +180,9 @@ if err == tklib.TCL_ERROR: self.raiseTclError() + def interpaddr(self): + return int(tkffi.cast('size_t', self.interp)) + def _var_invoke(self, func, *args, **kwargs): if self.threaded and self.thread_id != tklib.Tcl_GetCurrentThread(): # The current thread is not the interpreter thread. diff --git a/pypy/doc/faq.rst b/pypy/doc/faq.rst --- a/pypy/doc/faq.rst +++ b/pypy/doc/faq.rst @@ -182,6 +182,57 @@ technical difficulties. +What about numpy, numpypy, micronumpy? +-------------------------------------- + +Way back in 2011, the PyPy team `started to reimplement`_ numpy in PyPy. It +has two pieces: + + * the builtin module :source:`pypy/module/micronumpy`: this is written in + RPython and roughly covers the content of the ``numpy.core.multiarray`` + module. Confusingly enough, this is available in PyPy under the name + ``_numpypy``. It is included by default in all the official releases of + PyPy (but it might be dropped in the future). + + * a fork_ of the official numpy repository maintained by us and informally + called ``numpypy``: even more confusing, the name of the repo on bitbucket + is ``numpy``. The main difference with the upstream numpy, is that it is + based on the micronumpy module written in RPython, instead of of + ``numpy.core.multiarray`` which is written in C. + +Moreover, it is also possible to install the upstream version of ``numpy``: +its core is written in C and it runs on PyPy under the cpyext compatibility +layer. This is what you get if you do ``pypy -m pip install numpy``. + + +Should I install numpy or numpypy? +----------------------------------- + +TL;DR version: you should use numpy. You can install it by doing ``pypy -m pip +install numpy``. You might also be interested in using the experimental `PyPy +binary wheels`_ to save compilation time. + +The upstream ``numpy`` is written in C, and runs under the cpyext +compatibility layer. Nowadays, cpyext is mature enough that you can simply +use the upstream ``numpy``, since it passes 99.9% of the test suite. At the +moment of writing (October 2017) the main drawback of ``numpy`` is that cpyext +is infamously slow, and thus it has worse performance compared to +``numpypy``. However, we are actively working on improving it, as we expect to +reach the same speed, eventually. + +On the other hand, ``numpypy`` is more JIT-friendly and very fast to call, +since it is written in RPython: but it is a reimplementation, and it's hard to +be completely compatible: over the years the project slowly matured and +eventually it was able to call out to the LAPACK and BLAS libraries to speed +matrix calculations, and reached around an 80% parity with the upstream +numpy. However, 80% is far from 100%. Since cpyext/numpy compatibility is +progressing fast, we have discontinued support for ``numpypy``. + +.. _`started to reimplement`: https://morepypy.blogspot.co.il/2011/05/numpy-in-pypy-status-and-roadmap.html +.. _fork: https://bitbucket.org/pypy/numpy +.. _`PyPy binary wheels`: https://github.com/antocuni/pypy-wheels + + Is PyPy more clever than CPython about Tail Calls? -------------------------------------------------- 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 @@ -10,3 +10,19 @@ .. branch: docs-osx-brew-openssl +.. branch: keep-debug-symbols +Add a smartstrip tool, which can optionally keep the debug symbols in a +separate file, instead of just stripping them away. Use it in packaging + +.. branch: bsd-patches +Fix failures on FreeBSD, contributed by David Naylor as patches on the issue +tracker (issues 2694, 2695, 2696, 2697) + +.. branch: run-extra-tests +Run extra_tests/ in buildbot + +.. branch: vmprof-0.4.10 +Upgrade the _vmprof backend to vmprof 0.4.10 + +.. branch: fix-vmprof-stacklet-switch +Fix a vmprof+continulets (i.e. greenelts, eventlet, gevent, ...) diff --git a/pypy/goal/getnightly.py b/pypy/goal/getnightly.py --- a/pypy/goal/getnightly.py +++ b/pypy/goal/getnightly.py @@ -15,7 +15,7 @@ arch = 'linux' cmd = 'wget "%s"' TAR_OPTIONS += ' --wildcards' - binfiles = "'*/bin/pypy' '*/bin/libpypy-c.so'" + binfiles = "'*/bin/pypy*' '*/bin/libpypy-c.so*'" if os.uname()[-1].startswith('arm'): arch += '-armhf-raspbian' elif sys.platform.startswith('darwin'): diff --git a/pypy/interpreter/pyparser/future.py b/pypy/interpreter/pyparser/future.py --- a/pypy/interpreter/pyparser/future.py +++ b/pypy/interpreter/pyparser/future.py @@ -85,13 +85,17 @@ # permissive parsing of the given list of tokens; it relies on # the real parsing done afterwards to give errors. it.skip_newlines() - it.skip_name("r") or it.skip_name("u") or it.skip_name("ru") - if it.skip(pygram.tokens.STRING): - it.skip_newlines() - while (it.skip_name("from") and + docstring_possible = True + while True: + it.skip_name("r") or it.skip_name("u") or it.skip_name("ru") + if docstring_possible and it.skip(pygram.tokens.STRING): + it.skip_newlines() + docstring_possible = False + if not (it.skip_name("from") and it.skip_name("__future__") and it.skip_name("import")): + break it.skip(pygram.tokens.LPAR) # optionally # return in 'last_position' any line-column pair that points # somewhere inside the last __future__ import statement diff --git a/pypy/interpreter/pyparser/test/test_future.py b/pypy/interpreter/pyparser/test/test_future.py --- a/pypy/interpreter/pyparser/test/test_future.py +++ b/pypy/interpreter/pyparser/test/test_future.py @@ -208,3 +208,13 @@ 'from __future__ import with_statement;') f = run(s, (2, 23)) assert f == fut.CO_FUTURE_DIVISION | fut.CO_FUTURE_WITH_STATEMENT + +def test_future_doc_future(): + # for some reason people do this :-[ + s = ''' +from __future__ import generators +"Docstring" +from __future__ import division + ''' + f = run(s, (4, 24)) + assert f == fut.CO_FUTURE_DIVISION | fut.CO_GENERATOR_ALLOWED diff --git a/pypy/module/_continuation/test/test_stacklet.py b/pypy/module/_continuation/test/test_stacklet.py --- a/pypy/module/_continuation/test/test_stacklet.py +++ b/pypy/module/_continuation/test/test_stacklet.py @@ -8,6 +8,35 @@ cls.w_translated = cls.space.wrap( os.path.join(os.path.dirname(__file__), 'test_translated.py')) + cls.w_stack = cls.space.appexec([], """(): + import sys + def stack(f=None): + ''' + get the call-stack of the caller or the specified frame + ''' + if f is None: + f = sys._getframe(1) + res = [] + seen = set() + while f: + if f in seen: + # frame cycle + res.append('...') + break + if f.f_code.co_name == 'runtest': + # if we are running with -A, cut all the stack above + # the test function + break + seen.add(f) + res.append(f.f_code.co_name) + f = f.f_back + #print res + return res + return stack + """) + if cls.runappdirect: + # make sure that "self.stack" does not pass the self + cls.w_stack = staticmethod(cls.w_stack.im_func) def test_new_empty(self): from _continuation import continulet @@ -290,66 +319,100 @@ def test_random_switching(self): from _continuation import continulet # + seen = [] + # def t1(c1): - return c1.switch() + seen.append(3) + res = c1.switch() + seen.append(6) + return res + # def s1(c1, n): + seen.append(2) assert n == 123 c2 = t1(c1) - return c1.switch('a') + 1 + seen.append(7) + res = c1.switch('a') + 1 + seen.append(10) + return res # def s2(c2, c1): + seen.append(5) res = c1.switch(c2) + seen.append(8) assert res == 'a' - return c2.switch('b') + 2 + res = c2.switch('b') + 2 + seen.append(12) + return res # def f(): + seen.append(1) c1 = continulet(s1, 123) c2 = continulet(s2, c1) c1.switch() + seen.append(4) res = c2.switch() + seen.append(9) assert res == 'b' res = c1.switch(1000) + seen.append(11) assert res == 1001 - return c2.switch(2000) + res = c2.switch(2000) + seen.append(13) + return res # res = f() assert res == 2002 + assert seen == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] def test_f_back(self): import sys from _continuation import continulet + stack = self.stack # - def g(c): + def bar(c): + assert stack() == ['bar', 'foo', 'test_f_back'] c.switch(sys._getframe(0)) c.switch(sys._getframe(0).f_back) c.switch(sys._getframe(1)) + # + assert stack() == ['bar', 'foo', 'main', 'test_f_back'] c.switch(sys._getframe(1).f_back) - assert sys._getframe(2) is f3.f_back + # + assert stack() == ['bar', 'foo', 'main2', 'test_f_back'] + assert sys._getframe(2) is f3_foo.f_back c.switch(sys._getframe(2)) - def f(c): - g(c) + def foo(c): + bar(c) # - c = continulet(f) - f1 = c.switch() - assert f1.f_code.co_name == 'g' - f2 = c.switch() - assert f2.f_code.co_name == 'f' - f3 = c.switch() - assert f3 is f2 - assert f1.f_back is f3 + assert stack() == ['test_f_back'] + c = continulet(foo) + f1_bar = c.switch() + assert f1_bar.f_code.co_name == 'bar' + f2_foo = c.switch() + assert f2_foo.f_code.co_name == 'foo' + f3_foo = c.switch() + assert f3_foo is f2_foo + assert f1_bar.f_back is f3_foo + # def main(): - f4 = c.switch() - assert f4.f_code.co_name == 'main', repr(f4.f_code.co_name) - assert f3.f_back is f1 # not running, so a loop + f4_main = c.switch() + assert f4_main.f_code.co_name == 'main' + assert f3_foo.f_back is f1_bar # not running, so a loop + assert stack() == ['main', 'test_f_back'] + assert stack(f1_bar) == ['bar', 'foo', '...'] + # def main2(): - f5 = c.switch() - assert f5.f_code.co_name == 'main2', repr(f5.f_code.co_name) - assert f3.f_back is f1 # not running, so a loop + f5_main2 = c.switch() + assert f5_main2.f_code.co_name == 'main2' + assert f3_foo.f_back is f1_bar # not running, so a loop + assert stack(f1_bar) == ['bar', 'foo', '...'] + # main() main2() res = c.switch() assert res is None - assert f3.f_back is None + assert f3_foo.f_back is None def test_traceback_is_complete(self): import sys diff --git a/pypy/module/_continuation/test/test_translated.py b/pypy/module/_continuation/test/test_translated.py --- a/pypy/module/_continuation/test/test_translated.py +++ b/pypy/module/_continuation/test/test_translated.py @@ -5,6 +5,7 @@ py.test.skip("to run on top of a translated pypy-c") import sys, random +from rpython.tool.udir import udir # ____________________________________________________________ @@ -92,6 +93,33 @@ from pypy.conftest import option if not option.runappdirect: py.test.skip("meant only for -A run") + cls.w_vmprof_file = cls.space.wrap(str(udir.join('profile.vmprof'))) + + def test_vmprof(self): + """ + The point of this test is to check that we do NOT segfault. In + particular, we need to ensure that vmprof does not sample the stack in + the middle of a switch, else we read nonsense. + """ + try: + import _vmprof + except ImportError: + py.test.skip("no _vmprof") + # + def switch_forever(c): + while True: + c.switch() + # + f = open(self.vmprof_file, 'w+b') + _vmprof.enable(f.fileno(), 1/250.0, False, False, False, False) + c = _continuation.continulet(switch_forever) + for i in range(10**7): + if i % 100000 == 0: + print i + c.switch() + _vmprof.disable() + f.close() + def _setup(): for _i in range(20): diff --git a/pypy/module/_cppyy/__init__.py b/pypy/module/_cppyy/__init__.py --- a/pypy/module/_cppyy/__init__.py +++ b/pypy/module/_cppyy/__init__.py @@ -7,7 +7,7 @@ interpleveldefs = { '_resolve_name' : 'interp_cppyy.resolve_name', '_scope_byname' : 'interp_cppyy.scope_byname', - '_template_byname' : 'interp_cppyy.template_byname', + '_is_template' : 'interp_cppyy.is_template', '_std_string_name' : 'interp_cppyy.std_string_name', '_set_class_generator' : 'interp_cppyy.set_class_generator', '_set_function_generator': 'interp_cppyy.set_function_generator', @@ -15,7 +15,9 @@ '_get_nullptr' : 'interp_cppyy.get_nullptr', 'CPPClassBase' : 'interp_cppyy.W_CPPClass', 'addressof' : 'interp_cppyy.addressof', + '_bind_object' : 'interp_cppyy._bind_object', 'bind_object' : 'interp_cppyy.bind_object', + 'move' : 'interp_cppyy.move', } appleveldefs = { diff --git a/pypy/module/_cppyy/capi/loadable_capi.py b/pypy/module/_cppyy/capi/loadable_capi.py --- a/pypy/module/_cppyy/capi/loadable_capi.py +++ b/pypy/module/_cppyy/capi/loadable_capi.py @@ -217,7 +217,8 @@ 'method_req_args' : ([c_scope, c_index], c_int), 'method_arg_type' : ([c_scope, c_index, c_int], c_ccharp), 'method_arg_default' : ([c_scope, c_index, c_int], c_ccharp), - 'method_signature' : ([c_scope, c_index], c_ccharp), + 'method_signature' : ([c_scope, c_index, c_int], c_ccharp), + 'method_prototype' : ([c_scope, c_index, c_int], c_ccharp), 'method_is_template' : ([c_scope, c_index], c_int), 'method_num_template_args' : ([c_scope, c_index], c_int), @@ -498,9 +499,12 @@ def c_method_arg_default(space, cppscope, index, arg_index): args = [_ArgH(cppscope.handle), _ArgL(index), _ArgL(arg_index)] return charp2str_free(space, call_capi(space, 'method_arg_default', args)) -def c_method_signature(space, cppscope, index): - args = [_ArgH(cppscope.handle), _ArgL(index)] +def c_method_signature(space, cppscope, index, show_formalargs=True): + args = [_ArgH(cppscope.handle), _ArgL(index), _ArgL(show_formalargs)] return charp2str_free(space, call_capi(space, 'method_signature', args)) +def c_method_prototype(space, cppscope, index, show_formalargs=True): + args = [_ArgH(cppscope.handle), _ArgL(index), _ArgL(show_formalargs)] + return charp2str_free(space, call_capi(space, 'method_prototype', args)) def c_method_is_template(space, cppscope, index): args = [_ArgH(cppscope.handle), _ArgL(index)] diff --git a/pypy/module/_cppyy/converter.py b/pypy/module/_cppyy/converter.py --- a/pypy/module/_cppyy/converter.py +++ b/pypy/module/_cppyy/converter.py @@ -4,7 +4,7 @@ from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib.rarithmetic import r_singlefloat, r_longfloat -from rpython.rlib import rfloat +from rpython.rlib import rfloat, rawrefcount from pypy.module._rawffi.interp_rawffi import letter2tp from pypy.module._rawffi.array import W_Array, W_ArrayInstance @@ -21,9 +21,9 @@ # match for the qualified type. -def get_rawobject(space, w_obj): +def get_rawobject(space, w_obj, can_be_None=True): from pypy.module._cppyy.interp_cppyy import W_CPPClass - cppinstance = space.interp_w(W_CPPClass, w_obj, can_be_None=True) + cppinstance = space.interp_w(W_CPPClass, w_obj, can_be_None=can_be_None) if cppinstance: rawobject = cppinstance.get_rawobject() assert lltype.typeOf(rawobject) == capi.C_OBJECT @@ -48,17 +48,16 @@ return capi.C_NULL_OBJECT def is_nullpointer_specialcase(space, w_obj): - # 0, None, and nullptr may serve as "NULL", check for any of them + # 0 and nullptr may serve as "NULL" # integer 0 try: return space.int_w(w_obj) == 0 except Exception: pass - # None or nullptr + # C++-style nullptr from pypy.module._cppyy import interp_cppyy - return space.is_true(space.is_(w_obj, space.w_None)) or \ - space.is_true(space.is_(w_obj, interp_cppyy.get_nullptr(space))) + return space.is_true(space.is_(w_obj, interp_cppyy.get_nullptr(space))) def get_rawbuffer(space, w_obj): # raw buffer @@ -74,7 +73,7 @@ return rffi.cast(rffi.VOIDP, space.uint_w(arr.getbuffer(space))) except Exception: pass - # pre-defined NULL + # pre-defined nullptr if is_nullpointer_specialcase(space, w_obj): return rffi.cast(rffi.VOIDP, 0) raise TypeError("not an addressable buffer") @@ -392,6 +391,7 @@ _immutable_fields_ = ['typecode'] typecode = 'g' + class CStringConverter(TypeConverter): def convert_argument(self, space, w_obj, address, call_local): x = rffi.cast(rffi.LONGP, address) @@ -408,18 +408,27 @@ def free_argument(self, space, arg, call_local): lltype.free(rffi.cast(rffi.CCHARPP, arg)[0], flavor='raw') +class CStringConverterWithSize(CStringConverter): + _immutable_fields_ = ['size'] + + def __init__(self, space, extra): + self.size = extra + + def from_memory(self, space, w_obj, w_pycppclass, offset): + address = self._get_raw_address(space, w_obj, offset) + charpptr = rffi.cast(rffi.CCHARP, address) + strsize = self.size + if charpptr[self.size-1] == '\0': + strsize = self.size-1 # rffi will add \0 back + return space.newbytes(rffi.charpsize2str(charpptr, strsize)) + class VoidPtrConverter(TypeConverter): def _unwrap_object(self, space, w_obj): try: obj = get_rawbuffer(space, w_obj) except TypeError: - try: - # TODO: accept a 'capsule' rather than naked int - # (do accept int(0), though) - obj = rffi.cast(rffi.VOIDP, space.uint_w(w_obj)) - except Exception: - obj = rffi.cast(rffi.VOIDP, get_rawobject(space, w_obj)) + obj = rffi.cast(rffi.VOIDP, get_rawobject(space, w_obj, False)) return obj def cffi_type(self, space): @@ -463,12 +472,12 @@ def convert_argument(self, space, w_obj, address, call_local): x = rffi.cast(rffi.VOIDPP, address) ba = rffi.cast(rffi.CCHARP, address) - r = rffi.cast(rffi.VOIDPP, call_local) try: - r[0] = get_rawbuffer(space, w_obj) + x[0] = get_rawbuffer(space, w_obj) except TypeError: + r = rffi.cast(rffi.VOIDPP, call_local) r[0] = rffi.cast(rffi.VOIDP, get_rawobject(space, w_obj)) - x[0] = rffi.cast(rffi.VOIDP, call_local) + x[0] = rffi.cast(rffi.VOIDP, call_local) ba[capi.c_function_arg_typeoffset(space)] = self.typecode def finalize_call(self, space, w_obj, call_local): @@ -495,9 +504,13 @@ def _unwrap_object(self, space, w_obj): from pypy.module._cppyy.interp_cppyy import W_CPPClass if isinstance(w_obj, W_CPPClass): - if capi.c_is_subtype(space, w_obj.cppclass, self.clsdecl): + from pypy.module._cppyy.interp_cppyy import INSTANCE_FLAGS_IS_R_VALUE + if w_obj.flags & INSTANCE_FLAGS_IS_R_VALUE: + # reject moves as all are explicit + raise ValueError("lvalue expected") + if capi.c_is_subtype(space, w_obj.clsdecl, self.clsdecl): rawobject = w_obj.get_rawobject() - offset = capi.c_base_offset(space, w_obj.cppclass, self.clsdecl, rawobject, 1) + offset = capi.c_base_offset(space, w_obj.clsdecl, self.clsdecl, rawobject, 1) obj_address = capi.direct_ptradd(rawobject, offset) return rffi.cast(capi.C_OBJECT, obj_address) raise oefmt(space.w_TypeError, @@ -518,6 +531,17 @@ x = rffi.cast(rffi.VOIDPP, address) x[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_obj)) +class InstanceMoveConverter(InstanceRefConverter): + def _unwrap_object(self, space, w_obj): + # moving is same as by-ref, but have to check that move is allowed + from pypy.module._cppyy.interp_cppyy import W_CPPClass, INSTANCE_FLAGS_IS_R_VALUE + if isinstance(w_obj, W_CPPClass): + if w_obj.flags & INSTANCE_FLAGS_IS_R_VALUE: + w_obj.flags &= ~INSTANCE_FLAGS_IS_R_VALUE + return InstanceRefConverter._unwrap_object(self, space, w_obj) + raise oefmt(space.w_ValueError, "object is not an rvalue") + + class InstanceConverter(InstanceRefConverter): def convert_argument_libffi(self, space, w_obj, address, call_local): @@ -527,7 +551,7 @@ def from_memory(self, space, w_obj, w_pycppclass, offset): address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) from pypy.module._cppyy import interp_cppyy - return interp_cppyy.wrap_cppobject(space, address, self.clsdecl, do_cast=False) + return interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, do_cast=False) def to_memory(self, space, w_obj, w_value, offset): self._is_abstract(space) @@ -548,7 +572,7 @@ def from_memory(self, space, w_obj, w_pycppclass, offset): address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) from pypy.module._cppyy import interp_cppyy - return interp_cppyy.wrap_cppobject(space, address, self.clsdecl, do_cast=False) + return interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, do_cast=False) def to_memory(self, space, w_obj, w_value, offset): address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, offset)) @@ -582,8 +606,8 @@ def from_memory(self, space, w_obj, w_pycppclass, offset): address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) from pypy.module._cppyy import interp_cppyy - return interp_cppyy.wrap_cppobject(space, address, self.clsdecl, - do_cast=False, is_ref=True) + return interp_cppyy.wrap_cppinstance( + space, address, self.clsdecl, do_cast=False, is_ref=True) class StdStringConverter(InstanceConverter): @@ -606,7 +630,7 @@ assign = self.clsdecl.get_overload("__assign__") from pypy.module._cppyy import interp_cppyy assign.call( - interp_cppyy.wrap_cppobject(space, address, self.clsdecl, do_cast=False), [w_value]) + interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, do_cast=False), [w_value]) except Exception: InstanceConverter.to_memory(self, space, w_obj, w_value, offset) @@ -672,7 +696,7 @@ _converters = {} # builtin and custom types _a_converters = {} # array and ptr versions of above -def get_converter(space, name, default): +def get_converter(space, _name, default): # The matching of the name to a converter should follow: # 1) full, exact match # 1a) const-removed match @@ -680,9 +704,9 @@ # 3) accept ref as pointer (for the stubs, const& can be # by value, but that does not work for the ffi path) # 4) generalized cases (covers basically all user classes) - # 5) void converter, which fails on use + # 5) void* or void converter (which fails on use) - name = capi.c_resolve_name(space, name) + name = capi.c_resolve_name(space, _name) # 1) full, exact match try: @@ -701,7 +725,7 @@ clean_name = capi.c_resolve_name(space, helper.clean_type(name)) try: # array_index may be negative to indicate no size or no size found - array_size = helper.array_size(name) + array_size = helper.array_size(_name) # uses original arg return _a_converters[clean_name+compound](space, array_size) except KeyError: pass @@ -719,6 +743,8 @@ return InstancePtrConverter(space, clsdecl) elif compound == "&": return InstanceRefConverter(space, clsdecl) + elif compound == "&&": + return InstanceMoveConverter(space, clsdecl) elif compound == "**": return InstancePtrPtrConverter(space, clsdecl) elif compound == "": @@ -726,11 +752,13 @@ elif capi.c_is_enum(space, clean_name): return _converters['unsigned'](space, default) - # 5) void converter, which fails on use - # + # 5) void* or void converter (which fails on use) + if 0 <= compound.find('*'): + return VoidPtrConverter(space, default) # "user knows best" + # return a void converter here, so that the class can be build even - # when some types are unknown; this overload will simply fail on use - return VoidConverter(space, name) + # when some types are unknown + return VoidConverter(space, name) # fails on use _converters["bool"] = BoolConverter @@ -847,6 +875,10 @@ for name in names: _a_converters[name+'[]'] = ArrayConverter _a_converters[name+'*'] = PtrConverter + + # special case, const char* w/ size and w/o '\0' + _a_converters["const char[]"] = CStringConverterWithSize + _build_array_converters() # add another set of aliased names diff --git a/pypy/module/_cppyy/executor.py b/pypy/module/_cppyy/executor.py --- a/pypy/module/_cppyy/executor.py +++ b/pypy/module/_cppyy/executor.py @@ -159,7 +159,7 @@ from pypy.module._cppyy import interp_cppyy long_result = capi.c_call_l(space, cppmethod, cppthis, num_args, args) ptr_result = rffi.cast(capi.C_OBJECT, long_result) - pyres = interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass) + pyres = interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass) return pyres def execute_libffi(self, space, cif_descr, funcaddr, buffer): @@ -167,7 +167,7 @@ result = rffi.ptradd(buffer, cif_descr.exchange_result) from pypy.module._cppyy import interp_cppyy ptr_result = rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, result)[0]) - return interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass) + return interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass) class InstancePtrPtrExecutor(InstancePtrExecutor): @@ -176,7 +176,7 @@ voidp_result = capi.c_call_r(space, cppmethod, cppthis, num_args, args) ref_address = rffi.cast(rffi.VOIDPP, voidp_result) ptr_result = rffi.cast(capi.C_OBJECT, ref_address[0]) - return interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass) + return interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass) def execute_libffi(self, space, cif_descr, funcaddr, buffer): from pypy.module._cppyy.interp_cppyy import FastCallNotPossible @@ -188,8 +188,8 @@ from pypy.module._cppyy import interp_cppyy long_result = capi.c_call_o(space, cppmethod, cppthis, num_args, args, self.cppclass) ptr_result = rffi.cast(capi.C_OBJECT, long_result) - return interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass, - do_cast=False, python_owns=True, fresh=True) + return interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass, + do_cast=False, python_owns=True, fresh=True) def execute_libffi(self, space, cif_descr, funcaddr, buffer): from pypy.module._cppyy.interp_cppyy import FastCallNotPossible diff --git a/pypy/module/_cppyy/include/capi.h b/pypy/module/_cppyy/include/capi.h --- a/pypy/module/_cppyy/include/capi.h +++ b/pypy/module/_cppyy/include/capi.h @@ -19,14 +19,15 @@ RPY_EXTERN int cppyy_num_scopes(cppyy_scope_t parent); RPY_EXTERN - char* cppyy_scope_name(cppyy_scope_t parent, int iscope); - + char* cppyy_scope_name(cppyy_scope_t parent, cppyy_index_t iscope); RPY_EXTERN char* cppyy_resolve_name(const char* cppitem_name); RPY_EXTERN cppyy_scope_t cppyy_get_scope(const char* scope_name); RPY_EXTERN cppyy_type_t cppyy_actual_class(cppyy_type_t klass, cppyy_object_t obj); + RPY_EXTERN + size_t cppyy_size_of(cppyy_type_t klass); /* memory management ------------------------------------------------------ */ RPY_EXTERN @@ -120,6 +121,8 @@ RPY_EXTERN char* cppyy_method_name(cppyy_scope_t scope, cppyy_index_t idx); RPY_EXTERN + char* cppyy_method_mangled_name(cppyy_scope_t scope, cppyy_index_t idx); + RPY_EXTERN char* cppyy_method_result_type(cppyy_scope_t scope, cppyy_index_t idx); RPY_EXTERN int cppyy_method_num_args(cppyy_scope_t scope, cppyy_index_t idx); @@ -130,7 +133,9 @@ RPY_EXTERN char* cppyy_method_arg_default(cppyy_scope_t scope, cppyy_index_t idx, int arg_index); RPY_EXTERN - char* cppyy_method_signature(cppyy_scope_t scope, cppyy_index_t idx); + char* cppyy_method_signature(cppyy_scope_t scope, cppyy_index_t idx, int show_formalargs); + RPY_EXTERN + char* cppyy_method_prototype(cppyy_scope_t scope, cppyy_index_t idx, int show_formalargs); RPY_EXTERN int cppyy_method_is_template(cppyy_scope_t scope, cppyy_index_t idx); @@ -147,8 +152,12 @@ /* method properties ------------------------------------------------------ */ RPY_EXTERN + int cppyy_is_publicmethod(cppyy_type_t type, cppyy_index_t idx); + RPY_EXTERN int cppyy_is_constructor(cppyy_type_t type, cppyy_index_t idx); RPY_EXTERN + int cppyy_is_destructor(cppyy_type_t type, cppyy_index_t idx); + RPY_EXTERN int cppyy_is_staticmethod(cppyy_type_t type, cppyy_index_t idx); /* data member reflection information ------------------------------------- */ diff --git a/pypy/module/_cppyy/interp_cppyy.py b/pypy/module/_cppyy/interp_cppyy.py --- a/pypy/module/_cppyy/interp_cppyy.py +++ b/pypy/module/_cppyy/interp_cppyy.py @@ -2,7 +2,7 @@ from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.gateway import interp2app, unwrap_spec -from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty, interp_attrproperty_w +from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty from pypy.interpreter.baseobjspace import W_Root from rpython.rtyper.lltypesystem import rffi, lltype, llmemory @@ -15,6 +15,10 @@ from pypy.module._cppyy import converter, executor, ffitypes, helper +INSTANCE_FLAGS_PYTHON_OWNS = 0x0001 +INSTANCE_FLAGS_IS_REF = 0x0002 +INSTANCE_FLAGS_IS_R_VALUE = 0x0004 + class FastCallNotPossible(Exception): pass @@ -33,16 +37,21 @@ class State(object): def __init__(self, space): + # final scoped name -> opaque handle self.cppscope_cache = { - "void" : W_CPPClassDecl(space, "void", capi.C_NULL_TYPE) } + 'void' : W_CPPClassDecl(space, capi.C_NULL_TYPE, 'void') } + # opaque handle -> app-level python class + self.cppclass_registry = {} + # app-level class generator callback + self.w_clgen_callback = None + # app-level function generator callback (currently not used) + self.w_fngen_callback = None + # C++11's nullptr self.w_nullptr = None - self.cpptemplate_cache = {} - self.cppclass_registry = {} - self.w_clgen_callback = None - self.w_fngen_callback = None def get_nullptr(space): - if hasattr(space, "fake"): + # construct a unique address that compares to NULL, serves as nullptr + if hasattr(space, 'fake'): raise NotImplementedError state = space.fromcache(State) if state.w_nullptr is None: @@ -58,52 +67,48 @@ state.w_nullptr = nullarr return state.w_nullptr -@unwrap_spec(name='text') -def resolve_name(space, name): - return space.newtext(capi.c_resolve_name(space, name)) +@unwrap_spec(scoped_name='text') +def resolve_name(space, scoped_name): + return space.newtext(capi.c_resolve_name(space, scoped_name)) -@unwrap_spec(name='text') -def scope_byname(space, name): - true_name = capi.c_resolve_name(space, name) +# memoized lookup of handles by final, scoped, name of classes/namespaces +@unwrap_spec(final_scoped_name='text') +def scope_byname(space, final_scoped_name): state = space.fromcache(State) try: - return state.cppscope_cache[true_name] + return state.cppscope_cache[final_scoped_name] except KeyError: pass - opaque_handle = capi.c_get_scope_opaque(space, true_name) + opaque_handle = capi.c_get_scope_opaque(space, final_scoped_name) assert lltype.typeOf(opaque_handle) == capi.C_SCOPE if opaque_handle: - final_name = capi.c_final_name(space, opaque_handle) - if capi.c_is_namespace(space, opaque_handle): - cppscope = W_CPPNamespaceDecl(space, final_name, opaque_handle) - elif capi.c_has_complex_hierarchy(space, opaque_handle): - cppscope = W_CPPComplexClassDecl(space, final_name, opaque_handle) + isns = capi.c_is_namespace(space, opaque_handle) + if isns: + cppscope = W_CPPNamespaceDecl(space, opaque_handle, final_scoped_name) else: - cppscope = W_CPPClassDecl(space, final_name, opaque_handle) - state.cppscope_cache[name] = cppscope + if capi.c_has_complex_hierarchy(space, opaque_handle): + cppscope = W_CPPComplexClassDecl(space, opaque_handle, final_scoped_name) + else: + cppscope = W_CPPClassDecl(space, opaque_handle, final_scoped_name) - cppscope._build_methods() - cppscope._find_datamembers() + # store in the cache to prevent recursion + state.cppscope_cache[final_scoped_name] = cppscope + + if not isns: + # build methods/data; TODO: also defer this for classes (a functional __dir__ + # and instrospection for help() is enough and allows more lazy loading) + cppscope._build_methods() + cppscope._find_datamembers() + return cppscope return None -@unwrap_spec(name='text') -def template_byname(space, name): - state = space.fromcache(State) - try: - return state.cpptemplate_cache[name] - except KeyError: - pass - - if capi.c_is_template(space, name): - cpptemplate = W_CPPTemplateType(space, name) - state.cpptemplate_cache[name] = cpptemplate - return cpptemplate - - return None +@unwrap_spec(final_scoped_name='text') +def is_template(space, final_scoped_name): + return space.newbool(capi.c_is_template(space, final_scoped_name)) def std_string_name(space): return space.newtext(capi.std_string_name) @@ -189,8 +194,13 @@ # check number of given arguments against required (== total - defaults) args_expected = len(self.arg_defs) args_given = len(args_w) - if args_expected < args_given or args_given < self.args_required: - raise oefmt(self.space.w_TypeError, "wrong number of arguments") + + if args_given < self.args_required: + raise oefmt(self.space.w_TypeError, + "takes at least %d arguments (%d given)", self.args_required, args_given) + elif args_expected < args_given: + raise oefmt(self.space.w_TypeError, + "takes at most %d arguments (%d given)", args_expected, args_given) # initial setup of converters, executors, and libffi (if available) if self.converters is None: @@ -376,8 +386,11 @@ conv.free_argument(self.space, rffi.cast(capi.C_OBJECT, arg_i), loc_i) capi.c_deallocate_function_args(self.space, args) - def signature(self): - return capi.c_method_signature(self.space, self.scope, self.index) + def signature(self, show_formalargs=True): + return capi.c_method_signature(self.space, self.scope, self.index, show_formalargs) + + def prototype(self, show_formalargs=True): + return capi.c_method_prototype(self.space, self.scope, self.index, show_formalargs) def priority(self): total_arg_priority = 0 @@ -391,7 +404,7 @@ lltype.free(self.cif_descr, flavor='raw') def __repr__(self): - return "CPPMethod: %s" % self.signature() + return "CPPMethod: %s" % self.prototype() def _freeze_(self): assert 0, "you should never have a pre-built instance of this!" @@ -407,7 +420,7 @@ return capi.C_NULL_OBJECT def __repr__(self): - return "CPPFunction: %s" % self.signature() + return "CPPFunction: %s" % self.prototype() class CPPTemplatedCall(CPPMethod): @@ -440,7 +453,7 @@ return CPPMethod.call(self, cppthis, args_w) def __repr__(self): - return "CPPTemplatedCall: %s" % self.signature() + return "CPPTemplatedCall: %s" % self.prototype() class CPPConstructor(CPPMethod): @@ -462,7 +475,7 @@ return CPPMethod.call(self, cppthis, args_w) def __repr__(self): - return "CPPConstructor: %s" % self.signature() + return "CPPConstructor: %s" % self.prototype() class CPPSetItem(CPPMethod): @@ -549,12 +562,12 @@ w_exc_type = e.w_type elif all_same_type and not e.match(self.space, w_exc_type): all_same_type = False - errmsg += '\n '+cppyyfunc.signature()+' =>\n' + errmsg += '\n '+cppyyfunc.prototype()+' =>\n' errmsg += ' '+e.errorstr(self.space) except Exception as e: # can not special case this for non-overloaded functions as we anyway need an # OperationError error down from here - errmsg += '\n '+cppyyfunc.signature()+' =>\n' + errmsg += '\n '+cppyyfunc.prototype()+' =>\n' errmsg += ' Exception: '+str(e) if all_same_type and w_exc_type is not None: @@ -562,20 +575,20 @@ else: raise OperationError(self.space.w_TypeError, self.space.newtext(errmsg)) - def signature(self): - sig = self.functions[0].signature() + def prototype(self): + sig = self.functions[0].prototype() for i in range(1, len(self.functions)): - sig += '\n'+self.functions[i].signature() + sig += '\n'+self.functions[i].prototype() return self.space.newtext(sig) def __repr__(self): - return "W_CPPOverload(%s)" % [f.signature() for f in self.functions] + return "W_CPPOverload(%s)" % [f.prototype() for f in self.functions] W_CPPOverload.typedef = TypeDef( 'CPPOverload', is_static = interp2app(W_CPPOverload.is_static), call = interp2app(W_CPPOverload.call), - signature = interp2app(W_CPPOverload.signature), + prototype = interp2app(W_CPPOverload.prototype), ) @@ -591,24 +604,40 @@ @jit.unroll_safe @unwrap_spec(args_w='args_w') def call(self, w_cppinstance, args_w): + # TODO: factor out the following: + if capi.c_is_abstract(self.space, self.scope.handle): + raise oefmt(self.space.w_TypeError, + "cannot instantiate abstract class '%s'", + self.scope.name) w_result = W_CPPOverload.call(self, w_cppinstance, args_w) newthis = rffi.cast(capi.C_OBJECT, self.space.uint_w(w_result)) cppinstance = self.space.interp_w(W_CPPClass, w_cppinstance, can_be_None=True) if cppinstance is not None: cppinstance._rawobject = newthis memory_regulator.register(cppinstance) - return w_cppinstance - return wrap_cppobject(self.space, newthis, self.functions[0].scope, - do_cast=False, python_owns=True, fresh=True) def __repr__(self): - return "W_CPPConstructorOverload(%s)" % [f.signature() for f in self.functions] + return "W_CPPConstructorOverload(%s)" % [f.prototype() for f in self.functions] W_CPPConstructorOverload.typedef = TypeDef( 'CPPConstructorOverload', is_static = interp2app(W_CPPConstructorOverload.is_static), call = interp2app(W_CPPConstructorOverload.call), - signature = interp2app(W_CPPOverload.signature), + prototype = interp2app(W_CPPConstructorOverload.prototype), +) + + +class W_CPPTemplateOverload(W_CPPOverload): + @unwrap_spec(args_w='args_w') + def __getitem__(self, args_w): + pass + + def __repr__(self): + return "W_CPPTemplateOverload(%s)" % [f.prototype() for f in self.functions] + +W_CPPTemplateOverload.typedef = TypeDef( + 'CPPTemplateOverload', + __getitem__ = interp2app(W_CPPTemplateOverload.call), ) @@ -622,6 +651,9 @@ def __call__(self, args_w): return self.method.bound_call(self.cppthis, args_w) + def __repr__(self): + return "W_CPPBoundMethod(%s)" % [f.prototype() for f in self.functions] + W_CPPBoundMethod.typedef = TypeDef( 'CPPBoundMethod', __call__ = interp2app(W_CPPBoundMethod.__call__), @@ -643,8 +675,8 @@ def _get_offset(self, cppinstance): if cppinstance: - assert lltype.typeOf(cppinstance.cppclass.handle) == lltype.typeOf(self.scope.handle) - offset = self.offset + cppinstance.cppclass.get_base_offset(cppinstance, self.scope) + assert lltype.typeOf(cppinstance.clsdecl.handle) == lltype.typeOf(self.scope.handle) + offset = self.offset + cppinstance.clsdecl.get_base_offset(cppinstance, self.scope) else: offset = self.offset return offset @@ -652,7 +684,7 @@ def get(self, w_cppinstance, w_pycppclass): cppinstance = self.space.interp_w(W_CPPClass, w_cppinstance, can_be_None=True) if not cppinstance: - raise oefmt(self.space.w_ReferenceError, + raise oefmt(self.space.w_AttributeError, "attribute access requires an instance") offset = self._get_offset(cppinstance) return self.converter.from_memory(self.space, w_cppinstance, w_pycppclass, offset) @@ -660,7 +692,7 @@ def set(self, w_cppinstance, w_value): cppinstance = self.space.interp_w(W_CPPClass, w_cppinstance, can_be_None=True) if not cppinstance: - raise oefmt(self.space.w_ReferenceError, + raise oefmt(self.space.w_AttributeError, "attribute access requires an instance") offset = self._get_offset(cppinstance) self.converter.to_memory(self.space, w_cppinstance, w_value, offset) @@ -705,12 +737,12 @@ return space.w_False class W_CPPScopeDecl(W_Root): - _attrs_ = ['space', 'name', 'handle', 'methods', 'datamembers'] + _attrs_ = ['space', 'handle', 'name', 'methods', 'datamembers'] _immutable_fields_ = ['handle', 'name'] - def __init__(self, space, name, opaque_handle): + def __init__(self, space, opaque_handle, final_scoped_name): self.space = space - self.name = name + self.name = final_scoped_name assert lltype.typeOf(opaque_handle) == capi.C_SCOPE self.handle = opaque_handle self.methods = {} @@ -753,7 +785,7 @@ overload = self.get_overload(name) sig = '(%s)' % signature for f in overload.functions: - if 0 < f.signature().find(sig): + if f.signature(False) == sig: return W_CPPOverload(self.space, self, [f]) raise oefmt(self.space.w_LookupError, "no overload matches signature") @@ -769,6 +801,9 @@ # classes for inheritance. Both are python classes, though, and refactoring # may be in order at some point. class W_CPPNamespaceDecl(W_CPPScopeDecl): + _attrs_ = ['space', 'handle', 'name', 'methods', 'datamembers'] + _immutable_fields_ = ['handle', 'name'] + def _make_cppfunction(self, pyname, index): num_args = capi.c_method_num_args(self.space, self, index) args_required = capi.c_method_req_args(self.space, self, index) @@ -779,9 +814,6 @@ arg_defs.append((arg_type, arg_dflt)) return CPPFunction(self.space, self, index, arg_defs, args_required) - def _build_methods(self): - pass # force lazy lookups in namespaces - def _make_datamember(self, dm_name, dm_idx): type_name = capi.c_datamember_type(self.space, self, dm_idx) offset = capi.c_datamember_offset(self.space, self, dm_idx) @@ -791,9 +823,6 @@ self.datamembers[dm_name] = datamember return datamember - def _find_datamembers(self): - pass # force lazy lookups in namespaces - def find_overload(self, meth_name): indices = capi.c_method_indices_from_name(self.space, self, meth_name) if not indices: @@ -855,18 +884,21 @@ class W_CPPClassDecl(W_CPPScopeDecl): - _attrs_ = ['space', 'name', 'handle', 'methods', 'datamembers'] - _immutable_fields_ = ['handle', 'constructor', 'methods[*]', 'datamembers[*]'] + _attrs_ = ['space', 'handle', 'name', 'methods', 'datamembers'] + _immutable_fields_ = ['handle', 'name', 'methods[*]', 'datamembers[*]'] def _build_methods(self): assert len(self.methods) == 0 methods_temp = {} for i in range(capi.c_num_methods(self.space, self)): idx = capi.c_method_index_at(self.space, self, i) - pyname = helper.map_operator_name(self.space, - capi.c_method_name(self.space, self, idx), - capi.c_method_num_args(self.space, self, idx), - capi.c_method_result_type(self.space, self, idx)) + if capi.c_is_constructor(self.space, self, idx): + pyname = '__init__' + else: + pyname = helper.map_operator_name(self.space, + capi.c_method_name(self.space, self, idx), + capi.c_method_num_args(self.space, self, idx), + capi.c_method_result_type(self.space, self, idx)) cppmethod = self._make_cppfunction(pyname, idx) methods_temp.setdefault(pyname, []).append(cppmethod) # the following covers the case where the only kind of operator[](idx) @@ -883,7 +915,7 @@ # create the overload methods from the method sets for pyname, methods in methods_temp.iteritems(): CPPMethodSort(methods).sort() - if pyname == self.name: + if pyname == '__init__': overload = W_CPPConstructorOverload(self.space, self, methods[:]) else: overload = W_CPPOverload(self.space, self, methods[:]) @@ -934,11 +966,11 @@ raise self.missing_attribute_error(name) def get_base_offset(self, cppinstance, calling_scope): - assert self == cppinstance.cppclass + assert self == cppinstance.clsdecl return 0 def get_cppthis(self, cppinstance, calling_scope): - assert self == cppinstance.cppclass + assert self == cppinstance.clsdecl return cppinstance.get_rawobject() def is_namespace(self): @@ -973,13 +1005,13 @@ class W_CPPComplexClassDecl(W_CPPClassDecl): def get_base_offset(self, cppinstance, calling_scope): - assert self == cppinstance.cppclass + assert self == cppinstance.clsdecl offset = capi.c_base_offset(self.space, self, calling_scope, cppinstance.get_rawobject(), 1) return offset def get_cppthis(self, cppinstance, calling_scope): - assert self == cppinstance.cppclass + assert self == cppinstance.clsdecl offset = self.get_base_offset(cppinstance, calling_scope) return capi.direct_ptradd(cppinstance.get_rawobject(), offset) @@ -997,70 +1029,56 @@ W_CPPComplexClassDecl.typedef.acceptable_as_base_class = False -class W_CPPTemplateType(W_Root): - _attrs_ = ['space', 'name'] - _immutable_fields = ['name'] - - def __init__(self, space, name): - self.space = space - self.name = name - - @unwrap_spec(args_w='args_w') - def __call__(self, args_w): - # TODO: this is broken but unused (see pythonify.py) - fullname = "".join([self.name, '<', self.space.text_w(args_w[0]), '>']) - return scope_byname(self.space, fullname) - -W_CPPTemplateType.typedef = TypeDef( - 'CPPTemplateType', - __call__ = interp2app(W_CPPTemplateType.__call__), -) -W_CPPTemplateType.typedef.acceptable_as_base_class = False - - class W_CPPClass(W_Root): - _attrs_ = ['space', 'cppclass', '_rawobject', 'isref', 'python_owns', + _attrs_ = ['space', 'clsdecl', '_rawobject', 'flags', 'finalizer_registered'] - _immutable_fields_ = ["cppclass", "isref"] + _immutable_fields_ = ['clsdecl'] finalizer_registered = False - def __init__(self, space, cppclass, rawobject, isref, python_owns): + def __init__(self, space, decl, rawobject, isref, python_owns): self.space = space - self.cppclass = cppclass + self.clsdecl = decl assert lltype.typeOf(rawobject) == capi.C_OBJECT assert not isref or rawobject self._rawobject = rawobject assert not isref or not python_owns - self.isref = isref - self.python_owns = python_owns - self._opt_register_finalizer() + self.flags = 0 + if isref: + self.flags |= INSTANCE_FLAGS_IS_REF + if python_owns: + self.flags |= INSTANCE_FLAGS_PYTHON_OWNS + self._opt_register_finalizer() def _opt_register_finalizer(self): - if self.python_owns and not self.finalizer_registered \ - and not hasattr(self.space, "fake"): + if not self.finalizer_registered and not hasattr(self.space, "fake"): + assert self.flags & INSTANCE_FLAGS_PYTHON_OWNS self.register_finalizer(self.space) self.finalizer_registered = True def _nullcheck(self): - if not self._rawobject or (self.isref and not self.get_rawobject()): + if not self._rawobject or \ + ((self.flags & INSTANCE_FLAGS_IS_REF) and not self.get_rawobject()): raise oefmt(self.space.w_ReferenceError, "trying to access a NULL pointer") # allow user to determine ownership rules on a per object level def fget_python_owns(self, space): - return space.newbool(self.python_owns) + return space.newbool(bool(self.flags & INSTANCE_FLAGS_PYTHON_OWNS)) @unwrap_spec(value=bool) def fset_python_owns(self, space, value): - self.python_owns = space.is_true(value) - self._opt_register_finalizer() + if space.is_true(value): + self.flags |= INSTANCE_FLAGS_PYTHON_OWNS + self._opt_register_finalizer() + else: + self.flags &= ~INSTANCE_FLAGS_PYTHON_OWNS def get_cppthis(self, calling_scope): - return self.cppclass.get_cppthis(self, calling_scope) + return self.clsdecl.get_cppthis(self, calling_scope) def get_rawobject(self): - if not self.isref: + if not (self.flags & INSTANCE_FLAGS_IS_REF): return self._rawobject else: ptrptr = rffi.cast(rffi.VOIDPP, self._rawobject) @@ -1078,12 +1096,9 @@ return None def instance__init__(self, args_w): - if capi.c_is_abstract(self.space, self.cppclass.handle): - raise oefmt(self.space.w_TypeError, - "cannot instantiate abstract class '%s'", - self.cppclass.name) - constructor_overload = self.cppclass.get_overload(self.cppclass.name) - constructor_overload.call(self, args_w) + raise oefmt(self.space.w_TypeError, + "cannot instantiate abstract class '%s'", + self.clsdecl.name) def instance__eq__(self, w_other): # special case: if other is None, compare pointer-style @@ -1099,7 +1114,7 @@ for name in ["", "__gnu_cxx", "__1"]: nss = scope_byname(self.space, name) meth_idx = capi.c_get_global_operator( - self.space, nss, self.cppclass, other.cppclass, "operator==") + self.space, nss, self.clsdecl, other.clsdecl, "operator==") if meth_idx != -1: f = nss._make_cppfunction("operator==", meth_idx) ol = W_CPPOverload(self.space, nss, [f]) @@ -1118,14 +1133,15 @@ # fallback 2: direct pointer comparison (the class comparison is needed since # the first data member in a struct and the struct have the same address) other = self.space.interp_w(W_CPPClass, w_other, can_be_None=False) # TODO: factor out - iseq = (self._rawobject == other._rawobject) and (self.cppclass == other.cppclass) + iseq = (self._rawobject == other._rawobject) and (self.clsdecl == other.clsdecl) return self.space.newbool(iseq) def instance__ne__(self, w_other): return self.space.not_(self.instance__eq__(w_other)) def instance__nonzero__(self): - if not self._rawobject or (self.isref and not self.get_rawobject()): + if not self._rawobject or \ + ((self.flags & INSTANCE_FLAGS_IS_REF) and not self.get_rawobject()): return self.space.w_False return self.space.w_True @@ -1134,36 +1150,35 @@ if w_as_builtin is not None: return self.space.len(w_as_builtin) raise oefmt(self.space.w_TypeError, - "'%s' has no length", self.cppclass.name) + "'%s' has no length", self.clsdecl.name) def instance__cmp__(self, w_other): w_as_builtin = self._get_as_builtin() if w_as_builtin is not None: return self.space.cmp(w_as_builtin, w_other) raise oefmt(self.space.w_AttributeError, - "'%s' has no attribute __cmp__", self.cppclass.name) + "'%s' has no attribute __cmp__", self.clsdecl.name) def instance__repr__(self): w_as_builtin = self._get_as_builtin() if w_as_builtin is not None: return self.space.repr(w_as_builtin) return self.space.newtext("<%s object at 0x%x>" % - (self.cppclass.name, rffi.cast(rffi.ULONG, self.get_rawobject()))) + (self.clsdecl.name, rffi.cast(rffi.ULONG, self.get_rawobject()))) def destruct(self): - if self._rawobject and not self.isref: + if self._rawobject and not (self.flags & INSTANCE_FLAGS_IS_REF): memory_regulator.unregister(self) - capi.c_destruct(self.space, self.cppclass, self._rawobject) + capi.c_destruct(self.space, self.clsdecl, self._rawobject) self._rawobject = capi.C_NULL_OBJECT def _finalize_(self): - if self.python_owns: + if self.flags & INSTANCE_FLAGS_PYTHON_OWNS: self.destruct() W_CPPClass.typedef = TypeDef( 'CPPClass', - cppclass = interp_attrproperty_w('cppclass', cls=W_CPPClass), - _python_owns = GetSetProperty(W_CPPClass.fget_python_owns, W_CPPClass.fset_python_owns), + __python_owns__ = GetSetProperty(W_CPPClass.fget_python_owns, W_CPPClass.fset_python_owns), __init__ = interp2app(W_CPPClass.instance__init__), __eq__ = interp2app(W_CPPClass.instance__eq__), __ne__ = interp2app(W_CPPClass.instance__ne__), @@ -1220,21 +1235,21 @@ state = space.fromcache(State) return space.call_function(state.w_fngen_callback, w_callable, space.newint(npar)) -def wrap_cppobject(space, rawobject, cppclass, - do_cast=True, python_owns=False, is_ref=False, fresh=False): +def wrap_cppinstance(space, rawobject, clsdecl, + do_cast=True, python_owns=False, is_ref=False, fresh=False): rawobject = rffi.cast(capi.C_OBJECT, rawobject) # cast to actual if requested and possible w_pycppclass = None if do_cast and rawobject: - actual = capi.c_actual_class(space, cppclass, rawobject) - if actual != cppclass.handle: + actual = capi.c_actual_class(space, clsdecl, rawobject) + if actual != clsdecl.handle: try: w_pycppclass = get_pythonized_cppclass(space, actual) - offset = capi.c_base_offset1(space, actual, cppclass, rawobject, -1) + offset = capi.c_base_offset1(space, actual, clsdecl, rawobject, -1) rawobject = capi.direct_ptradd(rawobject, offset) - w_cppclass = space.findattr(w_pycppclass, space.newtext("__cppdecl__")) - cppclass = space.interp_w(W_CPPClassDecl, w_cppclass, can_be_None=False) + w_cppdecl = space.findattr(w_pycppclass, space.newtext("__cppdecl__")) + clsdecl = space.interp_w(W_CPPClassDecl, w_cppdecl, can_be_None=False) except Exception: # failed to locate/build the derived class, so stick to the base (note # that only get_pythonized_cppclass is expected to raise, so none of @@ -1242,18 +1257,18 @@ pass if w_pycppclass is None: - w_pycppclass = get_pythonized_cppclass(space, cppclass.handle) + w_pycppclass = get_pythonized_cppclass(space, clsdecl.handle) # try to recycle existing object if this one is not newly created if not fresh and rawobject: obj = memory_regulator.retrieve(rawobject) - if obj is not None and obj.cppclass is cppclass: + if obj is not None and obj.clsdecl is clsdecl: return obj # fresh creation w_cppinstance = space.allocate_instance(W_CPPClass, w_pycppclass) cppinstance = space.interp_w(W_CPPClass, w_cppinstance, can_be_None=False) - cppinstance.__init__(space, cppclass, rawobject, is_ref, python_owns) + cppinstance.__init__(space, clsdecl, rawobject, is_ref, python_owns) memory_regulator.register(cppinstance) return w_cppinstance @@ -1264,7 +1279,7 @@ except TypeError: pass # attempt to get address of C++ instance - return rffi.cast(rffi.INTPTR_T, converter.get_rawobject(space, w_obj)) + return rffi.cast(rffi.INTPTR_T, converter.get_rawobject(space, w_obj, False)) @unwrap_spec(w_obj=W_Root) def addressof(space, w_obj): @@ -1273,19 +1288,30 @@ return space.newlong(address) @unwrap_spec(owns=bool, cast=bool) -def bind_object(space, w_obj, w_pycppclass, owns=False, cast=False): - """Takes an address and a bound C++ class proxy, returns a bound instance.""" +def _bind_object(space, w_obj, w_clsdecl, owns=False, cast=False): try: # attempt address from array or C++ instance rawobject = rffi.cast(capi.C_OBJECT, _addressof(space, w_obj)) except Exception: # accept integer value as address rawobject = rffi.cast(capi.C_OBJECT, space.uint_w(w_obj)) - w_cppclass = space.findattr(w_pycppclass, space.newtext("__cppdecl__")) - if not w_cppclass: - w_cppclass = scope_byname(space, space.text_w(w_pycppclass)) - if not w_cppclass: + decl = space.interp_w(W_CPPClassDecl, w_clsdecl, can_be_None=False) + return wrap_cppinstance(space, rawobject, decl, python_owns=owns, do_cast=cast) + +@unwrap_spec(owns=bool, cast=bool) +def bind_object(space, w_obj, w_pycppclass, owns=False, cast=False): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit