Author: Armin Rigo <[email protected]>
Branch: py3.5
Changeset: r93509:4bb7cf3fd5a1
Date: 2017-12-20 09:27 +0100
http://bitbucket.org/pypy/pypy/changeset/4bb7cf3fd5a1/

Log:    In-progress: os.scandir() on Windows

diff --git a/lib-python/3/ssl.py b/lib-python/3/ssl.py
--- a/lib-python/3/ssl.py
+++ b/lib-python/3/ssl.py
@@ -140,6 +140,23 @@
 except NameError:
     _SSLv2_IF_EXISTS = None
 
+
+
+
+import os
+class DirEntry:
+    def __init__(self, path, name):
+        self.path = os.path.join(path, name)
+        self.name = name
+    def is_dir(self):
+        return os.path.isdir(self.path)
+def myscandir(path='.'):
+    for name in os.listdir(path):
+        yield DirEntry(path, name)
+os.scandir = myscandir
+
+
+
 if sys.platform == "win32":
     from _ssl import enum_certificates, enum_crls
 
diff --git a/pypy/module/posix/interp_scandir.py 
b/pypy/module/posix/interp_scandir.py
--- a/pypy/module/posix/interp_scandir.py
+++ b/pypy/module/posix/interp_scandir.py
@@ -13,28 +13,39 @@
 
 def scandir(space, w_path=None):
     "scandir(path='.') -> iterator of DirEntry objects for given path"
-    if _WIN32:
-        raise NotImplementedError("XXX WIN32")
-
     if space.is_none(w_path):
         w_path = space.newunicode(u".")
-    if space.isinstance_w(w_path, space.w_bytes):
-        path_bytes = space.bytes0_w(w_path)
-        result_is_bytes = True
+
+    if not _WIN32:
+        if space.isinstance_w(w_path, space.w_bytes):
+            path = space.bytes0_w(w_path)
+            result_is_bytes = True
+        else:
+            path = space.fsencode_w(w_path)
+            result_is_bytes = False
     else:
-        path_bytes = space.fsencode_w(w_path)
+        if space.isinstance_w(w_path, space.w_bytes):
+            raise oefmt(space.w_TypeError, "os.scandir() doesn't support bytes 
path"
+                                           " on Windows, use Unicode instead")
+        path = space.unicode_w(w_path)
         result_is_bytes = False
 
+    # 'path' is always bytes on posix and always unicode on windows
     try:
-        dirp = rposix_scandir.opendir(path_bytes)
+        dirp = rposix_scandir.opendir(path)
     except OSError as e:
         raise wrap_oserror2(space, e, w_path, eintr_retry=False)
-    path_prefix = path_bytes
-    if len(path_prefix) > 0 and path_prefix[-1] != '/':
-        path_prefix += '/'
-    w_path_prefix = space.newbytes(path_prefix)
-    if not result_is_bytes:
-        w_path_prefix = space.fsdecode(w_path_prefix)
+    path_prefix = path
+    if not _WIN32:
+        if len(path_prefix) > 0 and path_prefix[-1] != '/':
+            path_prefix += '/'
+        w_path_prefix = space.newbytes(path_prefix)
+        if not result_is_bytes:
+            w_path_prefix = space.fsdecode(w_path_prefix)
+    else:
+        if len(path_prefix) > 0 and path_prefix[-1] not in (u'\\', u'/', u':'):
+            path_prefix += u'\\'
+        w_path_prefix = space.newunicode(path_prefix)
     if rposix.HAVE_FSTATAT:
         dirfd = rposix.c_dirfd(dirp)
     else:
@@ -89,10 +100,14 @@
                                                   eintr_retry=False))
                 if not entry:
                     raise self.fail()
-                assert rposix_scandir.has_name_bytes(entry)
-                name = rposix_scandir.get_name_bytes(entry)
-                if name != '.' and name != '..':
-                    break
+                if not _WIN32:
+                    name = rposix_scandir.get_name_bytes(entry)
+                    if name != '.' and name != '..':
+                        break
+                else:
+                    name = rposix_scandir.get_name_unicode(entry)
+                    if name != u'.' and name != u'..':
+                        break
             #
             known_type = rposix_scandir.get_known_type(entry)
             inode = rposix_scandir.get_inode(entry)
@@ -113,12 +128,16 @@
 class FileNotFound(Exception):
     pass
 
-assert 0 <= rposix_scandir.DT_UNKNOWN <= 255
-assert 0 <= rposix_scandir.DT_REG <= 255
-assert 0 <= rposix_scandir.DT_DIR <= 255
-assert 0 <= rposix_scandir.DT_LNK <= 255
-FLAG_STAT  = 256
-FLAG_LSTAT = 512
+if not _WIN32:
+    assert 0 <= rposix_scandir.DT_UNKNOWN <= 255
+    assert 0 <= rposix_scandir.DT_REG <= 255
+    assert 0 <= rposix_scandir.DT_DIR <= 255
+    assert 0 <= rposix_scandir.DT_LNK <= 255
+    FLAG_STAT  = 256
+    FLAG_LSTAT = 512
+else:
+    FLAG_STAT  = 256
+    # XXX lstat and symlinks are not implemented on Windows
 
 
 class W_DirEntry(W_Root):
@@ -127,14 +146,17 @@
     def __init__(self, scandir_iterator, name, known_type, inode):
         self.space = scandir_iterator.space
         self.scandir_iterator = scandir_iterator
-        self.name = name     # always bytes on Posix
+        self.name = name     # always bytes on Posix; always unicode on Windows
         self.inode = inode
         self.flags = known_type
         assert known_type == (known_type & 255)
         #
-        w_name = self.space.newbytes(name)
-        if not scandir_iterator.result_is_bytes:
-            w_name = self.space.fsdecode(w_name)
+        if not _WIN32:
+            w_name = self.space.newbytes(name)
+            if not scandir_iterator.result_is_bytes:
+                w_name = self.space.fsdecode(w_name)
+        else:
+            w_name = self.space.newunicode(name)
         self.w_name = w_name
 
     def descr_repr(self, space):
diff --git a/pypy/module/posix/test/test_scandir.py 
b/pypy/module/posix/test/test_scandir.py
--- a/pypy/module/posix/test/test_scandir.py
+++ b/pypy/module/posix/test/test_scandir.py
@@ -31,6 +31,8 @@
 
     def setup_class(cls):
         space = cls.space
+        cls.w_WIN32 = space.wrap(sys.platform == 'win32')
+        cls.w_sep = space.wrap(os.sep)
         cls.w_posix = space.appexec([], test_posix2.GET_POSIX)
         cls.w_dir_empty = space.wrap(_make_dir('empty', {}))
         cls.w_dir0 = space.wrap(_make_dir('dir0', {'f1': 'file',
@@ -38,10 +40,11 @@
                                                    'f3': 'file'}))
         cls.w_dir1 = space.wrap(_make_dir('dir1', {'file1': 'file'}))
         cls.w_dir2 = space.wrap(_make_dir('dir2', {'subdir2': 'dir'}))
-        cls.w_dir3 = space.wrap(_make_dir('dir3', {'sfile3': 'symlink-file'}))
-        cls.w_dir4 = space.wrap(_make_dir('dir4', {'sdir4': 'symlink-dir'}))
-        cls.w_dir5 = space.wrap(_make_dir('dir5', {'sbrok5': 
'symlink-broken'}))
-        cls.w_dir6 = space.wrap(_make_dir('dir6', {'serr6': 'symlink-error'}))
+        if sys.platform != 'win32':
+            cls.w_dir3 = space.wrap(_make_dir('dir3', {'sfile3': 
'symlink-file'}))
+            cls.w_dir4 = space.wrap(_make_dir('dir4', {'sdir4': 
'symlink-dir'}))
+            cls.w_dir5 = space.wrap(_make_dir('dir5', {'sbrok5': 
'symlink-broken'}))
+            cls.w_dir6 = space.wrap(_make_dir('dir6', {'serr6': 
'symlink-error'}))
 
     def test_scandir_empty(self):
         posix = self.posix
@@ -60,27 +63,32 @@
         d = next(posix.scandir())
         assert type(d.name) is str
         assert type(d.path) is str
-        assert d.path == './' + d.name
+        assert d.path == '.' + self.sep + d.name
         d = next(posix.scandir(None))
         assert type(d.name) is str
         assert type(d.path) is str
-        assert d.path == './' + d.name
+        assert d.path == '.' + self.sep + d.name
         d = next(posix.scandir(u'.'))
         assert type(d.name) is str
         assert type(d.path) is str
-        assert d.path == './' + d.name
-        d = next(posix.scandir(b'.'))
-        assert type(d.name) is bytes
-        assert type(d.path) is bytes
-        assert d.path == b'./' + d.name
-        d = next(posix.scandir('/'))
+        assert d.path == '.' + self.sep + d.name
+        d = next(posix.scandir(self.sep))
         assert type(d.name) is str
         assert type(d.path) is str
-        assert d.path == '/' + d.name
-        d = next(posix.scandir(b'/'))
-        assert type(d.name) is bytes
-        assert type(d.path) is bytes
-        assert d.path == b'/' + d.name
+        assert d.path == self.sep + d.name
+        if not self.WIN32:
+            d = next(posix.scandir(b'.'))
+            assert type(d.name) is bytes
+            assert type(d.path) is bytes
+            assert d.path == b'./' + d.name
+            d = next(posix.scandir(b'/'))
+            assert type(d.name) is bytes
+            assert type(d.path) is bytes
+            assert d.path == b'/' + d.name
+        else:
+            raises(TypeError, posix.scandir, b'.')
+            raises(TypeError, posix.scandir, b'/')
+            raises(TypeError, posix.scandir, b'\\')
 
     def test_stat1(self):
         posix = self.posix
diff --git a/rpython/rlib/rposix_scandir.py b/rpython/rlib/rposix_scandir.py
--- a/rpython/rlib/rposix_scandir.py
+++ b/rpython/rlib/rposix_scandir.py
@@ -3,54 +3,136 @@
 from rpython.rtyper.lltypesystem import lltype, rffi
 
 
[email protected](0)
-def opendir(path):
-    path = rposix._as_bytes0(path)
-    return opendir_bytes(path)
+if not rwin32.WIN32:
+    @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 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 closedir(dirp):
+        rposix.c_closedir(dirp)
 
-if not rwin32.WIN32:
     NULL_DIRP = lltype.nullptr(rposix.DIRP.TO)
 
-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
-    OSError, or return a NULL pointer when exhausted.  Note
-    that this doesn't filter out the "." and ".." entries.
-    """
-    direntp = rposix.c_readdir(dirp)
-    if direntp:
-        error = rposix.get_saved_errno()
-        if error:
-            raise OSError(error, "readdir failed")
-    return direntp
+    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
+        OSError, or return a NULL pointer when exhausted.  Note
+        that this doesn't filter out the "." and ".." entries.
+        """
+        direntp = rposix.c_readdir(dirp)
+        if direntp:
+            error = rposix.get_saved_errno()
+            if error:
+                raise OSError(error, "readdir failed")
+        return direntp
 
-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)
 
-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', 0)
+    DT_REG = rposix.dirent_config.get('DT_REG', 255)
+    DT_DIR = rposix.dirent_config.get('DT_DIR', 255)
+    DT_LNK = rposix.dirent_config.get('DT_LNK', 255)
 
-DT_UNKNOWN = rposix.dirent_config.get('DT_UNKNOWN', 0)
-DT_REG = rposix.dirent_config.get('DT_REG', 255)
-DT_DIR = rposix.dirent_config.get('DT_DIR', 255)
-DT_LNK = rposix.dirent_config.get('DT_LNK', 255)
+    def get_known_type(direntp):
+        if rposix.HAVE_D_TYPE:
+            return rffi.getintfield(direntp, 'c_d_type')
+        return DT_UNKNOWN
 
-def get_known_type(direntp):
-    if rposix.HAVE_D_TYPE:
-        return rffi.getintfield(direntp, 'c_d_type')
-    return DT_UNKNOWN
+    def get_inode(direntp):
+        return rffi.getintfield(direntp, 'c_d_ino')
 
-def get_inode(direntp):
-    return rffi.getintfield(direntp, 'c_d_ino')
+else:
+    # ----- Win32 version -----
+    from rpython.rlib._os_support import unicode_traits
+    from rpython.rlib.rwin32file import make_win32_traits
+
+    win32traits = make_win32_traits(unicode_traits)
+
+
+    class DirP:
+        def __init__(self):
+            self.filedata = lltype.malloc(win32traits.WIN32_FIND_DATA, 
flavor='raw')
+            self.hFindFile = rwin32.INVALID_HANDLE_VALUE
+
+        def close(self):
+            lltype.free(self.filedata, flavor='raw')
+            if self.hFindFile != rwin32.INVALID_HANDLE_VALUE:
+                win32traits.FindClose(self.hFindFile)
+
+    class DirEntP:
+        def __init__(self, filedata):
+            self.filedata = filedata
+            # ^^^ note that this structure is overwritten by the next() call, 
so
+            # we must copy a few pieces of information out of it now:
+            self.dwFileAttributes = filedata.c_dwFileAttributes
+            self.CreationTimeLow = filedata.c_ftCreationTime.c_dwLowDateTime
+            self.CreationTimeHigh = filedata.c_ftCreationTime.c_dwHighDateTime
+            self.LastAccessTimeLow = 
filedata.c_ftLastAccessTime.c_dwLowDateTime
+            self.LastAccessTimeHigh = 
filedata.c_ftLastAccessTime.c_dwHighDateTime
+            self.LastWriteTimeLow = filedata.c_ftLastWriteTime.c_dwLowDateTime
+            self.LastWriteTimeHigh = 
filedata.c_ftLastWriteTime.c_dwHighDateTime
+            self.nFileSizeHigh = filedata.c_nFileSizeHigh
+            self.nFileSizeLow = filedata.c_nFileSizeLow
+
+
+    # must only be called with unicode!
+    def opendir(path):
+        if len(path) == 0:
+            path = u'.'
+        if path[-1] not in (u'\\', u'/', u':'):
+            path += u'\\'
+        mask = path + u'*.*'
+        dirp = DirP()
+        hFindFile = win32traits.FindFirstFile(mask, dirp.filedata)
+        if hFindFile == rwin32.INVALID_HANDLE_VALUE:
+            error = rwin32.GetLastError_saved()
+            dirp.close()
+            raise WindowsError(error,  "FindFirstFileW failed")
+        dirp.hFindFile = hFindFile
+        dirp.first_time = True
+        return dirp
+
+    def closedir(dirp):
+        dirp.close()
+
+    NULL_DIRP = None
+
+    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
+        WindowsError, or return None when exhausted.  Note
+        that this doesn't filter out the "." and ".." entries.
+        """
+        if dirp.first_time:
+            dirp.first_time = False
+        else:
+            if not win32traits.FindNextFile(dirp.hFindFile, dirp.filedata):
+                # error or no more files
+                error = rwin32.GetLastError_saved()
+                if error == win32traits.ERROR_NO_MORE_FILES:
+                    return None
+                raise WindowsError(error,  "FindNextFileW failed")
+        return DirEntP(dirp.filedata)
+
+    def get_name_unicode(direntp):
+        return unicode_traits.charp2str(rffi.cast(unicode_traits.CCHARP,
+                                                  
direntp.filedata.c_cFileName))
+
+    def get_known_type(filedata):
+        return 0
+
+    def get_inode(filedata):
+        return None
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to