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