[pypy-commit] pypy default: Start writing an RPython interface that should allow us to portably

arigo pypy.commits at gmail.com
Sun Aug 21 14:23:18 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r86374:e139e4ad76df
Date: 2016-08-21 18:58 +0200
http://bitbucket.org/pypy/pypy/changeset/e139e4ad76df/

Log:	Start writing an RPython interface that should allow us to portably
	implement os.scandir() in py3.5

diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py
--- a/rpython/rlib/rposix.py
+++ b/rpython/rlib/rposix.py
@@ -250,6 +250,7 @@
     OFF_T_SIZE = rffi_platform.SizeOf('off_t')
 
     HAVE_UTIMES = rffi_platform.Has('utimes')
+    HAVE_D_TYPE = rffi_platform.Has('DT_UNKNOWN')
     UTIMBUF = rffi_platform.Struct('struct %sutimbuf' % UNDERSCORE_ON_WIN32,
                                    [('actime', rffi.INT),
                                     ('modtime', rffi.INT)])
@@ -603,11 +604,17 @@
     class CConfig:
         _compilation_info_ = eci
         DIRENT = rffi_platform.Struct('struct dirent',
-            [('d_name', lltype.FixedSizeArray(rffi.CHAR, 1))])
+            [('d_name', lltype.FixedSizeArray(rffi.CHAR, 1))]
+            + [('d_type', rffi.INT)] if HAVE_D_TYPE else [])
+        if HAVE_D_TYPE:
+            DT_UNKNOWN = rffi_platform.ConstantInteger('DT_UNKNOWN')
+            DT_REG     = rffi_platform.ConstantInteger('DT_REG')
+            DT_DIR     = rffi_platform.ConstantInteger('DT_DIR')
+            DT_LNK     = rffi_platform.ConstantInteger('DT_LNK')
 
     DIRP = rffi.COpaquePtr('DIR')
-    config = rffi_platform.configure(CConfig)
-    DIRENT = config['DIRENT']
+    dirent_config = rffi_platform.configure(CConfig)
+    DIRENT = dirent_config['DIRENT']
     DIRENTP = lltype.Ptr(DIRENT)
     c_opendir = external('opendir',
         [rffi.CCHARP], DIRP, save_err=rffi.RFFI_SAVE_ERRNO)
@@ -617,7 +624,9 @@
     # 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)
+    c_closedir = external('closedir', [DIRP], rffi.INT, releasegil=False)
+else:
+    dirent_config = {}
 
 def _listdir(dirp):
     result = []
diff --git a/rpython/rlib/rposix_scandir.py b/rpython/rlib/rposix_scandir.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rposix_scandir.py
@@ -0,0 +1,59 @@
+from rpython.rlib import rposix
+from rpython.rlib.objectmodel import specialize
+from rpython.rtyper.lltypesystem import rffi
+
+
+ at specialize.argtype(0)
+def opendir(path):
+    path = rposix._as_bytes0(path)
+    return opendir_bytes(path)
+
+def opendir_bytes(path):
+    dirp = rposix.c_opendir(path)
+    if not dirp:
+        raise OSError(rposix.get_saved_errno(), "opendir failed")
+    return dirp
+
+def closedir(dirp):
+    rposix.c_closedir(dirp)
+
+def nextentry(dirp):
+    """Read the next entry and returns an opaque object.
+    Use the methods has_xxx() and get_xxx() to read from that
+    opaque object.  The opaque object is valid until the next
+    time nextentry() or closedir() is called.  This may raise
+    StopIteration, or OSError.  Note that this doesn't filter
+    out the "." and ".." entries.
+    """
+    direntp = rposix.c_readdir(dirp)
+    if direntp:
+        return direntp
+    error = rposix.get_saved_errno()
+    if error:
+        raise OSError(error, "readdir failed")
+    raise StopIteration
+
+def has_name_bytes(direntp):
+    return True
+
+def get_name_bytes(direntp):
+    namep = rffi.cast(rffi.CCHARP, direntp.c_d_name)
+    return rffi.charp2str(namep)
+
+DT_UNKNOWN = rposix.dirent_config.get('DT_UNKNOWN', None)
+DT_REG = rposix.dirent_config.get('DT_REG', None)
+DT_DIR = rposix.dirent_config.get('DT_DIR', None)
+DT_LNK = rposix.dirent_config.get('DT_LNK', None)
+
+def has_type(direntp):
+    return (DT_UNKNOWN is not None and
+            rffi.getintfield(direntp, 'c_d_type') != DT_UNKNOWN)
+
+def type_is_regular(direntp):
+    return rffi.getintfield(direntp, 'c_d_type') == DT_REG
+
+def type_is_dir(direntp):
+    return rffi.getintfield(direntp, 'c_d_type') == DT_DIR
+
+def type_is_link(direntp):
+    return rffi.getintfield(direntp, 'c_d_type') == DT_LNK
diff --git a/rpython/rlib/test/test_rposix_scandir.py b/rpython/rlib/test/test_rposix_scandir.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/test/test_rposix_scandir.py
@@ -0,0 +1,22 @@
+import sys, os
+import py
+from rpython.rlib import rposix_scandir
+
+
+class TestScanDir(object):
+
+    @py.test.mark.skipif("sys.platform == 'win32'")   # XXX
+    def test_name_bytes(self):
+        scan = rposix_scandir.opendir('/')
+        found = []
+        while True:
+            try:
+                p = rposix_scandir.nextentry(scan)
+            except StopIteration:
+                break
+            assert rposix_scandir.has_name_bytes(p)
+            found.append(rposix_scandir.get_name_bytes(p))
+        rposix_scandir.closedir(scan)
+        found.remove('.')
+        found.remove('..')
+        assert sorted(found) == sorted(os.listdir('/'))


More information about the pypy-commit mailing list