[pypy-commit] pypy py3k: hg merge default

rlamy pypy.commits at gmail.com
Wed Apr 6 11:07:02 EDT 2016


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: py3k
Changeset: r83542:c0fce2e85c79
Date: 2016-04-06 16:06 +0100
http://bitbucket.org/pypy/pypy/changeset/c0fce2e85c79/

Log:	hg merge default

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
@@ -35,3 +35,8 @@
 
 .. branch: win32-lib-name
 
+.. branch: remove-frame-forcing-in-executioncontext
+
+.. branch: rposix-for-3
+
+Wrap more POSIX functions in `rpython.rlib.rposix`.
diff --git a/pypy/module/__pypy__/test/test_magic.py b/pypy/module/__pypy__/test/test_magic.py
--- a/pypy/module/__pypy__/test/test_magic.py
+++ b/pypy/module/__pypy__/test/test_magic.py
@@ -57,7 +57,7 @@
         assert _promote(1) == 1
         assert _promote(1.1) == 1.1
         assert _promote("abc") == "abc"
-        assert _promote(u"abc") == u"abc"
+        raises(TypeError, _promote, u"abc")
         l = []
         assert _promote(l) is l
         class A(object):
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
@@ -14,7 +14,7 @@
         tmpfile2 = open(self.tmpfilename2, 'wb')
         tmpfileno2 = tmpfile2.fileno()
 
-        import struct, sys
+        import struct, sys, gc
 
         WORD = struct.calcsize('l')
         
@@ -46,6 +46,8 @@
             return count
         
         import _vmprof
+        gc.collect()  # try to make the weakref list deterministic
+        gc.collect()  # by freeing all dead code objects
         _vmprof.enable(tmpfileno, 0.01)
         _vmprof.disable()
         s = open(self.tmpfilename, 'rb').read()
@@ -60,6 +62,8 @@
             pass
         """, d)
 
+        gc.collect()
+        gc.collect()
         _vmprof.enable(tmpfileno2, 0.01)
 
         exec_("""def foo2():
diff --git a/rpython/jit/backend/detect_cpu.py b/rpython/jit/backend/detect_cpu.py
--- a/rpython/jit/backend/detect_cpu.py
+++ b/rpython/jit/backend/detect_cpu.py
@@ -146,7 +146,7 @@
         MODEL_X86_64: ['floats', 'singlefloats'],
         MODEL_X86_64_SSE4: ['floats', 'singlefloats'],
         MODEL_ARM: ['floats', 'singlefloats', 'longlong'],
-        MODEL_PPC_64: [], # we don't even have PPC directory, so no
+        MODEL_PPC_64: ['floats'],
         MODEL_S390_64: ['floats'],
     }[backend_name]
 
diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py
--- a/rpython/jit/metainterp/pyjitpl.py
+++ b/rpython/jit/metainterp/pyjitpl.py
@@ -1592,43 +1592,70 @@
                 resbox = self._do_jit_force_virtual(allboxes, descr, pc)
                 if resbox is not None:
                     return resbox
+
+            # 1. preparation
             self.metainterp.vable_and_vrefs_before_residual_call()
+
+            # 2. actually do the call now (we'll have cases later): the
+            #    result is stored into 'c_result' for now, which is a Const
+            metainterp = self.metainterp
             tp = descr.get_normalized_result_type()
-            resbox = NOT_HANDLED
-            opnum = -1
-            if effectinfo.oopspecindex == effectinfo.OS_LIBFFI_CALL:
-                opnum = rop.call_may_force_for_descr(descr)
-                resbox = self.metainterp.direct_libffi_call(allboxes, descr,
-                                                            tp)
-            if resbox is NOT_HANDLED:
-                if effectinfo.is_call_release_gil():
-                    opnum = rop.call_release_gil_for_descr(descr)
-                    resbox = self.metainterp.direct_call_release_gil(allboxes,
-                                                                descr, tp)
-                elif tp == 'i':
-                    resbox = self.metainterp.execute_and_record_varargs(
-                        rop.CALL_MAY_FORCE_I, allboxes, descr=descr)
-                elif tp == 'r':
-                    resbox = self.metainterp.execute_and_record_varargs(
-                        rop.CALL_MAY_FORCE_R, allboxes, descr=descr)
-                elif tp == 'f':
-                    resbox = self.metainterp.execute_and_record_varargs(
-                        rop.CALL_MAY_FORCE_F, allboxes, descr=descr)
-                elif tp == 'v':
-                    self.metainterp.execute_and_record_varargs(
-                        rop.CALL_MAY_FORCE_N, allboxes, descr=descr)
-                    resbox = None
-                else:
-                    assert False
-                if opnum == -1:
-                    opnum = rop.call_may_force_for_descr(descr)
-            cut_pos = self.metainterp.vrefs_after_residual_call(
-                self.metainterp._last_op, opnum, allboxes, descr, cut_pos)
-            vablebox = None
+            if tp == 'i':
+                opnum1 = rop.CALL_MAY_FORCE_I
+                value = executor.execute_varargs(metainterp.cpu, metainterp,
+                                                 opnum1, allboxes, descr)
+                c_result = ConstInt(value)
+            elif tp == 'r':
+                opnum1 = rop.CALL_MAY_FORCE_R
+                value = executor.execute_varargs(metainterp.cpu, metainterp,
+                                                 opnum1, allboxes, descr)
+                c_result = ConstPtr(value)
+            elif tp == 'f':
+                opnum1 = rop.CALL_MAY_FORCE_F
+                value = executor.execute_varargs(metainterp.cpu, metainterp,
+                                                 opnum1, allboxes, descr)
+                c_result = ConstFloat(value)
+            elif tp == 'v':
+                opnum1 = rop.CALL_MAY_FORCE_N
+                executor.execute_varargs(metainterp.cpu, metainterp,
+                                         opnum1, allboxes, descr)
+                c_result = None
+            else:
+                assert False
+
+            # 3. after this call, check the vrefs.  If any have been
+            #    forced by the call, then we record in the trace a
+            #    VIRTUAL_REF_FINISH---before we record any CALL
+            self.metainterp.vrefs_after_residual_call()
+
+            # 4. figure out what kind of CALL we need to record
+            #    from the effectinfo and the 'assembler_call' flag
             if assembler_call:
                 vablebox, resbox = self.metainterp.direct_assembler_call(
-                    self.metainterp._last_op, allboxes, descr, assembler_call_jd, cut_pos)
-            if resbox and resbox.type != 'v':
+                    allboxes, descr, assembler_call_jd)
+            else:
+                vablebox = None
+                resbox = None
+                if effectinfo.oopspecindex == effectinfo.OS_LIBFFI_CALL:
+                    resbox = self.metainterp.direct_libffi_call(allboxes, descr)
+                    # ^^^ may return None to mean "can't handle it myself"
+                if resbox is None:
+                    if effectinfo.is_call_release_gil():
+                        resbox = self.metainterp.direct_call_release_gil(
+                            allboxes, descr)
+                    else:
+                        resbox = self.metainterp.direct_call_may_force(
+                            allboxes, descr)
+
+            # 5. invalidate the heapcache based on the CALL_MAY_FORCE
+            #    operation executed above in step 2
+            self.metainterp.heapcache.invalidate_caches(opnum1, descr, allboxes)
+
+            # 6. put 'c_result' back into the recorded operation
+            if resbox.type == 'v':
+                resbox = None    # for void calls, must return None below
+            else:
+                resbox.copy_value_from(c_result)
                 self.make_result_of_lastop(resbox)
             self.metainterp.vable_after_residual_call(funcbox)
             self.metainterp.generate_guard(rop.GUARD_NOT_FORCED, None)
@@ -2170,7 +2197,6 @@
         profiler.count_ops(opnum, Counters.RECORDED_OPS)
         self.heapcache.invalidate_caches(opnum, descr, argboxes)
         op = self.history.record(opnum, argboxes, resvalue, descr)
-        self._last_op = op
         self.attach_debug_info(op)
         if op.type != 'v':
             return op
@@ -2781,7 +2807,7 @@
                                                   force_token],
                                 None, descr=vinfo.vable_token_descr)
 
-    def vrefs_after_residual_call(self, op, opnum, arglist, descr, cut_pos):
+    def vrefs_after_residual_call(self):
         vrefinfo = self.staticdata.virtualref_info
         for i in range(0, len(self.virtualref_boxes), 2):
             vrefbox = self.virtualref_boxes[i+1]
@@ -2791,9 +2817,7 @@
                 # during this CALL_MAY_FORCE.  Mark this fact by
                 # generating a VIRTUAL_REF_FINISH on it and replacing
                 # it by ConstPtr(NULL).
-                cut_pos = self.stop_tracking_virtualref(i, op, opnum, arglist,
-                                                        descr, cut_pos)
-        return cut_pos
+                self.stop_tracking_virtualref(i)
 
     def vable_after_residual_call(self, funcbox):
         vinfo = self.jitdriver_sd.virtualizable_info
@@ -2817,19 +2841,14 @@
                 # have the eventual exception raised (this is normally done
                 # after the call to vable_after_residual_call()).
 
-    def stop_tracking_virtualref(self, i, op, opnum, arglist, descr, cut_pos):
+    def stop_tracking_virtualref(self, i):
         virtualbox = self.virtualref_boxes[i]
         vrefbox = self.virtualref_boxes[i+1]
-        # record VIRTUAL_REF_FINISH just before the current CALL_MAY_FORCE
-        self.history.cut(cut_pos) # pop the CALL
-        self.history.record_nospec(rop.VIRTUAL_REF_FINISH,
-                            [vrefbox, virtualbox], None)
-        cut_pos = self.history.get_trace_position()
-        newop = self.history.record_nospec(opnum, arglist, descr)
-        op.set_position(newop.get_position())
-        # mark by replacing it with ConstPtr(NULL)
+        # record VIRTUAL_REF_FINISH here, which is before the actual
+        # CALL_xxx is recorded
+        self.history.record(rop.VIRTUAL_REF_FINISH, [vrefbox, virtualbox], None)
+        # mark this situation by replacing the vrefbox with ConstPtr(NULL)
         self.virtualref_boxes[i+1] = self.cpu.ts.CONST_NULL
-        return cut_pos
 
     def handle_possible_exception(self):
         if self.last_exc_value:
@@ -3026,24 +3045,26 @@
         newop.copy_value_from(op)
         return newop
 
-    def direct_assembler_call(self, op, arglist, descr, targetjitdriver_sd, cut_pos):
-        """ Generate a direct call to assembler for portal entry point,
-        patching the CALL_MAY_FORCE that occurred just now.
+    def direct_call_may_force(self, argboxes, calldescr):
+        """ Common case: record in the history a CALL_MAY_FORCE with
+        'c_result' as the result of that call.  (The actual call has
+        already been done.)
         """
-        self.history.cut(cut_pos)
+        opnum = rop.call_may_force_for_descr(calldescr)
+        return self.history.record_nospec(opnum, argboxes, calldescr)
+
+    def direct_assembler_call(self, arglist, calldescr, targetjitdriver_sd):
+        """ Record in the history a direct call to assembler for portal
+        entry point.
+        """
         num_green_args = targetjitdriver_sd.num_green_args
         greenargs = arglist[1:num_green_args+1]
         args = arglist[num_green_args+1:]
         assert len(args) == targetjitdriver_sd.num_red_args
         warmrunnerstate = targetjitdriver_sd.warmstate
         token = warmrunnerstate.get_assembler_token(greenargs)
-        opnum = OpHelpers.call_assembler_for_descr(descr)
-        oldop = op
+        opnum = OpHelpers.call_assembler_for_descr(calldescr)
         op = self.history.record_nospec(opnum, args, descr=token)
-        if opnum == rop.CALL_ASSEMBLER_N:
-            op = None
-        else:
-            op.copy_value_from(oldop)
         #
         # To fix an obscure issue, make sure the vable stays alive
         # longer than the CALL_ASSEMBLER operation.  We do it by
@@ -3054,7 +3075,7 @@
         else:
             return None, op
 
-    def direct_libffi_call(self, argboxes, orig_calldescr, tp):
+    def direct_libffi_call(self, argboxes, orig_calldescr):
         """Generate a direct call to C code using jit_ffi_call()
         """
         # an 'assert' that constant-folds away the rest of this function
@@ -3067,7 +3088,7 @@
         #
         box_cif_description = argboxes[1]
         if not isinstance(box_cif_description, ConstInt):
-            return NOT_HANDLED
+            return None     # cannot be handled by direct_libffi_call()
         cif_description = box_cif_description.getint()
         cif_description = llmemory.cast_int_to_adr(cif_description)
         cif_description = llmemory.cast_adr_to_ptr(cif_description,
@@ -3075,7 +3096,7 @@
         extrainfo = orig_calldescr.get_extra_info()
         calldescr = self.cpu.calldescrof_dynamic(cif_description, extrainfo)
         if calldescr is None:
-            return NOT_HANDLED
+            return None     # cannot be handled by direct_libffi_call()
         #
         box_exchange_buffer = argboxes[3]
         arg_boxes = []
@@ -3106,68 +3127,25 @@
         # (that is, errno and SetLastError/GetLastError on Windows)
         # Note these flags match the ones in clibffi.ll_callback
         c_saveall = ConstInt(rffi.RFFI_ERR_ALL | rffi.RFFI_ALT_ERRNO)
-        if tp == 'i':
-            value = executor.execute_varargs(self.cpu, self,
-                                             rop.CALL_MAY_FORCE_I,
-                                             argboxes, orig_calldescr)
-            box_result = self.history.record(
-                rop.CALL_RELEASE_GIL_I, [c_saveall, argboxes[2]] + arg_boxes,
-                value, descr=calldescr)
-        elif tp == 'f':
-            value = executor.execute_varargs(self.cpu, self,
-                                             rop.CALL_MAY_FORCE_F,
-                                             argboxes, orig_calldescr)
-            box_result = self.history.record(
-                rop.CALL_RELEASE_GIL_F, [c_saveall, argboxes[2]] + arg_boxes,
-                value, descr=calldescr)
-        elif tp == 'v':
-            executor.execute_varargs(self.cpu, self,
-                                             rop.CALL_MAY_FORCE_N,
-                                             argboxes, orig_calldescr)
-            self.history.record(
-                rop.CALL_RELEASE_GIL_N, [c_saveall, argboxes[2]] + arg_boxes,
-                None, descr=calldescr)
-            box_result = None
-        else:
-            assert False
-        #
+        opnum = rop.call_release_gil_for_descr(orig_calldescr)
+        assert opnum == rop.call_release_gil_for_descr(calldescr)
+        return self.history.record_nospec(opnum,
+                                          [c_saveall, argboxes[2]] + arg_boxes,
+                                          calldescr)
         # note that the result is written back to the exchange_buffer by the
         # following operation, which should be a raw_store
-        return box_result
-
-    def direct_call_release_gil(self, argboxes, calldescr, tp):
+
+    def direct_call_release_gil(self, argboxes, calldescr):
+        if not we_are_translated():       # for llgraph
+            calldescr._original_func_ = argboxes[0].getint()
         effectinfo = calldescr.get_extra_info()
         realfuncaddr, saveerr = effectinfo.call_release_gil_target
         funcbox = ConstInt(heaptracker.adr2int(realfuncaddr))
         savebox = ConstInt(saveerr)
-        if tp == 'i':
-            value = executor.execute_varargs(self.cpu, self,
-                                             rop.CALL_MAY_FORCE_I,
-                                             argboxes, calldescr)
-            resbox = self.history.record(rop.CALL_RELEASE_GIL_I,
-                                         [savebox, funcbox] + argboxes[1:],
-                                         value, calldescr)
-        elif tp == 'f':
-            value = executor.execute_varargs(self.cpu, self,
-                                             rop.CALL_MAY_FORCE_F,
-                                             argboxes, calldescr)
-            resbox = self.history.record(rop.CALL_RELEASE_GIL_F,
-                                         [savebox, funcbox] + argboxes[1:],
-                                         value, calldescr)
-        elif tp == 'v':
-            executor.execute_varargs(self.cpu, self,
-                                             rop.CALL_MAY_FORCE_N,
-                                             argboxes, calldescr)
-            self.history.record(rop.CALL_RELEASE_GIL_N,
-                                         [savebox, funcbox] + argboxes[1:],
-                                         None, calldescr)
-            resbox = None
-        else:
-            assert False, "no CALL_RELEASE_GIL_R"
-            
-        if not we_are_translated():       # for llgraph
-            calldescr._original_func_ = argboxes[0].getint()
-        return resbox
+        opnum = rop.call_release_gil_for_descr(calldescr)
+        return self.history.record_nospec(opnum,
+                                          [savebox, funcbox] + argboxes[1:],
+                                          calldescr)
 
     def do_not_in_trace_call(self, allboxes, descr):
         self.clear_exception()
@@ -3187,8 +3165,6 @@
     """Raised after we mutated metainterp.framestack, in order to force
     it to reload the current top-of-stack frame that gets interpreted."""
 
-NOT_HANDLED = history.CONST_FALSE
-
 # ____________________________________________________________
 
 def _get_opimpl_method(name, argcodes):
diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py
--- a/rpython/rlib/rposix.py
+++ b/rpython/rlib/rposix.py
@@ -22,21 +22,6 @@
     from rpython.rlib import rwin32
     from rpython.rlib.rwin32file import make_win32_traits
 
-class CConfig:
-    _compilation_info_ = ExternalCompilationInfo(
-        includes=['sys/stat.h',
-                  'unistd.h',
-                  'fcntl.h'],
-    )
-    for _name in """fchdir fchmod fchmodat fchown fchownat fexecve fdopendir
-                    fpathconf fstat fstatat fstatvfs ftruncate futimens futimes
-                    futimesat linkat lchflags lchmod lchown lstat lutimes
-                    mkdirat mkfifoat mknodat openat readlinkat renameat
-                    symlinkat unlinkat utimensat""".split():
-        locals()['HAVE_%s' % _name.upper()] = rffi_platform.Has(_name)
-cConfig = rffi_platform.configure(CConfig)
-globals().update(cConfig)
-
 
 class CConstantErrno(CConstant):
     # these accessors are used when calling get_errno() or set_errno()
@@ -618,14 +603,44 @@
     config = rffi_platform.configure(CConfig)
     DIRENT = config['DIRENT']
     DIRENTP = lltype.Ptr(DIRENT)
-    c_opendir = external('opendir', [rffi.CCHARP], DIRP,
-                         save_err=rffi.RFFI_SAVE_ERRNO)
+    c_opendir = external('opendir',
+        [rffi.CCHARP], DIRP, save_err=rffi.RFFI_SAVE_ERRNO)
+    c_fdopendir = external('fdopendir',
+        [rffi.INT], DIRP, save_err=rffi.RFFI_SAVE_ERRNO)
     # XXX macro=True is hack to make sure we get the correct kind of
     # dirent struct (which depends on defines)
     c_readdir = external('readdir', [DIRP], DIRENTP,
                          macro=True, save_err=rffi.RFFI_FULL_ERRNO_ZERO)
     c_closedir = external('closedir', [DIRP], rffi.INT)
 
+def _listdir(dirp):
+    result = []
+    while True:
+        direntp = c_readdir(dirp)
+        if not direntp:
+            error = get_saved_errno()
+            break
+        namep = rffi.cast(rffi.CCHARP, direntp.c_d_name)
+        name = rffi.charp2str(namep)
+        if name != '.' and name != '..':
+            result.append(name)
+    c_closedir(dirp)
+    if error:
+        raise OSError(error, "readdir failed")
+    return result
+
+def fdlistdir(dirfd):
+    """
+    Like listdir(), except that the directory is specified as an open
+    file descriptor.
+
+    Note: fdlistdir() closes the file descriptor.
+    """
+    dirp = c_fdopendir(dirfd)
+    if not dirp:
+        raise OSError(get_saved_errno(), "opendir failed")
+    return _listdir(dirp)
+
 @replace_os_function('listdir')
 @specialize.argtype(0)
 def listdir(path):
@@ -634,20 +649,7 @@
         dirp = c_opendir(path)
         if not dirp:
             raise OSError(get_saved_errno(), "opendir failed")
-        result = []
-        while True:
-            direntp = c_readdir(dirp)
-            if not direntp:
-                error = get_saved_errno()
-                break
-            namep = rffi.cast(rffi.CCHARP, direntp.c_d_name)
-            name = rffi.charp2str(namep)
-            if name != '.' and name != '..':
-                result.append(name)
-        c_closedir(dirp)
-        if error:
-            raise OSError(error, "readdir failed")
-        return result
+        return _listdir(dirp)
     else:  # _WIN32 case
         traits = _preferred_traits(path)
         win32traits = make_win32_traits(traits)
@@ -1739,3 +1741,259 @@
     def getcontroller(self):
         from rpython.rlib.rposix_environ import OsEnvironController
         return OsEnvironController()
+
+
+# ____________________________________________________________
+# Support for f... and ...at families of POSIX functions
+
+class CConfig:
+    _compilation_info_ = ExternalCompilationInfo(
+        includes=['sys/stat.h',
+                  'unistd.h',
+                  'fcntl.h'],
+    )
+    for _name in """faccessat fchdir fchmod fchmodat fchown fchownat fexecve
+            fdopendir fpathconf fstat fstatat fstatvfs ftruncate
+            futimens futimes futimesat linkat chflags lchflags lchmod lchown
+            lstat lutimes mkdirat mkfifoat mknodat openat readlinkat renameat
+            symlinkat unlinkat utimensat""".split():
+        locals()['HAVE_%s' % _name.upper()] = rffi_platform.Has(_name)
+cConfig = rffi_platform.configure(CConfig)
+globals().update(cConfig)
+
+if not _WIN32:
+    class CConfig:
+        _compilation_info_ = ExternalCompilationInfo(
+            includes=['sys/stat.h',
+                    'unistd.h',
+                    'fcntl.h'],
+        )
+        AT_FDCWD = rffi_platform.DefinedConstantInteger('AT_FDCWD')
+        AT_SYMLINK_NOFOLLOW = rffi_platform.DefinedConstantInteger('AT_SYMLINK_NOFOLLOW')
+        AT_EACCESS = rffi_platform.DefinedConstantInteger('AT_EACCESS')
+        AT_REMOVEDIR = rffi_platform.DefinedConstantInteger('AT_REMOVEDIR')
+        AT_EMPTY_PATH = rffi_platform.DefinedConstantInteger('AT_EMPTY_PATH')
+        UTIME_NOW = rffi_platform.DefinedConstantInteger('UTIME_NOW')
+        UTIME_OMIT = rffi_platform.DefinedConstantInteger('UTIME_OMIT')
+        TIMESPEC = rffi_platform.Struct('struct timespec', [
+            ('tv_sec', rffi.TIME_T),
+            ('tv_nsec', rffi.LONG)])
+
+    cConfig = rffi_platform.configure(CConfig)
+    globals().update(cConfig)
+    TIMESPEC2P = rffi.CArrayPtr(TIMESPEC)
+
+if HAVE_FACCESSAT:
+    c_faccessat = external('faccessat',
+        [rffi.INT, rffi.CCHARP, rffi.INT, rffi.INT], rffi.INT)
+
+    def faccessat(pathname, mode, dir_fd=AT_FDCWD,
+            effective_ids=False, follow_symlinks=True):
+        """Thin wrapper around faccessat(2) with an interface simlar to
+        Python3's os.access().
+        """
+        flags = 0
+        if not follow_symlinks:
+            flags |= AT_SYMLINK_NOFOLLOW
+        if effective_ids:
+            flags |= AT_EACCESS
+        error = c_faccessat(dir_fd, pathname, mode, flags)
+        return error == 0
+
+if HAVE_FCHMODAT:
+    c_fchmodat = external('fchmodat',
+        [rffi.INT, rffi.CCHARP, rffi.INT, rffi.INT], rffi.INT,
+        save_err=rffi.RFFI_SAVE_ERRNO,)
+
+    def fchmodat(path, mode, dir_fd=AT_FDCWD, follow_symlinks=True):
+        if follow_symlinks:
+            flag = 0
+        else:
+            flag = AT_SYMLINK_NOFOLLOW
+        error = c_fchmodat(dir_fd, path, mode, flag)
+        handle_posix_error('fchmodat', error)
+
+if HAVE_FCHOWNAT:
+    c_fchownat = external('fchownat',
+        [rffi.INT, rffi.CCHARP, rffi.INT, rffi.INT, rffi.INT], rffi.INT,
+        save_err=rffi.RFFI_SAVE_ERRNO,)
+
+    def fchownat(path, owner, group, dir_fd=AT_FDCWD,
+            follow_symlinks=True, empty_path=False):
+        flag = 0
+        if not follow_symlinks:
+            flag |= AT_SYMLINK_NOFOLLOW
+        if empty_path:
+            flag |= AT_EMPTY_PATH
+        error = c_fchownat(dir_fd, path, owner, group, flag)
+        handle_posix_error('fchownat', error)
+
+if HAVE_FEXECVE:
+    c_fexecve = external('fexecve',
+        [rffi.INT, rffi.CCHARPP, rffi.CCHARPP], rffi.INT,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+
+    def fexecve(fd, args, env):
+        envstrs = []
+        for item in env.iteritems():
+            envstr = "%s=%s" % item
+            envstrs.append(envstr)
+
+        # This list conversion already takes care of NUL bytes.
+        l_args = rffi.ll_liststr2charpp(args)
+        l_env = rffi.ll_liststr2charpp(envstrs)
+        c_fexecve(fd, l_args, l_env)
+
+        rffi.free_charpp(l_env)
+        rffi.free_charpp(l_args)
+        raise OSError(get_saved_errno(), "execve failed")
+
+if HAVE_LINKAT:
+    c_linkat = external('linkat',
+        [rffi.INT, rffi.CCHARP, rffi.INT, rffi.CCHARP, rffi.INT], rffi.INT)
+
+    def linkat(src, dst, src_dir_fd=AT_FDCWD, dst_dir_fd=AT_FDCWD,
+            follow_symlinks=True):
+        """Thin wrapper around linkat(2) with an interface similar to
+        Python3's os.link()
+        """
+        if follow_symlinks:
+            flag = 0
+        else:
+            flag = AT_SYMLINK_NOFOLLOW
+        error = c_linkat(src_dir_fd, src, dst_dir_fd, dst, flag)
+        handle_posix_error('linkat', error)
+
+if HAVE_FUTIMENS:
+    c_futimens = external('futimens', [rffi.INT, TIMESPEC2P], rffi.INT)
+
+    def futimens(fd, atime, atime_ns, mtime, mtime_ns):
+        l_times = lltype.malloc(TIMESPEC2P.TO, 2, flavor='raw')
+        rffi.setintfield(l_times[0], 'c_tv_sec', atime)
+        rffi.setintfield(l_times[0], 'c_tv_nsec', atime_ns)
+        rffi.setintfield(l_times[1], 'c_tv_sec', mtime)
+        rffi.setintfield(l_times[1], 'c_tv_nsec', mtime_ns)
+        error = c_futimens(fd, l_times)
+        lltype.free(l_times, flavor='raw')
+        handle_posix_error('futimens', error)
+
+if HAVE_UTIMENSAT:
+    c_utimensat = external('utimensat',
+        [rffi.INT, rffi.CCHARP, TIMESPEC2P, rffi.INT], rffi.INT)
+
+    def utimensat(pathname, atime, atime_ns, mtime, mtime_ns,
+            dir_fd=AT_FDCWD, follow_symlinks=True):
+        """Wrapper around utimensat(2)
+
+        To set access time to the current time, pass atime_ns=UTIME_NOW,
+        atime is then ignored.
+
+        To set modification time to the current time, pass mtime_ns=UTIME_NOW,
+        mtime is then ignored.
+        """
+        l_times = lltype.malloc(TIMESPEC2P.TO, 2, flavor='raw')
+        rffi.setintfield(l_times[0], 'c_tv_sec', atime)
+        rffi.setintfield(l_times[0], 'c_tv_nsec', atime_ns)
+        rffi.setintfield(l_times[1], 'c_tv_sec', mtime)
+        rffi.setintfield(l_times[1], 'c_tv_nsec', mtime_ns)
+        if follow_symlinks:
+            flag = 0
+        else:
+            flag = AT_SYMLINK_NOFOLLOW
+        error = c_utimensat(dir_fd, pathname, l_times, flag)
+        lltype.free(l_times, flavor='raw')
+        handle_posix_error('utimensat', error)
+
+if HAVE_MKDIRAT:
+    c_mkdirat = external('mkdirat',
+        [rffi.INT, rffi.CCHARP, rffi.INT], rffi.INT,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+
+    def mkdirat(pathname, mode, dir_fd=AT_FDCWD):
+        error = c_mkdirat(dir_fd, pathname, mode)
+        handle_posix_error('mkdirat', error)
+
+if HAVE_UNLINKAT:
+    c_unlinkat = external('unlinkat',
+        [rffi.INT, rffi.CCHARP, rffi.INT], rffi.INT,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+
+    def unlinkat(pathname, dir_fd=AT_FDCWD, removedir=False):
+        flag = AT_REMOVEDIR if removedir else 0
+        error = c_unlinkat(dir_fd, pathname, flag)
+        handle_posix_error('unlinkat', error)
+
+if HAVE_READLINKAT:
+    c_readlinkat = external(
+        'readlinkat',
+        [rffi.INT, rffi.CCHARP, rffi.CCHARP, rffi.SIZE_T], rffi.SSIZE_T,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+
+    def readlinkat(pathname, dir_fd=AT_FDCWD):
+        pathname = _as_bytes0(pathname)
+        bufsize = 1023
+        while True:
+            buf = lltype.malloc(rffi.CCHARP.TO, bufsize, flavor='raw')
+            res = widen(c_readlinkat(dir_fd, pathname, buf, bufsize))
+            if res < 0:
+                lltype.free(buf, flavor='raw')
+                error = get_saved_errno()    # failed
+                raise OSError(error, "readlinkat failed")
+            elif res < bufsize:
+                break                       # ok
+            else:
+                # buf too small, try again with a larger buffer
+                lltype.free(buf, flavor='raw')
+                bufsize *= 4
+        # convert the result to a string
+        result = rffi.charp2strn(buf, res)
+        lltype.free(buf, flavor='raw')
+        return result
+
+if HAVE_RENAMEAT:
+    c_renameat = external(
+        'renameat',
+        [rffi.INT, rffi.CCHARP, rffi.INT, rffi.CCHARP], rffi.INT,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+
+    def renameat(src, dst, src_dir_fd=AT_FDCWD, dst_dir_fd=AT_FDCWD):
+        error = c_renameat(src_dir_fd, src, dst_dir_fd, dst)
+        handle_posix_error('renameat', error)
+
+
+if HAVE_SYMLINKAT:
+    c_symlinkat = external('symlinkat',
+        [rffi.CCHARP, rffi.INT, rffi.CCHARP], rffi.INT,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+
+    def symlinkat(src, dst, dir_fd=AT_FDCWD):
+        error = c_symlinkat(src, dir_fd, dst)
+        handle_posix_error('symlinkat', error)
+
+if HAVE_OPENAT:
+    c_openat = external('openat',
+        [rffi.INT, rffi.CCHARP, rffi.INT, rffi.MODE_T], rffi.INT,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+
+    @enforceargs(s_Str0, int, int, int, typecheck=False)
+    def openat(path, flags, mode, dir_fd=AT_FDCWD):
+        fd = c_openat(dir_fd, path, flags, mode)
+        return handle_posix_error('open', fd)
+
+if HAVE_MKFIFOAT:
+    c_mkfifoat = external('mkfifoat',
+        [rffi.INT, rffi.CCHARP, rffi.MODE_T], rffi.INT,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+
+    def mkfifoat(path, mode, dir_fd=AT_FDCWD):
+        error = c_mkfifoat(dir_fd, path, mode)
+        handle_posix_error('mkfifoat', error)
+
+if HAVE_MKNODAT:
+    c_mknodat = external('mknodat',
+        [rffi.INT, rffi.CCHARP, rffi.MODE_T, rffi.INT], rffi.INT,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+
+    def mknodat(path, mode, device, dir_fd=AT_FDCWD):
+        error = c_mknodat(dir_fd, path, mode, device)
+        handle_posix_error('mknodat', error)
diff --git a/rpython/rlib/rvmprof/cintf.py b/rpython/rlib/rvmprof/cintf.py
--- a/rpython/rlib/rvmprof/cintf.py
+++ b/rpython/rlib/rvmprof/cintf.py
@@ -1,3 +1,4 @@
+import platform as host_platform
 import py
 import sys
 from rpython.tool.udir import udir
@@ -28,8 +29,7 @@
 
 
 def setup():
-    from rpython.jit.backend import detect_cpu
-    if detect_cpu.autodetect().startswith(detect_cpu.MODEL_S390_64):
+    if host_platform.machine() == 's390x':
         raise VMProfPlatformUnsupported("rvmprof not supported on"
                                         " s390x CPUs for now")
     compile_extra = ['-DRPYTHON_LL2CTYPES']
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
@@ -7,6 +7,12 @@
 import errno
 import py
 
+def rposix_requires(funcname):
+    return py.test.mark.skipif(not hasattr(rposix, funcname),
+        reason="Requires rposix.%s()" % funcname)
+
+win_only = py.test.mark.skipif("os.name != 'nt'")
+
 class TestPosixFunction:
     def test_access(self):
         filename = str(udir.join('test_access.txt'))
@@ -29,9 +35,8 @@
         for value in times:
             assert isinstance(value, float)
 
+    @py.test.mark.skipif("not hasattr(os, 'getlogin')")
     def test_getlogin(self):
-        if not hasattr(os, 'getlogin'):
-            py.test.skip('posix specific function')
         try:
             expected = os.getlogin()
         except OSError, e:
@@ -39,9 +44,8 @@
         data = rposix.getlogin()
         assert data == expected
 
+    @win_only
     def test_utimes(self):
-        if os.name != 'nt':
-            py.test.skip('Windows specific feature')
         # Windows support centiseconds
         def f(fname, t1):
             os.utime(fname, (t1, t1))
@@ -51,15 +55,12 @@
         t1 = 1159195039.25
         compile(f, (str, float))(str(fname), t1)
         assert t1 == os.stat(str(fname)).st_mtime
-        if sys.version_info < (2, 7):
-            py.test.skip('requires Python 2.7')
         t1 = 5000000000.0
         compile(f, (str, float))(str(fname), t1)
         assert t1 == os.stat(str(fname)).st_mtime
 
+    @win_only
     def test__getfullpathname(self):
-        if os.name != 'nt':
-            py.test.skip('nt specific function')
         posix = __import__(os.name)
         sysdrv = os.getenv('SystemDrive', 'C:')
         stuff = sysdrv + 'stuff'
@@ -99,11 +100,25 @@
     def test_mkdir(self):
         filename = str(udir.join('test_mkdir.dir'))
         rposix.mkdir(filename, 0)
-        exc = py.test.raises(OSError, rposix.mkdir, filename, 0)
-        assert exc.value.errno == errno.EEXIST
+        with py.test.raises(OSError) as excinfo:
+            rposix.mkdir(filename, 0)
+        assert excinfo.value.errno == errno.EEXIST
         if sys.platform == 'win32':
             assert exc.type is WindowsError
 
+    @rposix_requires('mkdirat')
+    def test_mkdirat(self):
+        relpath = 'test_mkdirat.dir'
+        filename = str(udir.join(relpath))
+        dirfd = os.open(os.path.dirname(filename), os.O_RDONLY)
+        try:
+            rposix.mkdirat(relpath, 0, dir_fd=dirfd)
+            with py.test.raises(OSError) as excinfo:
+                rposix.mkdirat(relpath, 0, dir_fd=dirfd)
+            assert excinfo.value.errno == errno.EEXIST
+        finally:
+            os.close(dirfd)
+
     def test_strerror(self):
         assert rposix.strerror(2) == os.strerror(2)
 
@@ -116,10 +131,8 @@
         os.unlink(filename)
 
 
+    @py.test.mark.skipif("os.name != 'posix'")
     def test_execve(self):
-        if os.name != 'posix':
-            py.test.skip('posix specific function')
-
         EXECVE_ENV = {"foo": "bar", "baz": "quux"}
 
         def run_execve(program, args=None, env=None, do_path_lookup=False):
@@ -258,11 +271,8 @@
         assert rposix.isatty(-1) is False
 
 
+ at py.test.mark.skipif("not hasattr(os, 'ttyname')")
 class TestOsExpect(ExpectTest):
-    def setup_class(cls):
-        if not hasattr(os, 'ttyname'):
-            py.test.skip("no ttyname")
-
     def test_ttyname(self):
         def f():
             import os
@@ -426,9 +436,8 @@
             except Exception:
                 pass
 
+    @win_only
     def test_is_valid_fd(self):
-        if os.name != 'nt':
-            py.test.skip('relevant for windows only')
         assert rposix.is_valid_fd(0) == 1
         fid = open(str(udir.join('validate_test.txt')), 'w')
         fd = fid.fileno()
@@ -448,6 +457,59 @@
     def _get_filename(self):
         return str(udir.join('test_open_ascii'))
 
+    @rposix_requires('openat')
+    def test_openat(self):
+        def f(dirfd):
+            try:
+                fd = rposix.openat('test_open_ascii', os.O_RDONLY, 0777, dirfd)
+                try:
+                    text = os.read(fd, 50)
+                    return text
+                finally:
+                    os.close(fd)
+            except OSError:
+                return ''
+
+        dirfd = os.open(os.path.dirname(self.ufilename), os.O_RDONLY)
+        try:
+            assert ll_to_string(interpret(f, [dirfd])) == "test"
+        finally:
+            os.close(dirfd)
+
+    @rposix_requires('unlinkat')
+    def test_unlinkat(self):
+        def f(dirfd):
+            return rposix.unlinkat('test_open_ascii', dir_fd=dirfd)
+
+        dirfd = os.open(os.path.dirname(self.ufilename), os.O_RDONLY)
+        try:
+            interpret(f, [dirfd])
+        finally:
+            os.close(dirfd)
+        assert not os.path.exists(self.ufilename)
+
+    def test_utimensat(self):
+        def f(dirfd):
+            return rposix.utimensat('test_open_ascii',
+                0, rposix.UTIME_NOW, 0, rposix.UTIME_NOW, dir_fd=dirfd)
+
+        dirfd = os.open(os.path.dirname(self.ufilename), os.O_RDONLY)
+        try:
+            interpret(f, [dirfd])  # does not crash
+        finally:
+            os.close(dirfd)
+
+    def test_fchmodat(self):
+        def f(dirfd):
+            return rposix.fchmodat('test_open_ascii', 0777, dirfd)
+
+        dirfd = os.open(os.path.dirname(self.ufilename), os.O_RDONLY)
+        try:
+            interpret(f, [dirfd])  # does not crash
+        finally:
+            os.close(dirfd)
+
+
 class TestPosixUnicode(BasePosixUnicodeOrAscii):
     def _get_filename(self):
         return (unicode(udir.join('test_open')) +
@@ -465,3 +527,30 @@
             os.open('/tmp/t', 0, 0)
             os.open(u'/tmp/t', 0, 0)
         compile(f, ())
+
+
+def test_fdlistdir(tmpdir):
+    tmpdir.join('file').write('text')
+    dirfd = os.open(str(tmpdir), os.O_RDONLY)
+    result = rposix.fdlistdir(dirfd)
+    # Note: fdlistdir() always closes dirfd
+    assert result == ['file']
+
+def test_symlinkat(tmpdir):
+    tmpdir.join('file').write('text')
+    dirfd = os.open(str(tmpdir), os.O_RDONLY)
+    try:
+        rposix.symlinkat('file', 'link', dir_fd=dirfd)
+        assert os.readlink(str(tmpdir.join('link'))) == 'file'
+    finally:
+        os.close(dirfd)
+
+def test_renameat(tmpdir):
+    tmpdir.join('file').write('text')
+    dirfd = os.open(str(tmpdir), os.O_RDONLY)
+    try:
+        rposix.renameat('file', 'file2', src_dir_fd=dirfd, dst_dir_fd=dirfd)
+    finally:
+        os.close(dirfd)
+    assert tmpdir.join('file').check(exists=False)
+    assert tmpdir.join('file2').check(exists=True)


More information about the pypy-commit mailing list