From pypy.commits at gmail.com Sun Oct 1 00:19:01 2017 From: pypy.commits at gmail.com (mattip) Date: Sat, 30 Sep 2017 21:19:01 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: more places where pypy3-c -> pypy3-c3.exe on win32, maybe refactor? Message-ID: <59d06cb5.cf97df0a.89a58.273a@mx.google.com> Author: Matti Picus Branch: py3.5 Changeset: r92523:45a53b73bc90 Date: 2017-10-01 07:17 +0300 http://bitbucket.org/pypy/pypy/changeset/45a53b73bc90/ Log: more places where pypy3-c -> pypy3-c3.exe on win32, maybe refactor? diff --git a/pypy/interpreter/test/test_app_main.py b/pypy/interpreter/test/test_app_main.py --- a/pypy/interpreter/test/test_app_main.py +++ b/pypy/interpreter/test/test_app_main.py @@ -1162,9 +1162,13 @@ import os old_sys_path = sys.path[:] sys.path.append(self.goal_dir) + if sys.platform == 'win32': + exename = 'pypy3-cw.exe' + else: + exename = 'pypy3-c' try: import app_main - pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', 'pypy3-c') + pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', exename) app_main.setup_bootstrap_path(pypy_c) newpath = sys.path[:] # we get at least lib_pypy @@ -1180,9 +1184,13 @@ import os old_sys_path = sys.path[:] sys.path.append(self.goal_dir) + if sys.platform == 'win32': + exename = 'pypy3-cw.exe' + else: + exename = 'pypy3-c' try: import app_main - pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', 'pypy3-c') + pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', exename) app_main.entry_point(pypy_c, [self.foo_py]) # assert it did not crash finally: diff --git a/testrunner/lib_python_tests.py b/testrunner/lib_python_tests.py --- a/testrunner/lib_python_tests.py +++ b/testrunner/lib_python_tests.py @@ -11,9 +11,14 @@ os.environ['PYTHONPATH'] = rootdir os.environ['PYTEST_PLUGINS'] = '' +if sys.platform == 'win32': + pypyopt = "--pypy=pypy/goal/pypy3-cw.exe" +else: + pypyopt = "--pypy=pypy/goal/pypy3-c" + popen = subprocess.Popen( [sys.executable, "pypy/test_all.py", - "--pypy=pypy/goal/pypy3-c", + pypyopt, "--timeout=3600", "-rs", "--resultlog=cpython.log", "lib-python", diff --git a/testrunner/pypyjit_tests.py b/testrunner/pypyjit_tests.py --- a/testrunner/pypyjit_tests.py +++ b/testrunner/pypyjit_tests.py @@ -11,9 +11,14 @@ os.environ['PYTHONPATH'] = rootdir os.environ['PYTEST_PLUGINS'] = '' +if sys.platform == 'win32': + pypyopt = "--pypy=pypy/goal/pypy3-cw.exe" +else: + pypyopt = "--pypy=pypy/goal/pypy3-c" + popen = subprocess.Popen( [sys.executable, "pypy/test_all.py", - "--pypy=pypy/goal/pypy3-c", + pypyopt, "--resultlog=pypyjit_new.log", "pypy/module/pypyjit/test_pypy_c", ] + sys.argv[1:], From pypy.commits at gmail.com Sun Oct 1 03:10:53 2017 From: pypy.commits at gmail.com (arigo) Date: Sun, 01 Oct 2017 00:10:53 -0700 (PDT) Subject: [pypy-commit] cffi default: Can't really "import platform" here, for pypy Message-ID: <59d094fd.0b99df0a.2ab93.53f1@mx.google.com> Author: Armin Rigo Branch: Changeset: r3036:829f829f9c5c Date: 2017-10-01 09:09 +0200 http://bitbucket.org/cffi/cffi/changeset/829f829f9c5c/ Log: Can't really "import platform" here, for pypy diff --git a/c/test_c.py b/c/test_c.py --- a/c/test_c.py +++ b/c/test_c.py @@ -2110,11 +2110,8 @@ if sys.platform.startswith("linux"): BWChar = new_primitive_type("wchar_t") assert sizeof(BWChar) == 4 - import platform - if platform.machine().startswith(('arm', 'aarch64')): - assert int(cast(BWChar, -1)) == 4294967295 # unsigned, on ARM - else: - assert int(cast(BWChar, -1)) == -1 # "often" signed... + # wchar_t is often signed on Linux, but not always (e.g. on ARM) + assert int(cast(BWChar, -1)) in (-1, 4294967295) def test_char16(): BChar16 = new_primitive_type("char16_t") From pypy.commits at gmail.com Sun Oct 1 03:10:51 2017 From: pypy.commits at gmail.com (arigo) Date: Sun, 01 Oct 2017 00:10:51 -0700 (PDT) Subject: [pypy-commit] cffi default: Turn off the UserWarning when implicitly trying to convert between Message-ID: <59d094fb.480f1c0a.464f7.74ac@mx.google.com> Author: Armin Rigo Branch: Changeset: r3035:a60ae346a78f Date: 2017-09-30 08:39 +0200 http://bitbucket.org/cffi/cffi/changeset/a60ae346a78f/ Log: Turn off the UserWarning when implicitly trying to convert between "char *" and a pointer to a numeric single-char type. diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -1550,7 +1550,8 @@ /* for backward compatibility, accept "char *" as either source of target. This is not what C does, though, so emit a warning that will eventually turn into an - error. */ + error. The warning is turned off if both types are + pointers to single bytes. */ char *msg = (ct->ct_flags & CT_IS_VOIDCHAR_PTR ? "implicit cast to 'char *' from a different pointer type: " "will be forbidden in the future (check that the types " @@ -1560,7 +1561,12 @@ "will be forbidden in the future (check that the types " "are as you expect; use an explicit ffi.cast() if they " "are correct)"); - if (PyErr_WarnEx(PyExc_UserWarning, msg, 1)) + if ((ct->ct_flags & ctinit->ct_flags & CT_POINTER) && + ct->ct_itemdescr->ct_size == 1 && + ctinit->ct_itemdescr->ct_size == 1) { + /* no warning */ + } + else if (PyErr_WarnEx(PyExc_UserWarning, msg, 1)) return -1; } else { diff --git a/c/test_c.py b/c/test_c.py --- a/c/test_c.py +++ b/c/test_c.py @@ -3918,9 +3918,11 @@ BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BVoidP = new_pointer_type(new_void_type()) + BUCharP = new_pointer_type(new_primitive_type("unsigned char")) z1 = cast(BCharP, 0) z2 = cast(BIntP, 0) z3 = cast(BVoidP, 0) + z4 = cast(BUCharP, 0) with warnings.catch_warnings(record=True) as w: newp(new_pointer_type(BIntP), z1) # warn assert len(w) == 1 @@ -3934,6 +3936,12 @@ assert len(w) == 2 newp(new_pointer_type(BIntP), z3) # fine assert len(w) == 2 + newp(new_pointer_type(BCharP), z4) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z1) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z3) # fine + assert len(w) == 2 # check that the warnings are associated with lines in this file assert w[1].lineno == w[0].lineno + 4 From pypy.commits at gmail.com Sun Oct 1 03:11:15 2017 From: pypy.commits at gmail.com (arigo) Date: Sun, 01 Oct 2017 00:11:15 -0700 (PDT) Subject: [pypy-commit] pypy len_w: Update to cffi/a60ae346a78f Message-ID: <59d09513.0a181c0a.8bd82.0fd5@mx.google.com> Author: Armin Rigo Branch: len_w Changeset: r92524:03c40bf2a144 Date: 2017-10-01 09:10 +0200 http://bitbucket.org/pypy/pypy/changeset/03c40bf2a144/ Log: Update to cffi/a60ae346a78f diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py --- a/pypy/module/_cffi_backend/ctypeptr.py +++ b/pypy/module/_cffi_backend/ctypeptr.py @@ -156,10 +156,11 @@ class W_CTypePtrBase(W_CTypePtrOrArray): # base class for both pointers and pointers-to-functions - _attrs_ = ['is_void_ptr', 'is_voidchar_ptr'] - _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr'] + _attrs_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] + _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] is_void_ptr = False is_voidchar_ptr = False + is_onebyte_ptr = False def convert_to_object(self, cdata): ptrdata = rffi.cast(rffi.CCHARPP, cdata)[0] @@ -179,12 +180,20 @@ if self.is_void_ptr or other.is_void_ptr: pass # cast from or to 'void *' elif self.is_voidchar_ptr or other.is_voidchar_ptr: - space = self.space - msg = ("implicit cast from '%s' to '%s' " - "will be forbidden in the future (check that the types " - "are as you expect; use an explicit ffi.cast() if they " - "are correct)" % (other.name, self.name)) - space.warn(space.newtext(msg), space.w_UserWarning) + # for backward compatibility, accept "char *" as either + # source of target. This is not what C does, though, + # so emit a warning that will eventually turn into an + # error. The warning is turned off if both types are + # pointers to single bytes. + if self.is_onebyte_ptr and other.is_onebyte_ptr: + pass # no warning + else: + space = self.space + msg = ("implicit cast from '%s' to '%s' " + "will be forbidden in the future (check that the types " + "are as you expect; use an explicit ffi.cast() if they " + "are correct)" % (other.name, self.name)) + space.warn(space.newtext(msg), space.w_UserWarning) else: raise self._convert_error("compatible pointer", w_ob) @@ -214,6 +223,7 @@ self.is_void_ptr = isinstance(ctitem, ctypevoid.W_CTypeVoid) self.is_voidchar_ptr = (self.is_void_ptr or isinstance(ctitem, ctypeprim.W_CTypePrimitiveChar)) + self.is_onebyte_ptr = (ctitem.size == 1) W_CTypePtrBase.__init__(self, space, size, extra, 2, ctitem) def newp(self, w_init, allocator): diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -2099,7 +2099,8 @@ if sys.platform.startswith("linux"): BWChar = new_primitive_type("wchar_t") assert sizeof(BWChar) == 4 - assert int(cast(BWChar, -1)) == -1 # signed, on linux + # wchar_t is often signed on Linux, but not always (e.g. on ARM) + assert int(cast(BWChar, -1)) in (-1, 4294967295) def test_char16(): BChar16 = new_primitive_type("char16_t") @@ -3903,9 +3904,11 @@ BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BVoidP = new_pointer_type(new_void_type()) + BUCharP = new_pointer_type(new_primitive_type("unsigned char")) z1 = cast(BCharP, 0) z2 = cast(BIntP, 0) z3 = cast(BVoidP, 0) + z4 = cast(BUCharP, 0) with warnings.catch_warnings(record=True) as w: newp(new_pointer_type(BIntP), z1) # warn assert len(w) == 1 @@ -3919,6 +3922,12 @@ assert len(w) == 2 newp(new_pointer_type(BIntP), z3) # fine assert len(w) == 2 + newp(new_pointer_type(BCharP), z4) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z1) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z3) # fine + assert len(w) == 2 # check that the warnings are associated with lines in this file assert w[1].lineno == w[0].lineno + 4 From pypy.commits at gmail.com Sun Oct 1 03:12:11 2017 From: pypy.commits at gmail.com (arigo) Date: Sun, 01 Oct 2017 00:12:11 -0700 (PDT) Subject: [pypy-commit] pypy default: Update to cffi/a60ae346a78f Message-ID: <59d0954b.4f9adf0a.44816.47fc@mx.google.com> Author: Armin Rigo Branch: Changeset: r92525:27fc762dfc31 Date: 2017-10-01 09:10 +0200 http://bitbucket.org/pypy/pypy/changeset/27fc762dfc31/ Log: Update to cffi/a60ae346a78f diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py --- a/pypy/module/_cffi_backend/ctypeptr.py +++ b/pypy/module/_cffi_backend/ctypeptr.py @@ -156,10 +156,11 @@ class W_CTypePtrBase(W_CTypePtrOrArray): # base class for both pointers and pointers-to-functions - _attrs_ = ['is_void_ptr', 'is_voidchar_ptr'] - _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr'] + _attrs_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] + _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] is_void_ptr = False is_voidchar_ptr = False + is_onebyte_ptr = False def convert_to_object(self, cdata): ptrdata = rffi.cast(rffi.CCHARPP, cdata)[0] @@ -179,12 +180,20 @@ if self.is_void_ptr or other.is_void_ptr: pass # cast from or to 'void *' elif self.is_voidchar_ptr or other.is_voidchar_ptr: - space = self.space - msg = ("implicit cast from '%s' to '%s' " - "will be forbidden in the future (check that the types " - "are as you expect; use an explicit ffi.cast() if they " - "are correct)" % (other.name, self.name)) - space.warn(space.newtext(msg), space.w_UserWarning) + # for backward compatibility, accept "char *" as either + # source of target. This is not what C does, though, + # so emit a warning that will eventually turn into an + # error. The warning is turned off if both types are + # pointers to single bytes. + if self.is_onebyte_ptr and other.is_onebyte_ptr: + pass # no warning + else: + space = self.space + msg = ("implicit cast from '%s' to '%s' " + "will be forbidden in the future (check that the types " + "are as you expect; use an explicit ffi.cast() if they " + "are correct)" % (other.name, self.name)) + space.warn(space.newtext(msg), space.w_UserWarning) else: raise self._convert_error("compatible pointer", w_ob) @@ -214,6 +223,7 @@ self.is_void_ptr = isinstance(ctitem, ctypevoid.W_CTypeVoid) self.is_voidchar_ptr = (self.is_void_ptr or isinstance(ctitem, ctypeprim.W_CTypePrimitiveChar)) + self.is_onebyte_ptr = (ctitem.size == 1) W_CTypePtrBase.__init__(self, space, size, extra, 2, ctitem) def newp(self, w_init, allocator): diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -2099,7 +2099,8 @@ if sys.platform.startswith("linux"): BWChar = new_primitive_type("wchar_t") assert sizeof(BWChar) == 4 - assert int(cast(BWChar, -1)) == -1 # signed, on linux + # wchar_t is often signed on Linux, but not always (e.g. on ARM) + assert int(cast(BWChar, -1)) in (-1, 4294967295) def test_char16(): BChar16 = new_primitive_type("char16_t") @@ -3903,9 +3904,11 @@ BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BVoidP = new_pointer_type(new_void_type()) + BUCharP = new_pointer_type(new_primitive_type("unsigned char")) z1 = cast(BCharP, 0) z2 = cast(BIntP, 0) z3 = cast(BVoidP, 0) + z4 = cast(BUCharP, 0) with warnings.catch_warnings(record=True) as w: newp(new_pointer_type(BIntP), z1) # warn assert len(w) == 1 @@ -3919,6 +3922,12 @@ assert len(w) == 2 newp(new_pointer_type(BIntP), z3) # fine assert len(w) == 2 + newp(new_pointer_type(BCharP), z4) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z1) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z3) # fine + assert len(w) == 2 # check that the warnings are associated with lines in this file assert w[1].lineno == w[0].lineno + 4 From pypy.commits at gmail.com Sun Oct 1 07:33:32 2017 From: pypy.commits at gmail.com (rlamy) Date: Sun, 01 Oct 2017 04:33:32 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: (arigo, ronan) 'Fix' subclass checking when the subclass doesn't have an MRO yet, cf. CPython issue #22735 Message-ID: <59d0d28c.c7b3df0a.49b1c.3980@mx.google.com> Author: Ronan Lamy Branch: py3.5 Changeset: r92526:ba1da1c2d9a3 Date: 2017-10-01 13:31 +0200 http://bitbucket.org/pypy/pypy/changeset/ba1da1c2d9a3/ Log: (arigo, ronan) 'Fix' subclass checking when the subclass doesn't have an MRO yet, cf. CPython issue #22735 diff --git a/pypy/module/__builtin__/test/test_descriptor.py b/pypy/module/__builtin__/test/test_descriptor.py --- a/pypy/module/__builtin__/test/test_descriptor.py +++ b/pypy/module/__builtin__/test/test_descriptor.py @@ -258,6 +258,17 @@ raises(TypeError, "super(D).__get__(12)") raises(TypeError, "super(D).__get__(C())") + def test_super_incomplete(self): + """ + class M(type): + def mro(cls): + if cls.__mro__ is None: + raises(AttributeError, lambda: super(cls, cls).xxx) + return type.mro(cls) + class A(metaclass=M): + pass + """ + def test_classmethods_various(self): class C(object): def foo(*a): return a 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 @@ -156,6 +156,7 @@ '_version_tag?', 'name?', 'mro_w?[*]', + 'hasmro?', ] # wether the class has an overridden __getattribute__ @@ -1346,7 +1347,21 @@ # ____________________________________________________________ def _issubtype(w_sub, w_type): - return w_type in w_sub.mro_w + if w_sub.hasmro: + return w_type in w_sub.mro_w + else: + return _issubtype_slow_and_wrong(w_sub, w_type) + +def _issubtype_slow_and_wrong(w_sub, w_type): + # This is only called in strange cases where w_sub is partially initialised, + # like from a custom MetaCls.mro(). Note that it's broken wrt. multiple + # inheritance, but that's what CPython does. + w_cls = w_sub + while w_cls: + if w_cls is w_type: + return True + w_cls = find_best_base(w_cls.bases_w) + return False @elidable_promote() def _pure_issubtype(w_sub, w_type, version_tag1, version_tag2): From pypy.commits at gmail.com Sun Oct 1 07:33:35 2017 From: pypy.commits at gmail.com (rlamy) Date: Sun, 01 Oct 2017 04:33:35 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: Merge heads Message-ID: <59d0d28f.c1acdf0a.90297.7ca2@mx.google.com> Author: Ronan Lamy Branch: py3.5 Changeset: r92527:2bf99e3d5873 Date: 2017-10-01 13:32 +0200 http://bitbucket.org/pypy/pypy/changeset/2bf99e3d5873/ Log: Merge heads diff --git a/pypy/interpreter/test/test_app_main.py b/pypy/interpreter/test/test_app_main.py --- a/pypy/interpreter/test/test_app_main.py +++ b/pypy/interpreter/test/test_app_main.py @@ -1162,9 +1162,13 @@ import os old_sys_path = sys.path[:] sys.path.append(self.goal_dir) + if sys.platform == 'win32': + exename = 'pypy3-cw.exe' + else: + exename = 'pypy3-c' try: import app_main - pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', 'pypy3-c') + pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', exename) app_main.setup_bootstrap_path(pypy_c) newpath = sys.path[:] # we get at least lib_pypy @@ -1180,9 +1184,13 @@ import os old_sys_path = sys.path[:] sys.path.append(self.goal_dir) + if sys.platform == 'win32': + exename = 'pypy3-cw.exe' + else: + exename = 'pypy3-c' try: import app_main - pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', 'pypy3-c') + pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', exename) app_main.entry_point(pypy_c, [self.foo_py]) # assert it did not crash finally: diff --git a/testrunner/lib_python_tests.py b/testrunner/lib_python_tests.py --- a/testrunner/lib_python_tests.py +++ b/testrunner/lib_python_tests.py @@ -11,9 +11,14 @@ os.environ['PYTHONPATH'] = rootdir os.environ['PYTEST_PLUGINS'] = '' +if sys.platform == 'win32': + pypyopt = "--pypy=pypy/goal/pypy3-cw.exe" +else: + pypyopt = "--pypy=pypy/goal/pypy3-c" + popen = subprocess.Popen( [sys.executable, "pypy/test_all.py", - "--pypy=pypy/goal/pypy3-c", + pypyopt, "--timeout=3600", "-rs", "--resultlog=cpython.log", "lib-python", diff --git a/testrunner/pypyjit_tests.py b/testrunner/pypyjit_tests.py --- a/testrunner/pypyjit_tests.py +++ b/testrunner/pypyjit_tests.py @@ -11,9 +11,14 @@ os.environ['PYTHONPATH'] = rootdir os.environ['PYTEST_PLUGINS'] = '' +if sys.platform == 'win32': + pypyopt = "--pypy=pypy/goal/pypy3-cw.exe" +else: + pypyopt = "--pypy=pypy/goal/pypy3-c" + popen = subprocess.Popen( [sys.executable, "pypy/test_all.py", - "--pypy=pypy/goal/pypy3-c", + pypyopt, "--resultlog=pypyjit_new.log", "pypy/module/pypyjit/test_pypy_c", ] + sys.argv[1:], From pypy.commits at gmail.com Sun Oct 1 10:09:25 2017 From: pypy.commits at gmail.com (rlamy) Date: Sun, 01 Oct 2017 07:09:25 -0700 (PDT) Subject: [pypy-commit] pypy default: Clean up update_all_slots() a bit Message-ID: <59d0f715.8b141c0a.57a7e.6b42@mx.google.com> Author: Ronan Lamy Branch: Changeset: r92528:a390ace21a3c Date: 2017-10-01 16:08 +0200 http://bitbucket.org/pypy/pypy/changeset/a390ace21a3c/ Log: Clean up update_all_slots() a bit 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 @@ -243,10 +243,10 @@ for method_name, slot_name, slot_names, slot_apifunc in slotdefs_for_tp_slots: slot_func_helper = None - if search_dict_w is None: + if typedef is not None: # built-in types: expose as many slots as possible, even # if it happens to come from some parent class - slot_apifunc = None # use get_slot_tp_function + slot_apifunc = get_slot_tp_function(space, typedef, slot_name) else: # For heaptypes, w_type.layout.typedef will be object's typedef, and # get_slot_tp_function will fail @@ -263,9 +263,6 @@ slot_func_helper = getattr(struct, slot_names[1]) if not slot_func_helper: - if typedef is not None: - if slot_apifunc is None: - slot_apifunc = get_slot_tp_function(space, typedef, slot_name) if not slot_apifunc: if not we_are_translated(): if slot_name not in missing_slots: From pypy.commits at gmail.com Sun Oct 1 10:24:17 2017 From: pypy.commits at gmail.com (rlamy) Date: Sun, 01 Oct 2017 07:24:17 -0700 (PDT) Subject: [pypy-commit] pypy default: Move get_slot_tp_function from slotdefs to typeobject Message-ID: <59d0fa91.5d87df0a.e03ab.b44d@mx.google.com> Author: Ronan Lamy Branch: Changeset: r92529:d4810ff5fe4b Date: 2017-10-01 16:23 +0200 http://bitbucket.org/pypy/pypy/changeset/d4810ff5fe4b/ Log: Move get_slot_tp_function from slotdefs to typeobject 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 @@ -429,23 +429,6 @@ from rpython.rlib.nonconst import NonConstant -SLOTS = {} - - at specialize.memo() -def get_slot_tp_function(space, typedef, name): - """Return a description of the slot C function to use for the built-in - type for 'typedef'. The 'name' is the slot name. This is a memo - function that, after translation, returns one of a built-in finite set. - """ - key = (typedef, name) - try: - return SLOTS[key] - except KeyError: - slot_func = build_slot_tp_function(space, typedef, name) - api_func = slot_func.api_func if slot_func else None - SLOTS[key] = api_func - return api_func - def build_slot_tp_function(space, typedef, name): w_type = space.gettypeobject(typedef) 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 @@ -31,7 +31,7 @@ PyObject, make_ref, from_ref, get_typedescr, make_typedescr, track_reference, Py_DecRef, as_pyobj) from pypy.module.cpyext.slotdefs import ( - slotdefs_for_tp_slots, slotdefs_for_wrappers, get_slot_tp_function, + slotdefs_for_tp_slots, slotdefs_for_wrappers, build_slot_tp_function, llslot) from pypy.module.cpyext.state import State from pypy.module.cpyext.structmember import PyMember_GetOne, PyMember_SetOne @@ -226,6 +226,22 @@ dict_w[name] = w_descr i += 1 +SLOTS = {} + at specialize.memo() +def get_slot_tp_function(space, typedef, name): + """Return a description of the slot C function to use for the built-in + type for 'typedef'. The 'name' is the slot name. This is a memo + function that, after translation, returns one of a built-in finite set. + """ + key = (typedef, name) + try: + return SLOTS[key] + except KeyError: + slot_func = build_slot_tp_function(space, typedef, name) + api_func = slot_func.api_func if slot_func else None + SLOTS[key] = api_func + return api_func + missing_slots={} def update_all_slots(space, w_type, pto): # fill slots in pto @@ -248,8 +264,6 @@ # if it happens to come from some parent class slot_apifunc = get_slot_tp_function(space, typedef, slot_name) else: - # For heaptypes, w_type.layout.typedef will be object's typedef, and - # get_slot_tp_function will fail w_descr = search_dict_w.get(method_name, None) if w_descr: # use the slot_apifunc (userslots) to lookup at runtime From pypy.commits at gmail.com Sun Oct 1 10:58:06 2017 From: pypy.commits at gmail.com (antocuni) Date: Sun, 01 Oct 2017 07:58:06 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-avoid-roundtrip: (antocuni, arigo, ronan): rewrite Py_DecRef in C Message-ID: <59d1027e.fca0df0a.7241f.80cf@mx.google.com> Author: Antonio Cuni Branch: cpyext-avoid-roundtrip Changeset: r92531:730786d85e6f Date: 2017-10-01 16:55 +0200 http://bitbucket.org/pypy/pypy/changeset/730786d85e6f/ Log: (antocuni, arigo, ronan): rewrite Py_DecRef in C diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -596,7 +596,7 @@ 'Py_FrozenFlag', 'Py_TabcheckFlag', 'Py_UnicodeFlag', 'Py_IgnoreEnvironmentFlag', 'Py_DivisionWarningFlag', 'Py_DontWriteBytecodeFlag', 'Py_NoUserSiteDirectory', '_Py_QnewFlag', 'Py_Py3kWarningFlag', 'Py_HashRandomizationFlag', '_Py_PackageContext', - '_PyTraceMalloc_Track', '_PyTraceMalloc_Untrack', 'PyMem_Malloc', + '_PyTraceMalloc_Track', '_PyTraceMalloc_Untrack', 'PyMem_Malloc', 'Py_DecRef', ] TYPES = {} FORWARD_DECLS = [] @@ -1381,6 +1381,7 @@ source_dir / "pythread.c", source_dir / "missing.c", source_dir / "pymem.c", + source_dir / "object.c", ] def build_eci(code, use_micronumpy=False, translating=False): 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 @@ -47,7 +47,9 @@ #define Py_XDECREF(op) do { if ((op) == NULL) ; else Py_DECREF(op); } while (0) #endif -#define Py_CLEAR(op) \ +PyAPI_FUNC(void) Py_DecRef(PyObject *); + +#define Py_CLEAR(op) \ do { \ if (op) { \ PyObject *_py_tmp = (PyObject *)(op); \ diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -336,9 +336,10 @@ def Py_IncRef(space, obj): incref(space, obj) - at cpython_api([PyObject], lltype.Void) -def Py_DecRef(space, obj): - decref(space, obj) +## @cpython_api([PyObject], lltype.Void) +## def Py_DecRef(space, obj): +## decref(space, obj) +Py_DecRef = decref # XXX remove me and kill all the Py_DecRef usages from RPython @cpython_api([PyObject], lltype.Void) def _Py_NewReference(space, obj): diff --git a/pypy/module/cpyext/src/object.c b/pypy/module/cpyext/src/object.c new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/src/object.c @@ -0,0 +1,10 @@ +/* Generic object operations; and implementation of None (NoObject) */ + +#include "Python.h" + +void +Py_DecRef(PyObject *o) +{ + Py_XDECREF(o); +} + diff --git a/pypy/module/cpyext/test/test_memoryobject.py b/pypy/module/cpyext/test/test_memoryobject.py --- a/pypy/module/cpyext/test/test_memoryobject.py +++ b/pypy/module/cpyext/test/test_memoryobject.py @@ -4,7 +4,7 @@ from pypy.module.cpyext.test.test_api import BaseApiTest from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase from rpython.rlib.buffer import StringBuffer -from pypy.module.cpyext.pyobject import make_ref, from_ref +from pypy.module.cpyext.pyobject import make_ref, from_ref, decref from pypy.module.cpyext.memoryobject import PyMemoryViewObject only_pypy ="config.option.runappdirect and '__pypy__' not in sys.builtin_module_names" @@ -31,8 +31,8 @@ w_f = space.wrap(f) assert space.eq_w(space.getattr(w_mv, w_f), space.getattr(w_memoryview, w_f)) - api.Py_DecRef(ref) - api.Py_DecRef(w_memoryview) + decref(space, ref) + decref(space, w_memoryview) class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase): def test_fillWithObject(self): diff --git a/pypy/module/cpyext/test/test_traceback.py b/pypy/module/cpyext/test/test_traceback.py --- a/pypy/module/cpyext/test/test_traceback.py +++ b/pypy/module/cpyext/test/test_traceback.py @@ -1,6 +1,6 @@ from rpython.rtyper.lltypesystem import lltype, rffi from pypy.module.cpyext.test.test_api import BaseApiTest -from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref +from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref, decref from pypy.module.cpyext.pytraceback import PyTracebackObject from pypy.interpreter.pytraceback import PyTraceback from pypy.interpreter.baseobjspace import AppExecCache @@ -39,6 +39,6 @@ assert lltype.normalizeptr(py_traceback) is None - api.Py_DecRef(py_obj) + decref(space, py_obj) # hack to allow the code object to be freed del space.fromcache(AppExecCache).content[src] diff --git a/pypy/module/cpyext/test/test_tupleobject.py b/pypy/module/cpyext/test/test_tupleobject.py --- a/pypy/module/cpyext/test/test_tupleobject.py +++ b/pypy/module/cpyext/test/test_tupleobject.py @@ -1,6 +1,6 @@ import py -from pypy.module.cpyext.pyobject import PyObject, PyObjectP, make_ref, from_ref +from pypy.module.cpyext.pyobject import PyObject, PyObjectP, make_ref, from_ref, decref from pypy.module.cpyext.test.test_api import BaseApiTest, raises_w from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase from rpython.rtyper.lltypesystem import rffi, lltype @@ -24,7 +24,7 @@ def test_tuple_realize_refuses_nulls(self, space, api): py_tuple = api.PyTuple_New(1) py.test.raises(FatalError, from_ref, space, py_tuple) - api.Py_DecRef(py_tuple) + decref(space, py_tuple) def test_tuple_resize(self, space, api): w_42 = space.wrap(42) @@ -43,7 +43,7 @@ assert space.int_w(space.len(w_tuple)) == 2 assert space.int_w(space.getitem(w_tuple, space.wrap(0))) == 42 assert space.int_w(space.getitem(w_tuple, space.wrap(1))) == 43 - api.Py_DecRef(ar[0]) + decref(space, ar[0]) py_tuple = api.PyTuple_New(3) rffi.cast(PyTupleObject, py_tuple).c_ob_item[0] = make_ref(space, w_42) @@ -59,7 +59,7 @@ assert space.int_w(space.len(w_tuple)) == 10 for i in range(10): assert space.int_w(space.getitem(w_tuple, space.wrap(i))) == 42 + i - api.Py_DecRef(ar[0]) + decref(space, ar[0]) lltype.free(ar, flavor='raw') @@ -71,7 +71,7 @@ w_tuple = from_ref(space, py_tuple) assert space.eq_w(w_tuple, space.newtuple([space.wrap(42), space.wrap(43)])) - api.Py_DecRef(py_tuple) + decref(space, py_tuple) def test_getslice(self, space, api): w_tuple = space.newtuple([space.wrap(i) for i in range(10)]) diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py --- a/pypy/module/cpyext/test/test_typeobject.py +++ b/pypy/module/cpyext/test/test_typeobject.py @@ -3,7 +3,7 @@ from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase from pypy.module.cpyext.test.test_api import BaseApiTest from pypy.module.cpyext.api import generic_cpy_call -from pypy.module.cpyext.pyobject import make_ref, from_ref +from pypy.module.cpyext.pyobject import make_ref, from_ref, decref from pypy.module.cpyext.typeobject import PyTypeObjectPtr class AppTestTypeObject(AppTestCpythonExtensionBase): @@ -487,7 +487,7 @@ assert py_type.c_tp_alloc assert from_ref(space, py_type.c_tp_mro).wrappeditems is w_class.mro_w - api.Py_DecRef(ref) + decref(space, ref) def test_type_dict(self, space, api): w_class = space.appexec([], """(): @@ -515,7 +515,7 @@ return C """) ref = make_ref(space, w_class) - api.Py_DecRef(ref) + decref(space, ref) def test_lookup(self, space, api): w_type = space.w_bytes @@ -531,7 +531,7 @@ import _numpypy return _numpypy.multiarray.dtype('int64').type(2)""") ref = make_ref(space, w_obj) - api.Py_DecRef(ref) + decref(space, ref) class AppTestSlots(AppTestCpythonExtensionBase): def setup_class(cls): From pypy.commits at gmail.com Sun Oct 1 10:58:04 2017 From: pypy.commits at gmail.com (antocuni) Date: Sun, 01 Oct 2017 07:58:04 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-avoid-roundtrip: (arigo, antocuni): start a branch where to try to avoid RPython->C->RPython->C roundtrips when we call things like tp_allocate&co. Message-ID: <59d1027c.4fabdf0a.22ce7.52f9@mx.google.com> Author: Antonio Cuni Branch: cpyext-avoid-roundtrip Changeset: r92530:87f91d94662e Date: 2017-10-01 16:13 +0200 http://bitbucket.org/pypy/pypy/changeset/87f91d94662e/ Log: (arigo, antocuni): start a branch where to try to avoid RPython->C->RPython->C roundtrips when we call things like tp_allocate&co. From pypy.commits at gmail.com Sun Oct 1 11:06:55 2017 From: pypy.commits at gmail.com (arigo) Date: Sun, 01 Oct 2017 08:06:55 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-avoid-roundtrip: (antocuni, arigo, ronan around): same with Py_IncRef() Message-ID: <59d1048f.05611c0a.5b3c5.7834@mx.google.com> Author: Armin Rigo Branch: cpyext-avoid-roundtrip Changeset: r92532:6046c5c9f4c2 Date: 2017-10-01 17:06 +0200 http://bitbucket.org/pypy/pypy/changeset/6046c5c9f4c2/ Log: (antocuni, arigo, ronan around): same with Py_IncRef() diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -596,7 +596,8 @@ 'Py_FrozenFlag', 'Py_TabcheckFlag', 'Py_UnicodeFlag', 'Py_IgnoreEnvironmentFlag', 'Py_DivisionWarningFlag', 'Py_DontWriteBytecodeFlag', 'Py_NoUserSiteDirectory', '_Py_QnewFlag', 'Py_Py3kWarningFlag', 'Py_HashRandomizationFlag', '_Py_PackageContext', - '_PyTraceMalloc_Track', '_PyTraceMalloc_Untrack', 'PyMem_Malloc', 'Py_DecRef', + '_PyTraceMalloc_Track', '_PyTraceMalloc_Untrack', 'PyMem_Malloc', + 'Py_IncRef', 'Py_DecRef', ] TYPES = {} FORWARD_DECLS = [] 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 @@ -47,8 +47,9 @@ #define Py_XDECREF(op) do { if ((op) == NULL) ; else Py_DECREF(op); } while (0) #endif +PyAPI_FUNC(void) Py_IncRef(PyObject *); PyAPI_FUNC(void) Py_DecRef(PyObject *); - + #define Py_CLEAR(op) \ do { \ if (op) { \ diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -332,13 +332,7 @@ get_w_obj_and_decref(space, obj) - at cpython_api([PyObject], lltype.Void) -def Py_IncRef(space, obj): - incref(space, obj) - -## @cpython_api([PyObject], lltype.Void) -## def Py_DecRef(space, obj): -## decref(space, obj) +Py_IncRef = incref # XXX remove me and kill all the Py_IncRef usages from RPython Py_DecRef = decref # XXX remove me and kill all the Py_DecRef usages from RPython @cpython_api([PyObject], lltype.Void) diff --git a/pypy/module/cpyext/src/object.c b/pypy/module/cpyext/src/object.c --- a/pypy/module/cpyext/src/object.c +++ b/pypy/module/cpyext/src/object.c @@ -3,8 +3,13 @@ #include "Python.h" void +Py_IncRef(PyObject *o) +{ + Py_XINCREF(o); +} + +void Py_DecRef(PyObject *o) { Py_XDECREF(o); } - diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py --- a/pypy/module/cpyext/test/test_cpyext.py +++ b/pypy/module/cpyext/test/test_cpyext.py @@ -436,7 +436,7 @@ { if (self) { - Py_INCREF(self); + Py_IncRef(self); return self; } else From pypy.commits at gmail.com Sun Oct 1 11:38:39 2017 From: pypy.commits at gmail.com (rlamy) Date: Sun, 01 Oct 2017 08:38:39 -0700 (PDT) Subject: [pypy-commit] pypy default: Split update_all_slots() Message-ID: <59d10bff.8e8bdf0a.62d48.3414@mx.google.com> Author: Ronan Lamy Branch: Changeset: r92533:b9cc080ea998 Date: 2017-10-01 17:38 +0200 http://bitbucket.org/pypy/pypy/changeset/b9cc080ea998/ Log: Split update_all_slots() 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 @@ -285,47 +285,50 @@ method_name, slot_name, w_type.getname(space)) continue slot_func_helper = slot_apifunc.get_llhelper(space) + fill_slot(space, pto, w_type, slot_names, slot_func_helper) - # XXX special case wrapper-functions and use a "specific" slot func + at specialize.arg(3) +def fill_slot(space, pto, w_type, slot_names, slot_func_helper): + # XXX special case wrapper-functions and use a "specific" slot func - if len(slot_names) == 1: - if not getattr(pto, slot_names[0]): - setattr(pto, slot_names[0], slot_func_helper) - elif ((w_type is space.w_list or w_type is space.w_tuple) and - slot_names[0] == 'c_tp_as_number'): - # XXX hack - how 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 - elif (space.issubtype_w(w_type, space.w_basestring) and - slot_names[0] == 'c_tp_as_number'): - # like above but for any str type - pass - else: - assert len(slot_names) == 2 - struct = getattr(pto, slot_names[0]) - if not struct: - #assert not space.config.translating - assert not pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE - if slot_names[0] == 'c_tp_as_number': - STRUCT_TYPE = PyNumberMethods - elif slot_names[0] == 'c_tp_as_sequence': - STRUCT_TYPE = PySequenceMethods - elif slot_names[0] == 'c_tp_as_buffer': - STRUCT_TYPE = PyBufferProcs - elif slot_names[0] == 'c_tp_as_mapping': - STRUCT_TYPE = PyMappingMethods - else: - raise AssertionError( - "Structure not allocated: %s" % (slot_names[0],)) - struct = lltype.malloc(STRUCT_TYPE, flavor='raw', zero=True) - setattr(pto, slot_names[0], struct) + if len(slot_names) == 1: + if not getattr(pto, slot_names[0]): + setattr(pto, slot_names[0], slot_func_helper) + elif ((w_type is space.w_list or w_type is space.w_tuple) and + slot_names[0] == 'c_tp_as_number'): + # XXX hack - how 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 + elif (space.issubtype_w(w_type, space.w_basestring) and + slot_names[0] == 'c_tp_as_number'): + # like above but for any str type + pass + else: + assert len(slot_names) == 2 + struct = getattr(pto, slot_names[0]) + if not struct: + #assert not space.config.translating + assert not pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE + if slot_names[0] == 'c_tp_as_number': + STRUCT_TYPE = PyNumberMethods + elif slot_names[0] == 'c_tp_as_sequence': + STRUCT_TYPE = PySequenceMethods + elif slot_names[0] == 'c_tp_as_buffer': + STRUCT_TYPE = PyBufferProcs + elif slot_names[0] == 'c_tp_as_mapping': + STRUCT_TYPE = PyMappingMethods + else: + raise AssertionError( + "Structure not allocated: %s" % (slot_names[0],)) + struct = lltype.malloc(STRUCT_TYPE, flavor='raw', zero=True) + setattr(pto, slot_names[0], struct) - if not getattr(struct, slot_names[1]): - setattr(struct, slot_names[1], slot_func_helper) + if not getattr(struct, slot_names[1]): + setattr(struct, slot_names[1], slot_func_helper) def add_operators(space, dict_w, pto): from pypy.module.cpyext.object import PyObject_HashNotImplemented From pypy.commits at gmail.com Sun Oct 1 12:17:09 2017 From: pypy.commits at gmail.com (arigo) Date: Sun, 01 Oct 2017 09:17:09 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-avoid-roundtrip: (antocuni, ronan, arigo) Message-ID: <59d11505.480f1c0a.464f7.c7e7@mx.google.com> Author: Armin Rigo Branch: cpyext-avoid-roundtrip Changeset: r92534:10f2a25eb826 Date: 2017-10-01 18:16 +0200 http://bitbucket.org/pypy/pypy/changeset/10f2a25eb826/ Log: (antocuni, ronan, arigo) Work work work until _Py_Dealloc() can be written in C diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -1038,7 +1038,7 @@ return wrapper_second_level -def setup_init_functions(eci, prefix): +def setup_init_functions(eci, prefix, space): # jump through hoops to avoid releasing the GIL during initialization # of the cpyext module. The C functions are called with no wrapper, # but must not do anything like calling back PyType_Ready(). We @@ -1055,6 +1055,7 @@ setdefenc = rffi.llexternal('_%s_setfilesystemdefaultencoding' % prefix, [rffi.CCHARP], lltype.Void, compilation_info=eci, _nowrapper=True) + @init_function def init_types(space): from pypy.module.cpyext.typeobject import py_type_ready from pypy.module.sys.interp_encoding import getfilesystemencoding @@ -1063,7 +1064,7 @@ py_type_ready(space, get_capsule_type()) s = space.text_w(getfilesystemencoding(space)) setdefenc(rffi.str2charp(s, track_allocation=False)) # "leaks" - INIT_FUNCTIONS.append(init_types) + from pypy.module.posix.interp_posix import add_fork_hook _reinit_tls = rffi.llexternal('%sThread_ReInitTLS' % prefix, [], lltype.Void, compilation_info=eci) @@ -1071,6 +1072,16 @@ _reinit_tls() add_fork_hook('child', reinit_tls) + state = space.fromcache(State) + state.C._Py_Dealloc = rffi.llexternal('_Py_Dealloc', + [PyObject], lltype.Void, + compilation_info=eci, + _nowrapper=True) + _, state.C.set_marker = rffi.CExternVariable( + Py_ssize_t, '_pypy_rawrefcount_w_marker_deallocating', + eci, _nowrapper=True, c_type='Py_ssize_t') + + def init_function(func): INIT_FUNCTIONS.append(func) return func @@ -1190,7 +1201,7 @@ ll2ctypes.lltype2ctypes(func.get_llhelper(space)), ctypes.c_void_p) - setup_init_functions(eci, prefix) + setup_init_functions(eci, prefix, space) return modulename.new(ext='') def attach_recursively(space, static_pyobjs, static_objs_w, attached_objs, i): @@ -1521,7 +1532,7 @@ relax=True) deco(func.get_wrapper(space)) - setup_init_functions(eci, prefix) + setup_init_functions(eci, prefix, space) trunk_include = pypydir.dirpath() / 'include' copy_header_files(cts, trunk_include, use_micronumpy) 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 @@ -49,6 +49,8 @@ PyAPI_FUNC(void) Py_IncRef(PyObject *); PyAPI_FUNC(void) Py_DecRef(PyObject *); +extern Py_ssize_t _pypy_rawrefcount_w_marker_deallocating; +PyAPI_FUNC(void) _Py_Dealloc(PyObject *); #define Py_CLEAR(op) \ do { \ diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -7,15 +7,16 @@ from pypy.module.cpyext.api import ( cpython_api, bootstrap_function, PyObject, PyObjectP, ADDR, CANNOT_FAIL, Py_TPFLAGS_HEAPTYPE, PyTypeObjectPtr, is_PyObject, - PyVarObject) + PyVarObject, Py_ssize_t, init_function) from pypy.module.cpyext.state import State from pypy.objspace.std.typeobject import W_TypeObject from pypy.objspace.std.objectobject import W_ObjectObject -from rpython.rlib.objectmodel import specialize +from rpython.rlib.objectmodel import specialize, we_are_translated from rpython.rlib.objectmodel import keepalive_until_here -from rpython.rtyper.annlowlevel import llhelper +from rpython.rtyper.annlowlevel import llhelper, cast_instance_to_base_ptr from rpython.rlib import rawrefcount, jit from rpython.rlib.debug import fatalerror +from rpython.translator.tool.cbuild import ExternalCompilationInfo #________________________________________________________ @@ -316,6 +317,7 @@ @specialize.ll() def decref(space, obj): + from pypy.module.cpyext.api import generic_cpy_call if is_pyobj(obj): obj = rffi.cast(PyObject, obj) if obj: @@ -323,7 +325,8 @@ assert obj.c_ob_pypy_link == 0 or obj.c_ob_refcnt > rawrefcount.REFCNT_FROM_PYPY obj.c_ob_refcnt -= 1 if obj.c_ob_refcnt == 0: - _Py_Dealloc(space, obj) + state = space.fromcache(State) + generic_cpy_call(space, state.C._Py_Dealloc, obj) #else: # w_obj = rawrefcount.to_obj(W_Root, ref) # if w_obj is not None: @@ -331,7 +334,6 @@ else: get_w_obj_and_decref(space, obj) - Py_IncRef = incref # XXX remove me and kill all the Py_IncRef usages from RPython Py_DecRef = decref # XXX remove me and kill all the Py_DecRef usages from RPython @@ -343,14 +345,12 @@ assert isinstance(w_type, W_TypeObject) get_typedescr(w_type.layout.typedef).realize(space, obj) - at cpython_api([PyObject], lltype.Void) -def _Py_Dealloc(space, obj): - from pypy.module.cpyext.api import generic_cpy_call - pto = obj.c_ob_type - #print >>sys.stderr, "Calling dealloc slot", pto.c_tp_dealloc, "of", obj, \ - # "'s type which is", rffi.charp2str(pto.c_tp_name) - rawrefcount.mark_deallocating(w_marker_deallocating, obj) - generic_cpy_call(space, pto.c_tp_dealloc, obj) + at init_function +def write_w_marker_deallocating(space): + if we_are_translated(): + llptr = cast_instance_to_base_ptr(w_marker_deallocating) + state = space.fromcache(State) + state.C.set_marker(rffi.cast(Py_ssize_t, llptr)) @cpython_api([rffi.VOIDP], lltype.Signed, error=CANNOT_FAIL) def _Py_HashPointer(space, ptr): diff --git a/pypy/module/cpyext/src/object.c b/pypy/module/cpyext/src/object.c --- a/pypy/module/cpyext/src/object.c +++ b/pypy/module/cpyext/src/object.c @@ -13,3 +13,13 @@ { Py_XDECREF(o); } + +Py_ssize_t _pypy_rawrefcount_w_marker_deallocating; /* set from pyobject.py */ + +void _Py_Dealloc(PyObject *obj) +{ + PyTypeObject *pto = obj->ob_type; + /* this is the same as rawrefcount.mark_deallocating() */ + obj->ob_pypy_link = _pypy_rawrefcount_w_marker_deallocating; + pto->tp_dealloc(obj); +} diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py --- a/pypy/module/cpyext/state.py +++ b/pypy/module/cpyext/state.py @@ -14,6 +14,7 @@ self.programname = lltype.nullptr(rffi.CCHARP.TO) self.version = lltype.nullptr(rffi.CCHARP.TO) self.builder = None + self.C = CNamespace() def reset(self): from pypy.module.cpyext.modsupport import PyMethodDef @@ -168,6 +169,11 @@ return w_mod +class CNamespace: + def _freeze_(self): + return True + + def _rawrefcount_perform(space): from pypy.module.cpyext.pyobject import PyObject, decref while True: From pypy.commits at gmail.com Sun Oct 1 12:39:37 2017 From: pypy.commits at gmail.com (arigo) Date: Sun, 01 Oct 2017 09:39:37 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-avoid-roundtrip: (ronan, antocuni, arigo) Message-ID: <59d11a49.532f1c0a.4ce35.67e1@mx.google.com> Author: Armin Rigo Branch: cpyext-avoid-roundtrip Changeset: r92535:804bbd063a8a Date: 2017-10-01 18:39 +0200 http://bitbucket.org/pypy/pypy/changeset/804bbd063a8a/ Log: (ronan, antocuni, arigo) Small change to prepare the subtype_dealloc() attack diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -26,9 +26,9 @@ basestruct = PyObject.TO W_BaseObject = W_ObjectObject - def get_dealloc(self): + def get_dealloc(self, space): from pypy.module.cpyext.typeobject import subtype_dealloc - return subtype_dealloc.api_func + return subtype_dealloc.api_func.get_llhelper(space) def allocate(self, space, w_type, itemcount=0, immortal=False): # typically called from PyType_GenericAlloc via typedescr.allocate @@ -107,8 +107,8 @@ return tp_alloc(space, w_type, itemcount) if tp_dealloc: - def get_dealloc(self): - return tp_dealloc.api_func + def get_dealloc(self, space): + return tp_dealloc.api_func.get_llhelper(space) if tp_attach: def attach(self, space, pyobj, w_obj, w_userdata=None): 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 @@ -758,7 +758,7 @@ # dealloc if space.gettypeobject(w_type.layout.typedef) is w_type: # only for the exact type, like 'space.w_tuple' or 'space.w_list' - pto.c_tp_dealloc = typedescr.get_dealloc().get_llhelper(space) + pto.c_tp_dealloc = typedescr.get_dealloc(space) else: # for all subtypes, use base's dealloc (requires sorting in attach_all) pto.c_tp_dealloc = pto.c_tp_base.c_tp_dealloc From pypy.commits at gmail.com Sun Oct 1 12:51:21 2017 From: pypy.commits at gmail.com (rlamy) Date: Sun, 01 Oct 2017 09:51:21 -0700 (PDT) Subject: [pypy-commit] pypy default: Extract warn_missing_slot() Message-ID: <59d11d09.2395df0a.36d82.62b4@mx.google.com> Author: Ronan Lamy Branch: Changeset: r92536:528a836d1686 Date: 2017-10-01 18:50 +0200 http://bitbucket.org/pypy/pypy/changeset/528a836d1686/ Log: Extract warn_missing_slot() 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 @@ -243,6 +243,13 @@ return api_func missing_slots={} +def warn_missing_slot(space, method_name, slot_name, w_type): + if not we_are_translated(): + if slot_name not in missing_slots: + missing_slots[slot_name] = w_type.getname(space) + print "missing slot %r/%r, discovered on %r" % ( + method_name, slot_name, w_type.getname(space)) + def update_all_slots(space, w_type, pto): # fill slots in pto # Not very sure about it, but according to @@ -278,11 +285,7 @@ if not slot_func_helper: if not slot_apifunc: - if not we_are_translated(): - if slot_name not in missing_slots: - missing_slots[slot_name] = w_type.getname(space) - print "missing slot %r/%r, discovered on %r" % ( - method_name, slot_name, w_type.getname(space)) + warn_missing_slot(space, method_name, slot_name, w_type) continue slot_func_helper = slot_apifunc.get_llhelper(space) fill_slot(space, pto, w_type, slot_names, slot_func_helper) @@ -290,7 +293,6 @@ @specialize.arg(3) def fill_slot(space, pto, w_type, slot_names, slot_func_helper): # XXX special case wrapper-functions and use a "specific" slot func - if len(slot_names) == 1: if not getattr(pto, slot_names[0]): setattr(pto, slot_names[0], slot_func_helper) From pypy.commits at gmail.com Sun Oct 1 16:26:09 2017 From: pypy.commits at gmail.com (antocuni) Date: Sun, 01 Oct 2017 13:26:09 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-jit: document and close the branch Message-ID: <59d14f61.e2a9df0a.ea875.d2e2@mx.google.com> Author: Antonio Cuni Branch: cpyext-jit Changeset: r92537:b6ca94977ea4 Date: 2017-10-01 22:24 +0200 http://bitbucket.org/pypy/pypy/changeset/b6ca94977ea4/ Log: document and close the branch 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 @@ -4,3 +4,12 @@ .. this is a revision shortly after release-pypy2.7-v5.9.0 .. startrev:899e5245de1e + +.. branch: cpyext-jit + +Differentiate the code to call METH_NOARGS, METH_O and METH_VARARGS in cpyext: +this allows to write specialized code which is much faster than previous +completely generic version. Moreover, let the JIT to look inside the cpyext +module: the net result is that cpyext calls are up to 7x faster. However, this +is true only for very simple situations: in all real life code, we are still +much slower than CPython (more optimizations to come) From pypy.commits at gmail.com Sun Oct 1 16:26:11 2017 From: pypy.commits at gmail.com (antocuni) Date: Sun, 01 Oct 2017 13:26:11 -0700 (PDT) Subject: [pypy-commit] pypy default: merge the cpyext-jit; this makes cpyext calls up to 7x faster in very simple cases, see whatsnew for details Message-ID: <59d14f63.5d87df0a.e03ab.04f6@mx.google.com> Author: Antonio Cuni Branch: Changeset: r92538:9f9989a1ffe6 Date: 2017-10-01 22:25 +0200 http://bitbucket.org/pypy/pypy/changeset/9f9989a1ffe6/ Log: merge the cpyext-jit; this makes cpyext calls up to 7x faster in very simple cases, see whatsnew for details 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 @@ -4,3 +4,12 @@ .. this is a revision shortly after release-pypy2.7-v5.9.0 .. startrev:899e5245de1e + +.. branch: cpyext-jit + +Differentiate the code to call METH_NOARGS, METH_O and METH_VARARGS in cpyext: +this allows to write specialized code which is much faster than previous +completely generic version. Moreover, let the JIT to look inside the cpyext +module: the net result is that cpyext calls are up to 7x faster. However, this +is true only for very simple situations: in all real life code, we are still +much slower than CPython (more optimizations to come) diff --git a/pypy/module/_cffi_backend/cffi1_module.py b/pypy/module/_cffi_backend/cffi1_module.py --- a/pypy/module/_cffi_backend/cffi1_module.py +++ b/pypy/module/_cffi_backend/cffi1_module.py @@ -1,4 +1,5 @@ from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rlib import jit from pypy.interpreter.error import oefmt from pypy.interpreter.module import Module @@ -15,7 +16,7 @@ INITFUNCPTR = lltype.Ptr(lltype.FuncType([rffi.VOIDPP], lltype.Void)) - + at jit.dont_look_inside def load_cffi1_module(space, name, path, initptr): # This is called from pypy.module.cpyext.api.load_extension_module() from pypy.module._cffi_backend.call_python import get_ll_cffi_call_python diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -449,6 +449,11 @@ if func.__name__ in FUNCTIONS_BY_HEADER[header]: raise ValueError("%s already registered" % func.__name__) func._always_inline_ = 'try' + # + # XXX: should we @jit.dont_look_inside all the @cpython_api functions, + # or we should only disable some of them? + func._jit_look_inside_ = False + # api_function = ApiFunction( argtypes, restype, func, error=_compute_error(error, restype), gil=gil, 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,5 @@ from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rlib import jit from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt @@ -42,8 +43,8 @@ from pypy.module.cpyext.object import _dealloc _dealloc(space, py_obj) - class W_PyCFunctionObject(W_Root): + # TODO create a slightly different class depending on the c_ml_flags def __init__(self, space, ml, w_self, w_module=None): self.ml = ml self.name = rffi.charp2str(rffi.cast(rffi.CCHARP,self.ml.c_ml_name)) @@ -56,7 +57,7 @@ w_self = self.w_self flags = rffi.cast(lltype.Signed, self.ml.c_ml_flags) flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST) - if space.is_true(w_kw) and not flags & METH_KEYWORDS: + if not flags & METH_KEYWORDS and space.is_true(w_kw): raise oefmt(space.w_TypeError, "%s() takes no keyword arguments", self.name) @@ -96,6 +97,20 @@ else: return space.w_None +class W_PyCFunctionObjectNoArgs(W_PyCFunctionObject): + def call(self, space, w_self, w_args, w_kw): + # Call the C function + if w_self is None: + w_self = self.w_self + func = self.ml.c_ml_meth + return generic_cpy_call(space, func, w_self, None) + +class W_PyCFunctionObjectSingleObject(W_PyCFunctionObject): + def call(self, space, w_self, w_o, w_kw): + if w_self is None: + w_self = self.w_self + func = self.ml.c_ml_meth + return generic_cpy_call(space, func, w_self, w_o) class W_PyCMethodObject(W_PyCFunctionObject): w_self = None @@ -215,6 +230,7 @@ (self.method_name, self.w_objclass.name)) + at jit.dont_look_inside def cwrapper_descr_call(space, w_self, __args__): self = space.interp_w(W_PyCWrapperObject, w_self) args_w, kw_w = __args__.unpack() @@ -225,10 +241,22 @@ space.setitem(w_kw, space.newtext(key), w_obj) return self.call(space, w_self, w_args, w_kw) +def cfunction_descr_call_noargs(space, w_self): + # special case for calling with flags METH_NOARGS + self = space.interp_w(W_PyCFunctionObjectNoArgs, w_self) + return self.call(space, None, None, None) +def cfunction_descr_call_single_object(space, w_self, w_o): + # special case for calling with flags METH_O + self = space.interp_w(W_PyCFunctionObjectSingleObject, w_self) + return self.call(space, None, w_o, None) + + at jit.dont_look_inside def cfunction_descr_call(space, w_self, __args__): + # specialize depending on the W_PyCFunctionObject self = space.interp_w(W_PyCFunctionObject, w_self) args_w, kw_w = __args__.unpack() + # XXX __args__.unpack is slow w_args = space.newtuple(args_w) w_kw = space.newdict() for key, w_obj in kw_w.items(): @@ -236,6 +264,7 @@ ret = self.call(space, None, w_args, w_kw) return ret + at jit.dont_look_inside def cclassmethod_descr_call(space, w_self, __args__): self = space.interp_w(W_PyCFunctionObject, w_self) args_w, kw_w = __args__.unpack() @@ -276,6 +305,26 @@ ) W_PyCFunctionObject.typedef.acceptable_as_base_class = False +W_PyCFunctionObjectNoArgs.typedef = TypeDef( + 'builtin_function_or_method', W_PyCFunctionObject.typedef, + __call__ = interp2app(cfunction_descr_call_noargs), + __doc__ = GetSetProperty(W_PyCFunctionObjectNoArgs.get_doc), + __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectNoArgs), + __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectNoArgs, + wrapfn="newtext_or_none"), + ) +W_PyCFunctionObjectNoArgs.typedef.acceptable_as_base_class = False + +W_PyCFunctionObjectSingleObject.typedef = TypeDef( + 'builtin_function_or_method', W_PyCFunctionObject.typedef, + __call__ = interp2app(cfunction_descr_call_single_object), + __doc__ = GetSetProperty(W_PyCFunctionObjectSingleObject.get_doc), + __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectSingleObject), + __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectSingleObject, + wrapfn="newtext_or_none"), + ) +W_PyCFunctionObjectSingleObject.typedef.acceptable_as_base_class = False + W_PyCMethodObject.typedef = TypeDef( 'method_descriptor', __get__ = interp2app(cmethod_descr_get), diff --git a/pypy/module/cpyext/modsupport.py b/pypy/module/cpyext/modsupport.py --- a/pypy/module/cpyext/modsupport.py +++ b/pypy/module/cpyext/modsupport.py @@ -1,11 +1,13 @@ from rpython.rtyper.lltypesystem import rffi, lltype from pypy.module.cpyext.api import cpython_api, cpython_struct, \ - METH_STATIC, METH_CLASS, METH_COEXIST, CANNOT_FAIL, CONST_STRING + METH_STATIC, METH_CLASS, METH_COEXIST, CANNOT_FAIL, CONST_STRING, \ + METH_NOARGS, METH_O from pypy.module.cpyext.pyobject import PyObject, as_pyobj from pypy.interpreter.module import Module from pypy.module.cpyext.methodobject import ( W_PyCFunctionObject, PyCFunction_NewEx, PyDescr_NewMethod, - PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New) + PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New, + W_PyCFunctionObjectNoArgs, W_PyCFunctionObjectSingleObject) from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall from pypy.module.cpyext.state import State from pypy.interpreter.error import oefmt @@ -79,6 +81,13 @@ space.newtext(rffi.charp2str(doc))) return w_mod # borrowed result kept alive in PyImport_AddModule() +def _create_pyc_function_object(space, method, w_self, w_name, flags): + flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST) + if flags == METH_NOARGS: + return W_PyCFunctionObjectNoArgs(space, method, w_self, w_name) + if flags == METH_O: + return W_PyCFunctionObjectSingleObject(space, method, w_self, w_name) + return W_PyCFunctionObject(space, method, w_self, w_name) def convert_method_defs(space, dict_w, methods, w_type, w_self=None, name=None): w_name = space.newtext_or_none(name) @@ -98,7 +107,8 @@ raise oefmt(space.w_ValueError, "module functions cannot set METH_CLASS or " "METH_STATIC") - w_obj = W_PyCFunctionObject(space, method, w_self, w_name) + w_obj = _create_pyc_function_object(space, method, w_self, + w_name, flags) else: if methodname in dict_w and not (flags & METH_COEXIST): continue diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -14,7 +14,7 @@ from rpython.rlib.objectmodel import specialize from rpython.rlib.objectmodel import keepalive_until_here from rpython.rtyper.annlowlevel import llhelper -from rpython.rlib import rawrefcount +from rpython.rlib import rawrefcount, jit from rpython.rlib.debug import fatalerror @@ -151,6 +151,7 @@ class InvalidPointerException(Exception): pass + at jit.dont_look_inside def create_ref(space, w_obj, w_userdata=None, immortal=False): """ Allocates a PyObject, and fills its fields with info from the given @@ -190,6 +191,7 @@ w_marker_deallocating = W_Root() + at jit.dont_look_inside def from_ref(space, ref): """ Finds the interpreter object corresponding to the given reference. If the @@ -227,6 +229,7 @@ assert isinstance(w_type, W_TypeObject) return get_typedescr(w_type.layout.typedef).realize(space, ref) + at jit.dont_look_inside def as_pyobj(space, w_obj, w_userdata=None, immortal=False): """ Returns a 'PyObject *' representing the given intepreter object. 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 @@ -410,6 +410,7 @@ ptr = get_new_method_def(space) ptr.c_ml_meth = rffi.cast(PyCFunction, llslot(space, tp_new_wrapper)) + at jit.dont_look_inside def is_tp_new_wrapper(space, ml): return ml.c_ml_meth == rffi.cast(PyCFunction, llslot(space, tp_new_wrapper)) diff --git a/pypy/module/pypyjit/policy.py b/pypy/module/pypyjit/policy.py --- a/pypy/module/pypyjit/policy.py +++ b/pypy/module/pypyjit/policy.py @@ -14,7 +14,7 @@ return True if '.' in modname: modname, rest = modname.split('.', 1) - if modname in ['unicodedata', 'gc', '_minimal_curses', 'cpyext']: + if modname in ['unicodedata', 'gc', '_minimal_curses']: return False else: rest = '' From pypy.commits at gmail.com Sun Oct 1 17:06:16 2017 From: pypy.commits at gmail.com (rlamy) Date: Sun, 01 Oct 2017 14:06:16 -0700 (PDT) Subject: [pypy-commit] pypy refactor-slots: Split off the builtin case from update_all_slots() Message-ID: <59d158c8.919bdf0a.2ce89.c1b3@mx.google.com> Author: Ronan Lamy Branch: refactor-slots Changeset: r92539:d6069a5b0d69 Date: 2017-10-01 23:05 +0200 http://bitbucket.org/pypy/pypy/changeset/d6069a5b0d69/ Log: Split off the builtin case from update_all_slots() 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 @@ -257,31 +257,19 @@ # overwrite slots that are already set: these ones are probably # coming from a parent C type. - if w_type.is_heaptype(): - typedef = None - search_dict_w = w_type.dict_w - else: - typedef = w_type.layout.typedef - search_dict_w = None - for method_name, slot_name, slot_names, slot_apifunc in slotdefs_for_tp_slots: slot_func_helper = None - if typedef is not None: - # built-in types: expose as many slots as possible, even - # if it happens to come from some parent class - slot_apifunc = get_slot_tp_function(space, typedef, slot_name) + w_descr = w_type.dict_w.get(method_name, None) + if w_descr: + # use the slot_apifunc (userslots) to lookup at runtime + pass + elif len(slot_names) ==1: + # 'inherit' from tp_base + slot_func_helper = getattr(pto.c_tp_base, slot_names[0]) else: - w_descr = search_dict_w.get(method_name, None) - if w_descr: - # use the slot_apifunc (userslots) to lookup at runtime - pass - elif len(slot_names) ==1: - # 'inherit' from tp_base - slot_func_helper = getattr(pto.c_tp_base, slot_names[0]) - else: - struct = getattr(pto.c_tp_base, slot_names[0]) - if struct: - slot_func_helper = getattr(struct, slot_names[1]) + struct = getattr(pto.c_tp_base, slot_names[0]) + if struct: + slot_func_helper = getattr(struct, slot_names[1]) if not slot_func_helper: if not slot_apifunc: @@ -290,6 +278,16 @@ slot_func_helper = slot_apifunc.get_llhelper(space) fill_slot(space, pto, w_type, slot_names, slot_func_helper) +def update_all_slots_builtin(space, w_type, pto): + typedef = w_type.layout.typedef + for method_name, slot_name, slot_names, slot_apifunc in slotdefs_for_tp_slots: + slot_apifunc = get_slot_tp_function(space, typedef, slot_name) + if not slot_apifunc: + warn_missing_slot(space, method_name, slot_name, w_type) + continue + slot_func_helper = slot_apifunc.get_llhelper(space) + fill_slot(space, pto, w_type, slot_names, slot_func_helper) + @specialize.arg(3) def fill_slot(space, pto, w_type, slot_names, slot_func_helper): # XXX special case wrapper-functions and use a "specific" slot func @@ -795,7 +793,10 @@ if pto.c_tp_itemsize < pto.c_tp_base.c_tp_itemsize: pto.c_tp_itemsize = pto.c_tp_base.c_tp_itemsize - update_all_slots(space, w_type, pto) + if w_type.is_heaptype(): + update_all_slots(space, w_type, pto) + else: + update_all_slots_builtin(space, w_type, pto) if not pto.c_tp_new: base_object_pyo = make_ref(space, space.w_object) base_object_pto = rffi.cast(PyTypeObjectPtr, base_object_pyo) From pypy.commits at gmail.com Sun Oct 1 17:06:47 2017 From: pypy.commits at gmail.com (mattip) Date: Sun, 01 Oct 2017 14:06:47 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: win32 can run without a console, return None in this case (amaury advising) Message-ID: <59d158e7.0f4a1c0a.de4ae.17d3@mx.google.com> Author: Matti Picus Branch: py3.5 Changeset: r92540:73f012309b3c Date: 2017-10-02 00:01 +0300 http://bitbucket.org/pypy/pypy/changeset/73f012309b3c/ Log: win32 can run without a console, return None in this case (amaury advising) 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 @@ -2217,10 +2217,13 @@ if not (rposix.is_valid_fd(fd) and os.isatty(fd)): return space.w_None if _WIN32: + ccp = rwin32.GetConsoleOutputCP() + if ccp == 0: + return space.w_None if fd == 0: - return space.newtext('cp%d' % rwin32.GetConsoleCP()) + return space.newtext('cp%d' % ccp) if fd in (1, 2): - return space.newtext('cp%d' % rwin32.GetConsoleOutputCP()) + return space.newtext('cp%d' % ccp) from rpython.rlib import rlocale if rlocale.HAVE_LANGINFO: codeset = rlocale.nl_langinfo(rlocale.CODESET) From pypy.commits at gmail.com Sun Oct 1 17:06:49 2017 From: pypy.commits at gmail.com (mattip) Date: Sun, 01 Oct 2017 14:06:49 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: pypy-cw.exe -> pypy-c.exe, cw is the non-console exe (amaury) Message-ID: <59d158e9.4e301c0a.e70a7.f086@mx.google.com> Author: Matti Picus Branch: py3.5 Changeset: r92541:6867bc3ac3d1 Date: 2017-10-02 00:05 +0300 http://bitbucket.org/pypy/pypy/changeset/6867bc3ac3d1/ Log: pypy-cw.exe -> pypy-c.exe, cw is the non-console exe (amaury) diff --git a/pypy/interpreter/test/test_app_main.py b/pypy/interpreter/test/test_app_main.py --- a/pypy/interpreter/test/test_app_main.py +++ b/pypy/interpreter/test/test_app_main.py @@ -1163,7 +1163,7 @@ old_sys_path = sys.path[:] sys.path.append(self.goal_dir) if sys.platform == 'win32': - exename = 'pypy3-cw.exe' + exename = 'pypy3-c.exe' else: exename = 'pypy3-c' try: @@ -1185,7 +1185,7 @@ old_sys_path = sys.path[:] sys.path.append(self.goal_dir) if sys.platform == 'win32': - exename = 'pypy3-cw.exe' + exename = 'pypy3-c.exe' else: exename = 'pypy3-c' try: diff --git a/pypy/pytest-A.cfg b/pypy/pytest-A.cfg --- a/pypy/pytest-A.cfg +++ b/pypy/pytest-A.cfg @@ -3,6 +3,6 @@ interp = ['python'] if sys.platform == 'win32': - test_driver = ['test_all.py', '-A', '--python=goal/pypy3-cw.exe'] + test_driver = ['test_all.py', '-A', '--python=goal/pypy3-c.exe'] else: test_driver = ['test_all.py', '-A', '--python=goal/pypy3-c'] diff --git a/testrunner/lib_python_tests.py b/testrunner/lib_python_tests.py --- a/testrunner/lib_python_tests.py +++ b/testrunner/lib_python_tests.py @@ -12,7 +12,7 @@ os.environ['PYTEST_PLUGINS'] = '' if sys.platform == 'win32': - pypyopt = "--pypy=pypy/goal/pypy3-cw.exe" + pypyopt = "--pypy=pypy/goal/pypy3-c.exe" else: pypyopt = "--pypy=pypy/goal/pypy3-c" diff --git a/testrunner/pypyjit_tests.py b/testrunner/pypyjit_tests.py --- a/testrunner/pypyjit_tests.py +++ b/testrunner/pypyjit_tests.py @@ -12,7 +12,7 @@ os.environ['PYTEST_PLUGINS'] = '' if sys.platform == 'win32': - pypyopt = "--pypy=pypy/goal/pypy3-cw.exe" + pypyopt = "--pypy=pypy/goal/pypy3-c.exe" else: pypyopt = "--pypy=pypy/goal/pypy3-c" From pypy.commits at gmail.com Sun Oct 1 18:16:16 2017 From: pypy.commits at gmail.com (rlamy) Date: Sun, 01 Oct 2017 15:16:16 -0700 (PDT) Subject: [pypy-commit] pypy default: Remove unnecessary from_ref() call from subtype_dealloc() Message-ID: <59d16930.03d71c0a.e89db.66bd@mx.google.com> Author: Ronan Lamy Branch: Changeset: r92542:d8d0ab3d4484 Date: 2017-10-02 00:15 +0200 http://bitbucket.org/pypy/pypy/changeset/d8d0ab3d4484/ Log: Remove unnecessary from_ref() call from subtype_dealloc() 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 @@ -550,25 +550,18 @@ pto = obj.c_ob_type base = pto this_func_ptr = llslot(space, subtype_dealloc) - w_obj = from_ref(space, rffi.cast(PyObject, base)) # This wrapper is created on a specific type, call it w_A. # We wish to call the dealloc function from one of the base classes of w_A, # the first of which is not this function itself. # w_obj is an instance of w_A or one of its subclasses. So climb up the # inheritance chain until base.c_tp_dealloc is exactly this_func, and then # continue on up until they differ. - #print 'subtype_dealloc, start from', rffi.charp2str(base.c_tp_name) while base.c_tp_dealloc != this_func_ptr: base = base.c_tp_base assert base - #print ' ne move to', rffi.charp2str(base.c_tp_name) - w_obj = from_ref(space, rffi.cast(PyObject, base)) while base.c_tp_dealloc == this_func_ptr: base = base.c_tp_base assert base - #print ' eq move to', rffi.charp2str(base.c_tp_name) - w_obj = from_ref(space, rffi.cast(PyObject, base)) - #print ' end with', rffi.charp2str(base.c_tp_name) dealloc = base.c_tp_dealloc # XXX call tp_del if necessary generic_cpy_call(space, dealloc, obj) From pypy.commits at gmail.com Sun Oct 1 18:32:05 2017 From: pypy.commits at gmail.com (amauryfa) Date: Sun, 01 Oct 2017 15:32:05 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: On Windows, use GetConsoleCP for stdin. Message-ID: <59d16ce5.090b1c0a.5e7af.20b5@mx.google.com> Author: Amaury Forgeot d'Arc Branch: py3.5 Changeset: r92543:e3aa955289ca Date: 2017-10-02 00:31 +0200 http://bitbucket.org/pypy/pypy/changeset/e3aa955289ca/ Log: On Windows, use GetConsoleCP for stdin. 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 @@ -2217,12 +2217,15 @@ if not (rposix.is_valid_fd(fd) and os.isatty(fd)): return space.w_None if _WIN32: - ccp = rwin32.GetConsoleOutputCP() - if ccp == 0: - return space.w_None if fd == 0: - return space.newtext('cp%d' % ccp) - if fd in (1, 2): + ccp = rwin32.GetConsoleCP() + elif fd in (1, 2): + ccp = rwin32.GetConsoleOutputCP() + else: + ccp = 0 + # GetConsoleCP() and GetConsoleOutputCP() return 0 if the + # application has no console. + if ccp != 0: return space.newtext('cp%d' % ccp) from rpython.rlib import rlocale if rlocale.HAVE_LANGINFO: From pypy.commits at gmail.com Sun Oct 1 19:03:38 2017 From: pypy.commits at gmail.com (rlamy) Date: Sun, 01 Oct 2017 16:03:38 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: hg merge default Message-ID: <59d1744a.84ad1c0a.23d32.1a08@mx.google.com> Author: Ronan Lamy Branch: py3.5 Changeset: r92544:41b2cbb76719 Date: 2017-10-02 01:02 +0200 http://bitbucket.org/pypy/pypy/changeset/41b2cbb76719/ Log: hg merge default diff --git a/pypy/doc/release-v5.9.0.rst b/pypy/doc/release-v5.9.0.rst --- a/pypy/doc/release-v5.9.0.rst +++ b/pypy/doc/release-v5.9.0.rst @@ -10,15 +10,18 @@ This new PyPy2.7 release includes the upstream stdlib version 2.7.13, and PyPy3.5 includes the upstream stdlib version 3.5.3. -NumPy and Pandas now work on PyPy2.7. Issues that appeared as excessive memory +NumPy and Pandas now work on PyPy2.7 (together with Cython 0.27.1). Issues +that appeared as excessive memory use were cleared up and other incompatibilities were resolved. The C-API compatibility layer does slow down code which crosses the python-c interface often, we have ideas on how it could be improved, and still recommend using pure python on PyPy or interfacing via CFFI_. Many other modules based on C-API exentions now work on PyPy as well. -Cython 0.27 (released last week) should support more projects with PyPy, both -on PyPy2.7 and PyPy3.5 beta. +Cython 0.27.1 (released very recently) supports more projects with PyPy, both +on PyPy2.7 and PyPy3.5 beta. Note version **0.27.1** is now the minimum +version that supports this version of PyPy, due to some interactions with +updated C-API interface code. We optimized the JSON parser for recurring string keys, which should decrease memory use to 50% and increase parsing speed by up to 15% for large JSON files 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 @@ -4,3 +4,12 @@ .. this is a revision shortly after release-pypy2.7-v5.9.0 .. startrev:899e5245de1e + +.. branch: cpyext-jit + +Differentiate the code to call METH_NOARGS, METH_O and METH_VARARGS in cpyext: +this allows to write specialized code which is much faster than previous +completely generic version. Moreover, let the JIT to look inside the cpyext +module: the net result is that cpyext calls are up to 7x faster. However, this +is true only for very simple situations: in all real life code, we are still +much slower than CPython (more optimizations to come) diff --git a/pypy/module/_cffi_backend/cffi1_module.py b/pypy/module/_cffi_backend/cffi1_module.py --- a/pypy/module/_cffi_backend/cffi1_module.py +++ b/pypy/module/_cffi_backend/cffi1_module.py @@ -1,4 +1,5 @@ from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rlib import jit from pypy.interpreter.error import oefmt from pypy.interpreter.module import Module @@ -15,7 +16,7 @@ INITFUNCPTR = lltype.Ptr(lltype.FuncType([rffi.VOIDPP], lltype.Void)) - + at jit.dont_look_inside def load_cffi1_module(space, name, path, initptr): # This is called from pypy.module.cpyext.api.load_extension_module() from pypy.module._cffi_backend.call_python import get_ll_cffi_call_python diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py --- a/pypy/module/_cffi_backend/ctypeptr.py +++ b/pypy/module/_cffi_backend/ctypeptr.py @@ -158,10 +158,11 @@ class W_CTypePtrBase(W_CTypePtrOrArray): # base class for both pointers and pointers-to-functions - _attrs_ = ['is_void_ptr', 'is_voidchar_ptr'] - _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr'] + _attrs_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] + _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] is_void_ptr = False is_voidchar_ptr = False + is_onebyte_ptr = False def convert_to_object(self, cdata): ptrdata = rffi.cast(rffi.CCHARPP, cdata)[0] @@ -181,12 +182,20 @@ if self.is_void_ptr or other.is_void_ptr: pass # cast from or to 'void *' elif self.is_voidchar_ptr or other.is_voidchar_ptr: - space = self.space - msg = ("implicit cast from '%s' to '%s' " - "will be forbidden in the future (check that the types " - "are as you expect; use an explicit ffi.cast() if they " - "are correct)" % (other.name, self.name)) - space.warn(space.newtext(msg), space.w_UserWarning) + # for backward compatibility, accept "char *" as either + # source of target. This is not what C does, though, + # so emit a warning that will eventually turn into an + # error. The warning is turned off if both types are + # pointers to single bytes. + if self.is_onebyte_ptr and other.is_onebyte_ptr: + pass # no warning + else: + space = self.space + msg = ("implicit cast from '%s' to '%s' " + "will be forbidden in the future (check that the types " + "are as you expect; use an explicit ffi.cast() if they " + "are correct)" % (other.name, self.name)) + space.warn(space.newtext(msg), space.w_UserWarning) else: raise self._convert_error("compatible pointer", w_ob) @@ -216,6 +225,7 @@ self.is_void_ptr = isinstance(ctitem, ctypevoid.W_CTypeVoid) self.is_voidchar_ptr = (self.is_void_ptr or isinstance(ctitem, ctypeprim.W_CTypePrimitiveChar)) + self.is_onebyte_ptr = (ctitem.size == 1) W_CTypePtrBase.__init__(self, space, size, extra, 2, ctitem) def newp(self, w_init, allocator): diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -2099,7 +2099,8 @@ if sys.platform.startswith("linux"): BWChar = new_primitive_type("wchar_t") assert sizeof(BWChar) == 4 - assert int(cast(BWChar, -1)) == -1 # signed, on linux + # wchar_t is often signed on Linux, but not always (e.g. on ARM) + assert int(cast(BWChar, -1)) in (-1, 4294967295) def test_char16(): BChar16 = new_primitive_type("char16_t") @@ -3903,9 +3904,11 @@ BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BVoidP = new_pointer_type(new_void_type()) + BUCharP = new_pointer_type(new_primitive_type("unsigned char")) z1 = cast(BCharP, 0) z2 = cast(BIntP, 0) z3 = cast(BVoidP, 0) + z4 = cast(BUCharP, 0) with warnings.catch_warnings(record=True) as w: newp(new_pointer_type(BIntP), z1) # warn assert len(w) == 1 @@ -3919,6 +3922,12 @@ assert len(w) == 2 newp(new_pointer_type(BIntP), z3) # fine assert len(w) == 2 + newp(new_pointer_type(BCharP), z4) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z1) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z3) # fine + assert len(w) == 2 # check that the warnings are associated with lines in this file assert w[1].lineno == w[0].lineno + 4 diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -453,6 +453,11 @@ if func.__name__ in FUNCTIONS_BY_HEADER[header]: raise ValueError("%s already registered" % func.__name__) func._always_inline_ = 'try' + # + # XXX: should we @jit.dont_look_inside all the @cpython_api functions, + # or we should only disable some of them? + func._jit_look_inside_ = False + # api_function = ApiFunction( argtypes, restype, func, error=_compute_error(error, restype), gil=gil, 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,5 @@ from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rlib import jit from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt @@ -42,8 +43,8 @@ from pypy.module.cpyext.object import _dealloc _dealloc(space, py_obj) - class W_PyCFunctionObject(W_Root): + # TODO create a slightly different class depending on the c_ml_flags def __init__(self, space, ml, w_self, w_module=None): self.ml = ml self.name = rffi.charp2str(rffi.cast(rffi.CCHARP, self.ml.c_ml_name)) @@ -56,7 +57,7 @@ w_self = self.w_self flags = rffi.cast(lltype.Signed, self.ml.c_ml_flags) flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST) - if space.is_true(w_kw) and not flags & METH_KEYWORDS: + if not flags & METH_KEYWORDS and space.is_true(w_kw): raise oefmt(space.w_TypeError, "%s() takes no keyword arguments", self.name) @@ -89,6 +90,20 @@ else: return space.w_None +class W_PyCFunctionObjectNoArgs(W_PyCFunctionObject): + def call(self, space, w_self, w_args, w_kw): + # Call the C function + if w_self is None: + w_self = self.w_self + func = self.ml.c_ml_meth + return generic_cpy_call(space, func, w_self, None) + +class W_PyCFunctionObjectSingleObject(W_PyCFunctionObject): + def call(self, space, w_self, w_o, w_kw): + if w_self is None: + w_self = self.w_self + func = self.ml.c_ml_meth + return generic_cpy_call(space, func, w_self, w_o) class W_PyCMethodObject(W_PyCFunctionObject): w_self = None @@ -208,6 +223,7 @@ (self.method_name, self.w_objclass.name)) + at jit.dont_look_inside def cwrapper_descr_call(space, w_self, __args__): self = space.interp_w(W_PyCWrapperObject, w_self) args_w, kw_w = __args__.unpack() @@ -218,10 +234,22 @@ space.setitem(w_kw, space.newtext(key), w_obj) return self.call(space, w_self, w_args, w_kw) +def cfunction_descr_call_noargs(space, w_self): + # special case for calling with flags METH_NOARGS + self = space.interp_w(W_PyCFunctionObjectNoArgs, w_self) + return self.call(space, None, None, None) +def cfunction_descr_call_single_object(space, w_self, w_o): + # special case for calling with flags METH_O + self = space.interp_w(W_PyCFunctionObjectSingleObject, w_self) + return self.call(space, None, w_o, None) + + at jit.dont_look_inside def cfunction_descr_call(space, w_self, __args__): + # specialize depending on the W_PyCFunctionObject self = space.interp_w(W_PyCFunctionObject, w_self) args_w, kw_w = __args__.unpack() + # XXX __args__.unpack is slow w_args = space.newtuple(args_w) w_kw = space.newdict() for key, w_obj in kw_w.items(): @@ -229,6 +257,7 @@ ret = self.call(space, None, w_args, w_kw) return ret + at jit.dont_look_inside def cclassmethod_descr_call(space, w_self, __args__): self = space.interp_w(W_PyCFunctionObject, w_self) args_w, kw_w = __args__.unpack() @@ -266,6 +295,26 @@ ) W_PyCFunctionObject.typedef.acceptable_as_base_class = False +W_PyCFunctionObjectNoArgs.typedef = TypeDef( + 'builtin_function_or_method', W_PyCFunctionObject.typedef, + __call__ = interp2app(cfunction_descr_call_noargs), + __doc__ = GetSetProperty(W_PyCFunctionObjectNoArgs.get_doc), + __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectNoArgs), + __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectNoArgs, + wrapfn="newtext_or_none"), + ) +W_PyCFunctionObjectNoArgs.typedef.acceptable_as_base_class = False + +W_PyCFunctionObjectSingleObject.typedef = TypeDef( + 'builtin_function_or_method', W_PyCFunctionObject.typedef, + __call__ = interp2app(cfunction_descr_call_single_object), + __doc__ = GetSetProperty(W_PyCFunctionObjectSingleObject.get_doc), + __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectSingleObject), + __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectSingleObject, + wrapfn="newtext_or_none"), + ) +W_PyCFunctionObjectSingleObject.typedef.acceptable_as_base_class = False + W_PyCMethodObject.typedef = TypeDef( 'method_descriptor', __get__ = interp2app(cmethod_descr_get), diff --git a/pypy/module/cpyext/modsupport.py b/pypy/module/cpyext/modsupport.py --- a/pypy/module/cpyext/modsupport.py +++ b/pypy/module/cpyext/modsupport.py @@ -1,13 +1,15 @@ from rpython.rtyper.lltypesystem import rffi, lltype from pypy.module.cpyext.api import ( cpython_api, METH_STATIC, METH_CLASS, METH_COEXIST, CANNOT_FAIL, cts, + METH_NOARGS, METH_O, parse_dir, bootstrap_function, generic_cpy_call, generic_cpy_call_dont_convert_result, slot_function) from pypy.module.cpyext.pyobject import PyObject, as_pyobj, make_typedescr from pypy.interpreter.module import Module from pypy.module.cpyext.methodobject import ( W_PyCFunctionObject, PyCFunction_NewEx, PyDescr_NewMethod, - PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New) + PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New, + W_PyCFunctionObjectNoArgs, W_PyCFunctionObjectSingleObject) from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall from pypy.module.cpyext.state import State from pypy.interpreter.error import oefmt @@ -165,6 +167,13 @@ "exception", w_mod.w_name) cur_slot = rffi.ptradd(cur_slot, 1) +def _create_pyc_function_object(space, method, w_self, w_name, flags): + flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST) + if flags == METH_NOARGS: + return W_PyCFunctionObjectNoArgs(space, method, w_self, w_name) + if flags == METH_O: + return W_PyCFunctionObjectSingleObject(space, method, w_self, w_name) + return W_PyCFunctionObject(space, method, w_self, w_name) def convert_method_defs(space, dict_w, methods, w_type, w_self=None, name=None): w_name = space.newtext_or_none(name) @@ -184,7 +193,8 @@ raise oefmt(space.w_ValueError, "module functions cannot set METH_CLASS or " "METH_STATIC") - w_obj = W_PyCFunctionObject(space, method, w_self, w_name) + w_obj = _create_pyc_function_object(space, method, w_self, + w_name, flags) else: if methodname in dict_w and not (flags & METH_COEXIST): continue diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -14,7 +14,7 @@ from rpython.rlib.objectmodel import specialize from rpython.rlib.objectmodel import keepalive_until_here from rpython.rtyper.annlowlevel import llhelper -from rpython.rlib import rawrefcount +from rpython.rlib import rawrefcount, jit from rpython.rlib.debug import fatalerror @@ -151,6 +151,7 @@ class InvalidPointerException(Exception): pass + at jit.dont_look_inside def create_ref(space, w_obj, w_userdata=None, immortal=False): """ Allocates a PyObject, and fills its fields with info from the given @@ -190,6 +191,7 @@ w_marker_deallocating = W_Root() + at jit.dont_look_inside def from_ref(space, ref): """ Finds the interpreter object corresponding to the given reference. If the @@ -227,6 +229,7 @@ assert isinstance(w_type, W_TypeObject) return get_typedescr(w_type.layout.typedef).realize(space, ref) + at jit.dont_look_inside def as_pyobj(space, w_obj, w_userdata=None, immortal=False): """ Returns a 'PyObject *' representing the given intepreter object. 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 @@ -420,23 +420,6 @@ from rpython.rlib.nonconst import NonConstant -SLOTS = {} - - at specialize.memo() -def get_slot_tp_function(space, typedef, name): - """Return a description of the slot C function to use for the built-in - type for 'typedef'. The 'name' is the slot name. This is a memo - function that, after translation, returns one of a built-in finite set. - """ - key = (typedef, name) - try: - return SLOTS[key] - except KeyError: - slot_func = build_slot_tp_function(space, typedef, name) - api_func = slot_func.api_func if slot_func else None - SLOTS[key] = api_func - return api_func - def build_slot_tp_function(space, typedef, name): w_type = space.gettypeobject(typedef) 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 @@ -29,7 +29,7 @@ PyObject, make_ref, from_ref, get_typedescr, make_typedescr, track_reference, Py_DecRef, as_pyobj, incref) from pypy.module.cpyext.slotdefs import ( - slotdefs_for_tp_slots, slotdefs_for_wrappers, get_slot_tp_function, + slotdefs_for_tp_slots, slotdefs_for_wrappers, build_slot_tp_function, llslot) from pypy.module.cpyext.state import State from pypy.module.cpyext.structmember import PyMember_GetOne, PyMember_SetOne @@ -226,7 +226,30 @@ dict_w[name] = w_descr i += 1 +SLOTS = {} + at specialize.memo() +def get_slot_tp_function(space, typedef, name): + """Return a description of the slot C function to use for the built-in + type for 'typedef'. The 'name' is the slot name. This is a memo + function that, after translation, returns one of a built-in finite set. + """ + key = (typedef, name) + try: + return SLOTS[key] + except KeyError: + slot_func = build_slot_tp_function(space, typedef, name) + api_func = slot_func.api_func if slot_func else None + SLOTS[key] = api_func + return api_func + missing_slots={} +def warn_missing_slot(space, method_name, slot_name, w_type): + if not we_are_translated(): + if slot_name not in missing_slots: + missing_slots[slot_name] = w_type.getname(space) + print "missing slot %r/%r, discovered on %r" % ( + method_name, slot_name, w_type.getname(space)) + def update_all_slots(space, w_type, pto): # fill slots in pto # Not very sure about it, but according to @@ -243,13 +266,11 @@ for method_name, slot_name, slot_names, slot_apifunc in slotdefs_for_tp_slots: slot_func_helper = None - if search_dict_w is None: + if typedef is not None: # built-in types: expose as many slots as possible, even # if it happens to come from some parent class - slot_apifunc = None # use get_slot_tp_function + slot_apifunc = get_slot_tp_function(space, typedef, slot_name) else: - # For heaptypes, w_type.layout.typedef will be object's typedef, and - # get_slot_tp_function will fail w_descr = search_dict_w.get(method_name, None) if w_descr: # use the slot_apifunc (userslots) to lookup at runtime @@ -263,59 +284,54 @@ slot_func_helper = getattr(struct, slot_names[1]) if not slot_func_helper: - if typedef is not None: - if slot_apifunc is None: - slot_apifunc = get_slot_tp_function(space, typedef, slot_name) if not slot_apifunc: - if not we_are_translated(): - if slot_name not in missing_slots: - missing_slots[slot_name] = w_type.getname(space) - print "missing slot %r/%r, discovered on %r" % ( - method_name, slot_name, w_type.getname(space)) + warn_missing_slot(space, method_name, slot_name, w_type) continue slot_func_helper = slot_apifunc.get_llhelper(space) + fill_slot(space, pto, w_type, slot_names, slot_func_helper) - # XXX special case wrapper-functions and use a "specific" slot func + at specialize.arg(3) +def fill_slot(space, pto, w_type, slot_names, slot_func_helper): + # XXX special case wrapper-functions and use a "specific" slot func + if len(slot_names) == 1: + if not getattr(pto, slot_names[0]): + setattr(pto, slot_names[0], slot_func_helper) + elif ((w_type is space.w_list or w_type is space.w_tuple) and + slot_names[0] == 'c_tp_as_number'): + # XXX hack - how 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 + elif ((space.issubtype_w(w_type, space.w_bytes) or + space.issubtype_w(w_type, space.w_unicode)) and + slot_names[0] == 'c_tp_as_number'): + # like above but for any str type + pass + else: + assert len(slot_names) == 2 + struct = getattr(pto, slot_names[0]) + if not struct: + #assert not space.config.translating + assert not pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE + if slot_names[0] == 'c_tp_as_number': + STRUCT_TYPE = PyNumberMethods + elif slot_names[0] == 'c_tp_as_sequence': + STRUCT_TYPE = PySequenceMethods + elif slot_names[0] == 'c_tp_as_buffer': + STRUCT_TYPE = PyBufferProcs + elif slot_names[0] == 'c_tp_as_mapping': + STRUCT_TYPE = PyMappingMethods + else: + raise AssertionError( + "Structure not allocated: %s" % (slot_names[0],)) + struct = lltype.malloc(STRUCT_TYPE, flavor='raw', zero=True) + setattr(pto, slot_names[0], struct) - if len(slot_names) == 1: - if not getattr(pto, slot_names[0]): - setattr(pto, slot_names[0], slot_func_helper) - elif ((w_type is space.w_list or w_type is space.w_tuple) and - slot_names[0] == 'c_tp_as_number'): - # XXX hack - how 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 - elif ((space.issubtype_w(w_type, space.w_bytes) or - space.issubtype_w(w_type, space.w_unicode)) and - slot_names[0] == 'c_tp_as_number'): - # like above but for any str type - pass - else: - assert len(slot_names) == 2 - struct = getattr(pto, slot_names[0]) - if not struct: - #assert not space.config.translating - assert not pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE - if slot_names[0] == 'c_tp_as_number': - STRUCT_TYPE = PyNumberMethods - elif slot_names[0] == 'c_tp_as_sequence': - STRUCT_TYPE = PySequenceMethods - elif slot_names[0] == 'c_tp_as_buffer': - STRUCT_TYPE = PyBufferProcs - elif slot_names[0] == 'c_tp_as_mapping': - STRUCT_TYPE = PyMappingMethods - else: - raise AssertionError( - "Structure not allocated: %s" % (slot_names[0],)) - struct = lltype.malloc(STRUCT_TYPE, flavor='raw', zero=True) - setattr(pto, slot_names[0], struct) - - if not getattr(struct, slot_names[1]): - setattr(struct, slot_names[1], slot_func_helper) + if not getattr(struct, slot_names[1]): + setattr(struct, slot_names[1], slot_func_helper) def add_operators(space, dict_w, pto): from pypy.module.cpyext.object import PyObject_HashNotImplemented @@ -395,6 +411,7 @@ ptr = get_new_method_def(space) ptr.c_ml_meth = rffi.cast(PyCFunction, llslot(space, tp_new_wrapper)) + at jit.dont_look_inside def is_tp_new_wrapper(space, ml): return ml.c_ml_meth == rffi.cast(PyCFunction, llslot(space, tp_new_wrapper)) @@ -529,25 +546,18 @@ pto = obj.c_ob_type base = pto this_func_ptr = llslot(space, subtype_dealloc) - w_obj = from_ref(space, rffi.cast(PyObject, base)) # This wrapper is created on a specific type, call it w_A. # We wish to call the dealloc function from one of the base classes of w_A, # the first of which is not this function itself. # w_obj is an instance of w_A or one of its subclasses. So climb up the # inheritance chain until base.c_tp_dealloc is exactly this_func, and then # continue on up until they differ. - #print 'subtype_dealloc, start from', rffi.charp2str(base.c_tp_name) while base.c_tp_dealloc != this_func_ptr: base = base.c_tp_base assert base - #print ' ne move to', rffi.charp2str(base.c_tp_name) - w_obj = from_ref(space, rffi.cast(PyObject, base)) while base.c_tp_dealloc == this_func_ptr: base = base.c_tp_base assert base - #print ' eq move to', rffi.charp2str(base.c_tp_name) - w_obj = from_ref(space, rffi.cast(PyObject, base)) - #print ' end with', rffi.charp2str(base.c_tp_name) dealloc = base.c_tp_dealloc # XXX call tp_del if necessary generic_cpy_call(space, dealloc, obj) @@ -902,7 +912,7 @@ return unrolling_iterable(TABLE) SLOT_TABLE = _parse_typeslots() -def fill_slot(ht, slotnum, ptr): +def fill_ht_slot(ht, slotnum, ptr): for num, membername, slotname, TARGET in SLOT_TABLE: if num == slotnum: setattr(getattr(ht, membername), slotname, rffi.cast(TARGET, ptr)) @@ -977,7 +987,7 @@ # Processed above i += 1 continue - fill_slot(res, slot, slotdef.c_pfunc) + fill_ht_slot(res, slot, slotdef.c_pfunc) # XXX: need to make a copy of the docstring slot, which usually # points to a static string literal i += 1 diff --git a/pypy/module/pypyjit/policy.py b/pypy/module/pypyjit/policy.py --- a/pypy/module/pypyjit/policy.py +++ b/pypy/module/pypyjit/policy.py @@ -14,7 +14,7 @@ return True if '.' in modname: modname, rest = modname.split('.', 1) - if modname in ['unicodedata', 'gc', '_minimal_curses', 'cpyext']: + if modname in ['unicodedata', 'gc', '_minimal_curses']: return False else: rest = '' From pypy.commits at gmail.com Sun Oct 1 19:53:04 2017 From: pypy.commits at gmail.com (rlamy) Date: Sun, 01 Oct 2017 16:53:04 -0700 (PDT) Subject: [pypy-commit] pypy default: Fix translation Message-ID: <59d17fe0.84881c0a.82bd3.4dbf@mx.google.com> Author: Ronan Lamy Branch: Changeset: r92546:d56dadcef996 Date: 2017-10-02 01:52 +0200 http://bitbucket.org/pypy/pypy/changeset/d56dadcef996/ Log: Fix translation 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 @@ -776,7 +776,7 @@ def __init__(self, method_name, slot_name, function, wrapper1, wrapper2, doc): self.method_name = method_name self.slot_name = slot_name - self.slot_names = ("c_" + slot_name).split(".") + self.slot_names = tuple(("c_" + slot_name).split(".")) self.slot_func = function self.wrapper_func = wrapper1 self.wrapper_func_kwds = wrapper2 From pypy.commits at gmail.com Sun Oct 1 19:53:01 2017 From: pypy.commits at gmail.com (rlamy) Date: Sun, 01 Oct 2017 16:53:01 -0700 (PDT) Subject: [pypy-commit] pypy default: Split off the builtin case from update_all_slots() Message-ID: <59d17fdd.43581c0a.77275.b152@mx.google.com> Author: Ronan Lamy Branch: Changeset: r92545:5c485217dd2c Date: 2017-10-01 23:05 +0200 http://bitbucket.org/pypy/pypy/changeset/5c485217dd2c/ Log: Split off the builtin case from update_all_slots() 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 @@ -257,31 +257,19 @@ # overwrite slots that are already set: these ones are probably # coming from a parent C type. - if w_type.is_heaptype(): - typedef = None - search_dict_w = w_type.dict_w - else: - typedef = w_type.layout.typedef - search_dict_w = None - for method_name, slot_name, slot_names, slot_apifunc in slotdefs_for_tp_slots: slot_func_helper = None - if typedef is not None: - # built-in types: expose as many slots as possible, even - # if it happens to come from some parent class - slot_apifunc = get_slot_tp_function(space, typedef, slot_name) + w_descr = w_type.dict_w.get(method_name, None) + if w_descr: + # use the slot_apifunc (userslots) to lookup at runtime + pass + elif len(slot_names) ==1: + # 'inherit' from tp_base + slot_func_helper = getattr(pto.c_tp_base, slot_names[0]) else: - w_descr = search_dict_w.get(method_name, None) - if w_descr: - # use the slot_apifunc (userslots) to lookup at runtime - pass - elif len(slot_names) ==1: - # 'inherit' from tp_base - slot_func_helper = getattr(pto.c_tp_base, slot_names[0]) - else: - struct = getattr(pto.c_tp_base, slot_names[0]) - if struct: - slot_func_helper = getattr(struct, slot_names[1]) + struct = getattr(pto.c_tp_base, slot_names[0]) + if struct: + slot_func_helper = getattr(struct, slot_names[1]) if not slot_func_helper: if not slot_apifunc: @@ -290,6 +278,16 @@ slot_func_helper = slot_apifunc.get_llhelper(space) fill_slot(space, pto, w_type, slot_names, slot_func_helper) +def update_all_slots_builtin(space, w_type, pto): + typedef = w_type.layout.typedef + for method_name, slot_name, slot_names, slot_apifunc in slotdefs_for_tp_slots: + slot_apifunc = get_slot_tp_function(space, typedef, slot_name) + if not slot_apifunc: + warn_missing_slot(space, method_name, slot_name, w_type) + continue + slot_func_helper = slot_apifunc.get_llhelper(space) + fill_slot(space, pto, w_type, slot_names, slot_func_helper) + @specialize.arg(3) def fill_slot(space, pto, w_type, slot_names, slot_func_helper): # XXX special case wrapper-functions and use a "specific" slot func @@ -788,7 +786,10 @@ if pto.c_tp_itemsize < pto.c_tp_base.c_tp_itemsize: pto.c_tp_itemsize = pto.c_tp_base.c_tp_itemsize - update_all_slots(space, w_type, pto) + if w_type.is_heaptype(): + update_all_slots(space, w_type, pto) + else: + update_all_slots_builtin(space, w_type, pto) if not pto.c_tp_new: base_object_pyo = make_ref(space, space.w_object) base_object_pto = rffi.cast(PyTypeObjectPtr, base_object_pyo) From pypy.commits at gmail.com Sun Oct 1 19:54:28 2017 From: pypy.commits at gmail.com (rlamy) Date: Sun, 01 Oct 2017 16:54:28 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: hg merge default Message-ID: <59d18034.e285df0a.c9190.3a1a@mx.google.com> Author: Ronan Lamy Branch: py3.5 Changeset: r92547:4978206b1665 Date: 2017-10-02 01:53 +0200 http://bitbucket.org/pypy/pypy/changeset/4978206b1665/ Log: hg merge default 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 @@ -768,7 +768,7 @@ def __init__(self, method_name, slot_name, function, wrapper1, wrapper2, doc): self.method_name = method_name self.slot_name = slot_name - self.slot_names = ("c_" + slot_name).split(".") + self.slot_names = tuple(("c_" + slot_name).split(".")) self.slot_func = function self.wrapper_func = wrapper1 self.wrapper_func_kwds = wrapper2 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 @@ -257,31 +257,19 @@ # overwrite slots that are already set: these ones are probably # coming from a parent C type. - if w_type.is_heaptype(): - typedef = None - search_dict_w = w_type.dict_w - else: - typedef = w_type.layout.typedef - search_dict_w = None - for method_name, slot_name, slot_names, slot_apifunc in slotdefs_for_tp_slots: slot_func_helper = None - if typedef is not None: - # built-in types: expose as many slots as possible, even - # if it happens to come from some parent class - slot_apifunc = get_slot_tp_function(space, typedef, slot_name) + w_descr = w_type.dict_w.get(method_name, None) + if w_descr: + # use the slot_apifunc (userslots) to lookup at runtime + pass + elif len(slot_names) ==1: + # 'inherit' from tp_base + slot_func_helper = getattr(pto.c_tp_base, slot_names[0]) else: - w_descr = search_dict_w.get(method_name, None) - if w_descr: - # use the slot_apifunc (userslots) to lookup at runtime - pass - elif len(slot_names) ==1: - # 'inherit' from tp_base - slot_func_helper = getattr(pto.c_tp_base, slot_names[0]) - else: - struct = getattr(pto.c_tp_base, slot_names[0]) - if struct: - slot_func_helper = getattr(struct, slot_names[1]) + struct = getattr(pto.c_tp_base, slot_names[0]) + if struct: + slot_func_helper = getattr(struct, slot_names[1]) if not slot_func_helper: if not slot_apifunc: @@ -290,6 +278,16 @@ slot_func_helper = slot_apifunc.get_llhelper(space) fill_slot(space, pto, w_type, slot_names, slot_func_helper) +def update_all_slots_builtin(space, w_type, pto): + typedef = w_type.layout.typedef + for method_name, slot_name, slot_names, slot_apifunc in slotdefs_for_tp_slots: + slot_apifunc = get_slot_tp_function(space, typedef, slot_name) + if not slot_apifunc: + warn_missing_slot(space, method_name, slot_name, w_type) + continue + slot_func_helper = slot_apifunc.get_llhelper(space) + fill_slot(space, pto, w_type, slot_names, slot_func_helper) + @specialize.arg(3) def fill_slot(space, pto, w_type, slot_names, slot_func_helper): # XXX special case wrapper-functions and use a "specific" slot func @@ -702,7 +700,10 @@ if pto.c_tp_itemsize < pto.c_tp_base.c_tp_itemsize: pto.c_tp_itemsize = pto.c_tp_base.c_tp_itemsize - update_all_slots(space, w_type, pto) + if w_type.is_heaptype(): + update_all_slots(space, w_type, pto) + else: + update_all_slots_builtin(space, w_type, pto) if not pto.c_tp_new: base_object_pyo = make_ref(space, space.w_object) base_object_pto = rffi.cast(PyTypeObjectPtr, base_object_pyo) From pypy.commits at gmail.com Mon Oct 2 05:03:23 2017 From: pypy.commits at gmail.com (rlamy) Date: Mon, 02 Oct 2017 02:03:23 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: Fix translation Message-ID: <59d200db.3296df0a.1313c.3319@mx.google.com> Author: Ronan Lamy Branch: py3.5 Changeset: r92548:527396233189 Date: 2017-10-02 11:02 +0200 http://bitbucket.org/pypy/pypy/changeset/527396233189/ Log: Fix translation diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -14,6 +14,7 @@ from rpython.rlib.rfile import (FILEP, c_fread, c_fclose, c_fwrite, c_fdopen, c_fileno, c_fopen)# for tests +from rpython.rlib import jit from rpython.translator import cdir from rpython.translator.tool.cbuild import ExternalCompilationInfo from rpython.translator.gensupp import NameManager @@ -1669,6 +1670,7 @@ state.fixup_extension(w_mod, name, path) return w_mod + at jit.dont_look_inside def exec_extension_module(space, w_mod): from pypy.module.cpyext.modsupport import exec_def if not space.config.objspace.usemodules.cpyext: From pypy.commits at gmail.com Mon Oct 2 06:34:36 2017 From: pypy.commits at gmail.com (arigo) Date: Mon, 02 Oct 2017 03:34:36 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-avoid-roundtrip: (antocuni, arigo) Message-ID: <59d2163c.508d1c0a.b996d.9c88@mx.google.com> Author: Armin Rigo Branch: cpyext-avoid-roundtrip Changeset: r92549:d519061c7cf0 Date: 2017-10-02 12:13 +0200 http://bitbucket.org/pypy/pypy/changeset/d519061c7cf0/ Log: (antocuni, arigo) Aaaaah. Found that this simple new test in test_ll2ctypes doesn't actually pass. Fix. diff --git a/rpython/rtyper/lltypesystem/ll2ctypes.py b/rpython/rtyper/lltypesystem/ll2ctypes.py --- a/rpython/rtyper/lltypesystem/ll2ctypes.py +++ b/rpython/rtyper/lltypesystem/ll2ctypes.py @@ -815,6 +815,10 @@ else: container = llobj._obj if isinstance(T.TO, lltype.FuncType): + if hasattr(llobj._obj0, '_real_integer_addr'): + ctypes_func_type = get_ctypes_type(T) + return ctypes.cast(llobj._obj0._real_integer_addr, + ctypes_func_type) # XXX a temporary workaround for comparison of lltype.FuncType key = llobj._obj.__dict__.copy() key['_TYPE'] = repr(key['_TYPE']) @@ -1039,7 +1043,8 @@ cobj = ctypes.cast(cobjkey, type(cobj)) _callable = get_ctypes_trampoline(T.TO, cobj) return lltype.functionptr(T.TO, name, - _callable=_callable) + _callable=_callable, + _real_integer_addr=cobjkey) elif isinstance(T.TO, lltype.OpaqueType): if T == llmemory.GCREF: container = _llgcopaque(cobj) diff --git a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py --- a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py +++ b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py @@ -1443,6 +1443,12 @@ assert seen[0]() == 42 assert seen[1]() == 43 + def test_keep_value_across_lltype_callable(self): + PtrF = lltype.Ptr(lltype.FuncType([], lltype.Void)) + f = rffi.cast(PtrF, 42) + assert lltype.typeOf(f) == PtrF + assert rffi.cast(lltype.Signed, f) == 42 + class TestPlatform(object): def test_lib_on_libpaths(self): From pypy.commits at gmail.com Mon Oct 2 06:34:38 2017 From: pypy.commits at gmail.com (arigo) Date: Mon, 02 Oct 2017 03:34:38 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-avoid-roundtrip: (antocuni, arigo) Message-ID: <59d2163e.11b51c0a.860ae.efe7@mx.google.com> Author: Armin Rigo Branch: cpyext-avoid-roundtrip Changeset: r92550:087ef45836c2 Date: 2017-10-02 12:14 +0200 http://bitbucket.org/pypy/pypy/changeset/087ef45836c2/ Log: (antocuni, arigo) Move subtype_dealloc to C. Lots of messes that might not be necessary any more thanks to d519061c7cf0. diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -1038,7 +1038,7 @@ return wrapper_second_level -def setup_init_functions(eci, prefix, space): +def setup_init_functions(eci, prefix): # jump through hoops to avoid releasing the GIL during initialization # of the cpyext module. The C functions are called with no wrapper, # but must not do anything like calling back PyType_Ready(). We @@ -1072,6 +1072,8 @@ _reinit_tls() add_fork_hook('child', reinit_tls) + +def attach_c_functions(space, eci): state = space.fromcache(State) state.C._Py_Dealloc = rffi.llexternal('_Py_Dealloc', [PyObject], lltype.Void, @@ -1080,6 +1082,10 @@ _, state.C.set_marker = rffi.CExternVariable( Py_ssize_t, '_pypy_rawrefcount_w_marker_deallocating', eci, _nowrapper=True, c_type='Py_ssize_t') + # XXX meeeeeeeeessssss mmmmmmmmmmmmmmmeeeeeeeeeeeeeeeeeeeeeessssssssssss + state.C._PyPy_get_subtype_dealloc = rffi.llexternal( + '_PyPy_get_subtype_dealloc', [], rffi.VOIDP, + compilation_info=eci, _nowrapper=True) def init_function(func): @@ -1150,6 +1156,7 @@ space.fromcache(State).install_dll(eci) modulename = py.path.local(eci.libraries[-1]) + attach_c_functions(space, eci) run_bootstrap_functions(space) # load the bridge, and init structure @@ -1201,7 +1208,7 @@ ll2ctypes.lltype2ctypes(func.get_llhelper(space)), ctypes.c_void_p) - setup_init_functions(eci, prefix, space) + setup_init_functions(eci, prefix) return modulename.new(ext='') def attach_recursively(space, static_pyobjs, static_objs_w, attached_objs, i): @@ -1394,6 +1401,7 @@ source_dir / "missing.c", source_dir / "pymem.c", source_dir / "object.c", + source_dir / "typeobject.c", ] def build_eci(code, use_micronumpy=False, translating=False): @@ -1488,6 +1496,7 @@ eci = build_eci(code, use_micronumpy, translating=True) space.fromcache(State).install_dll(eci) + attach_c_functions(space, eci) run_bootstrap_functions(space) # emit uninitialized static data @@ -1532,7 +1541,7 @@ relax=True) deco(func.get_wrapper(space)) - setup_init_functions(eci, prefix, space) + setup_init_functions(eci, prefix) trunk_include = pypydir.dirpath() / 'include' copy_header_files(cts, trunk_include, use_micronumpy) 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 @@ -335,6 +335,7 @@ PyAPI_FUNC(int) PyPyType_Register(PyTypeObject *); #define PyObject_Length PyObject_Size #define _PyObject_GC_Del PyObject_GC_Del +PyAPI_FUNC(void *) _PyPy_get_subtype_dealloc(void); #ifdef __cplusplus diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -7,7 +7,7 @@ from pypy.module.cpyext.api import ( cpython_api, bootstrap_function, PyObject, PyObjectP, ADDR, CANNOT_FAIL, Py_TPFLAGS_HEAPTYPE, PyTypeObjectPtr, is_PyObject, - PyVarObject, Py_ssize_t, init_function) + PyVarObject, Py_ssize_t, init_function, cts) from pypy.module.cpyext.state import State from pypy.objspace.std.typeobject import W_TypeObject from pypy.objspace.std.objectobject import W_ObjectObject @@ -27,8 +27,8 @@ W_BaseObject = W_ObjectObject def get_dealloc(self, space): - from pypy.module.cpyext.typeobject import subtype_dealloc - return subtype_dealloc.api_func.get_llhelper(space) + state = space.fromcache(State) + return cts.cast('destructor', state.C._PyPy_get_subtype_dealloc()) def allocate(self, space, w_type, itemcount=0, immortal=False): # typically called from PyType_GenericAlloc via typedescr.allocate diff --git a/pypy/module/cpyext/src/typeobject.c b/pypy/module/cpyext/src/typeobject.c new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/src/typeobject.c @@ -0,0 +1,35 @@ +#include "Python.h" + +static void subtype_dealloc(PyObject *obj) +{ + PyTypeObject *pto = obj->ob_type; + PyTypeObject *base = pto; + /* This wrapper is created on a specific type, call it w_A. + We wish to call the dealloc function from one of the base classes of w_A, + the first of which is not this function itself. + w_obj is an instance of w_A or one of its subclasses. So climb up the + inheritance chain until base.c_tp_dealloc is exactly this_func, and then + continue on up until they differ. + */ + while (base->tp_dealloc != &subtype_dealloc) + { + base = base->tp_base; + assert(base); + } + while (base->tp_dealloc == &subtype_dealloc) + { + base = base->tp_base; + assert(base); + } + /* XXX call tp_del if necessary */ + base->tp_dealloc(obj); + /* XXX cpy decrefs the pto here but we do it in the base-dealloc + hopefully this does not clash with the memory model assumed in + extension modules */ +} + +PyAPI_FUNC(void *) +_PyPy_get_subtype_dealloc(void) +{ + return &subtype_dealloc; +} 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 @@ -529,36 +529,6 @@ realize=type_realize, dealloc=type_dealloc) - at slot_function([PyObject], lltype.Void) -def subtype_dealloc(space, obj): - pto = obj.c_ob_type - base = pto - this_func_ptr = llslot(space, subtype_dealloc) - w_obj = from_ref(space, rffi.cast(PyObject, base)) - # This wrapper is created on a specific type, call it w_A. - # We wish to call the dealloc function from one of the base classes of w_A, - # the first of which is not this function itself. - # w_obj is an instance of w_A or one of its subclasses. So climb up the - # inheritance chain until base.c_tp_dealloc is exactly this_func, and then - # continue on up until they differ. - #print 'subtype_dealloc, start from', rffi.charp2str(base.c_tp_name) - while base.c_tp_dealloc != this_func_ptr: - base = base.c_tp_base - assert base - #print ' ne move to', rffi.charp2str(base.c_tp_name) - w_obj = from_ref(space, rffi.cast(PyObject, base)) - while base.c_tp_dealloc == this_func_ptr: - base = base.c_tp_base - assert base - #print ' eq move to', rffi.charp2str(base.c_tp_name) - w_obj = from_ref(space, rffi.cast(PyObject, base)) - #print ' end with', rffi.charp2str(base.c_tp_name) - dealloc = base.c_tp_dealloc - # XXX call tp_del if necessary - generic_cpy_call(space, dealloc, obj) - # XXX cpy decrefs the pto here but we do it in the base-dealloc - # hopefully this does not clash with the memory model assumed in - # extension modules @slot_function([PyObject, Py_ssize_tP], lltype.Signed, error=CANNOT_FAIL) def bf_segcount(space, w_obj, ref): @@ -764,7 +734,9 @@ pto.c_tp_dealloc = pto.c_tp_base.c_tp_dealloc if not pto.c_tp_dealloc: # strange, but happens (ABCMeta) - pto.c_tp_dealloc = llslot(space, subtype_dealloc) + state = space.fromcache(State) + d = cts.cast('destructor', state.C._PyPy_get_subtype_dealloc()) + pto.c_tp_dealloc = d if builder.cpyext_type_init is not None: builder.cpyext_type_init.append((pto, w_type)) From pypy.commits at gmail.com Mon Oct 2 06:34:43 2017 From: pypy.commits at gmail.com (arigo) Date: Mon, 02 Oct 2017 03:34:43 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-avoid-roundtrip: (antocuni, arigo) Message-ID: <59d21643.480f1c0a.464f7.bc25@mx.google.com> Author: Armin Rigo Branch: cpyext-avoid-roundtrip Changeset: r92551:5593c8b50dc4 Date: 2017-10-02 12:33 +0200 http://bitbucket.org/pypy/pypy/changeset/5593c8b50dc4/ Log: (antocuni, arigo) More. diff --git a/rpython/rtyper/lltypesystem/ll2ctypes.py b/rpython/rtyper/lltypesystem/ll2ctypes.py --- a/rpython/rtyper/lltypesystem/ll2ctypes.py +++ b/rpython/rtyper/lltypesystem/ll2ctypes.py @@ -817,7 +817,7 @@ if isinstance(T.TO, lltype.FuncType): if hasattr(llobj._obj0, '_real_integer_addr'): ctypes_func_type = get_ctypes_type(T) - return ctypes.cast(llobj._obj0._real_integer_addr, + return ctypes.cast(llobj._obj0._real_integer_addr(), ctypes_func_type) # XXX a temporary workaround for comparison of lltype.FuncType key = llobj._obj.__dict__.copy() @@ -1044,7 +1044,7 @@ _callable = get_ctypes_trampoline(T.TO, cobj) return lltype.functionptr(T.TO, name, _callable=_callable, - _real_integer_addr=cobjkey) + _real_integer_addr=lambda: cobjkey) elif isinstance(T.TO, lltype.OpaqueType): if T == llmemory.GCREF: container = _llgcopaque(cobj) @@ -1302,6 +1302,10 @@ # perform the call return self.trampoline(*argvalues) + def get_real_address(self): + cfunc = get_ctypes_callable(self.funcptr, self.calling_conv) + return ctypes.cast(cfunc, ctypes.c_void_p).value + def get_ctypes_trampoline(FUNCTYPE, cfunc): RESULT = FUNCTYPE.RESULT container_arguments = [] diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py --- a/rpython/rtyper/lltypesystem/rffi.py +++ b/rpython/rtyper/lltypesystem/rffi.py @@ -145,6 +145,9 @@ # Also, _nowrapper functions cannot release the GIL, by default. invoke_around_handlers = not sandboxsafe and not _nowrapper + if _nowrapper and isinstance(_callable, ll2ctypes.LL2CtypesCallable): + kwds['_real_integer_addr'] = _callable.get_real_address + if random_effects_on_gcobjs not in (False, True): random_effects_on_gcobjs = ( invoke_around_handlers or # because it can release the GIL diff --git a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py --- a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py +++ b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py @@ -1449,6 +1449,25 @@ assert lltype.typeOf(f) == PtrF assert rffi.cast(lltype.Signed, f) == 42 + def test_keep_value_across_rffi_llexternal(self): + c_source = py.code.Source(r""" + void ff1(void) { } + void *get_ff1(void) { return &ff1; } + """) + eci = ExternalCompilationInfo( + separate_module_sources=[c_source], + post_include_bits = [ + "RPY_EXTERN void ff1(void); RPY_EXTERN void *get_ff1(void);"]) + PtrFF1 = lltype.Ptr(lltype.FuncType([], lltype.Void)) + f1 = rffi.llexternal('ff1', [], lltype.Void, compilation_info=eci, + _nowrapper=True) + assert lltype.typeOf(f1) == PtrFF1 + getff1 = rffi.llexternal('get_ff1', [], PtrFF1, compilation_info=eci, + _nowrapper=True) + f2 = getff1() + assert rffi.cast(lltype.Signed, f2) == rffi.cast(lltype.Signed, f1) + #assert f2 == f1 -- fails, would be nice but oh well + class TestPlatform(object): def test_lib_on_libpaths(self): From pypy.commits at gmail.com Mon Oct 2 06:34:45 2017 From: pypy.commits at gmail.com (arigo) Date: Mon, 02 Oct 2017 03:34:45 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-avoid-roundtrip: (antocuni, arigo) Message-ID: <59d21645.05cf1c0a.913bb.4698@mx.google.com> Author: Armin Rigo Branch: cpyext-avoid-roundtrip Changeset: r92552:05c413f37f2c Date: 2017-10-02 12:34 +0200 http://bitbucket.org/pypy/pypy/changeset/05c413f37f2c/ Log: (antocuni, arigo) Simplification again, using the fixes in ll2ctypes. diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -1082,9 +1082,8 @@ _, state.C.set_marker = rffi.CExternVariable( Py_ssize_t, '_pypy_rawrefcount_w_marker_deallocating', eci, _nowrapper=True, c_type='Py_ssize_t') - # XXX meeeeeeeeessssss mmmmmmmmmmmmmmmeeeeeeeeeeeeeeeeeeeeeessssssssssss - state.C._PyPy_get_subtype_dealloc = rffi.llexternal( - '_PyPy_get_subtype_dealloc', [], rffi.VOIDP, + state.C._PyPy_subtype_dealloc = rffi.llexternal( + '_PyPy_subtype_dealloc', [PyObject], lltype.Void, compilation_info=eci, _nowrapper=True) 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 @@ -335,7 +335,7 @@ PyAPI_FUNC(int) PyPyType_Register(PyTypeObject *); #define PyObject_Length PyObject_Size #define _PyObject_GC_Del PyObject_GC_Del -PyAPI_FUNC(void *) _PyPy_get_subtype_dealloc(void); +PyAPI_FUNC(void) _PyPy_subtype_dealloc(PyObject *); #ifdef __cplusplus diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -28,7 +28,7 @@ def get_dealloc(self, space): state = space.fromcache(State) - return cts.cast('destructor', state.C._PyPy_get_subtype_dealloc()) + return state.C._PyPy_subtype_dealloc def allocate(self, space, w_type, itemcount=0, immortal=False): # typically called from PyType_GenericAlloc via typedescr.allocate diff --git a/pypy/module/cpyext/src/typeobject.c b/pypy/module/cpyext/src/typeobject.c --- a/pypy/module/cpyext/src/typeobject.c +++ b/pypy/module/cpyext/src/typeobject.c @@ -1,6 +1,7 @@ #include "Python.h" -static void subtype_dealloc(PyObject *obj) +void +_PyPy_subtype_dealloc(PyObject *obj) { PyTypeObject *pto = obj->ob_type; PyTypeObject *base = pto; @@ -11,12 +12,12 @@ inheritance chain until base.c_tp_dealloc is exactly this_func, and then continue on up until they differ. */ - while (base->tp_dealloc != &subtype_dealloc) + while (base->tp_dealloc != &_PyPy_subtype_dealloc) { base = base->tp_base; assert(base); } - while (base->tp_dealloc == &subtype_dealloc) + while (base->tp_dealloc == &_PyPy_subtype_dealloc) { base = base->tp_base; assert(base); @@ -27,9 +28,3 @@ hopefully this does not clash with the memory model assumed in extension modules */ } - -PyAPI_FUNC(void *) -_PyPy_get_subtype_dealloc(void) -{ - return &subtype_dealloc; -} 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 @@ -735,8 +735,7 @@ if not pto.c_tp_dealloc: # strange, but happens (ABCMeta) state = space.fromcache(State) - d = cts.cast('destructor', state.C._PyPy_get_subtype_dealloc()) - pto.c_tp_dealloc = d + pto.c_tp_dealloc = state.C._PyPy_subtype_dealloc if builder.cpyext_type_init is not None: builder.cpyext_type_init.append((pto, w_type)) From pypy.commits at gmail.com Mon Oct 2 06:43:08 2017 From: pypy.commits at gmail.com (arigo) Date: Mon, 02 Oct 2017 03:43:08 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-avoid-roundtrip: Kill PyObject_dealloc() Message-ID: <59d2183c.508d1c0a.b996d.9f84@mx.google.com> Author: Armin Rigo Branch: cpyext-avoid-roundtrip Changeset: r92553:d9618818e9a5 Date: 2017-10-02 12:42 +0200 http://bitbucket.org/pypy/pypy/changeset/d9618818e9a5/ Log: Kill PyObject_dealloc() diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -1085,6 +1085,9 @@ state.C._PyPy_subtype_dealloc = rffi.llexternal( '_PyPy_subtype_dealloc', [PyObject], lltype.Void, compilation_info=eci, _nowrapper=True) + state.C._PyPy_object_dealloc = rffi.llexternal( + '_PyPy_object_dealloc', [PyObject], lltype.Void, + compilation_info=eci, _nowrapper=True) def init_function(func): 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 @@ -336,6 +336,7 @@ #define PyObject_Length PyObject_Size #define _PyObject_GC_Del PyObject_GC_Del PyAPI_FUNC(void) _PyPy_subtype_dealloc(PyObject *); +PyAPI_FUNC(void) _PyPy_object_dealloc(PyObject *); #ifdef __cplusplus diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py --- a/pypy/module/cpyext/object.py +++ b/pypy/module/cpyext/object.py @@ -54,10 +54,6 @@ w_obj = PyObject_InitVar(space, py_objvar, type, itemcount) return py_obj - at slot_function([PyObject], lltype.Void) -def PyObject_dealloc(space, obj): - return _dealloc(space, obj) - def _dealloc(space, obj): # This frees an object after its refcount dropped to zero, so we # assert that it is really zero here. diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -106,9 +106,12 @@ def allocate(self, space, w_type, itemcount=0, immortal=False): return tp_alloc(space, w_type, itemcount) - if tp_dealloc: + if hasattr(tp_dealloc, 'api_func'): def get_dealloc(self, space): return tp_dealloc.api_func.get_llhelper(space) + elif tp_dealloc: + def get_dealloc(self, space): + return tp_dealloc if tp_attach: def attach(self, space, pyobj, w_obj, w_userdata=None): @@ -124,10 +127,10 @@ @bootstrap_function def init_pyobject(space): - from pypy.module.cpyext.object import PyObject_dealloc # typedescr for the 'object' type + state = space.fromcache(State) make_typedescr(space.w_object.layout.typedef, - dealloc=PyObject_dealloc) + dealloc=state.C._PyPy_object_dealloc) # almost all types, which should better inherit from object. make_typedescr(None) diff --git a/pypy/module/cpyext/src/object.c b/pypy/module/cpyext/src/object.c --- a/pypy/module/cpyext/src/object.c +++ b/pypy/module/cpyext/src/object.c @@ -16,10 +16,22 @@ Py_ssize_t _pypy_rawrefcount_w_marker_deallocating; /* set from pyobject.py */ -void _Py_Dealloc(PyObject *obj) +void +_Py_Dealloc(PyObject *obj) { PyTypeObject *pto = obj->ob_type; /* this is the same as rawrefcount.mark_deallocating() */ obj->ob_pypy_link = _pypy_rawrefcount_w_marker_deallocating; pto->tp_dealloc(obj); } + +void +_PyPy_object_dealloc(PyObject *obj) +{ + PyTypeObject *pto; + assert(obj->ob_refcnt == 0); + pto = obj->ob_type; + pto->tp_free(obj); + if (pto->tp_flags & Py_TPFLAGS_HEAPTYPE) + Py_DECREF(pto); +} From pypy.commits at gmail.com Mon Oct 2 07:36:21 2017 From: pypy.commits at gmail.com (rlamy) Date: Mon, 02 Oct 2017 04:36:21 -0700 (PDT) Subject: [pypy-commit] pypy refactor-slots: hg merge default Message-ID: <59d224b5.c9331c0a.1d716.3584@mx.google.com> Author: Ronan Lamy Branch: refactor-slots Changeset: r92554:9876c223e75f Date: 2017-10-02 11:44 +0200 http://bitbucket.org/pypy/pypy/changeset/9876c223e75f/ Log: hg merge default 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 @@ -776,7 +776,7 @@ def __init__(self, method_name, slot_name, function, wrapper1, wrapper2, doc): self.method_name = method_name self.slot_name = slot_name - self.slot_names = ("c_" + slot_name).split(".") + self.slot_names = tuple(("c_" + slot_name).split(".")) self.slot_func = function self.wrapper_func = wrapper1 self.wrapper_func_kwds = wrapper2 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 @@ -548,25 +548,18 @@ pto = obj.c_ob_type base = pto this_func_ptr = llslot(space, subtype_dealloc) - w_obj = from_ref(space, rffi.cast(PyObject, base)) # This wrapper is created on a specific type, call it w_A. # We wish to call the dealloc function from one of the base classes of w_A, # the first of which is not this function itself. # w_obj is an instance of w_A or one of its subclasses. So climb up the # inheritance chain until base.c_tp_dealloc is exactly this_func, and then # continue on up until they differ. - #print 'subtype_dealloc, start from', rffi.charp2str(base.c_tp_name) while base.c_tp_dealloc != this_func_ptr: base = base.c_tp_base assert base - #print ' ne move to', rffi.charp2str(base.c_tp_name) - w_obj = from_ref(space, rffi.cast(PyObject, base)) while base.c_tp_dealloc == this_func_ptr: base = base.c_tp_base assert base - #print ' eq move to', rffi.charp2str(base.c_tp_name) - w_obj = from_ref(space, rffi.cast(PyObject, base)) - #print ' end with', rffi.charp2str(base.c_tp_name) dealloc = base.c_tp_dealloc # XXX call tp_del if necessary generic_cpy_call(space, dealloc, obj) From pypy.commits at gmail.com Mon Oct 2 07:36:22 2017 From: pypy.commits at gmail.com (rlamy) Date: Mon, 02 Oct 2017 04:36:22 -0700 (PDT) Subject: [pypy-commit] pypy refactor-slots: Experiment with extracting slot factories from build_slot_tp_function() Message-ID: <59d224b6.919bdf0a.2ce89.11f8@mx.google.com> Author: Ronan Lamy Branch: refactor-slots Changeset: r92555:01b1c0cb2cb6 Date: 2017-10-02 13:07 +0200 http://bitbucket.org/pypy/pypy/changeset/01b1c0cb2cb6/ Log: Experiment with extracting slot factories from build_slot_tp_function() 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 @@ -427,8 +427,6 @@ return space.newint(generic_cpy_call(space, func_target, w_self, w_other)) -from rpython.rlib.nonconst import NonConstant - def build_slot_tp_function(space, typedef, name): w_type = space.gettypeobject(typedef) @@ -678,26 +676,7 @@ return space.call_function(get_fn, w_self, w_obj, w_value) slot_func = slot_tp_descr_get elif name == 'tp_descr_set': - set_fn = w_type.lookup('__set__') - delete_fn = w_type.lookup('__delete__') - if set_fn is None and delete_fn is None: - return - - @slot_function([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) - @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) - def slot_tp_descr_set(space, w_self, w_obj, w_value): - if w_value is not None: - if set_fn is None: - raise oefmt(space.w_TypeError, - "%s object has no __set__", typedef.name) - space.call_function(set_fn, w_self, w_obj, w_value) - else: - if delete_fn is None: - raise oefmt(space.w_TypeError, - "%s object has no __delete__", typedef.name) - space.call_function(delete_fn, w_self, w_obj) - return 0 - slot_func = slot_tp_descr_set + return make_tp_descr_set(space, typedef) else: # missing: tp_as_number.nb_nonzero, tp_as_number.nb_coerce # tp_as_sequence.c_sq_contains, tp_as_sequence.c_sq_length @@ -706,6 +685,29 @@ return slot_func +def make_tp_descr_set(space, typedef): + w_type = space.gettypeobject(typedef) + name = 'descr_set' + set_fn = w_type.lookup('__set__') + delete_fn = w_type.lookup('__delete__') + if set_fn is None and delete_fn is None: + return + + @slot_function([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_tp_descr_set(space, w_self, w_obj, w_value): + if w_value is not None: + if set_fn is None: + raise oefmt(space.w_TypeError, + "%s object has no __set__", typedef.name) + space.call_function(set_fn, w_self, w_obj, w_value) + else: + if delete_fn is None: + raise oefmt(space.w_TypeError, + "%s object has no __delete__", typedef.name) + space.call_function(delete_fn, w_self, w_obj) + return 0 + return slot_tp_descr_set def slot_from___buffer__(space, typedef, buff_fn): name = 'bf_getbuffer' From pypy.commits at gmail.com Mon Oct 2 07:36:24 2017 From: pypy.commits at gmail.com (rlamy) Date: Mon, 02 Oct 2017 04:36:24 -0700 (PDT) Subject: [pypy-commit] pypy refactor-slots: Add @slot_factory decorator Message-ID: <59d224b8.46a3df0a.fe11.9e36@mx.google.com> Author: Ronan Lamy Branch: refactor-slots Changeset: r92556:901c1e4af3a0 Date: 2017-10-02 13:35 +0200 http://bitbucket.org/pypy/pypy/changeset/901c1e4af3a0/ Log: Add @slot_factory decorator 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 @@ -427,6 +427,14 @@ return space.newint(generic_cpy_call(space, func_target, w_self, w_other)) +SLOT_FACTORIES = {} +def slot_factory(tp_name): + def decorate(func): + SLOT_FACTORIES[tp_name] = func + return func + return decorate + + def build_slot_tp_function(space, typedef, name): w_type = space.gettypeobject(typedef) @@ -675,8 +683,8 @@ w_obj = space.w_None return space.call_function(get_fn, w_self, w_obj, w_value) slot_func = slot_tp_descr_get - elif name == 'tp_descr_set': - return make_tp_descr_set(space, typedef) + elif name in SLOT_FACTORIES: + return SLOT_FACTORIES[name](space, typedef) else: # missing: tp_as_number.nb_nonzero, tp_as_number.nb_coerce # tp_as_sequence.c_sq_contains, tp_as_sequence.c_sq_length @@ -685,6 +693,7 @@ return slot_func + at slot_factory('tp_descr_set') def make_tp_descr_set(space, typedef): w_type = space.gettypeobject(typedef) name = 'descr_set' @@ -709,6 +718,7 @@ return 0 return slot_tp_descr_set + def slot_from___buffer__(space, typedef, buff_fn): name = 'bf_getbuffer' @slot_function([PyObject, Py_bufferP, rffi.INT_real], From pypy.commits at gmail.com Mon Oct 2 09:36:51 2017 From: pypy.commits at gmail.com (antocuni) Date: Mon, 02 Oct 2017 06:36:51 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-avoid-roundtrip: (antocuni, arigo): move these two api functions to C Message-ID: <59d240f3.0bb7df0a.667c7.60ce@mx.google.com> Author: Antonio Cuni Branch: cpyext-avoid-roundtrip Changeset: r92557:63bf91dc13e5 Date: 2017-10-02 15:36 +0200 http://bitbucket.org/pypy/pypy/changeset/63bf91dc13e5/ Log: (antocuni, arigo): move these two api functions to C diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -597,7 +597,7 @@ 'Py_DivisionWarningFlag', 'Py_DontWriteBytecodeFlag', 'Py_NoUserSiteDirectory', '_Py_QnewFlag', 'Py_Py3kWarningFlag', 'Py_HashRandomizationFlag', '_Py_PackageContext', '_PyTraceMalloc_Track', '_PyTraceMalloc_Untrack', 'PyMem_Malloc', - 'Py_IncRef', 'Py_DecRef', + 'Py_IncRef', 'Py_DecRef', 'PyObject_Free', 'PyObject_GC_Del', ] TYPES = {} FORWARD_DECLS = [] @@ -1073,12 +1073,17 @@ add_fork_hook('child', reinit_tls) -def attach_c_functions(space, eci): +def attach_c_functions(space, eci, prefix): state = space.fromcache(State) state.C._Py_Dealloc = rffi.llexternal('_Py_Dealloc', [PyObject], lltype.Void, compilation_info=eci, _nowrapper=True) + state.C.PyObject_Free = rffi.llexternal( + mangle_name(prefix, 'PyObject_Free'), + [rffi.VOIDP], lltype.Void, + compilation_info=eci, + _nowrapper=True) _, state.C.set_marker = rffi.CExternVariable( Py_ssize_t, '_pypy_rawrefcount_w_marker_deallocating', eci, _nowrapper=True, c_type='Py_ssize_t') @@ -1158,7 +1163,7 @@ space.fromcache(State).install_dll(eci) modulename = py.path.local(eci.libraries[-1]) - attach_c_functions(space, eci) + attach_c_functions(space, eci, prefix) run_bootstrap_functions(space) # load the bridge, and init structure @@ -1498,7 +1503,7 @@ eci = build_eci(code, use_micronumpy, translating=True) space.fromcache(State).install_dll(eci) - attach_c_functions(space, eci) + attach_c_functions(space, eci, prefix) run_bootstrap_functions(space) # emit uninitialized static data 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 @@ -323,6 +323,11 @@ PyAPI_FUNC(int) PyObject_AsWriteBuffer(PyObject *, void **, Py_ssize_t *); PyAPI_FUNC(int) PyObject_CheckReadBuffer(PyObject *); +/* on CPython, these are in objimpl.h */ + +PyAPI_FUNC(void) PyObject_Free(void *); +PyAPI_FUNC(void) PyObject_GC_Del(void *); + #define PyObject_MALLOC PyObject_Malloc #define PyObject_REALLOC PyObject_Realloc #define PyObject_FREE PyObject_Free @@ -330,7 +335,6 @@ #define PyObject_DEL PyObject_Free - /* PyPy internal ----------------------------------- */ PyAPI_FUNC(int) PyPyType_Register(PyTypeObject *); #define PyObject_Length PyObject_Size diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py --- a/pypy/module/cpyext/object.py +++ b/pypy/module/cpyext/object.py @@ -32,10 +32,6 @@ # XXX FIXME return realloc(ptr, size) - at cpython_api([rffi.VOIDP], lltype.Void) -def PyObject_Free(space, ptr): - lltype.free(ptr, flavor='raw') - @cpython_api([PyTypeObjectPtr], PyObject, result_is_ll=True) def _PyObject_New(space, type): return _PyObject_NewVar(space, type, 0) @@ -68,10 +64,6 @@ def _PyObject_GC_New(space, type): return _PyObject_New(space, type) - at cpython_api([rffi.VOIDP], lltype.Void) -def PyObject_GC_Del(space, obj): - PyObject_Free(space, obj) - @cpython_api([PyObject], PyObjectP, error=CANNOT_FAIL) def _PyObject_GetDictPtr(space, op): return lltype.nullptr(PyObjectP.TO) diff --git a/pypy/module/cpyext/src/object.c b/pypy/module/cpyext/src/object.c --- a/pypy/module/cpyext/src/object.c +++ b/pypy/module/cpyext/src/object.c @@ -35,3 +35,15 @@ if (pto->tp_flags & Py_TPFLAGS_HEAPTYPE) Py_DECREF(pto); } + +void +PyObject_Free(void *obj) +{ + free(obj); +} + +void +PyObject_GC_Del(void *obj) +{ + free(obj); +} 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 @@ -686,8 +686,6 @@ """ Fills a newly allocated PyTypeObject from an existing type. """ - from pypy.module.cpyext.object import PyObject_Free - assert isinstance(w_type, W_TypeObject) pto = rffi.cast(PyTypeObjectPtr, py_obj) @@ -701,9 +699,10 @@ # buffer protocol setup_buffer_procs(space, w_type, pto) - pto.c_tp_free = llslot(space, PyObject_Free) + state = space.fromcache(State) + pto.c_tp_free = state.C.PyObject_Free pto.c_tp_alloc = llslot(space, PyType_GenericAlloc) - builder = space.fromcache(State).builder + builder = state.builder if ((pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE) != 0 and builder.cpyext_type_init is None): # this ^^^ is not None only during startup of cpyext. At that @@ -734,7 +733,6 @@ pto.c_tp_dealloc = pto.c_tp_base.c_tp_dealloc if not pto.c_tp_dealloc: # strange, but happens (ABCMeta) - state = space.fromcache(State) pto.c_tp_dealloc = state.C._PyPy_subtype_dealloc if builder.cpyext_type_init is not None: From pypy.commits at gmail.com Mon Oct 2 10:32:18 2017 From: pypy.commits at gmail.com (antocuni) Date: Mon, 02 Oct 2017 07:32:18 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-avoid-roundtrip: (antocuni, arigo): port more functions to C, and mark some others which we would like to port Message-ID: <59d24df2.04061c0a.fba0a.b401@mx.google.com> Author: Antonio Cuni Branch: cpyext-avoid-roundtrip Changeset: r92558:98f6749ae4ed Date: 2017-10-02 16:31 +0200 http://bitbucket.org/pypy/pypy/changeset/98f6749ae4ed/ Log: (antocuni, arigo): port more functions to C, and mark some others which we would like to port diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -597,7 +597,7 @@ 'Py_DivisionWarningFlag', 'Py_DontWriteBytecodeFlag', 'Py_NoUserSiteDirectory', '_Py_QnewFlag', 'Py_Py3kWarningFlag', 'Py_HashRandomizationFlag', '_Py_PackageContext', '_PyTraceMalloc_Track', '_PyTraceMalloc_Untrack', 'PyMem_Malloc', - 'Py_IncRef', 'Py_DecRef', 'PyObject_Free', 'PyObject_GC_Del', + 'Py_IncRef', 'Py_DecRef', 'PyObject_Free', 'PyObject_GC_Del', 'PyType_GenericAlloc', ] TYPES = {} FORWARD_DECLS = [] @@ -1084,6 +1084,11 @@ [rffi.VOIDP], lltype.Void, compilation_info=eci, _nowrapper=True) + state.C.PyType_GenericAlloc = rffi.llexternal( + mangle_name(prefix, 'PyType_GenericAlloc'), + [PyTypeObjectPtr, Py_ssize_t], PyObject, + compilation_info=eci, + _nowrapper=True) _, state.C.set_marker = rffi.CExternVariable( Py_ssize_t, '_pypy_rawrefcount_w_marker_deallocating', eci, _nowrapper=True, c_type='Py_ssize_t') 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 @@ -262,8 +262,16 @@ ) & ~(SIZEOF_VOID_P - 1) \ ) -#define PyObject_INIT PyObject_Init -#define PyObject_INIT_VAR PyObject_InitVar + +#define PyObject_INIT(op, typeobj) \ + ( Py_TYPE(op) = (typeobj), ((PyObject *)(op))->ob_refcnt = 1,\ + ((PyObject *)(op))->ob_pypy_link = 0, (op) ) +#define PyObject_INIT_VAR(op, typeobj, size) \ + ( Py_SIZE(op) = (size), PyObject_INIT((op), (typeobj)) ) + + +PyAPI_FUNC(PyObject *) PyType_GenericAlloc(PyTypeObject *, Py_ssize_t); + /* #define PyObject_NEW(type, typeobj) \ ( (type *) PyObject_Init( \ diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py --- a/pypy/module/cpyext/object.py +++ b/pypy/module/cpyext/object.py @@ -36,6 +36,7 @@ def _PyObject_New(space, type): return _PyObject_NewVar(space, type, 0) +# CCC port to C @cpython_api([PyTypeObjectPtr, Py_ssize_t], PyObject, result_is_ll=True) def _PyObject_NewVar(space, type, itemcount): w_type = from_ref(space, rffi.cast(PyObject, type)) diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -30,6 +30,7 @@ state = space.fromcache(State) return state.C._PyPy_subtype_dealloc + # CCC port to C def allocate(self, space, w_type, itemcount=0, immortal=False): # typically called from PyType_GenericAlloc via typedescr.allocate # this returns a PyObject with ob_refcnt == 1. diff --git a/pypy/module/cpyext/src/object.c b/pypy/module/cpyext/src/object.c --- a/pypy/module/cpyext/src/object.c +++ b/pypy/module/cpyext/src/object.c @@ -47,3 +47,9 @@ { free(obj); } + +PyObject * +PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems) +{ + return _PyObject_NewVar(type, nitems); +} 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 @@ -657,6 +657,7 @@ _dealloc(space, obj) +# CCC port it to C def type_alloc(space, w_metatype, itemsize=0): metatype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_metatype)) # Don't increase refcount for non-heaptypes @@ -701,7 +702,7 @@ state = space.fromcache(State) pto.c_tp_free = state.C.PyObject_Free - pto.c_tp_alloc = llslot(space, PyType_GenericAlloc) + pto.c_tp_alloc = state.C.PyType_GenericAlloc builder = state.builder if ((pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE) != 0 and builder.cpyext_type_init is None): @@ -935,11 +936,6 @@ w_type2 = from_ref(space, rffi.cast(PyObject, b)) return int(abstract_issubclass_w(space, w_type1, w_type2)) #XXX correct? - at cpython_api([PyTypeObjectPtr, Py_ssize_t], PyObject, result_is_ll=True) -def PyType_GenericAlloc(space, type, nitems): - from pypy.module.cpyext.object import _PyObject_NewVar - return _PyObject_NewVar(space, type, nitems) - @cpython_api([PyTypeObjectPtr, PyObject, PyObject], PyObject) def PyType_GenericNew(space, type, w_args, w_kwds): return generic_cpy_call( From pypy.commits at gmail.com Mon Oct 2 13:02:57 2017 From: pypy.commits at gmail.com (rlamy) Date: Mon, 02 Oct 2017 10:02:57 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: Add a way to remove all wrappers around cpyext functions Message-ID: <59d27141.90d71c0a.183bb.83b5@mx.google.com> Author: Ronan Lamy Branch: cpyext-nowrapper Changeset: r92559:ef509cae3c70 Date: 2017-10-02 19:02 +0200 http://bitbucket.org/pypy/pypy/changeset/ef509cae3c70/ Log: Add a way to remove all wrappers around cpyext functions diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -258,7 +258,8 @@ class ApiFunction(object): def __init__(self, argtypes, restype, callable, error=CANNOT_FAIL, c_name=None, cdecl=None, gil=None, - result_borrowed=False, result_is_ll=False): + result_borrowed=False, result_is_ll=False, + nowrapper=False): self.argtypes = argtypes self.restype = restype self.functype = lltype.Ptr(lltype.FuncType(argtypes, restype)) @@ -270,8 +271,11 @@ # extract the signature from the (CPython-level) code object from pypy.interpreter import pycode sig = pycode.cpython_code_signature(callable.func_code) - assert sig.argnames[0] == 'space' - self.argnames = sig.argnames[1:] + if nowrapper: + self.argnames = sig.argnames + else: + assert sig.argnames[0] == 'space' + self.argnames = sig.argnames[1:] if gil == 'pygilstate_ensure': assert self.argnames[-1] == 'previous_state' del self.argnames[-1] @@ -280,6 +284,7 @@ self.gil = gil self.result_borrowed = result_borrowed self.result_is_ll = result_is_ll + self.nowrapper = nowrapper # def get_llhelper(space): return llhelper(self.functype, self.get_wrapper(space)) @@ -301,6 +306,8 @@ # This logic is obscure, because we try to avoid creating one # big wrapper() function for every callable. Instead we create # only one per "signature". + if self.nowrapper: + return self.callable argtypesw = zip(self.argtypes, [_name.startswith("w_") for _name in self.argnames]) @@ -432,7 +439,8 @@ DEFAULT_HEADER = 'pypy_decl.h' def cpython_api(argtypes, restype, error=_NOT_SPECIFIED, header=DEFAULT_HEADER, - gil=None, result_borrowed=False, result_is_ll=False): + gil=None, result_borrowed=False, result_is_ll=False, + nowrapper=False): """ Declares a function to be exported. - `argtypes`, `restype` are lltypes and describe the function signature. @@ -457,7 +465,8 @@ api_function = ApiFunction( argtypes, restype, func, error=_compute_error(error, restype), gil=gil, - result_borrowed=result_borrowed, result_is_ll=result_is_ll) + result_borrowed=result_borrowed, result_is_ll=result_is_ll, + nowrapper=nowrapper) FUNCTIONS_BY_HEADER[header][func.__name__] = api_function unwrapper = api_function.get_unwrapper() unwrapper.func = func diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py --- a/pypy/module/cpyext/object.py +++ b/pypy/module/cpyext/object.py @@ -32,8 +32,8 @@ # XXX FIXME return realloc(ptr, size) - at cpython_api([rffi.VOIDP], lltype.Void) -def PyObject_Free(space, ptr): + at cpython_api([rffi.VOIDP], lltype.Void, nowrapper=True) +def PyObject_Free(ptr): lltype.free(ptr, flavor='raw') @cpython_api([PyTypeObjectPtr], PyObject, result_is_ll=True) @@ -302,7 +302,7 @@ if opid == Py_EQ: return 1 if opid == Py_NE: - return 0 + return 0 w_res = PyObject_RichCompare(space, w_o1, w_o2, opid_int) return int(space.is_true(w_res)) From pypy.commits at gmail.com Mon Oct 2 13:12:21 2017 From: pypy.commits at gmail.com (rlamy) Date: Mon, 02 Oct 2017 10:12:21 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: fix translation Message-ID: <59d27375.69a9df0a.94bb0.fce7@mx.google.com> Author: Ronan Lamy Branch: cpyext-nowrapper Changeset: r92560:7e7eb71ef011 Date: 2017-10-02 19:11 +0200 http://bitbucket.org/pypy/pypy/changeset/7e7eb71ef011/ Log: fix translation diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py --- a/pypy/module/cpyext/object.py +++ b/pypy/module/cpyext/object.py @@ -74,7 +74,7 @@ @cpython_api([rffi.VOIDP], lltype.Void) def PyObject_GC_Del(space, obj): - PyObject_Free(space, obj) + PyObject_Free(obj) @cpython_api([PyObject], PyObjectP, error=CANNOT_FAIL) def _PyObject_GetDictPtr(space, op): From pypy.commits at gmail.com Mon Oct 2 18:16:58 2017 From: pypy.commits at gmail.com (rlamy) Date: Mon, 02 Oct 2017 15:16:58 -0700 (PDT) Subject: [pypy-commit] pypy refactor-slots: Make make_unary_slot a slot factory Message-ID: <59d2bada.0ac0df0a.72349.35f1@mx.google.com> Author: Ronan Lamy Branch: refactor-slots Changeset: r92562:4d5efb01a01c Date: 2017-10-02 17:19 +0200 http://bitbucket.org/pypy/pypy/changeset/4d5efb01a01c/ Log: Make make_unary_slot a slot factory 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 @@ -435,28 +435,11 @@ return decorate -def build_slot_tp_function(space, typedef, name): +def build_slot_tp_function(space, typedef, name, method_name): w_type = space.gettypeobject(typedef) handled = False # unary functions - for tp_name, attr in [('tp_as_number.c_nb_int', '__int__'), - ('tp_as_number.c_nb_long', '__long__'), - ('tp_as_number.c_nb_float', '__float__'), - ('tp_as_number.c_nb_negative', '__neg__'), - ('tp_as_number.c_nb_positive', '__pos__'), - ('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_as_number.c_nb_oct', '__oct__'), - ('tp_str', '__str__'), - ('tp_repr', '__repr__'), - ('tp_iter', '__iter__'), - ]: - if name == tp_name: - return make_unary_slot(space, typedef, name, attr) - for tp_name, attr in [('tp_hash', '__hash__'), ('tp_as_sequence.c_sq_length', '__len__'), ('tp_as_mapping.c_mp_length', '__len__'), @@ -676,7 +659,7 @@ return space.call_function(get_fn, w_self, w_obj, w_value) slot_func = slot_tp_descr_get elif name in SLOT_FACTORIES: - return SLOT_FACTORIES[name](space, typedef) + return SLOT_FACTORIES[name](space, typedef, name, method_name) else: # missing: tp_as_number.nb_nonzero, tp_as_number.nb_coerce # tp_as_sequence.c_sq_contains, tp_as_sequence.c_sq_length @@ -697,8 +680,26 @@ return space.call_function(slot_fn, w_self) return slot_func + +UNARY_SLOTS = [ + 'tp_as_number.c_nb_int', + 'tp_as_number.c_nb_long', + 'tp_as_number.c_nb_float', + 'tp_as_number.c_nb_negative', + 'tp_as_number.c_nb_positive', + 'tp_as_number.c_nb_absolute', + 'tp_as_number.c_nb_invert', + 'tp_as_number.c_nb_index', + 'tp_as_number.c_nb_hex', + 'tp_as_number.c_nb_oct', + 'tp_str', + 'tp_repr', + 'tp_iter'] +for name in UNARY_SLOTS: + slot_factory(name)(make_unary_slot) + @slot_factory('tp_descr_set') -def make_tp_descr_set(space, typedef): +def make_tp_descr_set(space, typedef, name, attr): w_type = space.gettypeobject(typedef) name = 'descr_set' set_fn = w_type.lookup('__set__') 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 @@ -228,7 +228,7 @@ SLOTS = {} @specialize.memo() -def get_slot_tp_function(space, typedef, name): +def get_slot_tp_function(space, typedef, name, method_name): """Return a description of the slot C function to use for the built-in type for 'typedef'. The 'name' is the slot name. This is a memo function that, after translation, returns one of a built-in finite set. @@ -237,7 +237,7 @@ try: return SLOTS[key] except KeyError: - slot_func = build_slot_tp_function(space, typedef, name) + slot_func = build_slot_tp_function(space, typedef, name, method_name) api_func = slot_func.api_func if slot_func else None SLOTS[key] = api_func return api_func @@ -281,7 +281,7 @@ def update_all_slots_builtin(space, w_type, pto): typedef = w_type.layout.typedef for method_name, slot_name, slot_names, slot_apifunc in slotdefs_for_tp_slots: - slot_apifunc = get_slot_tp_function(space, typedef, slot_name) + slot_apifunc = get_slot_tp_function(space, typedef, slot_name, method_name) if not slot_apifunc: warn_missing_slot(space, method_name, slot_name, w_type) continue From pypy.commits at gmail.com Mon Oct 2 18:17:00 2017 From: pypy.commits at gmail.com (rlamy) Date: Mon, 02 Oct 2017 15:17:00 -0700 (PDT) Subject: [pypy-commit] pypy refactor-slots: Convert build_slot_tp_function() to use only slot factories Message-ID: <59d2badc.cf97df0a.89a58.4bbe@mx.google.com> Author: Ronan Lamy Branch: refactor-slots Changeset: r92563:848194a517c3 Date: 2017-10-03 00:10 +0200 http://bitbucket.org/pypy/pypy/changeset/848194a517c3/ Log: Convert build_slot_tp_function() to use only slot factories 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 @@ -436,229 +436,7 @@ def build_slot_tp_function(space, typedef, name, method_name): - w_type = space.gettypeobject(typedef) - - handled = False - # unary functions - for tp_name, attr in [('tp_hash', '__hash__'), - ('tp_as_sequence.c_sq_length', '__len__'), - ('tp_as_mapping.c_mp_length', '__len__'), - ]: - if name == tp_name: - slot_fn = w_type.lookup(attr) - if slot_fn is None: - return - @slot_function([PyObject], lltype.Signed, error=-1) - @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) - def slot_func(space, w_obj): - return space.int_w(space.call_function(slot_fn, w_obj)) - handled = True - - - # binary functions - for tp_name, attr in [('tp_as_number.c_nb_add', '__add__'), - ('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__'), - ('tp_as_number.c_nb_divmod', '__divmod__'), - ('tp_as_number.c_nb_lshift', '__lshift__'), - ('tp_as_number.c_nb_rshift', '__rshift__'), - ('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__'), - ('tp_as_mapping.c_mp_subscript', '__getitem__'), - ]: - if name == tp_name: - slot_fn = w_type.lookup(attr) - if slot_fn is None: - return - - @slot_function([PyObject, PyObject], PyObject) - @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) - def slot_func(space, w_self, w_arg): - return space.call_function(slot_fn, w_self, w_arg) - 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.lookup(attr) - if slot_fn is None: - return - - @slot_function([PyObject, Py_ssize_t], PyObject) - @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.newint(arg)) - handled = True - - # ternary functions - for tp_name, attr in [('tp_as_number.c_nb_power', '__pow__'), - ]: - if name == tp_name: - slot_fn = w_type.lookup(attr) - if slot_fn is None: - return - - @slot_function([PyObject, PyObject, PyObject], PyObject) - @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) - def slot_func(space, w_self, w_arg1, w_arg2): - return space.call_function(slot_fn, w_self, w_arg1, w_arg2) - handled = True - # ternary-with-void returning-Py_size_t-type - for tp_name, attr in [('tp_as_mapping.c_mp_ass_subscript', '__setitem__'), - ]: - if name == tp_name: - slot_ass = w_type.lookup(attr) - if slot_ass is None: - return - slot_del = w_type.lookup('__delitem__') - if slot_del is None: - return - - @slot_function([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) - @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) - def slot_func(space, w_self, w_arg1, arg2): - if arg2: - w_arg2 = from_ref(space, rffi.cast(PyObject, arg2)) - space.call_function(slot_ass, w_self, w_arg1, w_arg2) - else: - space.call_function(slot_del, w_self, w_arg1) - return 0 - handled = True - # ternary-Py_size_t-void returning-Py_size_t-type - for tp_name, attr in [('tp_as_sequence.c_sq_ass_item', '__setitem__'), - ]: - if name == tp_name: - slot_ass = w_type.lookup(attr) - if slot_ass is None: - return - slot_del = w_type.lookup('__delitem__') - if slot_del is None: - return - - @slot_function([PyObject, lltype.Signed, PyObject], rffi.INT_real, error=-1) - @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) - def slot_func(space, w_self, arg1, arg2): - if arg2: - w_arg2 = from_ref(space, rffi.cast(PyObject, arg2)) - space.call_function(slot_ass, w_self, space.newint(arg1), w_arg2) - else: - space.call_function(slot_del, w_self, space.newint(arg1)) - return 0 - handled = True - if handled: - pass - elif name == 'tp_setattro': - setattr_fn = w_type.lookup('__setattr__') - delattr_fn = w_type.lookup('__delattr__') - if setattr_fn is None: - return - - @slot_function([PyObject, PyObject, PyObject], rffi.INT_real, - error=-1) - @func_renamer("cpyext_tp_setattro_%s" % (typedef.name,)) - def slot_tp_setattro(space, w_self, w_name, w_value): - if w_value is not None: - space.call_function(setattr_fn, w_self, w_name, w_value) - else: - space.call_function(delattr_fn, w_self, w_name) - return 0 - slot_func = slot_tp_setattro - elif name == 'tp_getattro': - getattr_fn = w_type.lookup('__getattribute__') - if getattr_fn is None: - return - - @slot_function([PyObject, PyObject], PyObject) - @func_renamer("cpyext_tp_getattro_%s" % (typedef.name,)) - def slot_tp_getattro(space, w_self, w_name): - return space.call_function(getattr_fn, w_self, w_name) - slot_func = slot_tp_getattro - - elif name == 'tp_call': - call_fn = w_type.lookup('__call__') - if call_fn is None: - return - - @slot_function([PyObject, PyObject, PyObject], PyObject) - @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) - def slot_tp_call(space, w_self, w_args, w_kwds): - args = Arguments(space, [w_self], - w_stararg=w_args, w_starstararg=w_kwds) - return space.call_args(call_fn, args) - slot_func = slot_tp_call - - elif name == 'tp_iternext': - iternext_fn = w_type.lookup('next') - if iternext_fn is None: - return - - @slot_function([PyObject], PyObject) - @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) - def slot_tp_iternext(space, w_self): - try: - return space.call_function(iternext_fn, w_self) - except OperationError as e: - if not e.match(space, space.w_StopIteration): - raise - return None - slot_func = slot_tp_iternext - - elif name == 'tp_init': - init_fn = w_type.lookup('__init__') - if init_fn is None: - return - - @slot_function([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) - @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) - def slot_tp_init(space, w_self, w_args, w_kwds): - args = Arguments(space, [w_self], - w_stararg=w_args, w_starstararg=w_kwds) - space.call_args(init_fn, args) - return 0 - slot_func = slot_tp_init - elif name == 'tp_new': - new_fn = w_type.lookup('__new__') - if new_fn is None: - return - - @slot_function([PyTypeObjectPtr, PyObject, PyObject], PyObject) - @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) - def slot_tp_new(space, w_self, w_args, w_kwds): - args = Arguments(space, [w_self], - w_stararg=w_args, w_starstararg=w_kwds) - return space.call_args(space.get(new_fn, w_self), args) - slot_func = slot_tp_new - elif name == 'tp_as_buffer.c_bf_getbuffer': - buff_fn = w_type.lookup('__buffer__') - if buff_fn is not None: - buff_w = slot_from___buffer__(space, typedef, buff_fn) - elif typedef.buffer: - buff_w = slot_from_buffer_w(space, typedef, buff_fn) - else: - return - slot_func = buff_w - elif name == 'tp_descr_get': - get_fn = w_type.lookup('__get__') - if get_fn is None: - return - - @slot_function([PyObject, PyObject, PyObject], PyObject) - @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) - def slot_tp_descr_get(space, w_self, w_obj, w_value): - if w_obj is None: - w_obj = space.w_None - return space.call_function(get_fn, w_self, w_obj, w_value) - slot_func = slot_tp_descr_get - elif name in SLOT_FACTORIES: + if name in SLOT_FACTORIES: return SLOT_FACTORIES[name](space, typedef, name, method_name) else: # missing: tp_as_number.nb_nonzero, tp_as_number.nb_coerce @@ -666,7 +444,6 @@ # richcmpfunc(s) return - return slot_func def make_unary_slot(space, typedef, name, attr): w_type = space.gettypeobject(typedef) @@ -680,7 +457,6 @@ return space.call_function(slot_fn, w_self) return slot_func - UNARY_SLOTS = [ 'tp_as_number.c_nb_int', 'tp_as_number.c_nb_long', @@ -698,10 +474,256 @@ for name in UNARY_SLOTS: slot_factory(name)(make_unary_slot) +def make_unary_slot_int(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + slot_fn = w_type.lookup(attr) + if slot_fn is None: + return + @slot_function([PyObject], lltype.Signed, error=-1) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_func(space, w_obj): + return space.int_w(space.call_function(slot_fn, w_obj)) + return slot_func + +UNARY_SLOTS_INT = [ + 'tp_hash', + 'tp_as_sequence.c_sq_length', + 'tp_as_mapping.c_mp_length',] +for name in UNARY_SLOTS_INT: + slot_factory(name)(make_unary_slot_int) + + +def make_binary_slot(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + slot_fn = w_type.lookup(attr) + if slot_fn is None: + return + + @slot_function([PyObject, PyObject], PyObject) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_func(space, w_self, w_arg): + return space.call_function(slot_fn, w_self, w_arg) + return slot_func + +BINARY_SLOTS = [ + 'tp_as_number.c_nb_add', + 'tp_as_number.c_nb_subtract', + 'tp_as_number.c_nb_multiply', + 'tp_as_number.c_nb_divide', + 'tp_as_number.c_nb_remainder', + 'tp_as_number.c_nb_divmod', + 'tp_as_number.c_nb_lshift', + 'tp_as_number.c_nb_rshift', + 'tp_as_number.c_nb_and', + 'tp_as_number.c_nb_xor', + 'tp_as_number.c_nb_or', + 'tp_as_sequence.c_sq_concat', + 'tp_as_sequence.c_sq_inplace_concat', + 'tp_as_mapping.c_mp_subscript',] +for name in BINARY_SLOTS: + slot_factory(name)(make_binary_slot) + + +def make_binary_slot_int(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + slot_fn = w_type.lookup(attr) + if slot_fn is None: + return + + @slot_function([PyObject, Py_ssize_t], PyObject) + @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.newint(arg)) + return slot_func + +BINARY_SLOTS_INT = [ + 'tp_as_sequence.c_sq_item', + 'tp_as_sequence.c_sq_repeat', + 'tp_as_sequence.c_sq_repeat', + 'tp_as_sequence.c_sq_inplace_repeat',] +for name in BINARY_SLOTS_INT: + slot_factory(name)(make_binary_slot_int) + + at slot_factory('tp_as_number.c_nb_power') +def make_nb_power(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + slot_fn = w_type.lookup(attr) + if slot_fn is None: + return + + @slot_function([PyObject, PyObject, PyObject], PyObject) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_func(space, w_self, w_arg1, w_arg2): + return space.call_function(slot_fn, w_self, w_arg1, w_arg2) + return slot_func + + at slot_factory('tp_as_mapping.c_mp_ass_subscript') +def make_sq_set_item(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + slot_ass = w_type.lookup(attr) + if slot_ass is None: + return + slot_del = w_type.lookup('__delitem__') + if slot_del is None: + return + + @slot_function([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_func(space, w_self, w_arg1, arg2): + if arg2: + w_arg2 = from_ref(space, rffi.cast(PyObject, arg2)) + space.call_function(slot_ass, w_self, w_arg1, w_arg2) + else: + space.call_function(slot_del, w_self, w_arg1) + return 0 + return slot_func + + + at slot_factory('tp_as_sequence.c_sq_ass_item') +def make_sq_ass_item(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + slot_ass = w_type.lookup(attr) + if slot_ass is None: + return + slot_del = w_type.lookup('__delitem__') + if slot_del is None: + return + + @slot_function([PyObject, lltype.Signed, PyObject], rffi.INT_real, error=-1) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_func(space, w_self, arg1, arg2): + if arg2: + w_arg2 = from_ref(space, rffi.cast(PyObject, arg2)) + space.call_function(slot_ass, w_self, space.newint(arg1), w_arg2) + else: + space.call_function(slot_del, w_self, space.newint(arg1)) + return 0 + return slot_func + +def make_tp_setattro(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + setattr_fn = w_type.lookup('__setattr__') + delattr_fn = w_type.lookup('__delattr__') + if setattr_fn is None or delattr_fn is None: + return + + @slot_function([PyObject, PyObject, PyObject], rffi.INT_real, + error=-1) + @func_renamer("cpyext_tp_setattro_%s" % (typedef.name,)) + def slot_tp_setattro(space, w_self, w_name, w_value): + if w_value is not None: + space.call_function(setattr_fn, w_self, w_name, w_value) + else: + space.call_function(delattr_fn, w_self, w_name) + return 0 + return slot_tp_setattro + + at slot_factory('tp_getattro') +def make_tp_getattro(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + getattr_fn = w_type.lookup('__getattribute__') + if getattr_fn is None: + return + + @slot_function([PyObject, PyObject], PyObject) + @func_renamer("cpyext_tp_getattro_%s" % (typedef.name,)) + def slot_tp_getattro(space, w_self, w_name): + return space.call_function(getattr_fn, w_self, w_name) + return slot_tp_getattro + + at slot_factory('tp_call') +def make_tp_call(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + call_fn = w_type.lookup('__call__') + if call_fn is None: + return + + @slot_function([PyObject, PyObject, PyObject], PyObject) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_tp_call(space, w_self, w_args, w_kwds): + args = Arguments(space, [w_self], + w_stararg=w_args, w_starstararg=w_kwds) + return space.call_args(call_fn, args) + return slot_tp_call + + at slot_factory('tp_iternext') +def make_tp_iternext(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + iternext_fn = w_type.lookup('next') + if iternext_fn is None: + return + + @slot_function([PyObject], PyObject) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_tp_iternext(space, w_self): + try: + return space.call_function(iternext_fn, w_self) + except OperationError as e: + if not e.match(space, space.w_StopIteration): + raise + return None + return slot_tp_iternext + + at slot_factory('tp_init') +def make_tp_init(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + init_fn = w_type.lookup('__init__') + if init_fn is None: + return + + @slot_function([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_tp_init(space, w_self, w_args, w_kwds): + args = Arguments(space, [w_self], + w_stararg=w_args, w_starstararg=w_kwds) + space.call_args(init_fn, args) + return 0 + return slot_tp_init + + at slot_factory('tp_new') +def make_tp_new(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + new_fn = w_type.lookup('__new__') + if new_fn is None: + return + + @slot_function([PyTypeObjectPtr, PyObject, PyObject], PyObject) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_tp_new(space, w_self, w_args, w_kwds): + args = Arguments(space, [w_self], + w_stararg=w_args, w_starstararg=w_kwds) + return space.call_args(space.get(new_fn, w_self), args) + return slot_tp_new + + at slot_factory('tp_as_buffer.c_bf_getbuffer') +def make_bf_getbuffer(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + buff_fn = w_type.lookup('__buffer__') + if buff_fn is not None: + return slot_from___buffer__(space, typedef, buff_fn) + elif typedef.buffer: + return slot_from_buffer_w(space, typedef) + else: + return None + + at slot_factory('tp_descr_get') +def make_tp_descr_get(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + get_fn = w_type.lookup('__get__') + if get_fn is None: + return + + @slot_function([PyObject, PyObject, PyObject], PyObject) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_tp_descr_get(space, w_self, w_obj, w_value): + if w_obj is None: + w_obj = space.w_None + return space.call_function(get_fn, w_self, w_obj, w_value) + return slot_tp_descr_get + @slot_factory('tp_descr_set') def make_tp_descr_set(space, typedef, name, attr): w_type = space.gettypeobject(typedef) - name = 'descr_set' set_fn = w_type.lookup('__set__') delete_fn = w_type.lookup('__delete__') if set_fn is None and delete_fn is None: @@ -751,7 +773,7 @@ return 0 return buff_w -def slot_from_buffer_w(space, typedef, buff_fn): +def slot_from_buffer_w(space, typedef): name = 'bf_getbuffer' @slot_function([PyObject, Py_bufferP, rffi.INT_real], rffi.INT_real, error=-1) From pypy.commits at gmail.com Mon Oct 2 18:16:56 2017 From: pypy.commits at gmail.com (rlamy) Date: Mon, 02 Oct 2017 15:16:56 -0700 (PDT) Subject: [pypy-commit] pypy refactor-slots: Extract make_unary_slot() Message-ID: <59d2bad8.81901c0a.c5d4a.b506@mx.google.com> Author: Ronan Lamy Branch: refactor-slots Changeset: r92561:9b482bc4eb37 Date: 2017-10-02 16:12 +0200 http://bitbucket.org/pypy/pypy/changeset/9b482bc4eb37/ Log: Extract make_unary_slot() 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 @@ -455,15 +455,7 @@ ('tp_iter', '__iter__'), ]: if name == tp_name: - slot_fn = w_type.lookup(attr) - if slot_fn is None: - return - - @slot_function([PyObject], PyObject) - @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) - def slot_func(space, w_self): - return space.call_function(slot_fn, w_self) - handled = True + return make_unary_slot(space, typedef, name, attr) for tp_name, attr in [('tp_hash', '__hash__'), ('tp_as_sequence.c_sq_length', '__len__'), @@ -693,6 +685,18 @@ return slot_func +def make_unary_slot(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + slot_fn = w_type.lookup(attr) + if slot_fn is None: + return + + @slot_function([PyObject], PyObject) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_func(space, w_self): + return space.call_function(slot_fn, w_self) + return slot_func + @slot_factory('tp_descr_set') def make_tp_descr_set(space, typedef): w_type = space.gettypeobject(typedef) From pypy.commits at gmail.com Tue Oct 3 05:35:18 2017 From: pypy.commits at gmail.com (rlamy) Date: Tue, 03 Oct 2017 02:35:18 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: Don't segfault in test_typeobject.py::test_incomplete_extend_3() Message-ID: <59d359d6.d87f1c0a.1f83a.6f3f@mx.google.com> Author: Ronan Lamy Branch: py3.5 Changeset: r92564:f9ce0cdc4b97 Date: 2017-10-03 11:34 +0200 http://bitbucket.org/pypy/pypy/changeset/f9ce0cdc4b97/ Log: Don't segfault in test_typeobject.py::test_incomplete_extend_3() 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 @@ -151,7 +151,7 @@ 'hasuserdel', 'weakrefable', 'hasdict', - 'layout', + 'layout?', 'terminator', '_version_tag?', 'name?', @@ -191,6 +191,7 @@ self.flag_sequence_bug_compat = False self.flag_map_or_seq = '?' # '?' means "don't know, check otherwise" + self.layout = None # the lines below may try to access self.layout if overridetypedef is not None: assert not force_new_layout layout = setup_builtin_type(self, overridetypedef) @@ -493,6 +494,10 @@ if not isinstance(w_subtype, W_TypeObject): raise oefmt(space.w_TypeError, "X is not a type object ('%T')", w_subtype) + if not w_subtype.layout: + raise oefmt(space.w_TypeError, + "%N.__new__(%N): uninitialized type %N may not be instantiated yet.", + self, w_subtype, w_subtype) if not w_subtype.issubtype(self): raise oefmt(space.w_TypeError, "%N.__new__(%N): %N is not a subtype of %N", From pypy.commits at gmail.com Tue Oct 3 06:55:46 2017 From: pypy.commits at gmail.com (mattip) Date: Tue, 03 Oct 2017 03:55:46 -0700 (PDT) Subject: [pypy-commit] pypy default: move branch documentation to release, restart whatsnew-head Message-ID: <59d36cb2.82a1df0a.ecd4f.32b8@mx.google.com> Author: Matti Picus Branch: Changeset: r92565:03d614975835 Date: 2017-10-03 13:48 +0300 http://bitbucket.org/pypy/pypy/changeset/03d614975835/ Log: move branch documentation to release, restart whatsnew-head diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -3,13 +3,6 @@ =========================== .. this is a revision shortly after release-pypy2.7-v5.9.0 -.. startrev:899e5245de1e +.. startrev:d56dadcef996 -.. branch: cpyext-jit -Differentiate the code to call METH_NOARGS, METH_O and METH_VARARGS in cpyext: -this allows to write specialized code which is much faster than previous -completely generic version. Moreover, let the JIT to look inside the cpyext -module: the net result is that cpyext calls are up to 7x faster. However, this -is true only for very simple situations: in all real life code, we are still -much slower than CPython (more optimizations to come) diff --git a/pypy/doc/whatsnew-pypy2-5.9.0.rst b/pypy/doc/whatsnew-pypy2-5.9.0.rst --- a/pypy/doc/whatsnew-pypy2-5.9.0.rst +++ b/pypy/doc/whatsnew-pypy2-5.9.0.rst @@ -85,3 +85,12 @@ .. branch: py_ssize_t Explicitly use Py_ssize_t as the Signed type in pypy c-api + +.. branch: cpyext-jit + +Differentiate the code to call METH_NOARGS, METH_O and METH_VARARGS in cpyext: +this allows to write specialized code which is much faster than previous +completely generic version. Moreover, let the JIT to look inside the cpyext +module: the net result is that cpyext calls are up to 7x faster. However, this +is true only for very simple situations: in all real life code, we are still +much slower than CPython (more optimizations to come) From pypy.commits at gmail.com Tue Oct 3 06:55:49 2017 From: pypy.commits at gmail.com (mattip) Date: Tue, 03 Oct 2017 03:55:49 -0700 (PDT) Subject: [pypy-commit] pypy release-pypy2.7-5.x: merge default into release Message-ID: <59d36cb5.138fdf0a.aec3d.fb41@mx.google.com> Author: Matti Picus Branch: release-pypy2.7-5.x Changeset: r92566:84a2f3e6a7f8 Date: 2017-10-03 13:49 +0300 http://bitbucket.org/pypy/pypy/changeset/84a2f3e6a7f8/ Log: merge default into release diff --git a/pypy/doc/release-v5.9.0.rst b/pypy/doc/release-v5.9.0.rst --- a/pypy/doc/release-v5.9.0.rst +++ b/pypy/doc/release-v5.9.0.rst @@ -10,15 +10,18 @@ This new PyPy2.7 release includes the upstream stdlib version 2.7.13, and PyPy3.5 includes the upstream stdlib version 3.5.3. -NumPy and Pandas now work on PyPy2.7. Issues that appeared as excessive memory +NumPy and Pandas now work on PyPy2.7 (together with Cython 0.27.1). Issues +that appeared as excessive memory use were cleared up and other incompatibilities were resolved. The C-API compatibility layer does slow down code which crosses the python-c interface often, we have ideas on how it could be improved, and still recommend using pure python on PyPy or interfacing via CFFI_. Many other modules based on C-API exentions now work on PyPy as well. -Cython 0.27 (released last week) should support more projects with PyPy, both -on PyPy2.7 and PyPy3.5 beta. +Cython 0.27.1 (released very recently) supports more projects with PyPy, both +on PyPy2.7 and PyPy3.5 beta. Note version **0.27.1** is now the minimum +version that supports this version of PyPy, due to some interactions with +updated C-API interface code. We optimized the JSON parser for recurring string keys, which should decrease memory use to 50% and increase parsing speed by up to 15% for large JSON files @@ -148,6 +151,7 @@ * Issue 2590_: fix the bounds in the GC when allocating a lot of objects with finalizers * Replace magical NOT RPYTHON comment with a decorator * Implement ``socket.sendmsg()``/``.recvmsg()`` for py3.5 + * Add ``memory_pressure`` for ``_SSLSocket`` objects * Degredations 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 @@ -1,87 +1,7 @@ -========================== -What's new in PyPy2.7 5.9+ -========================== +=========================== +What's new in PyPy2.7 5.10+ +=========================== -.. this is a revision shortly after release-pypy2.7-v5.8.0 -.. startrev: 558bd00b3dd8 +.. this is a revision shortly after release-pypy2.7-v5.9.0 +.. startrev:d56dadcef996 -In previous versions of PyPy, ``instance.method`` would return always -the same bound method object, when gotten out of the same instance (as -far as ``is`` and ``id()`` can tell). CPython doesn't do that. Now -PyPy, like CPython, returns a different bound method object every time. -For ``type.method``, PyPy2 still returns always the same *unbound* -method object; CPython does it for built-in types but not for -user-defined types. - -.. branch: cffi-complex -.. branch: cffi-char16-char32 - -The two ``cffi-*`` branches are part of the upgrade to cffi 1.11. - -.. branch: ctypes_char_indexing - -Indexing into char* behaves differently than CPython - -.. branch: vmprof-0.4.8 - -Improve and fix issues with vmprof - -.. branch: issue-2592 - -CPyext PyListObject.pop must return the value - -.. branch: cpyext-hash_notimpl - -If ``tp_hash`` is ``PyObject_HashNotImplemented``, set ``obj.__dict__['__hash__']`` to None - -.. branch: cppyy-packaging - -Renaming of ``cppyy`` to ``_cppyy``. -The former is now an external package installable with ``pip install cppyy``. - -.. branch: Enable_PGO_for_clang - -.. branch: nopax - -At the end of translation, run ``attr -q -s pax.flags -V m`` on -PAX-enabled systems on the produced binary. This seems necessary -because PyPy uses a JIT. - -.. branch: pypy_bytearray - -Improve ``bytearray`` performance (backported from py3.5) - -.. branch: gc-del-limit-growth - -Fix the bounds in the GC when allocating a lot of objects with finalizers, -fixes issue #2590 - -.. branch: arrays-force-less - -Small improvement to optimize list accesses with constant indexes better by -throwing away information about them less eagerly. - - -.. branch: getarrayitem-into-bridges - -More information is retained into a bridge: knowledge about the content of -arrays (at fixed indices) is stored in guards (and thus available at the -beginning of bridges). Also, some better feeding of information about known -fields of constant objects into bridges. - -.. branch: cpyext-leakchecking - -Add support for leakfinder in cpyext tests (disabled for now, due to too many -failures). - -.. branch: pypy_swappedbytes - -Added ``_swappedbytes_`` support for ``ctypes.Structure`` - -.. branch: pycheck-macros - -Convert many Py*_Check cpyext functions into macros, like CPython. - -.. branch: py_ssize_t - -Explicitly use Py_ssize_t as the Signed type in pypy c-api diff --git a/pypy/doc/whatsnew-pypy2-5.9.0.rst b/pypy/doc/whatsnew-pypy2-5.9.0.rst --- a/pypy/doc/whatsnew-pypy2-5.9.0.rst +++ b/pypy/doc/whatsnew-pypy2-5.9.0.rst @@ -85,3 +85,12 @@ .. branch: py_ssize_t Explicitly use Py_ssize_t as the Signed type in pypy c-api + +.. branch: cpyext-jit + +Differentiate the code to call METH_NOARGS, METH_O and METH_VARARGS in cpyext: +this allows to write specialized code which is much faster than previous +completely generic version. Moreover, let the JIT to look inside the cpyext +module: the net result is that cpyext calls are up to 7x faster. However, this +is true only for very simple situations: in all real life code, we are still +much slower than CPython (more optimizations to come) diff --git a/pypy/module/_cffi_backend/cffi1_module.py b/pypy/module/_cffi_backend/cffi1_module.py --- a/pypy/module/_cffi_backend/cffi1_module.py +++ b/pypy/module/_cffi_backend/cffi1_module.py @@ -1,4 +1,5 @@ from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rlib import jit from pypy.interpreter.error import oefmt from pypy.interpreter.module import Module @@ -15,7 +16,7 @@ INITFUNCPTR = lltype.Ptr(lltype.FuncType([rffi.VOIDPP], lltype.Void)) - + at jit.dont_look_inside def load_cffi1_module(space, name, path, initptr): # This is called from pypy.module.cpyext.api.load_extension_module() from pypy.module._cffi_backend.call_python import get_ll_cffi_call_python diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py --- a/pypy/module/_cffi_backend/ctypeptr.py +++ b/pypy/module/_cffi_backend/ctypeptr.py @@ -156,10 +156,11 @@ class W_CTypePtrBase(W_CTypePtrOrArray): # base class for both pointers and pointers-to-functions - _attrs_ = ['is_void_ptr', 'is_voidchar_ptr'] - _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr'] + _attrs_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] + _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] is_void_ptr = False is_voidchar_ptr = False + is_onebyte_ptr = False def convert_to_object(self, cdata): ptrdata = rffi.cast(rffi.CCHARPP, cdata)[0] @@ -179,12 +180,20 @@ if self.is_void_ptr or other.is_void_ptr: pass # cast from or to 'void *' elif self.is_voidchar_ptr or other.is_voidchar_ptr: - space = self.space - msg = ("implicit cast from '%s' to '%s' " - "will be forbidden in the future (check that the types " - "are as you expect; use an explicit ffi.cast() if they " - "are correct)" % (other.name, self.name)) - space.warn(space.newtext(msg), space.w_UserWarning) + # for backward compatibility, accept "char *" as either + # source of target. This is not what C does, though, + # so emit a warning that will eventually turn into an + # error. The warning is turned off if both types are + # pointers to single bytes. + if self.is_onebyte_ptr and other.is_onebyte_ptr: + pass # no warning + else: + space = self.space + msg = ("implicit cast from '%s' to '%s' " + "will be forbidden in the future (check that the types " + "are as you expect; use an explicit ffi.cast() if they " + "are correct)" % (other.name, self.name)) + space.warn(space.newtext(msg), space.w_UserWarning) else: raise self._convert_error("compatible pointer", w_ob) @@ -214,6 +223,7 @@ self.is_void_ptr = isinstance(ctitem, ctypevoid.W_CTypeVoid) self.is_voidchar_ptr = (self.is_void_ptr or isinstance(ctitem, ctypeprim.W_CTypePrimitiveChar)) + self.is_onebyte_ptr = (ctitem.size == 1) W_CTypePtrBase.__init__(self, space, size, extra, 2, ctitem) def newp(self, w_init, allocator): diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -2099,7 +2099,8 @@ if sys.platform.startswith("linux"): BWChar = new_primitive_type("wchar_t") assert sizeof(BWChar) == 4 - assert int(cast(BWChar, -1)) == -1 # signed, on linux + # wchar_t is often signed on Linux, but not always (e.g. on ARM) + assert int(cast(BWChar, -1)) in (-1, 4294967295) def test_char16(): BChar16 = new_primitive_type("char16_t") @@ -3903,9 +3904,11 @@ BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BVoidP = new_pointer_type(new_void_type()) + BUCharP = new_pointer_type(new_primitive_type("unsigned char")) z1 = cast(BCharP, 0) z2 = cast(BIntP, 0) z3 = cast(BVoidP, 0) + z4 = cast(BUCharP, 0) with warnings.catch_warnings(record=True) as w: newp(new_pointer_type(BIntP), z1) # warn assert len(w) == 1 @@ -3919,6 +3922,12 @@ assert len(w) == 2 newp(new_pointer_type(BIntP), z3) # fine assert len(w) == 2 + newp(new_pointer_type(BCharP), z4) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z1) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z3) # fine + assert len(w) == 2 # check that the warnings are associated with lines in this file assert w[1].lineno == w[0].lineno + 4 diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py --- a/pypy/module/_ssl/interp_ssl.py +++ b/pypy/module/_ssl/interp_ssl.py @@ -1316,7 +1316,7 @@ if not ctx: raise ssl_error(space, "failed to allocate SSL context") - rgc.add_memory_pressure(10 * 1024) + rgc.add_memory_pressure(10 * 1024 * 1024) self = space.allocate_instance(_SSLContext, w_subtype) self.ctx = ctx self.check_hostname = False diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -449,6 +449,11 @@ if func.__name__ in FUNCTIONS_BY_HEADER[header]: raise ValueError("%s already registered" % func.__name__) func._always_inline_ = 'try' + # + # XXX: should we @jit.dont_look_inside all the @cpython_api functions, + # or we should only disable some of them? + func._jit_look_inside_ = False + # api_function = ApiFunction( argtypes, restype, func, error=_compute_error(error, restype), gil=gil, @@ -558,6 +563,7 @@ 'PyObject_GetBuffer', 'PyBuffer_Release', 'PyBuffer_FromMemory', 'PyBuffer_FromReadWriteMemory', 'PyBuffer_FromObject', 'PyBuffer_FromReadWriteObject', 'PyBuffer_New', 'PyBuffer_Type', '_Py_get_buffer_type', + '_Py_setfilesystemdefaultencoding', 'PyCObject_FromVoidPtr', 'PyCObject_FromVoidPtrAndDesc', 'PyCObject_AsVoidPtr', 'PyCObject_GetDesc', 'PyCObject_Import', 'PyCObject_SetVoidPtr', @@ -653,7 +659,11 @@ 'PyClass_Type': 'space.gettypeobject(W_ClassObject.typedef)', 'PyStaticMethod_Type': 'space.gettypeobject(StaticMethod.typedef)', 'PyCFunction_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCFunctionObject.typedef)', - 'PyWrapperDescr_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCMethodObject.typedef)' + 'PyClassMethodDescr_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCClassMethodObject.typedef)', + 'PyGetSetDescr_Type': 'space.gettypeobject(cpyext.typeobject.W_GetSetPropertyEx.typedef)', + 'PyMemberDescr_Type': 'space.gettypeobject(cpyext.typeobject.W_MemberDescr.typedef)', + 'PyMethodDescr_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCMethodObject.typedef)', + 'PyWrapperDescr_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCWrapperObject.typedef)', }.items(): register_global(cpyname, 'PyTypeObject*', pypyexpr, header=pypy_decl) @@ -1045,11 +1055,17 @@ get_capsule_type = rffi.llexternal('_%s_get_capsule_type' % prefix, [], PyTypeObjectPtr, compilation_info=eci, _nowrapper=True) + setdefenc = rffi.llexternal('_%s_setfilesystemdefaultencoding' % prefix, + [rffi.CCHARP], lltype.Void, + compilation_info=eci, _nowrapper=True) def init_types(space): from pypy.module.cpyext.typeobject import py_type_ready + from pypy.module.sys.interp_encoding import getfilesystemencoding py_type_ready(space, get_buffer_type()) py_type_ready(space, get_cobject_type()) py_type_ready(space, get_capsule_type()) + s = space.text_w(getfilesystemencoding(space)) + setdefenc(rffi.str2charp(s, track_allocation=False)) # "leaks" INIT_FUNCTIONS.append(init_types) from pypy.module.posix.interp_posix import add_fork_hook _reinit_tls = rffi.llexternal('%sThread_ReInitTLS' % prefix, [], @@ -1315,17 +1331,20 @@ for decl in FORWARD_DECLS: decls[pypy_decl].append("%s;" % (decl,)) decls[pypy_decl].append(""" - /* hack for https://bugs.python.org/issue29943 */ - PyAPI_FUNC(int) %s(PySliceObject *arg0, - Signed arg1, Signed *arg2, - Signed *arg3, Signed *arg4, Signed *arg5); - static int PySlice_GetIndicesEx(PySliceObject *arg0, Py_ssize_t arg1, - Py_ssize_t *arg2, Py_ssize_t *arg3, Py_ssize_t *arg4, - Py_ssize_t *arg5) { - return %s(arg0, arg1, arg2, arg3, - arg4, arg5); - } - """ % ((mangle_name(prefix, 'PySlice_GetIndicesEx'),)*2)) +/* hack for https://bugs.python.org/issue29943 */ + +PyAPI_FUNC(int) %s(PySliceObject *arg0, + Signed arg1, Signed *arg2, + Signed *arg3, Signed *arg4, Signed *arg5); +#ifdef __GNUC__ +__attribute__((__unused__)) +#endif +static int PySlice_GetIndicesEx(PySliceObject *arg0, Py_ssize_t arg1, + Py_ssize_t *arg2, Py_ssize_t *arg3, Py_ssize_t *arg4, + Py_ssize_t *arg5) { + return %s(arg0, arg1, arg2, arg3, + arg4, arg5); +}""" % ((mangle_name(prefix, 'PySlice_GetIndicesEx'),)*2)) for header_name, header_functions in FUNCTIONS_BY_HEADER.iteritems(): header = decls[header_name] diff --git a/pypy/module/cpyext/include/descrobject.h b/pypy/module/cpyext/include/descrobject.h --- a/pypy/module/cpyext/include/descrobject.h +++ b/pypy/module/cpyext/include/descrobject.h @@ -1,34 +1,6 @@ #ifndef Py_DESCROBJECT_H #define Py_DESCROBJECT_H -#define PyDescr_COMMON \ - PyObject_HEAD \ - PyTypeObject *d_type; \ - PyObject *d_name - -typedef struct { - PyDescr_COMMON; -} PyDescrObject; - -typedef struct { - PyDescr_COMMON; - PyMethodDef *d_method; -} PyMethodDescrObject; - -typedef struct { - PyDescr_COMMON; - struct PyMemberDef *d_member; -} PyMemberDescrObject; - -typedef struct { - PyDescr_COMMON; - PyGetSetDef *d_getset; -} PyGetSetDescrObject; - -typedef struct { - PyDescr_COMMON; - struct wrapperbase *d_base; - void *d_wrapped; /* This can be any function pointer */ -} PyWrapperDescrObject; +#include "cpyext_descrobject.h" #endif diff --git a/pypy/module/cpyext/include/fileobject.h b/pypy/module/cpyext/include/fileobject.h --- a/pypy/module/cpyext/include/fileobject.h +++ b/pypy/module/cpyext/include/fileobject.h @@ -1,1 +1,2 @@ -#define Py_FileSystemDefaultEncoding NULL +PyAPI_DATA(const char *) Py_FileSystemDefaultEncoding; +PyAPI_FUNC(void) _Py_setfilesystemdefaultencoding(const char *); 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,5 @@ -from rpython.rtyper.lltypesystem import lltype, rffi, llmemory +from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rlib import jit from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt @@ -10,8 +11,8 @@ from pypy.module.cpyext.api import ( CONST_STRING, METH_CLASS, METH_COEXIST, METH_KEYWORDS, METH_NOARGS, METH_O, METH_STATIC, METH_VARARGS, PyObject, bootstrap_function, - cpython_api, generic_cpy_call, CANNOT_FAIL, - PyTypeObjectPtr, slot_function, cts) + cpython_api, generic_cpy_call, CANNOT_FAIL, slot_function, cts, + build_type_checkers) from pypy.module.cpyext.pyobject import ( Py_DecRef, from_ref, make_ref, as_pyobj, make_typedescr) @@ -42,8 +43,8 @@ from pypy.module.cpyext.object import _dealloc _dealloc(space, py_obj) - class W_PyCFunctionObject(W_Root): + # TODO create a slightly different class depending on the c_ml_flags def __init__(self, space, ml, w_self, w_module=None): self.ml = ml self.name = rffi.charp2str(rffi.cast(rffi.CCHARP,self.ml.c_ml_name)) @@ -56,7 +57,7 @@ w_self = self.w_self flags = rffi.cast(lltype.Signed, self.ml.c_ml_flags) flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST) - if space.is_true(w_kw) and not flags & METH_KEYWORDS: + if not flags & METH_KEYWORDS and space.is_true(w_kw): raise oefmt(space.w_TypeError, "%s() takes no keyword arguments", self.name) @@ -96,6 +97,20 @@ else: return space.w_None +class W_PyCFunctionObjectNoArgs(W_PyCFunctionObject): + def call(self, space, w_self, w_args, w_kw): + # Call the C function + if w_self is None: + w_self = self.w_self + func = self.ml.c_ml_meth + return generic_cpy_call(space, func, w_self, None) + +class W_PyCFunctionObjectSingleObject(W_PyCFunctionObject): + def call(self, space, w_self, w_o, w_kw): + if w_self is None: + w_self = self.w_self + func = self.ml.c_ml_meth + return generic_cpy_call(space, func, w_self, w_o) class W_PyCMethodObject(W_PyCFunctionObject): w_self = None @@ -109,7 +124,7 @@ return self.space.unwrap(self.descr_method_repr()) def descr_method_repr(self): - w_objclass = self.w_objclass + w_objclass = self.w_objclass assert isinstance(w_objclass, W_TypeObject) return self.space.newtext("" % ( self.name, w_objclass.name)) @@ -117,7 +132,7 @@ def descr_call(self, space, __args__): args_w, kw_w = __args__.unpack() if len(args_w) < 1: - w_objclass = self.w_objclass + w_objclass = self.w_objclass assert isinstance(w_objclass, W_TypeObject) raise oefmt(space.w_TypeError, "descriptor '%s' of '%s' object needs an argument", @@ -125,7 +140,7 @@ w_instance = args_w[0] # XXX: needs a stricter test if not space.isinstance_w(w_instance, self.w_objclass): - w_objclass = self.w_objclass + w_objclass = self.w_objclass assert isinstance(w_objclass, W_TypeObject) raise oefmt(space.w_TypeError, "descriptor '%s' requires a '%s' object but received a '%T'", @@ -137,6 +152,10 @@ ret = self.call(space, w_instance, w_args, w_kw) return ret +# PyPy addition, for Cython +_, _ = build_type_checkers("MethodDescr", W_PyCMethodObject) + + @cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL) def PyCFunction_Check(space, w_obj): from pypy.interpreter.function import BuiltinFunction @@ -163,6 +182,7 @@ (self.name, self.w_objclass.getname(self.space))) + class W_PyCWrapperObject(W_Root): def __init__(self, space, pto, method_name, wrapper_func, wrapper_func_kwds, doc, func, offset=None): @@ -210,6 +230,7 @@ (self.method_name, self.w_objclass.name)) + at jit.dont_look_inside def cwrapper_descr_call(space, w_self, __args__): self = space.interp_w(W_PyCWrapperObject, w_self) args_w, kw_w = __args__.unpack() @@ -220,10 +241,22 @@ space.setitem(w_kw, space.newtext(key), w_obj) return self.call(space, w_self, w_args, w_kw) +def cfunction_descr_call_noargs(space, w_self): + # special case for calling with flags METH_NOARGS + self = space.interp_w(W_PyCFunctionObjectNoArgs, w_self) + return self.call(space, None, None, None) +def cfunction_descr_call_single_object(space, w_self, w_o): + # special case for calling with flags METH_O + self = space.interp_w(W_PyCFunctionObjectSingleObject, w_self) + return self.call(space, None, w_o, None) + + at jit.dont_look_inside def cfunction_descr_call(space, w_self, __args__): + # specialize depending on the W_PyCFunctionObject self = space.interp_w(W_PyCFunctionObject, w_self) args_w, kw_w = __args__.unpack() + # XXX __args__.unpack is slow w_args = space.newtuple(args_w) w_kw = space.newdict() for key, w_obj in kw_w.items(): @@ -231,6 +264,7 @@ ret = self.call(space, None, w_args, w_kw) return ret + at jit.dont_look_inside def cclassmethod_descr_call(space, w_self, __args__): self = space.interp_w(W_PyCFunctionObject, w_self) args_w, kw_w = __args__.unpack() @@ -271,6 +305,26 @@ ) W_PyCFunctionObject.typedef.acceptable_as_base_class = False +W_PyCFunctionObjectNoArgs.typedef = TypeDef( + 'builtin_function_or_method', W_PyCFunctionObject.typedef, + __call__ = interp2app(cfunction_descr_call_noargs), + __doc__ = GetSetProperty(W_PyCFunctionObjectNoArgs.get_doc), + __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectNoArgs), + __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectNoArgs, + wrapfn="newtext_or_none"), + ) +W_PyCFunctionObjectNoArgs.typedef.acceptable_as_base_class = False + +W_PyCFunctionObjectSingleObject.typedef = TypeDef( + 'builtin_function_or_method', W_PyCFunctionObject.typedef, + __call__ = interp2app(cfunction_descr_call_single_object), + __doc__ = GetSetProperty(W_PyCFunctionObjectSingleObject.get_doc), + __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectSingleObject), + __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectSingleObject, + wrapfn="newtext_or_none"), + ) +W_PyCFunctionObjectSingleObject.typedef.acceptable_as_base_class = False + W_PyCMethodObject.typedef = TypeDef( 'method_descriptor', __get__ = interp2app(cmethod_descr_get), @@ -333,11 +387,15 @@ def PyClassMethod_New(space, w_func): return ClassMethod(w_func) - at cpython_api([PyTypeObjectPtr, lltype.Ptr(PyMethodDef)], PyObject) + at cts.decl(""" + PyObject * + PyDescr_NewClassMethod(PyTypeObject *type, PyMethodDef *method)""") def PyDescr_NewMethod(space, w_type, method): return W_PyCMethodObject(space, method, w_type) - at cpython_api([PyObject, lltype.Ptr(PyMethodDef)], PyObject) + at cts.decl(""" + PyObject * + PyDescr_NewClassMethod(PyTypeObject *type, PyMethodDef *method)""") def PyDescr_NewClassMethod(space, w_type, method): return W_PyCClassMethodObject(space, method, w_type) diff --git a/pypy/module/cpyext/modsupport.py b/pypy/module/cpyext/modsupport.py --- a/pypy/module/cpyext/modsupport.py +++ b/pypy/module/cpyext/modsupport.py @@ -1,11 +1,13 @@ from rpython.rtyper.lltypesystem import rffi, lltype from pypy.module.cpyext.api import cpython_api, cpython_struct, \ - METH_STATIC, METH_CLASS, METH_COEXIST, CANNOT_FAIL, CONST_STRING + METH_STATIC, METH_CLASS, METH_COEXIST, CANNOT_FAIL, CONST_STRING, \ + METH_NOARGS, METH_O from pypy.module.cpyext.pyobject import PyObject, as_pyobj from pypy.interpreter.module import Module from pypy.module.cpyext.methodobject import ( W_PyCFunctionObject, PyCFunction_NewEx, PyDescr_NewMethod, - PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New) + PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New, + W_PyCFunctionObjectNoArgs, W_PyCFunctionObjectSingleObject) from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall from pypy.module.cpyext.state import State from pypy.interpreter.error import oefmt @@ -79,6 +81,13 @@ space.newtext(rffi.charp2str(doc))) return w_mod # borrowed result kept alive in PyImport_AddModule() +def _create_pyc_function_object(space, method, w_self, w_name, flags): + flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST) + if flags == METH_NOARGS: + return W_PyCFunctionObjectNoArgs(space, method, w_self, w_name) + if flags == METH_O: + return W_PyCFunctionObjectSingleObject(space, method, w_self, w_name) + return W_PyCFunctionObject(space, method, w_self, w_name) def convert_method_defs(space, dict_w, methods, w_type, w_self=None, name=None): w_name = space.newtext_or_none(name) @@ -98,7 +107,8 @@ raise oefmt(space.w_ValueError, "module functions cannot set METH_CLASS or " "METH_STATIC") - w_obj = W_PyCFunctionObject(space, method, w_self, w_name) + w_obj = _create_pyc_function_object(space, method, w_self, + w_name, flags) else: if methodname in dict_w and not (flags & METH_COEXIST): continue diff --git a/pypy/module/cpyext/parse/cpyext_descrobject.h b/pypy/module/cpyext/parse/cpyext_descrobject.h new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/parse/cpyext_descrobject.h @@ -0,0 +1,29 @@ +#define PyDescr_COMMON \ + PyObject_HEAD \ + PyTypeObject *d_type; \ + PyObject *d_name + +typedef struct { + PyDescr_COMMON; +} PyDescrObject; + +typedef struct { + PyDescr_COMMON; + PyMethodDef *d_method; +} PyMethodDescrObject; + +typedef struct { + PyDescr_COMMON; + struct PyMemberDef *d_member; +} PyMemberDescrObject; + +typedef struct { + PyDescr_COMMON; + PyGetSetDef *d_getset; +} PyGetSetDescrObject; + +typedef struct { + PyDescr_COMMON; + struct wrapperbase *d_base; + void *d_wrapped; /* This can be any function pointer */ +} PyWrapperDescrObject; diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -14,7 +14,7 @@ from rpython.rlib.objectmodel import specialize from rpython.rlib.objectmodel import keepalive_until_here from rpython.rtyper.annlowlevel import llhelper -from rpython.rlib import rawrefcount +from rpython.rlib import rawrefcount, jit from rpython.rlib.debug import fatalerror @@ -151,6 +151,7 @@ class InvalidPointerException(Exception): pass + at jit.dont_look_inside def create_ref(space, w_obj, w_userdata=None, immortal=False): """ Allocates a PyObject, and fills its fields with info from the given @@ -190,6 +191,7 @@ w_marker_deallocating = W_Root() + at jit.dont_look_inside def from_ref(space, ref): """ Finds the interpreter object corresponding to the given reference. If the @@ -227,6 +229,7 @@ assert isinstance(w_type, W_TypeObject) return get_typedescr(w_type.layout.typedef).realize(space, ref) + at jit.dont_look_inside def as_pyobj(space, w_obj, w_userdata=None, immortal=False): """ Returns a 'PyObject *' representing the given intepreter object. 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 @@ -429,23 +429,6 @@ from rpython.rlib.nonconst import NonConstant -SLOTS = {} - - at specialize.memo() -def get_slot_tp_function(space, typedef, name): - """Return a description of the slot C function to use for the built-in - type for 'typedef'. The 'name' is the slot name. This is a memo - function that, after translation, returns one of a built-in finite set. - """ - key = (typedef, name) - try: - return SLOTS[key] - except KeyError: - slot_func = build_slot_tp_function(space, typedef, name) - api_func = slot_func.api_func if slot_func else None - SLOTS[key] = api_func - return api_func - def build_slot_tp_function(space, typedef, name): w_type = space.gettypeobject(typedef) @@ -793,7 +776,7 @@ def __init__(self, method_name, slot_name, function, wrapper1, wrapper2, doc): self.method_name = method_name self.slot_name = slot_name - self.slot_names = ("c_" + slot_name).split(".") + self.slot_names = tuple(("c_" + slot_name).split(".")) self.slot_func = function self.wrapper_func = wrapper1 self.wrapper_func_kwds = wrapper2 diff --git a/pypy/module/cpyext/src/missing.c b/pypy/module/cpyext/src/missing.c --- a/pypy/module/cpyext/src/missing.c +++ b/pypy/module/cpyext/src/missing.c @@ -27,3 +27,7 @@ int Py_Py3kWarningFlag = 0; int Py_HashRandomizationFlag = 0; +const char *Py_FileSystemDefaultEncoding; /* filled when cpyext is imported */ +void _Py_setfilesystemdefaultencoding(const char *enc) { + Py_FileSystemDefaultEncoding = enc; +} 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 @@ -83,6 +83,35 @@ return cls; } +// for CPython +#ifndef PyMethodDescr_Check +int PyMethodDescr_Check(PyObject* method) +{ + PyObject *meth = PyObject_GetAttrString((PyObject*)&PyList_Type, "append"); + if (!meth) return 0; + int res = PyObject_TypeCheck(method, meth->ob_type); + Py_DECREF(meth); + return res; +} +#endif + +PyObject* make_classmethod(PyObject* method) +{ + // adapted from __Pyx_Method_ClassMethod + if (PyMethodDescr_Check(method)) { + PyMethodDescrObject *descr = (PyMethodDescrObject *)method; + PyTypeObject *d_type = descr->d_type; + return PyDescr_NewClassMethod(d_type, descr->d_method); + } + else if (PyMethod_Check(method)) { + return PyClassMethod_New(PyMethod_GET_FUNCTION(method)); + } + else { + PyErr_SetString(PyExc_TypeError, "unknown method kind"); + return NULL; + } +} + static PyObject * foo_unset(fooobject *self) { @@ -95,6 +124,7 @@ {"copy", (PyCFunction)foo_copy, METH_NOARGS, NULL}, {"create", (PyCFunction)foo_create, METH_NOARGS|METH_STATIC, NULL}, {"classmeth", (PyCFunction)foo_classmeth, METH_NOARGS|METH_CLASS, NULL}, + {"fake_classmeth", (PyCFunction)foo_classmeth, METH_NOARGS, NULL}, {"unset_string_member", (PyCFunction)foo_unset, METH_NOARGS, NULL}, {NULL, NULL} /* sentinel */ }; @@ -167,19 +197,19 @@ /* copied from numpy scalartypes.c for inherited classes */ if (t->tp_bases && (PyTuple_GET_SIZE(t->tp_bases) > 1)) { - PyTypeObject *sup; - /* We are inheriting from a Python type as well so + PyTypeObject *sup; + /* We are inheriting from a Python type as well so give it first dibs on conversion */ sup = (PyTypeObject *)PyTuple_GET_ITEM(t->tp_bases, 1); - /* Prevent recursion */ - if (new_fooType != sup->tp_new) + /* Prevent recursion */ + if (new_fooType != sup->tp_new) { o = sup->tp_new(t, args, kwds); return o; } } o = t->tp_alloc(t, 0); - return o; + return o; }; static PyMemberDef foo_members[] = { @@ -714,7 +744,7 @@ "foo", "Module Doc", -1, - foo_functions, + foo_functions, NULL, NULL, NULL, @@ -748,6 +778,7 @@ #endif { PyObject *d; + PyObject *fake_classmeth, *classmeth; #if PY_MAJOR_VERSION >= 3 PyObject *module = PyModule_Create(&moduledef); #else @@ -805,6 +836,12 @@ INITERROR; gettype2 = PyObject_New(PyObject, &GetType2); + fake_classmeth = PyDict_GetItemString((PyObject *)fooType.tp_dict, "fake_classmeth"); + classmeth = make_classmethod(fake_classmeth); + if (classmeth == NULL) + INITERROR; + if (PyDict_SetItemString((PyObject *)fooType.tp_dict, "fake_classmeth", classmeth) < 0) + INITERROR; d = PyModule_GetDict(module); if (d == NULL) diff --git a/pypy/module/cpyext/test/test_fileobject.py b/pypy/module/cpyext/test/test_fileobject.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_fileobject.py @@ -0,0 +1,13 @@ +from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase + + +class AppTestFileObject(AppTestCpythonExtensionBase): + def test_defaultencoding(self): + import sys + module = self.import_extension('foo', [ + ("defenc", "METH_NOARGS", + """ + return PyString_FromString(Py_FileSystemDefaultEncoding); + """), + ]) + assert module.defenc() == sys.getfilesystemencoding() diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py --- a/pypy/module/cpyext/test/test_typeobject.py +++ b/pypy/module/cpyext/test/test_typeobject.py @@ -130,6 +130,12 @@ assert repr(descr) == "" raises(TypeError, descr, None) + def test_cython_fake_classmethod(self): + module = self.import_module(name='foo') + print(module.fooType.fake_classmeth) + print(type(module.fooType.fake_classmeth)) + assert module.fooType.fake_classmeth() is module.fooType + def test_new(self): # XXX cpython segfaults but if run singly (with -k test_new) this passes module = self.import_module(name='foo') @@ -1356,6 +1362,52 @@ assert Asize == Bsize assert Asize > basesize + def test_multiple_inheritance_bug1(self): + module = self.import_extension('foo', [ + ("get_type", "METH_NOARGS", + ''' + Py_INCREF(&Foo_Type); + return (PyObject *)&Foo_Type; + ''' + ), ("forty_two", "METH_O", + ''' + return PyInt_FromLong(42); + ''' + )], prologue=''' + static PyTypeObject Foo_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "foo.foo", + }; + static PyObject *dummy_new(PyTypeObject *t, PyObject *a, + PyObject *k) + { + abort(); /* never actually called in CPython */ + } + ''', more_init = ''' + Foo_Type.tp_base = (PyTypeObject *)PyExc_Exception; + Foo_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; + Foo_Type.tp_new = dummy_new; + if (PyType_Ready(&Foo_Type) < 0) INITERROR; + ''') + Foo = module.get_type() + class A(Foo, SyntaxError): + pass + assert A.__base__ is SyntaxError + A(42) # assert is not aborting + + class Bar(Exception): + __new__ = module.forty_two + + class B(Bar, SyntaxError): + pass + + assert B() == 42 + + # aaaaa even more hackiness + class C(A): + pass + C(42) # assert is not aborting + class AppTestHashable(AppTestCpythonExtensionBase): def test_unhashable(self): 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 @@ -23,7 +23,6 @@ Py_TPFLAGS_TYPE_SUBCLASS, Py_TPFLAGS_INT_SUBCLASS, Py_TPFLAGS_STRING_SUBCLASS, # change on py3 ) -from pypy.module.cpyext.cparser import parse_source from pypy.module.cpyext.methodobject import (W_PyCClassMethodObject, W_PyCWrapperObject, PyCFunction_NewEx, PyCFunction, PyMethodDef, W_PyCMethodObject, W_PyCFunctionObject) @@ -32,7 +31,7 @@ PyObject, make_ref, from_ref, get_typedescr, make_typedescr, track_reference, Py_DecRef, as_pyobj) from pypy.module.cpyext.slotdefs import ( - slotdefs_for_tp_slots, slotdefs_for_wrappers, get_slot_tp_function, + slotdefs_for_tp_slots, slotdefs_for_wrappers, build_slot_tp_function, llslot) from pypy.module.cpyext.state import State from pypy.module.cpyext.structmember import PyMember_GetOne, PyMember_SetOne @@ -42,6 +41,8 @@ from pypy.objspace.std.typeobject import W_TypeObject, find_best_base +cts.parse_header(parse_dir / "cpyext_descrobject.h") + #WARN_ABOUT_MISSING_SLOT_FUNCTIONS = False PyType_Check, PyType_CheckExact = build_type_checkers_flags("Type") @@ -116,57 +117,24 @@ ) assert not W_MemberDescr.typedef.acceptable_as_base_class # no __new__ -PyDescrObject = lltype.ForwardReference() -PyDescrObjectPtr = lltype.Ptr(PyDescrObject) -PyDescrObjectFields = PyObjectFields + ( - ("d_type", PyTypeObjectPtr), - ("d_name", PyObject), - ) -cpython_struct("PyDescrObject", PyDescrObjectFields, - PyDescrObject) - -PyMemberDescrObjectStruct = lltype.ForwardReference() -PyMemberDescrObject = lltype.Ptr(PyMemberDescrObjectStruct) -PyMemberDescrObjectFields = PyDescrObjectFields + ( - ("d_member", lltype.Ptr(PyMemberDef)), - ) -cpython_struct("PyMemberDescrObject", PyMemberDescrObjectFields, - PyMemberDescrObjectStruct, level=2) - -PyGetSetDescrObjectStruct = lltype.ForwardReference() -PyGetSetDescrObject = lltype.Ptr(PyGetSetDescrObjectStruct) -PyGetSetDescrObjectFields = PyDescrObjectFields + ( - ("d_getset", lltype.Ptr(PyGetSetDef)), - ) -cpython_struct("PyGetSetDescrObject", PyGetSetDescrObjectFields, - PyGetSetDescrObjectStruct, level=2) - -PyMethodDescrObjectStruct = lltype.ForwardReference() -PyMethodDescrObject = lltype.Ptr(PyMethodDescrObjectStruct) -PyMethodDescrObjectFields = PyDescrObjectFields + ( - ("d_method", lltype.Ptr(PyMethodDef)), - ) -cpython_struct("PyMethodDescrObject", PyMethodDescrObjectFields, - PyMethodDescrObjectStruct, level=2) - @bootstrap_function def init_memberdescrobject(space): make_typedescr(W_MemberDescr.typedef, - basestruct=PyMemberDescrObject.TO, + basestruct=cts.gettype('PyMemberDescrObject'), attach=memberdescr_attach, realize=memberdescr_realize, ) make_typedescr(W_GetSetPropertyEx.typedef, - basestruct=PyGetSetDescrObject.TO, + basestruct=cts.gettype('PyGetSetDescrObject'), attach=getsetdescr_attach, ) make_typedescr(W_PyCClassMethodObject.typedef, - basestruct=PyMethodDescrObject.TO, + basestruct=cts.gettype('PyMethodDescrObject'), attach=methoddescr_attach, realize=classmethoddescr_realize, ) make_typedescr(W_PyCMethodObject.typedef, - basestruct=PyMethodDescrObject.TO, + basestruct=cts.gettype('PyMethodDescrObject'), attach=methoddescr_attach, realize=methoddescr_realize, ) @@ -176,7 +144,7 @@ Fills a newly allocated PyMemberDescrObject with the given W_MemberDescr object. The values must not be modified. """ - py_memberdescr = rffi.cast(PyMemberDescrObject, py_obj) + py_memberdescr = cts.cast('PyMemberDescrObject*', py_obj) # XXX assign to d_dname, d_type? assert isinstance(w_obj, W_MemberDescr) py_memberdescr.c_d_member = w_obj.member @@ -195,7 +163,7 @@ Fills a newly allocated PyGetSetDescrObject with the given W_GetSetPropertyEx object. The values must not be modified. """ - py_getsetdescr = rffi.cast(PyGetSetDescrObject, py_obj) + py_getsetdescr = cts.cast('PyGetSetDescrObject*', py_obj) if isinstance(w_obj, GetSetProperty): py_getsetdef = make_GetSet(space, w_obj) assert space.isinstance_w(w_userdata, space.w_type) @@ -207,7 +175,7 @@ py_getsetdescr.c_d_getset = w_obj.getset def methoddescr_attach(space, py_obj, w_obj, w_userdata=None): - py_methoddescr = rffi.cast(PyMethodDescrObject, py_obj) + py_methoddescr = cts.cast('PyMethodDescrObject*', py_obj) # XXX assign to d_dname, d_type? assert isinstance(w_obj, W_PyCFunctionObject) py_methoddescr.c_d_method = w_obj.ml @@ -258,7 +226,30 @@ dict_w[name] = w_descr i += 1 +SLOTS = {} + at specialize.memo() +def get_slot_tp_function(space, typedef, name): + """Return a description of the slot C function to use for the built-in + type for 'typedef'. The 'name' is the slot name. This is a memo + function that, after translation, returns one of a built-in finite set. + """ + key = (typedef, name) + try: + return SLOTS[key] + except KeyError: + slot_func = build_slot_tp_function(space, typedef, name) + api_func = slot_func.api_func if slot_func else None + SLOTS[key] = api_func + return api_func + missing_slots={} +def warn_missing_slot(space, method_name, slot_name, w_type): + if not we_are_translated(): + if slot_name not in missing_slots: + missing_slots[slot_name] = w_type.getname(space) + print "missing slot %r/%r, discovered on %r" % ( + method_name, slot_name, w_type.getname(space)) + def update_all_slots(space, w_type, pto): # fill slots in pto # Not very sure about it, but according to @@ -266,87 +257,78 @@ # overwrite slots that are already set: these ones are probably # coming from a parent C type. - if w_type.is_heaptype(): - typedef = None - search_dict_w = w_type.dict_w - else: - typedef = w_type.layout.typedef - search_dict_w = None - for method_name, slot_name, slot_names, slot_apifunc in slotdefs_for_tp_slots: slot_func_helper = None - if search_dict_w is None: - # built-in types: expose as many slots as possible, even - # if it happens to come from some parent class - slot_apifunc = None # use get_slot_tp_function + w_descr = w_type.dict_w.get(method_name, None) + if w_descr: + # use the slot_apifunc (userslots) to lookup at runtime + pass + elif len(slot_names) ==1: + # 'inherit' from tp_base + slot_func_helper = getattr(pto.c_tp_base, slot_names[0]) else: - # For heaptypes, w_type.layout.typedef will be object's typedef, and - # get_slot_tp_function will fail - w_descr = search_dict_w.get(method_name, None) - if w_descr: - # use the slot_apifunc (userslots) to lookup at runtime - pass - elif len(slot_names) ==1: - # 'inherit' from tp_base - slot_func_helper = getattr(pto.c_tp_base, slot_names[0]) - else: - struct = getattr(pto.c_tp_base, slot_names[0]) - if struct: - slot_func_helper = getattr(struct, slot_names[1]) + struct = getattr(pto.c_tp_base, slot_names[0]) + if struct: + slot_func_helper = getattr(struct, slot_names[1]) if not slot_func_helper: - if typedef is not None: - if slot_apifunc is None: - slot_apifunc = get_slot_tp_function(space, typedef, slot_name) if not slot_apifunc: - if not we_are_translated(): - if slot_name not in missing_slots: - missing_slots[slot_name] = w_type.getname(space) - print "missing slot %r/%r, discovered on %r" % ( - method_name, slot_name, w_type.getname(space)) + warn_missing_slot(space, method_name, slot_name, w_type) continue slot_func_helper = slot_apifunc.get_llhelper(space) + fill_slot(space, pto, w_type, slot_names, slot_func_helper) - # XXX special case wrapper-functions and use a "specific" slot func +def update_all_slots_builtin(space, w_type, pto): + typedef = w_type.layout.typedef + for method_name, slot_name, slot_names, slot_apifunc in slotdefs_for_tp_slots: + slot_apifunc = get_slot_tp_function(space, typedef, slot_name) + if not slot_apifunc: + warn_missing_slot(space, method_name, slot_name, w_type) + continue + slot_func_helper = slot_apifunc.get_llhelper(space) + fill_slot(space, pto, w_type, slot_names, slot_func_helper) - if len(slot_names) == 1: - if not getattr(pto, slot_names[0]): - setattr(pto, slot_names[0], slot_func_helper) - elif ((w_type is space.w_list or w_type is space.w_tuple) and - slot_names[0] == 'c_tp_as_number'): - # XXX hack - how 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 - elif (space.issubtype_w(w_type, space.w_basestring) and - slot_names[0] == 'c_tp_as_number'): - # like above but for any str type - pass - else: - assert len(slot_names) == 2 - struct = getattr(pto, slot_names[0]) - if not struct: - #assert not space.config.translating - assert not pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE - if slot_names[0] == 'c_tp_as_number': - STRUCT_TYPE = PyNumberMethods - elif slot_names[0] == 'c_tp_as_sequence': - STRUCT_TYPE = PySequenceMethods - elif slot_names[0] == 'c_tp_as_buffer': - STRUCT_TYPE = PyBufferProcs - elif slot_names[0] == 'c_tp_as_mapping': - STRUCT_TYPE = PyMappingMethods - else: - raise AssertionError( - "Structure not allocated: %s" % (slot_names[0],)) - struct = lltype.malloc(STRUCT_TYPE, flavor='raw', zero=True) - setattr(pto, slot_names[0], struct) + at specialize.arg(3) +def fill_slot(space, pto, w_type, slot_names, slot_func_helper): + # XXX special case wrapper-functions and use a "specific" slot func + if len(slot_names) == 1: + if not getattr(pto, slot_names[0]): + setattr(pto, slot_names[0], slot_func_helper) + elif ((w_type is space.w_list or w_type is space.w_tuple) and + slot_names[0] == 'c_tp_as_number'): + # XXX hack - how 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 + elif (space.issubtype_w(w_type, space.w_basestring) and + slot_names[0] == 'c_tp_as_number'): + # like above but for any str type + pass + else: + assert len(slot_names) == 2 + struct = getattr(pto, slot_names[0]) + if not struct: + #assert not space.config.translating + assert not pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE + if slot_names[0] == 'c_tp_as_number': + STRUCT_TYPE = PyNumberMethods + elif slot_names[0] == 'c_tp_as_sequence': + STRUCT_TYPE = PySequenceMethods + elif slot_names[0] == 'c_tp_as_buffer': + STRUCT_TYPE = PyBufferProcs + elif slot_names[0] == 'c_tp_as_mapping': + STRUCT_TYPE = PyMappingMethods + else: + raise AssertionError( + "Structure not allocated: %s" % (slot_names[0],)) + struct = lltype.malloc(STRUCT_TYPE, flavor='raw', zero=True) + setattr(pto, slot_names[0], struct) - if not getattr(struct, slot_names[1]): - setattr(struct, slot_names[1], slot_func_helper) + if not getattr(struct, slot_names[1]): + setattr(struct, slot_names[1], slot_func_helper) def add_operators(space, dict_w, pto): from pypy.module.cpyext.object import PyObject_HashNotImplemented @@ -426,6 +408,10 @@ ptr = get_new_method_def(space) ptr.c_ml_meth = rffi.cast(PyCFunction, llslot(space, tp_new_wrapper)) + at jit.dont_look_inside +def is_tp_new_wrapper(space, ml): + return ml.c_ml_meth == rffi.cast(PyCFunction, llslot(space, tp_new_wrapper)) + def add_tp_new_wrapper(space, dict_w, pto): if "__new__" in dict_w: return @@ -452,7 +438,7 @@ pto.c_tp_flags |= Py_TPFLAGS_INT_SUBCLASS elif space.issubtype_w(w_obj, space.w_long): pto.c_tp_flags |= Py_TPFLAGS_LONG_SUBCLASS - elif space.issubtype_w(w_obj, space.w_bytes): + elif space.issubtype_w(w_obj, space.w_bytes): pto.c_tp_flags |= Py_TPFLAGS_STRING_SUBCLASS # STRING->BYTES on py3 elif space.issubtype_w(w_obj, space.w_unicode): pto.c_tp_flags |= Py_TPFLAGS_UNICODE_SUBCLASS @@ -562,25 +548,18 @@ pto = obj.c_ob_type base = pto this_func_ptr = llslot(space, subtype_dealloc) - w_obj = from_ref(space, rffi.cast(PyObject, base)) # This wrapper is created on a specific type, call it w_A. # We wish to call the dealloc function from one of the base classes of w_A, # the first of which is not this function itself. # w_obj is an instance of w_A or one of its subclasses. So climb up the # inheritance chain until base.c_tp_dealloc is exactly this_func, and then # continue on up until they differ. - #print 'subtype_dealloc, start from', rffi.charp2str(base.c_tp_name) while base.c_tp_dealloc != this_func_ptr: base = base.c_tp_base assert base - #print ' ne move to', rffi.charp2str(base.c_tp_name) - w_obj = from_ref(space, rffi.cast(PyObject, base)) while base.c_tp_dealloc == this_func_ptr: base = base.c_tp_base assert base - #print ' eq move to', rffi.charp2str(base.c_tp_name) - w_obj = from_ref(space, rffi.cast(PyObject, base)) - #print ' end with', rffi.charp2str(base.c_tp_name) dealloc = base.c_tp_dealloc # XXX call tp_del if necessary generic_cpy_call(space, dealloc, obj) @@ -807,7 +786,10 @@ if pto.c_tp_itemsize < pto.c_tp_base.c_tp_itemsize: pto.c_tp_itemsize = pto.c_tp_base.c_tp_itemsize - update_all_slots(space, w_type, pto) + if w_type.is_heaptype(): + update_all_slots(space, w_type, pto) + else: + update_all_slots_builtin(space, w_type, pto) if not pto.c_tp_new: base_object_pyo = make_ref(space, space.w_object) base_object_pto = rffi.cast(PyTypeObjectPtr, base_object_pyo) diff --git a/pypy/module/pypyjit/policy.py b/pypy/module/pypyjit/policy.py --- a/pypy/module/pypyjit/policy.py +++ b/pypy/module/pypyjit/policy.py @@ -14,7 +14,7 @@ return True if '.' in modname: modname, rest = modname.split('.', 1) - if modname in ['unicodedata', 'gc', '_minimal_curses', 'cpyext']: + if modname in ['unicodedata', 'gc', '_minimal_curses']: return False else: rest = '' 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 @@ -626,6 +626,12 @@ if w_newdescr is None: # see test_crash_mro_without_object_1 raise oefmt(space.w_TypeError, "cannot create '%N' instances", self) + # + # issue #2666 + if space.config.objspace.usemodules.cpyext: + w_newtype, w_newdescr = self.hack_which_new_to_call( + w_newtype, w_newdescr) + # w_newfunc = space.get(w_newdescr, self) if (space.config.objspace.std.newshortcut and not we_are_jitted() and @@ -646,6 +652,46 @@ "__init__() should return None") return w_newobject + def hack_which_new_to_call(self, w_newtype, w_newdescr): + # issue #2666: for cpyext, we need to hack in order to reproduce + # an "optimization" of CPython that actually changes behaviour + # in corner cases. + # + # * Normally, we use the __new__ found in the MRO in the normal way. + # + # * If by chance this __new__ happens to be implemented as a C + # function, then instead, we discard it and use directly + # self.__base__.tp_new. + # + # * Most of the time this is the same (and faster for CPython), but + # it can fail if self.__base__ happens not to be the first base. + # + from pypy.module.cpyext.methodobject import W_PyCFunctionObject + + if isinstance(w_newdescr, W_PyCFunctionObject): + return self._really_hack_which_new_to_call(w_newtype, w_newdescr) + else: + return w_newtype, w_newdescr + + def _really_hack_which_new_to_call(self, w_newtype, w_newdescr): + # This logic is moved in yet another helper function that + # is recursive. We call this only if we see a + # W_PyCFunctionObject. That's a performance optimization + # because in the common case, we won't call any function that + # contains the stack checks. + from pypy.module.cpyext.methodobject import W_PyCFunctionObject + from pypy.module.cpyext.typeobject import is_tp_new_wrapper + + if (isinstance(w_newdescr, W_PyCFunctionObject) and + w_newtype is not self and + is_tp_new_wrapper(self.space, w_newdescr.ml)): + w_bestbase = find_best_base(self.bases_w) + if w_bestbase is not None: + w_newtype, w_newdescr = w_bestbase.lookup_where('__new__') + return w_bestbase._really_hack_which_new_to_call(w_newtype, + w_newdescr) + return w_newtype, w_newdescr + def descr_repr(self, space): w_mod = self.get_module() if w_mod is None or not space.isinstance_w(w_mod, space.w_text): From pypy.commits at gmail.com Tue Oct 3 06:55:52 2017 From: pypy.commits at gmail.com (mattip) Date: Tue, 03 Oct 2017 03:55:52 -0700 (PDT) Subject: [pypy-commit] pypy release-pypy3.5-5.x: merge py3.5 into release Message-ID: <59d36cb8.d23f1c0a.14151.04cf@mx.google.com> Author: Matti Picus Branch: release-pypy3.5-5.x Changeset: r92567:d72f9800a42b Date: 2017-10-03 13:53 +0300 http://bitbucket.org/pypy/pypy/changeset/d72f9800a42b/ Log: merge py3.5 into release diff too long, truncating to 2000 out of 2653 lines diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -60,8 +60,8 @@ Wim Lavrijsen Eric van Riet Paap Richard Emslie + Remi Meier Alexander Schremmer - Remi Meier Dan Villiom Podlaski Christiansen Lukas Diekmann Sven Hager @@ -102,6 +102,7 @@ Michael Foord Stephan Diehl Stefano Rivera + Jean-Paul Calderone Stefan Schwarzer Tomek Meka Valentino Volonghi @@ -110,14 +111,13 @@ Bob Ippolito Bruno Gola David Malcolm - Jean-Paul Calderone Squeaky Edd Barrett Timo Paulssen Marius Gedminas + Nicolas Truessel Alexandre Fayolle Simon Burton - Nicolas Truessel Martin Matusiak Laurence Tratt Wenzhu Man @@ -156,6 +156,7 @@ Stefan H. Muller Tim Felgentreff Eugene Oden + Dodan Mihai Jeff Terrace Henry Mason Vasily Kuznetsov @@ -182,11 +183,13 @@ Rocco Moretti Gintautas Miliauskas Lucian Branescu Mihaila + Mariano Anaya anatoly techtonik - Dodan Mihai Karl Bartel + Stefan Beyer Gabriel Lavoie Jared Grubb + Alecsandru Patrascu Olivier Dormond Wouter van Heyst Sebastian Pawluś @@ -194,6 +197,7 @@ Victor Stinner Andrews Medina Aaron Iles + p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -204,6 +208,7 @@ Michael Cheng Mikael Schönenberg Stanislaw Halik + Mihnea Saracin Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -214,14 +219,12 @@ Jonathan David Riehl Beatrice During Alex Perry - p_zieschang at yahoo.de Robert Zaremba Alan McIntyre Alexander Sedov Vaibhav Sood Reuben Cummings Attila Gobi - Alecsandru Patrascu Christopher Pope Tristan Arthur Christian Tismer @@ -243,7 +246,6 @@ Jacek Generowicz Sylvain Thenault Jakub Stasiak - Stefan Beyer Andrew Dalke Alejandro J. Cura Vladimir Kryachko @@ -275,6 +277,7 @@ Christoph Gerum Miguel de Val Borro Artur Lisiecki + afteryu Toni Mattis Laurens Van Houtven Bobby Impollonia @@ -305,6 +308,7 @@ Anna Katrina Dominguez Kim Jin Su Amber Brown + Anthony Sottile Nate Bragg Ben Darnell Juan Francisco Cantero Hurtado @@ -325,12 +329,14 @@ Mike Bayer Rodrigo Araújo Daniil Yarancev + Min RK OlivierBlanvillain Jonas Pfannschmidt Zearin Andrey Churin Dan Crosta reubano at gmail.com + Stanisław Halik Julien Phalip Roman Podoliaka Eli Stevens 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 @@ -203,7 +203,7 @@ f_locals local namespace seen by this frame f_restricted 0 or 1 if frame is in restricted execution mode f_trace tracing function for this frame, or None""" - return isinstance(object, types.FrameType) + return isinstance(object, (types.FrameType, types.FakeFrameType)) def iscode(object): """Return true if the object is a code object. diff --git a/lib-python/2.7/types.py b/lib-python/2.7/types.py --- a/lib-python/2.7/types.py +++ b/lib-python/2.7/types.py @@ -71,6 +71,12 @@ FrameType = type(tb.tb_frame) del tb +# PyPy extension +try: + FakeFrameType = type(next(sys._current_frames().itervalues())) +except (AttributeError, StopIteration): + FakeFrameType = FrameType + SliceType = slice EllipsisType = type(Ellipsis) diff --git a/lib_pypy/cffi.egg-info/PKG-INFO b/lib_pypy/cffi.egg-info/PKG-INFO --- a/lib_pypy/cffi.egg-info/PKG-INFO +++ b/lib_pypy/cffi.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: cffi -Version: 1.11.0 +Version: 1.11.1 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski diff --git a/lib_pypy/cffi/__init__.py b/lib_pypy/cffi/__init__.py --- a/lib_pypy/cffi/__init__.py +++ b/lib_pypy/cffi/__init__.py @@ -4,8 +4,8 @@ from .api import FFI from .error import CDefError, FFIError, VerificationError, VerificationMissing -__version__ = "1.11.0" -__version_info__ = (1, 11, 0) +__version__ = "1.11.1" +__version_info__ = (1, 11, 1) # The verifier module file names are based on the CRC32 of a string that # contains the following version number. It may be older than __version__ diff --git a/lib_pypy/cffi/_embedding.h b/lib_pypy/cffi/_embedding.h --- a/lib_pypy/cffi/_embedding.h +++ b/lib_pypy/cffi/_embedding.h @@ -247,7 +247,7 @@ if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.11.0" + "\ncompiled with cffi version: 1.11.1" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); diff --git a/lib_pypy/pyrepl/historical_reader.py b/lib_pypy/pyrepl/historical_reader.py --- a/lib_pypy/pyrepl/historical_reader.py +++ b/lib_pypy/pyrepl/historical_reader.py @@ -17,7 +17,7 @@ # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -from pyrepl import reader, commands +from pyrepl import reader, commands, input from pyrepl.reader import Reader as R isearch_keymap = tuple( @@ -215,7 +215,6 @@ isearch_forwards, isearch_backwards, operate_and_get_next]: self.commands[c.__name__] = c self.commands[c.__name__.replace('_', '-')] = c - from pyrepl import input self.isearch_trans = input.KeymapTranslator( isearch_keymap, invalid_cls=isearch_end, character_cls=isearch_add_character) diff --git a/pypy/doc/contributor.rst b/pypy/doc/contributor.rst --- a/pypy/doc/contributor.rst +++ b/pypy/doc/contributor.rst @@ -27,8 +27,8 @@ Wim Lavrijsen Eric van Riet Paap Richard Emslie + Remi Meier Alexander Schremmer - Remi Meier Dan Villiom Podlaski Christiansen Lukas Diekmann Sven Hager @@ -69,6 +69,7 @@ Michael Foord Stephan Diehl Stefano Rivera + Jean-Paul Calderone Stefan Schwarzer Tomek Meka Valentino Volonghi @@ -77,14 +78,13 @@ Bob Ippolito Bruno Gola David Malcolm - Jean-Paul Calderone Squeaky Edd Barrett Timo Paulssen Marius Gedminas + Nicolas Truessel Alexandre Fayolle Simon Burton - Nicolas Truessel Martin Matusiak Laurence Tratt Wenzhu Man @@ -123,6 +123,7 @@ Stefan H. Muller Tim Felgentreff Eugene Oden + Dodan Mihai Jeff Terrace Henry Mason Vasily Kuznetsov @@ -149,11 +150,13 @@ Rocco Moretti Gintautas Miliauskas Lucian Branescu Mihaila + Mariano Anaya anatoly techtonik - Dodan Mihai Karl Bartel + Stefan Beyer Gabriel Lavoie Jared Grubb + Alecsandru Patrascu Olivier Dormond Wouter van Heyst Sebastian Pawluś @@ -161,6 +164,7 @@ Victor Stinner Andrews Medina Aaron Iles + p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -171,6 +175,7 @@ Michael Cheng Mikael Schönenberg Stanislaw Halik + Mihnea Saracin Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -181,14 +186,12 @@ Jonathan David Riehl Beatrice During Alex Perry - p_zieschang at yahoo.de Robert Zaremba Alan McIntyre Alexander Sedov Vaibhav Sood Reuben Cummings Attila Gobi - Alecsandru Patrascu Christopher Pope Tristan Arthur Christian Tismer @@ -210,7 +213,6 @@ Jacek Generowicz Sylvain Thenault Jakub Stasiak - Stefan Beyer Andrew Dalke Alejandro J. Cura Vladimir Kryachko @@ -242,6 +244,7 @@ Christoph Gerum Miguel de Val Borro Artur Lisiecki + afteryu Toni Mattis Laurens Van Houtven Bobby Impollonia @@ -272,6 +275,7 @@ Anna Katrina Dominguez Kim Jin Su Amber Brown + Anthony Sottile Nate Bragg Ben Darnell Juan Francisco Cantero Hurtado @@ -292,12 +296,14 @@ Mike Bayer Rodrigo Araújo Daniil Yarancev + Min RK OlivierBlanvillain Jonas Pfannschmidt Zearin Andrey Churin Dan Crosta reubano at gmail.com + Stanisław Halik Julien Phalip Roman Podoliaka Eli Stevens diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst --- a/pypy/doc/cpython_differences.rst +++ b/pypy/doc/cpython_differences.rst @@ -436,7 +436,8 @@ * the ``__builtins__`` name is always referencing the ``__builtin__`` module, never a dictionary as it sometimes is in CPython. Assigning to - ``__builtins__`` has no effect. + ``__builtins__`` has no effect. (For usages of tools like + RestrictedPython, see `issue #2653`_.) * directly calling the internal magic methods of a few built-in types with invalid arguments may have a slightly different result. For @@ -556,4 +557,4 @@ .. _`is ignored in PyPy`: http://bugs.python.org/issue14621 .. _`little point`: http://events.ccc.de/congress/2012/Fahrplan/events/5152.en.html .. _`#2072`: https://bitbucket.org/pypy/pypy/issue/2072/ - +.. _`issue #2653`: https://bitbucket.org/pypy/pypy/issues/2653/ diff --git a/pypy/doc/index-of-release-notes.rst b/pypy/doc/index-of-release-notes.rst --- a/pypy/doc/index-of-release-notes.rst +++ b/pypy/doc/index-of-release-notes.rst @@ -6,6 +6,7 @@ .. toctree:: + release-v5.9.0.rst release-v5.8.0.rst release-v5.7.1.rst release-v5.7.0.rst diff --git a/pypy/doc/index-of-whatsnew.rst b/pypy/doc/index-of-whatsnew.rst --- a/pypy/doc/index-of-whatsnew.rst +++ b/pypy/doc/index-of-whatsnew.rst @@ -7,6 +7,7 @@ .. toctree:: whatsnew-head.rst + whatsnew-pypy2-5.9.0.rst whatsnew-pypy2-5.8.0.rst whatsnew-pypy2-5.7.0.rst whatsnew-pypy2-5.6.0.rst @@ -36,6 +37,7 @@ .. toctree:: whatsnew-pypy3-head.rst + whatsnew-pypy3-5.9.0.rst whatsnew-pypy3-5.8.0.rst whatsnew-pypy3-5.7.0.rst diff --git a/pypy/doc/release-v5.9.0.rst b/pypy/doc/release-v5.9.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/release-v5.9.0.rst @@ -0,0 +1,217 @@ +===================================== +PyPy2.7 and PyPy3.5 v5.9 dual release +===================================== + +The PyPy team is proud to release both PyPy2.7 v5.9 (an interpreter supporting +Python 2.7 syntax), and a beta-quality PyPy3.5 v5.9 (an interpreter for Python +3.5 syntax). The two releases are both based on much the same codebase, thus +the dual release. Note that PyPy3.5 supports Linux 64bit only for now. + +This new PyPy2.7 release includes the upstream stdlib version 2.7.13, and +PyPy3.5 includes the upstream stdlib version 3.5.3. + +NumPy and Pandas now work on PyPy2.7 (together with Cython 0.27.1). Issues +that appeared as excessive memory +use were cleared up and other incompatibilities were resolved. The C-API +compatibility layer does slow down code which crosses the python-c interface +often, we have ideas on how it could be improved, and still recommend +using pure python on PyPy or interfacing via CFFI_. Many other modules +based on C-API exentions now work on PyPy as well. + +Cython 0.27.1 (released very recently) supports more projects with PyPy, both +on PyPy2.7 and PyPy3.5 beta. Note version **0.27.1** is now the minimum +version that supports this version of PyPy, due to some interactions with +updated C-API interface code. + +We optimized the JSON parser for recurring string keys, which should decrease +memory use to 50% and increase parsing speed by up to 15% for large JSON files +with many repeating dictionary keys (which is quite common). + +CFFI_, which is part of the PyPy release, has been updated to 1.11.1, +improving an already great package for interfacing with C. CFFI now supports +complex arguments in API mode, as well as ``char16_t`` and ``char32_t`` and has +improved support for callbacks. + +Please let us know if your use case is slow, we have ideas how to make things +faster but need real-world examples (not micro-benchmarks) of problematic code. + +Work sponsored by a Mozilla grant_ continues on PyPy3.5; numerous fixes from +CPython were ported to PyPy. Of course the bug fixes and performance enhancements +mentioned above are part of both PyPy2.7 and PyPy3.5 beta. + +As always, this release fixed many other issues and bugs raised by the +growing community of PyPy users. We strongly recommend updating. + +You can download the v5.9 releases here: + + http://pypy.org/download.html + +We would like to thank our donors for the continued support of the PyPy +project. + +We would also like to thank our contributors and +encourage new people to join the project. PyPy has many +layers and we need help with all of them: `PyPy`_ and `RPython`_ documentation +improvements, tweaking popular `modules`_ to run on pypy, or general `help`_ +with making RPython's JIT even better. + +.. _vmprof: http://vmprof.readthedocs.io +.. _CFFI: https://cffi.readthedocs.io/en/latest/whatsnew.html +.. _grant: https://morepypy.blogspot.com/2016/08/pypy-gets-funding-from-mozilla-for.html +.. _`PyPy`: index.html +.. _`RPython`: https://rpython.readthedocs.org +.. _`modules`: project-ideas.html#make-more-python-modules-pypy-friendly +.. _`help`: project-ideas.html + +What is PyPy? +============= + +PyPy is a very compliant Python interpreter, almost a drop-in replacement for +CPython 2.7 and CPython 3.5. It's fast (`PyPy and CPython 2.7.x`_ performance comparison) +due to its integrated tracing JIT compiler. + +We also welcome developers of other `dynamic languages`_ to see what RPython +can do for them. + +The PyPy 2.7 release supports: + + * **x86** machines on most common operating systems + (Linux 32/64 bits, Mac OS X 64 bits, Windows 32 bits, OpenBSD, FreeBSD) + + * newer **ARM** hardware (ARMv6 or ARMv7, with VFPv3) running Linux, + + * big- and little-endian variants of **PPC64** running Linux, + + * **s390x** running Linux + +.. _`PyPy and CPython 2.7.x`: http://speed.pypy.org +.. _`dynamic languages`: http://rpython.readthedocs.io/en/latest/examples.html + +Highlights of the PyPy2.7, cpyext, and RPython changes (since 5.8 released June, 2017) +====================================================================================== + +See also issues that were resolved_ + +Note that these are also merged into PyPy 3.5 + +* New features and cleanups + + * Add support for ``PyFrozenSet_New``, ``PyObject_HashNotImplemented``, + ``PyObject_Print(NULL, ...)``, ``PyObject_RichCompareBool(a, a, ...)``, + ``PyType_IS_GC`` (does nothing), ``PyUnicode_FromFormat`` + * ctypes ``char_p`` and ``unichar_p`` indexing now CPython compatible + * ``gcdump`` now reports largest object + * More complete support in the ``_curses`` CFFI module + * Add cPickle.Unpickler.find_global (issue 1853_) + * Fix ``PyErr_Fetch`` + ``PyErr_NormalizeException`` with no exception set + * Simplify ``gc.get_referrers()`` to return the opposite of ``gc.get_referents()`` + * Update RevDB to version pypy2.7-v5.6.2 + * Previously, ``instance.method`` would return always the same bound method + object, when gotten from the same instance (as far as ``is`` and ``id()`` + can tell). CPython doesn't do that. Now PyPy, like CPython, returns a + different bound method object every time. For ``type.method``, PyPy2 still + returns always the same *unbound* method object; CPython does it for built-in + types but not for user-defined types + * Link to disable PaX protection for the JIT when needed + * Update build instructions and an rarely used Makefile + * Recreate support for using leakfinder in cpyext tests which had suffered + bit-rot, disable due to many false positives + * Add more functionality to ``sysconfig`` + * Added ``_swappedbytes_`` support for ``ctypes.Structure`` + * Better support the ``inspect`` module on ``frames`` + +* Bug Fixes + + * Fix issue 2592_ - cpyext ``PyListObject.pop``, ``pop_end`` must return a value + * Implement ``PyListOjbect.getstorage_copy`` + * Fix for ``reversed(dictproxy)`` issue 2601_ + * Fix for duplicate names in ctypes' ``_fields__``, issue 2621_ + * Update built-in ``pyexpat`` module on win32 to use UTF-8 version not UTF-16 + * ``gc.get_objects`` now handles objects with finalizers more consistently + * Fixed memory leak in ``SSLContext.getpeercert`` returning validated + certificates and ``SSLContext.get_ca_certs(binary_mode=True)`` + (_get_crl_dp) `CPython issue 29738`_ + +* Performance improvements: + + * Improve performance of ``bytearray.extend`` by rewriting portions in app-level + * Optimize list accesses with constant indexes better by retaining more + information about them + * Add a jit driver for ``array.count`` and ``array.index`` + * Improve information retained in a bridge wrt ``array`` + * Move some dummy CAPI functions and ``Py*_Check`` functions from RPython into + pure C macros + * In the fast ``zip(intlist1, intlist2)`` implementation, don't wrap and unwrap + all the ints + * Cache string keys that occur in JSON dicts, as they are likely to repeat + +* RPython improvements + + * Do not preallocate a RPython list if we only know an upper bound on its size + * Issue 2590_: fix the bounds in the GC when allocating a lot of objects with finalizers + * Replace magical NOT RPYTHON comment with a decorator + * Implement ``socket.sendmsg()``/``.recvmsg()`` for py3.5 + * Add ``memory_pressure`` for ``_SSLSocket`` objects + +* Degredations + + * Disable vmprof on win32, due to upstream changes that break the internal ``_vmprof`` module + +.. _here: cpython_differences.html +.. _1853: https://bitbucket.org/pypy/pypy/issues/1853 +.. _2592: https://bitbucket.org/pypy/pypy/issues/2592 +.. _2590: https://bitbucket.org/pypy/pypy/issues/2590 +.. _2621: https://bitbucket.org/pypy/pypy/issues/2621 + +Highlights of the PyPy3.5 release (since 5.8 beta released June 2017) +====================================================================== + +* New features + + * Add support for ``_PyNamespace_New``, ``PyMemoryView_FromMemory``, + ``Py_EnterRecursiveCall`` raising RecursionError, ``PyObject_LengthHint``, + ``PyUnicode_FromKindAndData``, ``PyDict_SetDefault``, ``PyGenObject``, + ``PyGenObject``, ``PyUnicode_Substring``, ``PyLong_FromUnicodeObject`` + * Implement ``PyType_FromSpec`` (PEP 384) and fix issues with PEP 489 support + * Support the new version of ``os.stat()`` on win32 + * Use ``stat3()`` on Posix + * Accept buffer objects as filenames, except for `oslistdir`` + * Make slices of array ``memoryview`` s usable as writable buffers if contiguous + * Better handling of ``'%s'`` formatting for byte strings which might be utf-8 encoded + * Update the macros ``Py_DECREF`` and similar to use the CPython 3.5 version + * Ensure that ``mappingproxy`` is recognised as a mapping, not a sequence + * Enable PGO for CLang + * Rework ``cppyy`` packaging and rename the backend to ``_cppyy`` + * Support for libressl 2.5.4 + * Mirror CPython ``classmethod __reduce__`` which fixes pickling test + * Use utf-8 for ``readline`` history file + * Allow assigning ``'__class__'`` between ``ModuleType`` and its subclasses + * Add async slot functions in cpyext + +* Bug Fixes + + * Try to make ``openssl`` CFFI bindings more general and future-proof + * Better support ``importlib`` by only listing built-in modules in ``sys.builtin`` + * Add ``memory_pressure`` to large CFFI allocations in ``_lzma``, issue 2579_ + * Fix for ``reversed(mapping object)`` issue 2601_ + * Fixing regression with non-started generator receiving non-``None``, should + always raise ``TypeError`` + * ``itertools.islice``: use same logic as CPython, fixes 2643_ + +* Performance improvements: + + * + +* The following features of Python 3.5 are not implemented yet in PyPy: + + * PEP 442: Safe object finalization + +.. _resolved: whatsnew-pypy2-5.9.0.html +.. _2579: https://bitbucket.org/pypy/pypy/issues/2579 +.. _2601: https://bitbucket.org/pypy/pypy/issues/2601 +.. _2643: https://bitbucket.org/pypy/pypy/issues/2643 +.. _CPython issue 29738: https://bugs.python.org/issue29738 + +Please update, and continue to help us make PyPy better. + +Cheers 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 @@ -1,87 +1,15 @@ -========================== -What's new in PyPy2.7 5.9+ -========================== - -.. this is a revision shortly after release-pypy2.7-v5.8.0 -.. startrev: 558bd00b3dd8 - -In previous versions of PyPy, ``instance.method`` would return always -the same bound method object, when gotten out of the same instance (as -far as ``is`` and ``id()`` can tell). CPython doesn't do that. Now -PyPy, like CPython, returns a different bound method object every time. -For ``type.method``, PyPy2 still returns always the same *unbound* -method object; CPython does it for built-in types but not for -user-defined types. - -.. branch: cffi-complex -.. branch: cffi-char16-char32 - -The two ``cffi-*`` branches are part of the upgrade to cffi 1.11. - -.. branch: ctypes_char_indexing - -Indexing into char* behaves differently than CPython - -.. branch: vmprof-0.4.8 - -Improve and fix issues with vmprof - -.. branch: issue-2592 - -CPyext PyListObject.pop must return the value - -.. branch: cpyext-hash_notimpl - -If ``tp_hash`` is ``PyObject_HashNotImplemented``, set ``obj.__dict__['__hash__']`` to None - -.. branch: cppyy-packaging - -Renaming of ``cppyy`` to ``_cppyy``. -The former is now an external package installable with ``pip install cppyy``. - -.. branch: Enable_PGO_for_clang - -.. branch: nopax - -At the end of translation, run ``attr -q -s pax.flags -V m`` on -PAX-enabled systems on the produced binary. This seems necessary -because PyPy uses a JIT. - -.. branch: pypy_bytearray - -Improve ``bytearray`` performance (backported from py3.5) - -.. branch: gc-del-limit-growth - -Fix the bounds in the GC when allocating a lot of objects with finalizers, -fixes issue #2590 - -.. branch: arrays-force-less - -Small improvement to optimize list accesses with constant indexes better by -throwing away information about them less eagerly. - - -.. branch: getarrayitem-into-bridges - -More information is retained into a bridge: knowledge about the content of -arrays (at fixed indices) is stored in guards (and thus available at the -beginning of bridges). Also, some better feeding of information about known -fields of constant objects into bridges. - -.. branch: cpyext-leakchecking - -Add support for leakfinder in cpyext tests (disabled for now, due to too many -failures). - -.. branch: pypy_swappedbytes - -Added ``_swappedbytes_`` support for ``ctypes.Structure`` - -.. branch: pycheck-macros - -Convert many Py*_Check cpyext functions into macros, like CPython. - -.. branch: py_ssize_t - -Explicitly use Py_ssize_t as the Signed type in pypy c-api +=========================== +What's new in PyPy2.7 5.10+ +=========================== + +.. this is a revision shortly after release-pypy2.7-v5.9.0 +.. startrev:899e5245de1e + +.. branch: cpyext-jit + +Differentiate the code to call METH_NOARGS, METH_O and METH_VARARGS in cpyext: +this allows to write specialized code which is much faster than previous +completely generic version. Moreover, let the JIT to look inside the cpyext +module: the net result is that cpyext calls are up to 7x faster. However, this +is true only for very simple situations: in all real life code, we are still +much slower than CPython (more optimizations to come) diff --git a/pypy/doc/whatsnew-pypy2-5.9.0.rst b/pypy/doc/whatsnew-pypy2-5.9.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/whatsnew-pypy2-5.9.0.rst @@ -0,0 +1,87 @@ +========================= +What's new in PyPy2.7 5.9 +========================= + +.. this is a revision shortly after release-pypy2.7-v5.8.0 +.. startrev: 558bd00b3dd8 + +In previous versions of PyPy, ``instance.method`` would return always +the same bound method object, when gotten out of the same instance (as +far as ``is`` and ``id()`` can tell). CPython doesn't do that. Now +PyPy, like CPython, returns a different bound method object every time. +For ``type.method``, PyPy2 still returns always the same *unbound* +method object; CPython does it for built-in types but not for +user-defined types. + +.. branch: cffi-complex +.. branch: cffi-char16-char32 + +The two ``cffi-*`` branches are part of the upgrade to cffi 1.11. + +.. branch: ctypes_char_indexing + +Indexing into char* behaves differently than CPython + +.. branch: vmprof-0.4.8 + +Improve and fix issues with vmprof + +.. branch: issue-2592 + +CPyext PyListObject.pop must return the value + +.. branch: cpyext-hash_notimpl + +If ``tp_hash`` is ``PyObject_HashNotImplemented``, set ``obj.__dict__['__hash__']`` to None + +.. branch: cppyy-packaging + +Renaming of ``cppyy`` to ``_cppyy``. +The former is now an external package installable with ``pip install cppyy``. + +.. branch: Enable_PGO_for_clang + +.. branch: nopax + +At the end of translation, run ``attr -q -s pax.flags -V m`` on +PAX-enabled systems on the produced binary. This seems necessary +because PyPy uses a JIT. + +.. branch: pypy_bytearray + +Improve ``bytearray`` performance (backported from py3.5) + +.. branch: gc-del-limit-growth + +Fix the bounds in the GC when allocating a lot of objects with finalizers, +fixes issue #2590 + +.. branch: arrays-force-less + +Small improvement to optimize list accesses with constant indexes better by +throwing away information about them less eagerly. + + +.. branch: getarrayitem-into-bridges + +More information is retained into a bridge: knowledge about the content of +arrays (at fixed indices) is stored in guards (and thus available at the +beginning of bridges). Also, some better feeding of information about known +fields of constant objects into bridges. + +.. branch: cpyext-leakchecking + +Add support for leakfinder in cpyext tests (disabled for now, due to too many +failures). + +.. branch: pypy_swappedbytes + +Added ``_swappedbytes_`` support for ``ctypes.Structure`` + +.. branch: pycheck-macros + +Convert many Py*_Check cpyext functions into macros, like CPython. + +.. branch: py_ssize_t + +Explicitly use Py_ssize_t as the Signed type in pypy c-api diff --git a/pypy/doc/whatsnew-pypy3-5.9.0.rst b/pypy/doc/whatsnew-pypy3-5.9.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/whatsnew-pypy3-5.9.0.rst @@ -0,0 +1,7 @@ +======================= +What's new in PyPy3 5.9 +======================= + +.. this is the revision after release-pypy3.5-5.8 +.. startrev: afbf09453369 + diff --git a/pypy/doc/whatsnew-pypy3-head.rst b/pypy/doc/whatsnew-pypy3-head.rst --- a/pypy/doc/whatsnew-pypy3-head.rst +++ b/pypy/doc/whatsnew-pypy3-head.rst @@ -1,9 +1,9 @@ ========================= -What's new in PyPy3 5.8+ +What's new in PyPy3 5.9+ ========================= -.. this is the revision after release-pypy3.3-5.8.x was branched -.. startrev: c173df164527 +.. this is the revision after release-pypy3.5-5.9 +.. startrev: be41e3ac0a29 .. branch: multiphase diff --git a/pypy/interpreter/test/test_app_main.py b/pypy/interpreter/test/test_app_main.py --- a/pypy/interpreter/test/test_app_main.py +++ b/pypy/interpreter/test/test_app_main.py @@ -1162,9 +1162,13 @@ import os old_sys_path = sys.path[:] sys.path.append(self.goal_dir) + if sys.platform == 'win32': + exename = 'pypy3-c.exe' + else: + exename = 'pypy3-c' try: import app_main - pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', 'pypy3-c') + pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', exename) app_main.setup_bootstrap_path(pypy_c) newpath = sys.path[:] # we get at least lib_pypy @@ -1180,9 +1184,13 @@ import os old_sys_path = sys.path[:] sys.path.append(self.goal_dir) + if sys.platform == 'win32': + exename = 'pypy3-c.exe' + else: + exename = 'pypy3-c' try: import app_main - pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', 'pypy3-c') + pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', exename) app_main.entry_point(pypy_c, [self.foo_py]) # assert it did not crash finally: diff --git a/pypy/module/__builtin__/test/test_descriptor.py b/pypy/module/__builtin__/test/test_descriptor.py --- a/pypy/module/__builtin__/test/test_descriptor.py +++ b/pypy/module/__builtin__/test/test_descriptor.py @@ -258,6 +258,17 @@ raises(TypeError, "super(D).__get__(12)") raises(TypeError, "super(D).__get__(C())") + def test_super_incomplete(self): + """ + class M(type): + def mro(cls): + if cls.__mro__ is None: + raises(AttributeError, lambda: super(cls, cls).xxx) + return type.mro(cls) + class A(metaclass=M): + pass + """ + def test_classmethods_various(self): class C(object): def foo(*a): return a diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py --- a/pypy/module/_cffi_backend/__init__.py +++ b/pypy/module/_cffi_backend/__init__.py @@ -3,7 +3,7 @@ from rpython.rlib import rdynload, clibffi from rpython.rtyper.lltypesystem import rffi -VERSION = "1.11.0" +VERSION = "1.11.1" FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI try: diff --git a/pypy/module/_cffi_backend/cffi1_module.py b/pypy/module/_cffi_backend/cffi1_module.py --- a/pypy/module/_cffi_backend/cffi1_module.py +++ b/pypy/module/_cffi_backend/cffi1_module.py @@ -1,4 +1,5 @@ from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rlib import jit from pypy.interpreter.error import oefmt from pypy.interpreter.module import Module @@ -15,7 +16,7 @@ INITFUNCPTR = lltype.Ptr(lltype.FuncType([rffi.VOIDPP], lltype.Void)) - + at jit.dont_look_inside def load_cffi1_module(space, name, path, initptr): # This is called from pypy.module.cpyext.api.load_extension_module() from pypy.module._cffi_backend.call_python import get_ll_cffi_call_python diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py --- a/pypy/module/_cffi_backend/ctypeptr.py +++ b/pypy/module/_cffi_backend/ctypeptr.py @@ -158,10 +158,11 @@ class W_CTypePtrBase(W_CTypePtrOrArray): # base class for both pointers and pointers-to-functions - _attrs_ = ['is_void_ptr', 'is_voidchar_ptr'] - _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr'] + _attrs_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] + _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] is_void_ptr = False is_voidchar_ptr = False + is_onebyte_ptr = False def convert_to_object(self, cdata): ptrdata = rffi.cast(rffi.CCHARPP, cdata)[0] @@ -181,12 +182,20 @@ if self.is_void_ptr or other.is_void_ptr: pass # cast from or to 'void *' elif self.is_voidchar_ptr or other.is_voidchar_ptr: - space = self.space - msg = ("implicit cast from '%s' to '%s' " - "will be forbidden in the future (check that the types " - "are as you expect; use an explicit ffi.cast() if they " - "are correct)" % (other.name, self.name)) - space.warn(space.newtext(msg), space.w_UserWarning) + # for backward compatibility, accept "char *" as either + # source of target. This is not what C does, though, + # so emit a warning that will eventually turn into an + # error. The warning is turned off if both types are + # pointers to single bytes. + if self.is_onebyte_ptr and other.is_onebyte_ptr: + pass # no warning + else: + space = self.space + msg = ("implicit cast from '%s' to '%s' " + "will be forbidden in the future (check that the types " + "are as you expect; use an explicit ffi.cast() if they " + "are correct)" % (other.name, self.name)) + space.warn(space.newtext(msg), space.w_UserWarning) else: raise self._convert_error("compatible pointer", w_ob) @@ -216,6 +225,7 @@ self.is_void_ptr = isinstance(ctitem, ctypevoid.W_CTypeVoid) self.is_voidchar_ptr = (self.is_void_ptr or isinstance(ctitem, ctypeprim.W_CTypePrimitiveChar)) + self.is_onebyte_ptr = (ctitem.size == 1) W_CTypePtrBase.__init__(self, space, size, extra, 2, ctitem) def newp(self, w_init, allocator): diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -1,7 +1,7 @@ # ____________________________________________________________ import sys -assert __version__ == "1.11.0", ("This test_c.py file is for testing a version" +assert __version__ == "1.11.1", ("This test_c.py file is for testing a version" " of cffi that differs from the one that we" " get from 'import _cffi_backend'") if sys.version_info < (3,): @@ -2099,7 +2099,8 @@ if sys.platform.startswith("linux"): BWChar = new_primitive_type("wchar_t") assert sizeof(BWChar) == 4 - assert int(cast(BWChar, -1)) == -1 # signed, on linux + # wchar_t is often signed on Linux, but not always (e.g. on ARM) + assert int(cast(BWChar, -1)) in (-1, 4294967295) def test_char16(): BChar16 = new_primitive_type("char16_t") @@ -3903,9 +3904,11 @@ BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BVoidP = new_pointer_type(new_void_type()) + BUCharP = new_pointer_type(new_primitive_type("unsigned char")) z1 = cast(BCharP, 0) z2 = cast(BIntP, 0) z3 = cast(BVoidP, 0) + z4 = cast(BUCharP, 0) with warnings.catch_warnings(record=True) as w: newp(new_pointer_type(BIntP), z1) # warn assert len(w) == 1 @@ -3919,6 +3922,12 @@ assert len(w) == 2 newp(new_pointer_type(BIntP), z3) # fine assert len(w) == 2 + newp(new_pointer_type(BCharP), z4) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z1) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z3) # fine + assert len(w) == 2 # check that the warnings are associated with lines in this file assert w[1].lineno == w[0].lineno + 4 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 @@ -1,6 +1,6 @@ import sys from rpython.rlib.rstring import StringBuilder -from rpython.rlib.objectmodel import specialize, always_inline +from rpython.rlib.objectmodel import specialize, always_inline, r_dict from rpython.rlib import rfloat, runicode from rpython.rtyper.lltypesystem import lltype, rffi from pypy.interpreter.error import oefmt, OperationError @@ -42,6 +42,22 @@ ll_res.chars[i] = cast_primitive(UniChar, ch) return hlunicode(ll_res) +def slice_eq(a, b): + (ll_chars1, start1, length1, _) = a + (ll_chars2, start2, length2, _) = b + if length1 != length2: + return False + j = start2 + for i in range(start1, start1 + length1): + if ll_chars1[i] != ll_chars2[j]: + return False + j += 1 + return True + +def slice_hash(a): + (ll_chars, start, length, h) = a + return h + class DecoderError(Exception): def __init__(self, msg, pos): self.msg = msg @@ -60,8 +76,7 @@ self.ll_chars = rffi.str2charp(s) self.end_ptr = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw') self.pos = 0 - self.last_type = TYPE_UNKNOWN - self.memo = {} + self.cache = r_dict(slice_eq, slice_hash) def close(self): rffi.free_charp(self.ll_chars) @@ -249,21 +264,16 @@ def decode_object(self, i): start = i - w_dict = self.space.newdict() - # + i = self.skip_whitespace(i) if self.ll_chars[i] == '}': self.pos = i+1 - return w_dict - # + return self.space.newdict() + + d = {} while True: # parse a key: value - self.last_type = TYPE_UNKNOWN - w_name = self.decode_any(i) - if self.last_type != TYPE_STRING: - raise DecoderError("Key name must be string for object starting at", start) - w_name = self.memo.setdefault(self.space.unicode_w(w_name), w_name) - + name = self.decode_key(i) i = self.skip_whitespace(self.pos) ch = self.ll_chars[i] if ch != ':': @@ -272,13 +282,13 @@ i = self.skip_whitespace(i) # w_value = self.decode_any(i) - self.space.setitem(w_dict, w_name, w_value) + d[name] = w_value i = self.skip_whitespace(self.pos) ch = self.ll_chars[i] i += 1 if ch == '}': self.pos = i - return w_dict + return self._create_dict(d) elif ch == ',': pass elif ch == '\0': @@ -287,6 +297,9 @@ raise DecoderError("Unexpected '%s' when decoding object" % ch, i-1) + def _create_dict(self, d): + from pypy.objspace.std.dictmultiobject import from_unicode_key_dict + return from_unicode_key_dict(self.space, d) def decode_string(self, i): start = i @@ -298,22 +311,23 @@ i += 1 bits |= ord(ch) if ch == '"': - if bits & 0x80: - # the 8th bit is set, it's an utf8 strnig - content_utf8 = self.getslice(start, i-1) - content_unicode = unicodehelper.decode_utf8(self.space, content_utf8) - else: - # ascii only, fast path (ascii is a strict subset of - # latin1, and we already checked that all the chars are < - # 128) - content_unicode = strslice2unicode_latin1(self.s, start, i-1) - self.last_type = TYPE_STRING self.pos = i - return self.space.newunicode(content_unicode) + return self.space.newunicode( + self._create_string(start, i - 1, bits)) elif ch == '\\' or ch < '\x20': self.pos = i-1 return self.decode_string_escaped(start) + def _create_string(self, start, end, bits): + if bits & 0x80: + # the 8th bit is set, it's an utf8 string + content_utf8 = self.getslice(start, end) + return unicodehelper.decode_utf8(self.space, content_utf8) + else: + # ascii only, fast path (ascii is a strict subset of + # latin1, and we already checked that all the chars are < + # 128) + return strslice2unicode_latin1(self.s, start, end) def decode_string_escaped(self, start): i = self.pos @@ -326,9 +340,7 @@ i += 1 if ch == '"': content_utf8 = builder.build() - content_unicode = unicodehelper.decode_utf8( - self.space, content_utf8, allow_surrogates=True) - self.last_type = TYPE_STRING + content_unicode = unicodehelper.decode_utf8(self.space, content_utf8, allow_surrogates=True) self.pos = i return self.space.newunicode(content_unicode) elif ch == '\\': @@ -390,6 +402,47 @@ lowsurr = int(hexdigits, 16) # the possible ValueError is caugth by the caller return 0x10000 + (((highsurr - 0xd800) << 10) | (lowsurr - 0xdc00)) + def decode_key(self, i): + """ returns an unwrapped unicode """ + from rpython.rlib.rarithmetic import intmask + + i = self.skip_whitespace(i) + ll_chars = self.ll_chars + ch = ll_chars[i] + if ch != '"': + raise DecoderError("Key name must be string at char", i) + i += 1 + + start = i + bits = 0 + strhash = ord(ll_chars[i]) << 7 + while True: + ch = ll_chars[i] + i += 1 + if ch == '"': + break + elif ch == '\\' or ch < '\x20': + self.pos = i-1 + return self.space.unicode_w(self.decode_string_escaped(start)) + strhash = intmask((1000003 * strhash) ^ ord(ll_chars[i])) + bits |= ord(ch) + length = i - start - 1 + if length == 0: + strhash = -1 + else: + strhash ^= length + strhash = intmask(strhash) + self.pos = i + # check cache first: + key = (ll_chars, start, length, strhash) + try: + return self.cache[key] + except KeyError: + pass + res = self._create_string(start, i - 1, bits) + self.cache[key] = res + return res + def loads(space, w_s, w_errorcls=None): s = space.text_w(w_s) decoder = JSONDecoder(space, s) diff --git a/pypy/module/_pypyjson/targetjson.py b/pypy/module/_pypyjson/targetjson.py --- a/pypy/module/_pypyjson/targetjson.py +++ b/pypy/module/_pypyjson/targetjson.py @@ -5,9 +5,15 @@ import time from pypy.interpreter.error import OperationError -from pypy.module._pypyjson.interp_decoder import loads +from pypy.module._pypyjson.interp_decoder import loads, JSONDecoder from rpython.rlib.objectmodel import specialize, dont_inline +def _create_dict(self, d): + w_res = W_Dict() + w_res.dictval = d + return w_res + +JSONDecoder._create_dict = _create_dict ## MSG = open('msg.json').read() @@ -65,10 +71,14 @@ def isinstance_w(self, w_x, w_type): return isinstance(w_x, w_type) - def str_w(self, w_x): + def bytes_w(self, w_x): assert isinstance(w_x, W_String) return w_x.strval + def unicode_w(self, w_x): + assert isinstance(w_x, W_Unicode) + return w_x.unival + @dont_inline def call_method(self, obj, name, arg): assert name == 'append' @@ -83,13 +93,17 @@ assert isinstance(key, W_Unicode) d.dictval[key.unival] = value - def wrapunicode(self, x): + def newunicode(self, x): return W_Unicode(x) - def wrapint(self, x): + def newtext(self, x): + return W_String(x) + newbytes = newtext + + def newint(self, x): return W_Int(x) - def wrapfloat(self, x): + def newfloat(self, x): return W_Float(x) @specialize.argtype(1) 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 @@ -10,7 +10,18 @@ assert dec.skip_whitespace(8) == len(s) dec.close() - +def test_decode_key(): + s1 = "123" * 100 + s = ' "%s" "%s" ' % (s1, s1) + dec = JSONDecoder('fake space', s) + assert dec.pos == 0 + x = dec.decode_key(0) + assert x == s1 + # check caching + y = dec.decode_key(dec.pos) + assert y == s1 + assert y is x + dec.close() class AppTest(object): spaceconfig = {"usemodules": ['_pypyjson']} @@ -189,6 +200,12 @@ res = _pypyjson.loads(json) assert res == {u'a': u'\ud83d'} + def test_cache_keys(self): + import _pypyjson + json = '[{"a": 1}, {"a": 2}]' + res = _pypyjson.loads(json) + assert res == [{u'a': 1}, {u'a': 2}] + def test_tab_in_string_should_fail(self): import _pypyjson # http://json.org/JSON_checker/test/fail25.json @@ -226,7 +243,7 @@ ('{"spam":[42}', "Unexpected '}' when decoding array", 11), ('["]', 'Unterminated string starting at', 1), ('["spam":', "Unexpected ':' when decoding array", 7), - ('[{]', "Unexpected ']' at", 2), + ('[{]', "Key name must be string at char", 2), ] for inputtext, errmsg, errpos in test_cases: exc = raises(ValueError, _pypyjson.loads, inputtext) diff --git a/pypy/module/_vmprof/test/test__vmprof.py b/pypy/module/_vmprof/test/test__vmprof.py --- a/pypy/module/_vmprof/test/test__vmprof.py +++ b/pypy/module/_vmprof/test/test__vmprof.py @@ -45,10 +45,10 @@ _, size = struct.unpack("ll", s[i:i + 2 * WORD]) count += 1 i += 2 * WORD + size - elif s[i] == '\x06': + elif s[i] == 6: print(s[i:i+24]) i += 1+8+8+8 - elif s[i] == '\x07': + elif s[i] == 7: i += 1 # skip string size, = struct.unpack("l", s[i:i + WORD]) diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -14,6 +14,7 @@ from rpython.rlib.rfile import (FILEP, c_fread, c_fclose, c_fwrite, c_fdopen, c_fileno, c_fopen)# for tests +from rpython.rlib import jit from rpython.translator import cdir from rpython.translator.tool.cbuild import ExternalCompilationInfo from rpython.translator.gensupp import NameManager @@ -453,6 +454,11 @@ if func.__name__ in FUNCTIONS_BY_HEADER[header]: raise ValueError("%s already registered" % func.__name__) func._always_inline_ = 'try' + # + # XXX: should we @jit.dont_look_inside all the @cpython_api functions, + # or we should only disable some of them? + func._jit_look_inside_ = False + # api_function = ApiFunction( argtypes, restype, func, error=_compute_error(error, restype), gil=gil, @@ -562,6 +568,7 @@ '_PyObject_CallFunction_SizeT', '_PyObject_CallMethod_SizeT', 'PyObject_GetBuffer', 'PyBuffer_Release', + '_Py_setfilesystemdefaultencoding', 'PyCObject_FromVoidPtr', 'PyCObject_FromVoidPtrAndDesc', 'PyCObject_AsVoidPtr', 'PyCObject_GetDesc', 'PyCObject_Import', 'PyCObject_SetVoidPtr', @@ -668,7 +675,11 @@ 'PySlice_Type': 'space.gettypeobject(W_SliceObject.typedef)', 'PyStaticMethod_Type': 'space.gettypeobject(StaticMethod.typedef)', 'PyCFunction_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCFunctionObject.typedef)', - 'PyWrapperDescr_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCMethodObject.typedef)', + 'PyClassMethodDescr_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCClassMethodObject.typedef)', + 'PyGetSetDescr_Type': 'space.gettypeobject(cpyext.typeobject.W_GetSetPropertyEx.typedef)', + 'PyMemberDescr_Type': 'space.gettypeobject(cpyext.typeobject.W_MemberDescr.typedef)', + 'PyMethodDescr_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCMethodObject.typedef)', + 'PyWrapperDescr_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCWrapperObject.typedef)', 'PyInstanceMethod_Type': 'space.gettypeobject(cpyext.classobject.InstanceMethod.typedef)', }.items(): register_global(cpyname, 'PyTypeObject*', pypyexpr, header=pypy_decl) @@ -1058,10 +1069,16 @@ get_capsule_type = rffi.llexternal('_%s_get_capsule_type' % prefix, [], PyTypeObjectPtr, compilation_info=eci, _nowrapper=True) + setdefenc = rffi.llexternal('_%s_setfilesystemdefaultencoding' % prefix, + [rffi.CCHARP], lltype.Void, + compilation_info=eci, _nowrapper=True) def init_types(space): from pypy.module.cpyext.typeobject import py_type_ready + from pypy.module.sys.interp_encoding import getfilesystemencoding py_type_ready(space, get_cobject_type()) py_type_ready(space, get_capsule_type()) + s = space.text_w(getfilesystemencoding(space)) + setdefenc(rffi.str2charp(s, track_allocation=False)) # "leaks" INIT_FUNCTIONS.append(init_types) from pypy.module.posix.interp_posix import add_fork_hook global py_fatalerror @@ -1330,6 +1347,21 @@ decls = defaultdict(list) for decl in FORWARD_DECLS: decls[pypy_decl].append("%s;" % (decl,)) + decls[pypy_decl].append(""" +/* hack for https://bugs.python.org/issue29943 */ + +PyAPI_FUNC(int) %s(PyObject *arg0, + Signed arg1, Signed *arg2, + Signed *arg3, Signed *arg4, Signed *arg5); +#ifdef __GNUC__ +__attribute__((__unused__)) +#endif +static int PySlice_GetIndicesEx(PyObject *arg0, Py_ssize_t arg1, + Py_ssize_t *arg2, Py_ssize_t *arg3, Py_ssize_t *arg4, + Py_ssize_t *arg5) { + return %s(arg0, arg1, arg2, arg3, + arg4, arg5); +}""" % ((mangle_name(prefix, 'PySlice_GetIndicesEx'),)*2)) for header_name, header_functions in FUNCTIONS_BY_HEADER.iteritems(): header = decls[header_name] @@ -1638,6 +1670,7 @@ state.fixup_extension(w_mod, name, path) return w_mod + at jit.dont_look_inside def exec_extension_module(space, w_mod): from pypy.module.cpyext.modsupport import exec_def if not space.config.objspace.usemodules.cpyext: diff --git a/pypy/module/cpyext/include/descrobject.h b/pypy/module/cpyext/include/descrobject.h --- a/pypy/module/cpyext/include/descrobject.h +++ b/pypy/module/cpyext/include/descrobject.h @@ -1,34 +1,6 @@ #ifndef Py_DESCROBJECT_H #define Py_DESCROBJECT_H -#define PyDescr_COMMON \ - PyObject_HEAD \ - PyTypeObject *d_type; \ - PyObject *d_name - -typedef struct { - PyDescr_COMMON; -} PyDescrObject; - -typedef struct { - PyDescr_COMMON; - PyMethodDef *d_method; -} PyMethodDescrObject; - -typedef struct { - PyDescr_COMMON; - struct PyMemberDef *d_member; -} PyMemberDescrObject; - -typedef struct { - PyDescr_COMMON; - PyGetSetDef *d_getset; -} PyGetSetDescrObject; - -typedef struct { - PyDescr_COMMON; - struct wrapperbase *d_base; - void *d_wrapped; /* This can be any function pointer */ -} PyWrapperDescrObject; +#include "cpyext_descrobject.h" #endif diff --git a/pypy/module/cpyext/include/fileobject.h b/pypy/module/cpyext/include/fileobject.h --- a/pypy/module/cpyext/include/fileobject.h +++ b/pypy/module/cpyext/include/fileobject.h @@ -1,1 +1,2 @@ -#define Py_FileSystemDefaultEncoding NULL +PyAPI_DATA(const char *) Py_FileSystemDefaultEncoding; +PyAPI_FUNC(void) _Py_setfilesystemdefaultencoding(const char *); 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,5 @@ -from rpython.rtyper.lltypesystem import lltype, rffi, llmemory +from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rlib import jit from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt @@ -10,8 +11,8 @@ from pypy.module.cpyext.api import ( CONST_STRING, METH_CLASS, METH_COEXIST, METH_KEYWORDS, METH_NOARGS, METH_O, METH_STATIC, METH_VARARGS, PyObject, bootstrap_function, - cpython_api, generic_cpy_call, CANNOT_FAIL, - PyTypeObjectPtr, slot_function, cts) + cpython_api, generic_cpy_call, CANNOT_FAIL, slot_function, cts, + build_type_checkers) from pypy.module.cpyext.pyobject import ( Py_DecRef, from_ref, make_ref, as_pyobj, make_typedescr) @@ -42,8 +43,8 @@ from pypy.module.cpyext.object import _dealloc _dealloc(space, py_obj) - class W_PyCFunctionObject(W_Root): + # TODO create a slightly different class depending on the c_ml_flags def __init__(self, space, ml, w_self, w_module=None): self.ml = ml self.name = rffi.charp2str(rffi.cast(rffi.CCHARP, self.ml.c_ml_name)) @@ -56,7 +57,7 @@ w_self = self.w_self flags = rffi.cast(lltype.Signed, self.ml.c_ml_flags) flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST) - if space.is_true(w_kw) and not flags & METH_KEYWORDS: + if not flags & METH_KEYWORDS and space.is_true(w_kw): raise oefmt(space.w_TypeError, "%s() takes no keyword arguments", self.name) @@ -89,6 +90,20 @@ else: return space.w_None +class W_PyCFunctionObjectNoArgs(W_PyCFunctionObject): + def call(self, space, w_self, w_args, w_kw): + # Call the C function + if w_self is None: + w_self = self.w_self + func = self.ml.c_ml_meth + return generic_cpy_call(space, func, w_self, None) + +class W_PyCFunctionObjectSingleObject(W_PyCFunctionObject): + def call(self, space, w_self, w_o, w_kw): + if w_self is None: + w_self = self.w_self + func = self.ml.c_ml_meth + return generic_cpy_call(space, func, w_self, w_o) class W_PyCMethodObject(W_PyCFunctionObject): w_self = None @@ -102,7 +117,7 @@ return self.space.unwrap(self.descr_method_repr()) def descr_method_repr(self): - w_objclass = self.w_objclass + w_objclass = self.w_objclass assert isinstance(w_objclass, W_TypeObject) return self.space.newtext("" % ( self.name, w_objclass.name)) @@ -110,7 +125,7 @@ def descr_call(self, space, __args__): args_w, kw_w = __args__.unpack() if len(args_w) < 1: - w_objclass = self.w_objclass + w_objclass = self.w_objclass assert isinstance(w_objclass, W_TypeObject) raise oefmt(space.w_TypeError, "descriptor '%8' of '%s' object needs an argument", @@ -118,7 +133,7 @@ w_instance = args_w[0] # XXX: needs a stricter test if not space.isinstance_w(w_instance, self.w_objclass): - w_objclass = self.w_objclass + w_objclass = self.w_objclass assert isinstance(w_objclass, W_TypeObject) raise oefmt(space.w_TypeError, "descriptor '%8' requires a '%s' object but received a '%T'", @@ -130,6 +145,10 @@ ret = self.call(space, w_instance, w_args, w_kw) return ret +# PyPy addition, for Cython +_, _ = build_type_checkers("MethodDescr", W_PyCMethodObject) + + @cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL) def PyCFunction_Check(space, w_obj): from pypy.interpreter.function import BuiltinFunction @@ -156,6 +175,7 @@ (self.name.decode('utf-8'), self.w_objclass.getname(self.space))) + class W_PyCWrapperObject(W_Root): def __init__(self, space, pto, method_name, wrapper_func, wrapper_func_kwds, doc, func, offset=None): @@ -203,6 +223,7 @@ (self.method_name, self.w_objclass.name)) + at jit.dont_look_inside def cwrapper_descr_call(space, w_self, __args__): self = space.interp_w(W_PyCWrapperObject, w_self) args_w, kw_w = __args__.unpack() @@ -213,10 +234,22 @@ space.setitem(w_kw, space.newtext(key), w_obj) return self.call(space, w_self, w_args, w_kw) +def cfunction_descr_call_noargs(space, w_self): + # special case for calling with flags METH_NOARGS + self = space.interp_w(W_PyCFunctionObjectNoArgs, w_self) + return self.call(space, None, None, None) +def cfunction_descr_call_single_object(space, w_self, w_o): + # special case for calling with flags METH_O + self = space.interp_w(W_PyCFunctionObjectSingleObject, w_self) + return self.call(space, None, w_o, None) + + at jit.dont_look_inside def cfunction_descr_call(space, w_self, __args__): + # specialize depending on the W_PyCFunctionObject self = space.interp_w(W_PyCFunctionObject, w_self) args_w, kw_w = __args__.unpack() + # XXX __args__.unpack is slow w_args = space.newtuple(args_w) w_kw = space.newdict() for key, w_obj in kw_w.items(): @@ -224,6 +257,7 @@ ret = self.call(space, None, w_args, w_kw) return ret + at jit.dont_look_inside def cclassmethod_descr_call(space, w_self, __args__): self = space.interp_w(W_PyCFunctionObject, w_self) args_w, kw_w = __args__.unpack() @@ -261,6 +295,26 @@ ) W_PyCFunctionObject.typedef.acceptable_as_base_class = False +W_PyCFunctionObjectNoArgs.typedef = TypeDef( + 'builtin_function_or_method', W_PyCFunctionObject.typedef, + __call__ = interp2app(cfunction_descr_call_noargs), + __doc__ = GetSetProperty(W_PyCFunctionObjectNoArgs.get_doc), + __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectNoArgs), + __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectNoArgs, + wrapfn="newtext_or_none"), + ) +W_PyCFunctionObjectNoArgs.typedef.acceptable_as_base_class = False + +W_PyCFunctionObjectSingleObject.typedef = TypeDef( + 'builtin_function_or_method', W_PyCFunctionObject.typedef, + __call__ = interp2app(cfunction_descr_call_single_object), + __doc__ = GetSetProperty(W_PyCFunctionObjectSingleObject.get_doc), + __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectSingleObject), + __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectSingleObject, + wrapfn="newtext_or_none"), + ) +W_PyCFunctionObjectSingleObject.typedef.acceptable_as_base_class = False + W_PyCMethodObject.typedef = TypeDef( 'method_descriptor', __get__ = interp2app(cmethod_descr_get), @@ -323,11 +377,15 @@ def PyClassMethod_New(space, w_func): return ClassMethod(w_func) - at cpython_api([PyTypeObjectPtr, lltype.Ptr(PyMethodDef)], PyObject) + at cts.decl(""" + PyObject * + PyDescr_NewClassMethod(PyTypeObject *type, PyMethodDef *method)""") def PyDescr_NewMethod(space, w_type, method): return W_PyCMethodObject(space, method, w_type) - at cpython_api([PyObject, lltype.Ptr(PyMethodDef)], PyObject) + at cts.decl(""" + PyObject * + PyDescr_NewClassMethod(PyTypeObject *type, PyMethodDef *method)""") def PyDescr_NewClassMethod(space, w_type, method): return W_PyCClassMethodObject(space, method, w_type) diff --git a/pypy/module/cpyext/modsupport.py b/pypy/module/cpyext/modsupport.py --- a/pypy/module/cpyext/modsupport.py +++ b/pypy/module/cpyext/modsupport.py @@ -1,13 +1,15 @@ from rpython.rtyper.lltypesystem import rffi, lltype from pypy.module.cpyext.api import ( cpython_api, METH_STATIC, METH_CLASS, METH_COEXIST, CANNOT_FAIL, cts, + METH_NOARGS, METH_O, parse_dir, bootstrap_function, generic_cpy_call, generic_cpy_call_dont_convert_result, slot_function) from pypy.module.cpyext.pyobject import PyObject, as_pyobj, make_typedescr from pypy.interpreter.module import Module from pypy.module.cpyext.methodobject import ( W_PyCFunctionObject, PyCFunction_NewEx, PyDescr_NewMethod, - PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New) + PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New, + W_PyCFunctionObjectNoArgs, W_PyCFunctionObjectSingleObject) from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall from pypy.module.cpyext.state import State from pypy.interpreter.error import oefmt @@ -165,6 +167,13 @@ "exception", w_mod.w_name) cur_slot = rffi.ptradd(cur_slot, 1) +def _create_pyc_function_object(space, method, w_self, w_name, flags): + flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST) + if flags == METH_NOARGS: + return W_PyCFunctionObjectNoArgs(space, method, w_self, w_name) + if flags == METH_O: + return W_PyCFunctionObjectSingleObject(space, method, w_self, w_name) + return W_PyCFunctionObject(space, method, w_self, w_name) def convert_method_defs(space, dict_w, methods, w_type, w_self=None, name=None): w_name = space.newtext_or_none(name) @@ -184,7 +193,8 @@ raise oefmt(space.w_ValueError, "module functions cannot set METH_CLASS or " "METH_STATIC") - w_obj = W_PyCFunctionObject(space, method, w_self, w_name) + w_obj = _create_pyc_function_object(space, method, w_self, + w_name, flags) else: if methodname in dict_w and not (flags & METH_COEXIST): continue diff --git a/pypy/module/cpyext/parse/cpyext_descrobject.h b/pypy/module/cpyext/parse/cpyext_descrobject.h new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/parse/cpyext_descrobject.h @@ -0,0 +1,29 @@ +typedef struct { + PyObject_HEAD + PyTypeObject *d_type; + PyObject *d_name; + PyObject *d_qualname; +} PyDescrObject; + +#define PyDescr_COMMON PyDescrObject d_common + +typedef struct { + PyDescr_COMMON; + PyMethodDef *d_method; +} PyMethodDescrObject; + +typedef struct { + PyDescr_COMMON; + struct PyMemberDef *d_member; +} PyMemberDescrObject; + +typedef struct { + PyDescr_COMMON; + PyGetSetDef *d_getset; +} PyGetSetDescrObject; + +typedef struct { + PyDescr_COMMON; + struct wrapperbase *d_base; + void *d_wrapped; /* This can be any function pointer */ +} PyWrapperDescrObject; diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -14,7 +14,7 @@ from rpython.rlib.objectmodel import specialize from rpython.rlib.objectmodel import keepalive_until_here from rpython.rtyper.annlowlevel import llhelper -from rpython.rlib import rawrefcount +from rpython.rlib import rawrefcount, jit from rpython.rlib.debug import fatalerror @@ -151,6 +151,7 @@ class InvalidPointerException(Exception): pass + at jit.dont_look_inside def create_ref(space, w_obj, w_userdata=None, immortal=False): """ Allocates a PyObject, and fills its fields with info from the given @@ -190,6 +191,7 @@ w_marker_deallocating = W_Root() + at jit.dont_look_inside def from_ref(space, ref): """ Finds the interpreter object corresponding to the given reference. If the @@ -227,6 +229,7 @@ assert isinstance(w_type, W_TypeObject) return get_typedescr(w_type.layout.typedef).realize(space, ref) + at jit.dont_look_inside def as_pyobj(space, w_obj, w_userdata=None, immortal=False): """ Returns a 'PyObject *' representing the given intepreter object. 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 @@ -420,23 +420,6 @@ from rpython.rlib.nonconst import NonConstant -SLOTS = {} - - at specialize.memo() -def get_slot_tp_function(space, typedef, name): - """Return a description of the slot C function to use for the built-in - type for 'typedef'. The 'name' is the slot name. This is a memo - function that, after translation, returns one of a built-in finite set. - """ - key = (typedef, name) - try: - return SLOTS[key] - except KeyError: - slot_func = build_slot_tp_function(space, typedef, name) - api_func = slot_func.api_func if slot_func else None - SLOTS[key] = api_func - return api_func - def build_slot_tp_function(space, typedef, name): w_type = space.gettypeobject(typedef) @@ -785,7 +768,7 @@ def __init__(self, method_name, slot_name, function, wrapper1, wrapper2, doc): self.method_name = method_name self.slot_name = slot_name - self.slot_names = ("c_" + slot_name).split(".") + self.slot_names = tuple(("c_" + slot_name).split(".")) self.slot_func = function self.wrapper_func = wrapper1 self.wrapper_func_kwds = wrapper2 diff --git a/pypy/module/cpyext/src/missing.c b/pypy/module/cpyext/src/missing.c --- a/pypy/module/cpyext/src/missing.c +++ b/pypy/module/cpyext/src/missing.c @@ -27,3 +27,7 @@ int Py_Py3kWarningFlag = 0; int Py_HashRandomizationFlag = 0; +const char *Py_FileSystemDefaultEncoding; /* filled when cpyext is imported */ +void _Py_setfilesystemdefaultencoding(const char *enc) { + Py_FileSystemDefaultEncoding = enc; +} 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 @@ -83,6 +83,35 @@ return cls; } +// for CPython +#ifndef PyMethodDescr_Check +int PyMethodDescr_Check(PyObject* method) +{ + PyObject *meth = PyObject_GetAttrString((PyObject*)&PyList_Type, "append"); + if (!meth) return 0; + int res = PyObject_TypeCheck(method, meth->ob_type); + Py_DECREF(meth); + return res; +} +#endif + +PyObject* make_classmethod(PyObject* method) +{ + // adapted from __Pyx_Method_ClassMethod + if (PyMethodDescr_Check(method)) { + PyMethodDescrObject *descr = (PyMethodDescrObject *)method; + PyTypeObject *d_type = descr->d_common.d_type; + return PyDescr_NewClassMethod(d_type, descr->d_method); + } + else if (PyMethod_Check(method)) { + return PyClassMethod_New(PyMethod_GET_FUNCTION(method)); + } + else { + PyErr_SetString(PyExc_TypeError, "unknown method kind"); + return NULL; + } +} + static PyObject * foo_unset(fooobject *self) { @@ -95,6 +124,7 @@ {"copy", (PyCFunction)foo_copy, METH_NOARGS, NULL}, {"create", (PyCFunction)foo_create, METH_NOARGS|METH_STATIC, NULL}, {"classmeth", (PyCFunction)foo_classmeth, METH_NOARGS|METH_CLASS, NULL}, + {"fake_classmeth", (PyCFunction)foo_classmeth, METH_NOARGS, NULL}, {"unset_string_member", (PyCFunction)foo_unset, METH_NOARGS, NULL}, {NULL, NULL} /* sentinel */ }; @@ -167,19 +197,19 @@ /* copied from numpy scalartypes.c for inherited classes */ if (t->tp_bases && (PyTuple_GET_SIZE(t->tp_bases) > 1)) { - PyTypeObject *sup; - /* We are inheriting from a Python type as well so + PyTypeObject *sup; + /* We are inheriting from a Python type as well so give it first dibs on conversion */ sup = (PyTypeObject *)PyTuple_GET_ITEM(t->tp_bases, 1); - /* Prevent recursion */ - if (new_fooType != sup->tp_new) + /* Prevent recursion */ + if (new_fooType != sup->tp_new) { o = sup->tp_new(t, args, kwds); return o; } } o = t->tp_alloc(t, 0); - return o; + return o; }; static PyMemberDef foo_members[] = { @@ -717,7 +747,7 @@ "foo", "Module Doc", -1, - foo_functions, + foo_functions, NULL, NULL, NULL, @@ -751,6 +781,7 @@ #endif { PyObject *d; + PyObject *fake_classmeth, *classmeth; #if PY_MAJOR_VERSION >= 3 PyObject *module = PyModule_Create(&moduledef); #else @@ -808,6 +839,12 @@ INITERROR; gettype2 = PyObject_New(PyObject, &GetType2); + fake_classmeth = PyDict_GetItemString((PyObject *)fooType.tp_dict, "fake_classmeth"); + classmeth = make_classmethod(fake_classmeth); + if (classmeth == NULL) + INITERROR; + if (PyDict_SetItemString((PyObject *)fooType.tp_dict, "fake_classmeth", classmeth) < 0) + INITERROR; d = PyModule_GetDict(module); if (d == NULL) diff --git a/pypy/module/cpyext/test/test_fileobject.py b/pypy/module/cpyext/test/test_fileobject.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_fileobject.py @@ -0,0 +1,13 @@ +from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase + + +class AppTestFileObject(AppTestCpythonExtensionBase): + def test_defaultencoding(self): + import sys + module = self.import_extension('foo', [ + ("defenc", "METH_NOARGS", + """ + return PyUnicode_FromString(Py_FileSystemDefaultEncoding); + """), + ]) + assert module.defenc() == sys.getfilesystemencoding() diff --git a/pypy/module/cpyext/test/test_memoryobject.py b/pypy/module/cpyext/test/test_memoryobject.py --- a/pypy/module/cpyext/test/test_memoryobject.py +++ b/pypy/module/cpyext/test/test_memoryobject.py @@ -72,7 +72,7 @@ """ /* Create an approximation of the buffer for a 0d ndarray */ Py_buffer buf; - PyObject *str = PyBytes_FromString("hello, world."); + PyObject *ret, *str = PyBytes_FromString("hello, world."); buf.buf = PyBytes_AsString(str); buf.obj = str; buf.readonly = 1; @@ -80,7 +80,7 @@ buf.itemsize = 13; buf.ndim = 0; buf.shape = NULL; - PyObject* ret = PyMemoryView_FromBuffer(&buf); + ret = PyMemoryView_FromBuffer(&buf); return ret; """)]) result = module.create_view() diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py --- a/pypy/module/cpyext/test/test_typeobject.py +++ b/pypy/module/cpyext/test/test_typeobject.py @@ -135,6 +135,12 @@ "") raises(TypeError, descr, None) + def test_cython_fake_classmethod(self): + module = self.import_module(name='foo') + print(module.fooType.fake_classmeth) + print(type(module.fooType.fake_classmeth)) + assert module.fooType.fake_classmeth() is module.fooType + def test_new(self): # XXX cpython segfaults but if run singly (with -k test_new) this passes module = self.import_module(name='foo') @@ -1315,6 +1321,52 @@ assert Asize == Bsize assert Asize > basesize + def test_multiple_inheritance_bug1(self): + module = self.import_extension('foo', [ + ("get_type", "METH_NOARGS", + ''' + Py_INCREF(&Foo_Type); + return (PyObject *)&Foo_Type; + ''' + ), ("forty_two", "METH_O", + ''' + return PyLong_FromLong(42); + ''' + )], prologue=''' + static PyTypeObject Foo_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "foo.foo", + }; From pypy.commits at gmail.com Tue Oct 3 07:43:41 2017 From: pypy.commits at gmail.com (rlamy) Date: Tue, 03 Oct 2017 04:43:41 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: Skip tests using pythonapi Message-ID: <59d377ed.2e8edf0a.abae0.ad21@mx.google.com> Author: Ronan Lamy Branch: py3.5 Changeset: r92568:011a075b1e24 Date: 2017-10-03 13:43 +0200 http://bitbucket.org/pypy/pypy/changeset/011a075b1e24/ Log: Skip tests using pythonapi diff --git a/lib-python/3/ctypes/test/test_values.py b/lib-python/3/ctypes/test/test_values.py --- a/lib-python/3/ctypes/test/test_values.py +++ b/lib-python/3/ctypes/test/test_values.py @@ -4,6 +4,7 @@ import unittest import sys +from test.support import cpython_only from ctypes import * import _ctypes_test @@ -28,6 +29,7 @@ ctdll = CDLL(_ctypes_test.__file__) self.assertRaises(ValueError, c_int.in_dll, ctdll, "Undefined_Symbol") + at cpython_only class PythonValuesTestCase(unittest.TestCase): """This test only works when python itself is a dll/shared library""" From pypy.commits at gmail.com Tue Oct 3 08:00:37 2017 From: pypy.commits at gmail.com (fijal) Date: Tue, 03 Oct 2017 05:00:37 -0700 (PDT) Subject: [pypy-commit] pypy memory-accounting: fix the problem of lazy setfield Message-ID: <59d37be5.55ad1c0a.addca.1c71@mx.google.com> Author: fijal Branch: memory-accounting Changeset: r92569:4038d01ba9e9 Date: 2017-10-03 13:59 +0200 http://bitbucket.org/pypy/pypy/changeset/4038d01ba9e9/ Log: fix the problem of lazy setfield diff --git a/rpython/translator/backendopt/writeanalyze.py b/rpython/translator/backendopt/writeanalyze.py --- a/rpython/translator/backendopt/writeanalyze.py +++ b/rpython/translator/backendopt/writeanalyze.py @@ -65,6 +65,11 @@ elif op.opname == "gc_store_indexed": if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]): return self._gc_store_indexed_result(op) + elif op.opname == 'gc_add_memory_pressure': + # special_memory_pressure would be overwritten by zero, because + # the JIT cannot see the field write, which is why we assume + # it can write anything + return top_set return empty_set def _array_result(self, TYPE): From pypy.commits at gmail.com Tue Oct 3 08:21:21 2017 From: pypy.commits at gmail.com (antocuni) Date: Tue, 03 Oct 2017 05:21:21 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: (antocuni, ronan): rename nowrapper into no_gc: this new flag imply @rgc.no_collect now Message-ID: <59d380c1.1986df0a.5a2ac.18a1@mx.google.com> Author: Antonio Cuni Branch: cpyext-nowrapper Changeset: r92570:5286504e4045 Date: 2017-10-03 14:19 +0200 http://bitbucket.org/pypy/pypy/changeset/5286504e4045/ Log: (antocuni, ronan): rename nowrapper into no_gc: this new flag imply @rgc.no_collect now diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -41,6 +41,7 @@ from rpython.rlib import rthread from rpython.rlib.debug import fatalerror_notb from rpython.rlib import rstackovf +from rpython.rlib import rgc from pypy.objspace.std.typeobject import W_TypeObject, find_best_base from pypy.module.cpyext.cparser import CTypeSpace @@ -259,7 +260,13 @@ def __init__(self, argtypes, restype, callable, error=CANNOT_FAIL, c_name=None, cdecl=None, gil=None, result_borrowed=False, result_is_ll=False, - nowrapper=False): + no_gc=False): + """ + no_gc=True means that this function is not allowed to do any operation + which involves the GC; as a consequence, we can avoid emitting the + wrapper. The net result is that calling this function from C is much + faster. This also implies @rgc.no_collect. + """ self.argtypes = argtypes self.restype = restype self.functype = lltype.Ptr(lltype.FuncType(argtypes, restype)) @@ -271,8 +278,9 @@ # extract the signature from the (CPython-level) code object from pypy.interpreter import pycode sig = pycode.cpython_code_signature(callable.func_code) - if nowrapper: + if no_gc: self.argnames = sig.argnames + self.callable = rgc.no_collect(self.callable) else: assert sig.argnames[0] == 'space' self.argnames = sig.argnames[1:] @@ -284,7 +292,7 @@ self.gil = gil self.result_borrowed = result_borrowed self.result_is_ll = result_is_ll - self.nowrapper = nowrapper + self.no_gc = no_gc # def get_llhelper(space): return llhelper(self.functype, self.get_wrapper(space)) @@ -306,7 +314,7 @@ # This logic is obscure, because we try to avoid creating one # big wrapper() function for every callable. Instead we create # only one per "signature". - if self.nowrapper: + if self.no_gc: return self.callable argtypesw = zip(self.argtypes, @@ -440,7 +448,7 @@ DEFAULT_HEADER = 'pypy_decl.h' def cpython_api(argtypes, restype, error=_NOT_SPECIFIED, header=DEFAULT_HEADER, gil=None, result_borrowed=False, result_is_ll=False, - nowrapper=False): + no_gc=False): """ Declares a function to be exported. - `argtypes`, `restype` are lltypes and describe the function signature. @@ -466,7 +474,7 @@ argtypes, restype, func, error=_compute_error(error, restype), gil=gil, result_borrowed=result_borrowed, result_is_ll=result_is_ll, - nowrapper=nowrapper) + no_gc=no_gc) FUNCTIONS_BY_HEADER[header][func.__name__] = api_function unwrapper = api_function.get_unwrapper() unwrapper.func = func diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py --- a/pypy/module/cpyext/object.py +++ b/pypy/module/cpyext/object.py @@ -32,7 +32,7 @@ # XXX FIXME return realloc(ptr, size) - at cpython_api([rffi.VOIDP], lltype.Void, nowrapper=True) + at cpython_api([rffi.VOIDP], lltype.Void, no_gc=True) def PyObject_Free(ptr): lltype.free(ptr, flavor='raw') From pypy.commits at gmail.com Tue Oct 3 08:21:23 2017 From: pypy.commits at gmail.com (antocuni) Date: Tue, 03 Oct 2017 05:21:23 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: merge heads Message-ID: <59d380c3.0f4a1c0a.de4ae.480d@mx.google.com> Author: Antonio Cuni Branch: cpyext-nowrapper Changeset: r92571:1fd69c872733 Date: 2017-10-03 14:20 +0200 http://bitbucket.org/pypy/pypy/changeset/1fd69c872733/ Log: merge heads diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py --- a/pypy/module/cpyext/object.py +++ b/pypy/module/cpyext/object.py @@ -74,7 +74,7 @@ @cpython_api([rffi.VOIDP], lltype.Void) def PyObject_GC_Del(space, obj): - PyObject_Free(space, obj) + PyObject_Free(obj) @cpython_api([PyObject], PyObjectP, error=CANNOT_FAIL) def _PyObject_GetDictPtr(space, op): From pypy.commits at gmail.com Tue Oct 3 08:36:20 2017 From: pypy.commits at gmail.com (arigo) Date: Tue, 03 Oct 2017 05:36:20 -0700 (PDT) Subject: [pypy-commit] pypy default: (ronan, antocuni, arigo) Message-ID: <59d38444.0a181c0a.8bd82.313e@mx.google.com> Author: Armin Rigo Branch: Changeset: r92572:1a1441967500 Date: 2017-10-01 18:39 +0200 http://bitbucket.org/pypy/pypy/changeset/1a1441967500/ Log: (ronan, antocuni, arigo) Small change to prepare the subtype_dealloc() attack diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -25,9 +25,9 @@ basestruct = PyObject.TO W_BaseObject = W_ObjectObject - def get_dealloc(self): + def get_dealloc(self, space): from pypy.module.cpyext.typeobject import subtype_dealloc - return subtype_dealloc.api_func + return subtype_dealloc.api_func.get_llhelper(space) def allocate(self, space, w_type, itemcount=0, immortal=False): # typically called from PyType_GenericAlloc via typedescr.allocate @@ -106,8 +106,8 @@ return tp_alloc(space, w_type, itemcount) if tp_dealloc: - def get_dealloc(self): - return tp_dealloc.api_func + def get_dealloc(self, space): + return tp_dealloc.api_func.get_llhelper(space) if tp_attach: def attach(self, space, pyobj, w_obj, w_userdata=None): 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 @@ -765,7 +765,7 @@ # dealloc if space.gettypeobject(w_type.layout.typedef) is w_type: # only for the exact type, like 'space.w_tuple' or 'space.w_list' - pto.c_tp_dealloc = typedescr.get_dealloc().get_llhelper(space) + pto.c_tp_dealloc = typedescr.get_dealloc(space) else: # for all subtypes, use base's dealloc (requires sorting in attach_all) pto.c_tp_dealloc = pto.c_tp_base.c_tp_dealloc From pypy.commits at gmail.com Tue Oct 3 08:36:23 2017 From: pypy.commits at gmail.com (arigo) Date: Tue, 03 Oct 2017 05:36:23 -0700 (PDT) Subject: [pypy-commit] pypy default: (antocuni, arigo) Message-ID: <59d38447.d50b1c0a.c979d.6f43@mx.google.com> Author: Armin Rigo Branch: Changeset: r92573:3e50a1272650 Date: 2017-10-02 12:13 +0200 http://bitbucket.org/pypy/pypy/changeset/3e50a1272650/ Log: (antocuni, arigo) Aaaaah. Found that this simple new test in test_ll2ctypes doesn't actually pass. Fix. diff --git a/rpython/rtyper/lltypesystem/ll2ctypes.py b/rpython/rtyper/lltypesystem/ll2ctypes.py --- a/rpython/rtyper/lltypesystem/ll2ctypes.py +++ b/rpython/rtyper/lltypesystem/ll2ctypes.py @@ -815,6 +815,10 @@ else: container = llobj._obj if isinstance(T.TO, lltype.FuncType): + if hasattr(llobj._obj0, '_real_integer_addr'): + ctypes_func_type = get_ctypes_type(T) + return ctypes.cast(llobj._obj0._real_integer_addr, + ctypes_func_type) # XXX a temporary workaround for comparison of lltype.FuncType key = llobj._obj.__dict__.copy() key['_TYPE'] = repr(key['_TYPE']) @@ -1039,7 +1043,8 @@ cobj = ctypes.cast(cobjkey, type(cobj)) _callable = get_ctypes_trampoline(T.TO, cobj) return lltype.functionptr(T.TO, name, - _callable=_callable) + _callable=_callable, + _real_integer_addr=cobjkey) elif isinstance(T.TO, lltype.OpaqueType): if T == llmemory.GCREF: container = _llgcopaque(cobj) diff --git a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py --- a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py +++ b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py @@ -1443,6 +1443,12 @@ assert seen[0]() == 42 assert seen[1]() == 43 + def test_keep_value_across_lltype_callable(self): + PtrF = lltype.Ptr(lltype.FuncType([], lltype.Void)) + f = rffi.cast(PtrF, 42) + assert lltype.typeOf(f) == PtrF + assert rffi.cast(lltype.Signed, f) == 42 + class TestPlatform(object): def test_lib_on_libpaths(self): From pypy.commits at gmail.com Tue Oct 3 08:36:25 2017 From: pypy.commits at gmail.com (arigo) Date: Tue, 03 Oct 2017 05:36:25 -0700 (PDT) Subject: [pypy-commit] pypy default: (antocuni, arigo) Message-ID: <59d38449.54081c0a.c63d5.6411@mx.google.com> Author: Armin Rigo Branch: Changeset: r92574:ace97c304a30 Date: 2017-10-02 12:33 +0200 http://bitbucket.org/pypy/pypy/changeset/ace97c304a30/ Log: (antocuni, arigo) More. diff --git a/rpython/rtyper/lltypesystem/ll2ctypes.py b/rpython/rtyper/lltypesystem/ll2ctypes.py --- a/rpython/rtyper/lltypesystem/ll2ctypes.py +++ b/rpython/rtyper/lltypesystem/ll2ctypes.py @@ -817,7 +817,7 @@ if isinstance(T.TO, lltype.FuncType): if hasattr(llobj._obj0, '_real_integer_addr'): ctypes_func_type = get_ctypes_type(T) - return ctypes.cast(llobj._obj0._real_integer_addr, + return ctypes.cast(llobj._obj0._real_integer_addr(), ctypes_func_type) # XXX a temporary workaround for comparison of lltype.FuncType key = llobj._obj.__dict__.copy() @@ -1044,7 +1044,7 @@ _callable = get_ctypes_trampoline(T.TO, cobj) return lltype.functionptr(T.TO, name, _callable=_callable, - _real_integer_addr=cobjkey) + _real_integer_addr=lambda: cobjkey) elif isinstance(T.TO, lltype.OpaqueType): if T == llmemory.GCREF: container = _llgcopaque(cobj) @@ -1302,6 +1302,10 @@ # perform the call return self.trampoline(*argvalues) + def get_real_address(self): + cfunc = get_ctypes_callable(self.funcptr, self.calling_conv) + return ctypes.cast(cfunc, ctypes.c_void_p).value + def get_ctypes_trampoline(FUNCTYPE, cfunc): RESULT = FUNCTYPE.RESULT container_arguments = [] diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py --- a/rpython/rtyper/lltypesystem/rffi.py +++ b/rpython/rtyper/lltypesystem/rffi.py @@ -145,6 +145,9 @@ # Also, _nowrapper functions cannot release the GIL, by default. invoke_around_handlers = not sandboxsafe and not _nowrapper + if _nowrapper and isinstance(_callable, ll2ctypes.LL2CtypesCallable): + kwds['_real_integer_addr'] = _callable.get_real_address + if random_effects_on_gcobjs not in (False, True): random_effects_on_gcobjs = ( invoke_around_handlers or # because it can release the GIL diff --git a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py --- a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py +++ b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py @@ -1449,6 +1449,25 @@ assert lltype.typeOf(f) == PtrF assert rffi.cast(lltype.Signed, f) == 42 + def test_keep_value_across_rffi_llexternal(self): + c_source = py.code.Source(r""" + void ff1(void) { } + void *get_ff1(void) { return &ff1; } + """) + eci = ExternalCompilationInfo( + separate_module_sources=[c_source], + post_include_bits = [ + "RPY_EXTERN void ff1(void); RPY_EXTERN void *get_ff1(void);"]) + PtrFF1 = lltype.Ptr(lltype.FuncType([], lltype.Void)) + f1 = rffi.llexternal('ff1', [], lltype.Void, compilation_info=eci, + _nowrapper=True) + assert lltype.typeOf(f1) == PtrFF1 + getff1 = rffi.llexternal('get_ff1', [], PtrFF1, compilation_info=eci, + _nowrapper=True) + f2 = getff1() + assert rffi.cast(lltype.Signed, f2) == rffi.cast(lltype.Signed, f1) + #assert f2 == f1 -- fails, would be nice but oh well + class TestPlatform(object): def test_lib_on_libpaths(self): From pypy.commits at gmail.com Tue Oct 3 09:24:15 2017 From: pypy.commits at gmail.com (rlamy) Date: Tue, 03 Oct 2017 06:24:15 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: (antocuni, ronan) Remove the 'unwrapper' wrapper for no_gc functions Message-ID: <59d38f7f.8e841c0a.59a74.c354@mx.google.com> Author: Ronan Lamy Branch: cpyext-nowrapper Changeset: r92575:e99ce0672a27 Date: 2017-10-03 15:23 +0200 http://bitbucket.org/pypy/pypy/changeset/e99ce0672a27/ Log: (antocuni, ronan) Remove the 'unwrapper' wrapper for no_gc functions diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -352,6 +352,8 @@ return wrapper def get_unwrapper(self): + if self.no_gc: + return self.callable names = self.argnames argtypesw = zip(self.argtypes, [_name.startswith("w_") for _name in self.argnames]) From pypy.commits at gmail.com Tue Oct 3 10:44:45 2017 From: pypy.commits at gmail.com (rlamy) Date: Tue, 03 Oct 2017 07:44:45 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: (antocuni, ronan, arigo around) Make _Py_Dealloc no_gc and fix its calling from RPython Message-ID: <59d3a25d.0bbadf0a.1cc15.e0c4@mx.google.com> Author: Ronan Lamy Branch: cpyext-nowrapper Changeset: r92576:cdc783955b0e Date: 2017-10-03 16:44 +0200 http://bitbucket.org/pypy/pypy/changeset/cdc783955b0e/ Log: (antocuni, ronan, arigo around) Make _Py_Dealloc no_gc and fix its calling from RPython diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -353,7 +353,21 @@ def get_unwrapper(self): if self.no_gc: - return self.callable + @specialize.ll() + @rgc.no_collect + def unwrapper(*args): + # see "Handling of the GIL" above + tid = rthread.get_ident() + assert cpyext_glob_tid_ptr[0] == 0 + cpyext_glob_tid_ptr[0] = tid + try: + # Call the function + return self.callable(*args) + finally: + assert cpyext_glob_tid_ptr[0] == tid + cpyext_glob_tid_ptr[0] = 0 + return unwrapper + names = self.argnames argtypesw = zip(self.argtypes, [_name.startswith("w_") for _name in self.argnames]) diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -323,7 +323,7 @@ assert obj.c_ob_pypy_link == 0 or obj.c_ob_refcnt > rawrefcount.REFCNT_FROM_PYPY obj.c_ob_refcnt -= 1 if obj.c_ob_refcnt == 0: - _Py_Dealloc(space, obj) + _Py_Dealloc(obj) #else: # w_obj = rawrefcount.to_obj(W_Root, ref) # if w_obj is not None: @@ -348,14 +348,11 @@ assert isinstance(w_type, W_TypeObject) get_typedescr(w_type.layout.typedef).realize(space, obj) - at cpython_api([PyObject], lltype.Void) -def _Py_Dealloc(space, obj): - from pypy.module.cpyext.api import generic_cpy_call + at cpython_api([PyObject], lltype.Void, no_gc=True) +def _Py_Dealloc(obj): pto = obj.c_ob_type - #print >>sys.stderr, "Calling dealloc slot", pto.c_tp_dealloc, "of", obj, \ - # "'s type which is", rffi.charp2str(pto.c_tp_name) rawrefcount.mark_deallocating(w_marker_deallocating, obj) - generic_cpy_call(space, pto.c_tp_dealloc, obj) + pto.c_tp_dealloc(obj) @cpython_api([rffi.VOIDP], lltype.Signed, error=CANNOT_FAIL) def _Py_HashPointer(space, ptr): From pypy.commits at gmail.com Sat Oct 7 09:08:19 2017 From: pypy.commits at gmail.com (rlamy) Date: Sat, 07 Oct 2017 06:08:19 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: fix PyObject_GC_Del() Message-ID: <59d8d1c3.196b1c0a.40752.03f4@mx.google.com> Author: Ronan Lamy Branch: cpyext-nowrapper Changeset: r92584:91457554c166 Date: 2017-10-03 18:23 +0200 http://bitbucket.org/pypy/pypy/changeset/91457554c166/ Log: fix PyObject_GC_Del() diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py --- a/pypy/module/cpyext/object.py +++ b/pypy/module/cpyext/object.py @@ -74,7 +74,8 @@ @cpython_api([rffi.VOIDP], lltype.Void, no_gc=True) def PyObject_GC_Del(obj): - PyObject_Free(obj) + # like PyObject_Free + lltype.free(obj, flavor='raw') @cpython_api([PyObject], PyObjectP, error=CANNOT_FAIL) def _PyObject_GetDictPtr(space, op): From pypy.commits at gmail.com Sat Oct 7 09:08:14 2017 From: pypy.commits at gmail.com (antocuni) Date: Sat, 07 Oct 2017 06:08:14 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: (antocuni, ronan, armin) kill the @rgc.no_collect for now, as it doesn't do exactly what we need. Eventually we should probably write a custom graph analyzer, but for now we just ignore the issue and see how much we can go forward with this approach Message-ID: <59d8d1be.4f861c0a.d22d9.2d43@mx.google.com> Author: Antonio Cuni Branch: cpyext-nowrapper Changeset: r92582:a11cce743736 Date: 2017-10-03 17:53 +0200 http://bitbucket.org/pypy/pypy/changeset/a11cce743736/ Log: (antocuni, ronan, armin) kill the @rgc.no_collect for now, as it doesn't do exactly what we need. Eventually we should probably write a custom graph analyzer, but for now we just ignore the issue and see how much we can go forward with this approach diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -41,7 +41,6 @@ from rpython.rlib import rthread from rpython.rlib.debug import fatalerror_notb from rpython.rlib import rstackovf -from rpython.rlib import rgc from pypy.objspace.std.typeobject import W_TypeObject, find_best_base from pypy.module.cpyext.cparser import CTypeSpace @@ -265,7 +264,7 @@ no_gc=True means that this function is not allowed to do any operation which involves the GC; as a consequence, we can avoid emitting the wrapper. The net result is that calling this function from C is much - faster. This also implies @rgc.no_collect. + faster. """ self.argtypes = argtypes self.restype = restype @@ -280,7 +279,6 @@ sig = pycode.cpython_code_signature(callable.func_code) if no_gc: self.argnames = sig.argnames - self.callable = rgc.no_collect(self.callable) else: assert sig.argnames[0] == 'space' self.argnames = sig.argnames[1:] @@ -354,7 +352,6 @@ def get_unwrapper(self): if self.no_gc: @specialize.ll() - @rgc.no_collect def unwrapper(*args): # see "Handling of the GIL" above tid = rthread.get_ident() diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -16,7 +16,6 @@ from rpython.rtyper.annlowlevel import llhelper from rpython.rlib import rawrefcount, jit from rpython.rlib.debug import fatalerror -from rpython.rlib import rgc #________________________________________________________ @@ -315,7 +314,6 @@ def incref(space, obj): make_ref(space, obj) - at rgc.no_collect def decref(obj): assert is_pyobj(obj) obj = rffi.cast(PyObject, obj) From pypy.commits at gmail.com Sat Oct 7 09:08:16 2017 From: pypy.commits at gmail.com (antocuni) Date: Sat, 07 Oct 2017 06:08:16 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: typo Message-ID: <59d8d1c0.dc4a1c0a.4a601.5f28@mx.google.com> Author: Antonio Cuni Branch: cpyext-nowrapper Changeset: r92583:7fef75641ebf Date: 2017-10-03 17:54 +0200 http://bitbucket.org/pypy/pypy/changeset/7fef75641ebf/ Log: typo 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 @@ -385,7 +385,7 @@ subtype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_subtype)) w_obj = generic_cpy_call(space, tp_new, subtype, w_args, w_kwds) finally: - decref_w_obj(w_subtype) + decref_w_obj(space, w_subtype) return w_obj @specialize.memo() From pypy.commits at gmail.com Sat Oct 7 09:08:22 2017 From: pypy.commits at gmail.com (rlamy) Date: Sat, 07 Oct 2017 06:08:22 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: hack to fix translation because fijal wants to go to dinner Message-ID: <59d8d1c6.09a0df0a.ea0b7.b104@mx.google.com> Author: Ronan Lamy Branch: cpyext-nowrapper Changeset: r92585:9442c8d6ad20 Date: 2017-10-03 18:34 +0200 http://bitbucket.org/pypy/pypy/changeset/9442c8d6ad20/ Log: hack to fix translation because fijal wants to go to dinner 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 @@ -3,6 +3,7 @@ from rpython.rlib import jit from rpython.rlib.objectmodel import specialize, we_are_translated from rpython.rtyper.lltypesystem import rffi, lltype +from rpython.rtyper.annlowlevel import llhelper from pypy.interpreter.baseobjspace import W_Root, DescrMismatch from pypy.interpreter.error import oefmt @@ -547,7 +548,7 @@ def subtype_dealloc(obj): pto = obj.c_ob_type base = pto - this_func_ptr = llslot(None, subtype_dealloc) + this_func_ptr = ll_subtype_dealloc # This wrapper is created on a specific type, call it w_A. # We wish to call the dealloc function from one of the base classes of w_A, # the first of which is not this function itself. @@ -566,6 +567,10 @@ # hopefully this does not clash with the memory model assumed in # extension modules +# XXX +ll_subtype_dealloc = llhelper(subtype_dealloc.api_func.functype, + subtype_dealloc.api_func.callable) + @slot_function([PyObject, Py_ssize_tP], lltype.Signed, error=CANNOT_FAIL) def bf_segcount(space, w_obj, ref): if ref: From pypy.commits at gmail.com Sat Oct 7 09:08:40 2017 From: pypy.commits at gmail.com (antocuni) Date: Sat, 07 Oct 2017 06:08:40 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: typo Message-ID: <59d8d1d8.478f1c0a.a6499.9a44@mx.google.com> Author: Antonio Cuni Branch: cpyext-nowrapper Changeset: r92586:3ad28c3bd8e4 Date: 2017-10-03 19:42 +0200 http://bitbucket.org/pypy/pypy/changeset/3ad28c3bd8e4/ Log: typo diff --git a/pypy/module/cpyext/test/test_memoryobject.py b/pypy/module/cpyext/test/test_memoryobject.py --- a/pypy/module/cpyext/test/test_memoryobject.py +++ b/pypy/module/cpyext/test/test_memoryobject.py @@ -4,7 +4,7 @@ from pypy.module.cpyext.test.test_api import BaseApiTest from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase from rpython.rlib.buffer import StringBuffer -from pypy.module.cpyext.pyobject import make_ref, from_ref, decref +from pypy.module.cpyext.pyobject import make_ref, from_ref, decref, decref_w_obj from pypy.module.cpyext.memoryobject import PyMemoryViewObject only_pypy ="config.option.runappdirect and '__pypy__' not in sys.builtin_module_names" @@ -32,7 +32,7 @@ assert space.eq_w(space.getattr(w_mv, w_f), space.getattr(w_memoryview, w_f)) decref(ref) - decref(w_memoryview) + decref_w_obj(space, w_memoryview) class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase): def test_fillWithObject(self): From pypy.commits at gmail.com Sat Oct 7 09:08:49 2017 From: pypy.commits at gmail.com (antocuni) Date: Sat, 07 Oct 2017 06:08:49 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: forgot to rename these Message-ID: <59d8d1e1.8a821c0a.f5f88.7f8c@mx.google.com> Author: Antonio Cuni Branch: cpyext-nowrapper Changeset: r92588:fccecff29953 Date: 2017-10-03 20:39 +0200 http://bitbucket.org/pypy/pypy/changeset/fccecff29953/ Log: forgot to rename these diff --git a/pypy/module/cpyext/pystate.py b/pypy/module/cpyext/pystate.py --- a/pypy/module/cpyext/pystate.py +++ b/pypy/module/cpyext/pystate.py @@ -75,7 +75,7 @@ def ThreadState_dealloc(ts, space): assert space is not None - Py_DecRef(ts.c_dict) + decref(ts.c_dict) ThreadStateCapsule = encapsulator(PyThreadState.TO, dealloc=ThreadState_dealloc) @@ -319,7 +319,7 @@ interpreter lock must be held.""" if not space.config.translation.thread: raise NoThreads - Py_DecRef(tstate.c_dict) + decref(tstate.c_dict) tstate.c_dict = lltype.nullptr(PyObject.TO) space.threadlocals.leave_thread(space) space.getexecutioncontext().cleanup_cpyext_state() From pypy.commits at gmail.com Sat Oct 7 09:08:43 2017 From: pypy.commits at gmail.com (antocuni) Date: Sat, 07 Oct 2017 06:08:43 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: (antocuni, ronan): fix the Py_DecRef usages inside cppyy Message-ID: <59d8d1db.c82d1c0a.dfdda.6021@mx.google.com> Author: Antonio Cuni Branch: cpyext-nowrapper Changeset: r92587:97550ad28d1a Date: 2017-10-03 20:27 +0200 http://bitbucket.org/pypy/pypy/changeset/97550ad28d1a/ Log: (antocuni, ronan): fix the Py_DecRef usages inside cppyy 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 @@ -659,7 +659,7 @@ raise NotImplementedError space.getbuiltinmodule("cpyext") from pypy.module.cpyext.pyobject import Py_DecRef, PyObject - Py_DecRef(space, rffi.cast(PyObject, rffi.cast(rffi.VOIDPP, arg)[0])) + Py_DecRef(rffi.cast(PyObject, rffi.cast(rffi.VOIDPP, arg)[0])) class MacroConverter(TypeConverter): 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 @@ -224,7 +224,7 @@ result = rffi.cast(PyObject, lresult) w_obj = from_ref(space, result) if result: - Py_DecRef(space, result) + Py_DecRef(result) return w_obj def execute(self, space, cppmethod, cppthis, num_args, args): From pypy.commits at gmail.com Sat Oct 7 09:08:52 2017 From: pypy.commits at gmail.com (wlav) Date: Sat, 07 Oct 2017 06:08:52 -0700 (PDT) Subject: [pypy-commit] pypy cppyy-packaging: simplify/cleanup Message-ID: <59d8d1e4.c7341c0a.2c97e.8782@mx.google.com> Author: Wim Lavrijsen Branch: cppyy-packaging Changeset: r92589:9e7102e53565 Date: 2017-08-09 19:05 -0700 http://bitbucket.org/pypy/pypy/changeset/9e7102e53565/ Log: simplify/cleanup 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 @@ -706,9 +706,7 @@ class W_CPPScope(W_Root): _attrs_ = ['space', 'name', 'handle', 'methods', 'datamembers'] - _immutable_fields_ = ['kind', 'name'] - - kind = "scope" + _immutable_fields_ = ['handle', 'name'] def __init__(self, space, name, opaque_handle): self.space = space @@ -755,9 +753,6 @@ overload = W_CPPOverload(self.space, self, methods[:]) self.methods[pyname] = overload - def full_name(self): - return capi.c_scoped_final_name(self.space, self.handle) - def get_method_names(self): return self.space.newlist([self.space.newtext(name) for name in self.methods]) @@ -790,11 +785,6 @@ return W_CPPOverload(self.space, self, [f]) raise oefmt(self.space.w_LookupError, "no overload matches signature") - def missing_attribute_error(self, name): - return oefmt(self.space.w_AttributeError, - "%s '%s' has no attribute %s", - self.kind, self.name, name) - def __eq__(self, other): return self.handle == other.handle @@ -807,9 +797,6 @@ # classes for inheritance. Both are python classes, though, and refactoring # may be in order at some point. class W_CPPNamespace(W_CPPScope): - _immutable_fields_ = ['kind'] - - kind = "namespace" def _make_cppfunction(self, pyname, index): num_args = capi.c_method_num_args(self.space, self, index) @@ -877,6 +864,10 @@ if dname: alldir.append(self.space.newtext(dname)) return self.space.newlist(alldir) + def missing_attribute_error(self, name): + return oefmt(self.space.w_AttributeError, + "namespace '%s' has no attribute %s", self.name, name) + W_CPPNamespace.typedef = TypeDef( 'CPPNamespace', @@ -892,13 +883,42 @@ class W_CPPClass(W_CPPScope): _attrs_ = ['space', 'name', 'handle', 'methods', 'datamembers'] - _immutable_fields_ = ['kind', 'constructor', 'methods[*]', 'datamembers[*]'] - - kind = "class" + _immutable_fields_ = ['handle', 'constructor', 'methods[*]', 'datamembers[*]'] def __init__(self, space, name, opaque_handle): W_CPPScope.__init__(self, space, name, opaque_handle) + 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)) + cppmethod = self._make_cppfunction(pyname, idx) + methods_temp.setdefault(pyname, []).append(cppmethod) + # the following covers the case where the only kind of operator[](idx) + # returns are the ones that produce non-const references; these can be + # used for __getitem__ just as much as for __setitem__, though + if not "__getitem__" in methods_temp: + try: + for m in methods_temp["__setitem__"]: + cppmethod = self._make_cppfunction("__getitem__", m.index) + methods_temp.setdefault("__getitem__", []).append(cppmethod) + except KeyError: + pass # just means there's no __setitem__ either + + # create the overload methods from the method sets + for pyname, methods in methods_temp.iteritems(): + CPPMethodSort(methods).sort() + if pyname == self.name: + overload = W_CPPConstructorOverload(self.space, self, methods[:]) + else: + overload = W_CPPOverload(self.space, self, methods[:]) + self.methods[pyname] = overload + 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) @@ -937,9 +957,6 @@ datamember = W_CPPDataMember(self.space, self, type_name, offset) self.datamembers[datamember_name] = datamember - def construct(self): - return self.get_overload(self.name).call(None, []) - def find_overload(self, name): raise self.missing_attribute_error(name) @@ -965,6 +982,11 @@ bases.append(self.space.newtext(base_name)) return self.space.newlist(bases) + def missing_attribute_error(self, name): + return oefmt(self.space.w_AttributeError, + "class '%s' has no attribute %s", self.name, name) + + W_CPPClass.typedef = TypeDef( 'CPPClass', type_name = interp_attrproperty('name', W_CPPClass, wrapfn="newtext"), From pypy.commits at gmail.com Sat Oct 7 09:08:54 2017 From: pypy.commits at gmail.com (wlav) Date: Sat, 07 Oct 2017 06:08:54 -0700 (PDT) Subject: [pypy-commit] pypy cppyy-packaging: simplify (namespaces always lazy) Message-ID: <59d8d1e6.cd5e1c0a.120cc.c44d@mx.google.com> Author: Wim Lavrijsen Branch: cppyy-packaging Changeset: r92590:2aa24a1ebd73 Date: 2017-08-09 20:33 -0700 http://bitbucket.org/pypy/pypy/changeset/2aa24a1ebd73/ Log: simplify (namespaces always lazy) diff --git a/pypy/module/_cppyy/pythonify.py b/pypy/module/_cppyy/pythonify.py --- a/pypy/module/_cppyy/pythonify.py +++ b/pypy/module/_cppyy/pythonify.py @@ -88,45 +88,23 @@ return method -def make_cppnamespace(scope, namespace_name, cppns, build_in_full=True): +def make_cppnamespace(scope, namespace_name, cppns, build_in_full=False): # build up a representation of a C++ namespace (namespaces are classes) # create a meta class to allow properties (for static data write access) metans = type(CPPNamespace)(namespace_name+'_meta', (CPPNamespace,), {}) - if cppns: - d = {"_cpp_proxy" : cppns} - else: - d = dict() - def cpp_proxy_loader(cls): - import _cppyy - cpp_proxy = _cppyy._scope_byname(cls.__name__ != '::' and cls.__name__ or '') - del cls.__class__._cpp_proxy - cls._cpp_proxy = cpp_proxy - return cpp_proxy - metans._cpp_proxy = property(cpp_proxy_loader) - # create the python-side C++ namespace representation, cache in scope if given + d = {"_cpp_proxy" : cppns} pycppns = metans(namespace_name, (object,), d) if scope: setattr(scope, namespace_name, pycppns) - if build_in_full: # if False, rely on lazy build-up - # insert static methods into the "namespace" dictionary - for func_name in cppns.get_method_names(): - cppol = cppns.get_overload(func_name) - pyfunc = make_static_function(func_name, cppol) - setattr(pycppns, func_name, pyfunc) - - # add all data members to the dictionary of the class to be created, and - # static ones also to the meta class (needed for property setters) - for dm_name in cppns.get_datamember_names(): - cppdm = cppns.get_datamember(dm_name) - setattr(pycppns, dm_name, cppdm) - setattr(metans, dm_name, cppdm) - - modname = pycppns.__name__.replace('::', '.') - sys.modules['cppyy.gbl.'+modname] = pycppns # note naming (cppyy) + # install as modules to allow importing from (note naming: cppyy) + modname = 'cppyy.gbl' + if scope: + modname = 'cppyy.gbl.'+pycppns.__name__.replace('::', '.') + sys.modules[modname] = pycppns return pycppns def _drop_cycles(bases): @@ -148,7 +126,7 @@ return instance return __new__ -def make_pycppclass(scope, class_name, final_class_name, cppclass): +def make_cppclass(scope, class_name, final_class_name, cppclass): # get a list of base classes for class creation bases = [get_pycppclass(base) for base in cppclass.get_base_names()] @@ -229,7 +207,7 @@ pycppitem = make_cppnamespace(scope, true_name, cppitem) setattr(scope, name, pycppitem) else: - pycppitem = make_pycppclass(scope, true_name, name, cppitem) + pycppitem = make_cppclass(scope, true_name, name, cppitem) # templates if not cppitem: @@ -423,15 +401,13 @@ # function generator callback _cppyy._set_function_generator(fngen_callback) - # user interface objects (note the two-step of not calling scope_byname here: - # creation of global functions may cause the creation of classes in the global - # namespace, so gbl must exist at that point to cache them) + # user interface objects global gbl - gbl = make_cppnamespace(None, "::", None, False) # global C++ namespace + gbl = make_cppnamespace(None, '::', _cppyy._scope_byname('')) gbl.__doc__ = "Global C++ namespace." # pre-create std to allow direct importing - gbl.std = make_cppnamespace(None, "std", None, False) + gbl.std = make_cppnamespace(gbl, 'std', _cppyy._scope_byname('std')) # install a type for enums to refer to # TODO: this is correct for C++98, not for C++11 and in general there will @@ -444,10 +420,6 @@ # install for user access _cppyy.gbl = gbl - # install as modules to allow importing from (note naming: cppyy) - sys.modules['cppyy.gbl'] = gbl - sys.modules['cppyy.gbl.std'] = gbl.std - # user-defined pythonizations interface _pythonizations = {} def add_pythonization(class_name, callback): From pypy.commits at gmail.com Sat Oct 7 09:08:56 2017 From: pypy.commits at gmail.com (wlav) Date: Sat, 07 Oct 2017 06:08:56 -0700 (PDT) Subject: [pypy-commit] pypy cppyy-packaging: remove superfluous method Message-ID: <59d8d1e8.81901c0a.53f5d.e52c@mx.google.com> Author: Wim Lavrijsen Branch: cppyy-packaging Changeset: r92591:24329533531e Date: 2017-08-14 17:17 -0700 http://bitbucket.org/pypy/pypy/changeset/24329533531e/ Log: remove superfluous method 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 @@ -722,37 +722,6 @@ self.datamembers = {} # Idem as for self.methods: a type could hold itself by pointer. - 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)) - cppmethod = self._make_cppfunction(pyname, idx) - methods_temp.setdefault(pyname, []).append(cppmethod) - # the following covers the case where the only kind of operator[](idx) - # returns are the ones that produce non-const references; these can be - # used for __getitem__ just as much as for __setitem__, though - if not "__getitem__" in methods_temp: - try: - for m in methods_temp["__setitem__"]: - cppmethod = self._make_cppfunction("__getitem__", m.index) - methods_temp.setdefault("__getitem__", []).append(cppmethod) - except KeyError: - pass # just means there's no __setitem__ either - - # create the overload methods from the method sets - for pyname, methods in methods_temp.iteritems(): - CPPMethodSort(methods).sort() - if pyname == self.name: - overload = W_CPPConstructorOverload(self.space, self, methods[:]) - else: - overload = W_CPPOverload(self.space, self, methods[:]) - self.methods[pyname] = overload - def get_method_names(self): return self.space.newlist([self.space.newtext(name) for name in self.methods]) From pypy.commits at gmail.com Sat Oct 7 09:08:58 2017 From: pypy.commits at gmail.com (wlav) Date: Sat, 07 Oct 2017 06:08:58 -0700 (PDT) Subject: [pypy-commit] pypy cppyy-packaging: clearer naming and __cppname__ property for classes Message-ID: <59d8d1ea.2f97df0a.7aa8d.77ae@mx.google.com> Author: Wim Lavrijsen Branch: cppyy-packaging Changeset: r92592:0a1c3153a7e5 Date: 2017-09-22 15:17 -0700 http://bitbucket.org/pypy/pypy/changeset/0a1c3153a7e5/ Log: clearer naming and __cppname__ property for classes 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 @@ -13,7 +13,7 @@ '_set_function_generator': 'interp_cppyy.set_function_generator', '_register_class' : 'interp_cppyy.register_class', '_get_nullptr' : 'interp_cppyy.get_nullptr', - 'CPPInstanceBase' : 'interp_cppyy.W_CPPInstance', + 'CPPClassBase' : 'interp_cppyy.W_CPPClass', 'addressof' : 'interp_cppyy.addressof', 'bind_object' : 'interp_cppyy.bind_object', } 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 @@ -594,7 +594,7 @@ """Return a python string taking into account \0""" from pypy.module._cppyy import interp_cppyy - cppstr = space.interp_w(interp_cppyy.W_CPPInstance, w_self, can_be_None=False) + cppstr = space.interp_w(interp_cppyy.W_CPPClass, w_self, can_be_None=False) return space.newtext(c_stdstring2charp(space, cppstr._rawobject)) # setup pythonizations for later use at run-time 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 @@ -22,8 +22,8 @@ def get_rawobject(space, w_obj): - from pypy.module._cppyy.interp_cppyy import W_CPPInstance - cppinstance = space.interp_w(W_CPPInstance, 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) if cppinstance: rawobject = cppinstance.get_rawobject() assert lltype.typeOf(rawobject) == capi.C_OBJECT @@ -31,15 +31,15 @@ return capi.C_NULL_OBJECT def set_rawobject(space, w_obj, address): - from pypy.module._cppyy.interp_cppyy import W_CPPInstance - cppinstance = space.interp_w(W_CPPInstance, 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) if cppinstance: assert lltype.typeOf(cppinstance._rawobject) == capi.C_OBJECT cppinstance._rawobject = rffi.cast(capi.C_OBJECT, address) def get_rawobject_nonnull(space, w_obj): - from pypy.module._cppyy.interp_cppyy import W_CPPInstance - cppinstance = space.interp_w(W_CPPInstance, 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) if cppinstance: cppinstance._nullcheck() rawobject = cppinstance.get_rawobject() @@ -484,24 +484,24 @@ typecode = 'V' class InstanceRefConverter(TypeConverter): - _immutable_fields_ = ['typecode', 'cppclass'] + _immutable_fields_ = ['typecode', 'clsdecl'] typecode = 'V' - def __init__(self, space, cppclass): - from pypy.module._cppyy.interp_cppyy import W_CPPClass - assert isinstance(cppclass, W_CPPClass) - self.cppclass = cppclass + def __init__(self, space, clsdecl): + from pypy.module._cppyy.interp_cppyy import W_CPPClassDecl + assert isinstance(clsdecl, W_CPPClassDecl) + self.clsdecl = clsdecl def _unwrap_object(self, space, w_obj): - from pypy.module._cppyy.interp_cppyy import W_CPPInstance - if isinstance(w_obj, W_CPPInstance): - if capi.c_is_subtype(space, w_obj.cppclass, self.cppclass): + 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): rawobject = w_obj.get_rawobject() - offset = capi.c_base_offset(space, w_obj.cppclass, self.cppclass, rawobject, 1) + offset = capi.c_base_offset(space, w_obj.cppclass, self.clsdecl, rawobject, 1) obj_address = capi.direct_ptradd(rawobject, offset) return rffi.cast(capi.C_OBJECT, obj_address) raise oefmt(space.w_TypeError, - "cannot pass %T as %s", w_obj, self.cppclass.name) + "cannot pass %T as %s", w_obj, self.clsdecl.name) def cffi_type(self, space): state = space.fromcache(ffitypes.State) @@ -527,7 +527,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.cppclass, do_cast=False) + return interp_cppyy.wrap_cppobject(space, address, self.clsdecl, do_cast=False) def to_memory(self, space, w_obj, w_value, offset): self._is_abstract(space) @@ -548,7 +548,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.cppclass, do_cast=False) + return interp_cppyy.wrap_cppobject(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)) @@ -574,15 +574,15 @@ raise FastCallNotPossible def finalize_call(self, space, w_obj, call_local): - from pypy.module._cppyy.interp_cppyy import W_CPPInstance - assert isinstance(w_obj, W_CPPInstance) + from pypy.module._cppyy.interp_cppyy import W_CPPClass + assert isinstance(w_obj, W_CPPClass) r = rffi.cast(rffi.VOIDPP, call_local) w_obj._rawobject = rffi.cast(capi.C_OBJECT, r[0]) 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.cppclass, + return interp_cppyy.wrap_cppobject(space, address, self.clsdecl, do_cast=False, is_ref=True) class StdStringConverter(InstanceConverter): @@ -593,8 +593,8 @@ InstanceConverter.__init__(self, space, cppclass) def _unwrap_object(self, space, w_obj): - from pypy.module._cppyy.interp_cppyy import W_CPPInstance - if isinstance(w_obj, W_CPPInstance): + from pypy.module._cppyy.interp_cppyy import W_CPPClass + if isinstance(w_obj, W_CPPClass): arg = InstanceConverter._unwrap_object(self, space, w_obj) return capi.c_stdstring2stdstring(space, arg) else: @@ -603,15 +603,15 @@ def to_memory(self, space, w_obj, w_value, offset): try: address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) - assign = self.cppclass.get_overload("__assign__") + assign = self.clsdecl.get_overload("__assign__") from pypy.module._cppyy import interp_cppyy assign.call( - interp_cppyy.wrap_cppobject(space, address, self.cppclass, do_cast=False), [w_value]) + interp_cppyy.wrap_cppobject(space, address, self.clsdecl, do_cast=False), [w_value]) except Exception: InstanceConverter.to_memory(self, space, w_obj, w_value, offset) def free_argument(self, space, arg, call_local): - capi.c_destruct(space, self.cppclass, rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, arg)[0])) + capi.c_destruct(space, self.clsdecl, rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, arg)[0])) class StdStringRefConverter(InstancePtrConverter): _immutable_fields_ = ['cppclass', 'typecode'] @@ -710,19 +710,19 @@ # 4) generalized cases (covers basically all user classes) from pypy.module._cppyy import interp_cppyy - cppclass = interp_cppyy.scope_byname(space, clean_name) - if cppclass: + scope_decl = interp_cppyy.scope_byname(space, clean_name) + if scope_decl: # type check for the benefit of the annotator - from pypy.module._cppyy.interp_cppyy import W_CPPClass - cppclass = space.interp_w(W_CPPClass, cppclass, can_be_None=False) + from pypy.module._cppyy.interp_cppyy import W_CPPClassDecl + clsdecl = space.interp_w(W_CPPClassDecl, scope_decl, can_be_None=False) if compound == "*": - return InstancePtrConverter(space, cppclass) + return InstancePtrConverter(space, clsdecl) elif compound == "&": - return InstanceRefConverter(space, cppclass) + return InstanceRefConverter(space, clsdecl) elif compound == "**": - return InstancePtrPtrConverter(space, cppclass) + return InstancePtrPtrConverter(space, clsdecl) elif compound == "": - return InstanceConverter(space, cppclass) + return InstanceConverter(space, clsdecl) elif capi.c_is_enum(space, clean_name): return _converters['unsigned'](space, default) 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 @@ -281,8 +281,8 @@ cppclass = interp_cppyy.scope_byname(space, clean_name) if cppclass: # type check for the benefit of the annotator - from pypy.module._cppyy.interp_cppyy import W_CPPClass - cppclass = space.interp_w(W_CPPClass, cppclass, can_be_None=False) + from pypy.module._cppyy.interp_cppyy import W_CPPClassDecl + cppclass = space.interp_w(W_CPPClassDecl, cppclass, can_be_None=False) if compound == '': return InstanceExecutor(space, cppclass) elif compound == '*' or compound == '&': 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 @@ -34,7 +34,7 @@ class State(object): def __init__(self, space): self.cppscope_cache = { - "void" : W_CPPClass(space, "void", capi.C_NULL_TYPE) } + "void" : W_CPPClassDecl(space, "void", capi.C_NULL_TYPE) } self.w_nullptr = None self.cpptemplate_cache = {} self.cppclass_registry = {} @@ -77,11 +77,11 @@ if opaque_handle: final_name = capi.c_final_name(space, opaque_handle) if capi.c_is_namespace(space, opaque_handle): - cppscope = W_CPPNamespace(space, final_name, opaque_handle) + cppscope = W_CPPNamespaceDecl(space, final_name, opaque_handle) elif capi.c_has_complex_hierarchy(space, opaque_handle): - cppscope = W_ComplexCPPClass(space, final_name, opaque_handle) + cppscope = W_CPPComplexClass(space, final_name, opaque_handle) else: - cppscope = W_CPPClass(space, final_name, opaque_handle) + cppscope = W_CPPClassDecl(space, final_name, opaque_handle) state.cppscope_cache[name] = cppscope cppscope._build_methods() @@ -119,8 +119,8 @@ state.w_fngen_callback = w_callback def register_class(space, w_pycppclass): - w_cppclass = space.findattr(w_pycppclass, space.newtext("_cpp_proxy")) - cppclass = space.interp_w(W_CPPClass, w_cppclass, can_be_None=False) + w_cppclass = space.findattr(w_pycppclass, space.newtext("__cppdecl__")) + cppclass = space.interp_w(W_CPPClassDecl, w_cppclass, can_be_None=False) # add back-end specific method pythonizations (doing this on the wrapped # class allows simple aliasing of methods) capi.pythonize(space, cppclass.name, w_pycppclass) @@ -169,7 +169,7 @@ @staticmethod def unpack_cppthis(space, w_cppinstance, declaring_scope): - cppinstance = space.interp_w(W_CPPInstance, w_cppinstance, can_be_None=False) + cppinstance = space.interp_w(W_CPPClass, w_cppinstance, can_be_None=False) cppinstance._nullcheck() return cppinstance.get_cppthis(declaring_scope) @@ -593,7 +593,7 @@ def call(self, w_cppinstance, args_w): 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_CPPInstance, w_cppinstance, can_be_None=True) + 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) @@ -650,7 +650,7 @@ return offset def get(self, w_cppinstance, w_pycppclass): - cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance, can_be_None=True) + cppinstance = self.space.interp_w(W_CPPClass, w_cppinstance, can_be_None=True) if not cppinstance: raise oefmt(self.space.w_ReferenceError, "attribute access requires an instance") @@ -658,7 +658,7 @@ return self.converter.from_memory(self.space, w_cppinstance, w_pycppclass, offset) def set(self, w_cppinstance, w_value): - cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance, can_be_None=True) + cppinstance = self.space.interp_w(W_CPPClass, w_cppinstance, can_be_None=True) if not cppinstance: raise oefmt(self.space.w_ReferenceError, "attribute access requires an instance") @@ -704,7 +704,7 @@ except Exception: return space.w_False -class W_CPPScope(W_Root): +class W_CPPScopeDecl(W_Root): _attrs_ = ['space', 'name', 'handle', 'methods', 'datamembers'] _immutable_fields_ = ['handle', 'name'] @@ -725,6 +725,7 @@ def get_method_names(self): return self.space.newlist([self.space.newtext(name) for name in self.methods]) + @unwrap_spec(name='text') def get_overload(self, name): try: return self.methods[name] @@ -737,6 +738,7 @@ def get_datamember_names(self): return self.space.newlist([self.space.newtext(name) for name in self.datamembers]) + @unwrap_spec(name='text') def get_datamember(self, name): try: return self.datamembers[name] @@ -746,7 +748,8 @@ self.datamembers[name] = new_dm return new_dm - def dispatch(self, name, signature): + @unwrap_spec(name='text', signature='text') + def scope__dispatch__(self, name, signature): overload = self.get_overload(name) sig = '(%s)' % signature for f in overload.functions: @@ -765,8 +768,7 @@ # with info from multiple dictionaries and do not need to bother with meta # classes for inheritance. Both are python classes, though, and refactoring # may be in order at some point. -class W_CPPNamespace(W_CPPScope): - +class W_CPPNamespaceDecl(W_CPPScopeDecl): 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) @@ -838,25 +840,24 @@ "namespace '%s' has no attribute %s", self.name, name) -W_CPPNamespace.typedef = TypeDef( - 'CPPNamespace', - get_method_names = interp2app(W_CPPNamespace.get_method_names), - get_overload = interp2app(W_CPPNamespace.get_overload, unwrap_spec=['self', 'text']), - get_datamember_names = interp2app(W_CPPNamespace.get_datamember_names), - get_datamember = interp2app(W_CPPNamespace.get_datamember, unwrap_spec=['self', 'text']), - is_namespace = interp2app(W_CPPNamespace.is_namespace), - __dir__ = interp2app(W_CPPNamespace.ns__dir__), +W_CPPNamespaceDecl.typedef = TypeDef( + 'CPPNamespaceDecl', + get_method_names = interp2app(W_CPPNamespaceDecl.get_method_names), + get_overload = interp2app(W_CPPNamespaceDecl.get_overload), + get_datamember_names = interp2app(W_CPPNamespaceDecl.get_datamember_names), + get_datamember = interp2app(W_CPPNamespaceDecl.get_datamember), + is_namespace = interp2app(W_CPPNamespaceDecl.is_namespace), + __cppname__ = interp_attrproperty('name', W_CPPNamespaceDecl, wrapfn="newtext"), + __dispatch__ = interp2app(W_CPPNamespaceDecl.scope__dispatch__), + __dir__ = interp2app(W_CPPNamespaceDecl.ns__dir__), ) -W_CPPNamespace.typedef.acceptable_as_base_class = False +W_CPPNamespaceDecl.typedef.acceptable_as_base_class = False -class W_CPPClass(W_CPPScope): +class W_CPPClassDecl(W_CPPScopeDecl): _attrs_ = ['space', 'name', 'handle', 'methods', 'datamembers'] _immutable_fields_ = ['handle', 'constructor', 'methods[*]', 'datamembers[*]'] - def __init__(self, space, name, opaque_handle): - W_CPPScope.__init__(self, space, name, opaque_handle) - def _build_methods(self): assert len(self.methods) == 0 methods_temp = {} @@ -956,22 +957,21 @@ "class '%s' has no attribute %s", self.name, name) -W_CPPClass.typedef = TypeDef( - 'CPPClass', - type_name = interp_attrproperty('name', W_CPPClass, wrapfn="newtext"), - get_base_names = interp2app(W_CPPClass.get_base_names), - get_method_names = interp2app(W_CPPClass.get_method_names), - get_overload = interp2app(W_CPPClass.get_overload, unwrap_spec=['self', 'text']), - get_datamember_names = interp2app(W_CPPClass.get_datamember_names), - get_datamember = interp2app(W_CPPClass.get_datamember, unwrap_spec=['self', 'text']), - is_namespace = interp2app(W_CPPClass.is_namespace), - dispatch = interp2app(W_CPPClass.dispatch, unwrap_spec=['self', 'text', 'text']) +W_CPPClassDecl.typedef = TypeDef( + 'CPPClassDecl', + get_base_names = interp2app(W_CPPClassDecl.get_base_names), + get_method_names = interp2app(W_CPPClassDecl.get_method_names), + get_overload = interp2app(W_CPPClassDecl.get_overload), + get_datamember_names = interp2app(W_CPPClassDecl.get_datamember_names), + get_datamember = interp2app(W_CPPClassDecl.get_datamember), + is_namespace = interp2app(W_CPPClassDecl.is_namespace), + __cppname__ = interp_attrproperty('name', W_CPPClassDecl, wrapfn="newtext"), + __dispatch__ = interp2app(W_CPPClassDecl.scope__dispatch__) ) -W_CPPClass.typedef.acceptable_as_base_class = False +W_CPPClassDecl.typedef.acceptable_as_base_class = False -class W_ComplexCPPClass(W_CPPClass): - +class W_CPPComplexClassDecl(W_CPPClassDecl): def get_base_offset(self, cppinstance, calling_scope): assert self == cppinstance.cppclass offset = capi.c_base_offset(self.space, @@ -983,18 +983,18 @@ offset = self.get_base_offset(cppinstance, calling_scope) return capi.direct_ptradd(cppinstance.get_rawobject(), offset) -W_ComplexCPPClass.typedef = TypeDef( - 'ComplexCPPClass', - type_name = interp_attrproperty('name', W_CPPClass, wrapfn="newtext"), - get_base_names = interp2app(W_ComplexCPPClass.get_base_names), - get_method_names = interp2app(W_ComplexCPPClass.get_method_names), - get_overload = interp2app(W_ComplexCPPClass.get_overload, unwrap_spec=['self', 'text']), - get_datamember_names = interp2app(W_ComplexCPPClass.get_datamember_names), - get_datamember = interp2app(W_ComplexCPPClass.get_datamember, unwrap_spec=['self', 'text']), - is_namespace = interp2app(W_ComplexCPPClass.is_namespace), - dispatch = interp2app(W_CPPClass.dispatch, unwrap_spec=['self', 'text', 'text']) +W_CPPComplexClassDecl.typedef = TypeDef( + 'CPPComplexClassDecl', + get_base_names = interp2app(W_CPPComplexClassDecl.get_base_names), + get_method_names = interp2app(W_CPPComplexClassDecl.get_method_names), + get_overload = interp2app(W_CPPComplexClassDecl.get_overload), + get_datamember_names = interp2app(W_CPPComplexClassDecl.get_datamember_names), + get_datamember = interp2app(W_CPPComplexClassDecl.get_datamember), + is_namespace = interp2app(W_CPPComplexClassDecl.is_namespace), + __cppname__ = interp_attrproperty('name', W_CPPComplexClassDecl, wrapfn="newtext"), + __dispatch__ = interp2app(W_CPPComplexClassDecl.scope__dispatch__) ) -W_ComplexCPPClass.typedef.acceptable_as_base_class = False +W_CPPComplexClassDecl.typedef.acceptable_as_base_class = False class W_CPPTemplateType(W_Root): @@ -1018,7 +1018,7 @@ W_CPPTemplateType.typedef.acceptable_as_base_class = False -class W_CPPInstance(W_Root): +class W_CPPClass(W_Root): _attrs_ = ['space', 'cppclass', '_rawobject', 'isref', 'python_owns', 'finalizer_registered'] _immutable_fields_ = ["cppclass", "isref"] @@ -1094,8 +1094,8 @@ # find a global overload in gbl, in __gnu_cxx (for iterators), or in the # scopes of the argument classes (TODO: implement that last option) try: - # TODO: expecting w_other to be an W_CPPInstance is too limiting - other = self.space.interp_w(W_CPPInstance, w_other, can_be_None=False) + # TODO: expecting w_other to be an W_CPPClass is too limiting + other = self.space.interp_w(W_CPPClass, w_other, can_be_None=False) for name in ["", "__gnu_cxx", "__1"]: nss = scope_byname(self.space, name) meth_idx = capi.c_get_global_operator( @@ -1117,7 +1117,7 @@ # 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_CPPInstance, w_other, can_be_None=False) # TODO: factor out + 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) return self.space.newbool(iseq) @@ -1160,20 +1160,20 @@ if self.python_owns: self.destruct() -W_CPPInstance.typedef = TypeDef( - 'CPPInstance', - cppclass = interp_attrproperty_w('cppclass', cls=W_CPPInstance), - _python_owns = GetSetProperty(W_CPPInstance.fget_python_owns, W_CPPInstance.fset_python_owns), - __init__ = interp2app(W_CPPInstance.instance__init__), - __eq__ = interp2app(W_CPPInstance.instance__eq__), - __ne__ = interp2app(W_CPPInstance.instance__ne__), - __nonzero__ = interp2app(W_CPPInstance.instance__nonzero__), - __len__ = interp2app(W_CPPInstance.instance__len__), - __cmp__ = interp2app(W_CPPInstance.instance__cmp__), - __repr__ = interp2app(W_CPPInstance.instance__repr__), - __destruct__ = interp2app(W_CPPInstance.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), + __init__ = interp2app(W_CPPClass.instance__init__), + __eq__ = interp2app(W_CPPClass.instance__eq__), + __ne__ = interp2app(W_CPPClass.instance__ne__), + __nonzero__ = interp2app(W_CPPClass.instance__nonzero__), + __len__ = interp2app(W_CPPClass.instance__len__), + __cmp__ = interp2app(W_CPPClass.instance__cmp__), + __repr__ = interp2app(W_CPPClass.instance__repr__), + __destruct__ = interp2app(W_CPPClass.destruct), ) -W_CPPInstance.typedef.acceptable_as_base_class = True +W_CPPClass.typedef.acceptable_as_base_class = True class MemoryRegulator: @@ -1185,7 +1185,7 @@ # Note that for now, the associated test carries an m_padding to make # a difference in the addresses. def __init__(self): - self.objects = rweakref.RWeakValueDictionary(int, W_CPPInstance) + self.objects = rweakref.RWeakValueDictionary(int, W_CPPClass) def register(self, obj): if not obj._rawobject: @@ -1233,8 +1233,8 @@ w_pycppclass = get_pythonized_cppclass(space, actual) offset = capi.c_base_offset1(space, actual, cppclass, rawobject, -1) rawobject = capi.direct_ptradd(rawobject, offset) - w_cppclass = space.findattr(w_pycppclass, space.newtext("_cpp_proxy")) - cppclass = space.interp_w(W_CPPClass, w_cppclass, can_be_None=False) + w_cppclass = space.findattr(w_pycppclass, space.newtext("__cppdecl__")) + cppclass = space.interp_w(W_CPPClassDecl, w_cppclass, 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 @@ -1251,8 +1251,8 @@ return obj # fresh creation - w_cppinstance = space.allocate_instance(W_CPPInstance, w_pycppclass) - cppinstance = space.interp_w(W_CPPInstance, w_cppinstance, can_be_None=False) + 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) memory_regulator.register(cppinstance) return w_cppinstance @@ -1281,11 +1281,11 @@ 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("_cpp_proxy")) + 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: raise oefmt(space.w_TypeError, "no such class: %s", space.text_w(w_pycppclass)) - cppclass = space.interp_w(W_CPPClass, w_cppclass, can_be_None=False) + cppclass = space.interp_w(W_CPPClassDecl, w_cppclass, can_be_None=False) return wrap_cppobject(space, rawobject, cppclass, do_cast=cast, python_owns=owns) diff --git a/pypy/module/_cppyy/pythonify.py b/pypy/module/_cppyy/pythonify.py --- a/pypy/module/_cppyy/pythonify.py +++ b/pypy/module/_cppyy/pythonify.py @@ -4,11 +4,10 @@ import sys -# For now, keep namespaces and classes separate as namespaces are extensible -# with info from multiple dictionaries and do not need to bother with meta -# classes for inheritance. Both are python classes, though, and refactoring -# may be in order at some point. -class CPPScope(type): +# Metaclasses are needed to store C++ static data members as properties. Since +# the interp-level does not support metaclasses, they are created at app-level. +# These are the metaclass base classes: +class CPPMetaScope(type): def __getattr__(self, name): try: return get_pycppitem(self, name) # will cache on self @@ -16,14 +15,17 @@ raise AttributeError("%s object has no attribute '%s' (details: %s)" % (self, name, str(e))) -class CPPNamespace(CPPScope): - def __dir__(cls): - return cls._cpp_proxy.__dir__() +class CPPMetaNamespace(CPPMetaScope): + def __dir__(self): + return self.__cppdecl__.__dir__() -class CPPClass(CPPScope): +class CPPMetaClass(CPPMetaScope): pass -# class CPPInstance defined in _init_pythonify() +# namespace base class (class base class defined in _init_pythonify) +class CPPNamespace(object): + __metatype__ = CPPMetaNamespace + class CPPTemplate(object): def __init__(self, name, scope=None): @@ -88,24 +90,25 @@ return method -def make_cppnamespace(scope, namespace_name, cppns, build_in_full=False): +def make_cppnamespace(scope, name, decl): # build up a representation of a C++ namespace (namespaces are classes) - # create a meta class to allow properties (for static data write access) - metans = type(CPPNamespace)(namespace_name+'_meta', (CPPNamespace,), {}) + # create a metaclass to allow properties (for static data write access) + import _cppyy + ns_meta = type(name+'_meta', (CPPMetaNamespace,), {}) # create the python-side C++ namespace representation, cache in scope if given - d = {"_cpp_proxy" : cppns} - pycppns = metans(namespace_name, (object,), d) + d = {"__cppdecl__" : decl, "__cppname__" : decl.__cppname__ } + pyns = ns_meta(name, (CPPNamespace,), d) if scope: - setattr(scope, namespace_name, pycppns) + setattr(scope, name, pyns) # install as modules to allow importing from (note naming: cppyy) modname = 'cppyy.gbl' if scope: - modname = 'cppyy.gbl.'+pycppns.__name__.replace('::', '.') - sys.modules[modname] = pycppns - return pycppns + modname = 'cppyy.gbl.'+pyns.__cppname__.replace('::', '.') + sys.modules[modname] = pyns + return pyns def _drop_cycles(bases): # TODO: figure this out, as it seems to be a PyPy bug?! @@ -126,12 +129,12 @@ return instance return __new__ -def make_cppclass(scope, class_name, final_class_name, cppclass): +def make_cppclass(scope, class_name, final_class_name, decl): # get a list of base classes for class creation - bases = [get_pycppclass(base) for base in cppclass.get_base_names()] + bases = [get_pycppclass(base) for base in decl.get_base_names()] if not bases: - bases = [CPPInstance,] + bases = [CPPClass,] else: # it's technically possible that the required class now has been built # if one of the base classes uses it in e.g. a function interface @@ -140,50 +143,50 @@ except KeyError: pass - # prepare dictionary for meta class + # prepare dictionary for metaclass d_meta = {} # prepare dictionary for python-side C++ class representation def dispatch(self, name, signature): - cppol = cppclass.dispatch(name, signature) + cppol = decl.dispatch(name, signature) return types.MethodType(make_method(name, cppol), self, type(self)) - d_class = {"_cpp_proxy" : cppclass, - "__dispatch__" : dispatch, + d_class = {"__cppdecl__" : decl, + "__cppname__" : decl.__cppname__, "__new__" : make_new(class_name), } # insert (static) methods into the class dictionary - for name in cppclass.get_method_names(): - cppol = cppclass.get_overload(name) + for name in decl.get_method_names(): + cppol = decl.get_overload(name) if cppol.is_static(): d_class[name] = make_static_function(name, cppol) else: d_class[name] = make_method(name, cppol) # add all data members to the dictionary of the class to be created, and - # static ones also to the meta class (needed for property setters) - for name in cppclass.get_datamember_names(): - cppdm = cppclass.get_datamember(name) + # static ones also to the metaclass (needed for property setters) + for name in decl.get_datamember_names(): + cppdm = decl.get_datamember(name) d_class[name] = cppdm if cppdm.is_static(): d_meta[name] = cppdm - # create a meta class to allow properties (for static data write access) + # create a metaclass to allow properties (for static data write access) metabases = [type(base) for base in bases] - metacpp = type(CPPClass)(class_name+'_meta', _drop_cycles(metabases), d_meta) + metacpp = type(CPPMetaScope)(class_name+'_meta', _drop_cycles(metabases), d_meta) # create the python-side C++ class - pycppclass = metacpp(class_name, _drop_cycles(bases), d_class) + pycls = metacpp(class_name, _drop_cycles(bases), d_class) # store the class on its outer scope - setattr(scope, final_class_name, pycppclass) + setattr(scope, final_class_name, pycls) # the call to register will add back-end specific pythonizations and thus # needs to run first, so that the generic pythonizations can use them import _cppyy - _cppyy._register_class(pycppclass) - _pythonize(pycppclass) - return pycppclass + _cppyy._register_class(pycls) + _pythonize(pycls) + return pycls def make_cpptemplatetype(scope, template_name): return CPPTemplate(template_name, scope) @@ -203,11 +206,14 @@ # classes cppitem = _cppyy._scope_byname(true_name) if cppitem: + name = true_name + if scope != gbl: + name = true_name[len(scope.__cppname__)+2:] if cppitem.is_namespace(): - pycppitem = make_cppnamespace(scope, true_name, cppitem) + pycppitem = make_cppnamespace(scope, name, cppitem) setattr(scope, name, pycppitem) else: - pycppitem = make_cppclass(scope, true_name, name, cppitem) + pycppitem = make_cppclass(scope, name, true_name, cppitem) # templates if not cppitem: @@ -219,7 +225,7 @@ # functions if not cppitem: try: - cppitem = scope._cpp_proxy.get_overload(name) + cppitem = scope.__cppdecl__.get_overload(name) pycppitem = make_static_function(name, cppitem) setattr(scope.__class__, name, pycppitem) pycppitem = getattr(scope, name) # binds function as needed @@ -229,7 +235,7 @@ # data if not cppitem: try: - cppdm = scope._cpp_proxy.get_datamember(name) + cppdm = scope.__cppdecl__.get_datamember(name) setattr(scope, name, cppdm) if cppdm.is_static(): setattr(scope.__class__, name, cppdm) @@ -388,11 +394,11 @@ # at pypy-c startup, rather than on the "import _cppyy" statement import _cppyy - # root of all proxy classes: CPPInstance in pythonify exists to combine the - # CPPClass meta class with the interp-level CPPInstanceBase - global CPPInstance - class CPPInstance(_cppyy.CPPInstanceBase): - __metaclass__ = CPPClass + # root of all proxy classes: CPPClass in pythonify exists to combine the + # CPPMetaScope metaclass with the interp-level CPPClassBase + global CPPClass + class CPPClass(_cppyy.CPPClassBase): + __metaclass__ = CPPMetaScope pass # class generator callback @@ -403,8 +409,9 @@ # user interface objects global gbl - gbl = make_cppnamespace(None, '::', _cppyy._scope_byname('')) - gbl.__doc__ = "Global C++ namespace." + gbl = make_cppnamespace(None, 'gbl', _cppyy._scope_byname('')) + gbl.__module__ = 'cppyy' + gbl.__doc__ = 'Global C++ namespace.' # pre-create std to allow direct importing gbl.std = make_cppnamespace(gbl, 'std', _cppyy._scope_byname('std')) diff --git a/pypy/module/_cppyy/src/dummy_backend.cxx b/pypy/module/_cppyy/src/dummy_backend.cxx --- a/pypy/module/_cppyy/src/dummy_backend.cxx +++ b/pypy/module/_cppyy/src/dummy_backend.cxx @@ -164,6 +164,14 @@ static long s_scope_id = 0; static long s_method_id = 0; + { // namespace '' + s_handles[""] = (cppyy_scope_t)++s_scope_id; + } + + { // namespace std + s_handles["std"] = (cppyy_scope_t)++s_scope_id; + } + { // class example01 -- s_handles["example01"] = (cppyy_scope_t)++s_scope_id; diff --git a/pypy/module/_cppyy/test/test_cppyy.py b/pypy/module/_cppyy/test/test_cppyy.py --- a/pypy/module/_cppyy/test/test_cppyy.py +++ b/pypy/module/_cppyy/test/test_cppyy.py @@ -95,7 +95,7 @@ assert t.get_overload("getCount").call(None) == 0 - e1 = t.get_overload(t.type_name).call(None, 7) + e1 = t.get_overload(t.__cppname__).call(None, 7) assert t.get_overload("getCount").call(None) == 1 res = t.get_overload("addDataToInt").call(e1, 4) assert res == 11 @@ -105,8 +105,8 @@ assert t.get_overload("getCount").call(None) == 0 raises(ReferenceError, 't.get_overload("addDataToInt").call(e1, 4)') - e1 = t.get_overload(t.type_name).call(None, 7) - e2 = t.get_overload(t.type_name).call(None, 8) + e1 = t.get_overload(t.__cppname__).call(None, 7) + e2 = t.get_overload(t.__cppname__).call(None, 8) assert t.get_overload("getCount").call(None) == 2 e1.__destruct__() assert t.get_overload("getCount").call(None) == 1 @@ -128,7 +128,7 @@ assert t.get_overload("getCount").call(None) == 0 - e1 = t.get_overload(t.type_name).call(None, 7) + e1 = t.get_overload(t.__cppname__).call(None, 7) assert t.get_overload("getCount").call(None) == 1 res = t.get_overload("addDataToInt").call(e1, 4) assert res == 11 @@ -138,8 +138,8 @@ gc.collect() assert t.get_overload("getCount").call(None) == 0 - e1 = t.get_overload(t.type_name).call(None, 7) - e2 = t.get_overload(t.type_name).call(None, 8) + e1 = t.get_overload(t.__cppname__).call(None, 7) + e2 = t.get_overload(t.__cppname__).call(None, 8) assert t.get_overload("getCount").call(None) == 2 e1 = None gc.collect() @@ -159,7 +159,7 @@ assert t.get_overload("getCount").call(None) == 0 - e1 = t.get_overload(t.type_name).call(None, 7) + e1 = t.get_overload(t.__cppname__).call(None, 7) assert t.get_overload("getCount").call(None) == 1 assert e1._python_owns == True e1._python_owns = False @@ -178,12 +178,12 @@ t = self.example01 - e = t.get_overload(t.type_name).call(None, 13) + e = t.get_overload(t.__cppname__).call(None, 13) res = t.get_overload("addDataToDouble").call(e, 16) assert round(res-29, 8) == 0. e.__destruct__() - e = t.get_overload(t.type_name).call(None, -13) + e = t.get_overload(t.__cppname__).call(None, -13) res = t.get_overload("addDataToDouble").call(e, 16) assert round(res-3, 8) == 0. e.__destruct__() @@ -196,7 +196,7 @@ t = self.example01 - e = t.get_overload(t.type_name).call(None, 42) + e = t.get_overload(t.__cppname__).call(None, 42) res = t.get_overload("addDataToAtoi").call(e, "13") assert res == 55 res = t.get_overload("addToStringValue").call(e, "12") # TODO: this leaks @@ -213,12 +213,12 @@ t1 = self.example01 t2 = self.payload - pl = t2.get_overload(t2.type_name).call(None, 3.14) + pl = t2.get_overload(t2.__cppname__).call(None, 3.14) assert round(t2.get_overload("getData").call(pl)-3.14, 8) == 0 - t1.get_overload("staticSetPayload").call(None, pl, 41.) # now pl is a CPPInstance + t1.get_overload("staticSetPayload").call(None, pl, 41.) assert t2.get_overload("getData").call(pl) == 41. - e = t1.get_overload(t1.type_name).call(None, 50) + e = t1.get_overload(t1.__cppname__).call(None, 50) t1.get_overload("setPayload").call(e, pl); assert round(t2.get_overload("getData").call(pl)-50., 8) == 0 @@ -233,12 +233,12 @@ t1 = self.example01 t2 = self.payload - pl1 = t2.get_overload(t2.type_name).call(None, 3.14) + pl1 = t2.get_overload(t2.__cppname__).call(None, 3.14) assert round(t2.get_overload("getData").call(pl1)-3.14, 8) == 0 pl2 = t1.get_overload("staticCyclePayload").call(None, pl1, 38.) assert t2.get_overload("getData").call(pl2) == 38. - e = t1.get_overload(t1.type_name).call(None, 50) + e = t1.get_overload(t1.__cppname__).call(None, 50) pl2 = t1.get_overload("cyclePayload").call(e, pl1); assert round(t2.get_overload("getData").call(pl2)-50., 8) == 0 diff --git a/pypy/module/_cppyy/test/test_fragile.py b/pypy/module/_cppyy/test/test_fragile.py --- a/pypy/module/_cppyy/test/test_fragile.py +++ b/pypy/module/_cppyy/test/test_fragile.py @@ -34,7 +34,7 @@ assert fragile.B == fragile.B assert fragile.B().check() == ord('B') - raises(AttributeError, getattr, fragile.B().gime_no_such(), "_cpp_proxy") + raises(AttributeError, getattr, fragile.B().gime_no_such(), "__cppdecl__") assert fragile.C == fragile.C assert fragile.C().check() == ord('C') @@ -237,17 +237,35 @@ from cppyy.gbl.fragile import nested1 assert _cppyy.gbl.fragile.nested1 is nested1 + assert nested1.__name__ == 'nested1' + assert nested1.__module__ == 'cppyy.gbl.fragile' + assert nested1.__cppname__ == 'nested1' from cppyy.gbl.fragile.nested1 import A, nested2 assert _cppyy.gbl.fragile.nested1.A is A + assert A.__name__ == 'A' + assert A.__module__ == 'cppyy.gbl.fragile.nested1' + assert A.__cppname__ == 'fragile::nested1::A' assert _cppyy.gbl.fragile.nested1.nested2 is nested2 + assert A.__name__ == 'A' + assert A.__module__ == 'cppyy.gbl.fragile.nested1' + assert nested2.__cppname__ == 'fragile::nested1::nested2' from cppyy.gbl.fragile.nested1.nested2 import A, nested3 assert _cppyy.gbl.fragile.nested1.nested2.A is A + assert A.__name__ == 'A' + assert A.__module__ == 'cppyy.gbl.fragile.nested1' + assert A.__cppname__ == 'fragile::nested1::nested2::A' assert _cppyy.gbl.fragile.nested1.nested2.nested3 is nested3 + assert A.__name__ == 'A' + assert A.__module__ == 'cppyy.gbl.fragile.nested1' + assert nested3.__cppname__ == 'fragile::nested1::nested2::nested3' from cppyy.gbl.fragile.nested1.nested2.nested3 import A assert _cppyy.gbl.fragile.nested1.nested2.nested3.A is nested3.A + assert A.__name__ == 'A' + assert A.__module__ == 'cppyy.gbl.fragile.nested1' + assert A.__cppname__ == 'fragile::nested1::nested2::nested3::A' def test12_missing_casts(self): """Test proper handling when a hierarchy is not fully available""" diff --git a/pypy/module/_cppyy/test/test_zjit.py b/pypy/module/_cppyy/test/test_zjit.py --- a/pypy/module/_cppyy/test/test_zjit.py +++ b/pypy/module/_cppyy/test/test_zjit.py @@ -281,7 +281,7 @@ cls = interp_cppyy.scope_byname(space, "example01") inst = cls.get_overload("example01").call(None, [FakeInt(0)]) cppmethod = cls.get_overload(method_name) - assert isinstance(inst, interp_cppyy.W_CPPInstance) + assert isinstance(inst, interp_cppyy.W_CPPClass) i = 10 while i > 0: drv.jit_merge_point(inst=inst, cppmethod=cppmethod, i=i) From pypy.commits at gmail.com Sat Oct 7 09:09:00 2017 From: pypy.commits at gmail.com (wlav) Date: Sat, 07 Oct 2017 06:09:00 -0700 (PDT) Subject: [pypy-commit] pypy cppyy-packaging: fix typo Message-ID: <59d8d1ec.cd5e1c0a.120cc.c45a@mx.google.com> Author: Wim Lavrijsen Branch: cppyy-packaging Changeset: r92593:90cbc419e451 Date: 2017-10-02 22:48 -0700 http://bitbucket.org/pypy/pypy/changeset/90cbc419e451/ Log: fix typo 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 @@ -79,7 +79,7 @@ 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_CPPComplexClass(space, final_name, opaque_handle) + cppscope = W_CPPComplexClassDecl(space, final_name, opaque_handle) else: cppscope = W_CPPClassDecl(space, final_name, opaque_handle) state.cppscope_cache[name] = cppscope From pypy.commits at gmail.com Sat Oct 7 09:09:02 2017 From: pypy.commits at gmail.com (wlav) Date: Sat, 07 Oct 2017 06:09:02 -0700 (PDT) Subject: [pypy-commit] pypy cppyy-packaging: cleanup Message-ID: <59d8d1ee.2587df0a.e5230.7643@mx.google.com> Author: Wim Lavrijsen Branch: cppyy-packaging Changeset: r92594:16133916e940 Date: 2017-10-03 15:43 -0700 http://bitbucket.org/pypy/pypy/changeset/16133916e940/ Log: cleanup diff --git a/pypy/module/_cppyy/ffitypes.py b/pypy/module/_cppyy/ffitypes.py --- a/pypy/module/_cppyy/ffitypes.py +++ b/pypy/module/_cppyy/ffitypes.py @@ -12,9 +12,6 @@ class State(object): def __init__(self, space): - self.library = None - self.capi_calls = {} - nt = newtype # module from _cffi_backend # builtin types From pypy.commits at gmail.com Sat Oct 7 09:09:04 2017 From: pypy.commits at gmail.com (wlav) Date: Sat, 07 Oct 2017 06:09:04 -0700 (PDT) Subject: [pypy-commit] pypy cppyy-packaging: allow envar CPPYY_BACKEND_LIBRARY to direct the location of the backend library Message-ID: <59d8d1f0.1b88df0a.d65d7.3d70@mx.google.com> Author: Wim Lavrijsen Branch: cppyy-packaging Changeset: r92595:582dbcdc2741 Date: 2017-10-03 15:44 -0700 http://bitbucket.org/pypy/pypy/changeset/582dbcdc2741/ Log: allow envar CPPYY_BACKEND_LIBRARY to direct the location of the backend library 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 @@ -1,3 +1,4 @@ +import os from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib.rarithmetic import intmask from rpython.rlib import jit, jit_libffi, libffi, rdynload, objectmodel @@ -15,7 +16,7 @@ C_METHOD, C_INDEX, C_INDEX_ARRAY, WLAVC_INDEX, C_FUNC_PTR -reflection_library = 'libcppyy_backend.so' +backend_library = 'libcppyy_backend.so' # this is not technically correct, but will do for now std_string_name = 'std::basic_string' @@ -112,7 +113,7 @@ class State(object): def __init__(self, space): - self.library = None + self.backend = None self.capi_calls = {} nt = newtype # module from _cffi_backend @@ -261,24 +262,32 @@ self.c_offset_farg = 0 -def load_reflection_library(space): +def load_backend(space): state = space.fromcache(State) - if state.library is None: + if state.backend is None: from pypy.module._cffi_backend.libraryobj import W_Library - state.library = W_Library(space, reflection_library, rdynload.RTLD_LOCAL | rdynload.RTLD_LAZY) - if state.library: + dldflags = rdynload.RTLD_LOCAL | rdynload.RTLD_LAZY + if os.environ.get('CPPYY_BACKEND_LIBRARY'): + libname = os.environ['CPPYY_BACKEND_LIBRARY'] + state.backend = W_Library(space, libname, dldflags) + else: + # try usual lookups + state.backend = W_Library(space, backend_library, dldflags) + + if state.backend: # fix constants - state.c_sizeof_farg = _cdata_to_size_t(space, call_capi(space, 'function_arg_sizeof', [])) - state.c_offset_farg = _cdata_to_size_t(space, call_capi(space, 'function_arg_typeoffset', [])) - return state.library + state.c_sizeof_farg = _cdata_to_size_t( + space, call_capi(space, 'function_arg_sizeof', [])) + state.c_offset_farg = _cdata_to_size_t( + space, call_capi(space, 'function_arg_typeoffset', [])) def verify_backend(space): try: - load_reflection_library(space) + load_backend(space) except Exception: if objectmodel.we_are_translated(): raise oefmt(space.w_ImportError, - "missing reflection library %s", reflection_library) + "missing reflection library %s", backend_library) return False return True @@ -287,11 +296,11 @@ try: c_call = state.capi_calls[name] except KeyError: - if state.library is None: - load_reflection_library(space) + if state.backend is None: + load_backend(space) iface = state.capi_call_ifaces[name] cfunc = W_RCTypeFunc(space, iface[0], iface[1], False) - c_call = state.library.load_function(cfunc, 'cppyy_'+name) + c_call = state.backend.load_function(cfunc, 'cppyy_'+name) # TODO: there must be a better way to trick the leakfinder ... if not objectmodel.we_are_translated(): leakfinder.remember_free(c_call.ctype.cif_descr._obj0) From pypy.commits at gmail.com Sat Oct 7 09:09:11 2017 From: pypy.commits at gmail.com (wlav) Date: Sat, 07 Oct 2017 06:09:11 -0700 (PDT) Subject: [pypy-commit] pypy cppyy-packaging: merge default into branch Message-ID: <59d8d1f7.95a2df0a.35b47.67dd@mx.google.com> Author: Wim Lavrijsen Branch: cppyy-packaging Changeset: r92596:a5393d11a343 Date: 2017-10-03 15:46 -0700 http://bitbucket.org/pypy/pypy/changeset/a5393d11a343/ Log: merge default into branch diff too long, truncating to 2000 out of 9976 lines diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -60,8 +60,8 @@ Wim Lavrijsen Eric van Riet Paap Richard Emslie + Remi Meier Alexander Schremmer - Remi Meier Dan Villiom Podlaski Christiansen Lukas Diekmann Sven Hager @@ -102,6 +102,7 @@ Michael Foord Stephan Diehl Stefano Rivera + Jean-Paul Calderone Stefan Schwarzer Tomek Meka Valentino Volonghi @@ -110,14 +111,13 @@ Bob Ippolito Bruno Gola David Malcolm - Jean-Paul Calderone Squeaky Edd Barrett Timo Paulssen Marius Gedminas + Nicolas Truessel Alexandre Fayolle Simon Burton - Nicolas Truessel Martin Matusiak Laurence Tratt Wenzhu Man @@ -156,6 +156,7 @@ Stefan H. Muller Tim Felgentreff Eugene Oden + Dodan Mihai Jeff Terrace Henry Mason Vasily Kuznetsov @@ -182,11 +183,13 @@ Rocco Moretti Gintautas Miliauskas Lucian Branescu Mihaila + Mariano Anaya anatoly techtonik - Dodan Mihai Karl Bartel + Stefan Beyer Gabriel Lavoie Jared Grubb + Alecsandru Patrascu Olivier Dormond Wouter van Heyst Sebastian Pawluś @@ -194,6 +197,7 @@ Victor Stinner Andrews Medina Aaron Iles + p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -204,6 +208,7 @@ Michael Cheng Mikael Schönenberg Stanislaw Halik + Mihnea Saracin Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -214,14 +219,12 @@ Jonathan David Riehl Beatrice During Alex Perry - p_zieschang at yahoo.de Robert Zaremba Alan McIntyre Alexander Sedov Vaibhav Sood Reuben Cummings Attila Gobi - Alecsandru Patrascu Christopher Pope Tristan Arthur Christian Tismer @@ -243,7 +246,6 @@ Jacek Generowicz Sylvain Thenault Jakub Stasiak - Stefan Beyer Andrew Dalke Alejandro J. Cura Vladimir Kryachko @@ -275,6 +277,7 @@ Christoph Gerum Miguel de Val Borro Artur Lisiecki + afteryu Toni Mattis Laurens Van Houtven Bobby Impollonia @@ -305,6 +308,7 @@ Anna Katrina Dominguez Kim Jin Su Amber Brown + Anthony Sottile Nate Bragg Ben Darnell Juan Francisco Cantero Hurtado @@ -325,12 +329,14 @@ Mike Bayer Rodrigo Araújo Daniil Yarancev + Min RK OlivierBlanvillain Jonas Pfannschmidt Zearin Andrey Churin Dan Crosta reubano at gmail.com + Stanisław Halik Julien Phalip Roman Podoliaka Eli Stevens 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 @@ -361,17 +361,20 @@ if handle is None: if flags & _FUNCFLAG_CDECL: - self._handle = _ffi.CDLL(name, mode) + pypy_dll = _ffi.CDLL(name, mode) else: - self._handle = _ffi.WinDLL(name, mode) - else: - self._handle = handle + 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 + self._handle = handle def __repr__(self): - return "<%s '%s', handle %r at 0x%x>" % ( - self.__class__.__name__, self._name, self._handle, - id(self) & (_sys.maxint * 2 + 1)) - + return "<%s '%s', handle %x at %x>" % \ + (self.__class__.__name__, self._name, + (self._handle & (_sys.maxint*2 + 1)), + id(self) & (_sys.maxint*2 + 1)) def __getattr__(self, name): if name.startswith('__') and name.endswith('__'): diff --git a/lib-python/2.7/ctypes/test/test_byteswap.py b/lib-python/2.7/ctypes/test/test_byteswap.py --- a/lib-python/2.7/ctypes/test/test_byteswap.py +++ b/lib-python/2.7/ctypes/test/test_byteswap.py @@ -23,7 +23,6 @@ setattr(bits, "i%s" % i, 1) dump(bits) - @xfail def test_endian_short(self): if sys.byteorder == "little": self.assertIs(c_short.__ctype_le__, c_short) @@ -51,7 +50,6 @@ self.assertEqual(bin(s), "3412") self.assertEqual(s.value, 0x1234) - @xfail def test_endian_int(self): if sys.byteorder == "little": self.assertIs(c_int.__ctype_le__, c_int) @@ -80,7 +78,6 @@ self.assertEqual(bin(s), "78563412") self.assertEqual(s.value, 0x12345678) - @xfail def test_endian_longlong(self): if sys.byteorder == "little": self.assertIs(c_longlong.__ctype_le__, c_longlong) @@ -109,7 +106,6 @@ self.assertEqual(bin(s), "EFCDAB9078563412") self.assertEqual(s.value, 0x1234567890ABCDEF) - @xfail def test_endian_float(self): if sys.byteorder == "little": self.assertIs(c_float.__ctype_le__, c_float) @@ -128,7 +124,6 @@ self.assertAlmostEqual(s.value, math.pi, 6) self.assertEqual(bin(struct.pack(">f", math.pi)), bin(s)) - @xfail def test_endian_double(self): if sys.byteorder == "little": self.assertIs(c_double.__ctype_le__, c_double) @@ -156,7 +151,6 @@ self.assertIs(c_char.__ctype_le__, c_char) self.assertIs(c_char.__ctype_be__, c_char) - @xfail def test_struct_fields_1(self): if sys.byteorder == "little": base = BigEndianStructure @@ -192,7 +186,6 @@ pass self.assertRaises(TypeError, setattr, T, "_fields_", [("x", typ)]) - @xfail def test_struct_struct(self): # nested structures with different byteorders @@ -221,7 +214,6 @@ self.assertEqual(s.point.x, 1) self.assertEqual(s.point.y, 2) - @xfail def test_struct_fields_2(self): # standard packing in struct uses no alignment. # So, we have to align using pad bytes. @@ -245,7 +237,6 @@ s2 = struct.pack(fmt, 0x12, 0x1234, 0x12345678, 3.14) self.assertEqual(bin(s1), bin(s2)) - @xfail def test_unaligned_nonnative_struct_fields(self): if sys.byteorder == "little": base = BigEndianStructure diff --git a/lib-python/2.7/ctypes/test/test_unaligned_structures.py b/lib-python/2.7/ctypes/test/test_unaligned_structures.py --- a/lib-python/2.7/ctypes/test/test_unaligned_structures.py +++ b/lib-python/2.7/ctypes/test/test_unaligned_structures.py @@ -37,10 +37,7 @@ for typ in byteswapped_structures: ## print >> sys.stderr, typ.value self.assertEqual(typ.value.offset, 1) - try: - o = typ() - except NotImplementedError as e: - self.skipTest(str(e)) # for PyPy + o = typ() o.value = 4 self.assertEqual(o.value, 4) diff --git a/lib-python/2.7/distutils/sysconfig_pypy.py b/lib-python/2.7/distutils/sysconfig_pypy.py --- a/lib-python/2.7/distutils/sysconfig_pypy.py +++ b/lib-python/2.7/distutils/sysconfig_pypy.py @@ -218,6 +218,10 @@ compiler.shared_lib_extension = so_ext +def get_config_h_filename(): + """Returns the path of pyconfig.h.""" + inc_dir = get_python_inc(plat_specific=1) + return os.path.join(inc_dir, 'pyconfig.h') from sysconfig_cpython import ( parse_makefile, _variable_rx, expand_makefile_vars) 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 @@ -203,7 +203,7 @@ f_locals local namespace seen by this frame f_restricted 0 or 1 if frame is in restricted execution mode f_trace tracing function for this frame, or None""" - return isinstance(object, types.FrameType) + return isinstance(object, (types.FrameType, types.FakeFrameType)) def iscode(object): """Return true if the object is a code object. diff --git a/lib-python/2.7/multiprocessing/heap.py b/lib-python/2.7/multiprocessing/heap.py --- a/lib-python/2.7/multiprocessing/heap.py +++ b/lib-python/2.7/multiprocessing/heap.py @@ -62,7 +62,7 @@ self.size = size self.name = 'pym-%d-%d' % (os.getpid(), Arena._counter.next()) self.buffer = mmap.mmap(-1, self.size, tagname=self.name) - assert win32.GetLastError() == 0, 'tagname already in use' + #assert win32.GetLastError() == 0, 'tagname already in use' self._state = (self.size, self.name) def __getstate__(self): @@ -72,7 +72,7 @@ def __setstate__(self, state): self.size, self.name = self._state = state self.buffer = mmap.mmap(-1, self.size, tagname=self.name) - assert win32.GetLastError() == win32.ERROR_ALREADY_EXISTS + #assert win32.GetLastError() == win32.ERROR_ALREADY_EXISTS else: diff --git a/lib-python/2.7/string.py b/lib-python/2.7/string.py --- a/lib-python/2.7/string.py +++ b/lib-python/2.7/string.py @@ -75,7 +75,7 @@ for i in range(256): buf[i] = i for i in range(n): - buf[ord(fromstr[i])] = tostr[i] + buf[ord(fromstr[i])] = ord(tostr[i]) return str(buf) diff --git a/lib-python/2.7/types.py b/lib-python/2.7/types.py --- a/lib-python/2.7/types.py +++ b/lib-python/2.7/types.py @@ -71,6 +71,12 @@ FrameType = type(tb.tb_frame) del tb +# PyPy extension +try: + FakeFrameType = type(next(sys._current_frames().itervalues())) +except (AttributeError, StopIteration): + FakeFrameType = FrameType + SliceType = slice EllipsisType = type(Ellipsis) 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 @@ -82,7 +82,7 @@ return False def in_dll(self, dll, name): - return self.from_address(dll._handle.getaddressindll(name)) + return self.from_address(dll.__pypy_dll__.getaddressindll(name)) def from_buffer(self, obj, offset=0): size = self._sizeofinstances() diff --git a/lib_pypy/_ctypes/function.py b/lib_pypy/_ctypes/function.py --- a/lib_pypy/_ctypes/function.py +++ b/lib_pypy/_ctypes/function.py @@ -430,7 +430,7 @@ ffires = restype.get_ffi_argtype() return _ffi.FuncPtr.fromaddr(ptr, '', ffiargs, ffires, self._flags_) - cdll = self.dll._handle + cdll = self.dll.__pypy_dll__ try: ffi_argtypes = [argtype.get_ffi_argtype() for argtype in argtypes] ffi_restype = restype.get_ffi_argtype() 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 @@ -142,6 +142,10 @@ ptr._buffer = tp._ffiarray(1, autofree=True) ptr._buffer[0] = obj._buffer result = ptr + elif isinstance(obj, bytes): + result = tp() + result._buffer[0] = buffer(obj)._pypy_raw_address() + return result elif not (isinstance(obj, _CData) and type(obj)._is_pointer_like()): raise TypeError("cast() argument 1 must be a pointer, not %s" % (type(obj),)) diff --git a/lib_pypy/_ctypes/primitive.py b/lib_pypy/_ctypes/primitive.py --- a/lib_pypy/_ctypes/primitive.py +++ b/lib_pypy/_ctypes/primitive.py @@ -61,6 +61,54 @@ pyobj_container = GlobalPyobjContainer() +def swap_bytes(value, sizeof, typeof, get_or_set): + def swap_2(): + return ((value >> 8) & 0x00FF) | ((value << 8) & 0xFF00) + + def swap_4(): + return ((value & 0x000000FF) << 24) | \ + ((value & 0x0000FF00) << 8) | \ + ((value & 0x00FF0000) >> 8) | \ + ((value >> 24) & 0xFF) + + def swap_8(): + return ((value & 0x00000000000000FFL) << 56) | \ + ((value & 0x000000000000FF00L) << 40) | \ + ((value & 0x0000000000FF0000L) << 24) | \ + ((value & 0x00000000FF000000L) << 8) | \ + ((value & 0x000000FF00000000L) >> 8) | \ + ((value & 0x0000FF0000000000L) >> 24) | \ + ((value & 0x00FF000000000000L) >> 40) | \ + ((value >> 56) & 0xFF) + + def swap_double_float(typ): + from struct import pack, unpack + if get_or_set == 'set': + if sys.byteorder == 'little': + st = pack(''.join(['>', typ]), value) + else: + st = pack(''.join(['<', typ]), value) + return unpack(typ, st)[0] + else: + packed = pack(typ, value) + if sys.byteorder == 'little': + st = unpack(''.join(['>', typ]), packed) + else: + st = unpack(''.join(['<', typ]), packed) + return st[0] + + if typeof in ('c_float', 'c_float_le', 'c_float_be'): + return swap_double_float('f') + elif typeof in ('c_double', 'c_double_le', 'c_double_be'): + return swap_double_float('d') + else: + if sizeof == 2: + return swap_2() + elif sizeof == 4: + return swap_4() + elif sizeof == 8: + return swap_8() + def generic_xxx_p_from_param(cls, value): if value is None: return cls(None) @@ -271,6 +319,31 @@ def _as_ffi_pointer_(self, ffitype): return as_ffi_pointer(self, ffitype) result._as_ffi_pointer_ = _as_ffi_pointer_ + if name[-2:] != '_p' and name[-3:] not in ('_le', '_be') \ + and name not in ('c_wchar', '_SimpleCData', 'c_longdouble', 'c_bool', 'py_object'): + from sys import byteorder + if byteorder == 'big': + name += '_le' + swapped = self.__new__(self, name, bases, dct) + result.__ctype_le__ = swapped + result.__ctype_be__ = result + swapped.__ctype_be__ = result + swapped.__ctype_le__ = swapped + else: + name += '_be' + swapped = self.__new__(self, name, bases, dct) + result.__ctype_be__ = swapped + result.__ctype_le__ = result + swapped.__ctype_le__ = result + swapped.__ctype_be__ = swapped + from _ctypes import sizeof + def _getval(self): + return swap_bytes(self._buffer[0], sizeof(self), name, 'get') + def _setval(self, value): + d = result() + d.value = value + self._buffer[0] = swap_bytes(d.value, sizeof(self), name, 'set') + swapped.value = property(_getval, _setval) return result 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 @@ -40,6 +40,22 @@ else: rawfields.append((f[0], f[1]._ffishape_)) + # hack for duplicate field names + already_seen = set() + names1 = names + names = [] + for f in names1: + if f not in already_seen: + names.append(f) + already_seen.add(f) + already_seen = set() + for i in reversed(range(len(rawfields))): + if rawfields[i][0] in already_seen: + rawfields[i] = (('$DUP%d$%s' % (i, rawfields[i][0]),) + + rawfields[i][1:]) + already_seen.add(rawfields[i][0]) + # /hack + _set_shape(self, rawfields, self._is_union) fields = {} @@ -130,6 +146,7 @@ obj._buffer.__setattr__(self.name, arg) + def _set_shape(tp, rawfields, is_union=False): tp._ffistruct_ = _rawffi.Structure(rawfields, is_union, getattr(tp, '_pack_', 0)) @@ -224,19 +241,27 @@ res.__dict__['_index'] = -1 return res - class StructOrUnion(_CData): __metaclass__ = StructOrUnionMeta def __new__(cls, *args, **kwds): from _ctypes import union - self = super(_CData, cls).__new__(cls) - if ('_abstract_' in cls.__dict__ or cls is Structure + if ('_abstract_' in cls.__dict__ or cls is Structure or cls is union.Union): raise TypeError("abstract class") if hasattr(cls, '_swappedbytes_'): - raise NotImplementedError("missing in PyPy: structure/union with " - "swapped (non-native) byte ordering") + fields = [None] * len(cls._fields_) + for i in range(len(cls._fields_)): + if cls._fields_[i][1] == cls._fields_[i][1].__dict__.get('__ctype_be__', None): + swapped = cls._fields_[i][1].__dict__.get('__ctype_le__', cls._fields_[i][1]) + else: + swapped = cls._fields_[i][1].__dict__.get('__ctype_be__', cls._fields_[i][1]) + if len(cls._fields_[i]) < 3: + fields[i] = (cls._fields_[i][0], swapped) + else: + fields[i] = (cls._fields_[i][0], swapped, cls._fields_[i][2]) + names_and_fields(cls, fields, _CData, cls.__dict__.get('_anonymous_', None)) + self = super(_CData, cls).__new__(cls) if hasattr(cls, '_ffistruct_'): self.__dict__['_buffer'] = self._ffistruct_(autofree=True) return self diff --git a/lib_pypy/cPickle.py b/lib_pypy/cPickle.py --- a/lib_pypy/cPickle.py +++ b/lib_pypy/cPickle.py @@ -116,10 +116,20 @@ @builtinify def dump(obj, file, protocol=None): + if protocol > HIGHEST_PROTOCOL: + # use cPickle error message, not pickle.py one + raise ValueError("pickle protocol %d asked for; " + "the highest available protocol is %d" % ( + protocol, HIGHEST_PROTOCOL)) Pickler(file, protocol).dump(obj) @builtinify def dumps(obj, protocol=None): + if protocol > HIGHEST_PROTOCOL: + # use cPickle error message, not pickle.py one + raise ValueError("pickle protocol %d asked for; " + "the highest available protocol is %d" % ( + protocol, HIGHEST_PROTOCOL)) file = StringIO() Pickler(file, protocol).dump(obj) return file.getvalue() diff --git a/lib_pypy/cffi.egg-info/PKG-INFO b/lib_pypy/cffi.egg-info/PKG-INFO --- a/lib_pypy/cffi.egg-info/PKG-INFO +++ b/lib_pypy/cffi.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: cffi -Version: 1.11.0 +Version: 1.11.1 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski diff --git a/lib_pypy/cffi/__init__.py b/lib_pypy/cffi/__init__.py --- a/lib_pypy/cffi/__init__.py +++ b/lib_pypy/cffi/__init__.py @@ -4,8 +4,8 @@ from .api import FFI from .error import CDefError, FFIError, VerificationError, VerificationMissing -__version__ = "1.11.0" -__version_info__ = (1, 11, 0) +__version__ = "1.11.1" +__version_info__ = (1, 11, 1) # The verifier module file names are based on the CRC32 of a string that # contains the following version number. It may be older than __version__ diff --git a/lib_pypy/cffi/_embedding.h b/lib_pypy/cffi/_embedding.h --- a/lib_pypy/cffi/_embedding.h +++ b/lib_pypy/cffi/_embedding.h @@ -247,7 +247,7 @@ if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.11.0" + "\ncompiled with cffi version: 1.11.1" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); diff --git a/lib_pypy/pyrepl/historical_reader.py b/lib_pypy/pyrepl/historical_reader.py --- a/lib_pypy/pyrepl/historical_reader.py +++ b/lib_pypy/pyrepl/historical_reader.py @@ -17,7 +17,7 @@ # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -from pyrepl import reader, commands +from pyrepl import reader, commands, input from pyrepl.reader import Reader as R isearch_keymap = tuple( @@ -214,7 +214,6 @@ isearch_forwards, isearch_backwards, operate_and_get_next]: self.commands[c.__name__] = c self.commands[c.__name__.replace('_', '-')] = c - from pyrepl import input self.isearch_trans = input.KeymapTranslator( isearch_keymap, invalid_cls=isearch_end, character_cls=isearch_add_character) diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py --- a/pypy/config/pypyoption.py +++ b/pypy/config/pypyoption.py @@ -71,6 +71,8 @@ working_modules.remove("_cppyy") # not tested on win32 if "faulthandler" in working_modules: working_modules.remove("faulthandler") # missing details + if "_vmprof" in working_modules: + working_modules.remove("_vmprof") # FIXME: missing details # The _locale module is needed by site.py on Windows default_modules.add("_locale") diff --git a/pypy/doc/build.rst b/pypy/doc/build.rst --- a/pypy/doc/build.rst +++ b/pypy/doc/build.rst @@ -119,8 +119,15 @@ To run untranslated tests, you need the Boehm garbage collector libgc. -On Debian and Ubuntu, this is the command to install all build-time -dependencies:: +On recent Debian and Ubuntu (like 17.04), this is the command to install +all build-time dependencies:: + + apt-get install gcc make libffi-dev pkg-config zlib1g-dev libbz2-dev \ + libsqlite3-dev libncurses5-dev libexpat1-dev libssl-dev libgdbm-dev \ + tk-dev libgc-dev python-cffi \ + liblzma-dev libncursesw5-dev # these two only needed on PyPy3 + +On older Debian and Ubuntu (12.04 to 16.04):: apt-get install gcc make libffi-dev pkg-config libz-dev libbz2-dev \ libsqlite3-dev libncurses-dev libexpat1-dev libssl-dev libgdbm-dev \ diff --git a/pypy/doc/contributor.rst b/pypy/doc/contributor.rst --- a/pypy/doc/contributor.rst +++ b/pypy/doc/contributor.rst @@ -27,8 +27,8 @@ Wim Lavrijsen Eric van Riet Paap Richard Emslie + Remi Meier Alexander Schremmer - Remi Meier Dan Villiom Podlaski Christiansen Lukas Diekmann Sven Hager @@ -69,6 +69,7 @@ Michael Foord Stephan Diehl Stefano Rivera + Jean-Paul Calderone Stefan Schwarzer Tomek Meka Valentino Volonghi @@ -77,14 +78,13 @@ Bob Ippolito Bruno Gola David Malcolm - Jean-Paul Calderone Squeaky Edd Barrett Timo Paulssen Marius Gedminas + Nicolas Truessel Alexandre Fayolle Simon Burton - Nicolas Truessel Martin Matusiak Laurence Tratt Wenzhu Man @@ -123,6 +123,7 @@ Stefan H. Muller Tim Felgentreff Eugene Oden + Dodan Mihai Jeff Terrace Henry Mason Vasily Kuznetsov @@ -149,11 +150,13 @@ Rocco Moretti Gintautas Miliauskas Lucian Branescu Mihaila + Mariano Anaya anatoly techtonik - Dodan Mihai Karl Bartel + Stefan Beyer Gabriel Lavoie Jared Grubb + Alecsandru Patrascu Olivier Dormond Wouter van Heyst Sebastian Pawluś @@ -161,6 +164,7 @@ Victor Stinner Andrews Medina Aaron Iles + p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -171,6 +175,7 @@ Michael Cheng Mikael Schönenberg Stanislaw Halik + Mihnea Saracin Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -181,14 +186,12 @@ Jonathan David Riehl Beatrice During Alex Perry - p_zieschang at yahoo.de Robert Zaremba Alan McIntyre Alexander Sedov Vaibhav Sood Reuben Cummings Attila Gobi - Alecsandru Patrascu Christopher Pope Tristan Arthur Christian Tismer @@ -210,7 +213,6 @@ Jacek Generowicz Sylvain Thenault Jakub Stasiak - Stefan Beyer Andrew Dalke Alejandro J. Cura Vladimir Kryachko @@ -242,6 +244,7 @@ Christoph Gerum Miguel de Val Borro Artur Lisiecki + afteryu Toni Mattis Laurens Van Houtven Bobby Impollonia @@ -272,6 +275,7 @@ Anna Katrina Dominguez Kim Jin Su Amber Brown + Anthony Sottile Nate Bragg Ben Darnell Juan Francisco Cantero Hurtado @@ -292,12 +296,14 @@ Mike Bayer Rodrigo Araújo Daniil Yarancev + Min RK OlivierBlanvillain Jonas Pfannschmidt Zearin Andrey Churin Dan Crosta reubano at gmail.com + Stanisław Halik Julien Phalip Roman Podoliaka Eli Stevens diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst --- a/pypy/doc/cpython_differences.rst +++ b/pypy/doc/cpython_differences.rst @@ -429,7 +429,8 @@ * the ``__builtins__`` name is always referencing the ``__builtin__`` module, never a dictionary as it sometimes is in CPython. Assigning to - ``__builtins__`` has no effect. + ``__builtins__`` has no effect. (For usages of tools like + RestrictedPython, see `issue #2653`_.) * directly calling the internal magic methods of a few built-in types with invalid arguments may have a slightly different result. For @@ -535,7 +536,12 @@ or ``float`` subtypes. Currently PyPy does not support the ``__class__`` attribute assignment for any non heaptype subtype. +* In PyPy, module and class dictionaries are optimized under the assumption + that deleting attributes from them are rare. Because of this, e.g. + ``del foo.bar`` where ``foo`` is a module (or class) that contains the + function ``bar``, is significantly slower than CPython. + .. _`is ignored in PyPy`: http://bugs.python.org/issue14621 .. _`little point`: http://events.ccc.de/congress/2012/Fahrplan/events/5152.en.html .. _`#2072`: https://bitbucket.org/pypy/pypy/issue/2072/ - +.. _`issue #2653`: https://bitbucket.org/pypy/pypy/issues/2653/ diff --git a/pypy/doc/index-of-release-notes.rst b/pypy/doc/index-of-release-notes.rst --- a/pypy/doc/index-of-release-notes.rst +++ b/pypy/doc/index-of-release-notes.rst @@ -6,6 +6,7 @@ .. toctree:: + release-v5.9.0.rst release-v5.8.0.rst release-v5.7.1.rst release-v5.7.0.rst diff --git a/pypy/doc/index-of-whatsnew.rst b/pypy/doc/index-of-whatsnew.rst --- a/pypy/doc/index-of-whatsnew.rst +++ b/pypy/doc/index-of-whatsnew.rst @@ -7,6 +7,7 @@ .. toctree:: whatsnew-head.rst + whatsnew-pypy2-5.9.0.rst whatsnew-pypy2-5.8.0.rst whatsnew-pypy2-5.7.0.rst whatsnew-pypy2-5.6.0.rst @@ -36,6 +37,7 @@ .. toctree:: whatsnew-pypy3-head.rst + whatsnew-pypy3-5.9.0.rst whatsnew-pypy3-5.8.0.rst whatsnew-pypy3-5.7.0.rst diff --git a/pypy/doc/release-v5.9.0.rst b/pypy/doc/release-v5.9.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/release-v5.9.0.rst @@ -0,0 +1,217 @@ +===================================== +PyPy2.7 and PyPy3.5 v5.9 dual release +===================================== + +The PyPy team is proud to release both PyPy2.7 v5.9 (an interpreter supporting +Python 2.7 syntax), and a beta-quality PyPy3.5 v5.9 (an interpreter for Python +3.5 syntax). The two releases are both based on much the same codebase, thus +the dual release. Note that PyPy3.5 supports Linux 64bit only for now. + +This new PyPy2.7 release includes the upstream stdlib version 2.7.13, and +PyPy3.5 includes the upstream stdlib version 3.5.3. + +NumPy and Pandas now work on PyPy2.7 (together with Cython 0.27.1). Issues +that appeared as excessive memory +use were cleared up and other incompatibilities were resolved. The C-API +compatibility layer does slow down code which crosses the python-c interface +often, we have ideas on how it could be improved, and still recommend +using pure python on PyPy or interfacing via CFFI_. Many other modules +based on C-API exentions now work on PyPy as well. + +Cython 0.27.1 (released very recently) supports more projects with PyPy, both +on PyPy2.7 and PyPy3.5 beta. Note version **0.27.1** is now the minimum +version that supports this version of PyPy, due to some interactions with +updated C-API interface code. + +We optimized the JSON parser for recurring string keys, which should decrease +memory use to 50% and increase parsing speed by up to 15% for large JSON files +with many repeating dictionary keys (which is quite common). + +CFFI_, which is part of the PyPy release, has been updated to 1.11.1, +improving an already great package for interfacing with C. CFFI now supports +complex arguments in API mode, as well as ``char16_t`` and ``char32_t`` and has +improved support for callbacks. + +Please let us know if your use case is slow, we have ideas how to make things +faster but need real-world examples (not micro-benchmarks) of problematic code. + +Work sponsored by a Mozilla grant_ continues on PyPy3.5; numerous fixes from +CPython were ported to PyPy. Of course the bug fixes and performance enhancements +mentioned above are part of both PyPy2.7 and PyPy3.5 beta. + +As always, this release fixed many other issues and bugs raised by the +growing community of PyPy users. We strongly recommend updating. + +You can download the v5.9 releases here: + + http://pypy.org/download.html + +We would like to thank our donors for the continued support of the PyPy +project. + +We would also like to thank our contributors and +encourage new people to join the project. PyPy has many +layers and we need help with all of them: `PyPy`_ and `RPython`_ documentation +improvements, tweaking popular `modules`_ to run on pypy, or general `help`_ +with making RPython's JIT even better. + +.. _vmprof: http://vmprof.readthedocs.io +.. _CFFI: https://cffi.readthedocs.io/en/latest/whatsnew.html +.. _grant: https://morepypy.blogspot.com/2016/08/pypy-gets-funding-from-mozilla-for.html +.. _`PyPy`: index.html +.. _`RPython`: https://rpython.readthedocs.org +.. _`modules`: project-ideas.html#make-more-python-modules-pypy-friendly +.. _`help`: project-ideas.html + +What is PyPy? +============= + +PyPy is a very compliant Python interpreter, almost a drop-in replacement for +CPython 2.7 and CPython 3.5. It's fast (`PyPy and CPython 2.7.x`_ performance comparison) +due to its integrated tracing JIT compiler. + +We also welcome developers of other `dynamic languages`_ to see what RPython +can do for them. + +The PyPy 2.7 release supports: + + * **x86** machines on most common operating systems + (Linux 32/64 bits, Mac OS X 64 bits, Windows 32 bits, OpenBSD, FreeBSD) + + * newer **ARM** hardware (ARMv6 or ARMv7, with VFPv3) running Linux, + + * big- and little-endian variants of **PPC64** running Linux, + + * **s390x** running Linux + +.. _`PyPy and CPython 2.7.x`: http://speed.pypy.org +.. _`dynamic languages`: http://rpython.readthedocs.io/en/latest/examples.html + +Highlights of the PyPy2.7, cpyext, and RPython changes (since 5.8 released June, 2017) +====================================================================================== + +See also issues that were resolved_ + +Note that these are also merged into PyPy 3.5 + +* New features and cleanups + + * Add support for ``PyFrozenSet_New``, ``PyObject_HashNotImplemented``, + ``PyObject_Print(NULL, ...)``, ``PyObject_RichCompareBool(a, a, ...)``, + ``PyType_IS_GC`` (does nothing), ``PyUnicode_FromFormat`` + * ctypes ``char_p`` and ``unichar_p`` indexing now CPython compatible + * ``gcdump`` now reports largest object + * More complete support in the ``_curses`` CFFI module + * Add cPickle.Unpickler.find_global (issue 1853_) + * Fix ``PyErr_Fetch`` + ``PyErr_NormalizeException`` with no exception set + * Simplify ``gc.get_referrers()`` to return the opposite of ``gc.get_referents()`` + * Update RevDB to version pypy2.7-v5.6.2 + * Previously, ``instance.method`` would return always the same bound method + object, when gotten from the same instance (as far as ``is`` and ``id()`` + can tell). CPython doesn't do that. Now PyPy, like CPython, returns a + different bound method object every time. For ``type.method``, PyPy2 still + returns always the same *unbound* method object; CPython does it for built-in + types but not for user-defined types + * Link to disable PaX protection for the JIT when needed + * Update build instructions and an rarely used Makefile + * Recreate support for using leakfinder in cpyext tests which had suffered + bit-rot, disable due to many false positives + * Add more functionality to ``sysconfig`` + * Added ``_swappedbytes_`` support for ``ctypes.Structure`` + * Better support the ``inspect`` module on ``frames`` + +* Bug Fixes + + * Fix issue 2592_ - cpyext ``PyListObject.pop``, ``pop_end`` must return a value + * Implement ``PyListOjbect.getstorage_copy`` + * Fix for ``reversed(dictproxy)`` issue 2601_ + * Fix for duplicate names in ctypes' ``_fields__``, issue 2621_ + * Update built-in ``pyexpat`` module on win32 to use UTF-8 version not UTF-16 + * ``gc.get_objects`` now handles objects with finalizers more consistently + * Fixed memory leak in ``SSLContext.getpeercert`` returning validated + certificates and ``SSLContext.get_ca_certs(binary_mode=True)`` + (_get_crl_dp) `CPython issue 29738`_ + +* Performance improvements: + + * Improve performance of ``bytearray.extend`` by rewriting portions in app-level + * Optimize list accesses with constant indexes better by retaining more + information about them + * Add a jit driver for ``array.count`` and ``array.index`` + * Improve information retained in a bridge wrt ``array`` + * Move some dummy CAPI functions and ``Py*_Check`` functions from RPython into + pure C macros + * In the fast ``zip(intlist1, intlist2)`` implementation, don't wrap and unwrap + all the ints + * Cache string keys that occur in JSON dicts, as they are likely to repeat + +* RPython improvements + + * Do not preallocate a RPython list if we only know an upper bound on its size + * Issue 2590_: fix the bounds in the GC when allocating a lot of objects with finalizers + * Replace magical NOT RPYTHON comment with a decorator + * Implement ``socket.sendmsg()``/``.recvmsg()`` for py3.5 + * Add ``memory_pressure`` for ``_SSLSocket`` objects + +* Degredations + + * Disable vmprof on win32, due to upstream changes that break the internal ``_vmprof`` module + +.. _here: cpython_differences.html +.. _1853: https://bitbucket.org/pypy/pypy/issues/1853 +.. _2592: https://bitbucket.org/pypy/pypy/issues/2592 +.. _2590: https://bitbucket.org/pypy/pypy/issues/2590 +.. _2621: https://bitbucket.org/pypy/pypy/issues/2621 + +Highlights of the PyPy3.5 release (since 5.8 beta released June 2017) +====================================================================== + +* New features + + * Add support for ``_PyNamespace_New``, ``PyMemoryView_FromMemory``, + ``Py_EnterRecursiveCall`` raising RecursionError, ``PyObject_LengthHint``, + ``PyUnicode_FromKindAndData``, ``PyDict_SetDefault``, ``PyGenObject``, + ``PyGenObject``, ``PyUnicode_Substring``, ``PyLong_FromUnicodeObject`` + * Implement ``PyType_FromSpec`` (PEP 384) and fix issues with PEP 489 support + * Support the new version of ``os.stat()`` on win32 + * Use ``stat3()`` on Posix + * Accept buffer objects as filenames, except for `oslistdir`` + * Make slices of array ``memoryview`` s usable as writable buffers if contiguous + * Better handling of ``'%s'`` formatting for byte strings which might be utf-8 encoded + * Update the macros ``Py_DECREF`` and similar to use the CPython 3.5 version + * Ensure that ``mappingproxy`` is recognised as a mapping, not a sequence + * Enable PGO for CLang + * Rework ``cppyy`` packaging and rename the backend to ``_cppyy`` + * Support for libressl 2.5.4 + * Mirror CPython ``classmethod __reduce__`` which fixes pickling test + * Use utf-8 for ``readline`` history file + * Allow assigning ``'__class__'`` between ``ModuleType`` and its subclasses + * Add async slot functions in cpyext + +* Bug Fixes + + * Try to make ``openssl`` CFFI bindings more general and future-proof + * Better support ``importlib`` by only listing built-in modules in ``sys.builtin`` + * Add ``memory_pressure`` to large CFFI allocations in ``_lzma``, issue 2579_ + * Fix for ``reversed(mapping object)`` issue 2601_ + * Fixing regression with non-started generator receiving non-``None``, should + always raise ``TypeError`` + * ``itertools.islice``: use same logic as CPython, fixes 2643_ + +* Performance improvements: + + * + +* The following features of Python 3.5 are not implemented yet in PyPy: + + * PEP 442: Safe object finalization + +.. _resolved: whatsnew-pypy2-5.9.0.html +.. _2579: https://bitbucket.org/pypy/pypy/issues/2579 +.. _2601: https://bitbucket.org/pypy/pypy/issues/2601 +.. _2643: https://bitbucket.org/pypy/pypy/issues/2643 +.. _CPython issue 29738: https://bugs.python.org/issue29738 + +Please update, and continue to help us make PyPy better. + +Cheers 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 @@ -1,62 +1,8 @@ -========================== -What's new in PyPy2.7 5.9+ -========================== - -.. this is a revision shortly after release-pypy2.7-v5.8.0 -.. startrev: 558bd00b3dd8 - -In previous versions of PyPy, ``instance.method`` would return always -the same bound method object, when gotten out of the same instance (as -far as ``is`` and ``id()`` can tell). CPython doesn't do that. Now -PyPy, like CPython, returns a different bound method object every time. -For ``type.method``, PyPy2 still returns always the same *unbound* -method object; CPython does it for built-in types but not for -user-defined types. - -.. branch: cffi-complex -.. branch: cffi-char16-char32 - -The two ``cffi-*`` branches are part of the upgrade to cffi 1.11. - -.. branch: ctypes_char_indexing - -Indexing into char* behaves differently than CPython - -.. branch: vmprof-0.4.8 - -Improve and fix issues with vmprof - -.. branch: issue-2592 - -CPyext PyListObject.pop must return the value - -.. branch: cpyext-hash_notimpl - -If ``tp_hash`` is ``PyObject_HashNotImplemented``, set ``obj.__dict__['__hash__']`` to None - -.. branch: cppyy-packaging - -Renaming of ``cppyy`` to ``_cppyy``. -The former is now an external package installable with ``pip install cppyy``. - -.. branch: Enable_PGO_for_clang - -.. branch: nopax - -At the end of translation, run ``attr -q -s pax.flags -V m`` on -PAX-enabled systems on the produced binary. This seems necessary -because PyPy uses a JIT. - -.. branch: pypy_bytearray - -Improve ``bytearray`` performance (backported from py3.5) - -.. branch: gc-del-limit-growth - -Fix the bounds in the GC when allocating a lot of objects with finalizers, -fixes issue #2590 - -.. branch: arrays-force-less - -Small improvement to optimize list accesses with constant indexes better by -throwing away information about them less eagerly. +=========================== +What's new in PyPy2.7 5.10+ +=========================== + +.. this is a revision shortly after release-pypy2.7-v5.9.0 +.. startrev:d56dadcef996 + + diff --git a/pypy/doc/whatsnew-pypy2-5.9.0.rst b/pypy/doc/whatsnew-pypy2-5.9.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/whatsnew-pypy2-5.9.0.rst @@ -0,0 +1,96 @@ +========================= +What's new in PyPy2.7 5.9 +========================= + +.. this is a revision shortly after release-pypy2.7-v5.8.0 +.. startrev: 558bd00b3dd8 + +In previous versions of PyPy, ``instance.method`` would return always +the same bound method object, when gotten out of the same instance (as +far as ``is`` and ``id()`` can tell). CPython doesn't do that. Now +PyPy, like CPython, returns a different bound method object every time. +For ``type.method``, PyPy2 still returns always the same *unbound* +method object; CPython does it for built-in types but not for +user-defined types. + +.. branch: cffi-complex +.. branch: cffi-char16-char32 + +The two ``cffi-*`` branches are part of the upgrade to cffi 1.11. + +.. branch: ctypes_char_indexing + +Indexing into char* behaves differently than CPython + +.. branch: vmprof-0.4.8 + +Improve and fix issues with vmprof + +.. branch: issue-2592 + +CPyext PyListObject.pop must return the value + +.. branch: cpyext-hash_notimpl + +If ``tp_hash`` is ``PyObject_HashNotImplemented``, set ``obj.__dict__['__hash__']`` to None + +.. branch: cppyy-packaging + +Renaming of ``cppyy`` to ``_cppyy``. +The former is now an external package installable with ``pip install cppyy``. + +.. branch: Enable_PGO_for_clang + +.. branch: nopax + +At the end of translation, run ``attr -q -s pax.flags -V m`` on +PAX-enabled systems on the produced binary. This seems necessary +because PyPy uses a JIT. + +.. branch: pypy_bytearray + +Improve ``bytearray`` performance (backported from py3.5) + +.. branch: gc-del-limit-growth + +Fix the bounds in the GC when allocating a lot of objects with finalizers, +fixes issue #2590 + +.. branch: arrays-force-less + +Small improvement to optimize list accesses with constant indexes better by +throwing away information about them less eagerly. + + +.. branch: getarrayitem-into-bridges + +More information is retained into a bridge: knowledge about the content of +arrays (at fixed indices) is stored in guards (and thus available at the +beginning of bridges). Also, some better feeding of information about known +fields of constant objects into bridges. + +.. branch: cpyext-leakchecking + +Add support for leakfinder in cpyext tests (disabled for now, due to too many +failures). + +.. branch: pypy_swappedbytes + +Added ``_swappedbytes_`` support for ``ctypes.Structure`` + +.. branch: pycheck-macros + +Convert many Py*_Check cpyext functions into macros, like CPython. + +.. branch: py_ssize_t + +Explicitly use Py_ssize_t as the Signed type in pypy c-api + +.. branch: cpyext-jit + +Differentiate the code to call METH_NOARGS, METH_O and METH_VARARGS in cpyext: +this allows to write specialized code which is much faster than previous +completely generic version. Moreover, let the JIT to look inside the cpyext +module: the net result is that cpyext calls are up to 7x faster. However, this +is true only for very simple situations: in all real life code, we are still +much slower than CPython (more optimizations to come) diff --git a/pypy/doc/whatsnew-pypy3-5.9.0.rst b/pypy/doc/whatsnew-pypy3-5.9.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/whatsnew-pypy3-5.9.0.rst @@ -0,0 +1,7 @@ +======================= +What's new in PyPy3 5.9 +======================= + +.. this is the revision after release-pypy3.5-5.8 +.. startrev: afbf09453369 + diff --git a/pypy/doc/whatsnew-pypy3-head.rst b/pypy/doc/whatsnew-pypy3-head.rst --- a/pypy/doc/whatsnew-pypy3-head.rst +++ b/pypy/doc/whatsnew-pypy3-head.rst @@ -1,7 +1,7 @@ ========================= -What's new in PyPy3 5.7+ +What's new in PyPy3 5.9+ ========================= -.. this is the revision after release-pypy3.3-5.7.x was branched -.. startrev: afbf09453369 +.. this is the revision after release-pypy3.5-5.9 +.. startrev: be41e3ac0a29 diff --git a/pypy/doc/windows.rst b/pypy/doc/windows.rst --- a/pypy/doc/windows.rst +++ b/pypy/doc/windows.rst @@ -114,12 +114,15 @@ INCLUDE, LIB and PATH (for DLLs) environment variables appropriately. -Abridged method (for -Ojit builds using Visual Studio 2008) ------------------------------------------------------------ +Abridged method (using Visual Studio 2008) +------------------------------------------ Download the versions of all the external packages from +https://bitbucket.org/pypy/pypy/downloads/local_59.zip +(for post-5.8 builds) with sha256 checksum +``6344230e90ab7a9cb84efbae1ba22051cdeeb40a31823e0808545b705aba8911`` https://bitbucket.org/pypy/pypy/downloads/local_5.8.zip -(for post-5.7.1 builds) with sha256 checksum +(to reproduce 5.8 builds) with sha256 checksum ``fbe769bf3a4ab6f5a8b0a05b61930fc7f37da2a9a85a8f609cf5a9bad06e2554`` or https://bitbucket.org/pypy/pypy/downloads/local_2.4.zip (for 2.4 release and later) or @@ -135,8 +138,8 @@ Now you should be good to go. If you choose this method, you do not need to download/build anything else. -Nonabrided method (building from scratch) ------------------------------------------ +Nonabridged method (building from scratch) +------------------------------------------ If you want to, you can rebuild everything from scratch by continuing. @@ -209,17 +212,85 @@ The expat XML parser ~~~~~~~~~~~~~~~~~~~~ -Download the source code of expat on sourceforge: -https://github.com/libexpat/libexpat/releases and extract it in the base directory. -Version 2.1.1 is known to pass tests. Then open the project file ``expat.dsw`` -with Visual Studio; follow the instruction for converting the project files, -switch to the "Release" configuration, use the ``expat_static`` project, -reconfigure the runtime for Multi-threaded DLL (/MD) and build. Do the same for -the ``expat`` project to build the ``expat.dll`` (for tests via ll2ctypes) +CPython compiles expat from source as part of the build. PyPy uses the same +code base, but expects to link to a static lib of expat. Here are instructions +to reproduce the static lib in version 2.2.4. -Then, copy the file ``win32\bin\release\libexpat.lib`` into -LIB, and both ``lib\expat.h`` and ``lib\expat_external.h`` in -INCLUDE, and ``win32\bin\release\libexpat.dll`` into PATH. +Download the source code of expat: https://github.com/libexpat/libexpat. +``git checkout`` the proper tag, in this case ``R_2_2_4``. Run +``vcvars.bat`` to set up the visual compiler tools, and CD into the source +directory. Create a file ``stdbool.h`` with the content + +.. code-block:: c + + #pragma once + + #define false 0 + #define true 1 + + #define bool int + +and put it in a place on the ``INCLUDE`` path, or create it in the local +directory and add ``.`` to the ``INCLUDE`` path:: + + SET INCLUDE=%INCLUDE%;. + +Then compile all the ``*.c`` file into ``*.obj``:: + + cl.exe /nologo /MD /O2 *c /c + rem for debug + cl.exe /nologo /MD /O0 /Ob0 /Zi *c /c + +You may need to move some variable declarations to the beginning of the +function, to be compliant with C89 standard. Here is the diff for version 2.2.4 + +.. code-block:: diff + + diff --git a/expat/lib/xmltok.c b/expat/lib/xmltok.c + index 007aed0..a2dcaad 100644 + --- a/expat/lib/xmltok.c + +++ b/expat/lib/xmltok.c + @@ -399,19 +399,21 @@ utf8_toUtf8(const ENCODING *UNUSED_P(enc), + /* Avoid copying partial characters (due to limited space). */ + const ptrdiff_t bytesAvailable = fromLim - *fromP; + const ptrdiff_t bytesStorable = toLim - *toP; + + const char * fromLimBefore; + + ptrdiff_t bytesToCopy; + if (bytesAvailable > bytesStorable) { + fromLim = *fromP + bytesStorable; + output_exhausted = true; + } + + /* Avoid copying partial characters (from incomplete input). */ + - const char * const fromLimBefore = fromLim; + + fromLimBefore = fromLim; + align_limit_to_full_utf8_characters(*fromP, &fromLim); + if (fromLim < fromLimBefore) { + input_incomplete = true; + } + + - const ptrdiff_t bytesToCopy = fromLim - *fromP; + + bytesToCopy = fromLim - *fromP; + memcpy((void *)*toP, (const void *)*fromP, (size_t)bytesToCopy); + *fromP += bytesToCopy; + *toP += bytesToCopy; + + +Create ``libexpat.lib`` (for translation) and ``libexpat.dll`` (for tests):: + + cl /LD *.obj libexpat.def /Felibexpat.dll + rem for debug + rem cl /LDd /Zi *.obj libexpat.def /Felibexpat.dll + + rem this will override the export library created in the step above + rem but tests do not need the export library, they load the dll dynamically + lib *.obj /out:libexpat.lib + +Then, copy + +- ``libexpat.lib`` into LIB +- both ``lib\expat.h`` and ``lib\expat_external.h`` in INCLUDE +- ``libexpat.dll`` into PATH The OpenSSL library @@ -363,7 +434,7 @@ It is probably not too much work if the goal is only to get a translated PyPy executable, and to run all tests before translation. But you need to start somewhere, and you should start with some tests in -rpython/translator/c/test/, like ``test_standalone.py`` and +``rpython/translator/c/test/``, like ``test_standalone.py`` and ``test_newgc.py``: try to have them pass on top of CPython64/64. Keep in mind that this runs small translations, and some details may go @@ -373,7 +444,7 @@ should be something like ``long long``. What is more generally needed is to review all the C files in -rpython/translator/c/src for the word ``long``, because this means a +``rpython/translator/c/src`` for the word ``long``, because this means a 32-bit integer even on Win64. Replace it with ``Signed`` most of the times. You can replace one with the other without breaking anything on any other platform, so feel free to. diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -1,4 +1,5 @@ import sys +import py from rpython.rlib.cache import Cache from rpython.tool.uid import HUGEVAL_BYTES @@ -1338,8 +1339,22 @@ self.setitem(w_globals, w_key, self.builtin) return statement.exec_code(self, w_globals, w_locals) + @not_rpython + def appdef(self, source): + '''Create interp-level function object from app-level source. + + The source should be in the same format as for space.appexec(): + """(foo, bar): return 'baz'""" + ''' + source = source.lstrip() + assert source.startswith('('), "incorrect header in:\n%s" % (source,) + source = py.code.Source("def anonymous%s\n" % source) + w_glob = self.newdict(module=True) + self.exec_(str(source), w_glob, w_glob) + return self.getitem(w_glob, self.newtext('anonymous')) + @specialize.arg(2) - def appexec(self, posargs_w, source): + def appexec(self, posargs_w, source, cache=True): """ return value from executing given source at applevel. The source must look like '''(x, y): @@ -1347,7 +1362,11 @@ return result ''' """ - w_func = self.fromcache(AppExecCache).getorbuild(source) + if cache: + w_func = self.fromcache(AppExecCache).getorbuild(source) + else: + # NB: since appdef() is not-RPython, using cache=False also is. + w_func = self.appdef(source) args = Arguments(self, list(posargs_w)) return self.call_args(w_func, args) @@ -1423,7 +1442,7 @@ length = 1 return start, stop, step, length - def getindex_w(self, w_obj, w_exception, objdescr=None): + def getindex_w(self, w_obj, w_exception, objdescr=None, errmsg=None): """Return w_obj.__index__() as an RPython int. If w_exception is None, silently clamp in case of overflow; else raise w_exception. @@ -1433,8 +1452,10 @@ except OperationError as err: if objdescr is None or not err.match(self, self.w_TypeError): raise - raise oefmt(self.w_TypeError, "%s must be an integer, not %T", - objdescr, w_obj) + if errmsg is None: + errmsg = " must be an integer" + raise oefmt(self.w_TypeError, "%s%s, not %T", + objdescr, errmsg, w_obj) try: # allow_conversion=False it's not really necessary because the # return type of __index__ is already checked by space.index(), @@ -1610,12 +1631,15 @@ # return text_w(w_obj) or None return None if self.is_none(w_obj) else self.text_w(w_obj) + @specialize.argtype(1) def bytes_w(self, w_obj): """ Takes an application level :py:class:`bytes` (on PyPy2 this equals `str`) and returns a rpython byte string. """ + assert w_obj is not None return w_obj.str_w(self) + @specialize.argtype(1) def text_w(self, w_obj): """ PyPy2 takes either a :py:class:`str` and returns a rpython byte string, or it takes an :py:class:`unicode` @@ -1625,6 +1649,7 @@ On PyPy3 it takes a :py:class:`str` and it will return an utf-8 encoded rpython string. """ + assert w_obj is not None return w_obj.str_w(self) @not_rpython # tests only; should be replaced with bytes_w or text_w @@ -1667,12 +1692,14 @@ if len(string) != 1: raise oefmt(self.w_ValueError, "string must be of size 1") return string[0] - value = self.getindex_w(w_obj, None) + value = self.getindex_w(w_obj, None, "", + "an integer or string of size 1 is required") if not 0 <= value < 256: # this includes the OverflowError in case the long is too large raise oefmt(self.w_ValueError, "byte must be in range(0, 256)") return chr(value) + @specialize.argtype(1) def int_w(self, w_obj, allow_conversion=True): """ Unwrap an app-level int object into an interpret-level int. @@ -1685,26 +1712,35 @@ If allow_conversion=False, w_obj needs to be an app-level int or a subclass. """ + assert w_obj is not None return w_obj.int_w(self, allow_conversion) + @specialize.argtype(1) def int(self, w_obj): + assert w_obj is not None return w_obj.int(self) + @specialize.argtype(1) def uint_w(self, w_obj): + assert w_obj is not None return w_obj.uint_w(self) + @specialize.argtype(1) def bigint_w(self, w_obj, allow_conversion=True): """ Like int_w, but return a rlib.rbigint object and call __long__ if allow_conversion is True. """ + assert w_obj is not None return w_obj.bigint_w(self, allow_conversion) + @specialize.argtype(1) def float_w(self, w_obj, allow_conversion=True): """ Like int_w, but return an interp-level float and call __float__ if allow_conversion is True. """ + assert w_obj is not None return w_obj.float_w(self, allow_conversion) def realtext_w(self, w_obj): @@ -1714,7 +1750,9 @@ raise oefmt(self.w_TypeError, "argument must be a string") return self.bytes_w(w_obj) + @specialize.argtype(1) def unicode_w(self, w_obj): + assert w_obj is not None return w_obj.unicode_w(self) def unicode0_w(self, w_obj): @@ -1739,7 +1777,9 @@ # This is here mostly just for gateway.int_unwrapping_space_method(). return bool(self.int_w(w_obj)) + @specialize.argtype(1) def ord(self, w_obj): + assert w_obj is not None return w_obj.ord(self) # This is all interface for gateway.py. @@ -1926,15 +1966,7 @@ class AppExecCache(SpaceCache): @not_rpython def build(cache, source): - space = cache.space - # XXX will change once we have our own compiler - import py - source = source.lstrip() - assert source.startswith('('), "incorrect header in:\n%s" % (source,) - source = py.code.Source("def anonymous%s\n" % source) - w_glob = space.newdict(module=True) - space.exec_(str(source), w_glob, w_glob) - return space.getitem(w_glob, space.newtext('anonymous')) + return cache.space.appdef(source) # Table describing the regular part of the interface of object spaces, diff --git a/pypy/interpreter/test/test_typedef.py b/pypy/interpreter/test/test_typedef.py --- a/pypy/interpreter/test/test_typedef.py +++ b/pypy/interpreter/test/test_typedef.py @@ -419,3 +419,7 @@ def f(): return x assert f.__closure__[0].cell_contents is x + + def test_get_with_none_arg(self): + raises(TypeError, type.__dict__['__mro__'].__get__, None) + raises(TypeError, type.__dict__['__mro__'].__get__, None, None) diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py --- a/pypy/interpreter/typedef.py +++ b/pypy/interpreter/typedef.py @@ -297,6 +297,8 @@ if (space.is_w(w_obj, space.w_None) and not space.is_w(w_cls, space.type(space.w_None))): #print self, w_obj, w_cls + if space.is_w(w_cls, space.w_None): + raise oefmt(space.w_TypeError, "__get__(None, None) is invalid") return self else: try: diff --git a/pypy/module/__builtin__/descriptor.py b/pypy/module/__builtin__/descriptor.py --- a/pypy/module/__builtin__/descriptor.py +++ b/pypy/module/__builtin__/descriptor.py @@ -16,24 +16,31 @@ def descr_init(self, space, w_starttype, w_obj_or_type=None): if space.is_none(w_obj_or_type): w_type = None # unbound super object - w_obj_or_type = space.w_None + w_obj_or_type = None else: w_type = _super_check(space, w_starttype, w_obj_or_type) self.w_starttype = w_starttype self.w_objtype = w_type - self.w_self = w_obj_or_type + self.w_self = w_obj_or_type # may be None def descr_repr(self, space): if self.w_objtype is not None: objtype_name = "<%s object>" % self.w_objtype.getname(space) else: objtype_name = 'NULL' + if self.w_starttype is not None: + starttype_name = self.w_starttype.getname(space) + else: + starttype_name = 'NULL' return space.newtext(", %s>" % ( - self.w_starttype.getname(space), objtype_name)) + starttype_name, objtype_name)) def get(self, space, w_obj, w_type=None): - if self.w_self is None or space.is_w(w_obj, space.w_None): + if self.w_self is not None or space.is_w(w_obj, space.w_None): return self + if self.w_starttype is None: + raise oefmt(space.w_TypeError, + "__get__(x) is invalid on an uninitialized instance of 'super'") else: # if type(self) is W_Super: # XXX write a fast path for this common case @@ -45,6 +52,7 @@ # only use a special logic for bound super objects and not for # getting the __class__ of the super object itself. if self.w_objtype is not None and name != '__class__': + assert self.w_starttype is not None w_value = space.lookup_in_type_starting_at(self.w_objtype, self.w_starttype, name) @@ -54,10 +62,9 @@ return w_value # Only pass 'obj' param if this is instance-mode super # (see CPython sourceforge id #743627) - if self.w_self is self.w_objtype: + w_obj = self.w_self + if w_obj is None or w_obj is self.w_objtype: w_obj = space.w_None - else: - w_obj = self.w_self return space.get_and_call_function(w_get, w_value, w_obj, self.w_objtype) # fallback to object.__getattribute__() @@ -115,7 +122,11 @@ _immutable_fields_ = ["w_fget", "w_fset", "w_fdel"] def __init__(self, space): - pass + self.w_fget = space.w_None + self.w_fset = space.w_None + self.w_fdel = space.w_None + self.w_doc = space.w_None + self.getter_doc = False @unwrap_spec(w_fget=WrappedDefault(None), w_fset=WrappedDefault(None), diff --git a/pypy/module/__builtin__/test/test_descriptor.py b/pypy/module/__builtin__/test/test_descriptor.py --- a/pypy/module/__builtin__/test/test_descriptor.py +++ b/pypy/module/__builtin__/test/test_descriptor.py @@ -280,6 +280,22 @@ assert repr(A()).endswith('>!') assert repr(super(A, A())) == ", >" + def test_super_get_corner_case(self): + class A(object): + pass + s1 = super(A, A()) + assert s1.__get__(42) is s1 + assert s1.__get__(42, int) is s1 + s2 = super(A) + assert s2.__get__(None, "anything") is s2 + # + assert s1.__get__(None, "anything") is s1 + raises(TypeError, s2.__get__, 42) + raises(TypeError, s2.__get__, 42, int) + a = A() + assert s2.__get__(a).__self__ is a + assert s1.__get__(a) is s1 + def test_property_docstring(self): assert property.__doc__.startswith('property') @@ -411,3 +427,35 @@ assert x.y == 42 del x.x assert x.z == 42 + + def test_uninitialized_property(self): + p = property.__new__(property) + raises(AttributeError, p.__get__, 42) + raises(AttributeError, p.__set__, 42, None) + raises(AttributeError, p.__delete__, 42) + assert repr(p).startswith(", NULL>" + assert s.__thisclass__ is s.__self__ is s.__self_class__ is None + assert s.__get__(None, "anything") is s + raises(TypeError, s.__get__, 42) + raises(TypeError, s.__get__, int) + raises(TypeError, s.__get__, type(None)) + raises(AttributeError, "s.abcde") + raises(AttributeError, "s.abcde = 42") + raises(AttributeError, "del s.abcde") diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py --- a/pypy/module/_cffi_backend/__init__.py +++ b/pypy/module/_cffi_backend/__init__.py @@ -3,7 +3,7 @@ from rpython.rlib import rdynload, clibffi from rpython.rtyper.lltypesystem import rffi -VERSION = "1.11.0" +VERSION = "1.11.1" FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI try: diff --git a/pypy/module/_cffi_backend/cffi1_module.py b/pypy/module/_cffi_backend/cffi1_module.py --- a/pypy/module/_cffi_backend/cffi1_module.py +++ b/pypy/module/_cffi_backend/cffi1_module.py @@ -1,4 +1,5 @@ from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rlib import jit from pypy.interpreter.error import oefmt from pypy.interpreter.module import Module @@ -15,7 +16,7 @@ INITFUNCPTR = lltype.Ptr(lltype.FuncType([rffi.VOIDPP], lltype.Void)) - + at jit.dont_look_inside def load_cffi1_module(space, name, path, initptr): # This is called from pypy.module.cpyext.api.load_extension_module() from pypy.module._cffi_backend.call_python import get_ll_cffi_call_python diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py --- a/pypy/module/_cffi_backend/ctypeptr.py +++ b/pypy/module/_cffi_backend/ctypeptr.py @@ -156,10 +156,11 @@ class W_CTypePtrBase(W_CTypePtrOrArray): # base class for both pointers and pointers-to-functions - _attrs_ = ['is_void_ptr', 'is_voidchar_ptr'] - _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr'] + _attrs_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] + _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] is_void_ptr = False is_voidchar_ptr = False + is_onebyte_ptr = False def convert_to_object(self, cdata): ptrdata = rffi.cast(rffi.CCHARPP, cdata)[0] @@ -179,12 +180,20 @@ if self.is_void_ptr or other.is_void_ptr: pass # cast from or to 'void *' elif self.is_voidchar_ptr or other.is_voidchar_ptr: - space = self.space - msg = ("implicit cast from '%s' to '%s' " - "will be forbidden in the future (check that the types " - "are as you expect; use an explicit ffi.cast() if they " - "are correct)" % (other.name, self.name)) - space.warn(space.newtext(msg), space.w_UserWarning) + # for backward compatibility, accept "char *" as either + # source of target. This is not what C does, though, + # so emit a warning that will eventually turn into an + # error. The warning is turned off if both types are + # pointers to single bytes. + if self.is_onebyte_ptr and other.is_onebyte_ptr: + pass # no warning + else: + space = self.space + msg = ("implicit cast from '%s' to '%s' " + "will be forbidden in the future (check that the types " + "are as you expect; use an explicit ffi.cast() if they " + "are correct)" % (other.name, self.name)) + space.warn(space.newtext(msg), space.w_UserWarning) else: raise self._convert_error("compatible pointer", w_ob) @@ -214,6 +223,7 @@ self.is_void_ptr = isinstance(ctitem, ctypevoid.W_CTypeVoid) self.is_voidchar_ptr = (self.is_void_ptr or isinstance(ctitem, ctypeprim.W_CTypePrimitiveChar)) + self.is_onebyte_ptr = (ctitem.size == 1) W_CTypePtrBase.__init__(self, space, size, extra, 2, ctitem) def newp(self, w_init, allocator): diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -1,7 +1,7 @@ # ____________________________________________________________ import sys -assert __version__ == "1.11.0", ("This test_c.py file is for testing a version" +assert __version__ == "1.11.1", ("This test_c.py file is for testing a version" " of cffi that differs from the one that we" " get from 'import _cffi_backend'") if sys.version_info < (3,): @@ -2099,7 +2099,8 @@ if sys.platform.startswith("linux"): BWChar = new_primitive_type("wchar_t") assert sizeof(BWChar) == 4 - assert int(cast(BWChar, -1)) == -1 # signed, on linux + # wchar_t is often signed on Linux, but not always (e.g. on ARM) + assert int(cast(BWChar, -1)) in (-1, 4294967295) def test_char16(): BChar16 = new_primitive_type("char16_t") @@ -3903,9 +3904,11 @@ BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BVoidP = new_pointer_type(new_void_type()) + BUCharP = new_pointer_type(new_primitive_type("unsigned char")) z1 = cast(BCharP, 0) z2 = cast(BIntP, 0) z3 = cast(BVoidP, 0) + z4 = cast(BUCharP, 0) with warnings.catch_warnings(record=True) as w: newp(new_pointer_type(BIntP), z1) # warn assert len(w) == 1 @@ -3919,6 +3922,12 @@ assert len(w) == 2 newp(new_pointer_type(BIntP), z3) # fine assert len(w) == 2 + newp(new_pointer_type(BCharP), z4) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z1) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z3) # fine + assert len(w) == 2 # check that the warnings are associated with lines in this file assert w[1].lineno == w[0].lineno + 4 diff --git a/pypy/module/_csv/interp_csv.py b/pypy/module/_csv/interp_csv.py --- a/pypy/module/_csv/interp_csv.py +++ b/pypy/module/_csv/interp_csv.py @@ -29,10 +29,15 @@ return default return space.is_true(w_src) -def _get_int(space, w_src, default): +def _get_int(space, w_src, default, attrname): if w_src is None: return default - return space.int_w(w_src) + try: + return space.int_w(w_src) + except OperationError as e: + if e.match(space, space.w_TypeError): + raise oefmt(space.w_TypeError, '"%s" must be an int', attrname) + raise def _get_str(space, w_src, default, attrname): if w_src is None: @@ -100,7 +105,7 @@ dialect.escapechar = _get_char(space, w_escapechar, '\0', 'escapechar') dialect.lineterminator = _get_str(space, w_lineterminator, '\r\n', 'lineterminator') dialect.quotechar = _get_char(space, w_quotechar, '"', 'quotechar') - tmp_quoting = _get_int(space, w_quoting, QUOTE_MINIMAL) + tmp_quoting = _get_int(space, w_quoting, QUOTE_MINIMAL, 'quoting') dialect.skipinitialspace = _get_bool(space, w_skipinitialspace, False) dialect.strict = _get_bool(space, w_strict, False) diff --git a/pypy/module/_csv/test/test_dialect.py b/pypy/module/_csv/test/test_dialect.py --- a/pypy/module/_csv/test/test_dialect.py +++ b/pypy/module/_csv/test/test_dialect.py @@ -65,7 +65,8 @@ name = attempt[0] for value in attempt[1:]: kwargs = {name: value} - raises(TypeError, _csv.register_dialect, 'foo1', **kwargs) + exc_info = raises(TypeError, _csv.register_dialect, 'foo1', **kwargs) + assert name in exc_info.value.args[0] exc_info = raises(TypeError, _csv.register_dialect, 'foo1', lineterminator=4) assert exc_info.value.args[0] == '"lineterminator" must be a string' diff --git a/pypy/module/_multiprocessing/interp_win32.py b/pypy/module/_multiprocessing/interp_win32.py --- a/pypy/module/_multiprocessing/interp_win32.py +++ b/pypy/module/_multiprocessing/interp_win32.py @@ -109,6 +109,7 @@ raise wrap_windowserror(space, rwin32.lastSavedWindowsError()) def GetLastError(space): + """NOTE: don't use this. See issue #2658""" return space.newint(rwin32.GetLastError_saved()) # __________________________________________________________ diff --git a/pypy/module/_multiprocessing/test/test_connection.py b/pypy/module/_multiprocessing/test/test_connection.py --- a/pypy/module/_multiprocessing/test/test_connection.py +++ b/pypy/module/_multiprocessing/test/test_connection.py @@ -12,6 +12,7 @@ 'itertools', 'select', 'struct', 'binascii']} if sys.platform == 'win32': spaceconfig['usemodules'].append('_rawffi') + spaceconfig['usemodules'].append('_cffi_backend') else: spaceconfig['usemodules'].append('fcntl') @@ -39,6 +40,10 @@ class BaseConnectionTest(object): def test_connection(self): + import sys + # if not translated, for win32 + if not hasattr(sys, 'executable'): + sys.executable = 'from test_connection.py' rhandle, whandle = self.make_pair() whandle.send_bytes("abc") @@ -50,6 +55,10 @@ assert obj == obj2 def test_poll(self): + import sys + # if not translated, for win32 + if not hasattr(sys, 'executable'): + sys.executable = 'from test_connection.py' rhandle, whandle = self.make_pair() assert rhandle.poll() == False @@ -64,6 +73,10 @@ def test_read_into(self): import array, multiprocessing + import sys + # if not translated, for win32 + if not hasattr(sys, 'executable'): + sys.executable = 'from test_connection.py' rhandle, whandle = self.make_pair() obj = [1, 2.0, "hello"] @@ -81,6 +94,7 @@ } if sys.platform == 'win32': spaceconfig['usemodules'].append('_rawffi') + spaceconfig['usemodules'].append('_cffi_backend') def setup_class(cls): if sys.platform != "win32": @@ -109,6 +123,7 @@ } if sys.platform == 'win32': spaceconfig['usemodules'].append('_rawffi') + spaceconfig['usemodules'].append('_cffi_backend') else: spaceconfig['usemodules'].append('fcntl') 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 @@ -1,6 +1,6 @@ import sys from rpython.rlib.rstring import StringBuilder -from rpython.rlib.objectmodel import specialize, always_inline +from rpython.rlib.objectmodel import specialize, always_inline, r_dict from rpython.rlib import rfloat, runicode from rpython.rtyper.lltypesystem import lltype, rffi from pypy.interpreter.error import oefmt @@ -42,6 +42,22 @@ ll_res.chars[i] = cast_primitive(UniChar, ch) return hlunicode(ll_res) +def slice_eq(a, b): + (ll_chars1, start1, length1, _) = a + (ll_chars2, start2, length2, _) = b + if length1 != length2: + return False + j = start2 + for i in range(start1, start1 + length1): + if ll_chars1[i] != ll_chars2[j]: + return False + j += 1 From pypy.commits at gmail.com Sat Oct 7 09:09:13 2017 From: pypy.commits at gmail.com (wlav) Date: Sat, 07 Oct 2017 06:09:13 -0700 (PDT) Subject: [pypy-commit] pypy default: further cleanup of module _cppyy, with clingwrapper now living in external package shared with CPython Message-ID: <59d8d1f9.c23a1c0a.a1b02.cf3c@mx.google.com> Author: Wim Lavrijsen Branch: Changeset: r92597:87092e78f4c5 Date: 2017-10-03 22:55 -0700 http://bitbucket.org/pypy/pypy/changeset/87092e78f4c5/ Log: further cleanup of module _cppyy, with clingwrapper now living in external package shared with CPython diff too long, truncating to 2000 out of 7797 lines 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 @@ -1,12 +1,10 @@ from pypy.interpreter.mixedmodule import MixedModule class Module(MixedModule): - "This module provides runtime bindings to C++ code for which reflection\n\ - info has been generated. Current supported back-ends are Reflex and CINT.\n\ - See http://doc.pypy.org/en/latest/cppyy.html for full details." + "This module brigdes the cppyy frontend with its backend, through PyPy.\n\ + See http://cppyy.readthedocs.io/en/latest for full details." interpleveldefs = { - '_load_dictionary' : 'interp_cppyy.load_dictionary', '_resolve_name' : 'interp_cppyy.resolve_name', '_scope_byname' : 'interp_cppyy.scope_byname', '_template_byname' : 'interp_cppyy.template_byname', @@ -15,14 +13,13 @@ '_set_function_generator': 'interp_cppyy.set_function_generator', '_register_class' : 'interp_cppyy.register_class', '_get_nullptr' : 'interp_cppyy.get_nullptr', - 'CPPInstanceBase' : 'interp_cppyy.W_CPPInstance', + 'CPPClassBase' : 'interp_cppyy.W_CPPClass', 'addressof' : 'interp_cppyy.addressof', 'bind_object' : 'interp_cppyy.bind_object', } appleveldefs = { '_init_pythonify' : 'pythonify._init_pythonify', - 'load_reflection_info' : 'pythonify.load_reflection_info', 'add_pythonization' : 'pythonify.add_pythonization', 'Template' : 'pythonify.CPPTemplate', } diff --git a/pypy/module/_cppyy/backend/create_cppyy_package.py b/pypy/module/_cppyy/backend/create_cppyy_package.py deleted file mode 100755 --- a/pypy/module/_cppyy/backend/create_cppyy_package.py +++ /dev/null @@ -1,649 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function - -import os, sys -import argparse, re, shutil, tarfile, urllib2 - - -DEBUG_TESTBUILD = False - -TARBALL_CACHE_DIR = 'releases' - -ROOT_KEEP = ['build', 'cmake', 'config', 'core', 'etc', 'interpreter', - 'io', 'LICENSE', 'net', 'Makefile', 'CMakeLists.txt', 'math', - 'main'] # main only needed in more recent root b/c of rootcling -ROOT_CORE_KEEP = ['CMakeLists.txt', 'base', 'clib', 'clingutils', 'cont', - 'dictgen', 'foundation', 'lzma', 'macosx', 'meta', - 'metacling', 'metautils', 'rootcling_stage1', 'textinput', - 'thread', 'unix', 'utils', 'winnt', 'zip'] -ROOT_IO_KEEP = ['CMakeLists.txt', 'io', 'rootpcm'] -ROOT_NET_KEEP = ['CMakeLists.txt', 'net'] -ROOT_MATH_KEEP = ['CMakeLists.txt', 'mathcore'] -ROOT_ETC_KEEP = ['Makefile.arch', 'class.rules', 'cmake', 'dictpch', - 'gdb-backtrace.sh', 'gitinfo.txt', 'helgrind-root.supp', - 'hostcert.conf', 'system.plugins-ios', - 'valgrind-root-python.supp', 'valgrind-root.supp', 'vmc'] - -ROOT_EXPLICIT_REMOVE = ['core/base/v7', 'math/mathcore/v7', 'io/io/v7'] - - -ERR_RELEASE_NOT_FOUND = 2 - - -# -## CLI arguments -# -class ReleaseValidation(argparse.Action): - def __call__(self, parser, namespace, value, option_string=None): - if not re.match(r'6\.\d\d\.\d\d', value): - raise argparse.ArgumentTypeError( - "release number should of the form '6.dd.dd'") - setattr(namespace, self.dest, value) - return value - -parser = argparse.ArgumentParser( - description='Build PyPi package for cppyy containing the minimum of ROOT') -parser.add_argument('-r', '--release', type=str, nargs='?', - action=ReleaseValidation, help='ROOT release to use') - -args = parser.parse_args() - - -# -## ROOT source pull and cleansing -# -def clean_directory(directory, keeplist, trim_cmake=True): - removed_entries = [] - for entry in os.listdir(directory): - if entry[0] == '.' or entry in keeplist: - continue - removed_entries.append(entry) - entry = os.path.join(directory, entry) - print('now removing', entry) - if os.path.isdir(entry): - shutil.rmtree(entry) - else: - os.remove(entry) - - if not trim_cmake: - return - - # now take the removed entries out of the CMakeLists.txt - if removed_entries: - inp = os.path.join(directory, 'CMakeLists.txt') - print('trimming', inp) - outp = inp+'.new' - new_cml = open(outp, 'w') - for line in open(inp).readlines(): - if ('add_subdirectory' in line) or\ - ('COMMAND' in line and 'copy' in line) or\ - ('ROOT_ADD_TEST_SUBDIRECTORY' in line) or\ - ('install(DIRECTORY' in line): - for sub in removed_entries: - if sub in line: - line = '#'+line - break - new_cml.write(line) - new_cml.close() - os.rename(outp, inp) - else: - print('reusing existing %s/CMakeLists.txt' % (directory,)) - - -class ReleaseValidation(argparse.Action): - def __call__(self, parser, namespace, value, option_string=None): - if not re.match(r'6\.\d\d\.\d\d', value): - raise argparse.ArgumentTypeError( - "release number should of the form '6.dd.dd'") - setattr(namespace, self.dest, value) - return value - -parser = argparse.ArgumentParser( - description='Build PyPi package for cppyy containing the minimum of ROOT') -parser.add_argument('-r', '--release', type=str, nargs='?', - action=ReleaseValidation, help='ROOT release to use') - -args = parser.parse_args() - -if not os.path.exists(TARBALL_CACHE_DIR): - os.mkdir(TARBALL_CACHE_DIR) - -if args.release: - # use provided release - fn = 'root_v%s.source.tar.gz' % args.release - addr = 'https://root.cern.ch/download/'+fn - if not os.path.exists(os.path.join(TARBALL_CACHE_DIR, fn)): - try: - print('retrieving', fn) - resp = urllib2.urlopen(addr, fn) - out = open(os.path.join(TARBALL_CACHE_DIR, fn), 'wb') - out.write(resp.read()) - out.close() - except urllib2.HTTPError: - print('release %s not found' % args.release) - sys.exit(ERR_RELEASE_NOT_FOUND) - else: - print('reusing', fn, 'from local directory') -else: - print('provide release ... getting latest release is not yet implemented ...') - sys.exit(1) - # get latest and set fn, args.release, etc. - -# construct version for package -args.version = '' -testnext = False -for c in args.release: - if testnext: - testnext = False - if c == '0': - continue - if c == '.': - testnext = True - args.version += c -args.version += '.0' - -fn = os.path.join(TARBALL_CACHE_DIR, fn) -pkgdir = os.path.join('root-'+args.release) -if not os.path.exists(pkgdir): - print('now extracting', args.release) - tf = tarfile.TarFile.gzopen(fn) - tf.extractall() - tf.close() -else: - print('reusing existing directory', pkgdir) - -# remove everything except for the listed set of libraries -os.chdir(pkgdir) - -clean_directory(os.path.curdir, ROOT_KEEP) -clean_directory('core', ROOT_CORE_KEEP) -clean_directory('etc', ROOT_ETC_KEEP, trim_cmake=False) -clean_directory('io', ROOT_IO_KEEP) -clean_directory('math', ROOT_MATH_KEEP) -clean_directory('net', ROOT_NET_KEEP) - - -# trim main (only need rootcling) -print('trimming main') -for entry in os.listdir('main/src'): - if entry != 'rootcling.cxx': - os.remove('main/src/'+entry) -inp = 'main/CMakeLists.txt' -outp = inp+'.new' -new_cml = open(outp, 'w') -for line in open(inp).readlines(): - if ('ROOT_EXECUTABLE' in line or\ - 'SET_TARGET_PROPERTIES' in line) and\ - not 'rootcling' in line: - line = '#'+line - new_cml.write(line) -new_cml.close() -os.rename(outp, inp) - - -# remove afterimage and ftgl explicitly -print('trimming externals') -for cmf in ['AfterImage', 'FTGL']: - os.remove('cmake/modules/Find%s.cmake' % (cmf,)) -inp = 'cmake/modules/SearchInstalledSoftware.cmake' -outp = inp+'.new' -now_stripping = False -new_cml = open(outp, 'w') -for line in open(inp).readlines(): - if '#---Check for ftgl if needed' == line[0:28] or\ - '#---Check for AfterImage' == line[0:24]: - now_stripping = True - elif '#---Check' == line[0:9]: - now_stripping = False - if now_stripping: - line = '#'+line - new_cml.write(line) -new_cml.close() -os.rename(outp, inp) - -inp = 'cmake/modules/RootBuildOptions.cmake' -outp = inp+'.new' -new_cml = open(outp, 'w') -for line in open(inp).readlines(): - if 'ROOT_BUILD_OPTION(builtin_ftgl' in line or\ - 'ROOT_BUILD_OPTION(builtin_afterimage' in line: - line = '#'+line - new_cml.write(line) -new_cml.close() -os.rename(outp, inp) - - -# remove testing and examples -print('trimming testing') -inp = 'CMakeLists.txt' -outp = inp+'.new' -now_stripping = False -new_cml = open(outp, 'w') -for line in open(inp).readlines(): - if '#---Configure Testing using CTest' == line[0:33] or\ - '#---hsimple.root' == line[0:16]: - now_stripping = True - elif '#---Packaging' == line[0:13] or\ - '#---version' == line[0:11]: - now_stripping = False - if now_stripping: - line = '#'+line - new_cml.write(line) -new_cml.close() -os.rename(outp, inp) - -print('trimming RootCPack') -inp = 'cmake/modules/RootCPack.cmake' -outp = inp+'.new' -new_cml = open(outp, 'w') -for line in open(inp): - if 'README.txt' in line: - line = '#'+line - new_cml.write(line) -new_cml.close() -os.rename(outp, inp) - -# some more explicit removes: -for dir_to_remove in ROOT_EXPLICIT_REMOVE: - try: - shutil.rmtree(dir_to_remove) - except OSError: - pass - -# special fixes -inp = 'core/base/src/TVirtualPad.cxx' -outp = inp+'.new' -new_cml = open(outp, 'w') -for line in open(inp): - if '#include "X3DBuffer.h"' == line[0:22]: - line = """//#include "X3DBuffer.h" -typedef struct _x3d_sizeof_ { - int numPoints; - int numSegs; - int numPolys; -} Size3D; -""" - new_cml.write(line) -new_cml.close() -os.rename(outp, inp) - -inp = 'math/mathcore/src/Fitter.cxx' -if os.path.exists(inp): - outp = inp+'.new' - new_cml = open(outp, 'w') - for line in open(inp): - if '#include "TF1.h"' in line: - continue - new_cml.write(line) - new_cml.close() - os.rename(outp, inp) - -# done -os.chdir(os.path.pardir) - -# debugging: run a test build -if DEBUG_TESTBUILD: - print('running a debug test build') - tb = "test_builddir" - if os.path.exists(tb): - shutil.rmtree(tb) - os.mkdir(tb) - os.chdir(tb) - os.system('cmake ../%s -DCMAKE_INSTALL_PREFIX=../install -Dminimal=ON -Dasimage=OFF' % pkgdir) - os.system('make -j 32') - - -# -## package creation -# -countdown = 0 -pidir = 'Package-'+args.release -print('creating package', pidir) -if not os.path.exists(pidir): - os.mkdir(pidir) -os.chdir(pidir); countdown += 1 - -print('creating LICENSE.txt') -with open('LICENSE.txt', 'w') as outp: - outp.write("""There are three main parts: - - LLVM: distributed under University of Illinois/NCSA Open Source License - https://opensource.org/licenses/UoI-NCSA.php - ROOT: distributed under LGPL 2.1 - https://root.cern.ch/license - Cppyy: distributed under LBNL BSD - https://fedoraproject.org/wiki/Licensing/LBNLBSD -""") - -print('creating MANIFEST.in') -with open('MANIFEST.in', 'w') as outp: - outp.write("""# Include the license file -include LICENSE.txt - -# Include the data files -recursive-include src * -""") - -print('creating README.rst') -with open('README.rst', 'w') as outp: - outp.write("""PyPy cling-support -================== - ----- - -Find the documentation here: - http://doc.pypy.org/en/latest/cppyy.html -""") - -print('creating setup.cfg') -with open('setup.cfg', 'w') as outp: - outp.write("""[bdist_wheel] -universal=0 -""") - -print('creating setup.py') -with open('setup.py', 'w') as outp: - outp.write("""import os, sys, subprocess -from setuptools import setup, find_packages -from distutils import log -from distutils.command.build import build as _build -from setuptools.command.install import install as _install -from distutils.sysconfig import get_python_lib -from distutils.errors import DistutilsSetupError -from codecs import open - -here = os.path.abspath(os.path.dirname(__file__)) -with open(os.path.join(here, 'README.rst'), encoding='utf-8') as f: - long_description = f.read() - -builddir = None -def get_builddir(): - global builddir - if builddir is None: - topdir = os.getcwd() - builddir = os.path.join(topdir, 'builddir') - return builddir - -srcdir = None -def get_srcdir(): - global srcdir - if srcdir is None: - topdir = os.getcwd() - srcdir = os.path.join(topdir, 'src', 'backend') - return srcdir - -class my_cmake_build(_build): - def __init__(self, dist, *args, **kwargs): - _build.__init__(self, dist, *args, **kwargs) - # TODO: can't seem to find a better way of getting hold of - # the install_lib parameter during the build phase ... - prefix = '' - try: - prefix = dist.get_command_obj('install').install_lib - except AttributeError: - pass - if not prefix: - prefix = get_python_lib(1, 0) - self.prefix = os.path.join(prefix, 'cppyy_backend') - - def run(self): - # base run - _build.run(self) - - # custom run - log.info('Now building libcppyy_backend.so and dependencies') - builddir = get_builddir() - srcdir = get_srcdir() - if not os.path.exists(builddir): - log.info('Creating build directory %s ...' % builddir) - os.makedirs(builddir) - - os.chdir(builddir) - log.info('Running cmake for cppyy_backend') - if subprocess.call([ - 'cmake', srcdir, '-Dminimal=ON -Dasimage=OFF', - '-DCMAKE_INSTALL_PREFIX='+self.prefix]) != 0: - raise DistutilsSetupError('Failed to configure cppyy_backend') - - nprocs = os.getenv("MAKE_NPROCS") - if nprocs: - try: - ival = int(nprocs) - nprocs = '-j'+nprocs - except ValueError: - log.warn("Integer expected for MAKE_NPROCS, but got %s (ignored)", nprocs) - nprocs = '-j1' - else: - nprocs = '-j1' - log.info('Now building cppyy_backend and dependencies ...') - if subprocess.call(['make', nprocs]) != 0: - raise DistutilsSetupError('Failed to build cppyy_backend') - - log.info('build finished') - -class my_libs_install(_install): - def run(self): - # base install - _install.run(self) - - # custom install - log.info('Now installing libcppyy_backend.so and dependencies') - builddir = get_builddir() - if not os.path.exists(builddir): - raise DistutilsSetupError('Failed to find build dir!') - os.chdir(builddir) - - prefix = self.install_lib - log.info('Now installing in %s ...', prefix) - if subprocess.call(['make', 'install']) != 0: - raise DistutilsSetupError('Failed to install cppyy_backend') - - log.info('install finished') - - def get_outputs(self): - outputs = _install.get_outputs(self) - outputs.append(os.path.join(self.install_lib, 'cppyy_backend')) - return outputs - -setup( - name='PyPy-cppyy-backend', -""") - outp.write(" version='%s', # corresponds to ROOT %s, extra number is for packager\n"\ - % (args.version, args.release)) - outp.write(""" description='Cling support for PyPy', - long_description=long_description, - - url='http://pypy.org', - - # Author details - author='PyPy Developers', - author_email='pypy-dev at python.org', - - license='LLVM: UoI-NCSA; ROOT: LGPL 2.1; Cppyy: LBNL BSD', - - classifiers=[ - 'Development Status :: 4 - Beta', - - 'Intended Audience :: Developers', - - 'Topic :: Software Development', - 'Topic :: Software Development :: Interpreters', - - #'License :: OSI Approved :: MIT License', - - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: Implementation :: PyPy', - 'Programming Language :: C', - 'Programming Language :: C++', - - 'Natural Language :: English' - ], - - keywords='interpreter development', - - packages=find_packages('src', ['backend']), - include_package_data=True, - - extras_require={ - }, - - cmdclass = { - 'build': my_cmake_build, - 'install': my_libs_install, - }, -) -""") - - -print('creating src ... ROOT part') -if not os.path.exists('src'): - os.mkdir('src') -os.chdir('src'); countdown += 1 -if not os.path.exists('backend'): - src = os.path.join(os.path.pardir, os.path.pardir, pkgdir) - print('now copying', src) - shutil.copytree(src, 'backend') - -print('creating src ... cppyy part') -os.chdir('backend'); countdown += 1 -if not os.path.exists('cppyy'): - os.mkdir('cppyy') - os.chdir('cppyy'); countdown += 1 - - with open('CMakeLists.txt', 'w') as outp: - outp.write("""############################################################################ -# CMakeLists.txt file for building cppyy package -############################################################################ - -ROOT_GLOB_SOURCES(sources ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cxx) -set_source_files_properties(${sources} COMPILE_FLAGS "-fomit-frame-pointer -fvisibility=hidden -DRPY_EXTERN=RPY_EXPORTED -DRPYTHON_LL2CTYPES") - -add_definitions(${CLING_CXXFLAGS}) - -ROOT_LINKER_LIBRARY(cppyy_backend ${sources} - LIBRARIES ${CMAKE_DL_LIBS} - DEPENDENCIES Core Cling RIO Thread) - -add_dependencies(cppyy_backend CLING) -""") - - os.mkdir('src') - os.chdir('src'); countdown += 1 - print('pulling cppyy/clingcwrapper.cxx from pypy') - base = 'https://bitbucket.org/pypy/pypy/raw/default/pypy/module/cppyy/' - for cppyy_file in ['src/callcontext.h', 'include/capi.h', 'src/clingcwrapper.cxx', - 'include/clingcwrapper.h', 'include/cpp_cppyy.h', 'include/cppyy.h']: - resp = urllib2.urlopen(base+cppyy_file) - with open(os.path.basename(cppyy_file), 'w') as outp: - outp.write(resp.read()) - - # fix include - inp = 'capi.h' - outp = inp+'.new' - new_cml = open(outp, 'w') - for line in open(inp).readlines(): - if 'src/precommondefs.h' in line: - line = '#include "precommondefs.h"\n' - new_cml.write(line) - new_cml.close() - os.rename(outp, inp) - - with open('precommondefs.h', 'w') as outp: - outp.write("""/***** Start of precommondefs.h *****/ - -/* This is extracted from pyconfig.h from CPython. It sets the macros - that affect the features we get from system include files. - It must not #include anything. */ - -#ifndef __PYPY_PRECOMMONDEFS_H -#define __PYPY_PRECOMMONDEFS_H - - -/* Define on Darwin to activate all library features */ -#define _DARWIN_C_SOURCE 1 -/* This must be set to 64 on some systems to enable large file support. */ -#define _FILE_OFFSET_BITS 64 -/* Define on Linux to activate all library features */ -#define _GNU_SOURCE 1 -/* This must be defined on some systems to enable large file support. */ -#define _LARGEFILE_SOURCE 1 -/* Define on NetBSD to activate all library features */ -#define _NETBSD_SOURCE 1 -/* Define to activate features from IEEE Stds 1003.1-2001 */ -#ifndef _POSIX_C_SOURCE -# define _POSIX_C_SOURCE 200112L -#endif -/* Define on FreeBSD to activate all library features */ -#define __BSD_VISIBLE 1 -#define __XSI_VISIBLE 700 -/* Windows: winsock/winsock2 mess */ -#define WIN32_LEAN_AND_MEAN -#ifdef _WIN64 - typedef __int64 Signed; - typedef unsigned __int64 Unsigned; -# define SIGNED_MIN LLONG_MIN -#else - typedef long Signed; - typedef unsigned long Unsigned; -# define SIGNED_MIN LONG_MIN -#endif - -#if !defined(RPY_ASSERT) && !defined(RPY_LL_ASSERT) && !defined(NDEBUG) -# define NDEBUG -#endif - - -/* All functions and global variables declared anywhere should use - one of the following attributes: - - RPY_EXPORTED: the symbol is exported out of libpypy-c.so. - - RPY_EXTERN: the symbol is not exported out of libpypy-c.so, but - otherwise works like 'extern' by being available to - other C sources. - - static: as usual, this means the symbol is local to this C file. - - Don't use _RPY_HIDDEN directly. For tests involving building a custom - .so, translator/tool/cbuild.py overrides RPY_EXTERN so that it becomes - equal to RPY_EXPORTED. - - Any function or global variable declared with no attribute at all is - a bug; please report or fix it. -*/ -#ifdef __GNUC__ -# define RPY_EXPORTED extern __attribute__((visibility("default"))) -# define _RPY_HIDDEN __attribute__((visibility("hidden"))) -#else -# define RPY_EXPORTED extern __declspec(dllexport) -# define _RPY_HIDDEN /* nothing */ -#endif -#ifndef RPY_EXTERN -# define RPY_EXTERN extern _RPY_HIDDEN -#endif - - -#endif /* __PYPY_PRECOMMONDEFS_H */ - -/***** End of precommondefs.h *****/ -""") - -# back up to pip package top -for i in range(countdown-1): - os.chdir(os.path.pardir) - -# add cppyy module to cmake -os.chdir('src/backend') -inp = 'CMakeLists.txt' -print('adding cppyy to cmake') -outp = inp+'.new' -new_cml = open(outp, 'w') -for line in open(inp).readlines(): - if 'add_subdirectory' in line and 'net' in line: - line += 'add_subdirectory (cppyy)\n' - new_cml.write(line) -new_cml.close() -os.rename(outp, inp) - -# done! diff --git a/pypy/module/_cppyy/bench/Makefile b/pypy/module/_cppyy/bench/Makefile deleted file mode 100644 --- a/pypy/module/_cppyy/bench/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -all: bench02Dict_reflex.so - -ROOTSYS := ${ROOTSYS} - -ifeq ($(ROOTSYS),) - genreflex=genreflex - cppflags= -else - genreflex=$(ROOTSYS)/bin/genreflex - cppflags=-I$(ROOTSYS)/include -L$(ROOTSYS)/lib -endif - -PLATFORM := $(shell uname -s) -ifeq ($(PLATFORM),Darwin) - cppflags+=-dynamiclib -single_module -arch x86_64 -endif - -ifeq ($(shell $(genreflex) --help | grep -- --with-methptrgetter),) - genreflexflags= - cppflags2=-O3 -fPIC -else - genreflexflags=--with-methptrgetter - cppflags2=-Wno-pmf-conversions -O3 -fPIC -endif - - -bench02Dict_reflex.so: bench02.h bench02.cxx bench02.xml - $(genreflex) bench02.h $(genreflexflags) --selection=bench02.xml -I$(ROOTSYS)/include - g++ -o $@ bench02.cxx bench02_rflx.cpp -I$(ROOTSYS)/include -shared -std=c++11 -lHistPainter `root-config --libs` $(cppflags) $(cppflags2) diff --git a/pypy/module/_cppyy/bench/bench02.cxx b/pypy/module/_cppyy/bench/bench02.cxx deleted file mode 100644 --- a/pypy/module/_cppyy/bench/bench02.cxx +++ /dev/null @@ -1,79 +0,0 @@ -#include "bench02.h" - -#include "TROOT.h" -#include "TApplication.h" -#include "TDirectory.h" -#include "TInterpreter.h" -#include "TSystem.h" -#include "TBenchmark.h" -#include "TStyle.h" -#include "TError.h" -#include "Getline.h" -#include "TVirtualX.h" - -#include "Api.h" - -#include - -TClass *TClass::GetClass(const char*, Bool_t, Bool_t) { - static TClass* dummy = new TClass("__dummy__", kTRUE); - return dummy; // is deleted by gROOT at shutdown -} - -class TTestApplication : public TApplication { -public: - TTestApplication( - const char* acn, Int_t* argc, char** argv, Bool_t bLoadLibs = kTRUE); - virtual ~TTestApplication(); -}; - -TTestApplication::TTestApplication( - const char* acn, int* argc, char** argv, bool do_load) : TApplication(acn, argc, argv) { - if (do_load) { - // follow TRint to minimize differences with CINT - ProcessLine("#include ", kTRUE); - ProcessLine("#include <_string>", kTRUE); // for std::string iostream. - ProcessLine("#include ", kTRUE); // needed because they're used within the - ProcessLine("#include ", kTRUE); // core ROOT dicts and CINT won't be able - // to properly unload these files - } - - // save current interpreter context - gInterpreter->SaveContext(); - gInterpreter->SaveGlobalsContext(); - - // prevent crashes on accessing history - Gl_histinit((char*)"-"); - - // prevent ROOT from exiting python - SetReturnFromRun(kTRUE); -} - -TTestApplication::~TTestApplication() {} - -static const char* appname = "pypy-cppyy"; - -Bench02RootApp::Bench02RootApp() { - gROOT->SetBatch(kTRUE); - if (!gApplication) { - int argc = 1; - char* argv[1]; argv[0] = (char*)appname; - gApplication = new TTestApplication(appname, &argc, argv, kFALSE); - } -} - -Bench02RootApp::~Bench02RootApp() { - // TODO: ROOT globals cleanup ... (?) -} - -void Bench02RootApp::report() { - std::cout << "gROOT is: " << gROOT << std::endl; - std::cout << "gApplication is: " << gApplication << std::endl; -} - -void Bench02RootApp::close_file(TFile* f) { - std::cout << "closing file " << f->GetName() << " ... " << std::endl; - f->Write(); - f->Close(); - std::cout << "... file closed" << std::endl; -} diff --git a/pypy/module/_cppyy/bench/bench02.h b/pypy/module/_cppyy/bench/bench02.h deleted file mode 100644 --- a/pypy/module/_cppyy/bench/bench02.h +++ /dev/null @@ -1,72 +0,0 @@ -#include "TString.h" - -#include "TCanvas.h" -#include "TFile.h" -#include "TProfile.h" -#include "TNtuple.h" -#include "TH1F.h" -#include "TH2F.h" -#include "TRandom.h" -#include "TRandom3.h" - -#include "TROOT.h" -#include "TApplication.h" -#include "TSystem.h" - -#include "TArchiveFile.h" -#include "TBasket.h" -#include "TBenchmark.h" -#include "TBox.h" -#include "TBranchRef.h" -#include "TBrowser.h" -#include "TClassGenerator.h" -#include "TClassRef.h" -#include "TClassStreamer.h" -#include "TContextMenu.h" -#include "TEntryList.h" -#include "TEventList.h" -#include "TF1.h" -#include "TFileCacheRead.h" -#include "TFileCacheWrite.h" -#include "TFileMergeInfo.h" -#include "TFitResult.h" -#include "TFolder.h" -//#include "TFormulaPrimitive.h" -#include "TFunction.h" -#include "TFrame.h" -#include "TGlobal.h" -#include "THashList.h" -#include "TInetAddress.h" -#include "TInterpreter.h" -#include "TKey.h" -#include "TLegend.h" -#include "TMethodCall.h" -#include "TPluginManager.h" -#include "TProcessUUID.h" -#include "TSchemaRuleSet.h" -#include "TStyle.h" -#include "TSysEvtHandler.h" -#include "TTimer.h" -#include "TView.h" -//#include "TVirtualCollectionProxy.h" -#include "TVirtualFFT.h" -#include "TVirtualHistPainter.h" -#include "TVirtualIndex.h" -#include "TVirtualIsAProxy.h" -#include "TVirtualPadPainter.h" -#include "TVirtualRefProxy.h" -#include "TVirtualStreamerInfo.h" -#include "TVirtualViewer3D.h" - -#include -#include - - -class Bench02RootApp { -public: - Bench02RootApp(); - ~Bench02RootApp(); - - void report(); - void close_file(TFile* f); -}; diff --git a/pypy/module/_cppyy/bench/bench02.xml b/pypy/module/_cppyy/bench/bench02.xml deleted file mode 100644 --- a/pypy/module/_cppyy/bench/bench02.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pypy/module/_cppyy/bench/hsimple.C b/pypy/module/_cppyy/bench/hsimple.C deleted file mode 100644 --- a/pypy/module/_cppyy/bench/hsimple.C +++ /dev/null @@ -1,109 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -TFile *hsimple(Int_t get=0) -{ -// This program creates : -// - a one dimensional histogram -// - a two dimensional histogram -// - a profile histogram -// - a memory-resident ntuple -// -// These objects are filled with some random numbers and saved on a file. -// If get=1 the macro returns a pointer to the TFile of "hsimple.root" -// if this file exists, otherwise it is created. -// The file "hsimple.root" is created in $ROOTSYS/tutorials if the caller has -// write access to this directory, otherwise the file is created in $PWD - - TString filename = "hsimple.root"; - TString dir = gSystem->UnixPathName(gInterpreter->GetCurrentMacroName()); - dir.ReplaceAll("hsimple.C",""); - dir.ReplaceAll("/./","/"); - TFile *hfile = 0; - if (get) { - // if the argument get =1 return the file "hsimple.root" - // if the file does not exist, it is created - TString fullPath = dir+"hsimple.root"; - if (!gSystem->AccessPathName(fullPath,kFileExists)) { - hfile = TFile::Open(fullPath); //in $ROOTSYS/tutorials - if (hfile) return hfile; - } - //otherwise try $PWD/hsimple.root - if (!gSystem->AccessPathName("hsimple.root",kFileExists)) { - hfile = TFile::Open("hsimple.root"); //in current dir - if (hfile) return hfile; - } - } - //no hsimple.root file found. Must generate it ! - //generate hsimple.root in $ROOTSYS/tutorials if we have write access - if (!gSystem->AccessPathName(dir,kWritePermission)) { - filename = dir+"hsimple.root"; - } else if (!gSystem->AccessPathName(".",kWritePermission)) { - //otherwise generate hsimple.root in the current directory - } else { - printf("you must run the script in a directory with write access\n"); - return 0; - } - hfile = (TFile*)gROOT->FindObject(filename); if (hfile) hfile->Close(); - hfile = new TFile(filename,"RECREATE","Demo ROOT file with histograms"); - - // Create some histograms, a profile histogram and an ntuple - TH1F *hpx = new TH1F("hpx","This is the px distribution",100,-4,4); - hpx->SetFillColor(48); - TH2F *hpxpy = new TH2F("hpxpy","py vs px",40,-4,4,40,-4,4); - TProfile *hprof = new TProfile("hprof","Profile of pz versus px",100,-4,4,0,20); - TNtuple *ntuple = new TNtuple("ntuple","Demo ntuple","px:py:pz:random:i"); - - gBenchmark->Start("hsimple"); - - // Create a new canvas. - TCanvas *c1 = new TCanvas("c1","Dynamic Filling Example",200,10,700,500); - c1->SetFillColor(42); - c1->GetFrame()->SetFillColor(21); - c1->GetFrame()->SetBorderSize(6); - c1->GetFrame()->SetBorderMode(-1); - - - // Fill histograms randomly - TRandom3 random; - Float_t px, py, pz; - const Int_t kUPDATE = 1000; - for (Int_t i = 0; i < 50000; i++) { - // random.Rannor(px,py); - px = random.Gaus(0, 1); - py = random.Gaus(0, 1); - pz = px*px + py*py; - Float_t rnd = random.Rndm(1); - hpx->Fill(px); - hpxpy->Fill(px,py); - hprof->Fill(px,pz); - ntuple->Fill(px,py,pz,rnd,i); - if (i && (i%kUPDATE) == 0) { - if (i == kUPDATE) hpx->Draw(); - c1->Modified(); - c1->Update(); - if (gSystem->ProcessEvents()) - break; - } - } - gBenchmark->Show("hsimple"); - - // Save all objects in this file - hpx->SetFillColor(0); - hfile->Write(); - hpx->SetFillColor(48); - c1->Modified(); - return hfile; - -// Note that the file is automatically close when application terminates -// or when the file destructor is called. -} diff --git a/pypy/module/_cppyy/bench/hsimple.py b/pypy/module/_cppyy/bench/hsimple.py deleted file mode 100755 --- a/pypy/module/_cppyy/bench/hsimple.py +++ /dev/null @@ -1,110 +0,0 @@ -#*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* -#*-* -#*-* This program creates : -#*-* - a one dimensional histogram -#*-* - a two dimensional histogram -#*-* - a profile histogram -#*-* - a memory-resident ntuple -#*-* -#*-* These objects are filled with some random numbers and saved on a file. -#*-* -#*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* - -_reflex = True # to keep things equal, set to False for full macro - -try: - import cppyy, random - - if not hasattr(cppyy.gbl, 'gROOT'): - cppyy.load_reflection_info('bench02Dict_reflex.so') - _reflex = True - - TCanvas = cppyy.gbl.TCanvas - TFile = cppyy.gbl.TFile - TProfile = cppyy.gbl.TProfile - TNtuple = cppyy.gbl.TNtuple - TH1F = cppyy.gbl.TH1F - TH2F = cppyy.gbl.TH2F - TRandom3 = cppyy.gbl.TRandom3 - - gROOT = cppyy.gbl.gROOT - gBenchmark = cppyy.gbl.TBenchmark() - gSystem = cppyy.gbl.gSystem - -except ImportError: - from ROOT import TCanvas, TFile, TProfile, TNtuple, TH1F, TH2F, TRandom3 - from ROOT import gROOT, gBenchmark, gSystem - import random - -if _reflex: - gROOT.SetBatch(True) - -# Create a new ROOT binary machine independent file. -# Note that this file may contain any kind of ROOT objects, histograms, -# pictures, graphics objects, detector geometries, tracks, events, etc.. -# This file is now becoming the current directory. - -if not _reflex: - hfile = gROOT.FindObject('hsimple.root') - if hfile: - hfile.Close() - hfile = TFile('hsimple.root', 'RECREATE', 'Demo ROOT file with histograms' ) - -# Create some histograms, a profile histogram and an ntuple -hpx = TH1F('hpx', 'This is the px distribution', 100, -4, 4) -hpx.SetFillColor(48) -hpxpy = TH2F('hpxpy', 'py vs px', 40, -4, 4, 40, -4, 4) -hprof = TProfile('hprof', 'Profile of pz versus px', 100, -4, 4, 0, 20) -if not _reflex: - ntuple = TNtuple('ntuple', 'Demo ntuple', 'px:py:pz:random:i') - -gBenchmark.Start('hsimple') - -# Create a new canvas, and customize it. -c1 = TCanvas('c1', 'Dynamic Filling Example', 200, 10, 700, 500) -c1.SetFillColor(42) -c1.GetFrame().SetFillColor(21) -c1.GetFrame().SetBorderSize(6) -c1.GetFrame().SetBorderMode(-1) - -# Fill histograms randomly. -random = TRandom3() -kUPDATE = 1000 -for i in xrange(50000): - # Generate random numbers -# px, py = random.gauss(0, 1), random.gauss(0, 1) - px, py = random.Gaus(0, 1), random.Gaus(0, 1) - pz = px*px + py*py -# rnd = random.random() - rnd = random.Rndm(1) - - # Fill histograms - hpx.Fill(px) - hpxpy.Fill(px, py) - hprof.Fill(px, pz) - if not _reflex: - ntuple.Fill(px, py, pz, rnd, i) - - # Update display every kUPDATE events - if i and i%kUPDATE == 0: - if i == kUPDATE: - hpx.Draw() - - c1.Modified(True) - c1.Update() - - if gSystem.ProcessEvents(): # allow user interrupt - break - -gBenchmark.Show( 'hsimple' ) - -# Save all objects in this file -hpx.SetFillColor(0) -if not _reflex: - hfile.Write() -hpx.SetFillColor(48) -c1.Modified(True) -c1.Update() - -# Note that the file is automatically closed when application terminates -# or when the file destructor is called. diff --git a/pypy/module/_cppyy/bench/hsimple_rflx.py b/pypy/module/_cppyy/bench/hsimple_rflx.py deleted file mode 100755 --- a/pypy/module/_cppyy/bench/hsimple_rflx.py +++ /dev/null @@ -1,120 +0,0 @@ -#*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* -#*-* -#*-* This program creates : -#*-* - a one dimensional histogram -#*-* - a two dimensional histogram -#*-* - a profile histogram -#*-* - a memory-resident ntuple -#*-* -#*-* These objects are filled with some random numbers and saved on a file. -#*-* -#*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* - -try: - import warnings - warnings.simplefilter("ignore") - - import cppyy, random - cppyy.load_reflection_info('bench02Dict_reflex.so') - - app = cppyy.gbl.Bench02RootApp() - TCanvas = cppyy.gbl.TCanvas - TFile = cppyy.gbl.TFile - TProfile = cppyy.gbl.TProfile - TNtuple = cppyy.gbl.TNtuple - TH1F = cppyy.gbl.TH1F - TH2F = cppyy.gbl.TH2F - TRandom = cppyy.gbl.TRandom -except ImportError: - from ROOT import TCanvas, TFile, TProfile, TNtuple, TH1F, TH2F, TRandom - import random - -import math - -#gROOT = cppyy.gbl.gROOT -#gBenchmark = cppyy.gbl.gBenchmark -#gRandom = cppyy.gbl.gRandom -#gSystem = cppyy.gbl.gSystem - -#gROOT.Reset() - -# Create a new canvas, and customize it. -#c1 = TCanvas( 'c1', 'Dynamic Filling Example', 200, 10, 700, 500 ) -#c1.SetFillColor( 42 ) -#c1.GetFrame().SetFillColor( 21 ) -#c1.GetFrame().SetBorderSize( 6 ) -#c1.GetFrame().SetBorderMode( -1 ) - -# Create a new ROOT binary machine independent file. -# Note that this file may contain any kind of ROOT objects, histograms, -# pictures, graphics objects, detector geometries, tracks, events, etc.. -# This file is now becoming the current directory. - -#hfile = gROOT.FindObject( 'hsimple.root' ) -#if hfile: -# hfile.Close() -#hfile = TFile( 'hsimple.root', 'RECREATE', 'Demo ROOT file with histograms' ) - -# Create some histograms, a profile histogram and an ntuple -hpx = TH1F('hpx', 'This is the px distribution', 100, -4, 4) -hpx.Print() -#hpxpy = TH2F( 'hpxpy', 'py vs px', 40, -4, 4, 40, -4, 4 ) -#hprof = TProfile( 'hprof', 'Profile of pz versus px', 100, -4, 4, 0, 20 ) -#ntuple = TNtuple( 'ntuple', 'Demo ntuple', 'px:py:pz:random:i' ) - -# Set canvas/frame attributes. -#hpx.SetFillColor( 48 ) - -#gBenchmark.Start( 'hsimple' ) - -# Initialize random number generator. -#gRandom.SetSeed() -#rannor, rndm = gRandom.Rannor, gRandom.Rndm - -random = TRandom() -random.SetSeed(0) - -# Fill histograms randomly. -#px, py = Double(), Double() -kUPDATE = 1000 -for i in xrange(2500000): - # Generate random values. -# px, py = random.gauss(0, 1), random.gauss(0, 1) - px, py = random.Gaus(0, 1), random.Gaus(0, 1) -# pt = (px*px + py*py)**0.5 - pt = math.sqrt(px*px + py*py) -# pt = (px*px + py*py) -# random = rndm(1) - - # Fill histograms. - hpx.Fill(pt) -# hpxpyFill( px, py ) -# hprofFill( px, pz ) -# ntupleFill( px, py, pz, random, i ) - - # Update display every kUPDATE events. -# if i and i%kUPDATE == 0: -# if i == kUPDATE: -# hpx.Draw() - -# c1.Modified() -# c1.Update() - -# if gSystem.ProcessEvents(): # allow user interrupt -# break - -#gBenchmark.Show( 'hsimple' ) - -hpx.Print() - -# Save all objects in this file. -#hpx.SetFillColor( 0 ) -#hfile.Write() -#hfile.Close() -#hpx.SetFillColor( 48 ) -#c1.Modified() -#c1.Update() -#c1.Draw() - -# Note that the file is automatically closed when application terminates -# or when the file destructor is called. diff --git a/pypy/module/_cppyy/capi/__init__.py b/pypy/module/_cppyy/capi/__init__.py --- a/pypy/module/_cppyy/capi/__init__.py +++ b/pypy/module/_cppyy/capi/__init__.py @@ -1,16 +1,6 @@ from rpython.rtyper.lltypesystem import rffi, lltype -# There are two possible ways of accessing the backend through the reflection -# C-API: built it into pypy-c, or load it dynamically. The latter is preferred -# (and is the default) for use with Reflex. B/c of some builtin pythonizations, -# the former is recommended (for now) with CINT. - -# Note: if builtin_capi is chosen, then inside builtin_capi.py, there is still -# the selection of the desired backend (default is Reflex). - -# choose C-API access method: from pypy.module._cppyy.capi.loadable_capi import * -#from pypy.module._cppyy.capi.builtin_capi import * from pypy.module._cppyy.capi.capi_types import C_OBJECT,\ C_NULL_TYPE, C_NULL_OBJECT diff --git a/pypy/module/_cppyy/capi/builtin_capi.py b/pypy/module/_cppyy/capi/builtin_capi.py deleted file mode 100644 --- a/pypy/module/_cppyy/capi/builtin_capi.py +++ /dev/null @@ -1,590 +0,0 @@ -from rpython.rtyper.lltypesystem import rffi, lltype -from rpython.rlib.rarithmetic import intmask -from rpython.rlib import jit - -import cling_capi as backend - -from pypy.module._cppyy.capi.capi_types import C_SCOPE, C_TYPE, C_OBJECT,\ - C_METHOD, C_INDEX, C_INDEX_ARRAY, WLAVC_INDEX, C_FUNC_PTR - -identify = backend.identify -pythonize = backend.pythonize -register_pythonizations = backend.register_pythonizations -std_string_name = backend.std_string_name - -ts_reflect = backend.ts_reflect -ts_call = backend.ts_call -ts_memory = backend.ts_memory -ts_helper = backend.ts_helper - -def verify_backend(space): - return True # by definition - -c_load_dictionary = backend.c_load_dictionary - -# name to opaque C++ scope representation ------------------------------------ -_c_num_scopes = rffi.llexternal( - "cppyy_num_scopes", - [C_SCOPE], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_num_scopes(space, cppscope): - return _c_num_scopes(cppscope.handle) -_c_scope_name = rffi.llexternal( - "cppyy_scope_name", - [C_SCOPE, rffi.INT], rffi.CCHARP, - compilation_info = backend.eci) -def c_scope_name(space, cppscope, iscope): - return charp2str_free(space, _c_scope_name(cppscope.handle, iscope)) - -_c_resolve_name = rffi.llexternal( - "cppyy_resolve_name", - [rffi.CCHARP], rffi.CCHARP, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_resolve_name(space, name): - return charp2str_free(space, _c_resolve_name(name)) -_c_get_scope_opaque = rffi.llexternal( - "cppyy_get_scope", - [rffi.CCHARP], C_SCOPE, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_get_scope_opaque(space, name): - return _c_get_scope_opaque(name) -_c_actual_class = rffi.llexternal( - "cppyy_actual_class", - [C_TYPE, C_OBJECT], C_TYPE, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_actual_class(space, cppclass, cppobj): - return _c_actual_class(cppclass.handle, cppobj) - -# memory management ---------------------------------------------------------- -_c_allocate = rffi.llexternal( - "cppyy_allocate", - [C_TYPE], C_OBJECT, - releasegil=ts_memory, - compilation_info=backend.eci) -def c_allocate(space, cppclass): - return _c_allocate(cppclass.handle) -_c_deallocate = rffi.llexternal( - "cppyy_deallocate", - [C_TYPE, C_OBJECT], lltype.Void, - releasegil=ts_memory, - compilation_info=backend.eci) -def c_deallocate(space, cppclass, cppobject): - _c_deallocate(cppclass.handle, cppobject) -_c_destruct = rffi.llexternal( - "cppyy_destruct", - [C_TYPE, C_OBJECT], lltype.Void, - releasegil=ts_call, - compilation_info=backend.eci) -def c_destruct(space, cppclass, cppobject): - _c_destruct(cppclass.handle, cppobject) - -# method/function dispatching ------------------------------------------------ -_c_call_v = rffi.llexternal( - "cppyy_call_v", - [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP], lltype.Void, - releasegil=ts_call, - compilation_info=backend.eci) -def c_call_v(space, cppmethod, cppobject, nargs, args): - _c_call_v(cppmethod, cppobject, nargs, args) -_c_call_b = rffi.llexternal( - "cppyy_call_b", - [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP], rffi.UCHAR, - releasegil=ts_call, - compilation_info=backend.eci) -def c_call_b(space, cppmethod, cppobject, nargs, args): - return _c_call_b(cppmethod, cppobject, nargs, args) -_c_call_c = rffi.llexternal( - "cppyy_call_c", - [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP], rffi.CHAR, - releasegil=ts_call, - compilation_info=backend.eci) -def c_call_c(space, cppmethod, cppobject, nargs, args): - return _c_call_c(cppmethod, cppobject, nargs, args) -_c_call_h = rffi.llexternal( - "cppyy_call_h", - [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP], rffi.SHORT, - releasegil=ts_call, - compilation_info=backend.eci) -def c_call_h(space, cppmethod, cppobject, nargs, args): - return _c_call_h(cppmethod, cppobject, nargs, args) -_c_call_i = rffi.llexternal( - "cppyy_call_i", - [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP], rffi.INT, - releasegil=ts_call, - compilation_info=backend.eci) -def c_call_i(space, cppmethod, cppobject, nargs, args): - return _c_call_i(cppmethod, cppobject, nargs, args) -_c_call_l = rffi.llexternal( - "cppyy_call_l", - [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP], rffi.LONG, - releasegil=ts_call, - compilation_info=backend.eci) -def c_call_l(space, cppmethod, cppobject, nargs, args): - return _c_call_l(cppmethod, cppobject, nargs, args) -_c_call_ll = rffi.llexternal( - "cppyy_call_ll", - [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP], rffi.LONGLONG, - releasegil=ts_call, - compilation_info=backend.eci) -def c_call_ll(space, cppmethod, cppobject, nargs, args): - return _c_call_ll(cppmethod, cppobject, nargs, args) -_c_call_f = rffi.llexternal( - "cppyy_call_f", - [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP], rffi.FLOAT, - releasegil=ts_call, - compilation_info=backend.eci) -def c_call_f(space, cppmethod, cppobject, nargs, args): - return _c_call_f(cppmethod, cppobject, nargs, args) -_c_call_d = rffi.llexternal( - "cppyy_call_d", - [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP], rffi.DOUBLE, - releasegil=ts_call, - compilation_info=backend.eci) -def c_call_d(space, cppmethod, cppobject, nargs, args): - return _c_call_d(cppmethod, cppobject, nargs, args) -_c_call_ld = rffi.llexternal( - "cppyy_call_ld", - [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP], rffi.LONGDOUBLE, - releasegil=ts_call, - compilation_info=backend.eci) -def c_call_ld(space, cppmethod, cppobject, nargs, args): - return _c_call_ld(cppmethod, cppobject, nargs, args) - -_c_call_r = rffi.llexternal( - "cppyy_call_r", - [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP], rffi.VOIDP, - releasegil=ts_call, - compilation_info=backend.eci) -def c_call_r(space, cppmethod, cppobject, nargs, args): - return _c_call_r(cppmethod, cppobject, nargs, args) -_c_call_s = rffi.llexternal( - "cppyy_call_s", - [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP, rffi.SIZE_TP], rffi.CCHARP, - releasegil=ts_call, - compilation_info=backend.eci) -def c_call_s(space, cppmethod, cppobject, nargs, args): - length = lltype.malloc(rffi.SIZE_TP.TO, 1, flavor='raw') - try: - cstr = _c_call_s(cppmethod, cppobject, nargs, args, length) - cstr_len = intmask(length[0]) - finally: - lltype.free(length, flavor='raw') - return cstr, cstr_len - -_c_constructor = rffi.llexternal( - "cppyy_constructor", - [C_METHOD, C_TYPE, rffi.INT, rffi.VOIDP], C_OBJECT, - releasegil=ts_call, - compilation_info=backend.eci) -def c_constructor(space, cppmethod, cppobject, nargs, args): - return _c_constructor(cppmethod, cppobject, nargs, args) -_c_call_o = rffi.llexternal( - "cppyy_call_o", - [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP, C_TYPE], rffi.LONG, - releasegil=ts_call, - compilation_info=backend.eci) -def c_call_o(space, method, cppobj, nargs, args, cppclass): - return _c_call_o(method, cppobj, nargs, args, cppclass.handle) - -_c_get_function_address = rffi.llexternal( - "cppyy_get_function_address", - [C_SCOPE, C_INDEX], C_FUNC_PTR, - releasegil=ts_reflect, - compilation_info=backend.eci, - random_effects_on_gcobjs=False) -def c_get_function_address(space, cppscope, index): - return _c_get_function_address(cppscope.handle, index) - -# handling of function argument buffer --------------------------------------- -_c_allocate_function_args = rffi.llexternal( - "cppyy_allocate_function_args", - [rffi.SIZE_T], rffi.VOIDP, - releasegil=ts_memory, - compilation_info=backend.eci) -def c_allocate_function_args(space, size): - return _c_allocate_function_args(size) -_c_deallocate_function_args = rffi.llexternal( - "cppyy_deallocate_function_args", - [rffi.VOIDP], lltype.Void, - releasegil=ts_memory, - compilation_info=backend.eci) -def c_deallocate_function_args(space, args): - _c_deallocate_function_args(args) -_c_function_arg_sizeof = rffi.llexternal( - "cppyy_function_arg_sizeof", - [], rffi.SIZE_T, - releasegil=ts_memory, - compilation_info=backend.eci, - random_effects_on_gcobjs=False) - at jit.elidable -def c_function_arg_sizeof(space): - return _c_function_arg_sizeof() -_c_function_arg_typeoffset = rffi.llexternal( - "cppyy_function_arg_typeoffset", - [], rffi.SIZE_T, - releasegil=ts_memory, - compilation_info=backend.eci, - random_effects_on_gcobjs=False) - at jit.elidable -def c_function_arg_typeoffset(space): - return _c_function_arg_typeoffset() - -# scope reflection information ----------------------------------------------- -_c_is_namespace = rffi.llexternal( - "cppyy_is_namespace", - [C_SCOPE], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_is_namespace(space, scope): - return _c_is_namespace(scope) -_c_is_template = rffi.llexternal( - "cppyy_is_template", - [rffi.CCHARP], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_is_template(space, name): - return _c_is_template(name) -_c_is_abstract = rffi.llexternal( - "cppyy_is_abstract", - [C_SCOPE], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_is_abstract(space, cpptype): - return _c_is_abstract(cpptype) -_c_is_enum = rffi.llexternal( - "cppyy_is_enum", - [rffi.CCHARP], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_is_enum(space, name): - return _c_is_enum(name) - -# type/class reflection information ------------------------------------------ -_c_final_name = rffi.llexternal( - "cppyy_final_name", - [C_TYPE], rffi.CCHARP, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_final_name(space, cpptype): - return charp2str_free(space, _c_final_name(cpptype)) -_c_scoped_final_name = rffi.llexternal( - "cppyy_scoped_final_name", - [C_TYPE], rffi.CCHARP, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_scoped_final_name(space, cpptype): - return charp2str_free(space, _c_scoped_final_name(cpptype)) -_c_has_complex_hierarchy = rffi.llexternal( - "cppyy_has_complex_hierarchy", - [C_TYPE], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_has_complex_hierarchy(space, cpptype): - return _c_has_complex_hierarchy(cpptype) -_c_num_bases = rffi.llexternal( - "cppyy_num_bases", - [C_TYPE], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_num_bases(space, cppclass): - return _c_num_bases(cppclass.handle) -_c_base_name = rffi.llexternal( - "cppyy_base_name", - [C_TYPE, rffi.INT], rffi.CCHARP, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_base_name(space, cppclass, base_index): - return charp2str_free(space, _c_base_name(cppclass.handle, base_index)) -_c_is_subtype = rffi.llexternal( - "cppyy_is_subtype", - [C_TYPE, C_TYPE], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci, - random_effects_on_gcobjs=False) - at jit.elidable -def c_is_subtype(space, derived, base): - if derived == base: - return 1 - return _c_is_subtype(derived.handle, base.handle) - -_c_base_offset = rffi.llexternal( - "cppyy_base_offset", - [C_TYPE, C_TYPE, C_OBJECT, rffi.INT], rffi.LONG, # actually ptrdiff_t - releasegil=ts_reflect, - compilation_info=backend.eci, - random_effects_on_gcobjs=False) - at jit.elidable -def c_base_offset(space, derived, base, address, direction): - if derived == base: - return 0 - return _c_base_offset(derived.handle, base.handle, address, direction) -def c_base_offset1(space, derived_h, base, address, direction): - return _c_base_offset(derived_h, base.handle, address, direction) - -# method/function reflection information ------------------------------------- -_c_num_methods = rffi.llexternal( - "cppyy_num_methods", - [C_SCOPE], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_num_methods(space, cppscope): - return _c_num_methods(cppscope.handle) -_c_method_index_at = rffi.llexternal( - "cppyy_method_index_at", - [C_SCOPE, rffi.INT], C_INDEX, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_method_index_at(space, cppscope, imethod): - return _c_method_index_at(cppscope.handle, imethod) -_c_method_indices_from_name = rffi.llexternal( - "cppyy_method_indices_from_name", - [C_SCOPE, rffi.CCHARP], C_INDEX_ARRAY, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_method_indices_from_name(space, cppscope, name): - indices = _c_method_indices_from_name(cppscope.handle, name) - if not indices: - return [] - py_indices = [] - i = 0 - index = indices[i] - while index != -1: - i += 1 - py_indices.append(index) - index = indices[i] - c_free(space, rffi.cast(rffi.VOIDP, indices)) # c_free defined below - return py_indices - -_c_method_name = rffi.llexternal( - "cppyy_method_name", - [C_SCOPE, C_INDEX], rffi.CCHARP, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_method_name(space, cppscope, index): - return charp2str_free(space, _c_method_name(cppscope.handle, index)) -_c_method_result_type = rffi.llexternal( - "cppyy_method_result_type", - [C_SCOPE, C_INDEX], rffi.CCHARP, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_method_result_type(space, cppscope, index): - return charp2str_free(space, _c_method_result_type(cppscope.handle, index)) -_c_method_num_args = rffi.llexternal( - "cppyy_method_num_args", - [C_SCOPE, C_INDEX], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_method_num_args(space, cppscope, index): - return _c_method_num_args(cppscope.handle, index) -_c_method_req_args = rffi.llexternal( - "cppyy_method_req_args", - [C_SCOPE, C_INDEX], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_method_req_args(space, cppscope, index): - return _c_method_req_args(cppscope.handle, index) -_c_method_arg_type = rffi.llexternal( - "cppyy_method_arg_type", - [C_SCOPE, C_INDEX, rffi.INT], rffi.CCHARP, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_method_arg_type(space, cppscope, index, arg_index): - return charp2str_free(space, _c_method_arg_type(cppscope.handle, index, arg_index)) -_c_method_arg_default = rffi.llexternal( - "cppyy_method_arg_default", - [C_SCOPE, C_INDEX, rffi.INT], rffi.CCHARP, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_method_arg_default(space, cppscope, index, arg_index): - return charp2str_free(space, _c_method_arg_default(cppscope.handle, index, arg_index)) -_c_method_signature = rffi.llexternal( - "cppyy_method_signature", - [C_SCOPE, C_INDEX], rffi.CCHARP, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_method_signature(space, cppscope, index): - return charp2str_free(space, _c_method_signature(cppscope.handle, index)) - -_c_method_is_template = rffi.llexternal( - "cppyy_method_is_template", - [C_SCOPE, C_INDEX], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_method_is_template(space, cppscope, index): - return _c_method_is_template(cppscope.handle, index) -_c_method_num_template_args = rffi.llexternal( - "cppyy_method_num_template_args", - [C_SCOPE, C_INDEX], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -_c_method_template_arg_name = rffi.llexternal( - "cppyy_method_template_arg_name", - [C_SCOPE, C_INDEX, C_INDEX], rffi.CCHARP, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_template_args(space, cppscope, index): - nargs = _c_method_num_template_args(cppscope.handle, index) - args = [c_resolve_name(space, - charp2str_free(space, _c_method_template_arg_name(cppscope.handle, index, iarg))) - for iarg in range(nargs)] - return args - -_c_get_method = rffi.llexternal( - "cppyy_get_method", - [C_SCOPE, C_INDEX], C_METHOD, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_get_method(space, cppscope, index): - return _c_get_method(cppscope.handle, index) -_c_get_global_operator = rffi.llexternal( - "cppyy_get_global_operator", - [C_SCOPE, C_SCOPE, C_SCOPE, rffi.CCHARP], WLAVC_INDEX, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_get_global_operator(space, nss, lc, rc, op): - if nss is not None: - return _c_get_global_operator(nss.handle, lc.handle, rc.handle, op) - return rffi.cast(WLAVC_INDEX, -1) - -# method properties ---------------------------------------------------------- -_c_is_constructor = rffi.llexternal( - "cppyy_is_constructor", - [C_TYPE, C_INDEX], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_is_constructor(space, cppclass, index): - return _c_is_constructor(cppclass.handle, index) -_c_is_staticmethod = rffi.llexternal( - "cppyy_is_staticmethod", - [C_TYPE, C_INDEX], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_is_staticmethod(space, cppclass, index): - return _c_is_staticmethod(cppclass.handle, index) - -# data member reflection information ----------------------------------------- -_c_num_datamembers = rffi.llexternal( - "cppyy_num_datamembers", - [C_SCOPE], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_num_datamembers(space, cppscope): - return _c_num_datamembers(cppscope.handle) -_c_datamember_name = rffi.llexternal( - "cppyy_datamember_name", - [C_SCOPE, rffi.INT], rffi.CCHARP, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_datamember_name(space, cppscope, datamember_index): - return charp2str_free(space, _c_datamember_name(cppscope.handle, datamember_index)) -_c_datamember_type = rffi.llexternal( - "cppyy_datamember_type", - [C_SCOPE, rffi.INT], rffi.CCHARP, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_datamember_type(space, cppscope, datamember_index): - return charp2str_free(space, _c_datamember_type(cppscope.handle, datamember_index)) -_c_datamember_offset = rffi.llexternal( - "cppyy_datamember_offset", - [C_SCOPE, rffi.INT], rffi.LONG, # actually ptrdiff_t - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_datamember_offset(space, cppscope, datamember_index): - return _c_datamember_offset(cppscope.handle, datamember_index) - -_c_datamember_index = rffi.llexternal( - "cppyy_datamember_index", - [C_SCOPE, rffi.CCHARP], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_datamember_index(space, cppscope, name): - return _c_datamember_index(cppscope.handle, name) - -# data member properties ----------------------------------------------------- -_c_is_publicdata = rffi.llexternal( - "cppyy_is_publicdata", - [C_SCOPE, rffi.INT], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_is_publicdata(space, cppscope, datamember_index): - return _c_is_publicdata(cppscope.handle, datamember_index) -_c_is_staticdata = rffi.llexternal( - "cppyy_is_staticdata", - [C_SCOPE, rffi.INT], rffi.INT, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_is_staticdata(space, cppscope, datamember_index): - return _c_is_staticdata(cppscope.handle, datamember_index) - -# misc helpers --------------------------------------------------------------- -_c_strtoll = rffi.llexternal( - "cppyy_strtoll", - [rffi.CCHARP], rffi.LONGLONG, - releasegil=ts_helper, - compilation_info=backend.eci) -def c_strtoll(space, svalue): - return _c_strtoll(svalue) -_c_strtoull = rffi.llexternal( - "cppyy_strtoull", - [rffi.CCHARP], rffi.ULONGLONG, - releasegil=ts_helper, - compilation_info=backend.eci) -def c_strtoull(space, svalue): - return _c_strtoull(svalue) -_c_free = rffi.llexternal( - "cppyy_free", - [rffi.VOIDP], lltype.Void, - releasegil=ts_memory, - compilation_info=backend.eci) -def c_free(space, voidp): - return _c_free(voidp) - -def charp2str_free(space, charp): - string = rffi.charp2str(charp) - voidp = rffi.cast(rffi.VOIDP, charp) - _c_free(voidp) - return string - -_c_charp2stdstring = rffi.llexternal( - "cppyy_charp2stdstring", - [rffi.CCHARP, rffi.SIZE_T], C_OBJECT, - releasegil=ts_helper, - compilation_info=backend.eci) -def c_charp2stdstring(space, pystr, sz): - with rffi.scoped_view_charp(pystr) as cstr: - cppstr = _c_charp2stdstring(cstr, sz) - return cppstr -_c_stdstring2stdstring = rffi.llexternal( - "cppyy_stdstring2stdstring", - [C_OBJECT], C_OBJECT, - releasegil=ts_helper, - compilation_info=backend.eci) -def c_stdstring2stdstring(space, cppobject): - return _c_stdstring2stdstring(cppobject) - -_c_stdvector_valuetype = rffi.llexternal( - "cppyy_stdvector_valuetype", - [rffi.CCHARP], rffi.CCHARP, - releasegil=ts_helper, - compilation_info=backend.eci) -def c_stdvector_valuetype(space, pystr): - cstr = rffi.str2charp(pystr) - result = _c_stdvector_valuetype(cstr) - rffi.free_charp(cstr) - if result: - return charp2str_free(space, result) - return "" -_c_stdvector_valuesize = rffi.llexternal( - "cppyy_stdvector_valuesize", - [rffi.CCHARP], rffi.SIZE_T, - releasegil=ts_helper, - compilation_info=backend.eci) -def c_stdvector_valuesize(space, pystr): - cstr = rffi.str2charp(pystr) - result = _c_stdvector_valuesize(cstr) - rffi.free_charp(cstr) - return result diff --git a/pypy/module/_cppyy/capi/cling_capi.py b/pypy/module/_cppyy/capi/cling_capi.py deleted file mode 100644 --- a/pypy/module/_cppyy/capi/cling_capi.py +++ /dev/null @@ -1,196 +0,0 @@ -import py, os - -from pypy.objspace.std.iterobject import W_AbstractSeqIterObject - -from pypy.interpreter.error import OperationError -from pypy.interpreter.gateway import interp2app - -from rpython.translator.tool.cbuild import ExternalCompilationInfo -from rpython.rtyper.lltypesystem import rffi, lltype -from rpython.rlib.rarithmetic import intmask -from rpython.rlib import jit, libffi, rdynload - -from pypy.module._rawffi.array import W_ArrayInstance -from pypy.module._cppyy.capi.capi_types import C_OBJECT - -__all__ = ['identify', 'std_string_name', 'eci', 'c_load_dictionary'] - -pkgpath = py.path.local(__file__).dirpath().join(os.pardir) -srcpath = pkgpath.join("src") -incpath = pkgpath.join("include") - -import commands -(config_stat, incdir) = commands.getstatusoutput("root-config --incdir") - -if os.environ.get("ROOTSYS"): - if config_stat != 0: # presumably Reflex-only - rootincpath = [os.path.join(os.environ["ROOTSYS"], "interpreter/cling/include"), - os.path.join(os.environ["ROOTSYS"], "interpreter/llvm/inst/include"), - os.path.join(os.environ["ROOTSYS"], "include"),] - rootlibpath = [os.path.join(os.environ["ROOTSYS"], "lib64"), os.path.join(os.environ["ROOTSYS"], "lib")] - else: - rootincpath = [incdir] - rootlibpath = commands.getoutput("root-config --libdir").split() -else: - if config_stat == 0: - rootincpath = [incdir] - rootlibpath = commands.getoutput("root-config --libdir").split() - else: - rootincpath = [] - rootlibpath = [] - -def identify(): - return 'Cling' - -ts_reflect = False -ts_call = 'auto' -ts_memory = 'auto' -ts_helper = 'auto' - -std_string_name = 'std::basic_string' - -# force loading (and exposure) of libCore symbols -with rffi.scoped_str2charp('libCore.so') as ll_libname: - _coredll = rdynload.dlopen(ll_libname, rdynload.RTLD_GLOBAL | rdynload.RTLD_NOW) - -# require local translator path to pickup common defs -from rpython.translator import cdir -translator_c_dir = py.path.local(cdir) - -eci = ExternalCompilationInfo( - separate_module_files=[srcpath.join("clingcwrapper.cxx")], - include_dirs=[incpath, translator_c_dir] + rootincpath, - includes=["clingcwrapper.h"], - library_dirs=rootlibpath, - libraries=["Cling"], - compile_extra=["-fno-strict-aliasing", "-std=c++11"], - use_cpp_linker=True, -) - -_c_load_dictionary = rffi.llexternal( - "cppyy_load_dictionary", - [rffi.CCHARP], rdynload.DLLHANDLE, - releasegil=False, - compilation_info=eci) - -def c_load_dictionary(name): - pch = _c_load_dictionary(name) - return pch - -_c_stdstring2charp = rffi.llexternal( - "cppyy_stdstring2charp", - [C_OBJECT, rffi.SIZE_TP], rffi.CCHARP, - releasegil=ts_helper, - compilation_info=eci) -def c_stdstring2charp(space, cppstr): - sz = lltype.malloc(rffi.SIZE_TP.TO, 1, flavor='raw') - try: - cstr = _c_stdstring2charp(cppstr, sz) - cstr_len = intmask(sz[0]) - finally: - lltype.free(sz, flavor='raw') - return rffi.charpsize2str(cstr, cstr_len) - -# TODO: factor these out ... -# pythonizations - -# From pypy.commits at gmail.com Sat Oct 7 09:09:17 2017 From: pypy.commits at gmail.com (arigo) Date: Sat, 07 Oct 2017 06:09:17 -0700 (PDT) Subject: [pypy-commit] cffi release-1.11: hg merge default Message-ID: <59d8d1fd.1986df0a.ede72.a75c@mx.google.com> Author: Armin Rigo Branch: release-1.11 Changeset: r3037:e6f7f5693b3d Date: 2017-10-04 08:32 +0200 http://bitbucket.org/cffi/cffi/changeset/e6f7f5693b3d/ Log: hg merge default diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -1550,7 +1550,8 @@ /* for backward compatibility, accept "char *" as either source of target. This is not what C does, though, so emit a warning that will eventually turn into an - error. */ + error. The warning is turned off if both types are + pointers to single bytes. */ char *msg = (ct->ct_flags & CT_IS_VOIDCHAR_PTR ? "implicit cast to 'char *' from a different pointer type: " "will be forbidden in the future (check that the types " @@ -1560,7 +1561,12 @@ "will be forbidden in the future (check that the types " "are as you expect; use an explicit ffi.cast() if they " "are correct)"); - if (PyErr_WarnEx(PyExc_UserWarning, msg, 1)) + if ((ct->ct_flags & ctinit->ct_flags & CT_POINTER) && + ct->ct_itemdescr->ct_size == 1 && + ctinit->ct_itemdescr->ct_size == 1) { + /* no warning */ + } + else if (PyErr_WarnEx(PyExc_UserWarning, msg, 1)) return -1; } else { diff --git a/c/test_c.py b/c/test_c.py --- a/c/test_c.py +++ b/c/test_c.py @@ -2110,7 +2110,8 @@ if sys.platform.startswith("linux"): BWChar = new_primitive_type("wchar_t") assert sizeof(BWChar) == 4 - assert int(cast(BWChar, -1)) == -1 # signed, on linux + # wchar_t is often signed on Linux, but not always (e.g. on ARM) + assert int(cast(BWChar, -1)) in (-1, 4294967295) def test_char16(): BChar16 = new_primitive_type("char16_t") @@ -3914,9 +3915,11 @@ BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BVoidP = new_pointer_type(new_void_type()) + BUCharP = new_pointer_type(new_primitive_type("unsigned char")) z1 = cast(BCharP, 0) z2 = cast(BIntP, 0) z3 = cast(BVoidP, 0) + z4 = cast(BUCharP, 0) with warnings.catch_warnings(record=True) as w: newp(new_pointer_type(BIntP), z1) # warn assert len(w) == 1 @@ -3930,6 +3933,12 @@ assert len(w) == 2 newp(new_pointer_type(BIntP), z3) # fine assert len(w) == 2 + newp(new_pointer_type(BCharP), z4) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z1) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z3) # fine + assert len(w) == 2 # check that the warnings are associated with lines in this file assert w[1].lineno == w[0].lineno + 4 diff --git a/cffi/_cffi_include.h b/cffi/_cffi_include.h --- a/cffi/_cffi_include.h +++ b/cffi/_cffi_include.h @@ -238,9 +238,9 @@ _CFFI_UNUSED_FN static PyObject *_cffi_from_c_char16_t(uint16_t x) { if (sizeof(_cffi_wchar_t) == 2) - return _cffi_from_c_wchar_t(x); + return _cffi_from_c_wchar_t((_cffi_wchar_t)x); else - return _cffi_from_c_wchar3216_t(x); + return _cffi_from_c_wchar3216_t((int)x); } _CFFI_UNUSED_FN static int _cffi_to_c_char32_t(PyObject *o) @@ -254,7 +254,7 @@ _CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(int x) { if (sizeof(_cffi_wchar_t) == 4) - return _cffi_from_c_wchar_t(x); + return _cffi_from_c_wchar_t((_cffi_wchar_t)x); else return _cffi_from_c_wchar3216_t(x); } From pypy.commits at gmail.com Sat Oct 7 09:09:19 2017 From: pypy.commits at gmail.com (arigo) Date: Sat, 07 Oct 2017 06:09:19 -0700 (PDT) Subject: [pypy-commit] cffi release-1.11: tweaks in whatsnew Message-ID: <59d8d1ff.f586df0a.c427c.ca96@mx.google.com> Author: Armin Rigo Branch: release-1.11 Changeset: r3038:3371c64306f0 Date: 2017-10-04 08:37 +0200 http://bitbucket.org/cffi/cffi/changeset/3371c64306f0/ Log: tweaks in whatsnew diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst --- a/doc/source/whatsnew.rst +++ b/doc/source/whatsnew.rst @@ -8,11 +8,11 @@ * Fix tests, remove deprecated C API usage -* Fix for 3.6.0/3.6.1 being incompatible with each other unless we hack +* Fix (hack) for 3.6.0/3.6.1/3.6.2 giving incompatible binary extensions (cpython issue `#29943`_) -* Fix for 3.7.0a1+ (it didn't run successfully in CPython's 3.7 branch) - +* Fix for 3.7.0a1+ + .. _`#29943`: https://bugs.python.org/issue29943 From pypy.commits at gmail.com Sat Oct 7 09:09:22 2017 From: pypy.commits at gmail.com (arigo) Date: Sat, 07 Oct 2017 06:09:22 -0700 (PDT) Subject: [pypy-commit] cffi release-1.11: more tweaks Message-ID: <59d8d202.8ba9df0a.92399.40ab@mx.google.com> Author: Armin Rigo Branch: release-1.11 Changeset: r3040:ce479abbcb9f Date: 2017-10-04 08:45 +0200 http://bitbucket.org/cffi/cffi/changeset/ce479abbcb9f/ Log: more tweaks diff --git a/doc/source/installation.rst b/doc/source/installation.rst --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -53,6 +53,8 @@ * https://pypi.python.org/pypi/cffi +* Checksums of the "source" package version 1.11.1: + - MD5: ... - SHA: ... diff --git a/testing/cffi0/test_version.py b/testing/cffi0/test_version.py --- a/testing/cffi0/test_version.py +++ b/testing/cffi0/test_version.py @@ -36,8 +36,7 @@ v = cffi.__version__.replace('+', '') p = os.path.join(parent, 'doc', 'source', 'installation.rst') content = open(p).read() - py.test.skip("the link location now points to the general pip page") - assert ("/cffi-%s.tar.gz" % v) in content + assert (" package version %s:" % v) in content def test_setup_version(): parent = os.path.dirname(os.path.dirname(cffi.__file__)) From pypy.commits at gmail.com Sat Oct 7 09:09:24 2017 From: pypy.commits at gmail.com (arigo) Date: Sat, 07 Oct 2017 06:09:24 -0700 (PDT) Subject: [pypy-commit] cffi release-1.11: Update md5/sha sums Message-ID: <59d8d204.90921c0a.3f8a8.35ba@mx.google.com> Author: Armin Rigo Branch: release-1.11 Changeset: r3041:ada126bd7d1e Date: 2017-10-04 09:15 +0200 http://bitbucket.org/cffi/cffi/changeset/ada126bd7d1e/ Log: Update md5/sha sums diff --git a/doc/source/installation.rst b/doc/source/installation.rst --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -55,11 +55,11 @@ * Checksums of the "source" package version 1.11.1: - - MD5: ... + - MD5: 89dfa1bb3b3e28eafb5da3e345a19f56 - - SHA: ... + - SHA: f3a61a3b53b482a7d05704344e0f055d4672fa01 - - SHA256: ... + - SHA256: 4c40817cc0f71b5351eb0bdd0b585db4a285c2bcc03fbcb961b79bb8086b7576 * Or grab the most current version from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` From pypy.commits at gmail.com Sat Oct 7 09:09:26 2017 From: pypy.commits at gmail.com (arigo) Date: Sat, 07 Oct 2017 06:09:26 -0700 (PDT) Subject: [pypy-commit] cffi release-1.11: Added tag v1.11.1 for changeset ada126bd7d1e Message-ID: <59d8d206.c7341c0a.2c97e.87c5@mx.google.com> Author: Armin Rigo Branch: release-1.11 Changeset: r3042:9602c54244d0 Date: 2017-10-04 09:15 +0200 http://bitbucket.org/cffi/cffi/changeset/9602c54244d0/ Log: Added tag v1.11.1 for changeset ada126bd7d1e diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -12,3 +12,4 @@ 0000000000000000000000000000000000000000 release-0.2 ca6e81df7f1ea58d891129ad016a8888c08f238b release-0.1 0000000000000000000000000000000000000000 release-0.1 +ada126bd7d1e96cc76303c1fca64a556912549d8 v1.11.1 From pypy.commits at gmail.com Sat Oct 7 09:09:21 2017 From: pypy.commits at gmail.com (arigo) Date: Sat, 07 Oct 2017 06:09:21 -0700 (PDT) Subject: [pypy-commit] cffi release-1.11: Skip a test: the link location now points to the general pip page, Message-ID: <59d8d201.8e861c0a.873f.cfb8@mx.google.com> Author: Armin Rigo Branch: release-1.11 Changeset: r3039:b0f01afb01b4 Date: 2017-10-04 08:40 +0200 http://bitbucket.org/cffi/cffi/changeset/b0f01afb01b4/ Log: Skip a test: the link location now points to the general pip page, not to the place where the exact source code can be downloaded, because I don't know this place before I actually do the upload. diff --git a/testing/cffi0/test_version.py b/testing/cffi0/test_version.py --- a/testing/cffi0/test_version.py +++ b/testing/cffi0/test_version.py @@ -36,6 +36,7 @@ v = cffi.__version__.replace('+', '') p = os.path.join(parent, 'doc', 'source', 'installation.rst') content = open(p).read() + py.test.skip("the link location now points to the general pip page") assert ("/cffi-%s.tar.gz" % v) in content def test_setup_version(): From pypy.commits at gmail.com Sat Oct 7 09:09:28 2017 From: pypy.commits at gmail.com (arigo) Date: Sat, 07 Oct 2017 06:09:28 -0700 (PDT) Subject: [pypy-commit] cffi default: hg merge release-1.11 Message-ID: <59d8d208.cd5e1c0a.120cc.c48a@mx.google.com> Author: Armin Rigo Branch: Changeset: r3043:77d89fb08efb Date: 2017-10-04 09:15 +0200 http://bitbucket.org/cffi/cffi/changeset/77d89fb08efb/ Log: hg merge release-1.11 diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -12,3 +12,4 @@ 0000000000000000000000000000000000000000 release-0.2 ca6e81df7f1ea58d891129ad016a8888c08f238b release-0.1 0000000000000000000000000000000000000000 release-0.1 +ada126bd7d1e96cc76303c1fca64a556912549d8 v1.11.1 diff --git a/doc/source/installation.rst b/doc/source/installation.rst --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -53,11 +53,13 @@ * https://pypi.python.org/pypi/cffi - - MD5: ... +* Checksums of the "source" package version 1.11.1: - - SHA: ... + - MD5: 89dfa1bb3b3e28eafb5da3e345a19f56 - - SHA256: ... + - SHA: f3a61a3b53b482a7d05704344e0f055d4672fa01 + + - SHA256: 4c40817cc0f71b5351eb0bdd0b585db4a285c2bcc03fbcb961b79bb8086b7576 * Or grab the most current version from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst --- a/doc/source/whatsnew.rst +++ b/doc/source/whatsnew.rst @@ -8,11 +8,11 @@ * Fix tests, remove deprecated C API usage -* Fix for 3.6.0/3.6.1 being incompatible with each other unless we hack +* Fix (hack) for 3.6.0/3.6.1/3.6.2 giving incompatible binary extensions (cpython issue `#29943`_) -* Fix for 3.7.0a1+ (it didn't run successfully in CPython's 3.7 branch) - +* Fix for 3.7.0a1+ + .. _`#29943`: https://bugs.python.org/issue29943 diff --git a/testing/cffi0/test_version.py b/testing/cffi0/test_version.py --- a/testing/cffi0/test_version.py +++ b/testing/cffi0/test_version.py @@ -36,7 +36,7 @@ v = cffi.__version__.replace('+', '') p = os.path.join(parent, 'doc', 'source', 'installation.rst') content = open(p).read() - assert ("/cffi-%s.tar.gz" % v) in content + assert (" package version %s:" % v) in content def test_setup_version(): parent = os.path.dirname(os.path.dirname(cffi.__file__)) From pypy.commits at gmail.com Sat Oct 7 09:09:31 2017 From: pypy.commits at gmail.com (mattip) Date: Sat, 07 Oct 2017 06:09:31 -0700 (PDT) Subject: [pypy-commit] pypy default: Added tag release-pypy2.7-v5.9.0 for changeset 03d614975835 Message-ID: <59d8d20b.7a86df0a.d3849.5e1b@mx.google.com> Author: Matti Picus Branch: Changeset: r92598:0f78f387b064 Date: 2017-10-04 18:27 +1100 http://bitbucket.org/pypy/pypy/changeset/0f78f387b064/ Log: Added tag release-pypy2.7-v5.9.0 for changeset 03d614975835 diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -40,3 +40,4 @@ 2875f328eae2216a87f3d6f335092832eb031f56 release-pypy3.5-v5.7.1 c925e73810367cd960a32592dd7f728f436c125c release-pypy2.7-v5.8.0 a37ecfe5f142bc971a86d17305cc5d1d70abec64 release-pypy3.5-v5.8.0 +03d614975835870da65ff0481e1edad68ebbcb8d release-pypy2.7-v5.9.0 From pypy.commits at gmail.com Sat Oct 7 09:09:33 2017 From: pypy.commits at gmail.com (mattip) Date: Sat, 07 Oct 2017 06:09:33 -0700 (PDT) Subject: [pypy-commit] pypy default: Added tag release-pypy3.5-v5.9.0 for changeset d72f9800a42b Message-ID: <59d8d20d.06aadf0a.b3ad8.cf53@mx.google.com> Author: Matti Picus Branch: Changeset: r92599:e25bb633a068 Date: 2017-10-04 18:27 +1100 http://bitbucket.org/pypy/pypy/changeset/e25bb633a068/ Log: Added tag release-pypy3.5-v5.9.0 for changeset d72f9800a42b diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -41,3 +41,4 @@ c925e73810367cd960a32592dd7f728f436c125c release-pypy2.7-v5.8.0 a37ecfe5f142bc971a86d17305cc5d1d70abec64 release-pypy3.5-v5.8.0 03d614975835870da65ff0481e1edad68ebbcb8d release-pypy2.7-v5.9.0 +d72f9800a42b46a8056951b1da2426d2c2d8d502 release-pypy3.5-v5.9.0 From pypy.commits at gmail.com Sat Oct 7 09:09:36 2017 From: pypy.commits at gmail.com (mattip) Date: Sat, 07 Oct 2017 06:09:36 -0700 (PDT) Subject: [pypy-commit] pypy default: Moved tag release-pypy2.7-v5.9.0 to changeset 84a2f3e6a7f8 (from changeset 03d614975835) Message-ID: <59d8d210.43581c0a.12a55.3254@mx.google.com> Author: Matti Picus Branch: Changeset: r92600:5a1232a1c2e6 Date: 2017-10-04 18:32 +1100 http://bitbucket.org/pypy/pypy/changeset/5a1232a1c2e6/ Log: Moved tag release-pypy2.7-v5.9.0 to changeset 84a2f3e6a7f8 (from changeset 03d614975835) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -42,3 +42,5 @@ a37ecfe5f142bc971a86d17305cc5d1d70abec64 release-pypy3.5-v5.8.0 03d614975835870da65ff0481e1edad68ebbcb8d release-pypy2.7-v5.9.0 d72f9800a42b46a8056951b1da2426d2c2d8d502 release-pypy3.5-v5.9.0 +03d614975835870da65ff0481e1edad68ebbcb8d release-pypy2.7-v5.9.0 +84a2f3e6a7f88f2fe698e473998755b3bd1a12e2 release-pypy2.7-v5.9.0 From pypy.commits at gmail.com Sat Oct 7 09:09:40 2017 From: pypy.commits at gmail.com (antocuni) Date: Sat, 07 Oct 2017 06:09:40 -0700 (PDT) Subject: [pypy-commit] extradoc extradoc: (antocuni, arigo, ronan): a picture of what we are seeing right now Message-ID: <59d8d214.c5a4df0a.6609c.2754@mx.google.com> Author: Antonio Cuni Branch: extradoc Changeset: r5832:2f9fbc4cb3d1 Date: 2017-10-04 16:00 +0200 http://bitbucket.org/pypy/extradoc/changeset/2f9fbc4cb3d1/ Log: (antocuni, arigo, ronan): a picture of what we are seeing right now diff --git a/sprintinfo/cape-town-2017/2017-10-04-155524.jpg b/sprintinfo/cape-town-2017/2017-10-04-155524.jpg new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4bbb25cda6773e5d1b87986645f036127a38bce6 GIT binary patch [cut] diff --git a/talk/ep2017/the-joy-of-pypy-jit/sobel/v5.py b/talk/ep2017/the-joy-of-pypy-jit/sobel/v5.py --- a/talk/ep2017/the-joy-of-pypy-jit/sobel/v5.py +++ b/talk/ep2017/the-joy-of-pypy-jit/sobel/v5.py @@ -19,13 +19,6 @@ code.compile() return code['apply'] -Gx = Kernel([[-1.0, 0.0, +1.0], - [-2.0, 0.0, +2.0], - [-1.0, 0.0, +1.0]]) - -Gy = Kernel([[-1.0, -2.0, -1.0], - [0.0, 0.0, 0.0], - [+1.0, +2.0, +1.0]]) def sobel(img): """ @@ -34,6 +27,14 @@ img = Image(*img) out = Image(img.width, img.height) for p in img.noborder(): + Gx = Kernel([[-1.0, 0.0, +1.0], + [-2.0, 0.0, +2.0], + [-1.0, 0.0, +1.0]]) + + Gy = Kernel([[-1.0, -2.0, -1.0], + [0.0, 0.0, 0.0], + [+1.0, +2.0, +1.0]]) + dx = Gx(img, p) dy = Gy(img, p) value = min(int(sqrt(dx*dx + dy*dy) / 2.0), 255) From pypy.commits at gmail.com Sat Oct 7 09:09:44 2017 From: pypy.commits at gmail.com (antocuni) Date: Sat, 07 Oct 2017 06:09:44 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: (antocuni, arigo, ronan): fix translation Message-ID: <59d8d218.23acdf0a.108fc.3296@mx.google.com> Author: Antonio Cuni Branch: cpyext-nowrapper Changeset: r92601:a4307fb5912e Date: 2017-10-04 16:03 +0200 http://bitbucket.org/pypy/pypy/changeset/a4307fb5912e/ Log: (antocuni, arigo, ronan): fix translation Directly from table mountain, cape town: https://bytebucket.org/pypy/extradoc/raw/extradoc/sprintinfo/cape- town-2017/2017-10-04-155524.jpg diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -295,6 +295,11 @@ def get_llhelper(space): return llhelper(self.functype, self.get_wrapper(space)) self.get_llhelper = get_llhelper + # + def get_raw_llhelper(): + assert self.no_gc, 'Please call get_llhelper' + return llhelper(self.functype, self.callable) + self.get_raw_llhelper = get_raw_llhelper def _freeze_(self): return True 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 @@ -548,7 +548,7 @@ def subtype_dealloc(obj): pto = obj.c_ob_type base = pto - this_func_ptr = ll_subtype_dealloc + this_func_ptr = subtype_dealloc.api_func.get_raw_llhelper() # This wrapper is created on a specific type, call it w_A. # We wish to call the dealloc function from one of the base classes of w_A, # the first of which is not this function itself. @@ -567,9 +567,6 @@ # hopefully this does not clash with the memory model assumed in # extension modules -# XXX -ll_subtype_dealloc = llhelper(subtype_dealloc.api_func.functype, - subtype_dealloc.api_func.callable) @slot_function([PyObject, Py_ssize_tP], lltype.Signed, error=CANNOT_FAIL) def bf_segcount(space, w_obj, ref): From pypy.commits at gmail.com Sat Oct 7 09:09:47 2017 From: pypy.commits at gmail.com (antocuni) Date: Sat, 07 Oct 2017 06:09:47 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: (antocuni, arigo, ronan): make PyObject_dealloc no_gc=True Message-ID: <59d8d21b.2f97df0a.7aa8d.7801@mx.google.com> Author: Antonio Cuni Branch: cpyext-nowrapper Changeset: r92602:e92d38869b79 Date: 2017-10-04 18:00 +0200 http://bitbucket.org/pypy/pypy/changeset/e92d38869b79/ Log: (antocuni, arigo, ronan): make PyObject_dealloc no_gc=True diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py --- a/pypy/module/cpyext/object.py +++ b/pypy/module/cpyext/object.py @@ -54,20 +54,21 @@ w_obj = PyObject_InitVar(space, py_objvar, type, itemcount) return py_obj - at slot_function([PyObject], lltype.Void) -def PyObject_dealloc(space, obj): - return _dealloc(space, obj) - -def _dealloc(space, obj): + at slot_function([PyObject], lltype.Void, no_gc=True) +def PyObject_dealloc(obj): # This frees an object after its refcount dropped to zero, so we # assert that it is really zero here. assert obj.c_ob_refcnt == 0 pto = obj.c_ob_type obj_voidp = rffi.cast(rffi.VOIDP, obj) - generic_cpy_call(space, pto.c_tp_free, obj_voidp) + pto.c_tp_free(obj_voidp) if pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE: decref(rffi.cast(PyObject, pto)) +def _dealloc(space, obj): + ll_PyObject_dealloc = PyObject_dealloc.api_func.get_raw_llhelper() + return generic_cpy_call(space, ll_PyObject_dealloc, obj) + @cpython_api([PyTypeObjectPtr], PyObject, result_is_ll=True) def _PyObject_GC_New(space, type): return _PyObject_New(space, type) From pypy.commits at gmail.com Sat Oct 7 09:09:51 2017 From: pypy.commits at gmail.com (arigo) Date: Sat, 07 Oct 2017 06:09:51 -0700 (PDT) Subject: [pypy-commit] extradoc extradoc: PyConZA 2017 talk, mostly copied from ep2017 Message-ID: <59d8d21f.dc4a1c0a.4a601.5fc6@mx.google.com> Author: Armin Rigo Branch: extradoc Changeset: r5833:b1be3972614e Date: 2017-10-05 07:55 +0200 http://bitbucket.org/pypy/extradoc/changeset/b1be3972614e/ Log: PyConZA 2017 talk, mostly copied from ep2017 diff --git a/talk/pyconza2017/pypy-whats-new/Makefile b/talk/pyconza2017/pypy-whats-new/Makefile new file mode 100644 --- /dev/null +++ b/talk/pyconza2017/pypy-whats-new/Makefile @@ -0,0 +1,7 @@ +slides.pdf: slides.tex author.latex + pdflatex -halt-on-error $< + +slides.tex: slides.rst + rst2beamer.py slides.rst > slides.tex + sed 's/\\date{}/\\input{author.latex}/' -i slides.tex || exit + sed 's/\\documentclass\[\]{beamer}/\\documentclass\[aspectratio=169\]{beamer}/' -i slides.tex || exit diff --git a/talk/pyconza2017/pypy-whats-new/author.latex b/talk/pyconza2017/pypy-whats-new/author.latex new file mode 100644 --- /dev/null +++ b/talk/pyconza2017/pypy-whats-new/author.latex @@ -0,0 +1,7 @@ +\definecolor{rrblitbackground}{rgb}{0.4, 0.0, 0.0} + +\title[What's New in PyPy]{PyPy meets Python 3 and Numpy (and other What's New topics)} +\author[Armin Rigo]{Armin Rigo} + +\institute{PyConZA 2017} +\date{October 2017} diff --git a/talk/pyconza2017/pypy-whats-new/graphs.png b/talk/pyconza2017/pypy-whats-new/graphs.png new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9e5b0156179767c78b72f414147959bec8e982c0 GIT binary patch [cut] diff --git a/talk/pyconza2017/pypy-whats-new/slides.pdf b/talk/pyconza2017/pypy-whats-new/slides.pdf new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7fd5fdeae3fe34927641ade3c151867aa43c75f7 GIT binary patch [cut] diff --git a/talk/pyconza2017/pypy-whats-new/slides.rst b/talk/pyconza2017/pypy-whats-new/slides.rst new file mode 100644 --- /dev/null +++ b/talk/pyconza2017/pypy-whats-new/slides.rst @@ -0,0 +1,280 @@ +=========================================================== +PyPy meets Python 3 and Numpy (and other What's New topics) +=========================================================== + + +What's New In PyPy +================== + +1. Python 3.5 + +2. Numpy, Cython, Pandas, ... + +3. pypy-stm? + +4. RevDB: reverse debugging + +5. Others... + + +What is PyPy +============ + +* PyPy is another implementation of Python + +* Mostly, drop-in replacement + +* Supports Python 2.7, and almost Python 3.5 + +* Comes with a JIT, good performance + + +PyPy 3.5 +============================================================ + +PyPy 3.5 +========== + +* Python 3.5 support released in *gamma* + + - as stable (and mostly as fast) as PyPy 2.7, same JIT/GC/etc. + + - what could be wrong is a few details everywhere + + - please try it and *report issues!* + +* Thanks to Mozilla for funding this work! + + +PyPy 3.5 +========== + +* Async HTTP benchmarks: + +.. image:: graphs.png + :scale: 26% + + +PyPy 3.5 status +=============== + +* Roughly complete 3.5 support (plus f-strings!) + +* Reasonably good performance + +* Tested mostly on Linux so far + +* First "final" to be released very soon + +* Python 3.6 to follow + + +Scientific stack +============================================================ + +Scientific stack +================ + +Numpy or Numpypy? + +* Numpy = the standard numpy library + +* Numpypy = our own partial reimplementation + + +Scientific stack +================ + +Numpy or Numpypy? + +* Numpy = the standard numpy library *(use this)* + +* Numpypy = our own partial reimplementation *(deprecated)* + + +Scientific stack +================ + +* Numpy works (99.9%) + +* On both PyPy 2.7 and PyPy 3.5 + +* The rest of the scientific stack mostly works too (Jupyter, + Matplotlib, Pandas, etc.) + + +Cython, cpyext +================ + +* Cython mostly works + +* Actually, any CPython C extension module mostly works + +* Thanks to ``cpyext``, our CPython C API emulation layer + + +Performance? +============== + +* Numpy/Pandas/etc. are all slow-ish at the Python-C boundary + +* Less so than last year but still + +* Complex algorithms written inside Numpy in C or Fortran have the same + speed, of course + + - lots of ``ndarray[index]`` => slow + + - one call to ``numpy.linalg.eig()`` => fast + + - speed hack: ``p = ffi.cast("double *", ffi.from_buffer(ndarray))`` + + +Performance? +============== + +* We have plans to improve + +* Funding help welcome + +* For now: try it out on your own code and see + + + + +Software Transactional Memory +============================================================ + +Software Transactional Memory +============================= + +* ``pypy-stm``: getting rid of the Global Interpreter Lock + + +Software Transactional Memory +============================= + +* ...unfortunately, this approach does not seem to work :-( + + +Software Transactional Memory +============================= + +Unstable performance: + +* "Conflicts" between threads are hard to find + +* Fix one conflict, usually only to uncover the next one + +* As long as there is one, performance is bad + +* You may fix enough to get good performance... but: + +* Continue developing the program, and you'll often reintroduce conflicts + +* (Also, hard to test for, reliably) + + +PyPy-nogil? +=========== + +* Another possibility: a GIL-free but non-STM PyPy + +* Large amount of work, not looking forward to do it + +* Would require serious funding first + + + +Reverse Debugger +============================================================ + +Reverse Debugger +================ + +* RevDB: The essential tool you need once a year + + +Reverse Debugger +================ + +* Debugger with the ability to go forward *and backward in time* + +* Watchpoints to know when a value changes, while going in either + direction + +* http://bitbucket.org/pypy/revdb + + + +Others +============================================================ + +JIT improvements +====================== + +* Reductions in the warm-up time + +* Consumes less memory, too + + +VMProf +===================== + +* A good high-performance profiler for Python code + +* ``pip install vmprof`` + +* Works on CPython and on PyPy + +* The PyPy version shows the machine code generated by the JIT + + +CFFI improvements +======================= + +* CFFI: calling C from Python or the other way around + +* Works identically on CPython and on PyPy + +* Biggest improvement of last year is *embedding* + +* Use CFFI to embed Python inside another program---much easier than + with the CPython C API, and works identically on CPython or PyPy too + + +Next year? +================================================================= + +Next year? +========== + +* Polish PyPy 3.5 / 3.6 + +* Polish Numpy and the scientific stack + +* Port RevDB to PyPy 3.5 + +* Better memory profiling + +.. raw:: latex + + \vskip 5mm + \vphantom{Thank you!} + + +Question & answers +================== + +* Polish PyPy 3.5 / 3.6 + +* Polish Numpy and the scientific stack + +* Port RevDB to PyPy 3.5 + +* Better memory profiling + +.. raw:: latex + + \vskip 5mm + Thank you! \hskip 35mm \texttt{http://pypy.org/} From pypy.commits at gmail.com Sat Oct 7 09:09:54 2017 From: pypy.commits at gmail.com (arigo) Date: Sat, 07 Oct 2017 06:09:54 -0700 (PDT) Subject: [pypy-commit] cffi default: Push and pull at the mess Message-ID: <59d8d222.c7341c0a.2c97e.87f3@mx.google.com> Author: Armin Rigo Branch: Changeset: r3044:1b28d5d027ed Date: 2017-10-05 08:16 +0200 http://bitbucket.org/cffi/cffi/changeset/1b28d5d027ed/ Log: Push and pull at the mess diff --git a/c/misc_thread_common.h b/c/misc_thread_common.h --- a/c/misc_thread_common.h +++ b/c/misc_thread_common.h @@ -62,33 +62,35 @@ #endif -/* Seems that CPython 3.5.1 made our job harder. Did not find out how - to do that without these hacks. We can't use PyThreadState_GET(), - because that calls PyThreadState_Get() which fails an assert if the - result is NULL. We can use _PyThreadState_UncheckedGet() from 3.6, - though. It was added in 3.5.2 but should never be used in 3.5.x - because it is not available in 3.5.0 or 3.5.1. */ -#if PY_VERSION_HEX >= 0x03060000 +/* MESS. We can't use PyThreadState_GET(), because that calls + PyThreadState_Get() which fails an assert if the result is NULL. + + * in Python 2.7 and <= 3.4, the variable _PyThreadState_Current + is directly available, so use that. + + * in Python 3.5, the variable is available too, but it might be + the case that the headers don't define it (this changed in 3.5.1). + In case we're compiling with 3.5.x with x >= 1, we need to + manually define this variable. + + * in Python >= 3.6 there is _PyThreadState_UncheckedGet(). + It was added in 3.5.2 but should never be used in 3.5.x + because it is not available in 3.5.0 or 3.5.1. +*/ +#if PY_VERSION_HEX >= 0x03050100 && PY_VERSION_HEX < 0x03060000 +PyAPI_DATA(void *volatile) _PyThreadState_Current; +#endif + static PyThreadState *get_current_ts(void) { +#if PY_VERSION_HEX >= 0x03060000 return _PyThreadState_UncheckedGet(); +#elif defined(_Py_atomic_load_relaxed) + return (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current); +#else + return (PyThreadState*)_PyThreadState_Current; /* assume atomic read */ +#endif } -#else -# if PY_MAJOR_VERSION >= 3 && !defined(_Py_atomic_load_relaxed) - /* this was abruptly un-defined in 3.5.1 */ -extern void *volatile _PyThreadState_Current; - /* XXX simple volatile access is assumed atomic */ -# define _Py_atomic_load_relaxed(pp) (*(pp)) -# endif -static PyThreadState *get_current_ts(void) -{ -# if defined(_Py_atomic_load_relaxed) - return (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current); -# else - return _PyThreadState_Current; -# endif -} -#endif static PyGILState_STATE gil_ensure(void) { From pypy.commits at gmail.com Sat Oct 7 09:09:57 2017 From: pypy.commits at gmail.com (mattip) Date: Sat, 07 Oct 2017 06:09:57 -0700 (PDT) Subject: [pypy-commit] pypy.org extradoc: update downloads for v5.9 release Message-ID: <59d8d225.8e861c0a.873f.cff7@mx.google.com> Author: Matti Picus Branch: extradoc Changeset: r899:eff252dbfe4c Date: 2017-10-05 18:27 +1100 http://bitbucket.org/pypy/pypy.org/changeset/eff252dbfe4c/ Log: update downloads for v5.9 release diff --git a/source/download.txt b/source/download.txt --- a/source/download.txt +++ b/source/download.txt @@ -15,14 +15,14 @@ We provide binaries for x86, ARM, PPC and s390x running on different operating systems such as Linux, Mac OS X and Windows: -* the Python2.7 compatible release — **PyPy2.7 v5.8.0** — (`what's new in PyPy2.7?`_) +* the Python2.7 compatible release — **PyPy2.7 v5.9.0** — (`what's new in PyPy2.7?`_) -* the Python3.5 compatible beta quality release — **PyPy3.5 v5.8.0** — (`what's new in PyPy3.5?`_). +* the Python3.5 compatible beta quality release — **PyPy3.5 v5.9.0** — (`what's new in PyPy3.5?`_). * the Python2.7 Software Transactional Memory special release — **PyPy-STM 2.5.1** (Linux x86-64 only) -.. _what's new in PyPy2.7?: http://doc.pypy.org/en/latest/release-v5.8.0.html -.. _what's new in PyPy3.5?: http://doc.pypy.org/en/latest/release-v5.8.0.html +.. _what's new in PyPy2.7?: http://doc.pypy.org/en/latest/release-v5.9.0.html +.. _what's new in PyPy3.5?: http://doc.pypy.org/en/latest/release-v5.9.0.html .. class:: download_menu @@ -79,7 +79,7 @@ .. _release: -Python2.7 compatible PyPy 5.8.0 +Python2.7 compatible PyPy 5.9.0 ------------------------------- * `Linux x86 binary (32bit, tar.bz2 built on Ubuntu 12.04 - 16.04)`__ (see ``[1]`` below) @@ -98,24 +98,24 @@ * `All our downloads,`__ including previous versions. We also have a mirror_, but please use only if you have troubles accessing the links above -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-linux32.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-linux64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-linux-armhf-raspbian.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-linux-armhf-raring.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-linux-armel.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-osx64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-win32.zip -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-ppc64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-ppc64le.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-s390x.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-src.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-src.zip +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.9.0-linux32.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.9.0-linux64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.9.0-linux-armhf-raspbian.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.9.0-linux-armhf-raring.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.9.0-linux-armel.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.9.0-osx64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.9.0-win32.zip +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.9.0-ppc64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.9.0-ppc64le.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.9.0-s390x.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.9.0-src.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.9.0-src.zip .. _`vcredist_x86.exe`: http://www.microsoft.com/en-us/download/details.aspx?id=5582 .. __: https://bitbucket.org/pypy/pypy/downloads .. _mirror: http://buildbot.pypy.org/mirror/ .. _FreshPorts: http://www.freshports.org/lang/pypy -Python 3.5.3 compatible PyPy3.5 v5.8 +Python 3.5.3 compatible PyPy3.5 v5.9 ------------------------------------ .. class:: download_menu @@ -131,9 +131,9 @@ * `All our downloads,`__ including previous versions. We also have a mirror_, but please use only if you have troubles accessing the links above -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v5.8.0-linux64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v5.8.0-src.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v5.8.0-src.zip +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v5.9.0-linux64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v5.9.0-src.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v5.9.0-src.zip .. __: https://bitbucket.org/pypy/pypy/downloads If your CPU is really, really old, it may be a x86-32 without SSE2. @@ -199,7 +199,7 @@ uncompressed, they run in-place. For now you can uncompress them either somewhere in your home directory or, say, in ``/opt``, and if you want, put a symlink from somewhere like -``/usr/local/bin/pypy`` to ``/path/to/pypy2-5.8.0/bin/pypy``. Do +``/usr/local/bin/pypy`` to ``/path/to/pypy2-5.9.0/bin/pypy``. Do not move or copy the executable ``pypy`` outside the tree --- put a symlink to it, otherwise it will not find its libraries. @@ -303,9 +303,9 @@ Alternatively, the following smaller package contains the source at the same revision as the above binaries: - * `pypy2-v5.8.0-src.tar.bz2`__ (sources) + * `pypy2-v5.9.0-src.tar.bz2`__ (sources) - .. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-src.tar.bz2 + .. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.9.0-src.tar.bz2 2. Make sure you **installed the dependencies.** See the list here__. @@ -450,9 +450,28 @@ 2e464bcbc8216e55bb2433ace712130244fd1f3fa78de0c0c98745fd8ff12b03 pypy2-v5.8.0-ppc64.tar.bz2 5746823904df74423376e0326046e1171df9693a6d4c95e8ce14ca83534bae72 pypy2-v5.8.0-ppc64le.tar.bz2 +pypy2.7-5.9.0 sha256 + + a2431a9e4ef879da1a2b56b111013b4a6efb87d4173a37bf650de47834ac5fe4 pypy2-v5.9.0-linux32.tar.bz2 + 790febd4f09e22d6e2f81154efc7dc4b2feec72712aaf4f82aa91b550abb4b48 pypy2-v5.9.0-linux64.tar.bz2 + ac0676d91dfb388c799ec5c2845f42018a666423376f52f3ae13d61fd2e6f87d pypy2-v5.9.0-linux-armel.tar.bz2 + 2597b7b21acdef4f2b81074a594157c9450363c74a17f005548c6b102f93cff4 pypy2-v5.9.0-linux-armhf-raring.tar.bz2 + b8a20042a3f34486a372ff7c751539b2e16859c0a7ea69d5a73af92f0fdcb25a pypy2-v5.9.0-linux-armhf-raspbian.tar.bz2 + 94de50ed80c7f6392ed356c03fd54cdc84858df43ad21e9e971d1b6da0f6b867 pypy2-v5.9.0-osx64.tar.bz2 + 36d6b5158801c8aa4ef5b9d8990ca0a3782a38a04916be5644a33c2a82465101 pypy2-v5.9.0-s390x.tar.bz2 + de4bf05df47f1349dbac97233d9277bbaf1ef3331663ea2557fd5da3dbcfd0a7 pypy2-v5.9.0-src.tar.bz2 + db42dbed029eeac2da1dfe9bc71d63c934106acbed6bfad8910d2dabb557d9c2 pypy2-v5.9.0-src.zip + b61081e24e05b83d8110da1262be19f0094532c6cacc293e318a1c186d926533 pypy2-v5.9.0-win32.zip + pypy 3.5-v5.8.0 sha256:: 9d090127335c3c0fd2b14c8835bf91752e62756e55ea06aad3353f24a6854223 pypy3-v5.8.0-src.tar.bz2 57d871a7f1135719c138cee4e3533c3275d682a76a40ff668e95150c65923035 pypy3-v5.8.0-linux64.tar.bz2 8c868b5c8d15ce8acdf967f3c25da44bf52f6c7aa1fd1e50ebd50590f98066a4 pypy3-v5.8.0-src.zip +pypy 3.5-v5.9.0 sha256:: + + d8c41ede3758127718944cc2fd6bf78ed4303d946f85596cac91281ccce36165 pypy3-v5.9.0-linux64.tar.bz2 + a014f47f50a1480f871a0b82705f904b38c93c4ca069850eb37653fedafb1b97 pypy3-v5.9.0-src.tar.bz2 + c5d7fa206cdf425de3950ef8ff578deb0745a723b69b64bb121210a5b8df8c65 pypy3-v5.9.0-src.zip + From pypy.commits at gmail.com Sat Oct 7 09:10:01 2017 From: pypy.commits at gmail.com (fijal) Date: Sat, 07 Oct 2017 06:10:01 -0700 (PDT) Subject: [pypy-commit] pypy unicode-utf8: pass or skip remaining unicodeobject tests Message-ID: <59d8d229.c88b1c0a.4662b.2a4b@mx.google.com> Author: fijal Branch: unicode-utf8 Changeset: r92603:7643acecdab9 Date: 2017-10-05 10:27 +0200 http://bitbucket.org/pypy/pypy/changeset/7643acecdab9/ Log: pass or skip remaining unicodeobject tests diff --git a/TODO b/TODO new file mode 100644 --- /dev/null +++ b/TODO @@ -0,0 +1,1 @@ +* unskip tests in test_unicodeobject.py diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -1,6 +1,7 @@ from pypy.interpreter.error import OperationError from rpython.rlib.objectmodel import specialize from rpython.rlib import runicode, rutf8 +from rpython.rlib.rstring import StringBuilder from pypy.module._codecs import interp_codecs @specialize.memo() @@ -19,11 +20,11 @@ @specialize.memo() def encode_error_handler(space): # Fast version of the "strict" errors handler. - def raise_unicode_exception_encode(errors, encoding, msg, u, + def raise_unicode_exception_encode(errors, encoding, msg, u, u_len, startingpos, endingpos): raise OperationError(space.w_UnicodeEncodeError, space.newtuple([space.newtext(encoding), - space.newunicode(u), + space.newutf8(u, u_len), space.newint(startingpos), space.newint(endingpos), space.newtext(msg)])) @@ -95,9 +96,20 @@ def utf8_encode_ascii(utf8, utf8len, errors, errorhandler): if len(utf8) == utf8len: return utf8 - return rutf8.utf8_encode_ascii(utf8, errors, 'ascii', - 'ordinal not in range (128)', - errorhandler) + assert False, "implement" + b = StringBuilder(utf8len) + i = 0 + lgt = 0 + while i < len(utf8): + c = ord(utf8[i]) + if c <= 0x7F: + b.append(chr(c)) + lgt += 1 + i += 1 + else: + utf8_repl, newpos, length = errorhandler(errors, 'ascii', + 'ordinal not in range (128)', utf8, lgt, lgt + 1) + return b.build(), lgt def str_decode_ascii(s, slen, errors, final, errorhandler): try: diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py --- a/pypy/module/_codecs/interp_codecs.py +++ b/pypy/module/_codecs/interp_codecs.py @@ -39,7 +39,7 @@ w_input = space.newbytes(input) else: w_cls = space.w_UnicodeEncodeError - w_input = space.newutf8(input, -1) + w_input = space.newutf8(input, rutf8.check_utf8(input)) w_exc = space.call_function( w_cls, space.newtext(encoding), @@ -73,13 +73,7 @@ return self._make_errorhandler(space, True) def make_encode_errorhandler(self, space): - errorhandler = self._make_errorhandler(space, False) - def encode_call_errorhandler(errors, encoding, reason, input, startpos, - endpos): - replace, newpos, lgt = errorhandler(errors, encoding, reason, input, - startpos, endpos) - return replace, None, newpos, lgt - return encode_call_errorhandler + return self._make_errorhandler(space, False) def get_unicodedata_handler(self, space): if self.unicodedata_handler: @@ -384,6 +378,7 @@ state = space.fromcache(CodecState) func = getattr(unicodehelper, rname) utf8len = w_arg._length + # XXX deal with func() returning length or not result = func(w_arg._utf8, utf8len, errors, state.encode_error_handler) return space.newtuple([space.newbytes(result), space.newint(utf8len)]) diff --git a/pypy/objspace/std/test/test_unicodeobject.py b/pypy/objspace/std/test/test_unicodeobject.py --- a/pypy/objspace/std/test/test_unicodeobject.py +++ b/pypy/objspace/std/test/test_unicodeobject.py @@ -579,6 +579,7 @@ assert unicode('+AB', 'utf-7', 'replace') == u'\ufffd' def test_codecs_utf8(self): + skip("unskip this before merge") assert u''.encode('utf-8') == '' assert u'\u20ac'.encode('utf-8') == '\xe2\x82\xac' assert u'\ud800\udc02'.encode('utf-8') == '\xf0\x90\x80\x82' @@ -611,6 +612,7 @@ assert unicode('\xe2\x82\xac', 'utf-8') == u'\u20ac' def test_codecs_errors(self): + skip("some nonsense in handling of ignore and replace") # Error handling (encoding) raises(UnicodeError, u'Andr\202 x'.encode, 'ascii') raises(UnicodeError, u'Andr\202 x'.encode, 'ascii','strict') diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -86,26 +86,13 @@ def readbuf_w(self, space): # XXX for now from rpython.rlib.rstruct.unichar import pack_unichar, UNICODE_SIZE - XXX - FIXME -#<<<<<<< /home/arigo/hg/pypy/default/pypy/objspace/std/unicodeobject.py -# v = self._utf8.decode("utf8") -# builder = StringBuilder(len(v) * UNICODE_SIZE) -# for unich in v: -# pack_unichar(unich, builder) -# return StringBuffer(builder.build()) -#||||||| /tmp/unicodeobject~base.7TSwHV.py -# builder = StringBuilder(len(self._value) * UNICODE_SIZE) -# for unich in self._value: -# pack_unichar(unich, builder) -# return StringBuffer(builder.build()) -#======= -# buf = MutableStringBuffer(len(self._value) * UNICODE_SIZE) -# pos = 0 -# for unich in self._value: -# pack_unichar(unich, buf, pos) -# pos += UNICODE_SIZE -# return StringBuffer(buf.finish()) -#>>>>>>> /tmp/unicodeobject~other.TRKznC.py + v = self._utf8.decode("utf8") + builder = MutableStringBuffer(len(v) * UNICODE_SIZE) + pos = 0 + for unich in v: + pack_unichar(unich, builder, pos) + pos += UNICODE_SIZE + return StringBuffer(builder.finish()) def writebuf_w(self, space): raise oefmt(space.w_TypeError, @@ -798,11 +785,10 @@ s = space.utf8_w(w_object) try: rutf8.check_ascii(s) - except rutf8.AsciiCheckError as a: - XXX # must raise OperationError(w_UnicodeEncodeError) - XXX # maybe with eh = unicodehelper.encode_error_handler(space)? - eh = unicodehelper.raise_unicode_exception_encode - eh(None, "ascii", "ordinal not in range(128)", s, + except rutf8.CheckError as a: + eh = unicodehelper.encode_error_handler(space) + u_len = w_object._len() + eh(None, "ascii", "ordinal not in range(128)", s, u_len, a.pos, a.pos + 1) assert False, "always raises" return space.newbytes(s) From pypy.commits at gmail.com Sat Oct 7 09:10:03 2017 From: pypy.commits at gmail.com (fijal) Date: Sat, 07 Oct 2017 06:10:03 -0700 (PDT) Subject: [pypy-commit] pypy unicode-utf8: merge Message-ID: <59d8d22b.8e861c0a.873f.d009@mx.google.com> Author: fijal Branch: unicode-utf8 Changeset: r92604:f06c4111345a Date: 2017-10-05 10:28 +0200 http://bitbucket.org/pypy/pypy/changeset/f06c4111345a/ Log: merge diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -95,6 +95,8 @@ """ pos = r_uint(pos) pos -= 1 + if pos >= len(code): # for the case where pos - 1 == len(code): + return pos # assume there is an extra '\x00' character chr1 = ord(code[pos]) if chr1 <= 0x7F: return pos @@ -347,6 +349,16 @@ assert pos == len(s) return pos - continuation_bytes + at jit.elidable +def surrogate_in_utf8(value): + """Check if the UTF-8 byte string 'value' contains a surrogate. + The 'value' argument must be otherwise correctly formed for UTF-8. + """ + for i in range(len(value) - 2): + if value[i] == '\xed' and value[i + 1] >= '\xa0': + return True + return False + UTF8_INDEX_STORAGE = lltype.GcArray(lltype.Struct( 'utf8_loc', @@ -367,9 +379,9 @@ """ Create an index storage which stores index of each 4th character in utf8 encoded unicode string. """ - if len(utf8) == utf8len <= ASCII_INDEX_STORAGE_BLOCKS * 64: + if len(utf8) == utf8len < ASCII_INDEX_STORAGE_BLOCKS * 64: return ASCII_INDEX_STORAGE - arraysize = (utf8len + 63) // 64 + arraysize = utf8len // 64 + 1 storage = lltype.malloc(UTF8_INDEX_STORAGE, arraysize) baseindex = 0 current = 0 @@ -377,10 +389,14 @@ storage[current].baseindex = baseindex next = baseindex for i in range(16): - next = next_codepoint_pos(utf8, next) + if utf8len == 0: + next += 1 # assume there is an extra '\x00' character + else: + next = next_codepoint_pos(utf8, next) storage[current].ofs[i] = chr(next - baseindex) utf8len -= 4 - if utf8len <= 0: + if utf8len < 0: + assert current + 1 == len(storage) break next = next_codepoint_pos(utf8, next) next = next_codepoint_pos(utf8, next) diff --git a/rpython/rlib/test/test_rutf8.py b/rpython/rlib/test/test_rutf8.py --- a/rpython/rlib/test/test_rutf8.py +++ b/rpython/rlib/test/test_rutf8.py @@ -93,8 +93,17 @@ ord(item)) @given(strategies.text()) + at example(u'x' * 64 * 5) + at example(u'x' * (64 * 5 - 1)) def test_codepoint_position_at_index(u): index = rutf8.create_utf8_index_storage(u.encode('utf8'), len(u)) - for i in range(len(u)): + for i in range(len(u) + 1): assert (rutf8.codepoint_position_at_index(u.encode('utf8'), index, i) == len(u[:i].encode('utf8'))) + + at given(strategies.lists(strategies.characters())) +def test_surrogate_in_utf8(unichars): + uni = u''.join(unichars).encode('utf-8') + result = rutf8.surrogate_in_utf8(uni) + expected = any(uch for uch in unichars if u'\ud800' <= uch <= u'\udfff') + assert result == expected From pypy.commits at gmail.com Sat Oct 7 09:10:06 2017 From: pypy.commits at gmail.com (fijal) Date: Sat, 07 Oct 2017 06:10:06 -0700 (PDT) Subject: [pypy-commit] pypy unicode-utf8: try to use utf8 index storage for getitem Message-ID: <59d8d22e.30b8df0a.bb5f7.0d67@mx.google.com> Author: fijal Branch: unicode-utf8 Changeset: r92605:82feb3298ec4 Date: 2017-10-05 13:46 +0200 http://bitbucket.org/pypy/pypy/changeset/82feb3298ec4/ Log: try to use utf8 index storage for getitem diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -32,14 +32,12 @@ _immutable_fields_ = ['_utf8'] @enforceargs(utf8str=str) - def __init__(self, utf8str, length, ucs4str=None): + def __init__(self, utf8str, length): assert isinstance(utf8str, str) assert length >= 0 - if ucs4str is not None: - assert isinstance(ucs4str, unicode) self._utf8 = utf8str self._length = length - self._ucs4 = ucs4str + self._index_storage = None if not we_are_translated(): assert rutf8.check_utf8(utf8str, allow_surrogates=True) == length @@ -636,12 +634,15 @@ descr_rmul = descr_mul def _getitem_result(self, space, index): - if self._ucs4 is None: - self._ucs4 = self._utf8.decode('utf-8') - try: - return W_UnicodeObject(self._ucs4[index].encode('utf-8'), 1) - except IndexError: + if index >= self._length: raise oefmt(space.w_IndexError, "string index out of range") + if self._index_storage is None: + self._index_storage = rutf8.create_utf8_index_storage(self._utf8, + self._length) + start = rutf8.codepoint_position_at_index(self._utf8, + self._index_storage, index) + end = rutf8.next_codepoint_pos(self._utf8, start) + return W_UnicodeObject(self._utf8[start:end], 1) @unwrap_spec(width=int, w_fillchar=WrappedDefault(' ')) def descr_rjust(self, space, width, w_fillchar): From pypy.commits at gmail.com Sat Oct 7 09:10:09 2017 From: pypy.commits at gmail.com (fijal) Date: Sat, 07 Oct 2017 06:10:09 -0700 (PDT) Subject: [pypy-commit] pypy unicode-utf8: RPythonize hacks Message-ID: <59d8d231.c7a3df0a.75ad8.655e@mx.google.com> Author: fijal Branch: unicode-utf8 Changeset: r92606:af0fec89c369 Date: 2017-10-05 13:50 +0200 http://bitbucket.org/pypy/pypy/changeset/af0fec89c369/ Log: RPythonize hacks diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -835,7 +835,7 @@ try: lgt = rutf8.check_utf8(s, allow_surrogates=True) except rutf8.CheckError: - XXX + assert False, "fix in the future" eh = unicodehelper.decode_error_handler(space) eh(None, 'utf8', e.msg, s, e.startpos, e.endpos) assert False, "has to raise" From pypy.commits at gmail.com Sat Oct 7 09:10:13 2017 From: pypy.commits at gmail.com (fijal) Date: Sat, 07 Oct 2017 06:10:13 -0700 (PDT) Subject: [pypy-commit] pypy unicode-utf8: don't want to care for now Message-ID: <59d8d235.88c5df0a.4ec01.14f0@mx.google.com> Author: fijal Branch: unicode-utf8 Changeset: r92607:2ff1618c9fea Date: 2017-10-05 13:53 +0200 http://bitbucket.org/pypy/pypy/changeset/2ff1618c9fea/ Log: don't want to care for now diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py --- a/pypy/module/_codecs/interp_codecs.py +++ b/pypy/module/_codecs/interp_codecs.py @@ -443,7 +443,7 @@ # "allow_surrogates=True" @unwrap_spec(utf8='utf8', errors='text_or_none') def utf_8_encode(space, utf8, errors="strict"): - XXXX + raise Exception('foo') return space.newtuple([space.newbytes(utf8), space.newint(utf8len)]) #@unwrap_spec(uni=unicode, errors='text_or_none') #def utf_8_encode(space, uni, errors="strict"): From pypy.commits at gmail.com Sat Oct 7 09:10:16 2017 From: pypy.commits at gmail.com (fijal) Date: Sat, 07 Oct 2017 06:10:16 -0700 (PDT) Subject: [pypy-commit] pypy unicode-utf8: more of the same Message-ID: <59d8d238.c2311c0a.5fa17.9a18@mx.google.com> Author: fijal Branch: unicode-utf8 Changeset: r92608:fd73eab7bc5a Date: 2017-10-05 13:55 +0200 http://bitbucket.org/pypy/pypy/changeset/fd73eab7bc5a/ Log: more of the same diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py --- a/pypy/module/_codecs/interp_codecs.py +++ b/pypy/module/_codecs/interp_codecs.py @@ -626,7 +626,7 @@ @unwrap_spec(utf8='utf8', errors='text_or_none') def charmap_encode(space, utf8, errors="strict", w_mapping=None): from pypy.interpreter.unicodehelper import EncodeWrapper - XXXXX + raise Exception("foo") if errors is None: errors = 'strict' @@ -646,6 +646,7 @@ @unwrap_spec(chars='utf8') def charmap_build(space, chars): # XXX CPython sometimes uses a three-level trie + raise Exception("foo") XXXXXX w_charmap = space.newdict() pos = 0 From pypy.commits at gmail.com Sat Oct 7 09:10:19 2017 From: pypy.commits at gmail.com (fijal) Date: Sat, 07 Oct 2017 06:10:19 -0700 (PDT) Subject: [pypy-commit] pypy unicode-utf8: one more and I give up Message-ID: <59d8d23b.6896df0a.720ec.d8e4@mx.google.com> Author: fijal Branch: unicode-utf8 Changeset: r92609:9d3ca8dde893 Date: 2017-10-05 13:57 +0200 http://bitbucket.org/pypy/pypy/changeset/9d3ca8dde893/ Log: one more and I give up diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -115,7 +115,8 @@ try: rutf8.check_ascii(s, slen) return s, slen, len(s) - except rutf8.AsciiCheckError: + except rutf8.CheckError: + raise Exception("foo") return rutf8.str_decode_ascii(s, slen, errors, errorhandler) # XXX wrappers, think about speed From pypy.commits at gmail.com Sat Oct 7 09:10:23 2017 From: pypy.commits at gmail.com (antocuni) Date: Sat, 07 Oct 2017 06:10:23 -0700 (PDT) Subject: [pypy-commit] pypy cpyext-nowrapper: (antocuni, ronan around): WIP: refactor _PyObject_NewVar to avoid references to w_* and make it more similar to what CPython does. We need to do the same to PyObject_InitVar before we can declare it no_gc=True, though Message-ID: <59d8d23f.2587df0a.e5230.76d9@mx.google.com> Author: Antonio Cuni Branch: cpyext-nowrapper Changeset: r92610:b2d93af76cb7 Date: 2017-10-05 14:33 +0200 http://bitbucket.org/pypy/pypy/changeset/b2d93af76cb7/ Log: (antocuni, ronan around): WIP: refactor _PyObject_NewVar to avoid references to w_* and make it more similar to what CPython does. We need to do the same to PyObject_InitVar before we can declare it no_gc=True, though diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py --- a/pypy/module/cpyext/object.py +++ b/pypy/module/cpyext/object.py @@ -5,7 +5,7 @@ Py_TPFLAGS_HEAPTYPE, Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE, CONST_STRING, FILEP, fwrite) from pypy.module.cpyext.pyobject import ( - PyObject, PyObjectP, from_ref, Py_IncRef, decref, + PyObject, PyObjectP, from_ref, Py_IncRef, decref, incref, get_typedescr) from pypy.module.cpyext.typeobject import PyTypeObjectPtr from pypy.module.cpyext.pyerrors import PyErr_NoMemory, PyErr_BadInternalCall @@ -40,19 +40,32 @@ def _PyObject_New(space, type): return _PyObject_NewVar(space, type, 0) +def _VAR_SIZE(typeobj, nitems): + # equivalent to _PyObject_VAR_SIZE + SIZEOF_VOID_P = rffi.sizeof(rffi.VOIDP) + return (typeobj.c_tp_basicsize + + nitems * typeobj.c_tp_itemsize + + (SIZEOF_VOID_P - 1)) & ~(SIZEOF_VOID_P - 1) + @cpython_api([PyTypeObjectPtr, Py_ssize_t], PyObject, result_is_ll=True) -def _PyObject_NewVar(space, type, itemcount): - w_type = from_ref(space, rffi.cast(PyObject, type)) - assert isinstance(w_type, W_TypeObject) - typedescr = get_typedescr(w_type.layout.typedef) - py_obj = typedescr.allocate(space, w_type, itemcount=itemcount) - #py_obj.c_ob_refcnt = 0 --- will be set to 1 again by PyObject_Init{Var} - if type.c_tp_itemsize == 0: - w_obj = PyObject_Init(space, py_obj, type) - else: - py_objvar = rffi.cast(PyVarObject, py_obj) - w_obj = PyObject_InitVar(space, py_objvar, type, itemcount) - return py_obj +def _PyObject_NewVar(space, tp, nitems): + size = _VAR_SIZE(tp, nitems) + assert size >= rffi.sizeof(PyObject.TO) + buf = lltype.malloc(rffi.VOIDP.TO, size, + flavor='raw', zero=True, + add_memory_pressure=True) # XXX add_memory_pressure? + pyobj = rffi.cast(PyObject, buf) + # XXX should we do this? + ## if (pyobj == NULL) + ## return PyErr_NoMemory() + # + pyobj.c_ob_refcnt = 1 # why do we need this? + # + # XXX: incref tp? In the previous version, this was done by + # typedescr.alllocate(); however, CPython doesn't. + #incref(space, tp) + PyObject_InitVar(space, pyobj, tp, nitems) + return pyobj @slot_function([PyObject], lltype.Void, no_gc=True) def PyObject_dealloc(obj): From pypy.commits at gmail.com Sat Oct 7 09:10:26 2017 From: pypy.commits at gmail.com (mattip) Date: Sat, 07 Oct 2017 06:10:26 -0700 (PDT) Subject: [pypy-commit] pypy.org extradoc: generate html Message-ID: <59d8d242.a9a0df0a.f61bd.e21f@mx.google.com> Author: Matti Picus Branch: extradoc Changeset: r900:6cddadd87ea0 Date: 2017-10-05 23:40 +1100 http://bitbucket.org/pypy/pypy.org/changeset/6cddadd87ea0/ Log: generate html diff --git a/download.html b/download.html --- a/download.html +++ b/download.html @@ -77,8 +77,8 @@

We provide binaries for x86, ARM, PPC and s390x running on different operating systems such as Linux, Mac OS X and Windows:

    @@ -117,36 +117,36 @@
  • or translate your own PyPy.
-
-

Python2.7 compatible PyPy 5.8.0

+
+

Python2.7 compatible PyPy 5.9.0

-
-

Python 3.5.3 compatible PyPy3.5 v5.8

+
+

Python 3.5.3 compatible PyPy3.5 v5.9

Warning: PyPy3.5 is considered beta software. All binaries are thus called “beta”. It is known to be rarely much slower than PyPy 2. You are welcome to use it anyway; if you're lucky it will be fast in your case.

@@ -196,7 +196,7 @@ uncompressed, they run in-place. For now you can uncompress them either somewhere in your home directory or, say, in /opt, and if you want, put a symlink from somewhere like -/usr/local/bin/pypy to /path/to/pypy2-5.8.0/bin/pypy. Do +/usr/local/bin/pypy to /path/to/pypy2-5.9.0/bin/pypy. Do not move or copy the executable pypy outside the tree – put a symlink to it, otherwise it will not find its libraries.

@@ -277,7 +277,7 @@

Alternatively, the following smaller package contains the source at the same revision as the above binaries:

  • Make sure you installed the dependencies. See the list here.

    @@ -407,12 +407,30 @@ 2e464bcbc8216e55bb2433ace712130244fd1f3fa78de0c0c98745fd8ff12b03 pypy2-v5.8.0-ppc64.tar.bz2 5746823904df74423376e0326046e1171df9693a6d4c95e8ce14ca83534bae72 pypy2-v5.8.0-ppc64le.tar.bz2 +

    pypy2.7-5.9.0 sha256

    +
    +a2431a9e4ef879da1a2b56b111013b4a6efb87d4173a37bf650de47834ac5fe4 pypy2-v5.9.0-linux32.tar.bz2 +790febd4f09e22d6e2f81154efc7dc4b2feec72712aaf4f82aa91b550abb4b48 pypy2-v5.9.0-linux64.tar.bz2 +ac0676d91dfb388c799ec5c2845f42018a666423376f52f3ae13d61fd2e6f87d pypy2-v5.9.0-linux-armel.tar.bz2 +2597b7b21acdef4f2b81074a594157c9450363c74a17f005548c6b102f93cff4 pypy2-v5.9.0-linux-armhf-raring.tar.bz2 +b8a20042a3f34486a372ff7c751539b2e16859c0a7ea69d5a73af92f0fdcb25a pypy2-v5.9.0-linux-armhf-raspbian.tar.bz2 +94de50ed80c7f6392ed356c03fd54cdc84858df43ad21e9e971d1b6da0f6b867 pypy2-v5.9.0-osx64.tar.bz2 +36d6b5158801c8aa4ef5b9d8990ca0a3782a38a04916be5644a33c2a82465101 pypy2-v5.9.0-s390x.tar.bz2 +de4bf05df47f1349dbac97233d9277bbaf1ef3331663ea2557fd5da3dbcfd0a7 pypy2-v5.9.0-src.tar.bz2 +db42dbed029eeac2da1dfe9bc71d63c934106acbed6bfad8910d2dabb557d9c2 pypy2-v5.9.0-src.zip +b61081e24e05b83d8110da1262be19f0094532c6cacc293e318a1c186d926533 pypy2-v5.9.0-win32.zip

    pypy 3.5-v5.8.0 sha256:

     9d090127335c3c0fd2b14c8835bf91752e62756e55ea06aad3353f24a6854223  pypy3-v5.8.0-src.tar.bz2
     57d871a7f1135719c138cee4e3533c3275d682a76a40ff668e95150c65923035  pypy3-v5.8.0-linux64.tar.bz2
     8c868b5c8d15ce8acdf967f3c25da44bf52f6c7aa1fd1e50ebd50590f98066a4  pypy3-v5.8.0-src.zip
     
    +

    pypy 3.5-v5.9.0 sha256:

    +
    +d8c41ede3758127718944cc2fd6bf78ed4303d946f85596cac91281ccce36165  pypy3-v5.9.0-linux64.tar.bz2
    +a014f47f50a1480f871a0b82705f904b38c93c4ca069850eb37653fedafb1b97  pypy3-v5.9.0-src.tar.bz2
    +c5d7fa206cdf425de3950ef8ff578deb0745a723b69b64bb121210a5b8df8c65  pypy3-v5.9.0-src.zip
    +
  • Installing NumPy

    -

    There are two different versions of NumPy for PyPy.

    -
    -

    1. NumPy via cpyext

    -

    The generally recommended way is to install the original NumPy via the -CPython C API compatibility layer, cpyext. Modern versions of PyPy -support enough of the C API to make this a reasonable choice in many -cases. Performance-wise, the speed is mostly the same as CPython's -NumPy (it is the same code); the exception is that interactions between -the Python side and NumPy objects are mediated through the slower cpyext -layer (which hurts a few benchmarks that do a lot of element-by-element -array accesses, for example).

    -

    Installation works on any recent PyPy (the release above is fine, a recent -nightly will implement more of the new buffer protocol). -The currently released numpy 1.12 works except for nditers with the +

    There are two different versions of NumPy for PyPy. For details see this +FAQ question.

    +
    +

    1. Standard NumPy

    +

    Installation works on any recent PyPy (the release above is fine). +The currently released numpy 1.13 works except for nditers with the updateifcopy flag. For example, without using a virtualenv:

     $ ./pypy-xxx/bin/pypy -m ensurepip
    -$ ./pypy-xxx/bin/pypy -mpip install -U wheel # to update wheel
     $ ./pypy-xxx/bin/pip install cython numpy
     

    (See the general installation documentation for more.)

    2. NumPyPy

    -

    The “numpy” module can be installed from our own repository rather -than from the official source. This version uses internally our -built-in _numpypy module. This module is slightly incomplete. -Also, its performance is hard to predict exactly. For regular NumPy +

    The “numpy” module can also be installed from our own repository rather +than from the official source. This version uses our +built-in _numpypy multiarray replacement module, written in RPython. +This module is not complete, but if it works it should give correct answers. +Its performance is hard to predict exactly. For regular NumPy source code that handles large arrays, it is likely to be slower than -the native NumPy with cpyext. It is faster on the kind of code that -contains many Python loops doing things on an element-by-element basis.

    +the standard NumPy. It is faster on pure python code that loop over ndarrays +doing things on an element-by-element basis.

    Installation (see the installation documentation for installing pip):

     pypy -m pip install git+https://bitbucket.org/pypy/numpy.git
    @@ -258,9 +250,10 @@
     
     sudo pypy -c 'import numpy'
     
    -

    Note again that this version is still a work-in-progress: many things do +

    Note again that this version is incomplete: many things do not work and those that do may not be any faster than NumPy on CPython. -For further instructions see the pypy/numpy repository.

    +For further instructions see the pypy/numpy repository and the +FAQ question about the difference between the two.

    diff --git a/source/download.txt b/source/download.txt --- a/source/download.txt +++ b/source/download.txt @@ -223,29 +223,18 @@ Installing NumPy ------------------------------- -**There are two different versions of NumPy for PyPy.** +**There are two different versions of NumPy for PyPy.** For details see this +`FAQ question`_. +1. Standard NumPy ++++++++++++++++++ -1. NumPy via cpyext -+++++++++++++++++++ - -The generally recommended way is to install the original NumPy via the -CPython C API compatibility layer, cpyext. Modern versions of PyPy -support enough of the C API to make this a reasonable choice in many -cases. Performance-wise, the speed is mostly the same as CPython's -NumPy (it is the same code); the exception is that interactions between -the Python side and NumPy objects are mediated through the slower cpyext -layer (which hurts a few benchmarks that do a lot of element-by-element -array accesses, for example). - -Installation works on any recent PyPy (the release_ above is fine, a recent -nightly will implement more of the new buffer protocol). -The currently released numpy 1.12 works except for ``nditers`` with the +Installation works on any recent PyPy (the release_ above is fine). +The currently released numpy 1.13 works except for ``nditers`` with the ``updateifcopy`` flag. For example, without using a virtualenv:: $ ./pypy-xxx/bin/pypy -m ensurepip - $ ./pypy-xxx/bin/pypy -mpip install -U wheel # to update wheel $ ./pypy-xxx/bin/pip install cython numpy (See the general `installation documentation`_ for more.) @@ -254,13 +243,14 @@ 2. NumPyPy ++++++++++ -The "numpy" module can be installed from `our own repository`__ rather -than from the official source. This version uses internally our -built-in ``_numpypy`` module. This module is slightly incomplete. -Also, its performance is hard to predict exactly. For regular NumPy +The "numpy" module can also be installed from `our own repository`__ rather +than from the official source. This version uses our +built-in ``_numpypy`` multiarray replacement module, written in RPython. +This module is not complete, but if it works it should give correct answers. +Its performance is hard to predict exactly. For regular NumPy source code that handles large arrays, it is likely to be slower than -the native NumPy with cpyext. It is faster on the kind of code that -contains many Python loops doing things on an element-by-element basis. +the standard NumPy. It is faster on pure python code that loop over ndarrays +doing things on an element-by-element basis. .. __: https://bitbucket.org/pypy/numpy @@ -278,11 +268,13 @@ sudo pypy -c 'import numpy' -Note again that this version is still a work-in-progress: many things do +Note again that this version is incomplete: many things do not work and those that do may not be any faster than NumPy on CPython. -For further instructions see `the pypy/numpy repository`__. +For further instructions see `the pypy/numpy repository`__ and the +`FAQ question`_ about the difference between the two. .. __: https://bitbucket.org/pypy/numpy +.. _`FAQ question`: http://doc.pypy.org/en/latest/faq.html#should-i-install-numpy-or-numpypy .. _translate: From pypy.commits at gmail.com Sat Oct 28 15:19:24 2017 From: pypy.commits at gmail.com (mattip) Date: Sat, 28 Oct 2017 12:19:24 -0700 (PDT) Subject: [pypy-commit] pypy default: move test summary to end on terminal, after the multiple pages of failure info, typos Message-ID: <59f4d83c.09a0df0a.b232.9b2c@mx.google.com> Author: Matti Picus Branch: Changeset: r92866:0edecbafb119 Date: 2017-10-28 22:18 +0300 http://bitbucket.org/pypy/pypy/changeset/0edecbafb119/ Log: move test summary to end on terminal, after the multiple pages of failure info, typos diff --git a/_pytest/terminal.py b/_pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -366,11 +366,11 @@ EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, EXIT_USAGEERROR, EXIT_NOTESTSCOLLECTED) if exitstatus in summary_exit_codes: - self.config.hook.pytest_terminal_summary(terminalreporter=self) self.summary_errors() self.summary_failures() self.summary_warnings() self.summary_passes() + self.config.hook.pytest_terminal_summary(terminalreporter=self) if exitstatus == EXIT_INTERRUPTED: self._report_keyboardinterrupt() del self._keyboardinterrupt_memo diff --git a/pypy/test_all.py b/pypy/test_all.py --- a/pypy/test_all.py +++ b/pypy/test_all.py @@ -27,7 +27,7 @@ sys.path.insert(0,os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) import pytest if sys.platform == 'win32': - #Try to avoid opeing a dialog box if one of the tests causes a system error + #Try to avoid opening a dialog box if one of the tests causes a system error # We do this in runner.py, but buildbots run twisted which ruins inheritance # in windows subprocesses. import ctypes diff --git a/testrunner/runner.py b/testrunner/runner.py --- a/testrunner/runner.py +++ b/testrunner/runner.py @@ -21,7 +21,7 @@ win32api.CloseHandle(proch) except pywintypes.error, e: pass - #Try to avoid opeing a dialog box if one of the tests causes a system error + #Try to avoid opening a dialog box if one of the tests causes a system error import ctypes winapi = ctypes.windll.kernel32 SetErrorMode = winapi.SetErrorMode From pypy.commits at gmail.com Sat Oct 28 16:42:24 2017 From: pypy.commits at gmail.com (mattip) Date: Sat, 28 Oct 2017 13:42:24 -0700 (PDT) Subject: [pypy-commit] pypy py3.5-appexec: make appexec for applevel tests identical to default Message-ID: <59f4ebb0.83b91c0a.d0a5b.38a0@mx.google.com> Author: Matti Picus Branch: py3.5-appexec Changeset: r92868:ec989ce318a9 Date: 2017-10-28 23:40 +0300 http://bitbucket.org/pypy/pypy/changeset/ec989ce318a9/ Log: make appexec for applevel tests identical to default diff --git a/pypy/tool/pytest/objspace.py b/pypy/tool/pytest/objspace.py --- a/pypy/tool/pytest/objspace.py +++ b/pypy/tool/pytest/objspace.py @@ -79,7 +79,9 @@ body = body.lstrip() assert body.startswith('(') src = py.code.Source("def anonymous" + body) - return (src, args) + d = {} + exec src.compile() in d + return d['anonymous'](*args) def wrap(self, obj): if isinstance(obj, str): From pypy.commits at gmail.com Sat Oct 28 16:42:22 2017 From: pypy.commits at gmail.com (mattip) Date: Sat, 28 Oct 2017 13:42:22 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: tweak run_with_python() Message-ID: <59f4ebae.4f931c0a.1441.2f84@mx.google.com> Author: Matti Picus Branch: py3.5 Changeset: r92867:2e9a42fcdebf Date: 2017-10-28 23:23 +0300 http://bitbucket.org/pypy/pypy/changeset/2e9a42fcdebf/ Log: tweak run_with_python() diff --git a/pypy/tool/pytest/apptest.py b/pypy/tool/pytest/apptest.py --- a/pypy/tool/pytest/apptest.py +++ b/pypy/tool/pytest/apptest.py @@ -12,7 +12,7 @@ from pypy.interpreter.gateway import app2interp_temp from pypy.interpreter.error import OperationError from pypy.interpreter.function import Method -from rpython.tool import runsubprocess +from rpython.tool.runsubprocess import run_subprocess from pypy.tool.pytest import appsupport from pypy.tool.pytest.objspace import gettestobjspace from rpython.tool.udir import udir @@ -67,14 +67,10 @@ def _rename_module(name): return str(RENAMED_USEMODULES.get(name, name)) - -def run_with_python(python_, target_, usemodules, **definitions): - if python_ is None: - py.test.skip("Cannot find the default python3 interpreter to run with -A") - # we assume that the source of target_ is in utf-8. Unfortunately, we don't - # have any easy/standard way to determine from here the original encoding - # of the source file - helpers = r"""# -*- encoding: utf-8 -*- +# we assume that the source of target_ is in utf-8. Unfortunately, we don't +# have any easy/standard way to determine from here the original encoding +# of the source file +helpers = r"""# -*- encoding: utf-8 -*- if 1: import sys sys.path.append(%r) @@ -109,6 +105,10 @@ pass self = Test() """ + +def run_with_python(python_, target_, usemodules, **definitions): + if python_ is None: + py.test.skip("Cannot find the default python3 interpreter to run with -A") defs = [] for symbol, value in sorted(definitions.items()): if isinstance(value, tuple) and isinstance(value[0], py.code.Source): @@ -181,7 +181,7 @@ helper_dir = os.path.join(pypydir, 'tool', 'cpyext') env = os.environ.copy() env['PYTHONPATH'] = helper_dir - res, stdout, stderr = runsubprocess.run_subprocess( + res, stdout, stderr = run_subprocess( python_, [str(pyfile)], env=env) print pyfile.read() print >> sys.stdout, stdout From pypy.commits at gmail.com Sat Oct 28 18:58:11 2017 From: pypy.commits at gmail.com (wlav) Date: Sat, 28 Oct 2017 15:58:11 -0700 (PDT) Subject: [pypy-commit] pypy cppyy-packaging: cleanup Message-ID: <59f50b83.46c3df0a.23392.5604@mx.google.com> Author: Wim Lavrijsen Branch: cppyy-packaging Changeset: r92869:b9ac9a79e9e5 Date: 2017-10-12 13:34 -0700 http://bitbucket.org/pypy/pypy/changeset/b9ac9a79e9e5/ Log: cleanup diff --git a/pypy/module/_cppyy/test/test_datatypes.py b/pypy/module/_cppyy/test/test_datatypes.py --- a/pypy/module/_cppyy/test/test_datatypes.py +++ b/pypy/module/_cppyy/test/test_datatypes.py @@ -149,15 +149,15 @@ # integer types names = ['short', 'ushort', 'int', 'uint', 'long', 'ulong', 'llong', 'ullong'] for i in range(len(names)): - exec 'c.m_%s = %d' % (names[i],i) + setattr(c, 'm_'+names[i], i) assert eval('c.get_%s()' % names[i]) == i for i in range(len(names)): - exec 'c.set_%s(%d)' % (names[i],2*i) + getattr(c, 'set_'+names[i])(2*i) assert eval('c.m_%s' % names[i]) == 2*i for i in range(len(names)): - exec 'c.set_%s_cr(%d)' % (names[i],3*i) + getattr(c, 'set_'+names[i]+'_cr')(3*i) assert eval('c.m_%s' % names[i]) == 3*i # float types through functions @@ -182,11 +182,11 @@ atypes = ['h', 'H', 'i', 'I', 'l', 'L' ] for j in range(len(names)): b = array.array(atypes[j], a) - exec 'c.m_%s_array = b' % names[j] # buffer copies + setattr(c, 'm_'+names[j]+'_array', b) # buffer copies for i in range(self.N): assert eval('c.m_%s_array[i]' % names[j]) == b[i] - exec 'c.m_%s_array2 = b' % names[j] # pointer copies + setattr(c, 'm_'+names[j]+'_array2', b) # pointer copies b[i] = 28 for i in range(self.N): assert eval('c.m_%s_array2[i]' % names[j]) == b[i] @@ -241,10 +241,10 @@ assert isinstance(c, CppyyTestData) # char types - assert CppyyTestData.s_char == 'c' - assert c.s_char == 'c' - assert c.s_uchar == 'u' - assert CppyyTestData.s_uchar == 'u' + assert CppyyTestData.s_char == 'c' + assert c.s_char == 'c' + assert c.s_uchar == 'u' + assert CppyyTestData.s_uchar == 'u' # integer types assert CppyyTestData.s_short == -101 @@ -255,20 +255,20 @@ assert c.s_int == -202 assert c.s_uint == 202 assert CppyyTestData.s_uint == 202 - assert CppyyTestData.s_long == -303L - assert c.s_long == -303L - assert c.s_ulong == 303L - assert CppyyTestData.s_ulong == 303L - assert CppyyTestData.s_llong == -404L - assert c.s_llong == -404L - assert c.s_ullong == 404L - assert CppyyTestData.s_ullong == 404L + assert CppyyTestData.s_long == -303 + assert c.s_long == -303 + assert c.s_ulong == 303 + assert CppyyTestData.s_ulong == 303 + assert CppyyTestData.s_llong == -404 + assert c.s_llong == -404 + assert c.s_ullong == 404 + assert CppyyTestData.s_ullong == 404 # floating point types - assert round(CppyyTestData.s_float + 606., 5) == 0 - assert round(c.s_float + 606., 5) == 0 - assert round(CppyyTestData.s_double + 707., 8) == 0 - assert round(c.s_double + 707., 8) == 0 + assert round(CppyyTestData.s_float + 606., 5) == 0 + assert round(c.s_float + 606., 5) == 0 + assert round(CppyyTestData.s_double + 707., 8) == 0 + assert round(c.s_double + 707., 8) == 0 c.__destruct__() @@ -282,56 +282,56 @@ assert isinstance(c, CppyyTestData) # char types - CppyyTestData.s_char = 'a' - assert c.s_char == 'a' - c.s_char = 'b' - assert CppyyTestData.s_char == 'b' - CppyyTestData.s_uchar = 'c' - assert c.s_uchar == 'c' - c.s_uchar = 'd' - assert CppyyTestData.s_uchar == 'd' + CppyyTestData.s_char = 'a' + assert c.s_char == 'a' + c.s_char = 'b' + assert CppyyTestData.s_char == 'b' + CppyyTestData.s_uchar = 'c' + assert c.s_uchar == 'c' + c.s_uchar = 'd' + assert CppyyTestData.s_uchar == 'd' raises(ValueError, setattr, CppyyTestData, 's_uchar', -1) - raises(ValueError, setattr, c, 's_uchar', -1) + raises(ValueError, setattr, c, 's_uchar', -1) # integer types c.s_short = -102 - assert CppyyTestData.s_short == -102 - CppyyTestData.s_short = -203 + assert CppyyTestData.s_short == -102 + CppyyTestData.s_short = -203 assert c.s_short == -203 c.s_ushort = 127 - assert CppyyTestData.s_ushort == 127 - CppyyTestData.s_ushort = 227 + assert CppyyTestData.s_ushort == 127 + CppyyTestData.s_ushort = 227 assert c.s_ushort == 227 - CppyyTestData.s_int = -234 + CppyyTestData.s_int = -234 assert c.s_int == -234 c.s_int = -321 - assert CppyyTestData.s_int == -321 - CppyyTestData.s_uint = 1234 + assert CppyyTestData.s_int == -321 + CppyyTestData.s_uint = 1234 assert c.s_uint == 1234 c.s_uint = 4321 - assert CppyyTestData.s_uint == 4321 - raises(ValueError, setattr, c, 's_uint', -1) + assert CppyyTestData.s_uint == 4321 + raises(ValueError, setattr, c, 's_uint', -1) raises(ValueError, setattr, CppyyTestData, 's_uint', -1) - CppyyTestData.s_long = -87L - assert c.s_long == -87L - c.s_long = 876L - assert CppyyTestData.s_long == 876L - CppyyTestData.s_ulong = 876L - assert c.s_ulong == 876L - c.s_ulong = 678L - assert CppyyTestData.s_ulong == 678L + CppyyTestData.s_long = -87 + assert c.s_long == -87 + c.s_long = 876 + assert CppyyTestData.s_long == 876 + CppyyTestData.s_ulong = 876 + assert c.s_ulong == 876 + c.s_ulong = 678 + assert CppyyTestData.s_ulong == 678 raises(ValueError, setattr, CppyyTestData, 's_ulong', -1) - raises(ValueError, setattr, c, 's_ulong', -1) + raises(ValueError, setattr, c, 's_ulong', -1) # floating point types - CppyyTestData.s_float = -3.1415 - assert round(c.s_float, 5 ) == -3.1415 + CppyyTestData.s_float = -3.1415 + assert round(c.s_float, 5) == -3.1415 c.s_float = 3.1415 - assert round(CppyyTestData.s_float, 5 ) == 3.1415 + assert round(CppyyTestData.s_float, 5) == 3.1415 import math c.s_double = -math.pi - assert CppyyTestData.s_double == -math.pi - CppyyTestData.s_double = math.pi + assert CppyyTestData.s_double == -math.pi + CppyyTestData.s_double = math.pi assert c.s_double == math.pi c.__destruct__() From pypy.commits at gmail.com Sat Oct 28 18:58:13 2017 From: pypy.commits at gmail.com (wlav) Date: Sat, 28 Oct 2017 15:58:13 -0700 (PDT) Subject: [pypy-commit] pypy cppyy-packaging: consistency with CPython/cppyy (incl. capi and test upgrades) Message-ID: <59f50b85.d0e61c0a.20503.2bfa@mx.google.com> Author: Wim Lavrijsen Branch: cppyy-packaging Changeset: r92870:c10f636528b7 Date: 2017-10-28 10:14 -0700 http://bitbucket.org/pypy/pypy/changeset/c10f636528b7/ Log: consistency with CPython/cppyy (incl. capi and test upgrades) 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 @@ -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): @@ -687,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 @@ -695,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: @@ -716,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 @@ -743,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 @@ -864,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/interp_cppyy.py b/pypy/module/_cppyy/interp_cppyy.py --- a/pypy/module/_cppyy/interp_cppyy.py +++ b/pypy/module/_cppyy/interp_cppyy.py @@ -623,7 +623,21 @@ 'CPPConstructorOverload', is_static = interp2app(W_CPPConstructorOverload.is_static), call = interp2app(W_CPPConstructorOverload.call), - prototype = interp2app(W_CPPOverload.prototype), + 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), ) @@ -637,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__), @@ -667,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) @@ -675,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) @@ -1161,7 +1178,7 @@ W_CPPClass.typedef = TypeDef( '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__), @@ -1262,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): diff --git a/pypy/module/_cppyy/src/dummy_backend.cxx b/pypy/module/_cppyy/src/dummy_backend.cxx --- a/pypy/module/_cppyy/src/dummy_backend.cxx +++ b/pypy/module/_cppyy/src/dummy_backend.cxx @@ -955,7 +955,13 @@ return cppstring_to_cstring(""); } -char* cppyy_method_signature(cppyy_scope_t /* handle */, cppyy_index_t /* method_index */) { +char* cppyy_method_signature( + cppyy_scope_t /* handle */, cppyy_index_t /* method_index */, int /* show_formalargs */) { + return cppstring_to_cstring(""); +} + +char* cppyy_method_prototype( + cppyy_scope_t /* handle */, cppyy_index_t /* method_index */, int /* show_formalargs */) { return cppstring_to_cstring(""); } diff --git a/pypy/module/_cppyy/test/Makefile b/pypy/module/_cppyy/test/Makefile --- a/pypy/module/_cppyy/test/Makefile +++ b/pypy/module/_cppyy/test/Makefile @@ -7,7 +7,8 @@ fragileDict.so \ operatorsDict.so \ overloadsDict.so \ - stltypesDict.so + stltypesDict.so \ + templatesDict.so all : $(dicts) diff --git a/pypy/module/_cppyy/test/advancedcpp.cxx b/pypy/module/_cppyy/test/advancedcpp.cxx --- a/pypy/module/_cppyy/test/advancedcpp.cxx +++ b/pypy/module/_cppyy/test/advancedcpp.cxx @@ -106,17 +106,6 @@ } -// more template testing -long my_templated_method_class::get_size() { return -1; } - -long my_templated_method_class::get_char_size() { return (long)sizeof(char); } -long my_templated_method_class::get_int_size() { return (long)sizeof(int); } -long my_templated_method_class::get_long_size() { return (long)sizeof(long); } -long my_templated_method_class::get_float_size() { return (long)sizeof(float); } -long my_templated_method_class::get_double_size() { return (long)sizeof(double); } -long my_templated_method_class::get_self_size() { return (long)sizeof(my_templated_method_class); } - - // overload order testing int overload_one_way::gime() const { return 1; } std::string overload_one_way::gime() { return "aap"; } diff --git a/pypy/module/_cppyy/test/advancedcpp.h b/pypy/module/_cppyy/test/advancedcpp.h --- a/pypy/module/_cppyy/test/advancedcpp.h +++ b/pypy/module/_cppyy/test/advancedcpp.h @@ -246,8 +246,6 @@ int m_i; }; -template class std::vector; - //=========================================================================== class some_convertible { // for math conversions testing @@ -275,6 +273,7 @@ extern double my_global_double; // a couple of globals for access testing extern double my_global_array[500]; extern double* my_global_ptr; +static const char my_global_string[] = "aap " " noot " " mies"; //=========================================================================== class some_class_with_data { // for life-line and identity testing @@ -387,37 +386,6 @@ template char my_templated_function(char); template double my_templated_function(double); -class my_templated_method_class { -public: - long get_size(); // to get around bug in genreflex - template long get_size(); - - long get_char_size(); - long get_int_size(); - long get_long_size(); - long get_float_size(); - long get_double_size(); - - long get_self_size(); - -private: - double m_data[3]; -}; - -template -inline long my_templated_method_class::get_size() { - return sizeof(B); -} - -template long my_templated_method_class::get_size(); -template long my_templated_method_class::get_size(); -template long my_templated_method_class::get_size(); -template long my_templated_method_class::get_size(); -template long my_templated_method_class::get_size(); - -typedef my_templated_method_class my_typedef_t; -template long my_templated_method_class::get_size(); - //=========================================================================== class overload_one_way { // overload order testing diff --git a/pypy/module/_cppyy/test/advancedcpp.xml b/pypy/module/_cppyy/test/advancedcpp.xml --- a/pypy/module/_cppyy/test/advancedcpp.xml +++ b/pypy/module/_cppyy/test/advancedcpp.xml @@ -53,8 +53,6 @@ - - diff --git a/pypy/module/_cppyy/test/fragile.h b/pypy/module/_cppyy/test/fragile.h --- a/pypy/module/_cppyy/test/fragile.h +++ b/pypy/module/_cppyy/test/fragile.h @@ -30,9 +30,11 @@ void overload(int, no_such_class* p = 0) {} }; +static const int dummy_location = 0xdead; + class E { public: - E() : m_pp_no_such(0), m_pp_a(0) {} + E() : m_pp_no_such((no_such_class**)&dummy_location), m_pp_a(0) {} virtual int check() { return (int)'E'; } void overload(no_such_class**) {} diff --git a/pypy/module/_cppyy/test/templates.cxx b/pypy/module/_cppyy/test/templates.cxx new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/templates.cxx @@ -0,0 +1,12 @@ +#include "templates.h" + + +// template methods +long MyTemplatedMethodClass::get_size() { return -1; } + +long MyTemplatedMethodClass::get_char_size() { return (long)sizeof(char); } +long MyTemplatedMethodClass::get_int_size() { return (long)sizeof(int); } +long MyTemplatedMethodClass::get_long_size() { return (long)42; /* "lying" */ } +long MyTemplatedMethodClass::get_float_size() { return (long)sizeof(float); } +long MyTemplatedMethodClass::get_double_size() { return (long)sizeof(double); } +long MyTemplatedMethodClass::get_self_size() { return (long)sizeof(MyTemplatedMethodClass); } diff --git a/pypy/module/_cppyy/test/templates.h b/pypy/module/_cppyy/test/templates.h new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/templates.h @@ -0,0 +1,35 @@ +//=========================================================================== +class MyTemplatedMethodClass { // template methods +public: + long get_size(); // to get around bug in genreflex + template long get_size(); + + long get_char_size(); + long get_int_size(); + long get_long_size(); + long get_float_size(); + long get_double_size(); + + long get_self_size(); + +private: + double m_data[3]; +}; + +template +inline long MyTemplatedMethodClass::get_size() { + return sizeof(B); +} + +// +typedef MyTemplatedMethodClass MyTMCTypedef_t; + +// explicit instantiation +template long MyTemplatedMethodClass::get_size(); +template long MyTemplatedMethodClass::get_size(); + +// "lying" specialization +template<> +inline long MyTemplatedMethodClass::get_size() { + return 42; +} diff --git a/pypy/module/_cppyy/test/templates.xml b/pypy/module/_cppyy/test/templates.xml new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/templates.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/pypy/module/_cppyy/test/test_advancedcpp.py b/pypy/module/_cppyy/test/test_advancedcpp.py --- a/pypy/module/_cppyy/test/test_advancedcpp.py +++ b/pypy/module/_cppyy/test/test_advancedcpp.py @@ -28,9 +28,9 @@ def test01_default_arguments(self): """Test usage of default arguments""" - import _cppyy + import _cppyy as cppyy def test_defaulter(n, t): - defaulter = getattr(_cppyy.gbl, '%s_defaulter' % n) + defaulter = getattr(cppyy.gbl, '%s_defaulter' % n) d = defaulter() assert d.m_a == t(11) @@ -55,23 +55,23 @@ assert d.m_b == t(4) assert d.m_c == t(5) d.__destruct__() - test_defaulter('short', int) + test_defaulter('short', int) test_defaulter('ushort', int) - test_defaulter('int', int) - test_defaulter('uint', int) - test_defaulter('long', long) - test_defaulter('ulong', long) - test_defaulter('llong', long) + test_defaulter('int', int) + test_defaulter('uint', int) + test_defaulter('long', long) + test_defaulter('ulong', long) + test_defaulter('llong', long) test_defaulter('ullong', long) - test_defaulter('float', float) + test_defaulter('float', float) test_defaulter('double', float) def test02_simple_inheritance(self): """Test binding of a basic inheritance structure""" - import _cppyy - base_class = _cppyy.gbl.base_class - derived_class = _cppyy.gbl.derived_class + import _cppyy as cppyy + base_class = cppyy.gbl.base_class + derived_class = cppyy.gbl.derived_class assert issubclass(derived_class, base_class) assert not issubclass(base_class, derived_class) @@ -123,8 +123,8 @@ def test03_namespaces(self): """Test access to namespaces and inner classes""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl assert gbl.a_ns is gbl.a_ns assert gbl.a_ns.d_ns is gbl.a_ns.d_ns @@ -150,8 +150,8 @@ def test03a_namespace_lookup_on_update(self): """Test whether namespaces can be shared across dictionaries.""" - import _cppyy, ctypes - gbl = _cppyy.gbl + import _cppyy as cppyy, ctypes + gbl = cppyy.gbl lib2 = ctypes.CDLL("./advancedcpp2Dict.so", ctypes.RTLD_GLOBAL) @@ -179,8 +179,8 @@ def test04_template_types(self): """Test bindings of templated types""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl assert gbl.T1 is gbl.T1 assert gbl.T2 is gbl.T2 @@ -245,8 +245,8 @@ def test05_abstract_classes(self): """Test non-instatiatability of abstract classes""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl raises(TypeError, gbl.a_class) raises(TypeError, gbl.some_abstract_class) @@ -260,12 +260,12 @@ def test06_datamembers(self): """Test data member access when using virtual inheritence""" - import _cppyy - a_class = _cppyy.gbl.a_class - b_class = _cppyy.gbl.b_class - c_class_1 = _cppyy.gbl.c_class_1 - c_class_2 = _cppyy.gbl.c_class_2 - d_class = _cppyy.gbl.d_class + import _cppyy as cppyy + a_class = cppyy.gbl.a_class + b_class = cppyy.gbl.b_class + c_class_1 = cppyy.gbl.c_class_1 + c_class_2 = cppyy.gbl.c_class_2 + d_class = cppyy.gbl.d_class assert issubclass(b_class, a_class) assert issubclass(c_class_1, a_class) @@ -354,8 +354,8 @@ def test07_pass_by_reference(self): """Test reference passing when using virtual inheritance""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl b_class = gbl.b_class c_class = gbl.c_class_2 d_class = gbl.d_class @@ -387,71 +387,75 @@ def test08_void_pointer_passing(self): """Test passing of variants of void pointer arguments""" - import _cppyy - pointer_pass = _cppyy.gbl.pointer_pass - some_concrete_class = _cppyy.gbl.some_concrete_class + import _cppyy as cppyy + pointer_pass = cppyy.gbl.pointer_pass + some_concrete_class = cppyy.gbl.some_concrete_class pp = pointer_pass() o = some_concrete_class() - assert _cppyy.addressof(o) == pp.gime_address_ptr(o) - assert _cppyy.addressof(o) == pp.gime_address_ptr_ptr(o) - assert _cppyy.addressof(o) == pp.gime_address_ptr_ref(o) + assert cppyy.addressof(o) == pp.gime_address_ptr(o) + assert cppyy.addressof(o) == pp.gime_address_ptr_ptr(o) + assert cppyy.addressof(o) == pp.gime_address_ptr_ref(o) import array - addressofo = array.array('l', [_cppyy.addressof(o)]) - assert addressofo.buffer_info()[0] == pp.gime_address_ptr_ptr(addressofo) + addressofo = array.array('l', [cppyy.addressof(o)]) + assert addressofo[0] == pp.gime_address_ptr_ptr(addressofo) assert 0 == pp.gime_address_ptr(0) - assert 0 == pp.gime_address_ptr(None) + raises(TypeError, pp.gime_address_ptr, None) - ptr = _cppyy.bind_object(0, some_concrete_class) - assert _cppyy.addressof(ptr) == 0 + ptr = cppyy.bind_object(0, some_concrete_class) + assert cppyy.addressof(ptr) == 0 pp.set_address_ptr_ref(ptr) - assert _cppyy.addressof(ptr) == 0x1234 + assert cppyy.addressof(ptr) == 0x1234 pp.set_address_ptr_ptr(ptr) - assert _cppyy.addressof(ptr) == 0x4321 + assert cppyy.addressof(ptr) == 0x4321 + + assert cppyy.addressof(cppyy.nullptr) == 0 + raises(TypeError, cppyy.addressof, None) + assert cppyy.addressof(0) == 0 def test09_opaque_pointer_passing(self): """Test passing around of opaque pointers""" - import _cppyy - some_concrete_class = _cppyy.gbl.some_concrete_class + import _cppyy as cppyy + some_concrete_class = cppyy.gbl.some_concrete_class o = some_concrete_class() # TODO: figure out the PyPy equivalent of CObject (may have to do this # through the C-API from C++) - #cobj = _cppyy.as_cobject(o) - addr = _cppyy.addressof(o) + #cobj = cppyy.as_cobject(o) + addr = cppyy.addressof(o) - #assert o == _cppyy.bind_object(cobj, some_concrete_class) - #assert o == _cppyy.bind_object(cobj, type(o)) - #assert o == _cppyy.bind_object(cobj, o.__class__) - #assert o == _cppyy.bind_object(cobj, "some_concrete_class") - assert _cppyy.addressof(o) == _cppyy.addressof(_cppyy.bind_object(addr, some_concrete_class)) - assert o == _cppyy.bind_object(addr, some_concrete_class) - assert o == _cppyy.bind_object(addr, type(o)) - assert o == _cppyy.bind_object(addr, o.__class__) - assert o == _cppyy.bind_object(addr, "some_concrete_class") - raises(TypeError, _cppyy.bind_object, addr, "does_not_exist") - raises(TypeError, _cppyy.bind_object, addr, 1) + #assert o == cppyy.bind_object(cobj, some_concrete_class) + #assert o == cppyy.bind_object(cobj, type(o)) + #assert o == cppyy.bind_object(cobj, o.__class__) + #assert o == cppyy.bind_object(cobj, "some_concrete_class") + assert cppyy.addressof(o) == cppyy.addressof(cppyy.bind_object(addr, some_concrete_class)) + assert o == cppyy.bind_object(addr, some_concrete_class) + assert o == cppyy.bind_object(addr, type(o)) + assert o == cppyy.bind_object(addr, o.__class__) + assert o == cppyy.bind_object(addr, "some_concrete_class") + raises(TypeError, cppyy.bind_object, addr, "does_not_exist") + raises(TypeError, cppyy.bind_object, addr, 1) def test10_object_identity(self): """Test object identity""" - import _cppyy - some_concrete_class = _cppyy.gbl.some_concrete_class - some_class_with_data = _cppyy.gbl.some_class_with_data + import _cppyy as cppyy + some_concrete_class = cppyy.gbl.some_concrete_class + some_class_with_data = cppyy.gbl.some_class_with_data o = some_concrete_class() - addr = _cppyy.addressof(o) + addr = cppyy.addressof(o) - o2 = _cppyy.bind_object(addr, some_concrete_class) + o2 = cppyy.bind_object(addr, some_concrete_class) assert o is o2 - o3 = _cppyy.bind_object(addr, some_class_with_data) + o3 = cppyy.bind_object(addr, some_class_with_data) assert not o is o3 d1 = some_class_with_data() @@ -472,13 +476,13 @@ def test11_multi_methods(self): """Test calling of methods from multiple inheritance""" - import _cppyy - multi = _cppyy.gbl.multi + import _cppyy as cppyy + multi = cppyy.gbl.multi - assert _cppyy.gbl.multi1 is multi.__bases__[0] - assert _cppyy.gbl.multi2 is multi.__bases__[1] + assert cppyy.gbl.multi1 is multi.__bases__[0] + assert cppyy.gbl.multi2 is multi.__bases__[1] - dict_keys = multi.__dict__.keys() + dict_keys = list(multi.__dict__.keys()) assert dict_keys.count('get_my_own_int') == 1 assert dict_keys.count('get_multi1_int') == 0 assert dict_keys.count('get_multi2_int') == 0 @@ -491,9 +495,9 @@ def test12_actual_type(self): """Test that a pointer to base return does an auto-downcast""" - import _cppyy - base_class = _cppyy.gbl.base_class - derived_class = _cppyy.gbl.derived_class + import _cppyy as cppyy + base_class = cppyy.gbl.base_class + derived_class = cppyy.gbl.derived_class b = base_class() d = derived_class() @@ -520,85 +524,70 @@ assert not isinstance(voidp, base_class) assert not isinstance(voidp, derived_class) - d1 = _cppyy.bind_object(voidp, base_class, cast=True) + d1 = cppyy.bind_object(voidp, base_class, cast=True) assert isinstance(d1, derived_class) assert d1 is d - b1 = _cppyy.bind_object(voidp, base_class) + b1 = cppyy.bind_object(voidp, base_class) assert isinstance(b1, base_class) - assert _cppyy.addressof(b1) == _cppyy.addressof(d) + assert cppyy.addressof(b1) == cppyy.addressof(d) assert not (b1 is d) def test13_actual_type_virtual_multi(self): """Test auto-downcast in adverse inheritance situation""" - import _cppyy + import _cppyy as cppyy - c1 = _cppyy.gbl.create_c1() - assert type(c1) == _cppyy.gbl.c_class_1 + c1 = cppyy.gbl.create_c1() + assert type(c1) == cppyy.gbl.c_class_1 assert c1.m_c == 3 c1.__destruct__() - c2 = _cppyy.gbl.create_c2() - assert type(c2) == _cppyy.gbl.c_class_2 + c2 = cppyy.gbl.create_c2() + assert type(c2) == cppyy.gbl.c_class_2 assert c2.m_c == 3 c2.__destruct__() def test14_new_overloader(self): """Verify that class-level overloaded new/delete are called""" - import _cppyy + import _cppyy as cppyy - assert _cppyy.gbl.new_overloader.s_instances == 0 - nl = _cppyy.gbl.new_overloader() - assert _cppyy.gbl.new_overloader.s_instances == 1 + assert cppyy.gbl.new_overloader.s_instances == 0 + nl = cppyy.gbl.new_overloader() + assert cppyy.gbl.new_overloader.s_instances == 1 nl.__destruct__() import gc gc.collect() - assert _cppyy.gbl.new_overloader.s_instances == 0 + assert cppyy.gbl.new_overloader.s_instances == 0 def test15_template_instantiation_with_vector_of_float(self): """Test template instantiation with a std::vector""" - import _cppyy + import _cppyy as cppyy # the following will simply fail if there is a naming problem (e.g. # std::, allocator, etc., etc.); note the parsing required ... - b = _cppyy.gbl.my_templated_class(_cppyy.gbl.std.vector(float))() + b = cppyy.gbl.my_templated_class(cppyy.gbl.std.vector(float))() for i in range(5): b.m_b.push_back(i) assert round(b.m_b[i], 5) == float(i) - def test16_template_member_functions(self): - """Test template member functions lookup and calls""" - - import _cppyy - - m = _cppyy.gbl.my_templated_method_class() - - assert m.get_size('char')() == m.get_char_size() - assert m.get_size(int)() == m.get_int_size() - assert m.get_size(long)() == m.get_long_size() - assert m.get_size(float)() == m.get_float_size() - assert m.get_size('double')() == m.get_double_size() - assert m.get_size('my_templated_method_class')() == m.get_self_size() - assert m.get_size('my_typedef_t')() == m.get_self_size() - - def test17_template_global_functions(self): + def test16_template_global_functions(self): """Test template global function lookup and calls""" - import _cppyy + import _cppyy as cppyy - f = _cppyy.gbl.my_templated_function + f = cppyy.gbl.my_templated_function assert f('c') == 'c' assert type(f('c')) == type('c') assert f(3.) == 3. assert type(f(4.)) == type(4.) - def test18_assign_to_return_byref( self ): + def test17_assign_to_return_byref( self ): """Test assignment to an instance returned by reference""" from _cppyy import gbl @@ -614,7 +603,7 @@ # assert len(a) == 1 # assert a[0].m_i == 33 - def test19_math_converters(self): + def test18_math_converters(self): """Test operator int/long/double incl. typedef""" from _cppyy import gbl @@ -623,14 +612,14 @@ a.m_i = 1234 a.m_d = 4321. - assert int(a) == 1234 - assert int(a) == a.m_i - assert long(a) == a.m_i + assert int(a) == 1234 + assert int(a) == a.m_i + assert long(a) == a.m_i - assert float(a) == 4321. - assert float(a) == a.m_d + assert float(a) == 4321. + assert float(a) == a.m_d - def test20_comparator(self): + def test19_comparator(self): """Check that the global operator!=/== is picked up""" from _cppyy import gbl @@ -648,21 +637,22 @@ assert a.__eq__(a) == False assert b.__eq__(b) == False - def test21_overload_order_with_proper_return(self): + def test20_overload_order_with_proper_return(self): """Test return type against proper overload w/ const and covariance""" - import _cppyy + import _cppyy as cppyy - assert _cppyy.gbl.overload_one_way().gime() == 1 - assert _cppyy.gbl.overload_the_other_way().gime() == "aap" + assert cppyy.gbl.overload_one_way().gime() == 1 + assert cppyy.gbl.overload_the_other_way().gime() == "aap" - def test22_access_to_global_variables(self): + def test21_access_to_global_variables(self): """Access global_variables_and_pointers""" - import _cppyy + import _cppyy as cppyy - assert _cppyy.gbl.my_global_double == 12. - assert len(_cppyy.gbl.my_global_array) == 500 + assert cppyy.gbl.my_global_double == 12. + assert len(cppyy.gbl.my_global_array) == 500 + assert cppyy.gbl.my_global_string == "aap noot mies" # TODO: currently fails b/c double** not understood as &double* - #assert _cppyy.gbl.my_global_ptr[0] == 1234. + #assert cppyy.gbl.my_global_ptr[0] == 1234. diff --git a/pypy/module/_cppyy/test/test_cppyy.py b/pypy/module/_cppyy/test/test_cppyy.py --- a/pypy/module/_cppyy/test/test_cppyy.py +++ b/pypy/module/_cppyy/test/test_cppyy.py @@ -1,5 +1,4 @@ import py, os, sys -import subprocess from pypy.module._cppyy import interp_cppyy, executor from .support import setup_make @@ -165,8 +164,8 @@ e1 = self.instantiate(t, 7) assert t.get_overload("getCount").call(None) == 1 - assert e1._python_owns == True - e1._python_owns = False + assert e1.__python_owns__ == True + e1.__python_owns__ = False e1 = None gc.collect() assert t.get_overload("getCount").call(None) == 1 diff --git a/pypy/module/_cppyy/test/test_datatypes.py b/pypy/module/_cppyy/test/test_datatypes.py --- a/pypy/module/_cppyy/test/test_datatypes.py +++ b/pypy/module/_cppyy/test/test_datatypes.py @@ -21,8 +21,8 @@ def test01_instance_data_read_access(self): """Read access to instance public data and verify values""" - import _cppyy - CppyyTestData = _cppyy.gbl.CppyyTestData + import _cppyy as cppyy + CppyyTestData = cppyy.gbl.CppyyTestData c = CppyyTestData() assert isinstance(c, CppyyTestData) @@ -98,8 +98,8 @@ raises(IndexError, c.m_double_array.__getitem__, self.N) # can not access an instance member on the class - raises(ReferenceError, getattr, CppyyTestData, 'm_bool') - raises(ReferenceError, getattr, CppyyTestData, 'm_int') + raises(AttributeError, getattr, CppyyTestData, 'm_bool') + raises(AttributeError, getattr, CppyyTestData, 'm_int') assert not hasattr(CppyyTestData, 'm_bool') assert not hasattr(CppyyTestData, 'm_int') @@ -109,8 +109,8 @@ def test02_instance_data_write_access(self): """Test write access to instance public data and verify values""" - import _cppyy - CppyyTestData = _cppyy.gbl.CppyyTestData + import _cppyy as cppyy + CppyyTestData = cppyy.gbl.CppyyTestData c = CppyyTestData() assert isinstance(c, CppyyTestData) @@ -196,8 +196,8 @@ def test03_array_passing(self): """Test passing of array arguments""" - import _cppyy, array, sys - CppyyTestData = _cppyy.gbl.CppyyTestData + import _cppyy as cppyy, array, sys + CppyyTestData = cppyy.gbl.CppyyTestData c = CppyyTestData() assert isinstance(c, CppyyTestData) @@ -221,21 +221,20 @@ for i in range(self.N): assert ca[i] == b[i] - # NULL/None/nullptr passing (will use short*) + # NULL/nullptr passing (will use short*) assert not c.pass_array(0) raises(Exception, c.pass_array(0).__getitem__, 0) # raises SegfaultException - assert not c.pass_array(None) - raises(Exception, c.pass_array(None).__getitem__, 0) # id. - assert not c.pass_array(_cppyy.nullptr) - raises(Exception, c.pass_array(_cppyy.nullptr).__getitem__, 0) # id. id. + assert raises(TypeError, c.pass_array, None) + assert not c.pass_array(cppyy.nullptr) + raises(Exception, c.pass_array(cppyy.nullptr).__getitem__, 0) # id. id. c.__destruct__() def test04_class_read_access(self): """Test read access to class public data and verify values""" - import _cppyy, sys - CppyyTestData = _cppyy.gbl.CppyyTestData + import _cppyy as cppyy, sys + CppyyTestData = cppyy.gbl.CppyyTestData c = CppyyTestData() assert isinstance(c, CppyyTestData) @@ -275,8 +274,8 @@ def test05_class_data_write_access(self): """Test write access to class public data and verify values""" - import _cppyy, sys - CppyyTestData = _cppyy.gbl.CppyyTestData + import _cppyy as cppyy, sys + CppyyTestData = cppyy.gbl.CppyyTestData c = CppyyTestData() assert isinstance(c, CppyyTestData) @@ -339,8 +338,8 @@ def test06_range_access(self): """Test the ranges of integer types""" - import _cppyy, sys - CppyyTestData = _cppyy.gbl.CppyyTestData + import _cppyy as cppyy, sys + CppyyTestData = cppyy.gbl.CppyyTestData c = CppyyTestData() assert isinstance(c, CppyyTestData) @@ -355,8 +354,8 @@ def test07_type_conversions(self): """Test conversions between builtin types""" - import _cppyy, sys - CppyyTestData = _cppyy.gbl.CppyyTestData + import _cppyy as cppyy, sys + CppyyTestData = cppyy.gbl.CppyyTestData c = CppyyTestData() assert isinstance(c, CppyyTestData) @@ -373,8 +372,8 @@ def test08_global_builtin_type(self): """Test access to a global builtin type""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl assert gbl.g_int == gbl.get_global_int() @@ -389,8 +388,8 @@ def test09_global_ptr(self): """Test access of global objects through a pointer""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl raises(ReferenceError, 'gbl.g_pod.m_int') @@ -420,10 +419,10 @@ def test10_enum(self): """Test access to enums""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl - CppyyTestData = _cppyy.gbl.CppyyTestData + CppyyTestData = cppyy.gbl.CppyyTestData c = CppyyTestData() assert isinstance(c, CppyyTestData) @@ -468,8 +467,8 @@ def test11_string_passing(self): """Test passing/returning of a const char*""" - import _cppyy - CppyyTestData = _cppyy.gbl.CppyyTestData + import _cppyy as cppyy + CppyyTestData = cppyy.gbl.CppyyTestData c = CppyyTestData() assert c.get_valid_string('aap') == 'aap' @@ -478,8 +477,8 @@ def test12_copy_contructor(self): """Test copy constructor""" - import _cppyy - FourVector = _cppyy.gbl.FourVector + import _cppyy as cppyy + FourVector = cppyy.gbl.FourVector t1 = FourVector(1., 2., 3., -4.) t2 = FourVector(0., 0., 0., 0.) @@ -494,9 +493,9 @@ def test13_object_returns(self): """Test access to and return of PODs""" - import _cppyy + import _cppyy as cppyy - c = _cppyy.gbl.CppyyTestData() + c = cppyy.gbl.CppyyTestData() assert c.m_pod.m_int == 888 assert c.m_pod.m_double == 3.14 @@ -521,13 +520,13 @@ def test14_object_arguments(self): """Test setting and returning of a POD through arguments""" - import _cppyy + import _cppyy as cppyy - c = _cppyy.gbl.CppyyTestData() + c = cppyy.gbl.CppyyTestData() assert c.m_pod.m_int == 888 assert c.m_pod.m_double == 3.14 - p = _cppyy.gbl.CppyyTestPod() + p = cppyy.gbl.CppyyTestPod() p.m_int = 123 assert p.m_int == 123 p.m_double = 321. @@ -537,12 +536,12 @@ assert c.m_pod.m_int == 123 assert c.m_pod.m_double == 321. - c = _cppyy.gbl.CppyyTestData() + c = cppyy.gbl.CppyyTestData() c.set_pod_ptr_in(p) assert c.m_pod.m_int == 123 assert c.m_pod.m_double == 321. - c = _cppyy.gbl.CppyyTestData() + c = cppyy.gbl.CppyyTestData() c.set_pod_ptr_out(p) assert p.m_int == 888 assert p.m_double == 3.14 @@ -550,26 +549,26 @@ p.m_int = 555 p.m_double = 666. - c = _cppyy.gbl.CppyyTestData() + c = cppyy.gbl.CppyyTestData() c.set_pod_ref(p) assert c.m_pod.m_int == 555 assert c.m_pod.m_double == 666. - c = _cppyy.gbl.CppyyTestData() + c = cppyy.gbl.CppyyTestData() c.set_pod_ptrptr_in(p) assert c.m_pod.m_int == 555 assert c.m_pod.m_double == 666. assert p.m_int == 555 assert p.m_double == 666. - c = _cppyy.gbl.CppyyTestData() + c = cppyy.gbl.CppyyTestData() c.set_pod_void_ptrptr_in(p) assert c.m_pod.m_int == 555 assert c.m_pod.m_double == 666. assert p.m_int == 555 assert p.m_double == 666. - c = _cppyy.gbl.CppyyTestData() + c = cppyy.gbl.CppyyTestData() c.set_pod_ptrptr_out(p) assert c.m_pod.m_int == 888 assert c.m_pod.m_double == 3.14 @@ -579,7 +578,7 @@ p.m_int = 777 p.m_double = 888. - c = _cppyy.gbl.CppyyTestData() + c = cppyy.gbl.CppyyTestData() c.set_pod_void_ptrptr_out(p) assert c.m_pod.m_int == 888 assert c.m_pod.m_double == 3.14 @@ -587,12 +586,12 @@ assert p.m_double == 3.14 def test15_nullptr_passing(self): - """Integer 0 ('NULL') and None allowed to pass through instance*""" + """Integer 0 ('NULL') and nullptr allowed to pass through instance*""" - import _cppyy + import _cppyy as cppyy - for o in (0, None): - c = _cppyy.gbl.CppyyTestData() + for o in (0, cppyy.nullptr): + c = cppyy.gbl.CppyyTestData() assert c.m_pod.m_int == 888 assert c.m_pod.m_double == 3.14 assert not not c.m_ppod @@ -604,8 +603,8 @@ def test16_respect_privacy(self): """Test that privacy settings are respected""" - import _cppyy - CppyyTestData = _cppyy.gbl.CppyyTestData + import _cppyy as cppyy + CppyyTestData = cppyy.gbl.CppyyTestData c = CppyyTestData() assert isinstance(c, CppyyTestData) @@ -617,26 +616,26 @@ def test17_object_and_pointer_comparisons(self): """Verify object and pointer comparisons""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl - c1 = _cppyy.bind_object(0, gbl.CppyyTestData) + c1 = cppyy.bind_object(0, gbl.CppyyTestData) assert c1 == None assert None == c1 - c2 = _cppyy.bind_object(0, gbl.CppyyTestData) + c2 = cppyy.bind_object(0, gbl.CppyyTestData) assert c1 == c2 assert c2 == c1 # FourVector overrides operator== - l1 = _cppyy.bind_object(0, gbl.FourVector) + l1 = cppyy.bind_object(0, gbl.FourVector) assert l1 == None assert None == l1 assert c1 != l1 assert l1 != c1 - l2 = _cppyy.bind_object(0, gbl.FourVector) + l2 = cppyy.bind_object(0, gbl.FourVector) assert l1 == l2 assert l2 == l1 @@ -668,8 +667,8 @@ def test19_buffer_reshaping(self): """Test usage of buffer sizing""" - import _cppyy - CppyyTestData = _cppyy.gbl.CppyyTestData + import _cppyy as cppyy + CppyyTestData = cppyy.gbl.CppyyTestData c = CppyyTestData() for func in ['get_bool_array', 'get_bool_array2', @@ -680,43 +679,52 @@ 'get_ulong_array', 'get_ulong_array2']: arr = getattr(c, func)() arr = arr.shape.fromaddress(arr.itemaddress(0), self.N) + + """TODO: interface change ... + arr.reshape((self.N,)) + assert len(arr) == self.N + + raises(TypeError, arr.reshape, (1, 2)) + assert len(arr) == self.N + + raises(TypeError, arr.reshape, 2*self.N) assert len(arr) == self.N l = list(arr) for i in range(self.N): - assert arr[i] == l[i] + assert arr[i] == l[i]""" def test20_voidp(self): """Test usage of void* data""" - import _cppyy - CppyyTestData = _cppyy.gbl.CppyyTestData + import _cppyy as cppyy + CppyyTestData = cppyy.gbl.CppyyTestData c = CppyyTestData() - assert not _cppyy.nullptr + assert not cppyy.nullptr - assert c.s_voidp is _cppyy.nullptr - assert CppyyTestData.s_voidp is _cppyy.nullptr + assert c.s_voidp is cppyy.nullptr + assert CppyyTestData.s_voidp is cppyy.nullptr - assert c.m_voidp is _cppyy.nullptr - assert c.get_voidp() is _cppyy.nullptr + assert c.m_voidp is cppyy.nullptr + assert c.get_voidp() is cppyy.nullptr c2 = CppyyTestData() - assert c2.m_voidp is _cppyy.nullptr + assert c2.m_voidp is cppyy.nullptr c.set_voidp(c2.m_voidp) - assert c.m_voidp is _cppyy.nullptr + assert c.m_voidp is cppyy.nullptr c.set_voidp(c2.get_voidp()) - assert c.m_voidp is _cppyy.nullptr - c.set_voidp(_cppyy.nullptr) - assert c.m_voidp is _cppyy.nullptr + assert c.m_voidp is cppyy.nullptr + c.set_voidp(cppyy.nullptr) + assert c.m_voidp is cppyy.nullptr c.set_voidp(c2) def address_equality_test(a, b): - assert _cppyy.addressof(a) == _cppyy.addressof(b) - b2 = _cppyy.bind_object(a, CppyyTestData) + assert cppyy.addressof(a) == cppyy.addressof(b) + b2 = cppyy.bind_object(a, CppyyTestData) assert b is b2 # memory regulator recycles - b3 = _cppyy.bind_object(_cppyy.addressof(a), CppyyTestData) + b3 = cppyy.bind_object(cppyy.addressof(a), CppyyTestData) assert b is b3 # likewise address_equality_test(c.m_voidp, c2) @@ -724,8 +732,8 @@ def null_test(null): c.m_voidp = null - assert c.m_voidp is _cppyy.nullptr - map(null_test, [0, None, _cppyy.nullptr]) + assert c.m_voidp is cppyy.nullptr + map(null_test, [0, cppyy.nullptr]) c.m_voidp = c2 address_equality_test(c.m_voidp, c2) diff --git a/pypy/module/_cppyy/test/test_fragile.py b/pypy/module/_cppyy/test/test_fragile.py --- a/pypy/module/_cppyy/test/test_fragile.py +++ b/pypy/module/_cppyy/test/test_fragile.py @@ -71,7 +71,9 @@ e = fragile.E() raises(TypeError, e.overload, None) - raises(TypeError, getattr, e, 'm_pp_no_such') + # allowing access to e.m_pp_no_such is debatable, but it provides a raw pointer + # which may be useful ... + assert e.m_pp_no_such[0] == 0xdead def test04_wrong_arg_addressof(self): """Test addressof() error reporting""" @@ -90,10 +92,7 @@ _cppyy.addressof(f) raises(TypeError, _cppyy.addressof, o) raises(TypeError, _cppyy.addressof, 1) - # 0, None, and nullptr allowed - assert _cppyy.addressof(0) == 0 - assert _cppyy.addressof(None) == 0 - assert _cppyy.addressof(_cppyy.nullptr) == 0 + # see also test08_void_pointer_passing in test_advancedcpp.py def test05_wrong_this(self): """Test that using an incorrect self argument raises""" diff --git a/pypy/module/_cppyy/test/test_templates.py b/pypy/module/_cppyy/test/test_templates.py new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/test_templates.py @@ -0,0 +1,42 @@ +import py, os, sys +from .support import setup_make + + +currpath = py.path.local(__file__).dirpath() +test_dct = str(currpath.join("templatesDict.so")) + +def setup_module(mod): + setup_make("templatesDict.so") + +class AppTestTEMPLATES: + spaceconfig = dict(usemodules=['_cppyy', '_rawffi', 'itertools']) + + def setup_class(cls): + cls.w_test_dct = cls.space.newtext(test_dct) + cls.w_datatypes = cls.space.appexec([], """(): + import ctypes + return ctypes.CDLL(%r, ctypes.RTLD_GLOBAL)""" % (test_dct, )) + + def test01_template_member_functions(self): + """Template member functions lookup and calls""" + + import _cppyy + + m = _cppyy.gbl.MyTemplatedMethodClass() + + return + + # pre-instantiated + assert m.get_size['char']() == m.get_char_size() + assert m.get_size[int]() == m.get_int_size() + + # specialized + assert m.get_size[long]() == m.get_long_size() + + # auto-instantiation + assert m.get_size[float]() == m.get_float_size() + assert m.get_size['double']() == m.get_double_size() + assert m.get_size['MyTemplatedMethodClass']() == m.get_self_size() + + # auto through typedef + assert m.get_size['MyTMCTypedef_t']() == m.get_self_size() From pypy.commits at gmail.com Sat Oct 28 18:58:17 2017 From: pypy.commits at gmail.com (wlav) Date: Sat, 28 Oct 2017 15:58:17 -0700 (PDT) Subject: [pypy-commit] pypy cppyy-packaging: merge default into branch for testing Message-ID: <59f50b89.d1b8df0a.ddbfa.fa95@mx.google.com> Author: Wim Lavrijsen Branch: cppyy-packaging Changeset: r92871:766bf4f67f39 Date: 2017-10-28 15:47 -0700 http://bitbucket.org/pypy/pypy/changeset/766bf4f67f39/ Log: merge default into branch for testing 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/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/_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/_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) diff --git a/lib_pypy/cffi.egg-info/PKG-INFO b/lib_pypy/cffi.egg-info/PKG-INFO --- a/lib_pypy/cffi.egg-info/PKG-INFO +++ b/lib_pypy/cffi.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: cffi -Version: 1.11.1 +Version: 1.11.2 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski diff --git a/lib_pypy/cffi/__init__.py b/lib_pypy/cffi/__init__.py --- a/lib_pypy/cffi/__init__.py +++ b/lib_pypy/cffi/__init__.py @@ -4,8 +4,8 @@ from .api import FFI from .error import CDefError, FFIError, VerificationError, VerificationMissing -__version__ = "1.11.1" -__version_info__ = (1, 11, 1) +__version__ = "1.11.2" +__version_info__ = (1, 11, 2) # The verifier module file names are based on the CRC32 of a string that # contains the following version number. It may be older than __version__ diff --git a/lib_pypy/cffi/_cffi_include.h b/lib_pypy/cffi/_cffi_include.h --- a/lib_pypy/cffi/_cffi_include.h +++ b/lib_pypy/cffi/_cffi_include.h @@ -238,9 +238,9 @@ _CFFI_UNUSED_FN static PyObject *_cffi_from_c_char16_t(uint16_t x) { if (sizeof(_cffi_wchar_t) == 2) - return _cffi_from_c_wchar_t(x); + return _cffi_from_c_wchar_t((_cffi_wchar_t)x); else - return _cffi_from_c_wchar3216_t(x); + return _cffi_from_c_wchar3216_t((int)x); } _CFFI_UNUSED_FN static int _cffi_to_c_char32_t(PyObject *o) @@ -254,7 +254,7 @@ _CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(int x) { if (sizeof(_cffi_wchar_t) == 4) - return _cffi_from_c_wchar_t(x); + return _cffi_from_c_wchar_t((_cffi_wchar_t)x); else return _cffi_from_c_wchar3216_t(x); } diff --git a/lib_pypy/cffi/_embedding.h b/lib_pypy/cffi/_embedding.h --- a/lib_pypy/cffi/_embedding.h +++ b/lib_pypy/cffi/_embedding.h @@ -247,7 +247,7 @@ if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.11.1" + "\ncompiled with cffi version: 1.11.2" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); diff --git a/pypy/doc/build.rst b/pypy/doc/build.rst --- a/pypy/doc/build.rst +++ b/pypy/doc/build.rst @@ -119,7 +119,7 @@ To run untranslated tests, you need the Boehm garbage collector libgc. -On recent Debian and Ubuntu (like 17.04), this is the command to install +On recent Debian and Ubuntu (16.04 onwards), this is the command to install all build-time dependencies:: apt-get install gcc make libffi-dev pkg-config zlib1g-dev libbz2-dev \ @@ -127,7 +127,7 @@ tk-dev libgc-dev python-cffi \ liblzma-dev libncursesw5-dev # these two only needed on PyPy3 -On older Debian and Ubuntu (12.04 to 16.04):: +On older Debian and Ubuntu (12.04-14.04):: apt-get install gcc make libffi-dev pkg-config libz-dev libbz2-dev \ libsqlite3-dev libncurses-dev libexpat1-dev libssl-dev libgdbm-dev \ @@ -149,12 +149,23 @@ xz-devel # For lzma on PyPy3. (XXX plus the SLES11 version of libgdbm-dev and tk-dev) -On Mac OS X, most of these build-time dependencies are installed alongside +On Mac OS X:: + +Most of these build-time dependencies are installed alongside the Developer Tools. However, note that in order for the installation to find them you may need to run:: xcode-select --install +An exception is OpenSSL, which is no longer provided with the operating +system. It can be obtained via Homebrew (with ``$ brew install openssl``), +but it will not be available on the system path by default. The easiest +way to enable it for building pypy is to set an environment variable:: + + export PKG_CONFIG_PATH=$(brew --prefix)/opt/openssl/lib/pkgconfig + +After setting this, translation (described next) will find the OpenSSL libs +as expected. Run the translation ------------------- @@ -187,18 +198,18 @@ entire pypy interpreter. This step is currently singe threaded, and RAM hungry. As part of this step, the chain creates a large number of C code files and a Makefile to compile them in a - directory controlled by the ``PYPY_USESSION_DIR`` environment variable. + directory controlled by the ``PYPY_USESSION_DIR`` environment variable. 2. Create an executable ``pypy-c`` by running the Makefile. This step can - utilize all possible cores on the machine. -3. Copy the needed binaries to the current directory. -4. Generate c-extension modules for any cffi-based stdlib modules. + utilize all possible cores on the machine. +3. Copy the needed binaries to the current directory. +4. Generate c-extension modules for any cffi-based stdlib modules. The resulting executable behaves mostly like a normal Python interpreter (see :doc:`cpython_differences`), and is ready for testing, for use as a base interpreter for a new virtualenv, or for packaging into a binary suitable for installation on another machine running the same OS as the build -machine. +machine. Note that step 4 is merely done as a convenience, any of the steps may be rerun without rerunning the previous steps. @@ -255,7 +266,7 @@ * PyPy 2.5.1 or earlier: normal users would see permission errors. Installers need to run ``pypy -c "import gdbm"`` and other similar - commands at install time; the exact list is in + commands at install time; the exact list is in :source:`pypy/tool/release/package.py `. Users seeing a broken installation of PyPy can fix it after-the-fact if they have sudo rights, by running once e.g. ``sudo pypy -c "import gdbm``. 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/project-ideas.rst b/pypy/doc/project-ideas.rst --- a/pypy/doc/project-ideas.rst +++ b/pypy/doc/project-ideas.rst @@ -240,9 +240,12 @@ **matplotlib** https://github.com/matplotlib/matplotlib - TODO: the tkagg backend does not work, which makes tests fail on downstream - projects like Pandas, SciPy. It uses id(obj) as a c-pointer to obj in - tkagg.py, which requires refactoring + Status: using the matplotlib branch of PyPy and the tkagg-cffi branch of + matplotlib from https://github.com/mattip/matplotlib/tree/tkagg-cffi, the + tkagg backend can function. + + TODO: the matplotlib branch passes numpy arrays by value (copying all the + data), this proof-of-concept needs help to become completely compliant **wxPython** https://bitbucket.org/amauryfa/wxpython-cffi 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 @@ -5,4 +5,8 @@ .. this is a revision shortly after release-pypy2.7-v5.9.0 .. startrev:d56dadcef996 +.. branch: cppyy-packaging +Cleanup and improve cppyy packaging +.. branch: docs-osx-brew-openssl + diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py --- a/pypy/module/_cffi_backend/__init__.py +++ b/pypy/module/_cffi_backend/__init__.py @@ -3,7 +3,7 @@ from rpython.rlib import rdynload, clibffi from rpython.rtyper.lltypesystem import rffi -VERSION = "1.11.1" +VERSION = "1.11.2" FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI try: diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -1,7 +1,7 @@ # ____________________________________________________________ import sys -assert __version__ == "1.11.1", ("This test_c.py file is for testing a version" +assert __version__ == "1.11.2", ("This test_c.py file is for testing a version" " of cffi that differs from the one that we" " get from 'import _cffi_backend'") if sys.version_info < (3,): diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py --- a/pypy/module/_codecs/interp_codecs.py +++ b/pypy/module/_codecs/interp_codecs.py @@ -66,20 +66,17 @@ "position %d from error handler out of bounds", newpos) replace = space.unicode_w(w_replace) - return replace, newpos + if decode: + return replace, newpos + else: + return replace, None, newpos return call_errorhandler def make_decode_errorhandler(self, space): return self._make_errorhandler(space, True) def make_encode_errorhandler(self, space): - errorhandler = self._make_errorhandler(space, False) - def encode_call_errorhandler(errors, encoding, reason, input, startpos, - endpos): - replace, newpos = errorhandler(errors, encoding, reason, input, - startpos, endpos) - return replace, None, newpos - return encode_call_errorhandler + return self._make_errorhandler(space, False) def get_unicodedata_handler(self, space): if self.unicodedata_handler: 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 @@ -290,66 +290,87 @@ 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 # - def g(c): + def bar(c): c.switch(sys._getframe(0)) c.switch(sys._getframe(0).f_back) c.switch(sys._getframe(1)) c.switch(sys._getframe(1).f_back) - assert sys._getframe(2) is f3.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 + 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 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 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/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -577,6 +577,7 @@ 'PyComplex_AsCComplex', 'PyComplex_FromCComplex', 'PyObject_AsReadBuffer', 'PyObject_AsWriteBuffer', 'PyObject_CheckReadBuffer', + 'PyBuffer_GetPointer', 'PyBuffer_ToContiguous', 'PyBuffer_FromContiguous', 'PyOS_getsig', 'PyOS_setsig', 'PyThread_get_thread_ident', 'PyThread_allocate_lock', 'PyThread_free_lock', diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -258,14 +258,19 @@ if w_dict is None: return 0 if not space.isinstance_w(w_dict, space.w_dict): - return 0 + return 0 pos = ppos[0] py_obj = as_pyobj(space, w_dict) py_dict = rffi.cast(PyDictObject, py_obj) if pos == 0: # Store the current keys in the PyDictObject. + from pypy.objspace.std.listobject import W_ListObject decref(space, py_dict.c__tmpkeys) w_keys = space.call_method(space.w_dict, "keys", w_dict) + # w_keys must use the object strategy in order to keep the keys alive + if not isinstance(w_keys, W_ListObject): + return 0 # XXX should not call keys() above + w_keys.switch_to_object_strategy() py_dict.c__tmpkeys = create_ref(space, w_keys) Py_IncRef(space, py_dict.c__tmpkeys) else: @@ -278,10 +283,10 @@ decref(space, py_dict.c__tmpkeys) py_dict.c__tmpkeys = lltype.nullptr(PyObject.TO) return 0 - w_key = space.listview(w_keys)[pos] + w_key = space.listview(w_keys)[pos] # fast iff w_keys uses object strat w_value = space.getitem(w_dict, w_key) if pkey: - pkey[0] = as_pyobj(space, w_key) + pkey[0] = as_pyobj(space, w_key) if pvalue: pvalue[0] = as_pyobj(space, w_value) return 1 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 @@ -317,6 +317,31 @@ PyAPI_FUNC(int) PyObject_AsReadBuffer(PyObject *, const void **, Py_ssize_t *); PyAPI_FUNC(int) PyObject_AsWriteBuffer(PyObject *, void **, Py_ssize_t *); PyAPI_FUNC(int) PyObject_CheckReadBuffer(PyObject *); +PyAPI_FUNC(void *) PyBuffer_GetPointer(Py_buffer *view, Py_ssize_t *indices); +/* Get the memory area pointed to by the indices for the buffer given. + Note that view->ndim is the assumed size of indices +*/ + +PyAPI_FUNC(int) PyBuffer_ToContiguous(void *buf, Py_buffer *view, + Py_ssize_t len, char fort); +PyAPI_FUNC(int) PyBuffer_FromContiguous(Py_buffer *view, void *buf, + Py_ssize_t len, char fort); +/* Copy len bytes of data from the contiguous chunk of memory + pointed to by buf into the buffer exported by obj. Return + 0 on success and return -1 and raise a PyBuffer_Error on + error (i.e. the object does not have a buffer interface or + it is not working). + + If fort is 'F' and the object is multi-dimensional, + then the data will be copied into the array in + Fortran-style (first dimension varies the fastest). If + fort is 'C', then the data will be copied into the array + in C-style (last dimension varies the fastest). If fort + is 'A', then it does not matter and the copy will be made + in whatever way is more efficient. + +*/ + #define PyObject_MALLOC PyObject_Malloc #define PyObject_REALLOC PyObject_Realloc diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -15,7 +15,7 @@ from rpython.rlib.objectmodel import keepalive_until_here from rpython.rtyper.annlowlevel import llhelper from rpython.rlib import rawrefcount, jit -from rpython.rlib.debug import fatalerror +from rpython.rlib.debug import ll_assert, fatalerror #________________________________________________________ @@ -243,6 +243,11 @@ py_obj = rawrefcount.from_obj(PyObject, w_obj) if not py_obj: py_obj = create_ref(space, w_obj, w_userdata, immortal=immortal) + # + # Try to crash here, instead of randomly, if we don't keep w_obj alive + ll_assert(py_obj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY, + "Bug in cpyext: The W_Root object was garbage-collected " + "while being converted to PyObject.") return py_obj else: return lltype.nullptr(PyObject.TO) 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 @@ -13,7 +13,7 @@ ssizessizeargfunc, ssizeobjargproc, iternextfunc, initproc, richcmpfunc, cmpfunc, hashfunc, descrgetfunc, descrsetfunc, objobjproc, objobjargproc, readbufferproc, getbufferproc, ssizessizeobjargproc) -from pypy.module.cpyext.pyobject import make_ref, decref, from_ref +from pypy.module.cpyext.pyobject import make_ref, from_ref, as_pyobj from pypy.module.cpyext.pyerrors import PyErr_Occurred from pypy.module.cpyext.memoryobject import fill_Py_buffer from pypy.module.cpyext.state import State @@ -90,26 +90,29 @@ args_w = space.fixedview(w_args) return generic_cpy_call(space, func_binary, w_self, args_w[0]) +def _get_ob_type(space, w_obj): + # please ensure that w_obj stays alive + ob_type = as_pyobj(space, space.type(w_obj)) + return rffi.cast(PyTypeObjectPtr, ob_type) + def wrap_binaryfunc_l(space, w_self, w_args, func): func_binary = rffi.cast(binaryfunc, func) check_num_args(space, w_args, 1) args_w = space.fixedview(w_args) - ref = make_ref(space, w_self) - if (not ref.c_ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and + ob_type = _get_ob_type(space, w_self) + if (not ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and not space.issubtype_w(space.type(args_w[0]), space.type(w_self))): return space.w_NotImplemented - decref(space, ref) return generic_cpy_call(space, func_binary, w_self, args_w[0]) def wrap_binaryfunc_r(space, w_self, w_args, func): func_binary = rffi.cast(binaryfunc, func) check_num_args(space, w_args, 1) args_w = space.fixedview(w_args) - ref = make_ref(space, w_self) - if (not ref.c_ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and + ob_type = _get_ob_type(space, w_self) + if (not ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and not space.issubtype_w(space.type(args_w[0]), space.type(w_self))): return space.w_NotImplemented - decref(space, ref) return generic_cpy_call(space, func_binary, args_w[0], w_self) def wrap_ternaryfunc(space, w_self, w_args, func): @@ -127,11 +130,10 @@ func_ternary = rffi.cast(ternaryfunc, func) check_num_argsv(space, w_args, 1, 2) args_w = space.fixedview(w_args) - ref = make_ref(space, w_self) - if (not ref.c_ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and + ob_type = _get_ob_type(space, w_self) + if (not ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and not space.issubtype_w(space.type(args_w[0]), space.type(w_self))): return space.w_NotImplemented - decref(space, ref) arg3 = space.w_None if len(args_w) > 1: arg3 = args_w[1] @@ -323,12 +325,10 @@ def wrap_getreadbuffer(space, w_self, w_args, func): func_target = rffi.cast(readbufferproc, func) - py_obj = make_ref(space, w_self) - py_type = py_obj.c_ob_type + py_type = _get_ob_type(space, w_self) rbp = rffi.cast(rffi.VOIDP, 0) if py_type.c_tp_as_buffer: rbp = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer) - decref(space, py_obj) with lltype.scoped_alloc(rffi.VOIDPP.TO, 1) as ptr: index = rffi.cast(Py_ssize_t, 0) size = generic_cpy_call(space, func_target, w_self, index, ptr) @@ -341,9 +341,7 @@ def wrap_getwritebuffer(space, w_self, w_args, func): func_target = rffi.cast(readbufferproc, func) - py_obj = make_ref(space, w_self) - py_type = py_obj.c_ob_type - decref(space, py_obj) + py_type = _get_ob_type(space, w_self) rbp = rffi.cast(rffi.VOIDP, 0) if py_type.c_tp_as_buffer: rbp = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer) @@ -359,12 +357,10 @@ def wrap_getbuffer(space, w_self, w_args, func): func_target = rffi.cast(getbufferproc, func) - py_obj = make_ref(space, w_self) - py_type = py_obj.c_ob_type + py_type = _get_ob_type(space, w_self) rbp = rffi.cast(rffi.VOIDP, 0) if py_type.c_tp_as_buffer: rbp = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer) - decref(space, py_obj) with lltype.scoped_alloc(Py_buffer) as pybuf: _flags = 0 if space.len_w(w_args) > 0: diff --git a/pypy/module/cpyext/src/abstract.c b/pypy/module/cpyext/src/abstract.c --- a/pypy/module/cpyext/src/abstract.c +++ b/pypy/module/cpyext/src/abstract.c @@ -101,6 +101,163 @@ return 0; } +void* +PyBuffer_GetPointer(Py_buffer *view, Py_ssize_t *indices) +{ + char* pointer; + int i; + pointer = (char *)view->buf; + for (i = 0; i < view->ndim; i++) { + pointer += view->strides[i]*indices[i]; + if ((view->suboffsets != NULL) && (view->suboffsets[i] >= 0)) { + pointer = *((char**)pointer) + view->suboffsets[i]; + } + } + return (void*)pointer; +} + +void +_Py_add_one_to_index_F(int nd, Py_ssize_t *index, const Py_ssize_t *shape) +{ + int k; + + for (k=0; k=0; k--) { + if (index[k] < shape[k]-1) { + index[k]++; + break; + } + else { + index[k] = 0; + } + } +} + + /* view is not checked for consistency in either of these. It is + assumed that the size of the buffer is view->len in + view->len / view->itemsize elements. + */ + +int +PyBuffer_ToContiguous(void *buf, Py_buffer *view, Py_ssize_t len, char fort) +{ + int k; + void (*addone)(int, Py_ssize_t *, const Py_ssize_t *); + Py_ssize_t *indices, elements; + char *dest, *ptr; + + if (len > view->len) { + len = view->len; + } + + if (PyBuffer_IsContiguous(view, fort)) { + /* simplest copy is all that is needed */ + memcpy(buf, view->buf, len); + return 0; + } + + /* Otherwise a more elaborate scheme is needed */ + + /* view->ndim <= 64 */ + indices = (Py_ssize_t *)PyMem_Malloc(sizeof(Py_ssize_t)*(view->ndim)); + if (indices == NULL) { + PyErr_NoMemory(); + return -1; + } + for (k=0; kndim;k++) { + indices[k] = 0; + } + + if (fort == 'F') { + addone = _Py_add_one_to_index_F; + } + else { + addone = _Py_add_one_to_index_C; + } + dest = buf; + /* XXX : This is not going to be the fastest code in the world + several optimizations are possible. + */ + elements = len / view->itemsize; + while (elements--) { + ptr = PyBuffer_GetPointer(view, indices); + memcpy(dest, ptr, view->itemsize); + dest += view->itemsize; + addone(view->ndim, indices, view->shape); + } + PyMem_Free(indices); + return 0; +} + +int +PyBuffer_FromContiguous(Py_buffer *view, void *buf, Py_ssize_t len, char fort) +{ + int k; + void (*addone)(int, Py_ssize_t *, const Py_ssize_t *); + Py_ssize_t *indices, elements; + char *src, *ptr; + + if (len > view->len) { + len = view->len; + } + + if (PyBuffer_IsContiguous(view, fort)) { + /* simplest copy is all that is needed */ + memcpy(view->buf, buf, len); + return 0; + } + + /* Otherwise a more elaborate scheme is needed */ + + /* view->ndim <= 64 */ + indices = (Py_ssize_t *)PyMem_Malloc(sizeof(Py_ssize_t)*(view->ndim)); + if (indices == NULL) { + PyErr_NoMemory(); + return -1; + } + for (k=0; kndim;k++) { + indices[k] = 0; + } + + if (fort == 'F') { + addone = _Py_add_one_to_index_F; + } + else { + addone = _Py_add_one_to_index_C; + } + src = buf; + /* XXX : This is not going to be the fastest code in the world + several optimizations are possible. + */ + elements = len / view->itemsize; + while (elements--) { + ptr = PyBuffer_GetPointer(view, indices); + memcpy(ptr, src, view->itemsize); + src += view->itemsize; + addone(view->ndim, indices, view->shape); + } + + PyMem_Free(indices); + return 0; +} + + + /* Buffer C-API for Python 3.0 */ int diff --git a/pypy/module/cpyext/test/test_memoryobject.py b/pypy/module/cpyext/test/test_memoryobject.py --- a/pypy/module/cpyext/test/test_memoryobject.py +++ b/pypy/module/cpyext/test/test_memoryobject.py @@ -115,7 +115,36 @@ view = PyMemoryView_GET_BUFFER(memoryview); Py_DECREF(memoryview); return PyLong_FromLong(view->len / view->itemsize); - """)]) + """), + ("test_contiguous", "METH_O", + """ + Py_buffer* view; + PyObject * memoryview; + void * buf = NULL; + int ret; + Py_ssize_t len; + memoryview = PyMemoryView_FromObject(args); + if (memoryview == NULL) + return NULL; + view = PyMemoryView_GET_BUFFER(memoryview); + Py_DECREF(memoryview); + len = view->len; + if (len == 0) + return NULL; + buf = malloc(len); + ret = PyBuffer_ToContiguous(buf, view, view->len, 'A'); + if (ret != 0) + { + free(buf); + return NULL; + } + ret = PyBuffer_FromContiguous(view, buf, view->len, 'A'); + free(buf); + if (ret != 0) + return NULL; + Py_RETURN_NONE; + """), + ]) module = self.import_module(name='buffer_test') arr = module.PyMyArray(10) ten = foo.get_len(arr) @@ -124,6 +153,7 @@ assert ten == 10 ten = foo.test_buffer(arr) assert ten == 10 + foo.test_contiguous(arr) def test_releasebuffer(self): module = self.import_extension('foo', [ diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py --- a/pypy/module/cpyext/test/test_typeobject.py +++ b/pypy/module/cpyext/test/test_typeobject.py @@ -353,12 +353,8 @@ PyObject* name = PyString_FromString("mymodule"); PyObject *obj = PyType_Type.tp_alloc(&PyType_Type, 0); PyHeapTypeObject *type = (PyHeapTypeObject*)obj; - if ((type->ht_type.tp_flags & Py_TPFLAGS_HEAPTYPE) == 0) - { - PyErr_SetString(PyExc_ValueError, - "Py_TPFLAGS_HEAPTYPE not set"); - return NULL; - } + /* this is issue #2434: logic from pybind11 */ + type->ht_type.tp_flags |= Py_TPFLAGS_HEAPTYPE; type->ht_type.tp_name = ((PyTypeObject*)args)->tp_name; PyType_Ready(&type->ht_type); ret = PyObject_SetAttrString((PyObject*)&type->ht_type, diff --git a/pypy/module/pyexpat/interp_pyexpat.py b/pypy/module/pyexpat/interp_pyexpat.py --- a/pypy/module/pyexpat/interp_pyexpat.py +++ b/pypy/module/pyexpat/interp_pyexpat.py @@ -808,7 +808,7 @@ w_encoding) if space.is_none(w_namespace_separator): - namespace_separator = 0 + namespace_separator = -1 elif space.isinstance_w(w_namespace_separator, space.w_text): separator = space.text_w(w_namespace_separator) if len(separator) == 0: @@ -831,7 +831,7 @@ elif space.is_w(w_intern, space.w_None): w_intern = None - if namespace_separator: + if namespace_separator >= 0: xmlparser = XML_ParserCreateNS( encoding, rffi.cast(rffi.CHAR, namespace_separator)) diff --git a/pypy/module/pyexpat/test/test_parser.py b/pypy/module/pyexpat/test/test_parser.py --- a/pypy/module/pyexpat/test/test_parser.py +++ b/pypy/module/pyexpat/test/test_parser.py @@ -59,7 +59,7 @@ p.CharacterDataHandler = lambda s: data.append(s) encoding = encoding_arg is None and 'utf-8' or encoding_arg - res = p.Parse(u"\u00f6".encode(encoding), isfinal=True) + res = p.Parse(u"\u00f6".encode(encoding), True) assert res == 1 assert data == [u"\u00f6"] @@ -188,6 +188,34 @@ p.ParseFile(fake_reader) assert fake_reader.read_count == 4 + def test_entities(self): + import pyexpat + parser = pyexpat.ParserCreate(None, "") + + def startElement(tag, attrs): + assert tag == 'http://www.w3.org/1999/02/22-rdf-syntax-ns#RDF' + assert attrs == { + 'http://www.w3.org/XML/1998/namespacebase': + 'http://www.semanticweb.org/jiba/ontologies/2017/0/test'} + parser.StartElementHandler = startElement + parser.Parse(""" + + + + + + ]> + + + + """, True) + class AppTestPyexpat2: spaceconfig = dict(usemodules=['pyexpat', 'itertools', '_socket', 'time', 'struct', 'binascii']) diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_version.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_version.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_version.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_version.py @@ -37,7 +37,7 @@ v = cffi.__version__.replace('+', '') p = os.path.join(parent, 'doc', 'source', 'installation.rst') content = open(p).read() - assert ("/cffi-%s.tar.gz" % v) in content + assert (" package version %s:" % v) in content def test_setup_version(): parent = os.path.dirname(os.path.dirname(cffi.__file__)) diff --git a/pypy/module/test_lib_pypy/test_sqlite3.py b/pypy/module/test_lib_pypy/test_sqlite3.py --- a/pypy/module/test_lib_pypy/test_sqlite3.py +++ b/pypy/module/test_lib_pypy/test_sqlite3.py @@ -228,6 +228,14 @@ cur.execute("create table test(a)") cur.executemany("insert into test values (?)", [[1], [2], [3]]) assert cur.lastrowid is None + # issue 2682 + cur.execute('''insert + into test + values (?) + ''', (1, )) + assert cur.lastrowid is not None + cur.execute('''insert\t into test values (?) ''', (1, )) + assert cur.lastrowid is not None def test_authorizer_bad_value(self, con): def authorizer_cb(action, arg1, arg2, dbname, source): diff --git a/pypy/module/unicodedata/test/test_hyp.py b/pypy/module/unicodedata/test/test_hyp.py --- a/pypy/module/unicodedata/test/test_hyp.py +++ b/pypy/module/unicodedata/test/test_hyp.py @@ -40,6 +40,7 @@ @pytest.mark.parametrize('NF1, NF2, NF3', compositions) @example(s=u'---\uafb8\u11a7---') # issue 2289 + at example(s=u'\ufacf') @settings(max_examples=1000) @given(s=st.text()) def test_composition(s, space, NF1, NF2, NF3): diff --git a/pypy/objspace/std/stringmethods.py b/pypy/objspace/std/stringmethods.py --- a/pypy/objspace/std/stringmethods.py +++ b/pypy/objspace/std/stringmethods.py @@ -633,13 +633,16 @@ def _startswith(self, space, value, w_prefix, start, end): prefix = self._op_val(space, w_prefix) - if start > len(value): - return self._starts_ends_overflow(prefix) + if self._starts_ends_unicode: # bug-to-bug compat + if len(prefix) == 0: + return True + else: + if start > len(value): + return False return startswith(value, prefix, start, end) - def _starts_ends_overflow(self, prefix): - return False # bug-to-bug compat: this is for strings and - # bytearrays, but overridden for unicodes + _starts_ends_unicode = False # bug-to-bug compat: this is for strings and + # bytearrays, but overridden for unicodes def descr_endswith(self, space, w_suffix, w_start=None, w_end=None): value, start, end, _ = self._convert_idx_params(space, w_start, w_end) @@ -656,8 +659,12 @@ def _endswith(self, space, value, w_prefix, start, end): prefix = self._op_val(space, w_prefix) - if start > len(value): - return self._starts_ends_overflow(prefix) + if self._starts_ends_unicode: # bug-to-bug compat + if len(prefix) == 0: + return True + else: + if start > len(value): + return False return endswith(value, prefix, start, end) def _strip(self, space, w_chars, left, right, name='strip'): diff --git a/pypy/objspace/std/test/test_bytesobject.py b/pypy/objspace/std/test/test_bytesobject.py --- a/pypy/objspace/std/test/test_bytesobject.py +++ b/pypy/objspace/std/test/test_bytesobject.py @@ -1,3 +1,6 @@ +from pypy.interpreter.error import OperationError + + class TestW_BytesObject: def teardown_method(self, method): @@ -86,6 +89,78 @@ w_bytes = self.space.newbytes('abcd') assert self.space.listview_bytes(w_bytes) == list("abcd") + +try: + from hypothesis import given, strategies +except ImportError: + pass +else: + @given(u=strategies.binary(), + start=strategies.integers(min_value=0, max_value=10), + len1=strategies.integers(min_value=-1, max_value=10)) + def test_hypo_index_find(u, start, len1, space): + if start + len1 < 0: + return # skip this case + v = u[start : start + len1] + w_u = space.wrap(u) + w_v = space.wrap(v) + expected = u.find(v, start, start + len1) + try: + w_index = space.call_method(w_u, 'index', w_v, + space.newint(start), + space.newint(start + len1)) + except OperationError as e: + if not e.match(space, space.w_ValueError): + raise + assert expected == -1 + else: + assert space.int_w(w_index) == expected >= 0 + + w_index = space.call_method(w_u, 'find', w_v, + space.newint(start), + space.newint(start + len1)) + assert space.int_w(w_index) == expected + + rexpected = u.rfind(v, start, start + len1) + try: + w_index = space.call_method(w_u, 'rindex', w_v, + space.newint(start), + space.newint(start + len1)) + except OperationError as e: + if not e.match(space, space.w_ValueError): + raise + assert rexpected == -1 + else: + assert space.int_w(w_index) == rexpected >= 0 + + w_index = space.call_method(w_u, 'rfind', w_v, + space.newint(start), + space.newint(start + len1)) + assert space.int_w(w_index) == rexpected + + expected = u.startswith(v, start) + w_res = space.call_method(w_u, 'startswith', w_v, + space.newint(start)) + assert w_res is space.newbool(expected) + + expected = u.startswith(v, start, start + len1) + w_res = space.call_method(w_u, 'startswith', w_v, + space.newint(start), + space.newint(start + len1)) + assert w_res is space.newbool(expected) + + expected = u.endswith(v, start) + w_res = space.call_method(w_u, 'endswith', w_v, + space.newint(start)) + assert w_res is space.newbool(expected) + + expected = u.endswith(v, start, start + len1) + w_res = space.call_method(w_u, 'endswith', w_v, + space.newint(start), + space.newint(start + len1)) + assert w_res is space.newbool(expected) + + class AppTestBytesObject: def test_format_wrongchar(self): diff --git a/pypy/objspace/std/test/test_unicodeobject.py b/pypy/objspace/std/test/test_unicodeobject.py --- a/pypy/objspace/std/test/test_unicodeobject.py +++ b/pypy/objspace/std/test/test_unicodeobject.py @@ -1,5 +1,6 @@ import py import sys +from pypy.interpreter.error import OperationError class TestUnicodeObject: @@ -33,6 +34,77 @@ assert w_new is w_uni +try: + from hypothesis import given, strategies +except ImportError: + pass +else: + @given(u=strategies.text(), + start=strategies.integers(min_value=0, max_value=10), + len1=strategies.integers(min_value=-1, max_value=10)) + def test_hypo_index_find(u, start, len1, space): + if start + len1 < 0: + return # skip this case + v = u[start : start + len1] + w_u = space.wrap(u) + w_v = space.wrap(v) + expected = u.find(v, start, start + len1) + try: + w_index = space.call_method(w_u, 'index', w_v, + space.newint(start), + space.newint(start + len1)) + except OperationError as e: + if not e.match(space, space.w_ValueError): + raise + assert expected == -1 + else: + assert space.int_w(w_index) == expected >= 0 + + w_index = space.call_method(w_u, 'find', w_v, + space.newint(start), + space.newint(start + len1)) + assert space.int_w(w_index) == expected + + rexpected = u.rfind(v, start, start + len1) + try: + w_index = space.call_method(w_u, 'rindex', w_v, + space.newint(start), + space.newint(start + len1)) + except OperationError as e: + if not e.match(space, space.w_ValueError): + raise + assert rexpected == -1 + else: + assert space.int_w(w_index) == rexpected >= 0 + + w_index = space.call_method(w_u, 'rfind', w_v, + space.newint(start), + space.newint(start + len1)) + assert space.int_w(w_index) == rexpected + + expected = u.startswith(v, start) + w_res = space.call_method(w_u, 'startswith', w_v, + space.newint(start)) + assert w_res is space.newbool(expected) + + expected = u.startswith(v, start, start + len1) + w_res = space.call_method(w_u, 'startswith', w_v, + space.newint(start), + space.newint(start + len1)) + assert w_res is space.newbool(expected) + + expected = u.endswith(v, start) + w_res = space.call_method(w_u, 'endswith', w_v, + space.newint(start)) + assert w_res is space.newbool(expected) + + expected = u.endswith(v, start, start + len1) + w_res = space.call_method(w_u, 'endswith', w_v, + space.newint(start), + space.newint(start + len1)) + assert w_res is space.newbool(expected) + + class AppTestUnicodeStringStdOnly: def test_compares(self): assert u'a' == 'a' diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -419,8 +419,7 @@ cased = True return space.newbool(cased) - def _starts_ends_overflow(self, prefix): - return len(prefix) == 0 + _starts_ends_unicode = True def wrapunicode(space, uni): diff --git a/rpython/doc/jit/optimizer.rst b/rpython/doc/jit/optimizer.rst --- a/rpython/doc/jit/optimizer.rst +++ b/rpython/doc/jit/optimizer.rst @@ -42,10 +42,9 @@ There are better ways to compute the sum from ``[0..100]``, but it gives a better intuition on how traces are constructed than ``sum(range(101))``. Note that the trace syntax is the one used in the test suite. It is also very -similar to traces printed at runtime by PYPYLOG_. The first line gives the input variables, the -second line is a ``label`` operation, the last one is the backwards ``jump`` operation. - -.. _PYPYLOG: logging.html +similar to traces printed at runtime by :doc:`PYPYLOG <../logging>`. The first +line gives the input variables, the second line is a ``label`` operation, the +last one is the backwards ``jump`` operation. These instructions mentioned earlier are special: diff --git a/rpython/jit/metainterp/optimizeopt/intbounds.py b/rpython/jit/metainterp/optimizeopt/intbounds.py --- a/rpython/jit/metainterp/optimizeopt/intbounds.py +++ b/rpython/jit/metainterp/optimizeopt/intbounds.py @@ -305,6 +305,10 @@ # Transform into INT_ADD. The following guard will be killed # by optimize_GUARD_NO_OVERFLOW; if we see instead an # optimize_GUARD_OVERFLOW, then InvalidLoop. + + # NB: this case also takes care of int_add_ovf with 0 as on of the + # arguments: the result will be bounded, and then the optimization + # for int_add with 0 as argument will remove the op. op = self.replace_op_with(op, rop.INT_ADD) return self.emit(op) @@ -325,6 +329,7 @@ return None resbound = b0.sub_bound(b1) if resbound.bounded(): + # this case takes care of int_sub_ovf(x, 0) as well op = self.replace_op_with(op, rop.INT_SUB) return self.emit(op) @@ -342,6 +347,7 @@ b2 = self.getintbound(op.getarg(1)) resbound = b1.mul_bound(b2) if resbound.bounded(): + # this case also takes care of multiplication with 0 and 1 op = self.replace_op_with(op, rop.INT_MUL) return self.emit(op) diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py --- a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py +++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py @@ -1962,6 +1962,55 @@ """ self.optimize_loop(ops, expected) + ops = """ + [i0] + i1 = int_mul_ovf(0, i0) + guard_no_overflow() [] + jump(i1) + """ + expected = """ + [i0] + jump(0) + """ + self.optimize_loop(ops, expected) + + ops = """ + [i0] + i1 = int_mul_ovf(i0, 0) + guard_no_overflow() [] + jump(i1) + """ + expected = """ + [i0] + jump(0) + """ + self.optimize_loop(ops, expected) + + ops = """ + [i0] + i1 = int_mul_ovf(1, i0) + guard_no_overflow() [] + jump(i1) + """ + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected) + + ops = """ + [i0] + i1 = int_mul_ovf(i0, 1) + guard_no_overflow() [] + jump(i1) + """ + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_fold_constant_partial_ops_float(self): ops = """ [f0] diff --git a/rpython/rlib/runicode.py b/rpython/rlib/runicode.py --- a/rpython/rlib/runicode.py +++ b/rpython/rlib/runicode.py @@ -877,32 +877,31 @@ ch = ord(s[pos]) pos += 1 ch2 = 0 - if 0xD800 <= ch < 0xDC00: - if not allow_surrogates: - ru, rs, pos = errorhandler(errors, public_encoding_name, - 'surrogates not allowed', - s, pos-1, pos) - if rs is not None: - # py3k only - if len(rs) % 4 != 0: - errorhandler('strict', public_encoding_name, - 'surrogates not allowed', - s, pos-1, pos) - result.append(rs) - continue - for ch in ru: - if ord(ch) < 0xD800: - _STORECHAR32(result, ord(ch), byteorder) - else: - errorhandler('strict', public_encoding_name, - 'surrogates not allowed', - s, pos-1, pos) + if not allow_surrogates and 0xD800 <= ch < 0xE000: + ru, rs, pos = errorhandler(errors, public_encoding_name, + 'surrogates not allowed', + s, pos-1, pos) + if rs is not None: + # py3k only + if len(rs) % 4 != 0: + errorhandler('strict', public_encoding_name, + 'surrogates not allowed', + s, pos-1, pos) + result.append(rs) continue - elif MAXUNICODE < 65536 and pos < size: - ch2 = ord(s[pos]) - if 0xDC00 <= ch2 < 0xE000: - ch = (((ch & 0x3FF)<<10) | (ch2 & 0x3FF)) + 0x10000; - pos += 1 + for ch in ru: + if ord(ch) < 0xD800: + _STORECHAR32(result, ord(ch), byteorder) + else: + errorhandler('strict', public_encoding_name, + 'surrogates not allowed', + s, pos-1, pos) + continue + if 0xD800 <= ch < 0xDC00 and MAXUNICODE < 65536 and pos < size: + ch2 = ord(s[pos]) + if 0xDC00 <= ch2 < 0xE000: + ch = (((ch & 0x3FF)<<10) | (ch2 & 0x3FF)) + 0x10000; + pos += 1 _STORECHAR32(result, ch, byteorder) return result.build() diff --git a/rpython/rlib/test/test_runicode.py b/rpython/rlib/test/test_runicode.py --- a/rpython/rlib/test/test_runicode.py +++ b/rpython/rlib/test/test_runicode.py @@ -2,6 +2,7 @@ import py import sys, random +import struct from rpython.rlib import runicode from hypothesis import given, settings, strategies @@ -266,11 +267,12 @@ assert replace_with(u'rep', None) == '\x00<\x00r\x00e\x00p\x00>' assert replace_with(None, '\xca\xfe') == '\x00<\xca\xfe\x00>' - def test_utf32_surrogates(self): + @py.test.mark.parametrize('unich',[u"\ud800", u"\udc80"]) + def test_utf32_surrogates(self, unich): assert runicode.unicode_encode_utf_32_be( - u"\ud800", 1, None) == '\x00\x00\xd8\x00' + unich, 1, None) == struct.pack('>i', ord(unich)) py.test.raises(UnicodeEncodeError, runicode.unicode_encode_utf_32_be, - u"\ud800", 1, None, allow_surrogates=False) + unich, 1, None, allow_surrogates=False) def replace_with(ru, rs): def errorhandler(errors, enc, msg, u, startingpos, endingpos): if errors == 'strict': @@ -278,7 +280,7 @@ endingpos, msg) return ru, rs, endingpos return runicode.unicode_encode_utf_32_be( - u"<\ud800>", 3, None, + u"<%s>" % unich, 3, None, errorhandler, allow_surrogates=False) assert replace_with(u'rep', None) == u''.encode('utf-32-be') assert replace_with(None, '\xca\xfe\xca\xfe') == '\x00\x00\x00<\xca\xfe\xca\xfe\x00\x00\x00>' @@ -432,7 +434,7 @@ assert (self.decoder('aaaa' + seq + 'bbbb', len(seq) + 8, 'ignore', final=True) == (u'aaaabbbb', len(seq) + 8)) assert (self.decoder(seq, len(seq), 'custom', final=True, - errorhandler=self.custom_replace) == + errorhandler=self.custom_replace) == (FOO * len(seq), len(seq))) assert (self.decoder('aaaa' + seq + 'bbbb', len(seq) + 8, 'custom', final=True, errorhandler=self.custom_replace) == @@ -628,7 +630,7 @@ msg='invalid continuation byte') assert self.decoder(seq, len(seq), 'replace', final=True ) == (res, len(seq)) - assert (self.decoder('aaaa' + seq + 'bbbb', len(seq) + 8, + assert (self.decoder('aaaa' + seq + 'bbbb', len(seq) + 8, 'replace', final=True) == (u'aaaa' + res + u'bbbb', len(seq) + 8)) res = res.replace(FFFD, u'') From pypy.commits at gmail.com Sat Oct 28 19:05:28 2017 From: pypy.commits at gmail.com (wlav) Date: Sat, 28 Oct 2017 16:05:28 -0700 (PDT) Subject: [pypy-commit] pypy default: merge cppyy-packaging with improved consistency for cppyy CPython <-> PyPy Message-ID: <59f50d38.06b6df0a.1db93.99c6@mx.google.com> Author: Wim Lavrijsen Branch: Changeset: r92872:0b8528722439 Date: 2017-10-28 15:55 -0700 http://bitbucket.org/pypy/pypy/changeset/0b8528722439/ Log: merge cppyy-packaging with improved consistency for cppyy CPython <-> PyPy diff too long, truncating to 2000 out of 3147 lines 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 - at unwrap_spec(name='text') -def resolve_name(space, name): - return space.newtext(capi.c_resolve_name(space, name)) + at unwrap_spec(scoped_name='text') +def resolve_name(space, scoped_name): + return space.newtext(capi.c_resolve_name(space, scoped_name)) - at 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 + at 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 - at 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 + at 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) + + at 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.""" + w_clsdecl = space.findattr(w_pycppclass, space.newtext("__cppdecl__")) + if not w_clsdecl: + w_clsdecl = scope_byname(space, space.text_w(w_pycppclass)) + if not w_clsdecl: raise oefmt(space.w_TypeError, "no such class: %s", space.text_w(w_pycppclass)) - cppclass = space.interp_w(W_CPPClassDecl, w_cppclass, can_be_None=False) - return wrap_cppobject(space, rawobject, cppclass, do_cast=cast, python_owns=owns) + return _bind_object(space, w_obj, w_clsdecl, owns, cast) + +def move(space, w_obj): + """Casts the given instance into an C++-style rvalue.""" + obj = space.interp_w(W_CPPClass, w_obj, can_be_None=True) + if obj: + obj.flags |= INSTANCE_FLAGS_IS_R_VALUE + return w_obj diff --git a/pypy/module/_cppyy/pythonify.py b/pypy/module/_cppyy/pythonify.py --- a/pypy/module/_cppyy/pythonify.py +++ b/pypy/module/_cppyy/pythonify.py @@ -10,7 +10,7 @@ class CPPMetaScope(type): def __getattr__(self, name): try: - return get_pycppitem(self, name) # will cache on self + return get_scoped_pycppitem(self, name) # will cache on self except Exception as e: raise AttributeError("%s object has no attribute '%s' (details: %s)" % (self, name, str(e))) @@ -36,11 +36,14 @@ self._scope = scope def _arg_to_str(self, arg): - if arg == str: - import _cppyy - arg = _cppyy._std_string_name() - elif type(arg) != str: - arg = arg.__name__ + try: + arg = arg.__cppname__ + except AttributeError: + if arg == str: + import _cppyy + arg = _cppyy._std_string_name() + elif type(arg) != str: + arg = arg.__name__ return arg def __call__(self, *args): @@ -58,8 +61,36 @@ return self.__call__(*args) -def clgen_callback(name): - return get_pycppclass(name) +def scope_splitter(name): + is_open_template, scope = 0, "" + for c in name: + if c == ':' and not is_open_template: + if scope: + yield scope + scope = "" + continue + elif c == '<': + is_open_template += 1 + elif c == '>': + is_open_template -= 1 + scope += c + yield scope + +def get_pycppitem(final_scoped_name): + # walk scopes recursively down from global namespace ("::") to get the + # actual (i.e. not typedef'ed) class, triggering all necessary creation + scope = gbl + for name in scope_splitter(final_scoped_name): + scope = getattr(scope, name) + return scope +get_pycppclass = get_pycppitem # currently no distinction, but might + # in future for performance + + +# callbacks (originating from interp_cppyy.py) to allow interp-level to +# initiate creation of app-level classes and function +def clgen_callback(final_scoped_name): + return get_pycppclass(final_scoped_name) def fngen_callback(func, npar): # todo, some kind of arg transform spec if npar == 0: @@ -75,20 +106,19 @@ return wrapper +# construction of namespaces and classes, and their helpers +def make_module_name(scope): + if scope: + return scope.__module__ + '.' + scope.__name__ + return 'cppyy' + def make_static_function(func_name, cppol): def function(*args): return cppol.call(None, *args) function.__name__ = func_name - function.__doc__ = cppol.signature() + function.__doc__ = cppol.prototype() return staticmethod(function) -def make_method(meth_name, cppol): - def method(self, *args): - return cppol.call(self, *args) - method.__name__ = meth_name - method.__doc__ = cppol.signature() - return method - def make_cppnamespace(scope, name, decl): # build up a representation of a C++ namespace (namespaces are classes) @@ -98,20 +128,19 @@ ns_meta = type(name+'_meta', (CPPMetaNamespace,), {}) # create the python-side C++ namespace representation, cache in scope if given - d = {"__cppdecl__" : decl, "__cppname__" : decl.__cppname__ } + d = {"__cppdecl__" : decl, + "__module__" : make_module_name(scope), + "__cppname__" : decl.__cppname__ } pyns = ns_meta(name, (CPPNamespace,), d) if scope: setattr(scope, name, pyns) # install as modules to allow importing from (note naming: cppyy) - modname = 'cppyy.gbl' - if scope: - modname = 'cppyy.gbl.'+pyns.__cppname__.replace('::', '.') - sys.modules[modname] = pyns + sys.modules[make_module_name(pyns)] = pyns return pyns def _drop_cycles(bases): - # TODO: figure this out, as it seems to be a PyPy bug?! + # TODO: figure out why this is necessary? for b1 in bases: for b2 in bases: if not (b1 is b2) and issubclass(b2, b1): @@ -119,27 +148,37 @@ break return tuple(bases) -def make_new(class_name): + +def make_new(decl): def __new__(cls, *args): # create a place-holder only as there may be a derived class defined + # TODO: get rid of the import and add user-land bind_object that uses + # _bind_object (see interp_cppyy.py) import _cppyy - instance = _cppyy.bind_object(0, class_name, True) + instance = _cppyy._bind_object(0, decl, True) if not instance.__class__ is cls: instance.__class__ = cls # happens for derived class return instance return __new__ -def make_cppclass(scope, class_name, final_class_name, decl): +def make_method(meth_name, cppol): + def method(self, *args): + return cppol.call(self, *args) + method.__name__ = meth_name + method.__doc__ = cppol.prototype() + return method + +def make_cppclass(scope, cl_name, decl): # get a list of base classes for class creation bases = [get_pycppclass(base) for base in decl.get_base_names()] if not bases: bases = [CPPClass,] else: - # it's technically possible that the required class now has been built - # if one of the base classes uses it in e.g. a function interface + # it's possible that the required class now has been built if one of + # the base classes uses it in e.g. a function interface try: - return scope.__dict__[final_class_name] + return scope.__dict__[cl_name] except KeyError: pass @@ -147,39 +186,41 @@ d_meta = {} # prepare dictionary for python-side C++ class representation - def dispatch(self, name, signature): - cppol = decl.dispatch(name, signature) - return types.MethodType(make_method(name, cppol), self, type(self)) + def dispatch(self, m_name, signature): + cppol = decl.__dispatch__(m_name, signature) + return types.MethodType(make_method(m_name, cppol), self, type(self)) d_class = {"__cppdecl__" : decl, + "__new__" : make_new(decl), + "__module__" : make_module_name(scope), "__cppname__" : decl.__cppname__, - "__new__" : make_new(class_name), + "__dispatch__" : dispatch, } # insert (static) methods into the class dictionary - for name in decl.get_method_names(): - cppol = decl.get_overload(name) + for m_name in decl.get_method_names(): + cppol = decl.get_overload(m_name) if cppol.is_static(): - d_class[name] = make_static_function(name, cppol) + d_class[m_name] = make_static_function(m_name, cppol) else: - d_class[name] = make_method(name, cppol) + d_class[m_name] = make_method(m_name, cppol) # add all data members to the dictionary of the class to be created, and # static ones also to the metaclass (needed for property setters) - for name in decl.get_datamember_names(): - cppdm = decl.get_datamember(name) - d_class[name] = cppdm + for d_name in decl.get_datamember_names(): + cppdm = decl.get_datamember(d_name) + d_class[d_name] = cppdm if cppdm.is_static(): - d_meta[name] = cppdm + d_meta[d_name] = cppdm # create a metaclass to allow properties (for static data write access) metabases = [type(base) for base in bases] - metacpp = type(CPPMetaScope)(class_name+'_meta', _drop_cycles(metabases), d_meta) + metacpp = type(CPPMetaScope)(cl_name+'_meta', _drop_cycles(metabases), d_meta) # create the python-side C++ class - pycls = metacpp(class_name, _drop_cycles(bases), d_class) + pycls = metacpp(cl_name, _drop_cycles(bases), d_class) # store the class on its outer scope - setattr(scope, final_class_name, pycls) + setattr(scope, cl_name, pycls) # the call to register will add back-end specific pythonizations and thus # needs to run first, so that the generic pythonizations can use them @@ -192,32 +233,32 @@ return CPPTemplate(template_name, scope) -def get_pycppitem(scope, name): +def get_scoped_pycppitem(scope, name): import _cppyy - # resolve typedefs/aliases - full_name = (scope == gbl) and name or (scope.__name__+'::'+name) - true_name = _cppyy._resolve_name(full_name) - if true_name != full_name: - return get_pycppclass(true_name) + # resolve typedefs/aliases: these may cross namespaces, in which case + # the lookup must trigger the creation of all necessary scopes + scoped_name = (scope == gbl) and name or (scope.__cppname__+'::'+name) + final_scoped_name = _cppyy._resolve_name(scoped_name) + if final_scoped_name != scoped_name: + pycppitem = get_pycppitem(final_scoped_name) + # also store on the requested scope (effectively a typedef or pointer copy) + setattr(scope, name, pycppitem) + return pycppitem pycppitem = None - # classes - cppitem = _cppyy._scope_byname(true_name) + # scopes (classes and namespaces) + cppitem = _cppyy._scope_byname(final_scoped_name) if cppitem: - name = true_name - if scope != gbl: - name = true_name[len(scope.__cppname__)+2:] if cppitem.is_namespace(): pycppitem = make_cppnamespace(scope, name, cppitem) - setattr(scope, name, pycppitem) else: - pycppitem = make_cppclass(scope, name, true_name, cppitem) + pycppitem = make_cppclass(scope, name, cppitem) # templates if not cppitem: - cppitem = _cppyy._template_byname(true_name) + cppitem = _cppyy._is_template(final_scoped_name) if cppitem: pycppitem = make_cpptemplatetype(scope, name) setattr(scope, name, pycppitem) @@ -249,29 +290,6 @@ raise AttributeError("'%s' has no attribute '%s'" % (str(scope), name)) -def scope_splitter(name): - is_open_template, scope = 0, "" - for c in name: - if c == ':' and not is_open_template: - if scope: - yield scope - scope = "" - continue - elif c == '<': - is_open_template += 1 - elif c == '>': - is_open_template -= 1 - scope += c - yield scope - -def get_pycppclass(name): - # break up the name, to walk the scopes and get the class recursively - scope = gbl - for part in scope_splitter(name): - scope = getattr(scope, part) - return scope - - # pythonization by decoration (move to their own file?) def python_style_getitem(self, idx): # python-style indexing: check for size and allow indexing from the back @@ -346,8 +364,8 @@ # also the fallback on the indexed __getitem__, but that is slower) if not 'vector' in pyclass.__name__[:11] and \ ('begin' in pyclass.__dict__ and 'end' in pyclass.__dict__): - if _cppyy._scope_byname(pyclass.__name__+'::iterator') or \ - _cppyy._scope_byname(pyclass.__name__+'::const_iterator'): + if _cppyy._scope_byname(pyclass.__cppname__+'::iterator') or \ + _cppyy._scope_byname(pyclass.__cppname__+'::const_iterator'): def __iter__(self): i = self.begin() while i != self.end(): @@ -416,17 +434,21 @@ # pre-create std to allow direct importing gbl.std = make_cppnamespace(gbl, 'std', _cppyy._scope_byname('std')) + # add move cast + gbl.std.move = _cppyy.move + # install a type for enums to refer to # TODO: this is correct for C++98, not for C++11 and in general there will # be the same issue for all typedef'd builtin types setattr(gbl, 'internal_enum_type_t', int) - # install nullptr as a unique reference - setattr(gbl, 'nullptr', _cppyy._get_nullptr()) - # install for user access _cppyy.gbl = gbl + # install nullptr as a unique reference + _cppyy.nullptr = _cppyy._get_nullptr() + + # user-defined pythonizations interface _pythonizations = {} def add_pythonization(class_name, callback): diff --git a/pypy/module/_cppyy/src/dummy_backend.cxx b/pypy/module/_cppyy/src/dummy_backend.cxx --- a/pypy/module/_cppyy/src/dummy_backend.cxx +++ b/pypy/module/_cppyy/src/dummy_backend.cxx @@ -955,7 +955,13 @@ return cppstring_to_cstring(""); } -char* cppyy_method_signature(cppyy_scope_t /* handle */, cppyy_index_t /* method_index */) { +char* cppyy_method_signature( + cppyy_scope_t /* handle */, cppyy_index_t /* method_index */, int /* show_formalargs */) { + return cppstring_to_cstring(""); +} + +char* cppyy_method_prototype( + cppyy_scope_t /* handle */, cppyy_index_t /* method_index */, int /* show_formalargs */) { return cppstring_to_cstring(""); } diff --git a/pypy/module/_cppyy/test/Makefile b/pypy/module/_cppyy/test/Makefile --- a/pypy/module/_cppyy/test/Makefile +++ b/pypy/module/_cppyy/test/Makefile @@ -1,12 +1,14 @@ dicts = advancedcppDict.so \ advancedcpp2Dict.so \ + cpp11featuresDict.so \ crossingDict.so \ datatypesDict.so \ example01Dict.so \ fragileDict.so \ operatorsDict.so \ overloadsDict.so \ - stltypesDict.so + stltypesDict.so \ + templatesDict.so all : $(dicts) diff --git a/pypy/module/_cppyy/test/advancedcpp.cxx b/pypy/module/_cppyy/test/advancedcpp.cxx --- a/pypy/module/_cppyy/test/advancedcpp.cxx +++ b/pypy/module/_cppyy/test/advancedcpp.cxx @@ -106,17 +106,6 @@ } -// more template testing -long my_templated_method_class::get_size() { return -1; } - -long my_templated_method_class::get_char_size() { return (long)sizeof(char); } -long my_templated_method_class::get_int_size() { return (long)sizeof(int); } -long my_templated_method_class::get_long_size() { return (long)sizeof(long); } -long my_templated_method_class::get_float_size() { return (long)sizeof(float); } -long my_templated_method_class::get_double_size() { return (long)sizeof(double); } -long my_templated_method_class::get_self_size() { return (long)sizeof(my_templated_method_class); } - - // overload order testing int overload_one_way::gime() const { return 1; } std::string overload_one_way::gime() { return "aap"; } diff --git a/pypy/module/_cppyy/test/advancedcpp.h b/pypy/module/_cppyy/test/advancedcpp.h --- a/pypy/module/_cppyy/test/advancedcpp.h +++ b/pypy/module/_cppyy/test/advancedcpp.h @@ -246,8 +246,6 @@ int m_i; }; -template class std::vector; - //=========================================================================== class some_convertible { // for math conversions testing @@ -275,6 +273,7 @@ extern double my_global_double; // a couple of globals for access testing extern double my_global_array[500]; extern double* my_global_ptr; +static const char my_global_string[] = "aap " " noot " " mies"; //=========================================================================== class some_class_with_data { // for life-line and identity testing @@ -387,37 +386,6 @@ template char my_templated_function(char); template double my_templated_function(double); -class my_templated_method_class { -public: - long get_size(); // to get around bug in genreflex - template long get_size(); - - long get_char_size(); - long get_int_size(); - long get_long_size(); - long get_float_size(); - long get_double_size(); - - long get_self_size(); - -private: - double m_data[3]; -}; - -template -inline long my_templated_method_class::get_size() { - return sizeof(B); -} - -template long my_templated_method_class::get_size(); -template long my_templated_method_class::get_size(); -template long my_templated_method_class::get_size(); -template long my_templated_method_class::get_size(); -template long my_templated_method_class::get_size(); - -typedef my_templated_method_class my_typedef_t; -template long my_templated_method_class::get_size(); - //=========================================================================== class overload_one_way { // overload order testing diff --git a/pypy/module/_cppyy/test/advancedcpp.xml b/pypy/module/_cppyy/test/advancedcpp.xml --- a/pypy/module/_cppyy/test/advancedcpp.xml +++ b/pypy/module/_cppyy/test/advancedcpp.xml @@ -53,8 +53,6 @@ - - diff --git a/pypy/module/_cppyy/test/cpp11features.cxx b/pypy/module/_cppyy/test/cpp11features.cxx new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/cpp11features.cxx @@ -0,0 +1,18 @@ +#if __cplusplus >= 201103L + +#include "cpp11features.h" + + +// for std::shared_ptr<> testing +int TestSharedPtr::s_counter = 0; + +std::shared_ptr create_shared_ptr_instance() { + return std::shared_ptr(new TestSharedPtr); +} + + +// for move ctors etc. +int TestMoving1::s_move_counter = 0; +int TestMoving2::s_move_counter = 0; + +#endif // c++11 and later diff --git a/pypy/module/_cppyy/test/cpp11features.h b/pypy/module/_cppyy/test/cpp11features.h new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/cpp11features.h @@ -0,0 +1,45 @@ +#if __cplusplus >= 201103L + +#include + + +//=========================================================================== +class TestSharedPtr { // for std::shared_ptr<> testing +public: + static int s_counter; + +public: + TestSharedPtr() { ++s_counter; } + TestSharedPtr(const TestSharedPtr&) { ++s_counter; } + ~TestSharedPtr() { --s_counter; } +}; + +std::shared_ptr create_shared_ptr_instance(); + + +//=========================================================================== +class TestMoving1 { // for move ctors etc. +public: + static int s_move_counter; + +public: + TestMoving1() {} + TestMoving1(TestMoving1&&) { ++s_move_counter; } + TestMoving1(const TestMoving1&) {} + TestMoving1& operator=(TestMoving1&&) { ++s_move_counter; return *this; } + TestMoving1& operator=(TestMoving1&) { return *this; } +}; + +class TestMoving2 { // note opposite method order from TestMoving1 +public: + static int s_move_counter; + +public: + TestMoving2() {} + TestMoving2(const TestMoving2&) {} + TestMoving2(TestMoving2&& other) { ++s_move_counter; } + TestMoving2& operator=(TestMoving2&) { return *this; } + TestMoving2& operator=(TestMoving2&&) { ++s_move_counter; return *this; } +}; + +#endif // c++11 and later diff --git a/pypy/module/_cppyy/test/cpp11features.xml b/pypy/module/_cppyy/test/cpp11features.xml new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/cpp11features.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/pypy/module/_cppyy/test/fragile.h b/pypy/module/_cppyy/test/fragile.h --- a/pypy/module/_cppyy/test/fragile.h +++ b/pypy/module/_cppyy/test/fragile.h @@ -30,9 +30,11 @@ void overload(int, no_such_class* p = 0) {} }; +static const int dummy_location = 0xdead; + class E { public: - E() : m_pp_no_such(0), m_pp_a(0) {} + E() : m_pp_no_such((no_such_class**)&dummy_location), m_pp_a(0) {} virtual int check() { return (int)'E'; } void overload(no_such_class**) {} diff --git a/pypy/module/_cppyy/test/templates.cxx b/pypy/module/_cppyy/test/templates.cxx new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/templates.cxx @@ -0,0 +1,12 @@ +#include "templates.h" + + +// template methods +long MyTemplatedMethodClass::get_size() { return -1; } + +long MyTemplatedMethodClass::get_char_size() { return (long)sizeof(char); } +long MyTemplatedMethodClass::get_int_size() { return (long)sizeof(int); } +long MyTemplatedMethodClass::get_long_size() { return (long)42; /* "lying" */ } +long MyTemplatedMethodClass::get_float_size() { return (long)sizeof(float); } +long MyTemplatedMethodClass::get_double_size() { return (long)sizeof(double); } +long MyTemplatedMethodClass::get_self_size() { return (long)sizeof(MyTemplatedMethodClass); } diff --git a/pypy/module/_cppyy/test/templates.h b/pypy/module/_cppyy/test/templates.h new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/templates.h @@ -0,0 +1,35 @@ +//=========================================================================== +class MyTemplatedMethodClass { // template methods +public: + long get_size(); // to get around bug in genreflex + template long get_size(); + + long get_char_size(); + long get_int_size(); + long get_long_size(); + long get_float_size(); + long get_double_size(); + + long get_self_size(); + +private: + double m_data[3]; +}; + +template +inline long MyTemplatedMethodClass::get_size() { + return sizeof(B); +} + +// +typedef MyTemplatedMethodClass MyTMCTypedef_t; + +// explicit instantiation +template long MyTemplatedMethodClass::get_size(); +template long MyTemplatedMethodClass::get_size(); + +// "lying" specialization +template<> +inline long MyTemplatedMethodClass::get_size() { + return 42; +} diff --git a/pypy/module/_cppyy/test/templates.xml b/pypy/module/_cppyy/test/templates.xml new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/templates.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/pypy/module/_cppyy/test/test_advancedcpp.py b/pypy/module/_cppyy/test/test_advancedcpp.py --- a/pypy/module/_cppyy/test/test_advancedcpp.py +++ b/pypy/module/_cppyy/test/test_advancedcpp.py @@ -28,9 +28,9 @@ def test01_default_arguments(self): """Test usage of default arguments""" - import _cppyy + import _cppyy as cppyy def test_defaulter(n, t): - defaulter = getattr(_cppyy.gbl, '%s_defaulter' % n) + defaulter = getattr(cppyy.gbl, '%s_defaulter' % n) d = defaulter() assert d.m_a == t(11) @@ -55,23 +55,23 @@ assert d.m_b == t(4) assert d.m_c == t(5) d.__destruct__() - test_defaulter('short', int) + test_defaulter('short', int) test_defaulter('ushort', int) - test_defaulter('int', int) - test_defaulter('uint', int) - test_defaulter('long', long) - test_defaulter('ulong', long) - test_defaulter('llong', long) + test_defaulter('int', int) + test_defaulter('uint', int) + test_defaulter('long', long) + test_defaulter('ulong', long) + test_defaulter('llong', long) test_defaulter('ullong', long) - test_defaulter('float', float) + test_defaulter('float', float) test_defaulter('double', float) def test02_simple_inheritance(self): """Test binding of a basic inheritance structure""" - import _cppyy - base_class = _cppyy.gbl.base_class - derived_class = _cppyy.gbl.derived_class + import _cppyy as cppyy + base_class = cppyy.gbl.base_class + derived_class = cppyy.gbl.derived_class assert issubclass(derived_class, base_class) assert not issubclass(base_class, derived_class) @@ -123,8 +123,8 @@ def test03_namespaces(self): """Test access to namespaces and inner classes""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl assert gbl.a_ns is gbl.a_ns assert gbl.a_ns.d_ns is gbl.a_ns.d_ns @@ -150,8 +150,8 @@ def test03a_namespace_lookup_on_update(self): """Test whether namespaces can be shared across dictionaries.""" - import _cppyy, ctypes - gbl = _cppyy.gbl + import _cppyy as cppyy, ctypes + gbl = cppyy.gbl lib2 = ctypes.CDLL("./advancedcpp2Dict.so", ctypes.RTLD_GLOBAL) @@ -179,8 +179,8 @@ def test04_template_types(self): """Test bindings of templated types""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl assert gbl.T1 is gbl.T1 assert gbl.T2 is gbl.T2 @@ -245,8 +245,8 @@ def test05_abstract_classes(self): """Test non-instatiatability of abstract classes""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl raises(TypeError, gbl.a_class) raises(TypeError, gbl.some_abstract_class) @@ -260,12 +260,12 @@ def test06_datamembers(self): """Test data member access when using virtual inheritence""" - import _cppyy - a_class = _cppyy.gbl.a_class - b_class = _cppyy.gbl.b_class - c_class_1 = _cppyy.gbl.c_class_1 - c_class_2 = _cppyy.gbl.c_class_2 - d_class = _cppyy.gbl.d_class + import _cppyy as cppyy + a_class = cppyy.gbl.a_class + b_class = cppyy.gbl.b_class + c_class_1 = cppyy.gbl.c_class_1 + c_class_2 = cppyy.gbl.c_class_2 + d_class = cppyy.gbl.d_class assert issubclass(b_class, a_class) assert issubclass(c_class_1, a_class) @@ -354,8 +354,8 @@ def test07_pass_by_reference(self): """Test reference passing when using virtual inheritance""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl b_class = gbl.b_class c_class = gbl.c_class_2 d_class = gbl.d_class @@ -387,71 +387,75 @@ def test08_void_pointer_passing(self): """Test passing of variants of void pointer arguments""" - import _cppyy - pointer_pass = _cppyy.gbl.pointer_pass - some_concrete_class = _cppyy.gbl.some_concrete_class + import _cppyy as cppyy + pointer_pass = cppyy.gbl.pointer_pass + some_concrete_class = cppyy.gbl.some_concrete_class pp = pointer_pass() o = some_concrete_class() - assert _cppyy.addressof(o) == pp.gime_address_ptr(o) - assert _cppyy.addressof(o) == pp.gime_address_ptr_ptr(o) - assert _cppyy.addressof(o) == pp.gime_address_ptr_ref(o) + assert cppyy.addressof(o) == pp.gime_address_ptr(o) + assert cppyy.addressof(o) == pp.gime_address_ptr_ptr(o) + assert cppyy.addressof(o) == pp.gime_address_ptr_ref(o) import array - addressofo = array.array('l', [_cppyy.addressof(o)]) - assert addressofo.buffer_info()[0] == pp.gime_address_ptr_ptr(addressofo) + addressofo = array.array('l', [cppyy.addressof(o)]) + assert addressofo[0] == pp.gime_address_ptr_ptr(addressofo) assert 0 == pp.gime_address_ptr(0) - assert 0 == pp.gime_address_ptr(None) + raises(TypeError, pp.gime_address_ptr, None) - ptr = _cppyy.bind_object(0, some_concrete_class) - assert _cppyy.addressof(ptr) == 0 + ptr = cppyy.bind_object(0, some_concrete_class) + assert cppyy.addressof(ptr) == 0 pp.set_address_ptr_ref(ptr) - assert _cppyy.addressof(ptr) == 0x1234 + assert cppyy.addressof(ptr) == 0x1234 pp.set_address_ptr_ptr(ptr) - assert _cppyy.addressof(ptr) == 0x4321 + assert cppyy.addressof(ptr) == 0x4321 + + assert cppyy.addressof(cppyy.nullptr) == 0 + raises(TypeError, cppyy.addressof, None) + assert cppyy.addressof(0) == 0 def test09_opaque_pointer_passing(self): """Test passing around of opaque pointers""" - import _cppyy - some_concrete_class = _cppyy.gbl.some_concrete_class + import _cppyy as cppyy + some_concrete_class = cppyy.gbl.some_concrete_class o = some_concrete_class() # TODO: figure out the PyPy equivalent of CObject (may have to do this # through the C-API from C++) - #cobj = _cppyy.as_cobject(o) - addr = _cppyy.addressof(o) + #cobj = cppyy.as_cobject(o) + addr = cppyy.addressof(o) - #assert o == _cppyy.bind_object(cobj, some_concrete_class) - #assert o == _cppyy.bind_object(cobj, type(o)) - #assert o == _cppyy.bind_object(cobj, o.__class__) - #assert o == _cppyy.bind_object(cobj, "some_concrete_class") - assert _cppyy.addressof(o) == _cppyy.addressof(_cppyy.bind_object(addr, some_concrete_class)) - assert o == _cppyy.bind_object(addr, some_concrete_class) - assert o == _cppyy.bind_object(addr, type(o)) - assert o == _cppyy.bind_object(addr, o.__class__) - assert o == _cppyy.bind_object(addr, "some_concrete_class") - raises(TypeError, _cppyy.bind_object, addr, "does_not_exist") - raises(TypeError, _cppyy.bind_object, addr, 1) + #assert o == cppyy.bind_object(cobj, some_concrete_class) + #assert o == cppyy.bind_object(cobj, type(o)) + #assert o == cppyy.bind_object(cobj, o.__class__) + #assert o == cppyy.bind_object(cobj, "some_concrete_class") + assert cppyy.addressof(o) == cppyy.addressof(cppyy.bind_object(addr, some_concrete_class)) + assert o == cppyy.bind_object(addr, some_concrete_class) + assert o == cppyy.bind_object(addr, type(o)) + assert o == cppyy.bind_object(addr, o.__class__) + assert o == cppyy.bind_object(addr, "some_concrete_class") + raises(TypeError, cppyy.bind_object, addr, "does_not_exist") + raises(TypeError, cppyy.bind_object, addr, 1) def test10_object_identity(self): """Test object identity""" - import _cppyy - some_concrete_class = _cppyy.gbl.some_concrete_class - some_class_with_data = _cppyy.gbl.some_class_with_data + import _cppyy as cppyy + some_concrete_class = cppyy.gbl.some_concrete_class + some_class_with_data = cppyy.gbl.some_class_with_data o = some_concrete_class() - addr = _cppyy.addressof(o) + addr = cppyy.addressof(o) - o2 = _cppyy.bind_object(addr, some_concrete_class) + o2 = cppyy.bind_object(addr, some_concrete_class) assert o is o2 - o3 = _cppyy.bind_object(addr, some_class_with_data) + o3 = cppyy.bind_object(addr, some_class_with_data) assert not o is o3 d1 = some_class_with_data() @@ -472,13 +476,13 @@ def test11_multi_methods(self): """Test calling of methods from multiple inheritance""" - import _cppyy - multi = _cppyy.gbl.multi + import _cppyy as cppyy + multi = cppyy.gbl.multi - assert _cppyy.gbl.multi1 is multi.__bases__[0] - assert _cppyy.gbl.multi2 is multi.__bases__[1] From pypy.commits at gmail.com Sun Oct 29 09:49:37 2017 From: pypy.commits at gmail.com (rlamy) Date: Sun, 29 Oct 2017 06:49:37 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: Merged in nanjekye/pypy/sched_yield (pull request #574) Message-ID: <59f5dc71.2587df0a.3a662.a1da@mx.google.com> Author: Ronan Lamy Branch: py3.5 Changeset: r92874:4edfa3e729be Date: 2017-10-29 13:49 +0000 http://bitbucket.org/pypy/pypy/changeset/4edfa3e729be/ Log: Merged in nanjekye/pypy/sched_yield (pull request #574) sched_yield posix attribute diff --git a/pypy/module/posix/__init__.py b/pypy/module/posix/__init__.py --- a/pypy/module/posix/__init__.py +++ b/pypy/module/posix/__init__.py @@ -237,6 +237,9 @@ if getattr(rposix, _name) is not None: interpleveldefs[_name] = 'space.wrap(%d)' % getattr(rposix, _name) + if hasattr(rposix, 'sched_yield'): + interpleveldefs['sched_yield'] = 'interp_posix.sched_yield' + for _name in ["O_CLOEXEC"]: if getattr(rposix, _name) is not None: interpleveldefs[_name] = 'space.wrap(%d)' % getattr(rposix, _name) 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 @@ -2468,3 +2468,13 @@ wrap_oserror(space, e, eintr_retry=True) else: return space.newint(s) + +def sched_yield(space): + """ Voluntarily relinquish the CPU""" + while True: + try: + res = rposix.sched_yield() + except OSError as e: + wrap_oserror(space, e, eintr_retry=True) + else: + return space.newint(res) diff --git a/pypy/module/posix/test/test_posix2.py b/pypy/module/posix/test/test_posix2.py --- a/pypy/module/posix/test/test_posix2.py +++ b/pypy/module/posix/test/test_posix2.py @@ -975,6 +975,12 @@ assert isinstance(high, int) == True assert high > low + if hasattr(rposix, 'sched_yield'): + def test_sched_yield(self): + os = self.posix + #Always suceeds on Linux + os.sched_yield() + def test_write_buffer(self): os = self.posix fd = os.open(self.path2 + 'test_write_buffer', diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -1851,6 +1851,8 @@ rffi.INT, save_err=rffi.RFFI_FULL_ERRNO_ZERO) c_sched_get_priority_min = external('sched_get_priority_min', [rffi.INT], rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO) + if not _WIN32: + c_sched_yield = external('sched_yield', [], rffi.INT) @enforceargs(int) def sched_get_priority_max(policy): @@ -1860,9 +1862,9 @@ def sched_get_priority_min(policy): return handle_posix_error('sched_get_priority_min', c_sched_get_priority_min(policy)) - - - + def sched_yield(): + return handle_posix_error('sched_yield', c_sched_yield()) + #___________________________________________________________________ c_chroot = external('chroot', [rffi.CCHARP], rffi.INT, diff --git a/rpython/rlib/test/test_rposix.py b/rpython/rlib/test/test_rposix.py --- a/rpython/rlib/test/test_rposix.py +++ b/rpython/rlib/test/test_rposix.py @@ -810,4 +810,9 @@ assert isinstance(low, int) == True assert isinstance(high, int) == True assert high > low + + at rposix_requires('sched_yield') +def test_sched_yield(): + if sys.platform != 'win32': + rposix.sched_yield() From pypy.commits at gmail.com Sun Oct 29 09:49:43 2017 From: pypy.commits at gmail.com (nanjekye) Date: Sun, 29 Oct 2017 06:49:43 -0700 (PDT) Subject: [pypy-commit] pypy sched_yield: sched_yield posix attribute Message-ID: <59f5dc77.8a821c0a.ad7aa.5d90@mx.google.com> Author: Joannah Nanjekye Branch: sched_yield Changeset: r92873:f06ed6b90651 Date: 2017-10-27 19:57 +0300 http://bitbucket.org/pypy/pypy/changeset/f06ed6b90651/ Log: sched_yield posix attribute diff --git a/pypy/module/posix/__init__.py b/pypy/module/posix/__init__.py --- a/pypy/module/posix/__init__.py +++ b/pypy/module/posix/__init__.py @@ -237,6 +237,9 @@ if getattr(rposix, _name) is not None: interpleveldefs[_name] = 'space.wrap(%d)' % getattr(rposix, _name) + if hasattr(rposix, 'sched_yield'): + interpleveldefs['sched_yield'] = 'interp_posix.sched_yield' + for _name in ["O_CLOEXEC"]: if getattr(rposix, _name) is not None: interpleveldefs[_name] = 'space.wrap(%d)' % getattr(rposix, _name) 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 @@ -2468,3 +2468,13 @@ wrap_oserror(space, e, eintr_retry=True) else: return space.newint(s) + +def sched_yield(space): + """ Voluntarily relinquish the CPU""" + while True: + try: + res = rposix.sched_yield() + except OSError as e: + wrap_oserror(space, e, eintr_retry=True) + else: + return space.newint(res) diff --git a/pypy/module/posix/test/test_posix2.py b/pypy/module/posix/test/test_posix2.py --- a/pypy/module/posix/test/test_posix2.py +++ b/pypy/module/posix/test/test_posix2.py @@ -975,6 +975,12 @@ assert isinstance(high, int) == True assert high > low + if hasattr(rposix, 'sched_yield'): + def test_sched_yield(self): + os = self.posix + #Always suceeds on Linux + os.sched_yield() + def test_write_buffer(self): os = self.posix fd = os.open(self.path2 + 'test_write_buffer', diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -1851,6 +1851,8 @@ rffi.INT, save_err=rffi.RFFI_FULL_ERRNO_ZERO) c_sched_get_priority_min = external('sched_get_priority_min', [rffi.INT], rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO) + if not _WIN32: + c_sched_yield = external('sched_yield', [], rffi.INT) @enforceargs(int) def sched_get_priority_max(policy): @@ -1860,9 +1862,9 @@ def sched_get_priority_min(policy): return handle_posix_error('sched_get_priority_min', c_sched_get_priority_min(policy)) - - - + def sched_yield(): + return handle_posix_error('sched_yield', c_sched_yield()) + #___________________________________________________________________ c_chroot = external('chroot', [rffi.CCHARP], rffi.INT, diff --git a/rpython/rlib/test/test_rposix.py b/rpython/rlib/test/test_rposix.py --- a/rpython/rlib/test/test_rposix.py +++ b/rpython/rlib/test/test_rposix.py @@ -810,4 +810,9 @@ assert isinstance(low, int) == True assert isinstance(high, int) == True assert high > low + + at rposix_requires('sched_yield') +def test_sched_yield(): + if sys.platform != 'win32': + rposix.sched_yield() From pypy.commits at gmail.com Sun Oct 29 13:41:43 2017 From: pypy.commits at gmail.com (mattip) Date: Sun, 29 Oct 2017 10:41:43 -0700 (PDT) Subject: [pypy-commit] pypy py3.5-appexec: revert ec989ce318a9 and raise if space.appexec passed to space.is_true on runappdirect Message-ID: <59f612d7.ddafdf0a.7e53f.5435@mx.google.com> Author: Matti Picus Branch: py3.5-appexec Changeset: r92875:69ba67aff206 Date: 2017-10-29 19:40 +0200 http://bitbucket.org/pypy/pypy/changeset/69ba67aff206/ Log: revert ec989ce318a9 and raise if space.appexec passed to space.is_true on runappdirect diff --git a/pypy/module/_socket/test/test_sock_app.py b/pypy/module/_socket/test/test_sock_app.py --- a/pypy/module/_socket/test/test_sock_app.py +++ b/pypy/module/_socket/test/test_sock_app.py @@ -729,7 +729,12 @@ def setup_class(cls): if not hasattr(os, 'getpid'): pytest.skip("AF_NETLINK needs os.getpid()") - w_ok = space.appexec([], "(): import _socket; " + + + if cls.runappdirect: + import _socket + w_ok = hasattr(_socket, 'AF_NETLINK') + else: + w_ok = space.appexec([], "(): import _socket; " + "return hasattr(_socket, 'AF_NETLINK')") if not space.is_true(w_ok): pytest.skip("no AF_NETLINK on this platform") @@ -737,7 +742,8 @@ def test_connect_to_kernel_netlink_routing_socket(self): import _socket, os - s = _socket.socket(_socket.AF_NETLINK, _socket.SOCK_DGRAM, _socket.NETLINK_ROUTE) + s = _socket.socket(_socket.AF_NETLINK, _socket.SOCK_DGRAM, + _socket.NETLINK_ROUTE) assert s.getsockname() == (0, 0) s.bind((0, 0)) a, b = s.getsockname() @@ -749,7 +755,11 @@ def setup_class(cls): if not hasattr(os, 'getuid') or os.getuid() != 0: pytest.skip("AF_PACKET needs to be root for testing") - w_ok = space.appexec([], "(): import _socket; " + + if cls.runappdirect: + import _socket + w_ok = hasattr(_socket, 'AF_PACKET') + else: + w_ok = space.appexec([], "(): import _socket; " + "return hasattr(_socket, 'AF_PACKET')") if not space.is_true(w_ok): pytest.skip("no AF_PACKET on this platform") diff --git a/pypy/tool/pytest/apptest.py b/pypy/tool/pytest/apptest.py --- a/pypy/tool/pytest/apptest.py +++ b/pypy/tool/pytest/apptest.py @@ -239,7 +239,7 @@ space = gettestobjspace() filename = self._getdynfilename(target) func = app2interp_temp(src, filename=filename) - print "executing", func + # print "executing", func self.execute_appex(space, func, space) def repr_failure(self, excinfo): diff --git a/pypy/tool/pytest/objspace.py b/pypy/tool/pytest/objspace.py --- a/pypy/tool/pytest/objspace.py +++ b/pypy/tool/pytest/objspace.py @@ -79,9 +79,7 @@ body = body.lstrip() assert body.startswith('(') src = py.code.Source("def anonymous" + body) - d = {} - exec src.compile() in d - return d['anonymous'](*args) + return (src, args) def wrap(self, obj): if isinstance(obj, str): @@ -102,6 +100,8 @@ return list(itr) def is_true(self, obj): + if isinstance(obj, tuple) and isinstance(obj[0], py.code.Source): + raise ValueError('bool(appexec object) unknown') return bool(obj) def is_none(self, obj): From pypy.commits at gmail.com Sun Oct 29 15:06:48 2017 From: pypy.commits at gmail.com (mattip) Date: Sun, 29 Oct 2017 12:06:48 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: merge py3.5-appexec which will now raise on improper use of appexec in app tests Message-ID: <59f626c8.c4321c0a.2b71.8146@mx.google.com> Author: Matti Picus Branch: py3.5 Changeset: r92876:955b8000658b Date: 2017-10-29 21:01 +0200 http://bitbucket.org/pypy/pypy/changeset/955b8000658b/ Log: merge py3.5-appexec which will now raise on improper use of appexec in app tests diff --git a/pypy/module/_socket/test/test_sock_app.py b/pypy/module/_socket/test/test_sock_app.py --- a/pypy/module/_socket/test/test_sock_app.py +++ b/pypy/module/_socket/test/test_sock_app.py @@ -729,7 +729,12 @@ def setup_class(cls): if not hasattr(os, 'getpid'): pytest.skip("AF_NETLINK needs os.getpid()") - w_ok = space.appexec([], "(): import _socket; " + + + if cls.runappdirect: + import _socket + w_ok = hasattr(_socket, 'AF_NETLINK') + else: + w_ok = space.appexec([], "(): import _socket; " + "return hasattr(_socket, 'AF_NETLINK')") if not space.is_true(w_ok): pytest.skip("no AF_NETLINK on this platform") @@ -737,7 +742,8 @@ def test_connect_to_kernel_netlink_routing_socket(self): import _socket, os - s = _socket.socket(_socket.AF_NETLINK, _socket.SOCK_DGRAM, _socket.NETLINK_ROUTE) + s = _socket.socket(_socket.AF_NETLINK, _socket.SOCK_DGRAM, + _socket.NETLINK_ROUTE) assert s.getsockname() == (0, 0) s.bind((0, 0)) a, b = s.getsockname() @@ -749,7 +755,11 @@ def setup_class(cls): if not hasattr(os, 'getuid') or os.getuid() != 0: pytest.skip("AF_PACKET needs to be root for testing") - w_ok = space.appexec([], "(): import _socket; " + + if cls.runappdirect: + import _socket + w_ok = hasattr(_socket, 'AF_PACKET') + else: + w_ok = space.appexec([], "(): import _socket; " + "return hasattr(_socket, 'AF_PACKET')") if not space.is_true(w_ok): pytest.skip("no AF_PACKET on this platform") diff --git a/pypy/tool/pytest/apptest.py b/pypy/tool/pytest/apptest.py --- a/pypy/tool/pytest/apptest.py +++ b/pypy/tool/pytest/apptest.py @@ -239,7 +239,7 @@ space = gettestobjspace() filename = self._getdynfilename(target) func = app2interp_temp(src, filename=filename) - print "executing", func + # print "executing", func self.execute_appex(space, func, space) def repr_failure(self, excinfo): diff --git a/pypy/tool/pytest/objspace.py b/pypy/tool/pytest/objspace.py --- a/pypy/tool/pytest/objspace.py +++ b/pypy/tool/pytest/objspace.py @@ -100,6 +100,8 @@ return list(itr) def is_true(self, obj): + if isinstance(obj, tuple) and isinstance(obj[0], py.code.Source): + raise ValueError('bool(appexec object) unknown') return bool(obj) def is_none(self, obj): From pypy.commits at gmail.com Sun Oct 29 15:06:50 2017 From: pypy.commits at gmail.com (mattip) Date: Sun, 29 Oct 2017 12:06:50 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: document merged branches Message-ID: <59f626ca.06b6df0a.1db93.f530@mx.google.com> Author: Matti Picus Branch: py3.5 Changeset: r92877:41578430cbc4 Date: 2017-10-29 21:04 +0200 http://bitbucket.org/pypy/pypy/changeset/41578430cbc4/ Log: document merged branches diff --git a/pypy/doc/whatsnew-pypy3-head.rst b/pypy/doc/whatsnew-pypy3-head.rst --- a/pypy/doc/whatsnew-pypy3-head.rst +++ b/pypy/doc/whatsnew-pypy3-head.rst @@ -4,3 +4,10 @@ .. this is the revision after release-pypy3.5-5.9 .. startrev: be41e3ac0a29 + +.. branch: sched_yield +Add sched_yield posix attribute + +.. branch: py3.5-appexec +Raise if space.is_true(space.appexec()) used in app level tests, fix tests +that did this From pypy.commits at gmail.com Sun Oct 29 21:19:49 2017 From: pypy.commits at gmail.com (rlamy) Date: Sun, 29 Oct 2017 18:19:49 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: hg merge py3.5 Message-ID: <59f67e35.8fa3df0a.8d0a6.a50a@mx.google.com> Author: Ronan Lamy Branch: py3.6 Changeset: r92878:40e3c2a486e9 Date: 2017-10-30 01:18 +0000 http://bitbucket.org/pypy/pypy/changeset/40e3c2a486e9/ Log: hg merge py3.5 diff too long, truncating to 2000 out of 11351 lines diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -75,6 +75,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/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -40,3 +40,7 @@ 2875f328eae2216a87f3d6f335092832eb031f56 release-pypy3.5-v5.7.1 c925e73810367cd960a32592dd7f728f436c125c release-pypy2.7-v5.8.0 a37ecfe5f142bc971a86d17305cc5d1d70abec64 release-pypy3.5-v5.8.0 +03d614975835870da65ff0481e1edad68ebbcb8d release-pypy2.7-v5.9.0 +d72f9800a42b46a8056951b1da2426d2c2d8d502 release-pypy3.5-v5.9.0 +03d614975835870da65ff0481e1edad68ebbcb8d release-pypy2.7-v5.9.0 +84a2f3e6a7f88f2fe698e473998755b3bd1a12e2 release-pypy2.7-v5.9.0 diff --git a/lib-python/3/ctypes/test/test_bitfields.py b/lib-python/3/ctypes/test/test_bitfields.py --- a/lib-python/3/ctypes/test/test_bitfields.py +++ b/lib-python/3/ctypes/test/test_bitfields.py @@ -1,5 +1,5 @@ from ctypes import * -from ctypes.test import need_symbol +from ctypes.test import need_symbol, xfail import unittest import os @@ -278,6 +278,7 @@ self.assertEqual(b, b'\xef\xcd\xab\x21') @need_symbol('c_uint32') + @xfail def test_uint32_swap_big_endian(self): # Issue #23319 class Big(BigEndianStructure): diff --git a/lib-python/3/ctypes/test/test_byteswap.py b/lib-python/3/ctypes/test/test_byteswap.py --- a/lib-python/3/ctypes/test/test_byteswap.py +++ b/lib-python/3/ctypes/test/test_byteswap.py @@ -2,6 +2,7 @@ from binascii import hexlify from ctypes import * +from test.support import impl_detail def bin(s): return hexlify(memoryview(s)).decode().upper() @@ -22,6 +23,7 @@ setattr(bits, "i%s" % i, 1) dump(bits) + @impl_detail("slots are irrelevant on PyPy", pypy=False) def test_slots(self): class BigPoint(BigEndianStructure): __slots__ = () diff --git a/lib-python/3/ctypes/test/test_frombuffer.py b/lib-python/3/ctypes/test/test_frombuffer.py --- a/lib-python/3/ctypes/test/test_frombuffer.py +++ b/lib-python/3/ctypes/test/test_frombuffer.py @@ -85,7 +85,6 @@ del a gc.collect() # Should not crash - @xfail def test_from_buffer_copy(self): a = array.array("i", range(16)) x = (c_int * 16).from_buffer_copy(a) diff --git a/lib-python/3/ctypes/test/test_values.py b/lib-python/3/ctypes/test/test_values.py --- a/lib-python/3/ctypes/test/test_values.py +++ b/lib-python/3/ctypes/test/test_values.py @@ -4,6 +4,7 @@ import unittest import sys +from test.support import cpython_only from ctypes import * import _ctypes_test @@ -28,6 +29,7 @@ ctdll = CDLL(_ctypes_test.__file__) self.assertRaises(ValueError, c_int.in_dll, ctdll, "Undefined_Symbol") + at cpython_only class PythonValuesTestCase(unittest.TestCase): """This test only works when python itself is a dll/shared library""" diff --git a/lib-python/3/test/test_bytes.py b/lib-python/3/test/test_bytes.py --- a/lib-python/3/test/test_bytes.py +++ b/lib-python/3/test/test_bytes.py @@ -780,7 +780,6 @@ self.assertIs(type(BytesSubclass(A())), BytesSubclass) # Test PyBytes_FromFormat() - @test.support.impl_detail("don't test cpyext here") def test_from_format(self): ctypes = test.support.import_module('ctypes') _testcapi = test.support.import_module('_testcapi') diff --git a/lib-python/3/test/test_unicode.py b/lib-python/3/test/test_unicode.py --- a/lib-python/3/test/test_unicode.py +++ b/lib-python/3/test/test_unicode.py @@ -2436,6 +2436,10 @@ # Test PyUnicode_FromFormat() def test_from_format(self): support.import_module('ctypes') + try: + from ctypes import pythonapi + except ImportError: + self.skipTest( "no pythonapi in ctypes") from ctypes import ( pythonapi, py_object, sizeof, c_int, c_long, c_longlong, c_ssize_t, 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,9 +8,14 @@ class ArrayMeta(_CDataMeta): def __new__(self, name, cls, typedict): res = type.__new__(self, name, cls, typedict) + 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': @@ -55,7 +60,7 @@ for i in range(len(val)): target[i] = val[i] if len(val) < self._length_: - target[len(val)] = '\x00' + target[len(val)] = u'\x00' res.value = property(getvalue, setvalue) res._ffishape_ = (ffiarray, res._length_) @@ -164,7 +169,7 @@ if letter == 'c': return b"".join(l) if letter == 'u': - return "".join(l) + return u"".join(l) return l class Array(_CData, metaclass=ArrayMeta): 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 @@ -165,6 +165,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 @@ -113,7 +113,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 __bool__(self): return self._buffer[0] != 0 diff --git a/lib_pypy/_ctypes/primitive.py b/lib_pypy/_ctypes/primitive.py --- a/lib_pypy/_ctypes/primitive.py +++ b/lib_pypy/_ctypes/primitive.py @@ -232,9 +232,6 @@ elif tp == 'u': def _setvalue(self, val): - if isinstance(val, bytes): - val = val.decode(ConvMode.encoding, ConvMode.errors) - # possible if we use 'ignore' if val: self._buffer[0] = val def _getvalue(self): @@ -243,8 +240,6 @@ elif tp == 'c': def _setvalue(self, val): - if isinstance(val, str): - val = val.encode(ConvMode.encoding, ConvMode.errors) if val: self._buffer[0] = val def _getvalue(self): 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 @@ -290,6 +290,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/_curses.py b/lib_pypy/_curses.py --- a/lib_pypy/_curses.py +++ b/lib_pypy/_curses.py @@ -404,7 +404,7 @@ return val def get_wch(self, *args): - wch = ffi.new("int[1]") + wch = ffi.new("wint_t[1]") if len(args) == 0: val = lib.wget_wch(self._win, wch) elif len(args) == 2: diff --git a/lib_pypy/_sqlite3.py b/lib_pypy/_sqlite3.py --- a/lib_pypy/_sqlite3.py +++ b/lib_pypy/_sqlite3.py @@ -1080,21 +1080,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/cffi.egg-info/PKG-INFO b/lib_pypy/cffi.egg-info/PKG-INFO --- a/lib_pypy/cffi.egg-info/PKG-INFO +++ b/lib_pypy/cffi.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: cffi -Version: 1.11.1 +Version: 1.11.2 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski diff --git a/lib_pypy/cffi/__init__.py b/lib_pypy/cffi/__init__.py --- a/lib_pypy/cffi/__init__.py +++ b/lib_pypy/cffi/__init__.py @@ -4,8 +4,8 @@ from .api import FFI from .error import CDefError, FFIError, VerificationError, VerificationMissing -__version__ = "1.11.1" -__version_info__ = (1, 11, 1) +__version__ = "1.11.2" +__version_info__ = (1, 11, 2) # The verifier module file names are based on the CRC32 of a string that # contains the following version number. It may be older than __version__ diff --git a/lib_pypy/cffi/_cffi_include.h b/lib_pypy/cffi/_cffi_include.h --- a/lib_pypy/cffi/_cffi_include.h +++ b/lib_pypy/cffi/_cffi_include.h @@ -238,9 +238,9 @@ _CFFI_UNUSED_FN static PyObject *_cffi_from_c_char16_t(uint16_t x) { if (sizeof(_cffi_wchar_t) == 2) - return _cffi_from_c_wchar_t(x); + return _cffi_from_c_wchar_t((_cffi_wchar_t)x); else - return _cffi_from_c_wchar3216_t(x); + return _cffi_from_c_wchar3216_t((int)x); } _CFFI_UNUSED_FN static int _cffi_to_c_char32_t(PyObject *o) @@ -254,7 +254,7 @@ _CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(int x) { if (sizeof(_cffi_wchar_t) == 4) - return _cffi_from_c_wchar_t(x); + return _cffi_from_c_wchar_t((_cffi_wchar_t)x); else return _cffi_from_c_wchar3216_t(x); } diff --git a/lib_pypy/cffi/_embedding.h b/lib_pypy/cffi/_embedding.h --- a/lib_pypy/cffi/_embedding.h +++ b/lib_pypy/cffi/_embedding.h @@ -247,7 +247,7 @@ if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.11.1" + "\ncompiled with cffi version: 1.11.2" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); diff --git a/pypy/TODO b/pypy/TODO --- a/pypy/TODO +++ b/pypy/TODO @@ -1,18 +1,4 @@ -TODO for the python3 test suite: - -* test_memoryview - Needs bytes/str changes. Probably easy. Work for this has begun on - py3k-memoryview (by mjacob) https://bugs.pypy.org/issue1542 - -own-tests: - -* module/test_lib_pypy - These crash the buildbots (via SyntaxErrors): others were really - made to run under Python 2.x and so simply fail - -* module.cpyext.test.test_structseq test_StructSeq - structseq now subclasses tuple on py3, which breaks how - BaseCpyTypeDescr.realize allocates it +... antocuni's older TODO: @@ -20,14 +6,6 @@ * run coverage against the parser/astbuilder/astcompiler: it's probably full of dead code because the grammar changed -* re-enable strategies https://bugs.pypy.org/issue1540 : - - re-enable IntDictStrategy - - re-enable StdObjSpace.listview_str - - re-enable the kwargs dict strategy in dictmultiobject.py - - re-enable view_as_kwargs - -* unskip numpypy tests in module/test_lib_pypy/numpypy/ - * optimize W_UnicodeObject, right now it stores both an unicode and an utf8 version of the same string diff --git a/pypy/doc/build.rst b/pypy/doc/build.rst --- a/pypy/doc/build.rst +++ b/pypy/doc/build.rst @@ -119,7 +119,7 @@ To run untranslated tests, you need the Boehm garbage collector libgc. -On recent Debian and Ubuntu (like 17.04), this is the command to install +On recent Debian and Ubuntu (16.04 onwards), this is the command to install all build-time dependencies:: apt-get install gcc make libffi-dev pkg-config zlib1g-dev libbz2-dev \ @@ -127,7 +127,7 @@ tk-dev libgc-dev python-cffi \ liblzma-dev libncursesw5-dev # these two only needed on PyPy3 -On older Debian and Ubuntu (12.04 to 16.04):: +On older Debian and Ubuntu (12.04-14.04):: apt-get install gcc make libffi-dev pkg-config libz-dev libbz2-dev \ libsqlite3-dev libncurses-dev libexpat1-dev libssl-dev libgdbm-dev \ @@ -149,12 +149,23 @@ xz-devel # For lzma on PyPy3. (XXX plus the SLES11 version of libgdbm-dev and tk-dev) -On Mac OS X, most of these build-time dependencies are installed alongside +On Mac OS X:: + +Most of these build-time dependencies are installed alongside the Developer Tools. However, note that in order for the installation to find them you may need to run:: xcode-select --install +An exception is OpenSSL, which is no longer provided with the operating +system. It can be obtained via Homebrew (with ``$ brew install openssl``), +but it will not be available on the system path by default. The easiest +way to enable it for building pypy is to set an environment variable:: + + export PKG_CONFIG_PATH=$(brew --prefix)/opt/openssl/lib/pkgconfig + +After setting this, translation (described next) will find the OpenSSL libs +as expected. Run the translation ------------------- @@ -187,18 +198,18 @@ entire pypy interpreter. This step is currently singe threaded, and RAM hungry. As part of this step, the chain creates a large number of C code files and a Makefile to compile them in a - directory controlled by the ``PYPY_USESSION_DIR`` environment variable. + directory controlled by the ``PYPY_USESSION_DIR`` environment variable. 2. Create an executable ``pypy-c`` by running the Makefile. This step can - utilize all possible cores on the machine. -3. Copy the needed binaries to the current directory. -4. Generate c-extension modules for any cffi-based stdlib modules. + utilize all possible cores on the machine. +3. Copy the needed binaries to the current directory. +4. Generate c-extension modules for any cffi-based stdlib modules. The resulting executable behaves mostly like a normal Python interpreter (see :doc:`cpython_differences`), and is ready for testing, for use as a base interpreter for a new virtualenv, or for packaging into a binary suitable for installation on another machine running the same OS as the build -machine. +machine. Note that step 4 is merely done as a convenience, any of the steps may be rerun without rerunning the previous steps. @@ -255,7 +266,7 @@ * PyPy 2.5.1 or earlier: normal users would see permission errors. Installers need to run ``pypy -c "import gdbm"`` and other similar - commands at install time; the exact list is in + commands at install time; the exact list is in :source:`pypy/tool/release/package.py `. Users seeing a broken installation of PyPy can fix it after-the-fact if they have sudo rights, by running once e.g. ``sudo pypy -c "import gdbm``. diff --git a/pypy/doc/project-ideas.rst b/pypy/doc/project-ideas.rst --- a/pypy/doc/project-ideas.rst +++ b/pypy/doc/project-ideas.rst @@ -240,9 +240,12 @@ **matplotlib** https://github.com/matplotlib/matplotlib - TODO: the tkagg backend does not work, which makes tests fail on downstream - projects like Pandas, SciPy. It uses id(obj) as a c-pointer to obj in - tkagg.py, which requires refactoring + Status: using the matplotlib branch of PyPy and the tkagg-cffi branch of + matplotlib from https://github.com/mattip/matplotlib/tree/tkagg-cffi, the + tkagg backend can function. + + TODO: the matplotlib branch passes numpy arrays by value (copying all the + data), this proof-of-concept needs help to become completely compliant **wxPython** https://bitbucket.org/amauryfa/wxpython-cffi diff --git a/pypy/doc/release-v5.9.0.rst b/pypy/doc/release-v5.9.0.rst --- a/pypy/doc/release-v5.9.0.rst +++ b/pypy/doc/release-v5.9.0.rst @@ -10,15 +10,18 @@ This new PyPy2.7 release includes the upstream stdlib version 2.7.13, and PyPy3.5 includes the upstream stdlib version 3.5.3. -NumPy and Pandas now work on PyPy2.7. Issues that appeared as excessive memory +NumPy and Pandas now work on PyPy2.7 (together with Cython 0.27.1). Issues +that appeared as excessive memory use were cleared up and other incompatibilities were resolved. The C-API compatibility layer does slow down code which crosses the python-c interface often, we have ideas on how it could be improved, and still recommend using pure python on PyPy or interfacing via CFFI_. Many other modules based on C-API exentions now work on PyPy as well. -Cython 0.27 (released last week) should support more projects with PyPy, both -on PyPy2.7 and PyPy3.5 beta. +Cython 0.27.1 (released very recently) supports more projects with PyPy, both +on PyPy2.7 and PyPy3.5 beta. Note version **0.27.1** is now the minimum +version that supports this version of PyPy, due to some interactions with +updated C-API interface code. We optimized the JSON parser for recurring string keys, which should decrease memory use to 50% and increase parsing speed by up to 15% for large JSON files @@ -148,8 +151,7 @@ * Issue 2590_: fix the bounds in the GC when allocating a lot of objects with finalizers * Replace magical NOT RPYTHON comment with a decorator * Implement ``socket.sendmsg()``/``.recvmsg()`` for py3.5 - * Reduce excessive ``memory_pressure`` for ``_SSLContext`` objects and add - ``memory_pressure`` for ``_SSLSocket`` objects + * Add ``memory_pressure`` for ``_SSLSocket`` objects * Degredations diff --git a/pypy/doc/test/test_whatsnew.py b/pypy/doc/test/test_whatsnew.py --- a/pypy/doc/test/test_whatsnew.py +++ b/pypy/doc/test/test_whatsnew.py @@ -89,7 +89,7 @@ startrev, documented = parse_doc(last_whatsnew) merged, branch = get_merged_branches(ROOT, startrev, '') merged.discard('default') - merged.discard('py3k') + merged.discard('py3.5') merged.discard('') not_documented = merged.difference(documented) not_merged = documented.difference(merged) @@ -100,7 +100,7 @@ print '\n'.join(not_merged) print assert not not_documented - if branch == 'py3k': + if branch == 'py3.5': assert not not_merged else: assert branch in documented, 'Please document this branch before merging: %s' % branch diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -3,4 +3,10 @@ =========================== .. this is a revision shortly after release-pypy2.7-v5.9.0 -.. startrev:899e5245de1e +.. startrev:d56dadcef996 + +.. branch: cppyy-packaging +Cleanup and improve cppyy packaging + +.. branch: docs-osx-brew-openssl + diff --git a/pypy/doc/whatsnew-pypy2-5.9.0.rst b/pypy/doc/whatsnew-pypy2-5.9.0.rst --- a/pypy/doc/whatsnew-pypy2-5.9.0.rst +++ b/pypy/doc/whatsnew-pypy2-5.9.0.rst @@ -85,3 +85,12 @@ .. branch: py_ssize_t Explicitly use Py_ssize_t as the Signed type in pypy c-api + +.. branch: cpyext-jit + +Differentiate the code to call METH_NOARGS, METH_O and METH_VARARGS in cpyext: +this allows to write specialized code which is much faster than previous +completely generic version. Moreover, let the JIT to look inside the cpyext +module: the net result is that cpyext calls are up to 7x faster. However, this +is true only for very simple situations: in all real life code, we are still +much slower than CPython (more optimizations to come) diff --git a/pypy/doc/whatsnew-pypy3-head.rst b/pypy/doc/whatsnew-pypy3-head.rst --- a/pypy/doc/whatsnew-pypy3-head.rst +++ b/pypy/doc/whatsnew-pypy3-head.rst @@ -5,7 +5,9 @@ .. this is the revision after release-pypy3.5-5.9 .. startrev: be41e3ac0a29 -.. branch: multiphase +.. branch: sched_yield +Add sched_yield posix attribute -Implement PyType_FromSpec (PEP 384) and fix issues with PEP 489 support. - +.. branch: py3.5-appexec +Raise if space.is_true(space.appexec()) used in app level tests, fix tests +that did this diff --git a/pypy/interpreter/test/test_app_main.py b/pypy/interpreter/test/test_app_main.py --- a/pypy/interpreter/test/test_app_main.py +++ b/pypy/interpreter/test/test_app_main.py @@ -1162,9 +1162,13 @@ import os old_sys_path = sys.path[:] sys.path.append(self.goal_dir) + if sys.platform == 'win32': + exename = 'pypy3-c.exe' + else: + exename = 'pypy3-c' try: import app_main - pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', 'pypy3-c') + pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', exename) app_main.setup_bootstrap_path(pypy_c) newpath = sys.path[:] # we get at least lib_pypy @@ -1180,9 +1184,13 @@ import os old_sys_path = sys.path[:] sys.path.append(self.goal_dir) + if sys.platform == 'win32': + exename = 'pypy3-c.exe' + else: + exename = 'pypy3-c' try: import app_main - pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', 'pypy3-c') + pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', exename) app_main.entry_point(pypy_c, [self.foo_py]) # assert it did not crash finally: diff --git a/pypy/interpreter/test/test_interpreter.py b/pypy/interpreter/test/test_interpreter.py --- a/pypy/interpreter/test/test_interpreter.py +++ b/pypy/interpreter/test/test_interpreter.py @@ -1,7 +1,6 @@ import py import sys from pypy.interpreter import gateway, module, error -from hypothesis import given, strategies class TestInterpreter: @@ -300,30 +299,6 @@ assert "TypeError:" in res assert "'tuple' object is not a mapping" in res - @given(strategies.lists(strategies.one_of(strategies.none(), - strategies.lists(strategies.none())))) - def test_build_map_order(self, shape): - value = [10] - def build_expr(shape): - if shape is None: - value[0] += 1 - return '0: %d' % value[0] - else: - return '**{%s}' % (', '.join( - [build_expr(shape1) for shape1 in shape]),) - - expr = build_expr(shape)[2:] - code = """ - def f(): - return %s - """ % (expr, ) - res = self.codetest(code, 'f', []) - if value[0] == 10: - expected = {} - else: - expected = {0: value[0]} - assert res == expected, "got %r for %r" % (res, expr) - def test_build_map_unpack_with_call(self): code = """ def f(a,b,c,d): @@ -348,6 +323,36 @@ assert "TypeError:" in resg4 assert "got multiple values for keyword argument 'a'" in resg4 +try: + from hypothesis import given, strategies +except ImportError: + pass +else: + class TestHypothesisInterpreter(TestInterpreter): + @given(strategies.lists(strategies.one_of(strategies.none(), + strategies.lists(strategies.none())))) + def test_build_map_order(self, shape): + value = [10] + def build_expr(shape): + if shape is None: + value[0] += 1 + return '0: %d' % value[0] + else: + return '**{%s}' % (', '.join( + [build_expr(shape1) for shape1 in shape]),) + + expr = build_expr(shape)[2:] + code = """ + def f(): + return %s + """ % (expr, ) + res = self.codetest(code, 'f', []) + if value[0] == 10: + expected = {} + else: + expected = {0: value[0]} + assert res == expected, "got %r for %r" % (res, expr) + class AppTestInterpreter: def test_trivial(self): diff --git a/pypy/module/__builtin__/test/test_descriptor.py b/pypy/module/__builtin__/test/test_descriptor.py --- a/pypy/module/__builtin__/test/test_descriptor.py +++ b/pypy/module/__builtin__/test/test_descriptor.py @@ -258,6 +258,17 @@ raises(TypeError, "super(D).__get__(12)") raises(TypeError, "super(D).__get__(C())") + def test_super_incomplete(self): + """ + class M(type): + def mro(cls): + if cls.__mro__ is None: + raises(AttributeError, lambda: super(cls, cls).xxx) + return type.mro(cls) + class A(metaclass=M): + pass + """ + def test_classmethods_various(self): class C(object): def foo(*a): return a diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py --- a/pypy/module/_cffi_backend/__init__.py +++ b/pypy/module/_cffi_backend/__init__.py @@ -3,7 +3,7 @@ from rpython.rlib import rdynload, clibffi from rpython.rtyper.lltypesystem import rffi -VERSION = "1.11.1" +VERSION = "1.11.2" FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI try: diff --git a/pypy/module/_cffi_backend/cffi1_module.py b/pypy/module/_cffi_backend/cffi1_module.py --- a/pypy/module/_cffi_backend/cffi1_module.py +++ b/pypy/module/_cffi_backend/cffi1_module.py @@ -1,4 +1,5 @@ from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rlib import jit from pypy.interpreter.error import oefmt from pypy.interpreter.module import Module @@ -15,7 +16,7 @@ INITFUNCPTR = lltype.Ptr(lltype.FuncType([rffi.VOIDPP], lltype.Void)) - + at jit.dont_look_inside def load_cffi1_module(space, name, path, initptr): # This is called from pypy.module.cpyext.api.load_extension_module() from pypy.module._cffi_backend.call_python import get_ll_cffi_call_python diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py --- a/pypy/module/_cffi_backend/ctypeptr.py +++ b/pypy/module/_cffi_backend/ctypeptr.py @@ -158,10 +158,11 @@ class W_CTypePtrBase(W_CTypePtrOrArray): # base class for both pointers and pointers-to-functions - _attrs_ = ['is_void_ptr', 'is_voidchar_ptr'] - _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr'] + _attrs_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] + _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] is_void_ptr = False is_voidchar_ptr = False + is_onebyte_ptr = False def convert_to_object(self, cdata): ptrdata = rffi.cast(rffi.CCHARPP, cdata)[0] @@ -181,12 +182,20 @@ if self.is_void_ptr or other.is_void_ptr: pass # cast from or to 'void *' elif self.is_voidchar_ptr or other.is_voidchar_ptr: - space = self.space - msg = ("implicit cast from '%s' to '%s' " - "will be forbidden in the future (check that the types " - "are as you expect; use an explicit ffi.cast() if they " - "are correct)" % (other.name, self.name)) - space.warn(space.newtext(msg), space.w_UserWarning) + # for backward compatibility, accept "char *" as either + # source of target. This is not what C does, though, + # so emit a warning that will eventually turn into an + # error. The warning is turned off if both types are + # pointers to single bytes. + if self.is_onebyte_ptr and other.is_onebyte_ptr: + pass # no warning + else: + space = self.space + msg = ("implicit cast from '%s' to '%s' " + "will be forbidden in the future (check that the types " + "are as you expect; use an explicit ffi.cast() if they " + "are correct)" % (other.name, self.name)) + space.warn(space.newtext(msg), space.w_UserWarning) else: raise self._convert_error("compatible pointer", w_ob) @@ -216,6 +225,7 @@ self.is_void_ptr = isinstance(ctitem, ctypevoid.W_CTypeVoid) self.is_voidchar_ptr = (self.is_void_ptr or isinstance(ctitem, ctypeprim.W_CTypePrimitiveChar)) + self.is_onebyte_ptr = (ctitem.size == 1) W_CTypePtrBase.__init__(self, space, size, extra, 2, ctitem) def newp(self, w_init, allocator): diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -1,7 +1,7 @@ # ____________________________________________________________ import sys -assert __version__ == "1.11.1", ("This test_c.py file is for testing a version" +assert __version__ == "1.11.2", ("This test_c.py file is for testing a version" " of cffi that differs from the one that we" " get from 'import _cffi_backend'") if sys.version_info < (3,): @@ -2099,7 +2099,8 @@ if sys.platform.startswith("linux"): BWChar = new_primitive_type("wchar_t") assert sizeof(BWChar) == 4 - assert int(cast(BWChar, -1)) == -1 # signed, on linux + # wchar_t is often signed on Linux, but not always (e.g. on ARM) + assert int(cast(BWChar, -1)) in (-1, 4294967295) def test_char16(): BChar16 = new_primitive_type("char16_t") @@ -3903,9 +3904,11 @@ BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BVoidP = new_pointer_type(new_void_type()) + BUCharP = new_pointer_type(new_primitive_type("unsigned char")) z1 = cast(BCharP, 0) z2 = cast(BIntP, 0) z3 = cast(BVoidP, 0) + z4 = cast(BUCharP, 0) with warnings.catch_warnings(record=True) as w: newp(new_pointer_type(BIntP), z1) # warn assert len(w) == 1 @@ -3919,6 +3922,12 @@ assert len(w) == 2 newp(new_pointer_type(BIntP), z3) # fine assert len(w) == 2 + newp(new_pointer_type(BCharP), z4) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z1) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z3) # fine + assert len(w) == 2 # check that the warnings are associated with lines in this file assert w[1].lineno == w[0].lineno + 4 diff --git a/pypy/module/_codecs/test/test_codecs.py b/pypy/module/_codecs/test/test_codecs.py --- a/pypy/module/_codecs/test/test_codecs.py +++ b/pypy/module/_codecs/test/test_codecs.py @@ -762,9 +762,16 @@ assert s == b'\xe9' def test_lone_surrogates(self): - for encoding in ('utf-8', 'utf-16', 'utf-16-le', 'utf-16-be', - 'utf-32', 'utf-32-le', 'utf-32-be'): + encodings = ('utf-8', 'utf-16', 'utf-16-le', 'utf-16-be', + 'utf-32', 'utf-32-le', 'utf-32-be') + for encoding in encodings: raises(UnicodeEncodeError, u'\ud800'.encode, encoding) + assert (u'[\udc80]'.encode(encoding, "backslashreplace") == + '[\\udc80]'.encode(encoding)) + assert (u'[\udc80]'.encode(encoding, "ignore") == + '[]'.encode(encoding)) + assert (u'[\udc80]'.encode(encoding, "replace") == + '[?]'.encode(encoding)) def test_charmap_encode(self): assert 'xxx'.encode('charmap') == b'xxx' 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 @@ -290,66 +290,87 @@ 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 # - def g(c): + def bar(c): c.switch(sys._getframe(0)) c.switch(sys._getframe(0).f_back) c.switch(sys._getframe(1)) c.switch(sys._getframe(1).f_back) - assert sys._getframe(2) is f3.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 + 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 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 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/_cppyy/__init__.py b/pypy/module/_cppyy/__init__.py --- a/pypy/module/_cppyy/__init__.py +++ b/pypy/module/_cppyy/__init__.py @@ -1,12 +1,10 @@ from pypy.interpreter.mixedmodule import MixedModule class Module(MixedModule): - "This module provides runtime bindings to C++ code for which reflection\n\ - info has been generated. Current supported back-ends are Reflex and CINT.\n\ - See http://doc.pypy.org/en/latest/cppyy.html for full details." + "This module brigdes the cppyy frontend with its backend, through PyPy.\n\ + See http://cppyy.readthedocs.io/en/latest for full details." interpleveldefs = { - '_load_dictionary' : 'interp_cppyy.load_dictionary', '_resolve_name' : 'interp_cppyy.resolve_name', '_scope_byname' : 'interp_cppyy.scope_byname', '_template_byname' : 'interp_cppyy.template_byname', @@ -15,14 +13,13 @@ '_set_function_generator': 'interp_cppyy.set_function_generator', '_register_class' : 'interp_cppyy.register_class', '_get_nullptr' : 'interp_cppyy.get_nullptr', - 'CPPInstanceBase' : 'interp_cppyy.W_CPPInstance', + 'CPPClassBase' : 'interp_cppyy.W_CPPClass', 'addressof' : 'interp_cppyy.addressof', 'bind_object' : 'interp_cppyy.bind_object', } appleveldefs = { '_init_pythonify' : 'pythonify._init_pythonify', - 'load_reflection_info' : 'pythonify.load_reflection_info', 'add_pythonization' : 'pythonify.add_pythonization', 'Template' : 'pythonify.CPPTemplate', } diff --git a/pypy/module/_cppyy/backend/create_cppyy_package.py b/pypy/module/_cppyy/backend/create_cppyy_package.py deleted file mode 100755 --- a/pypy/module/_cppyy/backend/create_cppyy_package.py +++ /dev/null @@ -1,649 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function - -import os, sys -import argparse, re, shutil, tarfile, urllib2 - - -DEBUG_TESTBUILD = False - -TARBALL_CACHE_DIR = 'releases' - -ROOT_KEEP = ['build', 'cmake', 'config', 'core', 'etc', 'interpreter', - 'io', 'LICENSE', 'net', 'Makefile', 'CMakeLists.txt', 'math', - 'main'] # main only needed in more recent root b/c of rootcling -ROOT_CORE_KEEP = ['CMakeLists.txt', 'base', 'clib', 'clingutils', 'cont', - 'dictgen', 'foundation', 'lzma', 'macosx', 'meta', - 'metacling', 'metautils', 'rootcling_stage1', 'textinput', - 'thread', 'unix', 'utils', 'winnt', 'zip'] -ROOT_IO_KEEP = ['CMakeLists.txt', 'io', 'rootpcm'] -ROOT_NET_KEEP = ['CMakeLists.txt', 'net'] -ROOT_MATH_KEEP = ['CMakeLists.txt', 'mathcore'] -ROOT_ETC_KEEP = ['Makefile.arch', 'class.rules', 'cmake', 'dictpch', - 'gdb-backtrace.sh', 'gitinfo.txt', 'helgrind-root.supp', - 'hostcert.conf', 'system.plugins-ios', - 'valgrind-root-python.supp', 'valgrind-root.supp', 'vmc'] - -ROOT_EXPLICIT_REMOVE = ['core/base/v7', 'math/mathcore/v7', 'io/io/v7'] - - -ERR_RELEASE_NOT_FOUND = 2 - - -# -## CLI arguments -# -class ReleaseValidation(argparse.Action): - def __call__(self, parser, namespace, value, option_string=None): - if not re.match(r'6\.\d\d\.\d\d', value): - raise argparse.ArgumentTypeError( - "release number should of the form '6.dd.dd'") - setattr(namespace, self.dest, value) - return value - -parser = argparse.ArgumentParser( - description='Build PyPi package for cppyy containing the minimum of ROOT') -parser.add_argument('-r', '--release', type=str, nargs='?', - action=ReleaseValidation, help='ROOT release to use') - -args = parser.parse_args() - - -# -## ROOT source pull and cleansing -# -def clean_directory(directory, keeplist, trim_cmake=True): - removed_entries = [] - for entry in os.listdir(directory): - if entry[0] == '.' or entry in keeplist: - continue - removed_entries.append(entry) - entry = os.path.join(directory, entry) - print('now removing', entry) - if os.path.isdir(entry): - shutil.rmtree(entry) - else: - os.remove(entry) - - if not trim_cmake: - return - - # now take the removed entries out of the CMakeLists.txt - if removed_entries: - inp = os.path.join(directory, 'CMakeLists.txt') - print('trimming', inp) - outp = inp+'.new' - new_cml = open(outp, 'w') - for line in open(inp).readlines(): - if ('add_subdirectory' in line) or\ - ('COMMAND' in line and 'copy' in line) or\ - ('ROOT_ADD_TEST_SUBDIRECTORY' in line) or\ - ('install(DIRECTORY' in line): - for sub in removed_entries: - if sub in line: - line = '#'+line - break - new_cml.write(line) - new_cml.close() - os.rename(outp, inp) - else: - print('reusing existing %s/CMakeLists.txt' % (directory,)) - - -class ReleaseValidation(argparse.Action): - def __call__(self, parser, namespace, value, option_string=None): - if not re.match(r'6\.\d\d\.\d\d', value): - raise argparse.ArgumentTypeError( - "release number should of the form '6.dd.dd'") - setattr(namespace, self.dest, value) - return value - -parser = argparse.ArgumentParser( - description='Build PyPi package for cppyy containing the minimum of ROOT') -parser.add_argument('-r', '--release', type=str, nargs='?', - action=ReleaseValidation, help='ROOT release to use') - -args = parser.parse_args() - -if not os.path.exists(TARBALL_CACHE_DIR): - os.mkdir(TARBALL_CACHE_DIR) - -if args.release: - # use provided release - fn = 'root_v%s.source.tar.gz' % args.release - addr = 'https://root.cern.ch/download/'+fn - if not os.path.exists(os.path.join(TARBALL_CACHE_DIR, fn)): - try: - print('retrieving', fn) - resp = urllib2.urlopen(addr, fn) - out = open(os.path.join(TARBALL_CACHE_DIR, fn), 'wb') - out.write(resp.read()) - out.close() - except urllib2.HTTPError: - print('release %s not found' % args.release) - sys.exit(ERR_RELEASE_NOT_FOUND) - else: - print('reusing', fn, 'from local directory') -else: - print('provide release ... getting latest release is not yet implemented ...') - sys.exit(1) - # get latest and set fn, args.release, etc. - -# construct version for package -args.version = '' -testnext = False -for c in args.release: - if testnext: - testnext = False - if c == '0': - continue - if c == '.': - testnext = True - args.version += c -args.version += '.0' - -fn = os.path.join(TARBALL_CACHE_DIR, fn) -pkgdir = os.path.join('root-'+args.release) -if not os.path.exists(pkgdir): - print('now extracting', args.release) - tf = tarfile.TarFile.gzopen(fn) - tf.extractall() - tf.close() -else: - print('reusing existing directory', pkgdir) - -# remove everything except for the listed set of libraries -os.chdir(pkgdir) - -clean_directory(os.path.curdir, ROOT_KEEP) -clean_directory('core', ROOT_CORE_KEEP) -clean_directory('etc', ROOT_ETC_KEEP, trim_cmake=False) -clean_directory('io', ROOT_IO_KEEP) -clean_directory('math', ROOT_MATH_KEEP) -clean_directory('net', ROOT_NET_KEEP) - - -# trim main (only need rootcling) -print('trimming main') -for entry in os.listdir('main/src'): - if entry != 'rootcling.cxx': - os.remove('main/src/'+entry) -inp = 'main/CMakeLists.txt' -outp = inp+'.new' -new_cml = open(outp, 'w') -for line in open(inp).readlines(): - if ('ROOT_EXECUTABLE' in line or\ - 'SET_TARGET_PROPERTIES' in line) and\ - not 'rootcling' in line: - line = '#'+line - new_cml.write(line) -new_cml.close() -os.rename(outp, inp) - - -# remove afterimage and ftgl explicitly -print('trimming externals') -for cmf in ['AfterImage', 'FTGL']: - os.remove('cmake/modules/Find%s.cmake' % (cmf,)) -inp = 'cmake/modules/SearchInstalledSoftware.cmake' -outp = inp+'.new' -now_stripping = False -new_cml = open(outp, 'w') -for line in open(inp).readlines(): - if '#---Check for ftgl if needed' == line[0:28] or\ - '#---Check for AfterImage' == line[0:24]: - now_stripping = True - elif '#---Check' == line[0:9]: - now_stripping = False - if now_stripping: - line = '#'+line - new_cml.write(line) -new_cml.close() -os.rename(outp, inp) - -inp = 'cmake/modules/RootBuildOptions.cmake' -outp = inp+'.new' -new_cml = open(outp, 'w') -for line in open(inp).readlines(): - if 'ROOT_BUILD_OPTION(builtin_ftgl' in line or\ - 'ROOT_BUILD_OPTION(builtin_afterimage' in line: - line = '#'+line - new_cml.write(line) -new_cml.close() -os.rename(outp, inp) - - -# remove testing and examples -print('trimming testing') -inp = 'CMakeLists.txt' -outp = inp+'.new' -now_stripping = False -new_cml = open(outp, 'w') -for line in open(inp).readlines(): - if '#---Configure Testing using CTest' == line[0:33] or\ - '#---hsimple.root' == line[0:16]: - now_stripping = True - elif '#---Packaging' == line[0:13] or\ - '#---version' == line[0:11]: - now_stripping = False - if now_stripping: - line = '#'+line - new_cml.write(line) -new_cml.close() -os.rename(outp, inp) - -print('trimming RootCPack') -inp = 'cmake/modules/RootCPack.cmake' -outp = inp+'.new' -new_cml = open(outp, 'w') -for line in open(inp): - if 'README.txt' in line: - line = '#'+line - new_cml.write(line) -new_cml.close() -os.rename(outp, inp) - -# some more explicit removes: -for dir_to_remove in ROOT_EXPLICIT_REMOVE: - try: - shutil.rmtree(dir_to_remove) - except OSError: - pass - -# special fixes -inp = 'core/base/src/TVirtualPad.cxx' -outp = inp+'.new' -new_cml = open(outp, 'w') -for line in open(inp): - if '#include "X3DBuffer.h"' == line[0:22]: - line = """//#include "X3DBuffer.h" -typedef struct _x3d_sizeof_ { - int numPoints; - int numSegs; - int numPolys; -} Size3D; -""" - new_cml.write(line) -new_cml.close() -os.rename(outp, inp) - -inp = 'math/mathcore/src/Fitter.cxx' -if os.path.exists(inp): - outp = inp+'.new' - new_cml = open(outp, 'w') - for line in open(inp): - if '#include "TF1.h"' in line: - continue - new_cml.write(line) - new_cml.close() - os.rename(outp, inp) - -# done -os.chdir(os.path.pardir) - -# debugging: run a test build -if DEBUG_TESTBUILD: - print('running a debug test build') - tb = "test_builddir" - if os.path.exists(tb): - shutil.rmtree(tb) - os.mkdir(tb) - os.chdir(tb) - os.system('cmake ../%s -DCMAKE_INSTALL_PREFIX=../install -Dminimal=ON -Dasimage=OFF' % pkgdir) - os.system('make -j 32') - - -# -## package creation -# -countdown = 0 -pidir = 'Package-'+args.release -print('creating package', pidir) -if not os.path.exists(pidir): - os.mkdir(pidir) -os.chdir(pidir); countdown += 1 - -print('creating LICENSE.txt') -with open('LICENSE.txt', 'w') as outp: - outp.write("""There are three main parts: - - LLVM: distributed under University of Illinois/NCSA Open Source License - https://opensource.org/licenses/UoI-NCSA.php - ROOT: distributed under LGPL 2.1 - https://root.cern.ch/license - Cppyy: distributed under LBNL BSD - https://fedoraproject.org/wiki/Licensing/LBNLBSD -""") - -print('creating MANIFEST.in') -with open('MANIFEST.in', 'w') as outp: - outp.write("""# Include the license file -include LICENSE.txt - -# Include the data files -recursive-include src * -""") - -print('creating README.rst') -with open('README.rst', 'w') as outp: - outp.write("""PyPy cling-support -================== - ----- - -Find the documentation here: - http://doc.pypy.org/en/latest/cppyy.html -""") - -print('creating setup.cfg') -with open('setup.cfg', 'w') as outp: - outp.write("""[bdist_wheel] -universal=0 -""") - -print('creating setup.py') -with open('setup.py', 'w') as outp: - outp.write("""import os, sys, subprocess -from setuptools import setup, find_packages -from distutils import log -from distutils.command.build import build as _build -from setuptools.command.install import install as _install -from distutils.sysconfig import get_python_lib -from distutils.errors import DistutilsSetupError -from codecs import open - -here = os.path.abspath(os.path.dirname(__file__)) -with open(os.path.join(here, 'README.rst'), encoding='utf-8') as f: - long_description = f.read() - -builddir = None -def get_builddir(): - global builddir - if builddir is None: - topdir = os.getcwd() - builddir = os.path.join(topdir, 'builddir') - return builddir - -srcdir = None -def get_srcdir(): - global srcdir - if srcdir is None: - topdir = os.getcwd() - srcdir = os.path.join(topdir, 'src', 'backend') - return srcdir - -class my_cmake_build(_build): - def __init__(self, dist, *args, **kwargs): - _build.__init__(self, dist, *args, **kwargs) - # TODO: can't seem to find a better way of getting hold of - # the install_lib parameter during the build phase ... - prefix = '' - try: - prefix = dist.get_command_obj('install').install_lib - except AttributeError: - pass - if not prefix: - prefix = get_python_lib(1, 0) - self.prefix = os.path.join(prefix, 'cppyy_backend') - - def run(self): - # base run - _build.run(self) - - # custom run - log.info('Now building libcppyy_backend.so and dependencies') - builddir = get_builddir() - srcdir = get_srcdir() - if not os.path.exists(builddir): - log.info('Creating build directory %s ...' % builddir) - os.makedirs(builddir) - - os.chdir(builddir) - log.info('Running cmake for cppyy_backend') - if subprocess.call([ - 'cmake', srcdir, '-Dminimal=ON -Dasimage=OFF', - '-DCMAKE_INSTALL_PREFIX='+self.prefix]) != 0: - raise DistutilsSetupError('Failed to configure cppyy_backend') - - nprocs = os.getenv("MAKE_NPROCS") - if nprocs: - try: - ival = int(nprocs) - nprocs = '-j'+nprocs - except ValueError: - log.warn("Integer expected for MAKE_NPROCS, but got %s (ignored)", nprocs) - nprocs = '-j1' - else: - nprocs = '-j1' - log.info('Now building cppyy_backend and dependencies ...') - if subprocess.call(['make', nprocs]) != 0: - raise DistutilsSetupError('Failed to build cppyy_backend') - - log.info('build finished') - -class my_libs_install(_install): - def run(self): - # base install - _install.run(self) - - # custom install - log.info('Now installing libcppyy_backend.so and dependencies') - builddir = get_builddir() - if not os.path.exists(builddir): - raise DistutilsSetupError('Failed to find build dir!') - os.chdir(builddir) - - prefix = self.install_lib - log.info('Now installing in %s ...', prefix) - if subprocess.call(['make', 'install']) != 0: - raise DistutilsSetupError('Failed to install cppyy_backend') - - log.info('install finished') - - def get_outputs(self): - outputs = _install.get_outputs(self) - outputs.append(os.path.join(self.install_lib, 'cppyy_backend')) - return outputs - -setup( - name='PyPy-cppyy-backend', -""") - outp.write(" version='%s', # corresponds to ROOT %s, extra number is for packager\n"\ - % (args.version, args.release)) - outp.write(""" description='Cling support for PyPy', - long_description=long_description, - - url='http://pypy.org', - - # Author details - author='PyPy Developers', - author_email='pypy-dev at python.org', - - license='LLVM: UoI-NCSA; ROOT: LGPL 2.1; Cppyy: LBNL BSD', - - classifiers=[ - 'Development Status :: 4 - Beta', - - 'Intended Audience :: Developers', - - 'Topic :: Software Development', - 'Topic :: Software Development :: Interpreters', - - #'License :: OSI Approved :: MIT License', - - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: Implementation :: PyPy', - 'Programming Language :: C', - 'Programming Language :: C++', - - 'Natural Language :: English' - ], - - keywords='interpreter development', - - packages=find_packages('src', ['backend']), - include_package_data=True, - - extras_require={ - }, - - cmdclass = { - 'build': my_cmake_build, - 'install': my_libs_install, - }, -) -""") - - -print('creating src ... ROOT part') -if not os.path.exists('src'): - os.mkdir('src') -os.chdir('src'); countdown += 1 -if not os.path.exists('backend'): - src = os.path.join(os.path.pardir, os.path.pardir, pkgdir) - print('now copying', src) - shutil.copytree(src, 'backend') - -print('creating src ... cppyy part') -os.chdir('backend'); countdown += 1 -if not os.path.exists('cppyy'): - os.mkdir('cppyy') - os.chdir('cppyy'); countdown += 1 - - with open('CMakeLists.txt', 'w') as outp: - outp.write("""############################################################################ -# CMakeLists.txt file for building cppyy package -############################################################################ - -ROOT_GLOB_SOURCES(sources ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cxx) -set_source_files_properties(${sources} COMPILE_FLAGS "-fomit-frame-pointer -fvisibility=hidden -DRPY_EXTERN=RPY_EXPORTED -DRPYTHON_LL2CTYPES") - -add_definitions(${CLING_CXXFLAGS}) - -ROOT_LINKER_LIBRARY(cppyy_backend ${sources} - LIBRARIES ${CMAKE_DL_LIBS} - DEPENDENCIES Core Cling RIO Thread) - -add_dependencies(cppyy_backend CLING) -""") - - os.mkdir('src') - os.chdir('src'); countdown += 1 - print('pulling cppyy/clingcwrapper.cxx from pypy') - base = 'https://bitbucket.org/pypy/pypy/raw/default/pypy/module/cppyy/' - for cppyy_file in ['src/callcontext.h', 'include/capi.h', 'src/clingcwrapper.cxx', - 'include/clingcwrapper.h', 'include/cpp_cppyy.h', 'include/cppyy.h']: - resp = urllib2.urlopen(base+cppyy_file) - with open(os.path.basename(cppyy_file), 'w') as outp: - outp.write(resp.read()) - - # fix include - inp = 'capi.h' - outp = inp+'.new' - new_cml = open(outp, 'w') - for line in open(inp).readlines(): - if 'src/precommondefs.h' in line: - line = '#include "precommondefs.h"\n' - new_cml.write(line) - new_cml.close() - os.rename(outp, inp) - - with open('precommondefs.h', 'w') as outp: - outp.write("""/***** Start of precommondefs.h *****/ - -/* This is extracted from pyconfig.h from CPython. It sets the macros - that affect the features we get from system include files. - It must not #include anything. */ - -#ifndef __PYPY_PRECOMMONDEFS_H -#define __PYPY_PRECOMMONDEFS_H - - -/* Define on Darwin to activate all library features */ -#define _DARWIN_C_SOURCE 1 -/* This must be set to 64 on some systems to enable large file support. */ -#define _FILE_OFFSET_BITS 64 -/* Define on Linux to activate all library features */ -#define _GNU_SOURCE 1 -/* This must be defined on some systems to enable large file support. */ -#define _LARGEFILE_SOURCE 1 -/* Define on NetBSD to activate all library features */ -#define _NETBSD_SOURCE 1 -/* Define to activate features from IEEE Stds 1003.1-2001 */ -#ifndef _POSIX_C_SOURCE -# define _POSIX_C_SOURCE 200112L -#endif -/* Define on FreeBSD to activate all library features */ -#define __BSD_VISIBLE 1 -#define __XSI_VISIBLE 700 -/* Windows: winsock/winsock2 mess */ -#define WIN32_LEAN_AND_MEAN -#ifdef _WIN64 - typedef __int64 Signed; - typedef unsigned __int64 Unsigned; -# define SIGNED_MIN LLONG_MIN -#else - typedef long Signed; - typedef unsigned long Unsigned; -# define SIGNED_MIN LONG_MIN -#endif - -#if !defined(RPY_ASSERT) && !defined(RPY_LL_ASSERT) && !defined(NDEBUG) -# define NDEBUG -#endif - - -/* All functions and global variables declared anywhere should use - one of the following attributes: - - RPY_EXPORTED: the symbol is exported out of libpypy-c.so. - - RPY_EXTERN: the symbol is not exported out of libpypy-c.so, but - otherwise works like 'extern' by being available to - other C sources. - - static: as usual, this means the symbol is local to this C file. - - Don't use _RPY_HIDDEN directly. For tests involving building a custom - .so, translator/tool/cbuild.py overrides RPY_EXTERN so that it becomes - equal to RPY_EXPORTED. - - Any function or global variable declared with no attribute at all is - a bug; please report or fix it. -*/ -#ifdef __GNUC__ -# define RPY_EXPORTED extern __attribute__((visibility("default"))) -# define _RPY_HIDDEN __attribute__((visibility("hidden"))) -#else -# define RPY_EXPORTED extern __declspec(dllexport) -# define _RPY_HIDDEN /* nothing */ -#endif -#ifndef RPY_EXTERN -# define RPY_EXTERN extern _RPY_HIDDEN -#endif - - -#endif /* __PYPY_PRECOMMONDEFS_H */ - -/***** End of precommondefs.h *****/ -""") - -# back up to pip package top -for i in range(countdown-1): - os.chdir(os.path.pardir) - -# add cppyy module to cmake -os.chdir('src/backend') -inp = 'CMakeLists.txt' -print('adding cppyy to cmake') -outp = inp+'.new' -new_cml = open(outp, 'w') -for line in open(inp).readlines(): - if 'add_subdirectory' in line and 'net' in line: - line += 'add_subdirectory (cppyy)\n' - new_cml.write(line) -new_cml.close() -os.rename(outp, inp) - -# done! diff --git a/pypy/module/_cppyy/bench/Makefile b/pypy/module/_cppyy/bench/Makefile deleted file mode 100644 --- a/pypy/module/_cppyy/bench/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -all: bench02Dict_reflex.so - -ROOTSYS := ${ROOTSYS} - -ifeq ($(ROOTSYS),) - genreflex=genreflex - cppflags= -else - genreflex=$(ROOTSYS)/bin/genreflex - cppflags=-I$(ROOTSYS)/include -L$(ROOTSYS)/lib -endif - -PLATFORM := $(shell uname -s) -ifeq ($(PLATFORM),Darwin) - cppflags+=-dynamiclib -single_module -arch x86_64 -endif - -ifeq ($(shell $(genreflex) --help | grep -- --with-methptrgetter),) - genreflexflags= - cppflags2=-O3 -fPIC -else - genreflexflags=--with-methptrgetter - cppflags2=-Wno-pmf-conversions -O3 -fPIC -endif - - -bench02Dict_reflex.so: bench02.h bench02.cxx bench02.xml - $(genreflex) bench02.h $(genreflexflags) --selection=bench02.xml -I$(ROOTSYS)/include - g++ -o $@ bench02.cxx bench02_rflx.cpp -I$(ROOTSYS)/include -shared -std=c++11 -lHistPainter `root-config --libs` $(cppflags) $(cppflags2) diff --git a/pypy/module/_cppyy/bench/bench02.cxx b/pypy/module/_cppyy/bench/bench02.cxx deleted file mode 100644 --- a/pypy/module/_cppyy/bench/bench02.cxx +++ /dev/null @@ -1,79 +0,0 @@ -#include "bench02.h" - -#include "TROOT.h" -#include "TApplication.h" -#include "TDirectory.h" -#include "TInterpreter.h" -#include "TSystem.h" -#include "TBenchmark.h" -#include "TStyle.h" -#include "TError.h" -#include "Getline.h" -#include "TVirtualX.h" - -#include "Api.h" - -#include - -TClass *TClass::GetClass(const char*, Bool_t, Bool_t) { - static TClass* dummy = new TClass("__dummy__", kTRUE); - return dummy; // is deleted by gROOT at shutdown -} - -class TTestApplication : public TApplication { -public: - TTestApplication( - const char* acn, Int_t* argc, char** argv, Bool_t bLoadLibs = kTRUE); - virtual ~TTestApplication(); -}; - -TTestApplication::TTestApplication( - const char* acn, int* argc, char** argv, bool do_load) : TApplication(acn, argc, argv) { - if (do_load) { - // follow TRint to minimize differences with CINT - ProcessLine("#include ", kTRUE); - ProcessLine("#include <_string>", kTRUE); // for std::string iostream. - ProcessLine("#include ", kTRUE); // needed because they're used within the - ProcessLine("#include ", kTRUE); // core ROOT dicts and CINT won't be able - // to properly unload these files - } - - // save current interpreter context - gInterpreter->SaveContext(); - gInterpreter->SaveGlobalsContext(); - - // prevent crashes on accessing history - Gl_histinit((char*)"-"); - - // prevent ROOT from exiting python - SetReturnFromRun(kTRUE); -} - -TTestApplication::~TTestApplication() {} - -static const char* appname = "pypy-cppyy"; - -Bench02RootApp::Bench02RootApp() { - gROOT->SetBatch(kTRUE); - if (!gApplication) { - int argc = 1; - char* argv[1]; argv[0] = (char*)appname; - gApplication = new TTestApplication(appname, &argc, argv, kFALSE); - } -} - -Bench02RootApp::~Bench02RootApp() { - // TODO: ROOT globals cleanup ... (?) -} - -void Bench02RootApp::report() { - std::cout << "gROOT is: " << gROOT << std::endl; - std::cout << "gApplication is: " << gApplication << std::endl; -} - -void Bench02RootApp::close_file(TFile* f) { - std::cout << "closing file " << f->GetName() << " ... " << std::endl; - f->Write(); - f->Close(); - std::cout << "... file closed" << std::endl; -} diff --git a/pypy/module/_cppyy/bench/bench02.h b/pypy/module/_cppyy/bench/bench02.h deleted file mode 100644 --- a/pypy/module/_cppyy/bench/bench02.h +++ /dev/null @@ -1,72 +0,0 @@ -#include "TString.h" - -#include "TCanvas.h" -#include "TFile.h" -#include "TProfile.h" -#include "TNtuple.h" -#include "TH1F.h" -#include "TH2F.h" -#include "TRandom.h" -#include "TRandom3.h" - -#include "TROOT.h" -#include "TApplication.h" -#include "TSystem.h" - -#include "TArchiveFile.h" -#include "TBasket.h" -#include "TBenchmark.h" -#include "TBox.h" -#include "TBranchRef.h" -#include "TBrowser.h" -#include "TClassGenerator.h" -#include "TClassRef.h" -#include "TClassStreamer.h" -#include "TContextMenu.h" -#include "TEntryList.h" -#include "TEventList.h" -#include "TF1.h" -#include "TFileCacheRead.h" -#include "TFileCacheWrite.h" -#include "TFileMergeInfo.h" -#include "TFitResult.h" -#include "TFolder.h" -//#include "TFormulaPrimitive.h" -#include "TFunction.h" -#include "TFrame.h" -#include "TGlobal.h" -#include "THashList.h" -#include "TInetAddress.h" -#include "TInterpreter.h" -#include "TKey.h" -#include "TLegend.h" -#include "TMethodCall.h" -#include "TPluginManager.h" -#include "TProcessUUID.h" -#include "TSchemaRuleSet.h" -#include "TStyle.h" -#include "TSysEvtHandler.h" -#include "TTimer.h" -#include "TView.h" -//#include "TVirtualCollectionProxy.h" -#include "TVirtualFFT.h" -#include "TVirtualHistPainter.h" -#include "TVirtualIndex.h" -#include "TVirtualIsAProxy.h" -#include "TVirtualPadPainter.h" -#include "TVirtualRefProxy.h" -#include "TVirtualStreamerInfo.h" -#include "TVirtualViewer3D.h" - -#include -#include - - -class Bench02RootApp { -public: - Bench02RootApp(); - ~Bench02RootApp(); - - void report(); - void close_file(TFile* f); -}; diff --git a/pypy/module/_cppyy/bench/bench02.xml b/pypy/module/_cppyy/bench/bench02.xml deleted file mode 100644 --- a/pypy/module/_cppyy/bench/bench02.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pypy/module/_cppyy/bench/hsimple.C b/pypy/module/_cppyy/bench/hsimple.C deleted file mode 100644 --- a/pypy/module/_cppyy/bench/hsimple.C +++ /dev/null @@ -1,109 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -TFile *hsimple(Int_t get=0) -{ -// This program creates : -// - a one dimensional histogram -// - a two dimensional histogram -// - a profile histogram -// - a memory-resident ntuple -// -// These objects are filled with some random numbers and saved on a file. -// If get=1 the macro returns a pointer to the TFile of "hsimple.root" -// if this file exists, otherwise it is created. -// The file "hsimple.root" is created in $ROOTSYS/tutorials if the caller has -// write access to this directory, otherwise the file is created in $PWD - - TString filename = "hsimple.root"; - TString dir = gSystem->UnixPathName(gInterpreter->GetCurrentMacroName()); - dir.ReplaceAll("hsimple.C",""); - dir.ReplaceAll("/./","/"); - TFile *hfile = 0; - if (get) { - // if the argument get =1 return the file "hsimple.root" - // if the file does not exist, it is created - TString fullPath = dir+"hsimple.root"; - if (!gSystem->AccessPathName(fullPath,kFileExists)) { - hfile = TFile::Open(fullPath); //in $ROOTSYS/tutorials - if (hfile) return hfile; - } - //otherwise try $PWD/hsimple.root - if (!gSystem->AccessPathName("hsimple.root",kFileExists)) { - hfile = TFile::Open("hsimple.root"); //in current dir - if (hfile) return hfile; - } - } - //no hsimple.root file found. Must generate it ! - //generate hsimple.root in $ROOTSYS/tutorials if we have write access - if (!gSystem->AccessPathName(dir,kWritePermission)) { - filename = dir+"hsimple.root"; - } else if (!gSystem->AccessPathName(".",kWritePermission)) { - //otherwise generate hsimple.root in the current directory - } else { - printf("you must run the script in a directory with write access\n"); - return 0; - } - hfile = (TFile*)gROOT->FindObject(filename); if (hfile) hfile->Close(); - hfile = new TFile(filename,"RECREATE","Demo ROOT file with histograms"); - - // Create some histograms, a profile histogram and an ntuple - TH1F *hpx = new TH1F("hpx","This is the px distribution",100,-4,4); - hpx->SetFillColor(48); - TH2F *hpxpy = new TH2F("hpxpy","py vs px",40,-4,4,40,-4,4); - TProfile *hprof = new TProfile("hprof","Profile of pz versus px",100,-4,4,0,20); - TNtuple *ntuple = new TNtuple("ntuple","Demo ntuple","px:py:pz:random:i"); - - gBenchmark->Start("hsimple"); - - // Create a new canvas. - TCanvas *c1 = new TCanvas("c1","Dynamic Filling Example",200,10,700,500); - c1->SetFillColor(42); - c1->GetFrame()->SetFillColor(21); - c1->GetFrame()->SetBorderSize(6); - c1->GetFrame()->SetBorderMode(-1); - - - // Fill histograms randomly - TRandom3 random; - Float_t px, py, pz; - const Int_t kUPDATE = 1000; - for (Int_t i = 0; i < 50000; i++) { - // random.Rannor(px,py); - px = random.Gaus(0, 1); - py = random.Gaus(0, 1); - pz = px*px + py*py; - Float_t rnd = random.Rndm(1); - hpx->Fill(px); - hpxpy->Fill(px,py); - hprof->Fill(px,pz); - ntuple->Fill(px,py,pz,rnd,i); - if (i && (i%kUPDATE) == 0) { - if (i == kUPDATE) hpx->Draw(); - c1->Modified(); - c1->Update(); - if (gSystem->ProcessEvents()) - break; - } From pypy.commits at gmail.com Mon Oct 30 06:19:07 2017 From: pypy.commits at gmail.com (antocuni) Date: Mon, 30 Oct 2017 03:19:07 -0700 (PDT) Subject: [pypy-commit] extradoc extradoc: syntax highlighting, and a tweak Message-ID: <59f6fc9b.6896df0a.9740d.c45d@mx.google.com> Author: Antonio Cuni Branch: extradoc Changeset: r5853:8eccb15f4dd9 Date: 2017-10-30 10:45 +0100 http://bitbucket.org/pypy/extradoc/changeset/8eccb15f4dd9/ Log: syntax highlighting, and a tweak diff --git a/blog/draft/2017-10-how-to-make-80x-faster.rst b/blog/draft/2017-10-how-to-make-80x-faster.rst --- a/blog/draft/2017-10-how-to-make-80x-faster.rst +++ b/blog/draft/2017-10-how-to-make-80x-faster.rst @@ -26,7 +26,9 @@ However, for the scope of this post, the actual task at hand is not so important, so let's jump straight to the code. To drive the quadcopter, a ``Creature`` has a ``run_step`` method which runs at each ``delta_t`` (`full -code`_):: +code`_): + +.. sourcecode:: python class Creature(object): INPUTS = 2 # z_setpoint, current z position @@ -59,11 +61,13 @@ .. _`full code`: https://github.com/antocuni/evolvingcopter/blob/master/ev/creature.py -``run_step`` is run at 100Hz (in the virtual time frame of the simulation). At each +``run_step`` is called at 100Hz (in the virtual time frame of the simulation). At each generation, we test 500 creatures for a total of 12 virtual seconds each. So, we have a total of 600,000 executions of ``run_step`` at each generation. -At first, I simply tried to run this code on CPython; here is the result:: +At first, I simply tried to run this code on CPython; here is the result: + +.. sourcecode:: $ python -m ev.main Generation 1: ... [population = 500] [12.06 secs] @@ -75,7 +79,9 @@ Which means ~6.15 seconds/generation, excluding the first. -Then I tried with PyPy 5.9:: +Then I tried with PyPy 5.9: + +.. sourcecode:: $ pypy -m ev.main Generation 1: ... [population = 500] [63.90 secs] @@ -90,7 +96,9 @@ So, let's try to avoid cpyext. The first obvious step is to use numpypy_ instead of numpy (actually, there a hack_ to use just the micronumpy -part). Let's see if the speed improves:: +part). Let's see if the speed improves: + +.. sourcecode:: $ pypy -m ev.main # using numpypy Generation 1: ... [population = 500] [5.60 secs] @@ -154,8 +162,8 @@ All of this is very suboptimal: in this particular case, we know that the shape of ``self.matrix`` is always ``(3, 2)``: so, we are doing an incredible -amount of work. We also use ``malloc()`` to create a temporary array just to -call an RPython function which ultimately does a total of 6 multiplications +amount of work. We also ``malloc()`` two temporary arrays just to +call two functions which ultimately do a total of 6 multiplications and 8 additions. Note also that this is not a fault of the JIT: CPython+numpy has to do the same amount of work, just hidden inside C calls. @@ -166,7 +174,9 @@ is the main reason why the PyPy JIT does not even try to do it in this case. However, we **know** that the matrix is small, and always of the same -shape. So, let's unroll the loop manually:: +shape. So, let's unroll the loop manually: + +.. sourcecode:: python class SpecializedCreature(Creature): @@ -204,7 +214,9 @@ .. _`actual code`: https://github.com/antocuni/evolvingcopter/blob/master/ev/creature.py#L100 .. _`list strategies`: https://morepypy.blogspot.it/2011/10/more-compact-lists-with-list-strategies.html -So, let's try to see how it performs. First, with CPython:: +So, let's try to see how it performs. First, with CPython: + +.. sourcecode:: $ python -m ev.main Generation 1: ... [population = 500] [7.61 secs] @@ -215,7 +227,9 @@ Generation 6: ... [population = 500] [3.69 secs] This looks good: 60% faster than the original CPython+numpy -implementation. Let's try on PyPy:: +implementation. Let's try on PyPy: + +.. sourcecode:: Generation 1: ... [population = 500] [0.39 secs] Generation 2: ... [population = 500] [0.10 secs] @@ -265,7 +279,7 @@ How to reproduce the results ----------------------------- -:: +.. sourcecode:: $ git clone https://github.com/antocuni/evolvingcopter $ cd evolvingcopter From pypy.commits at gmail.com Mon Oct 30 06:19:09 2017 From: pypy.commits at gmail.com (antocuni) Date: Mon, 30 Oct 2017 03:19:09 -0700 (PDT) Subject: [pypy-commit] extradoc extradoc: remove the reference to list strategies: it's not true, since self.data items are of type numpy.float64. Bah. Message-ID: <59f6fc9d.e887df0a.2dc18.98e0@mx.google.com> Author: Antonio Cuni Branch: extradoc Changeset: r5854:03cc5eb071c6 Date: 2017-10-30 10:47 +0100 http://bitbucket.org/pypy/extradoc/changeset/03cc5eb071c6/ Log: remove the reference to list strategies: it's not true, since self.data items are of type numpy.float64. Bah. diff --git a/blog/draft/2017-10-how-to-make-80x-faster.rst b/blog/draft/2017-10-how-to-make-80x-faster.rst --- a/blog/draft/2017-10-how-to-make-80x-faster.rst +++ b/blog/draft/2017-10-how-to-make-80x-faster.rst @@ -182,8 +182,7 @@ def __init__(self, *args, **kwargs): Creature.__init__(self, *args, **kwargs) - # store the data in a plain Python list, which pypy is able to - # optimize as a float array + # store the data in a plain Python list self.data = list(self.matrix.ravel()) + list(self.constant) self.data_state = [0.0] assert self.matrix.shape == (2, 3) @@ -207,12 +206,7 @@ In the `actual code`_ there is also a sanity check which asserts that the computed output is the very same as the one returned by ``Creature.run_step``. -Note that is code is particularly PyPy-friendly. Thanks to PyPy's `list strategies`_ -optimizations, ``self.data`` as a simple list of floats is internally represented -as a flat array of C doubles, i.e. very fast and compact. - .. _`actual code`: https://github.com/antocuni/evolvingcopter/blob/master/ev/creature.py#L100 -.. _`list strategies`: https://morepypy.blogspot.it/2011/10/more-compact-lists-with-list-strategies.html So, let's try to see how it performs. First, with CPython: From pypy.commits at gmail.com Mon Oct 30 06:19:12 2017 From: pypy.commits at gmail.com (antocuni) Date: Mon, 30 Oct 2017 03:19:12 -0700 (PDT) Subject: [pypy-commit] extradoc extradoc: another tweak Message-ID: <59f6fca0.e291df0a.1229.ed9c@mx.google.com> Author: Antonio Cuni Branch: extradoc Changeset: r5855:c2f10979b271 Date: 2017-10-30 10:49 +0100 http://bitbucket.org/pypy/extradoc/changeset/c2f10979b271/ Log: another tweak diff --git a/blog/draft/2017-10-how-to-make-80x-faster.rst b/blog/draft/2017-10-how-to-make-80x-faster.rst --- a/blog/draft/2017-10-how-to-make-80x-faster.rst +++ b/blog/draft/2017-10-how-to-make-80x-faster.rst @@ -256,7 +256,7 @@ As I said before, this is a very particular example, and the techniques described here do not always apply: it is not realistic to expect an 80x -speedup, unfortunately. However, it clearly shows the potential of PyPy when +speedup on arbitrary code, unfortunately. However, it clearly shows the potential of PyPy when it comes to high-speed computing. And most importantly, it's not a toy benchmark which was designed specifically to have good performance on PyPy: it's a real world example, albeit small. From pypy.commits at gmail.com Mon Oct 30 08:52:52 2017 From: pypy.commits at gmail.com (spapanik) Date: Mon, 30 Oct 2017 05:52:52 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: Add inf and nan to cmath Message-ID: <59f720a4.178fdf0a.a7138.ac0b@mx.google.com> Author: Steve Papanik Branch: py3.6 Changeset: r92879:4a3e6bc30cc8 Date: 2017-10-30 12:46 +0000 http://bitbucket.org/pypy/pypy/changeset/4a3e6bc30cc8/ Log: Add inf and nan to cmath This also adds the corresponding complex constants. diff --git a/pypy/module/cmath/__init__.py b/pypy/module/cmath/__init__.py --- a/pypy/module/cmath/__init__.py +++ b/pypy/module/cmath/__init__.py @@ -40,6 +40,10 @@ interpleveldefs = { 'pi': 'space.newfloat(interp_cmath.pi)', 'e': 'space.newfloat(interp_cmath.e)', + 'inf': 'space.newfloat(interp_cmath.inf)', + 'nan': 'space.newfloat(interp_cmath.nan)', + 'infj': 'space.newcomplex(0.0, interp_cmath.inf)', + 'nanj': 'space.newcomplex(0.0, interp_cmath.nan)', 'isclose': 'interp_cmath.isclose', } interpleveldefs.update(dict([(name, 'interp_cmath.wrapped_' + name) diff --git a/pypy/module/cmath/interp_cmath.py b/pypy/module/cmath/interp_cmath.py --- a/pypy/module/cmath/interp_cmath.py +++ b/pypy/module/cmath/interp_cmath.py @@ -6,8 +6,13 @@ from pypy.module.cmath import names_and_docstrings from rpython.rlib import rcomplex, rfloat -pi = math.pi -e = math.e +pi = math.pi +e = math.e +inf = float('inf') +nan = float('nan') +infj = complex(0.0, inf) +nanj = complex(0.0, nan) + @specialize.arg(0) def call_c_func(c_func, space, x, y): diff --git a/pypy/module/cmath/test/test_cmath.py b/pypy/module/cmath/test/test_cmath.py --- a/pypy/module/cmath/test/test_cmath.py +++ b/pypy/module/cmath/test/test_cmath.py @@ -134,6 +134,24 @@ assert cmath.isclose(100000j, 100001j, abs_tol=1.5) assert not cmath.isclose(100000j, 100001j, abs_tol=0.5) + def test_infinity_and_nan_constants(self): + import cmath, math + assert cmath.inf.real == math.inf + assert cmath.inf.imag == 0.0 + assert cmath.infj.real == 0.0 + assert cmath.infj.imag == math.inf + + assert math.isnan(cmath.nan.real) + assert cmath.nan.imag == 0.0 + assert cmath.nanj.real == 0.0 + assert math.isnan(cmath.nanj.imag) + + # Check consistency with reprs. + assert repr(cmath.inf) == "inf" + assert repr(cmath.infj) == "infj" + assert repr(cmath.nan) == "nan" + assert repr(cmath.nanj) == "nanj" + def parse_testfile(fname): """Parse a file with test values @@ -211,7 +229,7 @@ def test_specific_values(): #if not float.__getformat__("double").startswith("IEEE"): # return - + import rpython # too fragile... fname = os.path.join(os.path.dirname(rpython.rlib.__file__), 'test', 'rcomplex_testcases.txt') From pypy.commits at gmail.com Mon Oct 30 10:10:49 2017 From: pypy.commits at gmail.com (thisch) Date: Mon, 30 Oct 2017 07:10:49 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: Comment-out unsupported crypto algorithms Message-ID: <59f732e9.28361c0a.ea46.ae89@mx.google.com> Author: Thomas Hisch Branch: py3.6 Changeset: r92880:4b8536756542 Date: 2017-10-30 13:59 +0000 http://bitbucket.org/pypy/pypy/changeset/4b8536756542/ Log: Comment-out unsupported crypto algorithms diff --git a/lib-python/3/hashlib.py b/lib-python/3/hashlib.py --- a/lib-python/3/hashlib.py +++ b/lib-python/3/hashlib.py @@ -56,9 +56,12 @@ # This tuple and __get_builtin_constructor() must be modified if a new # always available algorithm is added. __always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', - 'blake2b', 'blake2s', - 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', - 'shake_128', 'shake_256') + # FIXME the following algorithms were added in + # cpython3.6 but are missing in pypy + # 'blake2b', 'blake2s', + # 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', # + # 'shake_128', 'shake_256' +) algorithms_guaranteed = set(__always_supported) @@ -78,7 +81,7 @@ try: if name in ('SHA1', 'sha1'): import _sha1 - cache['SHA1'] = cache['sha1'] = _sha1.sha1 + cache['SHA1'] = cache['sha1'] = _sha1py.sha1 elif name in ('MD5', 'md5'): import _md5 cache['MD5'] = cache['md5'] = _md5.md5 From pypy.commits at gmail.com Mon Oct 30 10:10:51 2017 From: pypy.commits at gmail.com (thisch) Date: Mon, 30 Oct 2017 07:10:51 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: Fix typo introduced previous commit Message-ID: <59f732eb.87bbdf0a.c2a6c.5196@mx.google.com> Author: Thomas Hisch Branch: py3.6 Changeset: r92881:bcfbc11f8ebd Date: 2017-10-30 14:07 +0000 http://bitbucket.org/pypy/pypy/changeset/bcfbc11f8ebd/ Log: Fix typo introduced previous commit diff --git a/lib-python/3/hashlib.py b/lib-python/3/hashlib.py --- a/lib-python/3/hashlib.py +++ b/lib-python/3/hashlib.py @@ -59,7 +59,7 @@ # FIXME the following algorithms were added in # cpython3.6 but are missing in pypy # 'blake2b', 'blake2s', - # 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', # + # 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', # 'shake_128', 'shake_256' ) @@ -81,7 +81,7 @@ try: if name in ('SHA1', 'sha1'): import _sha1 - cache['SHA1'] = cache['sha1'] = _sha1py.sha1 + cache['SHA1'] = cache['sha1'] = _sha1.sha1 elif name in ('MD5', 'md5'): import _md5 cache['MD5'] = cache['md5'] = _md5.md5 From pypy.commits at gmail.com Mon Oct 30 11:29:58 2017 From: pypy.commits at gmail.com (...@funkyhat.org) Date: Mon, 30 Oct 2017 08:29:58 -0700 (PDT) Subject: [pypy-commit] pypy py3.6: Support b2a_base64 newline kwarg (new in 3.6) Message-ID: <59f74576.8dd71c0a.ee3de.dd99@mx.google.com> Author: m at funkyhat.org Branch: py3.6 Changeset: r92882:652e45c71368 Date: 2017-10-30 15:18 +0000 http://bitbucket.org/pypy/pypy/changeset/652e45c71368/ Log: Support b2a_base64 newline kwarg (new in 3.6) diff --git a/pypy/module/binascii/interp_base64.py b/pypy/module/binascii/interp_base64.py --- a/pypy/module/binascii/interp_base64.py +++ b/pypy/module/binascii/interp_base64.py @@ -78,8 +78,8 @@ table_b2a_base64 = ( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") - at unwrap_spec(bin='bufferstr') -def b2a_base64(space, bin): + at unwrap_spec(bin='bufferstr', newline=bool) +def b2a_base64(space, bin, __kwonly__, newline=True): "Base64-code line of data." newlength = (len(bin) + 2) // 3 @@ -109,5 +109,6 @@ elif leftbits == 4: res.append(table_b2a_base64[(leftchar & 0xf) << 2]) res.append(PAD) - res.append('\n') + if newline: + res.append('\n') return space.newbytes(res.build()) diff --git a/pypy/module/binascii/test/test_binascii.py b/pypy/module/binascii/test/test_binascii.py --- a/pypy/module/binascii/test/test_binascii.py +++ b/pypy/module/binascii/test/test_binascii.py @@ -121,16 +121,22 @@ raises(UnicodeEncodeError, self.binascii.a2b_base64, u"caf\xe9") def test_b2a_base64(self): - for input, expected in [ - (b"", b""), - (b"b", b"Yg=="), - (b"i\xb7\x1d", b"abcd"), - (b"i\xb7\x1dy", b"abcdeQ=="), - (b"i\xb7\x1dy\xf8", b"abcdefg="), - (b"i\xb7\x1dy\xf8!", b"abcdefgh"), - (b"i\xb7\x1d" * 345, b"abcd" * 345), - ]: - assert self.binascii.b2a_base64(input) == expected + b'\n' + for newline in (True, False, None): + for input, expected in [ + (b"", b""), + (b"b", b"Yg=="), + (b"i\xb7\x1d", b"abcd"), + (b"i\xb7\x1dy", b"abcdeQ=="), + (b"i\xb7\x1dy\xf8", b"abcdefg="), + (b"i\xb7\x1dy\xf8!", b"abcdefgh"), + (b"i\xb7\x1d" * 345, b"abcd" * 345), + ]: + kwargs = {} + if isinstance(newline, bool): + kwargs['newline'] = newline + if newline is not False: + expected += b'\n' + assert self.binascii.b2a_base64(input, **kwargs) == expected def test_a2b_qp(self): for input, expected in [ From pypy.commits at gmail.com Mon Oct 30 13:12:37 2017 From: pypy.commits at gmail.com (mattip) Date: Mon, 30 Oct 2017 10:12:37 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: merge default into branch Message-ID: <59f75d85.84addf0a.e128.1f9f@mx.google.com> Author: Matti Picus Branch: py3.5 Changeset: r92885:623c5cbb6132 Date: 2017-10-30 18:58 +0200 http://bitbucket.org/pypy/pypy/changeset/623c5cbb6132/ Log: merge default into branch diff too long, truncating to 2000 out of 3230 lines diff --git a/_pytest/terminal.py b/_pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -366,11 +366,11 @@ EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, EXIT_USAGEERROR, EXIT_NOTESTSCOLLECTED) if exitstatus in summary_exit_codes: - self.config.hook.pytest_terminal_summary(terminalreporter=self) self.summary_errors() self.summary_failures() self.summary_warnings() self.summary_passes() + self.config.hook.pytest_terminal_summary(terminalreporter=self) if exitstatus == EXIT_INTERRUPTED: self._report_keyboardinterrupt() del self._keyboardinterrupt_memo 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/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 @@ -163,7 +163,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): @@ -171,7 +171,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): @@ -180,7 +180,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 @@ -192,8 +192,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 - at unwrap_spec(name='text') -def resolve_name(space, name): - return space.newtext(capi.c_resolve_name(space, name)) + at unwrap_spec(scoped_name='text') +def resolve_name(space, scoped_name): + return space.newtext(capi.c_resolve_name(space, scoped_name)) - at 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 + at 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 - at 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 + at 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) + + at 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.""" + w_clsdecl = space.findattr(w_pycppclass, space.newtext("__cppdecl__")) + if not w_clsdecl: + w_clsdecl = scope_byname(space, space.text_w(w_pycppclass)) + if not w_clsdecl: raise oefmt(space.w_TypeError, "no such class: %s", space.text_w(w_pycppclass)) - cppclass = space.interp_w(W_CPPClassDecl, w_cppclass, can_be_None=False) - return wrap_cppobject(space, rawobject, cppclass, do_cast=cast, python_owns=owns) + return _bind_object(space, w_obj, w_clsdecl, owns, cast) + +def move(space, w_obj): + """Casts the given instance into an C++-style rvalue.""" + obj = space.interp_w(W_CPPClass, w_obj, can_be_None=True) + if obj: + obj.flags |= INSTANCE_FLAGS_IS_R_VALUE + return w_obj diff --git a/pypy/module/_cppyy/pythonify.py b/pypy/module/_cppyy/pythonify.py --- a/pypy/module/_cppyy/pythonify.py +++ b/pypy/module/_cppyy/pythonify.py @@ -10,7 +10,7 @@ class CPPMetaScope(type): def __getattr__(self, name): try: - return get_pycppitem(self, name) # will cache on self + return get_scoped_pycppitem(self, name) # will cache on self except Exception as e: raise AttributeError("%s object has no attribute '%s' (details: %s)" % (self, name, str(e))) @@ -36,11 +36,14 @@ self._scope = scope def _arg_to_str(self, arg): - if arg == str: - import _cppyy - arg = _cppyy._std_string_name() - elif type(arg) != str: - arg = arg.__name__ + try: + arg = arg.__cppname__ + except AttributeError: + if arg == str: + import _cppyy + arg = _cppyy._std_string_name() + elif type(arg) != str: + arg = arg.__name__ return arg def __call__(self, *args): @@ -58,8 +61,36 @@ return self.__call__(*args) -def clgen_callback(name): - return get_pycppclass(name) +def scope_splitter(name): + is_open_template, scope = 0, "" + for c in name: + if c == ':' and not is_open_template: + if scope: + yield scope + scope = "" + continue + elif c == '<': + is_open_template += 1 + elif c == '>': + is_open_template -= 1 + scope += c + yield scope + +def get_pycppitem(final_scoped_name): + # walk scopes recursively down from global namespace ("::") to get the + # actual (i.e. not typedef'ed) class, triggering all necessary creation + scope = gbl + for name in scope_splitter(final_scoped_name): + scope = getattr(scope, name) + return scope +get_pycppclass = get_pycppitem # currently no distinction, but might + # in future for performance + + +# callbacks (originating from interp_cppyy.py) to allow interp-level to +# initiate creation of app-level classes and function +def clgen_callback(final_scoped_name): + return get_pycppclass(final_scoped_name) def fngen_callback(func, npar): # todo, some kind of arg transform spec if npar == 0: @@ -75,20 +106,19 @@ return wrapper +# construction of namespaces and classes, and their helpers +def make_module_name(scope): + if scope: + return scope.__module__ + '.' + scope.__name__ + return 'cppyy' + def make_static_function(func_name, cppol): def function(*args): return cppol.call(None, *args) function.__name__ = func_name - function.__doc__ = cppol.signature() + function.__doc__ = cppol.prototype() return staticmethod(function) -def make_method(meth_name, cppol): - def method(self, *args): - return cppol.call(self, *args) - method.__name__ = meth_name - method.__doc__ = cppol.signature() - return method - def make_cppnamespace(scope, name, decl): # build up a representation of a C++ namespace (namespaces are classes) @@ -98,20 +128,19 @@ ns_meta = type(name+'_meta', (CPPMetaNamespace,), {}) # create the python-side C++ namespace representation, cache in scope if given - d = {"__cppdecl__" : decl, "__cppname__" : decl.__cppname__ } + d = {"__cppdecl__" : decl, + "__module__" : make_module_name(scope), + "__cppname__" : decl.__cppname__ } pyns = ns_meta(name, (CPPNamespace,), d) if scope: setattr(scope, name, pyns) # install as modules to allow importing from (note naming: cppyy) - modname = 'cppyy.gbl' - if scope: - modname = 'cppyy.gbl.'+pyns.__cppname__.replace('::', '.') - sys.modules[modname] = pyns + sys.modules[make_module_name(pyns)] = pyns return pyns def _drop_cycles(bases): - # TODO: figure this out, as it seems to be a PyPy bug?! + # TODO: figure out why this is necessary? for b1 in bases: for b2 in bases: if not (b1 is b2) and issubclass(b2, b1): @@ -119,27 +148,37 @@ break return tuple(bases) -def make_new(class_name): + +def make_new(decl): def __new__(cls, *args): # create a place-holder only as there may be a derived class defined + # TODO: get rid of the import and add user-land bind_object that uses + # _bind_object (see interp_cppyy.py) import _cppyy - instance = _cppyy.bind_object(0, class_name, True) + instance = _cppyy._bind_object(0, decl, True) if not instance.__class__ is cls: instance.__class__ = cls # happens for derived class return instance return __new__ -def make_cppclass(scope, class_name, final_class_name, decl): +def make_method(meth_name, cppol): + def method(self, *args): + return cppol.call(self, *args) + method.__name__ = meth_name + method.__doc__ = cppol.prototype() + return method + +def make_cppclass(scope, cl_name, decl): # get a list of base classes for class creation bases = [get_pycppclass(base) for base in decl.get_base_names()] if not bases: bases = [CPPClass,] else: - # it's technically possible that the required class now has been built - # if one of the base classes uses it in e.g. a function interface + # it's possible that the required class now has been built if one of + # the base classes uses it in e.g. a function interface try: - return scope.__dict__[final_class_name] + return scope.__dict__[cl_name] except KeyError: pass @@ -147,39 +186,41 @@ d_meta = {} # prepare dictionary for python-side C++ class representation - def dispatch(self, name, signature): - cppol = decl.dispatch(name, signature) - return types.MethodType(make_method(name, cppol), self, type(self)) + def dispatch(self, m_name, signature): + cppol = decl.__dispatch__(m_name, signature) + return types.MethodType(make_method(m_name, cppol), self, type(self)) d_class = {"__cppdecl__" : decl, + "__new__" : make_new(decl), + "__module__" : make_module_name(scope), "__cppname__" : decl.__cppname__, - "__new__" : make_new(class_name), + "__dispatch__" : dispatch, } # insert (static) methods into the class dictionary - for name in decl.get_method_names(): - cppol = decl.get_overload(name) + for m_name in decl.get_method_names(): + cppol = decl.get_overload(m_name) if cppol.is_static(): - d_class[name] = make_static_function(name, cppol) + d_class[m_name] = make_static_function(m_name, cppol) else: - d_class[name] = make_method(name, cppol) + d_class[m_name] = make_method(m_name, cppol) # add all data members to the dictionary of the class to be created, and # static ones also to the metaclass (needed for property setters) - for name in decl.get_datamember_names(): - cppdm = decl.get_datamember(name) - d_class[name] = cppdm + for d_name in decl.get_datamember_names(): + cppdm = decl.get_datamember(d_name) + d_class[d_name] = cppdm if cppdm.is_static(): - d_meta[name] = cppdm + d_meta[d_name] = cppdm # create a metaclass to allow properties (for static data write access) metabases = [type(base) for base in bases] - metacpp = type(CPPMetaScope)(class_name+'_meta', _drop_cycles(metabases), d_meta) + metacpp = type(CPPMetaScope)(cl_name+'_meta', _drop_cycles(metabases), d_meta) # create the python-side C++ class - pycls = metacpp(class_name, _drop_cycles(bases), d_class) + pycls = metacpp(cl_name, _drop_cycles(bases), d_class) # store the class on its outer scope - setattr(scope, final_class_name, pycls) + setattr(scope, cl_name, pycls) # the call to register will add back-end specific pythonizations and thus # needs to run first, so that the generic pythonizations can use them @@ -192,32 +233,32 @@ return CPPTemplate(template_name, scope) -def get_pycppitem(scope, name): +def get_scoped_pycppitem(scope, name): import _cppyy - # resolve typedefs/aliases - full_name = (scope == gbl) and name or (scope.__name__+'::'+name) - true_name = _cppyy._resolve_name(full_name) - if true_name != full_name: - return get_pycppclass(true_name) + # resolve typedefs/aliases: these may cross namespaces, in which case + # the lookup must trigger the creation of all necessary scopes + scoped_name = (scope == gbl) and name or (scope.__cppname__+'::'+name) + final_scoped_name = _cppyy._resolve_name(scoped_name) + if final_scoped_name != scoped_name: + pycppitem = get_pycppitem(final_scoped_name) + # also store on the requested scope (effectively a typedef or pointer copy) + setattr(scope, name, pycppitem) + return pycppitem pycppitem = None - # classes - cppitem = _cppyy._scope_byname(true_name) + # scopes (classes and namespaces) + cppitem = _cppyy._scope_byname(final_scoped_name) if cppitem: - name = true_name - if scope != gbl: - name = true_name[len(scope.__cppname__)+2:] if cppitem.is_namespace(): pycppitem = make_cppnamespace(scope, name, cppitem) - setattr(scope, name, pycppitem) else: - pycppitem = make_cppclass(scope, name, true_name, cppitem) + pycppitem = make_cppclass(scope, name, cppitem) # templates if not cppitem: - cppitem = _cppyy._template_byname(true_name) + cppitem = _cppyy._is_template(final_scoped_name) if cppitem: pycppitem = make_cpptemplatetype(scope, name) setattr(scope, name, pycppitem) @@ -249,29 +290,6 @@ raise AttributeError("'%s' has no attribute '%s'" % (str(scope), name)) -def scope_splitter(name): - is_open_template, scope = 0, "" - for c in name: - if c == ':' and not is_open_template: - if scope: - yield scope - scope = "" - continue - elif c == '<': - is_open_template += 1 - elif c == '>': - is_open_template -= 1 - scope += c - yield scope - -def get_pycppclass(name): - # break up the name, to walk the scopes and get the class recursively - scope = gbl - for part in scope_splitter(name): - scope = getattr(scope, part) - return scope - - # pythonization by decoration (move to their own file?) def python_style_getitem(self, idx): # python-style indexing: check for size and allow indexing from the back @@ -346,8 +364,8 @@ # also the fallback on the indexed __getitem__, but that is slower) if not 'vector' in pyclass.__name__[:11] and \ ('begin' in pyclass.__dict__ and 'end' in pyclass.__dict__): - if _cppyy._scope_byname(pyclass.__name__+'::iterator') or \ - _cppyy._scope_byname(pyclass.__name__+'::const_iterator'): + if _cppyy._scope_byname(pyclass.__cppname__+'::iterator') or \ + _cppyy._scope_byname(pyclass.__cppname__+'::const_iterator'): def __iter__(self): i = self.begin() while i != self.end(): @@ -416,17 +434,21 @@ # pre-create std to allow direct importing gbl.std = make_cppnamespace(gbl, 'std', _cppyy._scope_byname('std')) + # add move cast + gbl.std.move = _cppyy.move + # install a type for enums to refer to # TODO: this is correct for C++98, not for C++11 and in general there will # be the same issue for all typedef'd builtin types setattr(gbl, 'internal_enum_type_t', int) - # install nullptr as a unique reference - setattr(gbl, 'nullptr', _cppyy._get_nullptr()) - # install for user access _cppyy.gbl = gbl + # install nullptr as a unique reference + _cppyy.nullptr = _cppyy._get_nullptr() + + # user-defined pythonizations interface _pythonizations = {} def add_pythonization(class_name, callback): diff --git a/pypy/module/_cppyy/src/dummy_backend.cxx b/pypy/module/_cppyy/src/dummy_backend.cxx --- a/pypy/module/_cppyy/src/dummy_backend.cxx +++ b/pypy/module/_cppyy/src/dummy_backend.cxx @@ -955,7 +955,13 @@ return cppstring_to_cstring(""); } -char* cppyy_method_signature(cppyy_scope_t /* handle */, cppyy_index_t /* method_index */) { +char* cppyy_method_signature( + cppyy_scope_t /* handle */, cppyy_index_t /* method_index */, int /* show_formalargs */) { + return cppstring_to_cstring(""); +} + +char* cppyy_method_prototype( + cppyy_scope_t /* handle */, cppyy_index_t /* method_index */, int /* show_formalargs */) { return cppstring_to_cstring(""); } diff --git a/pypy/module/_cppyy/test/Makefile b/pypy/module/_cppyy/test/Makefile --- a/pypy/module/_cppyy/test/Makefile +++ b/pypy/module/_cppyy/test/Makefile @@ -1,12 +1,14 @@ dicts = advancedcppDict.so \ advancedcpp2Dict.so \ + cpp11featuresDict.so \ crossingDict.so \ datatypesDict.so \ example01Dict.so \ fragileDict.so \ operatorsDict.so \ overloadsDict.so \ - stltypesDict.so + stltypesDict.so \ + templatesDict.so all : $(dicts) diff --git a/pypy/module/_cppyy/test/advancedcpp.cxx b/pypy/module/_cppyy/test/advancedcpp.cxx --- a/pypy/module/_cppyy/test/advancedcpp.cxx +++ b/pypy/module/_cppyy/test/advancedcpp.cxx @@ -106,17 +106,6 @@ } -// more template testing -long my_templated_method_class::get_size() { return -1; } - -long my_templated_method_class::get_char_size() { return (long)sizeof(char); } -long my_templated_method_class::get_int_size() { return (long)sizeof(int); } -long my_templated_method_class::get_long_size() { return (long)sizeof(long); } -long my_templated_method_class::get_float_size() { return (long)sizeof(float); } -long my_templated_method_class::get_double_size() { return (long)sizeof(double); } -long my_templated_method_class::get_self_size() { return (long)sizeof(my_templated_method_class); } - - // overload order testing int overload_one_way::gime() const { return 1; } std::string overload_one_way::gime() { return "aap"; } diff --git a/pypy/module/_cppyy/test/advancedcpp.h b/pypy/module/_cppyy/test/advancedcpp.h --- a/pypy/module/_cppyy/test/advancedcpp.h +++ b/pypy/module/_cppyy/test/advancedcpp.h @@ -246,8 +246,6 @@ int m_i; }; -template class std::vector; - //=========================================================================== class some_convertible { // for math conversions testing @@ -275,6 +273,7 @@ extern double my_global_double; // a couple of globals for access testing extern double my_global_array[500]; extern double* my_global_ptr; +static const char my_global_string[] = "aap " " noot " " mies"; //=========================================================================== class some_class_with_data { // for life-line and identity testing @@ -387,37 +386,6 @@ template char my_templated_function(char); template double my_templated_function(double); -class my_templated_method_class { -public: - long get_size(); // to get around bug in genreflex - template long get_size(); - - long get_char_size(); - long get_int_size(); - long get_long_size(); - long get_float_size(); - long get_double_size(); - - long get_self_size(); - -private: - double m_data[3]; -}; - -template -inline long my_templated_method_class::get_size() { - return sizeof(B); -} - -template long my_templated_method_class::get_size(); -template long my_templated_method_class::get_size(); -template long my_templated_method_class::get_size(); -template long my_templated_method_class::get_size(); -template long my_templated_method_class::get_size(); - -typedef my_templated_method_class my_typedef_t; -template long my_templated_method_class::get_size(); - //=========================================================================== class overload_one_way { // overload order testing diff --git a/pypy/module/_cppyy/test/advancedcpp.xml b/pypy/module/_cppyy/test/advancedcpp.xml --- a/pypy/module/_cppyy/test/advancedcpp.xml +++ b/pypy/module/_cppyy/test/advancedcpp.xml @@ -53,8 +53,6 @@ - - diff --git a/pypy/module/_cppyy/test/cpp11features.cxx b/pypy/module/_cppyy/test/cpp11features.cxx new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/cpp11features.cxx @@ -0,0 +1,18 @@ +#if __cplusplus >= 201103L + +#include "cpp11features.h" + + +// for std::shared_ptr<> testing +int TestSharedPtr::s_counter = 0; + +std::shared_ptr create_shared_ptr_instance() { + return std::shared_ptr(new TestSharedPtr); +} + + +// for move ctors etc. +int TestMoving1::s_move_counter = 0; +int TestMoving2::s_move_counter = 0; + +#endif // c++11 and later diff --git a/pypy/module/_cppyy/test/cpp11features.h b/pypy/module/_cppyy/test/cpp11features.h new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/cpp11features.h @@ -0,0 +1,45 @@ +#if __cplusplus >= 201103L + +#include + + +//=========================================================================== +class TestSharedPtr { // for std::shared_ptr<> testing +public: + static int s_counter; + +public: + TestSharedPtr() { ++s_counter; } + TestSharedPtr(const TestSharedPtr&) { ++s_counter; } + ~TestSharedPtr() { --s_counter; } +}; + +std::shared_ptr create_shared_ptr_instance(); + + +//=========================================================================== +class TestMoving1 { // for move ctors etc. +public: + static int s_move_counter; + +public: + TestMoving1() {} + TestMoving1(TestMoving1&&) { ++s_move_counter; } + TestMoving1(const TestMoving1&) {} + TestMoving1& operator=(TestMoving1&&) { ++s_move_counter; return *this; } + TestMoving1& operator=(TestMoving1&) { return *this; } +}; + +class TestMoving2 { // note opposite method order from TestMoving1 +public: + static int s_move_counter; + +public: + TestMoving2() {} + TestMoving2(const TestMoving2&) {} + TestMoving2(TestMoving2&& other) { ++s_move_counter; } + TestMoving2& operator=(TestMoving2&) { return *this; } + TestMoving2& operator=(TestMoving2&&) { ++s_move_counter; return *this; } +}; + +#endif // c++11 and later diff --git a/pypy/module/_cppyy/test/cpp11features.xml b/pypy/module/_cppyy/test/cpp11features.xml new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/cpp11features.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/pypy/module/_cppyy/test/fragile.h b/pypy/module/_cppyy/test/fragile.h --- a/pypy/module/_cppyy/test/fragile.h +++ b/pypy/module/_cppyy/test/fragile.h @@ -30,9 +30,11 @@ void overload(int, no_such_class* p = 0) {} }; +static const int dummy_location = 0xdead; + class E { public: - E() : m_pp_no_such(0), m_pp_a(0) {} + E() : m_pp_no_such((no_such_class**)&dummy_location), m_pp_a(0) {} virtual int check() { return (int)'E'; } void overload(no_such_class**) {} diff --git a/pypy/module/_cppyy/test/templates.cxx b/pypy/module/_cppyy/test/templates.cxx new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/templates.cxx @@ -0,0 +1,12 @@ +#include "templates.h" + + +// template methods +long MyTemplatedMethodClass::get_size() { return -1; } + +long MyTemplatedMethodClass::get_char_size() { return (long)sizeof(char); } +long MyTemplatedMethodClass::get_int_size() { return (long)sizeof(int); } +long MyTemplatedMethodClass::get_long_size() { return (long)42; /* "lying" */ } +long MyTemplatedMethodClass::get_float_size() { return (long)sizeof(float); } +long MyTemplatedMethodClass::get_double_size() { return (long)sizeof(double); } +long MyTemplatedMethodClass::get_self_size() { return (long)sizeof(MyTemplatedMethodClass); } diff --git a/pypy/module/_cppyy/test/templates.h b/pypy/module/_cppyy/test/templates.h new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/templates.h @@ -0,0 +1,35 @@ +//=========================================================================== +class MyTemplatedMethodClass { // template methods +public: + long get_size(); // to get around bug in genreflex + template long get_size(); + + long get_char_size(); + long get_int_size(); + long get_long_size(); + long get_float_size(); + long get_double_size(); + + long get_self_size(); + +private: + double m_data[3]; +}; + +template +inline long MyTemplatedMethodClass::get_size() { + return sizeof(B); +} + +// +typedef MyTemplatedMethodClass MyTMCTypedef_t; + +// explicit instantiation +template long MyTemplatedMethodClass::get_size(); +template long MyTemplatedMethodClass::get_size(); + +// "lying" specialization +template<> +inline long MyTemplatedMethodClass::get_size() { + return 42; +} diff --git a/pypy/module/_cppyy/test/templates.xml b/pypy/module/_cppyy/test/templates.xml new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/templates.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/pypy/module/_cppyy/test/test_advancedcpp.py b/pypy/module/_cppyy/test/test_advancedcpp.py --- a/pypy/module/_cppyy/test/test_advancedcpp.py +++ b/pypy/module/_cppyy/test/test_advancedcpp.py @@ -28,9 +28,9 @@ def test01_default_arguments(self): """Test usage of default arguments""" - import _cppyy + import _cppyy as cppyy def test_defaulter(n, t): - defaulter = getattr(_cppyy.gbl, '%s_defaulter' % n) + defaulter = getattr(cppyy.gbl, '%s_defaulter' % n) d = defaulter() assert d.m_a == t(11) @@ -55,23 +55,23 @@ assert d.m_b == t(4) assert d.m_c == t(5) d.__destruct__() - test_defaulter('short', int) + test_defaulter('short', int) test_defaulter('ushort', int) - test_defaulter('int', int) - test_defaulter('uint', int) - test_defaulter('long', long) - test_defaulter('ulong', long) - test_defaulter('llong', long) + test_defaulter('int', int) + test_defaulter('uint', int) + test_defaulter('long', long) + test_defaulter('ulong', long) + test_defaulter('llong', long) test_defaulter('ullong', long) - test_defaulter('float', float) + test_defaulter('float', float) test_defaulter('double', float) def test02_simple_inheritance(self): """Test binding of a basic inheritance structure""" - import _cppyy - base_class = _cppyy.gbl.base_class - derived_class = _cppyy.gbl.derived_class + import _cppyy as cppyy + base_class = cppyy.gbl.base_class + derived_class = cppyy.gbl.derived_class assert issubclass(derived_class, base_class) assert not issubclass(base_class, derived_class) @@ -123,8 +123,8 @@ def test03_namespaces(self): """Test access to namespaces and inner classes""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl assert gbl.a_ns is gbl.a_ns assert gbl.a_ns.d_ns is gbl.a_ns.d_ns @@ -150,8 +150,8 @@ def test03a_namespace_lookup_on_update(self): """Test whether namespaces can be shared across dictionaries.""" - import _cppyy, ctypes - gbl = _cppyy.gbl + import _cppyy as cppyy, ctypes + gbl = cppyy.gbl lib2 = ctypes.CDLL("./advancedcpp2Dict.so", ctypes.RTLD_GLOBAL) @@ -179,8 +179,8 @@ def test04_template_types(self): """Test bindings of templated types""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl assert gbl.T1 is gbl.T1 assert gbl.T2 is gbl.T2 @@ -245,8 +245,8 @@ def test05_abstract_classes(self): """Test non-instatiatability of abstract classes""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl raises(TypeError, gbl.a_class) raises(TypeError, gbl.some_abstract_class) @@ -260,12 +260,12 @@ def test06_datamembers(self): """Test data member access when using virtual inheritence""" - import _cppyy - a_class = _cppyy.gbl.a_class - b_class = _cppyy.gbl.b_class - c_class_1 = _cppyy.gbl.c_class_1 - c_class_2 = _cppyy.gbl.c_class_2 - d_class = _cppyy.gbl.d_class + import _cppyy as cppyy + a_class = cppyy.gbl.a_class + b_class = cppyy.gbl.b_class + c_class_1 = cppyy.gbl.c_class_1 + c_class_2 = cppyy.gbl.c_class_2 + d_class = cppyy.gbl.d_class assert issubclass(b_class, a_class) assert issubclass(c_class_1, a_class) @@ -354,8 +354,8 @@ def test07_pass_by_reference(self): """Test reference passing when using virtual inheritance""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl b_class = gbl.b_class c_class = gbl.c_class_2 d_class = gbl.d_class @@ -387,71 +387,75 @@ def test08_void_pointer_passing(self): """Test passing of variants of void pointer arguments""" - import _cppyy - pointer_pass = _cppyy.gbl.pointer_pass - some_concrete_class = _cppyy.gbl.some_concrete_class + import _cppyy as cppyy + pointer_pass = cppyy.gbl.pointer_pass + some_concrete_class = cppyy.gbl.some_concrete_class pp = pointer_pass() o = some_concrete_class() - assert _cppyy.addressof(o) == pp.gime_address_ptr(o) - assert _cppyy.addressof(o) == pp.gime_address_ptr_ptr(o) - assert _cppyy.addressof(o) == pp.gime_address_ptr_ref(o) + assert cppyy.addressof(o) == pp.gime_address_ptr(o) + assert cppyy.addressof(o) == pp.gime_address_ptr_ptr(o) + assert cppyy.addressof(o) == pp.gime_address_ptr_ref(o) import array - addressofo = array.array('l', [_cppyy.addressof(o)]) - assert addressofo.buffer_info()[0] == pp.gime_address_ptr_ptr(addressofo) + addressofo = array.array('l', [cppyy.addressof(o)]) + assert addressofo[0] == pp.gime_address_ptr_ptr(addressofo) assert 0 == pp.gime_address_ptr(0) - assert 0 == pp.gime_address_ptr(None) + raises(TypeError, pp.gime_address_ptr, None) - ptr = _cppyy.bind_object(0, some_concrete_class) - assert _cppyy.addressof(ptr) == 0 + ptr = cppyy.bind_object(0, some_concrete_class) + assert cppyy.addressof(ptr) == 0 pp.set_address_ptr_ref(ptr) - assert _cppyy.addressof(ptr) == 0x1234 + assert cppyy.addressof(ptr) == 0x1234 pp.set_address_ptr_ptr(ptr) - assert _cppyy.addressof(ptr) == 0x4321 + assert cppyy.addressof(ptr) == 0x4321 + + assert cppyy.addressof(cppyy.nullptr) == 0 + raises(TypeError, cppyy.addressof, None) From pypy.commits at gmail.com Mon Oct 30 13:12:39 2017 From: pypy.commits at gmail.com (mattip) Date: Mon, 30 Oct 2017 10:12:39 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: fix merge quirks, document merged branch Message-ID: <59f75d87.0c181c0a.5626d.347a@mx.google.com> Author: Matti Picus Branch: py3.5 Changeset: r92886:81a7e5dcbc2d Date: 2017-10-30 19:10 +0200 http://bitbucket.org/pypy/pypy/changeset/81a7e5dcbc2d/ Log: fix merge quirks, document merged branch diff --git a/pypy/doc/whatsnew-pypy3-head.rst b/pypy/doc/whatsnew-pypy3-head.rst --- a/pypy/doc/whatsnew-pypy3-head.rst +++ b/pypy/doc/whatsnew-pypy3-head.rst @@ -11,3 +11,6 @@ .. branch: py3.5-appexec Raise if space.is_true(space.appexec()) used in app level tests, fix tests that did this + +.. branch: py3.5-mac-embedding +Download and patch dependencies when building cffi-based stdlib modules diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -1864,7 +1864,7 @@ def sched_yield(): return handle_posix_error('sched_yield', c_sched_yield()) - + #___________________________________________________________________ c_chroot = external('chroot', [rffi.CCHARP], rffi.INT, diff --git a/rpython/rlib/test/test_rposix.py b/rpython/rlib/test/test_rposix.py --- a/rpython/rlib/test/test_rposix.py +++ b/rpython/rlib/test/test_rposix.py @@ -810,11 +810,6 @@ assert isinstance(low, int) == True assert isinstance(high, int) == True assert high > low - - at rposix_requires('sched_yield') -def test_sched_yield(): - if sys.platform != 'win32': - rposix.sched_yield() @rposix_requires('sched_yield') def test_sched_yield(): From pypy.commits at gmail.com Mon Oct 30 13:12:33 2017 From: pypy.commits at gmail.com (mattip) Date: Mon, 30 Oct 2017 10:12:33 -0700 (PDT) Subject: [pypy-commit] pypy py3.5: merge py3.5-mac-embedding which can download stdlib cffi-module build depenencies Message-ID: <59f75d81.4a061c0a.23c83.f357@mx.google.com> Author: Matti Picus Branch: py3.5 Changeset: r92883:5a955fc2e868 Date: 2017-10-30 18:39 +0200 http://bitbucket.org/pypy/pypy/changeset/5a955fc2e868/ Log: merge py3.5-mac-embedding which can download stdlib cffi-module build depenencies diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -59,6 +59,7 @@ ^rpython/rlib/rvmprof/src/shared/libbacktrace/config.h$ ^rpython/rlib/rvmprof/src/shared/libbacktrace/config.log$ ^rpython/rlib/rvmprof/src/shared/libbacktrace/config.status$ +^pypy/tool/dest$ ^pypy/goal/pypy-translation-snapshot$ ^pypy/goal/pypy-c ^pypy/goal/pypy3-c diff --git a/lib_pypy/_cffi_ssl/_cffi_src/openssl/cryptography.py b/lib_pypy/_cffi_ssl/_cffi_src/openssl/cryptography.py --- a/lib_pypy/_cffi_ssl/_cffi_src/openssl/cryptography.py +++ b/lib_pypy/_cffi_ssl/_cffi_src/openssl/cryptography.py @@ -48,6 +48,9 @@ #else #define CRYPTOGRAPHY_IS_LIBRESSL 0 #endif + +#define CRYPTOGRAPHY_LIBRESSL_251_OR_GREATER \ + (CRYPTOGRAPHY_IS_LIBRESSL && LIBRESSL_VERSION_NUMBER >= 0x20501000) """ TYPES = """ diff --git a/lib_pypy/_cffi_ssl/_cffi_src/openssl/x509_vfy.py b/lib_pypy/_cffi_ssl/_cffi_src/openssl/x509_vfy.py --- a/lib_pypy/_cffi_ssl/_cffi_src/openssl/x509_vfy.py +++ b/lib_pypy/_cffi_ssl/_cffi_src/openssl/x509_vfy.py @@ -244,6 +244,14 @@ static const long X509_V_FLAG_SUITEB_192_LOS = 0; static const long X509_V_FLAG_SUITEB_128_LOS = 0; +#if CRYPTOGRAPHY_LIBRESSL_251_OR_GREATER +int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *, const char *, size_t); +int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *, const char *, size_t); +int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *, const unsigned char *, + size_t); +int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *, const char *); +void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *, unsigned int); +#else int (*X509_VERIFY_PARAM_set1_host)(X509_VERIFY_PARAM *, const char *, size_t) = NULL; int (*X509_VERIFY_PARAM_set1_email)(X509_VERIFY_PARAM *, const char *, @@ -254,6 +262,7 @@ void (*X509_VERIFY_PARAM_set_hostflags)(X509_VERIFY_PARAM *, unsigned int) = NULL; #endif +#endif /* OpenSSL 1.0.2+ or Solaris's backport */ #ifdef X509_V_FLAG_PARTIAL_CHAIN diff --git a/lib_pypy/_cffi_ssl/osx-roots.diff b/lib_pypy/_cffi_ssl/osx-roots.diff new file mode 100644 --- /dev/null +++ b/lib_pypy/_cffi_ssl/osx-roots.diff @@ -0,0 +1,475 @@ +diff -Naur libressl-2.6.2.orig/crypto/Makefile.am libressl-2.6.2/crypto/Makefile.am +--- libressl-2.6.2.orig/crypto/Makefile.am 2017-09-02 01:49:55.000000000 +0200 ++++ libressl-2.6.2/crypto/Makefile.am 2017-10-07 14:05:16.000000000 +0200 +@@ -92,7 +92,7 @@ + -mv crypto_portable.sym.tmp crypto_portable.sym + endif + +-libcrypto_la_LDFLAGS = -version-info @LIBCRYPTO_VERSION@ -no-undefined -export-symbols crypto_portable.sym ++libcrypto_la_LDFLAGS = -version-info @LIBCRYPTO_VERSION@ -no-undefined -export-symbols crypto_portable.sym -framework Security -framework CoreFoundation + libcrypto_la_LIBADD = libcompat.la + if !HAVE_EXPLICIT_BZERO + libcrypto_la_LIBADD += libcompatnoopt.la +@@ -863,6 +863,7 @@ + libcrypto_la_SOURCES += x509/x509_txt.c + libcrypto_la_SOURCES += x509/x509_v3.c + libcrypto_la_SOURCES += x509/x509_vfy.c ++libcrypto_la_SOURCES += x509/x509_vfy_apple.c + libcrypto_la_SOURCES += x509/x509_vpm.c + libcrypto_la_SOURCES += x509/x509cset.c + libcrypto_la_SOURCES += x509/x509name.c +diff -Naur libressl-2.6.2.orig/crypto/Makefile.in libressl-2.6.2/crypto/Makefile.in +--- libressl-2.6.2.orig/crypto/Makefile.in 2017-09-26 06:07:03.000000000 +0200 ++++ libressl-2.6.2/crypto/Makefile.in 2017-10-07 14:05:24.000000000 +0200 +@@ -426,20 +426,20 @@ + x509/x509_err.c x509/x509_ext.c x509/x509_lu.c x509/x509_obj.c \ + x509/x509_r2x.c x509/x509_req.c x509/x509_set.c \ + x509/x509_trs.c x509/x509_txt.c x509/x509_v3.c x509/x509_vfy.c \ +- x509/x509_vpm.c x509/x509cset.c x509/x509name.c \ +- x509/x509rset.c x509/x509spki.c x509/x509type.c x509/x_all.c \ +- x509v3/pcy_cache.c x509v3/pcy_data.c x509v3/pcy_lib.c \ +- x509v3/pcy_map.c x509v3/pcy_node.c x509v3/pcy_tree.c \ +- x509v3/v3_akey.c x509v3/v3_akeya.c x509v3/v3_alt.c \ +- x509v3/v3_bcons.c x509v3/v3_bitst.c x509v3/v3_conf.c \ +- x509v3/v3_cpols.c x509v3/v3_crld.c x509v3/v3_enum.c \ +- x509v3/v3_extku.c x509v3/v3_genn.c x509v3/v3_ia5.c \ +- x509v3/v3_info.c x509v3/v3_int.c x509v3/v3_lib.c \ +- x509v3/v3_ncons.c x509v3/v3_ocsp.c x509v3/v3_pci.c \ +- x509v3/v3_pcia.c x509v3/v3_pcons.c x509v3/v3_pku.c \ +- x509v3/v3_pmaps.c x509v3/v3_prn.c x509v3/v3_purp.c \ +- x509v3/v3_skey.c x509v3/v3_sxnet.c x509v3/v3_utl.c \ +- x509v3/v3err.c ++ x509/x509_vfy_apple.c x509/x509_vpm.c x509/x509cset.c \ ++ x509/x509name.c x509/x509rset.c x509/x509spki.c \ ++ x509/x509type.c x509/x_all.c x509v3/pcy_cache.c \ ++ x509v3/pcy_data.c x509v3/pcy_lib.c x509v3/pcy_map.c \ ++ x509v3/pcy_node.c x509v3/pcy_tree.c x509v3/v3_akey.c \ ++ x509v3/v3_akeya.c x509v3/v3_alt.c x509v3/v3_bcons.c \ ++ x509v3/v3_bitst.c x509v3/v3_conf.c x509v3/v3_cpols.c \ ++ x509v3/v3_crld.c x509v3/v3_enum.c x509v3/v3_extku.c \ ++ x509v3/v3_genn.c x509v3/v3_ia5.c x509v3/v3_info.c \ ++ x509v3/v3_int.c x509v3/v3_lib.c x509v3/v3_ncons.c \ ++ x509v3/v3_ocsp.c x509v3/v3_pci.c x509v3/v3_pcia.c \ ++ x509v3/v3_pcons.c x509v3/v3_pku.c x509v3/v3_pmaps.c \ ++ x509v3/v3_prn.c x509v3/v3_purp.c x509v3/v3_skey.c \ ++ x509v3/v3_sxnet.c x509v3/v3_utl.c x509v3/v3err.c + am__objects_27 = aes/libcrypto_la-aes-elf-x86_64.lo \ + aes/libcrypto_la-bsaes-elf-x86_64.lo \ + aes/libcrypto_la-vpaes-elf-x86_64.lo \ +@@ -759,11 +759,12 @@ + x509/libcrypto_la-x509_r2x.lo x509/libcrypto_la-x509_req.lo \ + x509/libcrypto_la-x509_set.lo x509/libcrypto_la-x509_trs.lo \ + x509/libcrypto_la-x509_txt.lo x509/libcrypto_la-x509_v3.lo \ +- x509/libcrypto_la-x509_vfy.lo x509/libcrypto_la-x509_vpm.lo \ +- x509/libcrypto_la-x509cset.lo x509/libcrypto_la-x509name.lo \ +- x509/libcrypto_la-x509rset.lo x509/libcrypto_la-x509spki.lo \ +- x509/libcrypto_la-x509type.lo x509/libcrypto_la-x_all.lo \ +- x509v3/libcrypto_la-pcy_cache.lo \ ++ x509/libcrypto_la-x509_vfy.lo \ ++ x509/libcrypto_la-x509_vfy_apple.lo \ ++ x509/libcrypto_la-x509_vpm.lo x509/libcrypto_la-x509cset.lo \ ++ x509/libcrypto_la-x509name.lo x509/libcrypto_la-x509rset.lo \ ++ x509/libcrypto_la-x509spki.lo x509/libcrypto_la-x509type.lo \ ++ x509/libcrypto_la-x_all.lo x509v3/libcrypto_la-pcy_cache.lo \ + x509v3/libcrypto_la-pcy_data.lo x509v3/libcrypto_la-pcy_lib.lo \ + x509v3/libcrypto_la-pcy_map.lo x509v3/libcrypto_la-pcy_node.lo \ + x509v3/libcrypto_la-pcy_tree.lo x509v3/libcrypto_la-v3_akey.lo \ +@@ -1000,7 +1001,7 @@ + $(ASM_X86_64_ELF) $(ASM_X86_64_MACOSX) + BUILT_SOURCES = crypto_portable.sym + CLEANFILES = crypto_portable.sym +-libcrypto_la_LDFLAGS = -version-info @LIBCRYPTO_VERSION@ -no-undefined -export-symbols crypto_portable.sym ++libcrypto_la_LDFLAGS = -version-info @LIBCRYPTO_VERSION@ -no-undefined -export-symbols crypto_portable.sym -framework Security -framework CoreFoundation + libcrypto_la_LIBADD = libcompat.la $(am__append_1) + libcrypto_la_CPPFLAGS = $(AM_CPPFLAGS) -DLIBRESSL_INTERNAL \ + -DOPENSSL_NO_HW_PADLOCK $(am__append_2) $(am__append_3) \ +@@ -1272,20 +1273,20 @@ + x509/x509_err.c x509/x509_ext.c x509/x509_lu.c x509/x509_obj.c \ + x509/x509_r2x.c x509/x509_req.c x509/x509_set.c \ + x509/x509_trs.c x509/x509_txt.c x509/x509_v3.c x509/x509_vfy.c \ +- x509/x509_vpm.c x509/x509cset.c x509/x509name.c \ +- x509/x509rset.c x509/x509spki.c x509/x509type.c x509/x_all.c \ +- x509v3/pcy_cache.c x509v3/pcy_data.c x509v3/pcy_lib.c \ +- x509v3/pcy_map.c x509v3/pcy_node.c x509v3/pcy_tree.c \ +- x509v3/v3_akey.c x509v3/v3_akeya.c x509v3/v3_alt.c \ +- x509v3/v3_bcons.c x509v3/v3_bitst.c x509v3/v3_conf.c \ +- x509v3/v3_cpols.c x509v3/v3_crld.c x509v3/v3_enum.c \ +- x509v3/v3_extku.c x509v3/v3_genn.c x509v3/v3_ia5.c \ +- x509v3/v3_info.c x509v3/v3_int.c x509v3/v3_lib.c \ +- x509v3/v3_ncons.c x509v3/v3_ocsp.c x509v3/v3_pci.c \ +- x509v3/v3_pcia.c x509v3/v3_pcons.c x509v3/v3_pku.c \ +- x509v3/v3_pmaps.c x509v3/v3_prn.c x509v3/v3_purp.c \ +- x509v3/v3_skey.c x509v3/v3_sxnet.c x509v3/v3_utl.c \ +- x509v3/v3err.c ++ x509/x509_vfy_apple.c x509/x509_vpm.c x509/x509cset.c \ ++ x509/x509name.c x509/x509rset.c x509/x509spki.c \ ++ x509/x509type.c x509/x_all.c x509v3/pcy_cache.c \ ++ x509v3/pcy_data.c x509v3/pcy_lib.c x509v3/pcy_map.c \ ++ x509v3/pcy_node.c x509v3/pcy_tree.c x509v3/v3_akey.c \ ++ x509v3/v3_akeya.c x509v3/v3_alt.c x509v3/v3_bcons.c \ ++ x509v3/v3_bitst.c x509v3/v3_conf.c x509v3/v3_cpols.c \ ++ x509v3/v3_crld.c x509v3/v3_enum.c x509v3/v3_extku.c \ ++ x509v3/v3_genn.c x509v3/v3_ia5.c x509v3/v3_info.c \ ++ x509v3/v3_int.c x509v3/v3_lib.c x509v3/v3_ncons.c \ ++ x509v3/v3_ocsp.c x509v3/v3_pci.c x509v3/v3_pcia.c \ ++ x509v3/v3_pcons.c x509v3/v3_pku.c x509v3/v3_pmaps.c \ ++ x509v3/v3_prn.c x509v3/v3_purp.c x509v3/v3_skey.c \ ++ x509v3/v3_sxnet.c x509v3/v3_utl.c x509v3/v3err.c + + # chacha + +@@ -2808,6 +2809,8 @@ + x509/$(DEPDIR)/$(am__dirstamp) + x509/libcrypto_la-x509_vfy.lo: x509/$(am__dirstamp) \ + x509/$(DEPDIR)/$(am__dirstamp) ++x509/libcrypto_la-x509_vfy_apple.lo: x509/$(am__dirstamp) \ ++ x509/$(DEPDIR)/$(am__dirstamp) + x509/libcrypto_la-x509_vpm.lo: x509/$(am__dirstamp) \ + x509/$(DEPDIR)/$(am__dirstamp) + x509/libcrypto_la-x509cset.lo: x509/$(am__dirstamp) \ +@@ -3583,6 +3586,7 @@ + @AMDEP_TRUE@@am__include@ @am__quote at x509/$(DEPDIR)/libcrypto_la-x509_txt.Plo at am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote at x509/$(DEPDIR)/libcrypto_la-x509_v3.Plo at am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote at x509/$(DEPDIR)/libcrypto_la-x509_vfy.Plo at am__quote@ ++ at AMDEP_TRUE@@am__include@ @am__quote at x509/$(DEPDIR)/libcrypto_la-x509_vfy_apple.Plo at am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote at x509/$(DEPDIR)/libcrypto_la-x509_vpm.Plo at am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote at x509/$(DEPDIR)/libcrypto_la-x509cset.Plo at am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote at x509/$(DEPDIR)/libcrypto_la-x509name.Plo at am__quote@ +@@ -7460,6 +7464,13 @@ + @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + @am__fastdepCC_FALSE@ $(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o x509/libcrypto_la-x509_vfy.lo `test -f 'x509/x509_vfy.c' || echo '$(srcdir)/'`x509/x509_vfy.c + ++x509/libcrypto_la-x509_vfy_apple.lo: x509/x509_vfy_apple.c ++ at am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT x509/libcrypto_la-x509_vfy_apple.lo -MD -MP -MF x509/$(DEPDIR)/libcrypto_la-x509_vfy_apple.Tpo -c -o x509/libcrypto_la-x509_vfy_apple.lo `test -f 'x509/x509_vfy_apple.c' || echo '$(srcdir)/'`x509/x509_vfy_apple.c ++ at am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) x509/$(DEPDIR)/libcrypto_la-x509_vfy_apple.Tpo x509/$(DEPDIR)/libcrypto_la-x509_vfy_apple.Plo ++ at AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='x509/x509_vfy_apple.c' object='x509/libcrypto_la-x509_vfy_apple.lo' libtool=yes @AMDEPBACKSLASH@ ++ at AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++ at am__fastdepCC_FALSE@ $(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o x509/libcrypto_la-x509_vfy_apple.lo `test -f 'x509/x509_vfy_apple.c' || echo '$(srcdir)/'`x509/x509_vfy_apple.c ++ + x509/libcrypto_la-x509_vpm.lo: x509/x509_vpm.c + @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT x509/libcrypto_la-x509_vpm.lo -MD -MP -MF x509/$(DEPDIR)/libcrypto_la-x509_vpm.Tpo -c -o x509/libcrypto_la-x509_vpm.lo `test -f 'x509/x509_vpm.c' || echo '$(srcdir)/'`x509/x509_vpm.c + @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) x509/$(DEPDIR)/libcrypto_la-x509_vpm.Tpo x509/$(DEPDIR)/libcrypto_la-x509_vpm.Plo +diff -Naur libressl-2.6.2.orig/crypto/x509/x509_vfy.c libressl-2.6.2/crypto/x509/x509_vfy.c +--- libressl-2.6.2.orig/crypto/x509/x509_vfy.c 2017-09-02 14:01:08.000000000 +0200 ++++ libressl-2.6.2/crypto/x509/x509_vfy.c 2017-10-07 14:05:16.000000000 +0200 +@@ -115,6 +115,13 @@ + + #define CRL_SCORE_TIME_DELTA 0x002 + ++/* ++ * If we are using Trust Evaluation Agent, rename the original function ++ */ ++#ifdef __APPLE__ ++#define X509_verify_cert X509_verify_cert_orig ++#endif ++ + static int null_callback(int ok, X509_STORE_CTX *e); + static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); + static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x); +diff -Naur libressl-2.6.2.orig/crypto/x509/x509_vfy_apple.c libressl-2.6.2/crypto/x509/x509_vfy_apple.c +--- libressl-2.6.2.orig/crypto/x509/x509_vfy_apple.c 1970-01-01 01:00:00.000000000 +0100 ++++ libressl-2.6.2/crypto/x509/x509_vfy_apple.c 2017-10-07 14:05:16.000000000 +0200 +@@ -0,0 +1,225 @@ ++/* ++ * Copyright (c) 2009 Apple Inc. All Rights Reserved. ++ * ++ * @APPLE_LICENSE_HEADER_START@ ++ * ++ * This file contains Original Code and/or Modifications of Original Code ++ * as defined in and that are subject to the Apple Public Source License ++ * Version 2.0 (the 'License'). You may not use this file except in ++ * compliance with the License. Please obtain a copy of the License at ++ * http://www.opensource.apple.com/apsl/ and read it before using this ++ * file. ++ * ++ * The Original Code and all software distributed under the License are ++ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER ++ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ++ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. ++ * Please see the License for the specific language governing rights and ++ * limitations under the License. ++ * ++ * @APPLE_LICENSE_HEADER_END@ ++ * ++ */ ++ ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#include ++#include ++#include ++ ++#include "cryptlib.h" ++#include "vpm_int.h" ++#include "x509_vfy_apple.h" ++ ++#define TEA_might_correct_error(err) (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY || err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT || err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) ++ ++ ++static bool add_cert_to_array(CFMutableArrayRef array, X509 *x509) ++{ ++ unsigned char *asn1_cert_data = NULL; ++ int asn1_cert_len = i2d_X509(x509, &asn1_cert_data); ++ ++ CFDataRef data = CFDataCreate(kCFAllocatorDefault, asn1_cert_data, asn1_cert_len); ++ ++ if (data == NULL) { ++ return false; ++ } ++ ++ SecCertificateRef cert = SecCertificateCreateWithData(NULL, data); ++ ++ free(asn1_cert_data); ++ ++ if (cert == NULL) { ++ CFRelease(data); ++ return false; ++ } ++ ++ CFArrayAppendValue(array, cert); ++ CFRelease(data); ++ ++ return true; ++} ++ ++static CFStringRef to_string(const char *s) { ++ if (s == NULL) ++ return NULL; ++ return CFStringCreateWithCString(kCFAllocatorDefault, s, ++ kCFStringEncodingASCII); ++} ++ ++static SecPolicyRef get_policy(X509_VERIFY_PARAM *param) { ++ switch (param->purpose) { ++ case X509_PURPOSE_SSL_CLIENT: ++ case X509_PURPOSE_SSL_SERVER: { ++ ++ if (!param->id) { ++ fprintf(stderr, "got no ID!\n"); ++ return NULL; ++ } ++ ++ CFStringRef hostname; ++ int nhosts = sk_OPENSSL_STRING_num(param->id->hosts); ++ ++ if (nhosts != 1) { ++ hostname = NULL; ++ ++ } else { ++ hostname = to_string(sk_OPENSSL_STRING_value(param->id->hosts, 0)); ++ CFShow(hostname); ++ } ++ ++ return SecPolicyCreateSSL(param->purpose == X509_PURPOSE_SSL_SERVER, ++ hostname); ++ } ++ ++ case X509_PURPOSE_NS_SSL_SERVER: ++ case X509_PURPOSE_SMIME_SIGN: ++ case X509_PURPOSE_SMIME_ENCRYPT: ++ case X509_PURPOSE_CRL_SIGN: ++ case X509_PURPOSE_ANY: ++ case X509_PURPOSE_OCSP_HELPER: ++ case X509_PURPOSE_TIMESTAMP_SIGN: ++ default: ++ fprintf(stderr, "unsupported purpose %d", param->purpose); ++ return NULL; ++ } ++} ++ ++/* ++ * Please see comment in x509_vfy_apple.h ++ */ ++int ++X509_verify_cert(X509_STORE_CTX *ctx) ++{ ++ uint64_t certLastIndex = 0; ++ uint64_t i = 0; ++ ++ /* Try OpenSSL, if we get a local certificate issue verify against trusted roots */ ++ int ret = X509_verify_cert_orig(ctx); ++ ++ /* Verify TEA is enabled and should be used. */ ++ if (0 == X509_TEA_is_enabled() || ++ ret == 1 || !TEA_might_correct_error(ctx->error)) { ++ return ret; ++ } ++ ++ /* Verify that the certificate chain exists, otherwise make it. */ ++ if (ctx->chain == NULL && (ctx->chain = sk_X509_new_null()) == NULL) { ++ fprintf(stderr, "Could not create the certificate chain"); ++ ctx->error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY; ++ return -1; ++ } ++ ++ /* Verify chain depth */ ++ certLastIndex = sk_X509_num(ctx->untrusted); ++ if (certLastIndex > ctx->param->depth) { ++ fprintf(stderr, "Pruning certificate chain to %" PRIu64, certLastIndex); ++ certLastIndex = ctx->param->depth; ++ } ++ ++ CFMutableArrayRef certArray = CFArrayCreateMutable(NULL, certLastIndex + 1, NULL); ++ CFRetain(certArray); ++ ++ if (!add_cert_to_array(certArray, ctx->cert)) { ++ fprintf(stderr, "Failed to add certificate to array"); ++ CFRelease(certArray); ++ ctx->error = X509_V_ERR_UNSPECIFIED; ++ return -1; ++ } ++ ++ for (i = 0; i < certLastIndex; ++i) { ++ X509 *t = sk_X509_value(ctx->untrusted, i); ++ if (!add_cert_to_array(certArray, t)) { ++ fprintf(stderr, "Failed to add chain certificate %lld to array", i); ++ CFRelease(certArray); ++ ctx->error = X509_V_ERR_UNSPECIFIED; ++ return 0; ++ } ++ } ++ ++ // We put ASN.1 encoded X509 on the CertificateChain, so we don't call TEACertificateChainSetEncodingHandler ++ SecPolicyRef policy = get_policy(ctx->param); ++ ++ if (policy == NULL) { ++ fprintf(stderr, "Failed to create policy!\n"); ++ CFRelease(certArray); ++ ctx->error = X509_V_ERR_UNSPECIFIED; ++ return -1; ++ } ++ ++ SecTrustRef trust = NULL; ++ ++ if (SecTrustCreateWithCertificates(certArray, policy, &trust) != errSecSuccess) { ++ fprintf(stderr, "Failed to create trust!\n"); ++ CFRelease(certArray); ++ ctx->error = X509_V_ERR_CERT_UNTRUSTED; ++ return -1; ++ } ++ ++ if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) { ++ fprintf(stderr, "Setting time not supported yet?\n"); ++ SecTrustSetVerifyDate(trust, CFDateCreate(NULL, ctx->param->check_time)); ++ } ++ ++ SecTrustResultType result = 0; ++ ++ if (SecTrustEvaluate(trust, &result) != errSecSuccess || result != kSecTrustResultUnspecified) { ++ CFRelease(certArray); ++ ctx->error = X509_V_ERR_CERT_UNTRUSTED; ++ return 0; ++ } ++ ++ CFRelease(certArray); ++ ctx->error = 0; ++ return 1; ++} ++ ++#pragma mark Trust Evaluation Agent ++ ++/* -1: not set ++ * 0: set to false ++ * 1: set to true ++ */ ++static int tea_enabled = -1; ++ ++void ++X509_TEA_set_state(int change) ++{ ++ tea_enabled = (change) ? 1 : 0; ++} ++ ++int ++X509_TEA_is_enabled() ++{ ++ if (tea_enabled < 0) ++ tea_enabled = (NULL == getenv(X509_TEA_ENV_DISABLE)); ++ ++ return tea_enabled != 0; ++} +diff -Naur libressl-2.6.2.orig/crypto/x509/x509_vfy_apple.h libressl-2.6.2/crypto/x509/x509_vfy_apple.h +--- libressl-2.6.2.orig/crypto/x509/x509_vfy_apple.h 1970-01-01 01:00:00.000000000 +0100 ++++ libressl-2.6.2/crypto/x509/x509_vfy_apple.h 2017-10-07 14:05:16.000000000 +0200 +@@ -0,0 +1,74 @@ ++/* ++ * Copyright (c) 2009 Apple Inc. All Rights Reserved. ++ * ++ * @APPLE_LICENSE_HEADER_START@ ++ * ++ * This file contains Original Code and/or Modifications of Original Code ++ * as defined in and that are subject to the Apple Public Source License ++ * Version 2.0 (the 'License'). You may not use this file except in ++ * compliance with the License. Please obtain a copy of the License at ++ * http://www.opensource.apple.com/apsl/ and read it before using this ++ * file. ++ * ++ * The Original Code and all software distributed under the License are ++ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER ++ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ++ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. ++ * Please see the License for the specific language governing rights and ++ * limitations under the License. ++ * ++ * @APPLE_LICENSE_HEADER_END@ ++ * ++ */ ++ ++#ifndef HEADER_X509_H ++#include ++#endif ++ ++#ifndef HEADER_X509_VFY_APPLE_H ++#define HEADER_X509_VFY_APPLE_H ++ ++/* Environment variable name to disable TEA. */ ++#define X509_TEA_ENV_DISABLE "OPENSSL_X509_TEA_DISABLE" ++ ++/* ++ * X509_verify_cert ++ * ++ * Originally located in x509_vfy.c. ++ * ++ * Verify certificate with OpenSSL created X509_verify_cert. If and only if ++ * OpenSSL cannot get certificate issuer locally then OS X security API will ++ * verify the certificate, using Trust Evaluation Agent. ++ * ++ * Return values: ++ * -------------- ++ * -1: Null was passed for either ctx or ctx->cert. ++ * 0: Certificate is trusted. ++ * 1: Certificate is not trusted. ++ */ ++int X509_verify_cert(X509_STORE_CTX *ctx); ++ ++/* ++ * X509_TEA_is_enabled ++ * ++ * Is the Trust Evaluation Agent (TEA) used for certificate verification when ++ * the issuer cannot be verified. ++ * ++ * Returns 0 if TEA is disabled and 1 if TEA is enabled. ++ */ ++int X509_TEA_is_enabled(); ++ ++/* ++ * X509_TEA_set_state ++ * ++ * Enables/disables certificate verification with Trust Evaluation Agent (TEA) ++ * when the issuer cannot be verified. ++ * ++ * Pass 0 to disable TEA and non-zero to enable TEA. ++ */ ++void X509_TEA_set_state(int change); ++ ++int X509_verify_cert_orig(X509_STORE_CTX *ctx); ++ ++#endif /* HEADER_X509_VFY_APPLE_H */ diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py --- a/pypy/goal/targetpypystandalone.py +++ b/pypy/goal/targetpypystandalone.py @@ -352,8 +352,9 @@ def hack_for_cffi_modules(self, driver): # HACKHACKHACK - # ugly hack to modify target goal from compile_* to build_cffi_imports - # this should probably get cleaned up and merged with driver.create_exe + # ugly hack to modify target goal from compile_* to build_cffi_imports, + # as done in package.py + # this is needed by the benchmark buildbot run, maybe do it as a seperate step there? from rpython.tool.runsubprocess import run_subprocess from rpython.translator.driver import taskdef import types @@ -363,11 +364,14 @@ def task_build_cffi_imports(self): ''' Use cffi to compile cffi interfaces to modules''' filename = os.path.join(pypydir, 'tool', 'build_cffi_imports.py') + if sys.platform == 'darwin': + argv = [filename, '--embed-dependencies'] + else: + argv = [filename,] status, out, err = run_subprocess(str(driver.compute_exe_name()), - [filename]) + argv) sys.stdout.write(out) sys.stderr.write(err) - # otherwise, ignore errors driver.task_build_cffi_imports = types.MethodType(task_build_cffi_imports, driver) driver.tasks['build_cffi_imports'] = driver.task_build_cffi_imports, [compile_goal] driver.default_goal = 'build_cffi_imports' diff --git a/pypy/tool/build_cffi_imports.py b/pypy/tool/build_cffi_imports.py --- a/pypy/tool/build_cffi_imports.py +++ b/pypy/tool/build_cffi_imports.py @@ -1,5 +1,5 @@ from __future__ import print_function -import sys, shutil, os +import sys, shutil, os, tempfile, hashlib from os.path import join class MissingDependenciesError(Exception): @@ -22,12 +22,131 @@ "xx": None, # for testing: 'None' should be completely ignored } -def create_cffi_import_libraries(pypy_c, options, basedir, only=None): +# for distribution, we may want to fetch dependencies not provided by +# the OS, such as a recent openssl/libressl or liblzma/xz. +cffi_dependencies = { + 'lzma': ('https://tukaani.org/xz/xz-5.2.3.tar.gz', + '71928b357d0a09a12a4b4c5fafca8c31c19b0e7d3b8ebb19622e96f26dbf28cb', + []), + 'ssl': ('http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-2.6.2.tar.gz', + 'b029d2492b72a9ba5b5fcd9f3d602c9fd0baa087912f2aaecc28f52f567ec478', + ['--without-openssldir']), + '_gdbm': ('http://ftp.gnu.org/gnu/gdbm/gdbm-1.13.tar.gz', + '9d252cbd7d793f7b12bcceaddda98d257c14f4d1890d851c386c37207000a253', + ['--without-readline']), +} + + +def _unpack_tarfile(filename, extract_dir): + """Unpack tar/tar.gz/tar.bz2/tar.xz `filename` to `extract_dir` + """ + import tarfile # late import for breaking circular dependency + try: + tarobj = tarfile.open(filename) + except tarfile.TarError: + raise ReadError( + "%s is not a compressed or uncompressed tar file" % filename) + try: + tarobj.extractall(extract_dir) + finally: + tarobj.close() + +def _sha256(filename): + dgst = hashlib.sha256() + + with open(filename, 'rb') as fp: + dgst.update(fp.read()) + return dgst.hexdigest() + + +def _build_dependency(name, destdir, patches=[]): + import multiprocessing + import shutil + import subprocess + + from rpython.tool.runsubprocess import run_subprocess + + try: + from urllib.request import urlretrieve + except ImportError: + from urllib import urlretrieve + + try: + url, dgst, args = cffi_dependencies[name] + except KeyError: + return 0, None, None + + archive_dir = os.path.join(tempfile.gettempdir(), 'pypy-archives') + + if not os.path.isdir(archive_dir): + os.makedirs(archive_dir) + + archive = os.path.join(archive_dir, url.rsplit('/', 1)[-1]) + + # next, fetch the archive to disk, if needed + if not os.path.exists(archive) or _sha256(archive) != dgst: + print('fetching archive', url, file=sys.stderr) + urlretrieve(url, archive) + + # extract the archive into our destination directory + print('unpacking archive', archive, file=sys.stderr) + _unpack_tarfile(archive, destdir) + + sources = os.path.join( + destdir, + os.path.basename(archive)[:-7], + ) + + # apply any patches + if patches: + for patch in patches: + print('applying patch', patch, file=sys.stderr) + status, stdout, stderr = run_subprocess( + '/usr/bin/patch', ['-p1', '-i', patch], cwd=sources, + ) + + if status != 0: + return status, stdout, stderr + + print('configuring', sources, file=sys.stderr) + + # configure & build it + status, stdout, stderr = run_subprocess( + './configure', + [ + '--prefix=/usr', + '--disable-shared', + '--enable-silent-rules', + '--disable-dependency-tracking', + ] + args, + cwd=sources, + ) + + if status != 0: + return status, stdout, stderr + + print('building', sources, file=sys.stderr) + + status, stdout, stderr = run_subprocess( + 'make', + [ + '-s', '-j' + str(multiprocessing.cpu_count()), + 'install', 'DESTDIR={}/'.format(destdir), + ], + cwd=sources, + ) + + return status, stdout, stderr + + +def create_cffi_import_libraries(pypy_c, options, basedir, only=None, + embed_dependencies=False): from rpython.tool.runsubprocess import run_subprocess shutil.rmtree(str(join(basedir,'lib_pypy','__pycache__')), ignore_errors=True) failures = [] + for key, module in sorted(cffi_build_scripts.items()): if only and key not in only: print("* SKIPPING", key, '(not specified in --only)') @@ -40,9 +159,49 @@ else: args = ['-c', 'import ' + module] cwd = None + env = os.environ.copy() + print('*', ' '.join(args), file=sys.stderr) + if embed_dependencies: + curdir = os.path.abspath(os.path.dirname(__file__)) + destdir = os.path.join(curdir, 'dest') + + shutil.rmtree(destdir, ignore_errors=True) + os.makedirs(destdir) + + if key == 'ssl' and sys.platform == 'darwin': + # this patch is loosely inspired by an Apple and adds + # a fallback to the OS X roots when none are available + patches = [ + os.path.join(curdir, + '../../lib_pypy/_cffi_ssl/osx-roots.diff'), + ] + else: + patches = [] + + status, stdout, stderr = _build_dependency(key, destdir, + patches=patches) + + if status != 0: + failures.append((key, module)) + print("stdout:") + print(stdout.decode('utf-8')) + print("stderr:") + print(stderr.decode('utf-8')) + continue + + env['CPPFLAGS'] = \ + '-I{}/usr/include {}'.format(destdir, env.get('CPPFLAGS', '')) + env['LDFLAGS'] = \ + '-L{}/usr/lib {}'.format(destdir, env.get('LDFLAGS', '')) + + if key == 'ssl' and sys.platform == 'darwin': + # needed for our roots patch + env['LDFLAGS'] += ' -framework CoreFoundation -framework Security' + try: - status, stdout, stderr = run_subprocess(str(pypy_c), args, cwd=cwd) + status, stdout, stderr = run_subprocess(str(pypy_c), args, + cwd=cwd, env=env) if status != 0: failures.append((key, module)) print("stdout:") @@ -73,6 +232,8 @@ ' you can specify an alternative pypy vm here') parser.add_argument('--only', dest='only', default=None, help='Only build the modules delimited by a colon. E.g. ssl,sqlite') + parser.add_argument('--embed-dependencies', dest='embed_dependencies', action='store_true', + help='embed dependencies for distribution') args = parser.parse_args() exename = join(os.getcwd(), args.exefile) @@ -89,7 +250,8 @@ only = None else: only = set(args.only.split(',')) - failures = create_cffi_import_libraries(exename, options, basedir, only=only) + failures = create_cffi_import_libraries(exename, options, basedir, only=only, + embed_dependencies=args.embed_dependencies) if len(failures) > 0: print('*** failed to build the CFFI modules %r' % ( [f[1] for f in failures],), file=sys.stderr) diff --git a/pypy/tool/release/package.py b/pypy/tool/release/package.py --- a/pypy/tool/release/package.py +++ b/pypy/tool/release/package.py @@ -83,7 +83,11 @@ if not _fake and not pypy_runs(pypy_c): raise OSError("Running %r failed!" % (str(pypy_c),)) if not options.no_cffi: - failures = create_cffi_import_libraries(str(pypy_c), options, str(basedir)) + failures = create_cffi_import_libraries( + str(pypy_c), options, str(basedir), + embed_dependencies=options.embed_dependencies, + ) + for key, module in failures: print >>sys.stderr, """!!!!!!!!!!\nBuilding {0} bindings failed. You can either install development headers package, @@ -266,6 +270,15 @@ def package(*args, **kwds): import argparse + + class NegateAction(argparse.Action): + def __init__(self, option_strings, dest, nargs=0, **kwargs): + super(NegateAction, self).__init__(option_strings, dest, nargs, + **kwargs) + + def __call__(self, parser, ns, values, option): + setattr(ns, self.dest, option[2:4] != 'no') + if sys.platform == 'win32': pypy_exe = 'pypy3.exe' else: @@ -296,12 +309,20 @@ help='destination dir for archive') parser.add_argument('--override_pypy_c', type=str, default='', help='use as pypy3 exe instead of pypy/goal/pypy3-c') + parser.add_argument('--embedded-dependencies', '--no-embedded-dependencies', + dest='embed_dependencies', + action=NegateAction, + default=(sys.platform == 'darwin'), + help='whether to embed dependencies for distribution ' + '(default on OS X)') options = parser.parse_args(args) if os.environ.has_key("PYPY_PACKAGE_NOSTRIP"): options.nostrip = True if os.environ.has_key("PYPY_PACKAGE_WITHOUTTK"): options.no_tk = True + if os.environ.has_key("PYPY_EMBED_DEPENDENCIES"): + options.embed_dependencies = True if not options.builddir: # The import actually creates the udir directory from rpython.tool.udir import udir From pypy.commits at gmail.com Mon Oct 30 13:12:35 2017 From: pypy.commits at gmail.com (mattip) Date: Mon, 30 Oct 2017 10:12:35 -0700 (PDT) Subject: [pypy-commit] pypy default: graft rpython changes from py3.5, maybe we should enforce sycnronization with a test? Message-ID: <59f75d83.1bb3df0a.419be.a9af@mx.google.com> Author: Matti Picus Branch: Changeset: r92884:80602ff2f2d3 Date: 2017-10-30 18:51 +0200 http://bitbucket.org/pypy/pypy/changeset/80602ff2f2d3/ Log: graft rpython changes from py3.5, maybe we should enforce sycnronization with a test? diff --git a/rpython/rlib/_rsocket_rffi.py b/rpython/rlib/_rsocket_rffi.py --- a/rpython/rlib/_rsocket_rffi.py +++ b/rpython/rlib/_rsocket_rffi.py @@ -162,7 +162,7 @@ IP_RECVRETOPTS IP_RETOPTS IP_TOS IP_TTL MSG_BTAG MSG_ETAG MSG_CTRUNC MSG_DONTROUTE MSG_DONTWAIT MSG_EOR MSG_OOB -MSG_PEEK MSG_TRUNC MSG_WAITALL +MSG_PEEK MSG_TRUNC MSG_WAITALL MSG_ERRQUEUE NI_DGRAM NI_MAXHOST NI_MAXSERV NI_NAMEREQD NI_NOFQDN NI_NUMERICHOST NI_NUMERICSERV @@ -348,7 +348,8 @@ ('ifr_name', rffi.CFixedArray(rffi.CHAR, 8))]) # insert handler for sendmsg / recvmsg here -if _POSIX: +HAVE_SENDMSG = bool(_POSIX) +if HAVE_SENDMSG: includes = ['stddef.h', 'sys/socket.h', 'unistd.h', @@ -534,7 +535,7 @@ int cmsg_status; struct iovec iov; struct recvmsg_info* retinfo; - int error_flag; // variable to be set in case of special errors. + int error_flag = 0; // variable to be set in case of special errors. int cmsgdatalen = 0; // variables that are set to 1, if the message charp has been allocated @@ -708,6 +709,7 @@ free(retinfo); } } + if (error_flag==0) error_flag = -1; return error_flag; err_closefds: diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -472,7 +472,7 @@ def close(fd): validate_fd(fd) handle_posix_error('close', c_close(fd)) - + c_lseek = external('_lseeki64' if _WIN32 else 'lseek', [rffi.INT, rffi.LONGLONG, rffi.INT], rffi.LONGLONG, macro=_MACRO_ON_POSIX, save_err=rffi.RFFI_SAVE_ERRNO) @@ -505,7 +505,7 @@ with rffi.scoped_alloc_buffer(count) as buf: void_buf = rffi.cast(rffi.VOIDP, buf.raw) return buf.str(handle_posix_error('pread', c_pread(fd, void_buf, count, offset))) - + @enforceargs(int, None, None) def pwrite(fd, data, offset): count = len(data) @@ -1851,6 +1851,8 @@ rffi.INT, save_err=rffi.RFFI_FULL_ERRNO_ZERO) c_sched_get_priority_min = external('sched_get_priority_min', [rffi.INT], rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO) + if not _WIN32: + c_sched_yield = external('sched_yield', [], rffi.INT) @enforceargs(int) def sched_get_priority_max(policy): @@ -1860,8 +1862,8 @@ def sched_get_priority_min(policy): return handle_posix_error('sched_get_priority_min', c_sched_get_priority_min(policy)) - - + def sched_yield(): + return handle_posix_error('sched_yield', c_sched_yield()) #___________________________________________________________________ diff --git a/rpython/rlib/rsocket.py b/rpython/rlib/rsocket.py --- a/rpython/rlib/rsocket.py +++ b/rpython/rlib/rsocket.py @@ -1074,7 +1074,7 @@ if address is not None: address.unlock() - if _c.geterrno() == _c.EINTR: + if (_c.geterrno() == _c.EINTR) or (_c.geterrno() == 11): raise last_error() if (reply == -10000): raise RSocketError("Invalid message size") @@ -1393,7 +1393,7 @@ return (make_socket(fd0, family, type, proto, SocketClass), make_socket(fd1, family, type, proto, SocketClass)) -if _c._POSIX: +if _c.HAVE_SENDMSG: def CMSG_LEN( demanded_len): """ Socket method to determine the optimal byte size of the ancillary. diff --git a/rpython/rlib/test/test_rposix.py b/rpython/rlib/test/test_rposix.py --- a/rpython/rlib/test/test_rposix.py +++ b/rpython/rlib/test/test_rposix.py @@ -676,7 +676,7 @@ prio = rposix.getpriority(rposix.PRIO_PROCESS, 0) rposix.setpriority(rposix.PRIO_PROCESS, 0, prio) py.test.raises(OSError, rposix.getpriority, rposix.PRIO_PGRP, 123456789) - + if sys.platform != 'win32': def test_sendfile(): from rpython.rlib import rsocket @@ -723,7 +723,7 @@ os.close(fd) s2.close() s1.close() - + @rposix_requires('pread') def test_pread(): fname = str(udir.join('os_test.txt')) @@ -794,14 +794,14 @@ assert rposix.sched_get_priority_max(rposix.SCHED_RR) != -1 assert rposix.sched_get_priority_max(rposix.SCHED_OTHER) != -1 assert rposix.sched_get_priority_max(rposix.SCHED_BATCH) != -1 - + @rposix_requires('sched_get_priority_min') def test_sched_get_priority_min(): assert rposix.sched_get_priority_min(rposix.SCHED_FIFO) != -1 assert rposix.sched_get_priority_min(rposix.SCHED_RR) != -1 assert rposix.sched_get_priority_min(rposix.SCHED_OTHER) != -1 assert rposix.sched_get_priority_min(rposix.SCHED_BATCH) != -1 - + @rposix_requires('sched_get_priority_min') def test_os_sched_priority_max_greater_than_min(): policy = rposix.SCHED_RR @@ -811,3 +811,8 @@ assert isinstance(high, int) == True assert high > low + at rposix_requires('sched_yield') +def test_sched_yield(): + if sys.platform != 'win32': + rposix.sched_yield() + From pypy.commits at gmail.com Mon Oct 30 13:16:36 2017 From: pypy.commits at gmail.com (mattip) Date: Mon, 30 Oct 2017 10:16:36 -0700 (PDT) Subject: [pypy-commit] buildbot default: revert part of 2336f92eb562 that tries to run rpython tests on non-default branch Message-ID: <59f75e74.05d21c0a.82886.2d72@mx.google.com> Author: Matti Picus Branch: Changeset: r1036:bf6e08ac46ee Date: 2017-10-30 19:16 +0200 http://bitbucket.org/pypy/buildbot/changeset/bf6e08ac46ee/ Log: revert part of 2336f92eb562 that tries to run rpython tests on non- default branch Instead we should have a commit hook that rejects changes to rpython from py3.5 diff --git a/bot2/pypybuildbot/master.py b/bot2/pypybuildbot/master.py --- a/bot2/pypybuildbot/master.py +++ b/bot2/pypybuildbot/master.py @@ -346,15 +346,6 @@ JITWIN32, # on allegro_win32, SalsaSalsa ], branch="py3.5", hour=3, minute=0), - # XXX disable this if you are SURE py3.5 and default rpython are identical - Nightly("nightly-3-01-py3.5", [ - LINUX32RPYTHON, # on tannit32, uses all cores - LINUX64RPYTHON, # on bencher4, uses all cores - ], branch="py3.5", hour=3, minute=0, onlyIfChanged=True, - fileIsImportant=isRPython, - change_filter=filter.ChangeFilter(branch='py3.5'), - ), - # S390X vm (ibm-research) Nightly("nightly-4-00", [ LINUX_S390XOWN, From pypy.commits at gmail.com Tue Oct 31 12:06:19 2017 From: pypy.commits at gmail.com (fijal) Date: Tue, 31 Oct 2017 09:06:19 -0700 (PDT) Subject: [pypy-commit] pypy memory-accounting: add one more data point Message-ID: <59f89f7b.a8a0df0a.76977.068a@mx.google.com> Author: fijal Branch: memory-accounting Changeset: r92887:3c6f624e8ed0 Date: 2017-10-31 17:05 +0100 http://bitbucket.org/pypy/pypy/changeset/3c6f624e8ed0/ Log: add one more data point diff --git a/pypy/module/gc/referents.py b/pypy/module/gc/referents.py --- a/pypy/module/gc/referents.py +++ b/pypy/module/gc/referents.py @@ -174,10 +174,13 @@ class W_GcStats(W_Root): def __init__(self): self.total_memory_pressure = rgc.get_stats(rgc.TOTAL_MEMORY_PRESSURE) + self.total_gc_memory = rgc.get_stats(rgc.TOTAL_MEMORY) W_GcStats.typedef = TypeDef("GcStats", total_memory_pressure=interp_attrproperty("total_memory_pressure", cls=W_GcStats, wrapfn="newint"), + total_gc_memory=interp_attrproperty("total_gc_memory", + cls=W_GcStats, wrapfn="newint") ) def get_stats(space): diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py --- a/rpython/memory/gc/incminimark.py +++ b/rpython/memory/gc/incminimark.py @@ -2923,6 +2923,8 @@ from rpython.memory.gc import inspector if stats_no == rgc.TOTAL_MEMORY: + return intmask(self.get_total_memory_used() + self.nursery_size) + elif stats_no == rgc.TOTAL_ALLOCATED_MEMORY: return 0 elif stats_no == rgc.TOTAL_MEMORY_PRESSURE: return inspector.count_memory_pressure(self) diff --git a/rpython/memory/gc/minimarkpage.py b/rpython/memory/gc/minimarkpage.py --- a/rpython/memory/gc/minimarkpage.py +++ b/rpython/memory/gc/minimarkpage.py @@ -140,6 +140,7 @@ # the total memory used, counting every block in use, without # the additional bookkeeping stuff. self.total_memory_used = r_uint(0) + self.total_memory_alloced = r_uint(0) def _new_page_ptr_list(self, length): diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py --- a/rpython/rlib/rgc.py +++ b/rpython/rlib/rgc.py @@ -650,7 +650,7 @@ else: return id(gcref._x) -TOTAL_MEMORY, TOTAL_MEMORY_PRESSURE = range(2) +TOTAL_MEMORY, TOTAL_ALLOCATED_MEMORY, TOTAL_MEMORY_PRESSURE = range(3) @not_rpython def get_stats(stat_no): From pypy.commits at gmail.com Tue Oct 31 13:18:31 2017 From: pypy.commits at gmail.com (fijal) Date: Tue, 31 Oct 2017 10:18:31 -0700 (PDT) Subject: [pypy-commit] pypy unicode-utf8: whack whack whack, fix UnicodeBuilder, so we can make json work Message-ID: <59f8b067.4fc7df0a.3841c.201f@mx.google.com> Author: fijal Branch: unicode-utf8 Changeset: r92888:1b909417f0e0 Date: 2017-10-31 18:17 +0100 http://bitbucket.org/pypy/pypy/changeset/1b909417f0e0/ Log: whack whack whack, fix UnicodeBuilder, so we can make json work diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py --- a/pypy/module/__pypy__/__init__.py +++ b/pypy/module/__pypy__/__init__.py @@ -12,7 +12,7 @@ interpleveldefs = { "StringBuilder": "interp_builders.W_StringBuilder", - #"UnicodeBuilder": "interp_builders.W_UnicodeBuilder", + "UnicodeBuilder": "interp_builders.W_UnicodeBuilder", } class TimeModule(MixedModule): diff --git a/pypy/module/__pypy__/interp_builders.py b/pypy/module/__pypy__/interp_builders.py --- a/pypy/module/__pypy__/interp_builders.py +++ b/pypy/module/__pypy__/interp_builders.py @@ -2,60 +2,98 @@ from pypy.interpreter.error import oefmt from pypy.interpreter.gateway import interp2app, unwrap_spec from pypy.interpreter.typedef import TypeDef -from rpython.rlib.rstring import UnicodeBuilder, StringBuilder +from rpython.rlib.rstring import StringBuilder +from pypy.objspace.std.unicodeobject import W_UnicodeObject from rpython.tool.sourcetools import func_with_new_name -def create_builder(name, strtype, builder_cls, newmethod): - if strtype is str: - unwrap = 'bytes' - else: - unwrap = unicode - class W_Builder(W_Root): - def __init__(self, space, size): - if size < 0: - self.builder = builder_cls() - else: - self.builder = builder_cls(size) +class W_StringBuilder(W_Root): + def __init__(self, space, size): + if size < 0: + self.builder = StringBuilder() + else: + self.builder = StringBuilder(size) - @unwrap_spec(size=int) - def descr__new__(space, w_subtype, size=-1): - return W_Builder(space, size) + @unwrap_spec(size=int) + def descr__new__(space, w_subtype, size=-1): + return W_StringBuilder(space, size) - @unwrap_spec(s=unwrap) - def descr_append(self, space, s): - self.builder.append(s) + @unwrap_spec(s='bytes') + def descr_append(self, space, s): + self.builder.append(s) - @unwrap_spec(s=unwrap, start=int, end=int) - def descr_append_slice(self, space, s, start, end): - if not 0 <= start <= end <= len(s): - raise oefmt(space.w_ValueError, "bad start/stop") - self.builder.append_slice(s, start, end) + @unwrap_spec(s='bytes', start=int, end=int) + def descr_append_slice(self, space, s, start, end): + if not 0 <= start <= end <= len(s): + raise oefmt(space.w_ValueError, "bad start/stop") + self.builder.append_slice(s, start, end) - def descr_build(self, space): - w_s = getattr(space, newmethod)(self.builder.build()) - # after build(), we can continue to append more strings - # to the same builder. This is supported since - # 2ff5087aca28 in RPython. - return w_s + def descr_build(self, space): + w_s = space.newbytes(self.builder.build()) + # after build(), we can continue to append more strings + # to the same builder. This is supported since + # 2ff5087aca28 in RPython. + return w_s - def descr_len(self, space): - if self.builder is None: - raise oefmt(space.w_ValueError, "no length of built builder") - return space.newint(self.builder.getlength()) + def descr_len(self, space): + if self.builder is None: + raise oefmt(space.w_ValueError, "no length of built builder") + return space.newint(self.builder.getlength()) - W_Builder.__name__ = "W_%s" % name - W_Builder.typedef = TypeDef(name, - __new__ = interp2app(func_with_new_name( - W_Builder.descr__new__.im_func, - '%s_new' % (name,))), - append = interp2app(W_Builder.descr_append), - append_slice = interp2app(W_Builder.descr_append_slice), - build = interp2app(W_Builder.descr_build), - __len__ = interp2app(W_Builder.descr_len), - ) - W_Builder.typedef.acceptable_as_base_class = False - return W_Builder +W_StringBuilder.typedef = TypeDef("StringBuilder", + __new__ = interp2app(func_with_new_name( + W_StringBuilder.descr__new__.im_func, + 'StringBuilder_new')), + append = interp2app(W_StringBuilder.descr_append), + append_slice = interp2app(W_StringBuilder.descr_append_slice), + build = interp2app(W_StringBuilder.descr_build), + __len__ = interp2app(W_StringBuilder.descr_len), +) +W_StringBuilder.typedef.acceptable_as_base_class = False -W_StringBuilder = create_builder("StringBuilder", str, StringBuilder, "newbytes") -#W_UnicodeBuilder = create_builder("UnicodeBuilder", unicode, UnicodeBuilder, "newunicode") +class W_UnicodeBuilder(W_Root): + def __init__(self, space, size): + if size < 0: + self.builder = StringBuilder() + else: + self.builder = StringBuilder(size) + + @unwrap_spec(size=int) + def descr__new__(space, w_subtype, size=-1): + return W_UnicodeBuilder(space, size) + + @unwrap_spec(s='utf8') + def descr_append(self, space, s): + self.builder.append(s) + + @unwrap_spec(start=int, end=int) + def descr_append_slice(self, space, w_s, start, end): + w_unicode = W_UnicodeObject.convert_arg_to_w_unicode(space, w_s) + if not 0 <= start <= end <= w_unicode._len(): + raise oefmt(space.w_ValueError, "bad start/stop") + byte_start = w_unicode._index_to_byte(start) + byte_end = w_unicode._index_to_byte(end) + self.builder.append_slice(w_unicode._utf8, byte_start, byte_end) + + def descr_build(self, space): + w_s = space.newtext(self.builder.build()) + # after build(), we can continue to append more strings + # to the same builder. This is supported since + # 2ff5087aca28 in RPython. + return w_s + + def descr_len(self, space): + if self.builder is None: + raise oefmt(space.w_ValueError, "no length of built builder") + return space.newint(self.builder.getlength()) + +W_UnicodeBuilder.typedef = TypeDef("UnicodeBuilder", + __new__ = interp2app(func_with_new_name( + W_UnicodeBuilder.descr__new__.im_func, + 'UnicodeBuilder_new')), + append = interp2app(W_UnicodeBuilder.descr_append), + append_slice = interp2app(W_UnicodeBuilder.descr_append_slice), + build = interp2app(W_UnicodeBuilder.descr_build), + __len__ = interp2app(W_UnicodeBuilder.descr_len), +) +W_UnicodeBuilder.typedef.acceptable_as_base_class = False 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 @@ -596,7 +596,7 @@ encoding = rffi.str2charp("latin-1") w_res = PyString_AsDecodedObject(space, w_str, encoding, None) rffi.free_charp(encoding) - assert space.unwrap(w_res) == u"caf\xe9" + assert w_res._utf8 == u"caf\xe9".encode('utf8') def test_eq(self, space): assert 1 == _PyString_Eq( From pypy.commits at gmail.com Tue Oct 31 13:31:55 2017 From: pypy.commits at gmail.com (fijal) Date: Tue, 31 Oct 2017 10:31:55 -0700 (PDT) Subject: [pypy-commit] pypy unicode-utf8: rewrite to_decimal Message-ID: <59f8b38b.508d1c0a.f0b96.c60c@mx.google.com> Author: fijal Branch: unicode-utf8 Changeset: r92889:81c556f8f8b4 Date: 2017-10-31 18:31 +0100 http://bitbucket.org/pypy/pypy/changeset/81c556f8f8b4/ Log: rewrite to_decimal diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -1746,21 +1746,22 @@ def unicode_to_decimal_w(space, w_unistr): if not isinstance(w_unistr, W_UnicodeObject): raise oefmt(space.w_TypeError, "expected unicode, got '%T'", w_unistr) - unistr = w_unistr._utf8.decode("utf8") - # XXX speed up + unistr = w_unistr._utf8 result = ['\0'] * len(unistr) digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] - for i in xrange(len(unistr)): - uchr = ord(unistr[i]) - if unicodedb.isspace(uchr): - result[i] = ' ' + i = 0 + res_pos = 0 + while i < len(unistr): + uchr = rutf8.codepoint_at_pos(unistr, i) + if rutf8.isspace(unistr, i): + result[res_pos] = ' ' continue try: - result[i] = digits[unicodedb.decimal(uchr)] + result[res_pos] = digits[unicodedb.decimal(uchr)] except KeyError: if 0 < uchr < 256: - result[i] = chr(uchr) + result[res_pos] = chr(uchr) else: w_encoding = space.newtext('decimal') w_start = space.newint(i) @@ -1770,6 +1771,8 @@ space.newtuple([w_encoding, w_unistr, w_start, w_end, w_reason])) + i = rutf8.next_codepoint_pos(unistr, i) + res_pos += 1 return ''.join(result) From pypy.commits at gmail.com Tue Oct 31 14:18:34 2017 From: pypy.commits at gmail.com (fijal) Date: Tue, 31 Oct 2017 11:18:34 -0700 (PDT) Subject: [pypy-commit] pypy unicode-utf8: fix readbuf_w Message-ID: <59f8be7a.cd5e1c0a.34b5a.99c7@mx.google.com> Author: fijal Branch: unicode-utf8 Changeset: r92890:f7f1771c68d3 Date: 2017-10-31 18:43 +0100 http://bitbucket.org/pypy/pypy/changeset/f7f1771c68d3/ Log: fix readbuf_w diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -87,13 +87,15 @@ def readbuf_w(self, space): # XXX for now - from rpython.rlib.rstruct.unichar import pack_unichar, UNICODE_SIZE - v = self._utf8.decode("utf8") - builder = MutableStringBuffer(len(v) * UNICODE_SIZE) + from rpython.rlib.rstruct.unichar import pack_codepoint, UNICODE_SIZE + builder = MutableStringBuffer(self._len() * UNICODE_SIZE) pos = 0 - for unich in v: - pack_unichar(unich, builder, pos) + i = 0 + while i < len(self._utf8): + unich = rutf8.codepoint_at_pos(self._utf8, i) + pack_codepoint(unich, builder, pos) pos += UNICODE_SIZE + i = rutf8.next_codepoint_pos(self._utf8, i) return StringBuffer(builder.finish()) def writebuf_w(self, space): diff --git a/rpython/rlib/rstruct/unichar.py b/rpython/rlib/rstruct/unichar.py --- a/rpython/rlib/rstruct/unichar.py +++ b/rpython/rlib/rstruct/unichar.py @@ -12,24 +12,27 @@ BIGENDIAN = sys.byteorder == "big" def pack_unichar(unich, buf, pos): + pack_codepoint(ord(unich), buf, pos) + +def pack_codepoint(unich, buf, pos): if UNICODE_SIZE == 2: if BIGENDIAN: - buf.setitem(pos, chr(ord(unich) >> 8)) - buf.setitem(pos+1, chr(ord(unich) & 0xFF)) + buf.setitem(pos, chr(unich >> 8)) + buf.setitem(pos+1, chr(unich & 0xFF)) else: - buf.setitem(pos, chr(ord(unich) & 0xFF)) - buf.setitem(pos+1, chr(ord(unich) >> 8)) + buf.setitem(pos, chr(unich & 0xFF)) + buf.setitem(pos+1, chr(unich >> 8)) else: if BIGENDIAN: - buf.setitem(pos, chr(ord(unich) >> 24)) - buf.setitem(pos+1, chr((ord(unich) >> 16) & 0xFF)) - buf.setitem(pos+2, chr((ord(unich) >> 8) & 0xFF)) - buf.setitem(pos+3, chr(ord(unich) & 0xFF)) + buf.setitem(pos, chr(unich >> 24)) + buf.setitem(pos+1, chr((unich >> 16) & 0xFF)) + buf.setitem(pos+2, chr((unich >> 8) & 0xFF)) + buf.setitem(pos+3, chr(unich & 0xFF)) else: - buf.setitem(pos, chr(ord(unich) & 0xFF)) - buf.setitem(pos+1, chr((ord(unich) >> 8) & 0xFF)) - buf.setitem(pos+2, chr((ord(unich) >> 16) & 0xFF)) - buf.setitem(pos+3, chr(ord(unich) >> 24)) + buf.setitem(pos, chr(unich & 0xFF)) + buf.setitem(pos+1, chr((unich >> 8) & 0xFF)) + buf.setitem(pos+2, chr((unich >> 16) & 0xFF)) + buf.setitem(pos+3, chr(unich >> 24)) def unpack_unichar(rawstring): assert len(rawstring) == UNICODE_SIZE From pypy.commits at gmail.com Tue Oct 31 14:18:36 2017 From: pypy.commits at gmail.com (mattip) Date: Tue, 31 Oct 2017 11:18:36 -0700 (PDT) Subject: [pypy-commit] pypy unicode-utf8: move test summary to end on terminal, after the multiple pages of failure info, typos Message-ID: <59f8be7c.93111c0a.e334e.c728@mx.google.com> Author: Matti Picus Branch: unicode-utf8 Changeset: r92891:c77076692e78 Date: 2017-10-28 22:18 +0300 http://bitbucket.org/pypy/pypy/changeset/c77076692e78/ Log: move test summary to end on terminal, after the multiple pages of failure info, typos diff --git a/_pytest/terminal.py b/_pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -366,11 +366,11 @@ EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, EXIT_USAGEERROR, EXIT_NOTESTSCOLLECTED) if exitstatus in summary_exit_codes: - self.config.hook.pytest_terminal_summary(terminalreporter=self) self.summary_errors() self.summary_failures() self.summary_warnings() self.summary_passes() + self.config.hook.pytest_terminal_summary(terminalreporter=self) if exitstatus == EXIT_INTERRUPTED: self._report_keyboardinterrupt() del self._keyboardinterrupt_memo diff --git a/pypy/test_all.py b/pypy/test_all.py --- a/pypy/test_all.py +++ b/pypy/test_all.py @@ -27,7 +27,7 @@ sys.path.insert(0,os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) import pytest if sys.platform == 'win32': - #Try to avoid opeing a dialog box if one of the tests causes a system error + #Try to avoid opening a dialog box if one of the tests causes a system error # We do this in runner.py, but buildbots run twisted which ruins inheritance # in windows subprocesses. import ctypes diff --git a/testrunner/runner.py b/testrunner/runner.py --- a/testrunner/runner.py +++ b/testrunner/runner.py @@ -21,7 +21,7 @@ win32api.CloseHandle(proch) except pywintypes.error, e: pass - #Try to avoid opeing a dialog box if one of the tests causes a system error + #Try to avoid opening a dialog box if one of the tests causes a system error import ctypes winapi = ctypes.windll.kernel32 SetErrorMode = winapi.SetErrorMode From pypy.commits at gmail.com Tue Oct 31 20:29:37 2017 From: pypy.commits at gmail.com (antocuni) Date: Tue, 31 Oct 2017 17:29:37 -0700 (PDT) Subject: [pypy-commit] pypy keep-debug-symbols: add a smartstrip tool, which can optionally keep the debug symbols in a separate file, instead of just stripping them away Message-ID: <59f91571.c4581c0a.5fcb4.65d3@mx.google.com> Author: Antonio Cuni Branch: keep-debug-symbols Changeset: r92892:005844269621 Date: 2017-11-01 01:19 +0100 http://bitbucket.org/pypy/pypy/changeset/005844269621/ Log: add a smartstrip tool, which can optionally keep the debug symbols in a separate file, instead of just stripping them away diff --git a/pypy/tool/release/smartstrip.py b/pypy/tool/release/smartstrip.py new file mode 100644 --- /dev/null +++ b/pypy/tool/release/smartstrip.py @@ -0,0 +1,32 @@ +""" +Strip symbols from an executable, but keep them in a .debug file +""" + +import sys +import os +import py + +def _strip(exe): + if sys.platform == 'win32': + pass + elif sys.platform == 'darwin': + # 'strip' fun: see issue #587 for why -x + os.system("strip -x " + str(exe)) # ignore errors + else: + os.system("strip " + str(exe)) # ignore errors + +def _extract_debug_symbols(exe, debug): + if sys.platform == 'linux2': + os.system("objcopy --only-keep-debug %s %s" % (exe, debug)) + os.system("objcopy --add-gnu-debuglink=%s %s" % (debug, exe)) + +def smartstrip(exe, keep_debug=True): + exe = py.path.local(exe) + debug = py.path.local(str(exe) + '.debug') + if keep_debug: + _extract_debug_symbols(exe, debug) + _strip(exe) + + +if __name__ == '__main__': + smartstrip(sys.argv[1]) diff --git a/pypy/tool/release/test/test_smartstrip.py b/pypy/tool/release/test/test_smartstrip.py new file mode 100644 --- /dev/null +++ b/pypy/tool/release/test/test_smartstrip.py @@ -0,0 +1,50 @@ +import pytest +import sys +import os +from commands import getoutput +from pypy.tool.release.smartstrip import smartstrip + + at pytest.fixture +def exe(tmpdir): + src = tmpdir.join("myprog.c") + src.write(""" + int foo(int a, int b) { + return a+b; + } + int main(void) { } + """) + exe = tmpdir.join("myprog") + ret = os.system("gcc -o %s %s" % (exe, src)) + assert ret == 0 + return exe + +def info_symbol(exe, symbol): + out = getoutput("gdb %s -ex 'info symbol %s' -ex 'quit'" % (exe, symbol)) + lines = out.splitlines() + return lines[-1] + + at pytest.mark.skipif(sys.platform == 'win32', + reason='strip not supported on windows') +class TestSmarStrip(object): + + def test_info_symbol(self, exe): + info = info_symbol(exe, "foo") + assert info == "foo in section .text" + + def test_strip(self, exe): + smartstrip(exe, keep_debug=False) + info = info_symbol(exe, "foo") + assert info.startswith("No symbol table is loaded") + + @pytest.mark.skipif(sys.platform != 'linux2', + reason='keep_debug not supported') + def test_keep_debug(self, exe, tmpdir): + smartstrip(exe, keep_debug=True) + debug = tmpdir.join("myprog.debug") + assert debug.check(file=True) + info = info_symbol(exe, "foo") + assert info == "foo in section .text of %s" % exe + # + debug.remove() + info = info_symbol(exe, "foo") + assert info.startswith("No symbol table is loaded") From pypy.commits at gmail.com Tue Oct 31 20:29:39 2017 From: pypy.commits at gmail.com (antocuni) Date: Tue, 31 Oct 2017 17:29:39 -0700 (PDT) Subject: [pypy-commit] pypy keep-debug-symbols: remove the nostrip option; introduce a --no-keep-debug option; use smartstrip(keep_debug=True) by default Message-ID: <59f91573.7cb8df0a.9f774.acea@mx.google.com> Author: Antonio Cuni Branch: keep-debug-symbols Changeset: r92893:7108fe737121 Date: 2017-11-01 01:28 +0100 http://bitbucket.org/pypy/pypy/changeset/7108fe737121/ Log: remove the nostrip option; introduce a --no-keep-debug option; use smartstrip(keep_debug=True) by default diff --git a/pypy/tool/release/package.py b/pypy/tool/release/package.py --- a/pypy/tool/release/package.py +++ b/pypy/tool/release/package.py @@ -21,6 +21,7 @@ import fnmatch import subprocess import glob +from pypy.tool.release.smartstrip import smartstrip if sys.version_info < (2,6): py.test.skip("requires 2.6 so far") @@ -212,15 +213,8 @@ old_dir = os.getcwd() try: os.chdir(str(builddir)) - if not options.nostrip: - for source, target in binaries: - if sys.platform == 'win32': - pass - elif sys.platform == 'darwin': - # 'strip' fun: see issue #587 for why -x - os.system("strip -x " + str(bindir.join(target))) # ignore errors - else: - os.system("strip " + str(bindir.join(target))) # ignore errors + for source, target in binaries: + smartstrip(bindir.join(target), keep_debug=options.keep_debug) # if USE_ZIPFILE_MODULE: import zipfile @@ -281,8 +275,8 @@ help='do not build and package the %r cffi module' % (key,)) parser.add_argument('--without-cffi', dest='no_cffi', action='store_true', help='skip building *all* the cffi modules listed above') - parser.add_argument('--nostrip', dest='nostrip', action='store_true', - help='do not strip the exe, making it ~10MB larger') + parser.add_argument('--no-keep-debug', dest='keep_debug', + action='store_false', help='do not keep debug symbols') parser.add_argument('--rename_pypy_c', dest='pypy_c', type=str, default=pypy_exe, help='target executable name, defaults to "pypy"') parser.add_argument('--archive-name', dest='name', type=str, default='', @@ -295,8 +289,8 @@ help='use as pypy exe instead of pypy/goal/pypy-c') options = parser.parse_args(args) - if os.environ.has_key("PYPY_PACKAGE_NOSTRIP"): - options.nostrip = True + if os.environ.has_key("PYPY_PACKAGE_NOKEEPDEBUG"): + options.keep_debug = False if os.environ.has_key("PYPY_PACKAGE_WITHOUTTK"): options.no_tk = True if not options.builddir: