[pypy-commit] pypy release-pypy3.5-v5.9.x: merge py3.5

fijal pypy.commits at gmail.com
Thu Dec 21 04:05:11 EST 2017


Author: fijal
Branch: release-pypy3.5-v5.9.x
Changeset: r93528:067f612e97ae
Date: 2017-12-21 11:04 +0200
http://bitbucket.org/pypy/pypy/changeset/067f612e97ae/

Log:	merge py3.5

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/lib-python/3/subprocess.py b/lib-python/3/subprocess.py
--- a/lib-python/3/subprocess.py
+++ b/lib-python/3/subprocess.py
@@ -1560,7 +1560,7 @@
                   'copyfile' in caller.f_globals):
         dest_dir = sys.pypy_resolvedirof(target_executable)
         src_dir = sys.pypy_resolvedirof(sys.executable)
-        for libname in ['libpypy3-c.so', 'libpypy3-c.dylib']:
+        for libname in ['libpypy3-c.so', 'libpypy3-c.dylib', 'libpypy3-c.dll']:
             dest_library = os.path.join(dest_dir, libname)
             src_library = os.path.join(src_dir, libname)
             if os.path.exists(src_library):
diff --git a/lib_pypy/_cffi_ssl/_stdssl/__init__.py b/lib_pypy/_cffi_ssl/_stdssl/__init__.py
--- a/lib_pypy/_cffi_ssl/_stdssl/__init__.py
+++ b/lib_pypy/_cffi_ssl/_stdssl/__init__.py
@@ -20,9 +20,15 @@
         SSL_ERROR_EOF, SSL_ERROR_NO_SOCKET, SSL_ERROR_INVALID_ERROR_CODE,
         pyerr_write_unraisable)
 from _cffi_ssl._stdssl import error
-from select import poll, POLLIN, POLLOUT, select
+from select import select
 from enum import IntEnum as _IntEnum
 
+if sys.platform == 'win32':
+    HAVE_POLL = False
+else:
+    from select import poll, POLLIN, POLLOUT
+    HAVE_POLL = True
+
 OPENSSL_VERSION = ffi.string(lib.OPENSSL_VERSION_TEXT).decode('utf-8')
 OPENSSL_VERSION_NUMBER = lib.OPENSSL_VERSION_NUMBER
 ver = OPENSSL_VERSION_NUMBER
@@ -158,8 +164,6 @@
     def _monotonic_clock():
         return time.clock_gettime(time.CLOCK_MONOTONIC)
 
-HAVE_POLL = True
-
 def _ssl_select(sock, writing, timeout):
     if HAVE_POLL:
         p = poll()
diff --git a/lib_pypy/_ssl/__init__.py b/lib_pypy/_ssl/__init__.py
--- a/lib_pypy/_ssl/__init__.py
+++ b/lib_pypy/_ssl/__init__.py
@@ -14,3 +14,14 @@
 # RAND_egd is optional and might not be available on e.g. libressl
 if hasattr(_stdssl, 'RAND_egd'):
     RAND_egd          = builtinify(RAND_egd)
+
+import sys
+if sys.platform == "win32" and 'enum_certificates' not in globals():
+    def enum_certificates(*args, **kwds):
+        import warnings
+        warnings.warn("ssl.enum_certificates() is not implemented")
+        return []
+    def enum_crls(*args, **kwds):
+        import warnings
+        warnings.warn("ssl.enum_crls() is not implemented")
+        return []
diff --git a/lib_pypy/faulthandler.py b/lib_pypy/faulthandler.py
new file mode 100644
--- /dev/null
+++ b/lib_pypy/faulthandler.py
@@ -0,0 +1,3 @@
+# This is only imported for platforms where the built-in faulthandler module is not 
+# available.  It provides no function at all so far, but it is enough to start the
+# CPython test suite.
\ No newline at end of file
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
@@ -16,3 +16,6 @@
 Download and patch dependencies when building cffi-based stdlib modules
 
 .. branch: os_lockf
+
+.. branch: py3.5-xattr
+Add posix.*attr() functions
diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py
--- a/pypy/interpreter/pycode.py
+++ b/pypy/interpreter/pycode.py
@@ -4,7 +4,7 @@
 The bytecode interpreter itself is implemented by the PyFrame class.
 """
 
-import imp, struct, types, new, sys, os
+import imp, struct, types, sys, os
 
 from pypy.interpreter import eval
 from pypy.interpreter.signature import Signature
@@ -80,7 +80,7 @@
 class PyCode(eval.Code):
     "CPython-style code objects."
     _immutable_fields_ = ["_signature", "co_argcount", "co_kwonlyargcount", "co_cellvars[*]",
-                          "co_code", "co_consts_w[*]", "co_filename",
+                          "co_code", "co_consts_w[*]", "co_filename", "w_filename",
                           "co_firstlineno", "co_flags", "co_freevars[*]",
                           "co_lnotab", "co_names_w[*]", "co_nlocals",
                           "co_stacksize", "co_varnames[*]",
@@ -111,6 +111,7 @@
         assert isinstance(filename, str)
         rstring.check_str0(filename)
         self.co_filename = filename
+        self.w_filename = space.newfilename(filename)
         self.co_name = name
         self.co_firstlineno = firstlineno
         self.co_lnotab = lnotab
@@ -203,6 +204,7 @@
         if lastdirname:
             basename = '%s/%s' % (lastdirname, basename)
         self.co_filename = '<builtin>/%s' % (basename,)
+        self.w_filename = self.space.newfilename(self.co_filename)
 
     co_names = property(lambda self: [self.space.str_w(w_name) for w_name in self.co_names_w]) # for trace
 
@@ -427,7 +429,7 @@
             space.newtuple(self.co_consts_w),
             space.newtuple(self.co_names_w),
             space.newtuple([space.newtext(v) for v in self.co_varnames]),
-            space.newtext(self.co_filename),
+            self.w_filename,
             space.newtext(self.co_name),
             space.newint(self.co_firstlineno),
             space.newbytes(self.co_lnotab),
@@ -451,7 +453,7 @@
         space = self.space
         # co_name should be an identifier
         name = self.co_name.decode('utf-8')
-        fn = space.fsdecode_w(space.newbytes(self.co_filename))
+        fn = space.unicode_w(self.w_filename)
         return space.newunicode(u'<code object %s at 0x%s, file "%s", line %d>' % (
             name, unicode(self.getaddrstring(space)), fn,
             -1 if self.co_firstlineno == 0 else self.co_firstlineno))
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -625,7 +625,7 @@
     co_varnames = GetSetProperty(PyCode.fget_co_varnames),
     co_freevars = GetSetProperty(PyCode.fget_co_freevars),
     co_cellvars = GetSetProperty(PyCode.fget_co_cellvars),
-    co_filename = interp_attrproperty('co_filename', cls=PyCode, wrapfn="newfilename"),
+    co_filename = interp_attrproperty_w('w_filename', cls=PyCode),
     co_name = interp_attrproperty('co_name', cls=PyCode, wrapfn="newtext"),
     co_firstlineno = interp_attrproperty('co_firstlineno', cls=PyCode, wrapfn="newint"),
     co_lnotab = interp_attrproperty('co_lnotab', cls=PyCode, wrapfn="newbytes"),
diff --git a/pypy/module/_io/interp_fileio.py b/pypy/module/_io/interp_fileio.py
--- a/pypy/module/_io/interp_fileio.py
+++ b/pypy/module/_io/interp_fileio.py
@@ -8,6 +8,7 @@
 from rpython.rlib.rstring import StringBuilder
 from rpython.rlib import rposix
 from rpython.rlib.rposix_stat import STAT_FIELD_TYPES
+from rpython.rlib.streamio import _setfd_binary
 from rpython.rtyper.lltypesystem import lltype, rffi
 from os import O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_TRUNC, O_EXCL
 import sys, os, stat, errno
@@ -239,6 +240,8 @@
             if HAS_BLKSIZE and st.st_blksize > 1:
                 self.blksize = st.st_blksize
 
+            _setfd_binary(self.fd)
+
             space.setattr(self, space.newtext("name"), w_name)
 
             if self.appending:
diff --git a/pypy/module/cpyext/funcobject.py b/pypy/module/cpyext/funcobject.py
--- a/pypy/module/cpyext/funcobject.py
+++ b/pypy/module/cpyext/funcobject.py
@@ -70,7 +70,7 @@
     py_code = rffi.cast(PyCodeObject, py_obj)
     assert isinstance(w_obj, PyCode)
     py_code.c_co_name = make_ref(space, space.newtext(w_obj.co_name))
-    py_code.c_co_filename = make_ref(space, space.newtext(w_obj.co_filename))
+    py_code.c_co_filename = make_ref(space, w_obj.w_filename)
     co_flags = 0
     for name, value in ALL_CODE_FLAGS:
         if w_obj.co_flags & getattr(pycode, name):
diff --git a/pypy/module/cpyext/test/test_unicodeobject.py b/pypy/module/cpyext/test/test_unicodeobject.py
--- a/pypy/module/cpyext/test/test_unicodeobject.py
+++ b/pypy/module/cpyext/test/test_unicodeobject.py
@@ -631,8 +631,9 @@
     def test_decode(self, space):
         b_text = rffi.str2charp('caf\x82xx')
         b_encoding = rffi.str2charp('cp437')
-        assert space.unicode_w(
-            PyUnicode_Decode(space, b_text, 4, b_encoding, None)) == u'caf\xe9'
+        b_errors = rffi.str2charp('strict')
+        assert space.unicode_w(PyUnicode_Decode(
+            space, b_text, 4, b_encoding, b_errors)) == u'caf\xe9'
 
         w_text = PyUnicode_FromEncodedObject(space, space.newbytes("test"), b_encoding, None)
         assert space.isinstance_w(w_text, space.w_unicode)
diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py
--- a/pypy/module/cpyext/unicodeobject.py
+++ b/pypy/module/cpyext/unicodeobject.py
@@ -492,7 +492,7 @@
     w_str = space.newbytes(rffi.charpsize2str(s, size))
     w_encoding = space.newtext(rffi.charp2str(encoding))
     if errors:
-        w_errors = space.newbytes(rffi.charp2str(errors))
+        w_errors = space.newtext(rffi.charp2str(errors))
     else:
         w_errors = None
     return space.call_method(w_str, 'decode', w_encoding, w_errors)
diff --git a/pypy/module/imp/importing.py b/pypy/module/imp/importing.py
--- a/pypy/module/imp/importing.py
+++ b/pypy/module/imp/importing.py
@@ -2,7 +2,7 @@
 Implementation of the interpreter-level default import logic.
 """
 
-import sys, os, stat, platform
+import sys, os, stat, re, platform
 
 from pypy.interpreter.module import Module, init_extra_module_attrs
 from pypy.interpreter.gateway import interp2app, unwrap_spec
@@ -44,18 +44,20 @@
         soabi += 'i'
 
     platform_name = sys.platform
-    if platform_name == 'linux2':
-        platform_name = 'linux'
+    if platform_name.startswith('linux'):
+        if re.match('(i[3-6]86|x86_64)$', platform.machine()):
+            if sys.maxsize < 2**32:
+                platform_name = 'i686-linux-gnu'
+                # xxx should detect if we are inside 'x32', but not for now
+                # because it's not supported anyway by PyPy.  (Relying
+                # on platform.machine() does not work, it may return x86_64
+                # anyway)
+            else:
+                platform_name = 'x86_64-linux-gnu'
+        else:
+            platform_name = 'linux-gnu'
 
     soabi += '-' + platform_name
-    # xxx used to also include platform.machine(), but this is wrong
-    # (might get AMD64 on a 32-bit python) and it is the source of a
-    # importlib bug if we get uppercase characters from there...
-
-    if platform_name == 'linux':
-        soabi += '-gnu'
-        if sys.maxsize == (2**31 - 1) and platform.machine() == 'x86_64':
-            soabi += 'x32'
 
     result = '.' + soabi + SO
     assert result == result.lower()   # this is an implicit requirement of importlib on Windows!
@@ -257,7 +259,7 @@
         if pathname is not None:
             w_pathname = get_sourcefile(space, pathname)
         else:
-            w_pathname = space.newfilename(code_w.co_filename)
+            w_pathname = code_w.w_filename
         if cpathname is not None:
             w_cpathname = space.newfilename(cpathname)
         else:
@@ -351,6 +353,7 @@
         return
 
     code_w.co_filename = pathname
+    code_w.w_filename = space.newfilename(pathname)
     constants = code_w.co_consts_w
     for const in constants:
         if const is not None and isinstance(const, PyCode):
diff --git a/pypy/module/imp/test/test_import.py b/pypy/module/imp/test/test_import.py
--- a/pypy/module/imp/test/test_import.py
+++ b/pypy/module/imp/test/test_import.py
@@ -769,9 +769,9 @@
 
 class TestAbi:
     def test_abi_tag(self):
-        space1 = maketestobjspace(make_config(None, soabi='TEST'))
+        space1 = maketestobjspace(make_config(None, soabi='footest'))
         space2 = maketestobjspace(make_config(None, soabi=''))
-        assert importing.get_so_extension(space1).startswith('.TEST')
+        assert importing.get_so_extension(space1).startswith('.footest')
         if sys.platform == 'win32':
             assert importing.get_so_extension(space2) == '.pyd'
         else:
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
@@ -80,6 +80,7 @@
         'urandom': 'interp_posix.urandom',
         'device_encoding': 'interp_posix.device_encoding',
         'get_terminal_size': 'interp_posix.get_terminal_size',
+        'symlink': 'interp_posix.symlink',
 
         'scandir': 'interp_scandir.scandir',
         'get_inheritable': 'interp_posix.get_inheritable',
@@ -111,8 +112,6 @@
         interpleveldefs['killpg'] = 'interp_posix.killpg'
     if hasattr(os, 'getpid'):
         interpleveldefs['getpid'] = 'interp_posix.getpid'
-    if hasattr(os, 'symlink'):
-        interpleveldefs['symlink'] = 'interp_posix.symlink'
     if hasattr(os, 'readlink'):
         interpleveldefs['readlink'] = 'interp_posix.readlink'
     if hasattr(os, 'fork'):
@@ -229,7 +228,7 @@
         'POSIX_FADV_RANDOM', 'POSIX_FADV_NOREUSE', 'POSIX_FADV_DONTNEED']:
             assert getattr(rposix, _name) is not None, "missing %r" % (_name,)
             interpleveldefs[_name] = 'space.wrap(%d)' % getattr(rposix, _name)
-    
+
     if hasattr(rposix, 'sched_get_priority_max'):
         interpleveldefs['sched_get_priority_max'] = 'interp_posix.sched_get_priority_max'
         interpleveldefs['sched_get_priority_min'] = 'interp_posix.sched_get_priority_min'
@@ -246,11 +245,21 @@
 
     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)
 
+    if hasattr(rposix, 'getxattr'):
+        interpleveldefs['getxattr'] = 'interp_posix.getxattr'
+        interpleveldefs['setxattr'] = 'interp_posix.setxattr'
+        interpleveldefs['removexattr'] = 'interp_posix.removexattr'
+        interpleveldefs['listxattr'] = 'interp_posix.listxattr'
+        for _name in ['XATTR_SIZE_MAX', 'XATTR_CREATE', 'XATTR_REPLACE']:
+            if getattr(rposix, _name) is not None:
+                interpleveldefs[_name] = 'space.wrap(%d)' % getattr(rposix, _name)
+
+
     def startup(self, space):
         from pypy.module.posix import interp_posix
         from pypy.module.imp import importing
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
@@ -122,7 +122,7 @@
     else:
         path_b = path.as_bytes
         assert path_b is not None
-        return func(path.as_bytes, *args)
+        return func(path_b, *args)
 
 
 class Path(object):
@@ -1273,6 +1273,9 @@
   and path should be relative; path will then be relative to that directory.
 dir_fd may not be implemented on your platform.
   If it is unavailable, using it will raise a NotImplementedError."""
+    if _WIN32:
+        raise oefmt(space.w_NotImplementedError,
+                    "symlink() is not implemented for PyPy on Windows")
     try:
         if rposix.HAVE_SYMLINKAT and dir_fd != DEFAULT_DIR_FD:
             src = space.fsencode_w(w_src)
@@ -2283,7 +2286,9 @@
 This function will not follow symbolic links.
 Equivalent to chflags(path, flags, follow_symlinks=False)."""
 
-def getxattr():
+ at unwrap_spec(path=path_or_fd(), attribute=path_or_fd(allow_fd=False),
+             follow_symlinks=bool)
+def getxattr(space, path, attribute, __kwonly__, follow_symlinks=True):
     """getxattr(path, attribute, *, follow_symlinks=True) -> value
 
 Return the value of extended attribute attribute on path.
@@ -2292,8 +2297,27 @@
 If follow_symlinks is False, and the last element of the path is a symbolic
   link, getxattr will examine the symbolic link itself instead of the file
   the link points to."""
+    if path.as_fd != -1:
+        if not follow_symlinks:
+            raise oefmt(space.w_ValueError,
+                "getxattr: cannot use fd and follow_symlinks together")
+        try:
+            result = rposix.fgetxattr(path.as_fd, attribute.as_bytes)
+        except OSError as e:
+            raise wrap_oserror(space, e, path.as_bytes)
+    else:
+        try:
+            result = rposix.getxattr(path.as_bytes, attribute.as_bytes,
+                follow_symlinks=follow_symlinks)
+        except OSError as e:
+            raise wrap_oserror(space, e, path.as_bytes)
+    return space.newbytes(result)
 
-def setxattr():
+ at unwrap_spec(path=path_or_fd(), attribute=path_or_fd(allow_fd=False),
+             flags=c_int,
+             follow_symlinks=bool)
+def setxattr(space, path, attribute, w_value, flags=0,
+             __kwonly__=None, follow_symlinks=True):
     """setxattr(path, attribute, value, flags=0, *, follow_symlinks=True)
 
 Set extended attribute attribute on path to value.
@@ -2301,9 +2325,26 @@
 If follow_symlinks is False, and the last element of the path is a symbolic
   link, setxattr will modify the symbolic link itself instead of the file
   the link points to."""
+    value = space.charbuf_w(w_value)
+    if path.as_fd != -1:
+        if not follow_symlinks:
+            raise oefmt(space.w_ValueError,
+                "setxattr: cannot use fd and follow_symlinks together")
+        try:
+            rposix.fsetxattr(path.as_fd, attribute.as_bytes, value)
+        except OSError as e:
+            raise wrap_oserror(space, e, path.as_bytes)
+    else:
+        try:
+            rposix.setxattr(path.as_bytes, attribute.as_bytes, value,
+                follow_symlinks=follow_symlinks)
+        except OSError as e:
+            raise wrap_oserror(space, e, path.as_bytes)
 
 
-def removexattr():
+ at unwrap_spec(path=path_or_fd(), attribute=path_or_fd(allow_fd=False),
+             follow_symlinks=bool)
+def removexattr(space, path, attribute, __kwonly__, follow_symlinks=True):
     """removexattr(path, attribute, *, follow_symlinks=True)
 
 Remove extended attribute attribute on path.
@@ -2311,8 +2352,24 @@
 If follow_symlinks is False, and the last element of the path is a symbolic
   link, removexattr will modify the symbolic link itself instead of the file
   the link points to."""
+    if path.as_fd != -1:
+        if not follow_symlinks:
+            raise oefmt(space.w_ValueError,
+                "removexattr: cannot use fd and follow_symlinks together")
+        try:
+            rposix.fremovexattr(path.as_fd, attribute.as_bytes)
+        except OSError as e:
+            raise wrap_oserror(space, e, path.as_bytes)
+    else:
+        try:
+            rposix.removexattr(path.as_bytes, attribute.as_bytes,
+                follow_symlinks=follow_symlinks)
+        except OSError as e:
+            raise wrap_oserror(space, e, path.as_bytes)
 
-def listxattr():
+
+ at unwrap_spec(path=path_or_fd(), follow_symlinks=bool)
+def listxattr(space, path, __kwonly__, follow_symlinks=True):
     """listxattr(path='.', *, follow_symlinks=True)
 
 Return a list of extended attributes on path.
@@ -2322,6 +2379,20 @@
 If follow_symlinks is False, and the last element of the path is a symbolic
   link, listxattr will examine the symbolic link itself instead of the file
   the link points to."""
+    if path.as_fd != -1:
+        if not follow_symlinks:
+            raise oefmt(space.w_ValueError,
+                        "listxattr: cannot use fd and follow_symlinks together")
+        try:
+            result = rposix.flistxattr(path.as_fd)
+        except OSError as e:
+            raise wrap_oserror(space, e, eintr_retry=False)
+    else:
+        try:
+            result = rposix.listxattr(path.as_bytes, follow_symlinks)
+        except OSError as e:
+            raise wrap_oserror(space, e, path.as_bytes)
+    return space.newlist([space.newfilename(attr) for attr in result])
 
 
 have_functions = []
@@ -2449,8 +2520,8 @@
 
 @unwrap_spec(policy=int)
 def sched_get_priority_max(space, policy):
-    """returns the maximum priority value that 
-    can be used with the scheduling algorithm 
+    """returns the maximum priority value that
+    can be used with the scheduling algorithm
     identified by policy
     """
     while True:
@@ -2464,7 +2535,7 @@
 @unwrap_spec(policy=int)
 def sched_get_priority_min(space, policy):
     """returns the minimum priority value that
-     can be used with the scheduling algorithm 
+     can be used with the scheduling algorithm
      identified by policy
     """
     while True:
@@ -2477,7 +2548,7 @@
 
 @unwrap_spec(fd=c_int, cmd=c_int, length=r_longlong)
 def lockf(space, fd, cmd, length):
-    """apply, test or remove a POSIX lock on an 
+    """apply, test or remove a POSIX lock on an
     open file.
     """
     while True:
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,13 @@
 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
 
 
 class W_DirEntry(W_Root):
@@ -127,14 +143,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:
+            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)
+        else:
+            w_name = self.space.newunicode(name)
         self.w_name = w_name
 
     def descr_repr(self, space):
@@ -156,93 +175,109 @@
     # the end of the class.  Every method only calls methods *before*
     # it in program order, so there is no cycle.
 
-    def get_lstat(self):
-        """Get the lstat() of the direntry."""
-        if (self.flags & FLAG_LSTAT) == 0:
-            # Unlike CPython, try to use fstatat() if possible
-            dirfd = self.scandir_iterator.dirfd
-            if dirfd != -1 and rposix.HAVE_FSTATAT:
-                st = rposix_stat.fstatat(self.name, dirfd,
-                                         follow_symlinks=False)
-            else:
-                path = self.space.fsencode_w(self.fget_path(self.space))
-                st = rposix_stat.lstat(path)
-            self.d_lstat = st
-            self.flags |= FLAG_LSTAT
-        return self.d_lstat
-
-    def get_stat(self):
-        """Get the stat() of the direntry.  This is implemented in
-        such a way that it won't do both a stat() and a lstat().
-        """
-        if (self.flags & FLAG_STAT) == 0:
-            # We don't have the 'd_stat'.  If the known_type says the
-            # direntry is not a DT_LNK, then try to get and cache the
-            # 'd_lstat' instead.  Then, or if we already have a
-            # 'd_lstat' from before, *and* if the 'd_lstat' is not a
-            # S_ISLNK, we can reuse it unchanged for 'd_stat'.
-            #
-            # Note how, in the common case where the known_type says
-            # it is a DT_REG or DT_DIR, then we call and cache lstat()
-            # and that's it.  Also note that in a d_type-less OS or on
-            # a filesystem that always answer DT_UNKNOWN, this method
-            # will instead only call at most stat(), but not cache it
-            # as 'd_lstat'.
-            known_type = self.flags & 255
-            if (known_type != rposix_scandir.DT_UNKNOWN and
-                known_type != rposix_scandir.DT_LNK):
-                self.get_lstat()    # fill the 'd_lstat' cache
-                have_lstat = True
-            else:
-                have_lstat = (self.flags & FLAG_LSTAT) != 0
-
-            if have_lstat:
-                # We have the lstat() but not the stat().  They are
-                # the same, unless the 'd_lstat' is a S_IFLNK.
-                must_call_stat = stat.S_ISLNK(self.d_lstat.st_mode)
-            else:
-                must_call_stat = True
-
-            if must_call_stat:
-                # Must call stat().  Try to use fstatat() if possible
+    if not _WIN32:
+        def get_lstat(self):
+            """Get the lstat() of the direntry."""
+            if (self.flags & FLAG_LSTAT) == 0:
+                # Unlike CPython, try to use fstatat() if possible
                 dirfd = self.scandir_iterator.dirfd
-                if dirfd != -1 and rposix.HAVE_FSTATAT:
+                if rposix.HAVE_FSTATAT and dirfd != -1:
                     st = rposix_stat.fstatat(self.name, dirfd,
-                                             follow_symlinks=True)
+                                             follow_symlinks=False)
                 else:
                     path = self.space.fsencode_w(self.fget_path(self.space))
-                    st = rposix_stat.stat(path)
+                    st = rposix_stat.lstat(path)
+                self.d_lstat = st
+                self.flags |= FLAG_LSTAT
+            return self.d_lstat
+
+        def get_stat(self):
+            """Get the stat() of the direntry.  This is implemented in
+            such a way that it won't do both a stat() and a lstat().
+            """
+            if (self.flags & FLAG_STAT) == 0:
+                # We don't have the 'd_stat'.  If the known_type says the
+                # direntry is not a DT_LNK, then try to get and cache the
+                # 'd_lstat' instead.  Then, or if we already have a
+                # 'd_lstat' from before, *and* if the 'd_lstat' is not a
+                # S_ISLNK, we can reuse it unchanged for 'd_stat'.
+                #
+                # Note how, in the common case where the known_type says
+                # it is a DT_REG or DT_DIR, then we call and cache lstat()
+                # and that's it.  Also note that in a d_type-less OS or on
+                # a filesystem that always answer DT_UNKNOWN, this method
+                # will instead only call at most stat(), but not cache it
+                # as 'd_lstat'.
+                known_type = self.flags & 255
+                if (known_type != rposix_scandir.DT_UNKNOWN and
+                    known_type != rposix_scandir.DT_LNK):
+                    self.get_lstat()    # fill the 'd_lstat' cache
+                    have_lstat = True
+                else:
+                    have_lstat = (self.flags & FLAG_LSTAT) != 0
+
+                if have_lstat:
+                    # We have the lstat() but not the stat().  They are
+                    # the same, unless the 'd_lstat' is a S_IFLNK.
+                    must_call_stat = stat.S_ISLNK(self.d_lstat.st_mode)
+                else:
+                    must_call_stat = True
+
+                if must_call_stat:
+                    # Must call stat().  Try to use fstatat() if possible
+                    dirfd = self.scandir_iterator.dirfd
+                    if dirfd != -1 and rposix.HAVE_FSTATAT:
+                        st = rposix_stat.fstatat(self.name, dirfd,
+                                                 follow_symlinks=True)
+                    else:
+                        path = self.space.fsencode_w(self.fget_path(self.space))
+                        st = rposix_stat.stat(path)
+                else:
+                    st = self.d_lstat
+
+                self.d_stat = st
+                self.flags |= FLAG_STAT
+            return self.d_stat
+
+        def get_stat_or_lstat(self, follow_symlinks):
+            if follow_symlinks:
+                return self.get_stat()
             else:
-                st = self.d_lstat
+                return self.get_lstat()
 
-            self.d_stat = st
-            self.flags |= FLAG_STAT
-        return self.d_stat
+        def check_mode(self, follow_symlinks):
+            """Get the stat() or lstat() of the direntry, and return the
+            S_IFMT.  If calling stat()/lstat() gives us ENOENT, return -1
+            instead; it is better to give up and answer "no, not this type"
+            to requests, rather than propagate the error.
+            """
+            try:
+                st = self.get_stat_or_lstat(follow_symlinks)
+            except OSError as e:
+                if e.errno == ENOENT:    # not found
+                    return -1
+                raise wrap_oserror2(self.space, e, self.fget_path(self.space),
+                                    eintr_retry=False)
+            return stat.S_IFMT(st.st_mode)
 
-    def get_stat_or_lstat(self, follow_symlinks):
-        if follow_symlinks:
-            return self.get_stat()
-        else:
-            return self.get_lstat()
+    else:
+        # Win32
+        stat_cached = False
 
-    def check_mode(self, follow_symlinks):
-        """Get the stat() or lstat() of the direntry, and return the
-        S_IFMT.  If calling stat()/lstat() gives us ENOENT, return -1
-        instead; it is better to give up and answer "no, not this type"
-        to requests, rather than propagate the error.
-        """
-        try:
-            st = self.get_stat_or_lstat(follow_symlinks)
-        except OSError as e:
-            if e.errno == ENOENT:    # not found
-                return -1
-            raise wrap_oserror2(self.space, e, self.fget_path(self.space),
-                                eintr_retry=False)
-        return stat.S_IFMT(st.st_mode)
+        def check_mode(self, follow_symlinks):
+            return self.flags
+
+        def get_stat_or_lstat(self, follow_symlinks):     # 'follow_symlinks' ignored
+            if not self.stat_cached:
+                path = self.space.unicode0_w(self.fget_path(self.space))
+                self.d_stat = rposix_stat.stat(path)
+                self.stat_cached = True
+            return self.d_stat
+
 
     def is_dir(self, follow_symlinks):
         known_type = self.flags & 255
-        if known_type != rposix_scandir.DT_UNKNOWN:
+        if not _WIN32 and known_type != rposix_scandir.DT_UNKNOWN:
             if known_type == rposix_scandir.DT_DIR:
                 return True
             elif follow_symlinks and known_type == rposix_scandir.DT_LNK:
@@ -253,7 +288,7 @@
 
     def is_file(self, follow_symlinks):
         known_type = self.flags & 255
-        if known_type != rposix_scandir.DT_UNKNOWN:
+        if not _WIN32 and known_type != rposix_scandir.DT_UNKNOWN:
             if known_type == rposix_scandir.DT_REG:
                 return True
             elif follow_symlinks and known_type == rposix_scandir.DT_LNK:
@@ -265,7 +300,7 @@
     def is_symlink(self):
         """Check if the direntry is a symlink.  May get the lstat()."""
         known_type = self.flags & 255
-        if known_type != rposix_scandir.DT_UNKNOWN:
+        if not _WIN32 and known_type != rposix_scandir.DT_UNKNOWN:
             return known_type == rposix_scandir.DT_LNK
         return self.check_mode(follow_symlinks=False) == stat.S_IFLNK
 
@@ -294,7 +329,15 @@
         return build_stat_result(space, st)
 
     def descr_inode(self, space):
-        return space.newint(self.inode)
+        inode = self.inode
+        if inode is None:    # _WIN32
+            try:
+                st = self.get_stat_or_lstat(follow_symlinks=False)
+            except OSError as e:
+                raise wrap_oserror2(space, e, self.fget_path(space),
+                                    eintr_retry=False)
+            inode = st.st_ino
+        return space.newint(inode)
 
 
 W_DirEntry.typedef = TypeDef(
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
@@ -386,8 +386,8 @@
 
     def test_times(self):
         """
-        posix.times() should return a posix.times_result object giving 
-        float-representations (seconds, effectively) of the four fields from 
+        posix.times() should return a posix.times_result object giving
+        float-representations (seconds, effectively) of the four fields from
         the underlying struct tms and the return value.
         """
         result = self.posix.times()
@@ -977,7 +977,7 @@
             assert posix.sched_get_priority_min(posix.SCHED_OTHER) != -1
             if getattr(posix, 'SCHED_BATCH', None):
                 assert posix.sched_get_priority_min(posix.SCHED_BATCH) != -1
-            
+
     if hasattr(rposix, 'sched_get_priority_min'):
         def test_os_sched_priority_max_greater_than_min(self):
             posix, os = self.posix, self.os
@@ -992,7 +992,7 @@
         def test_sched_yield(self):
             os = self.posix
             #Always suceeds on Linux
-            os.sched_yield() 
+            os.sched_yield()
 
     def test_write_buffer(self):
         os = self.posix
@@ -1157,7 +1157,7 @@
             expected = min(myprio + 3, 19)
             assert os.WEXITSTATUS(status1) == expected
 
-    if hasattr(os, 'symlink'):
+    if sys.platform != 'win32':
         def test_symlink(self):
             posix = self.posix
             bytes_dir = self.bytes_dir
@@ -1187,6 +1187,10 @@
             finally:
                 posix.close(f)
                 posix.unlink(bytes_dir + '/somelink'.encode())
+    else:
+        def test_symlink(self):
+            posix = self.posix
+            raises(NotImplementedError, posix.symlink, 'a', 'b')
 
     if hasattr(os, 'ftruncate'):
         def test_truncate(self):
@@ -1350,7 +1354,7 @@
             posix.close(fd)
             s2.close()
             s1.close()
-            
+
         def test_os_lockf(self):
             posix, os = self.posix, self.os
             fd = os.open(self.path2 + 'test_os_lockf', os.O_WRONLY | os.O_CREAT)
@@ -1441,6 +1445,25 @@
         e = raises(OSError, self.posix.symlink, 'bok', '/nonexistentdir/boz')
         assert str(e.value).endswith(": 'bok' -> '/nonexistentdir/boz'")
 
+    if hasattr(rposix, 'getxattr'):
+        def test_xattr_simple(self):
+            # Minimal testing here, lib-python has better tests.
+            os = self.posix
+            with open(self.path, 'wb'):
+                pass
+            init_names = os.listxattr(self.path)
+            excinfo = raises(OSError, os.getxattr, self.path, 'user.test')
+            assert excinfo.value.filename == self.path
+            os.setxattr(self.path, 'user.test', b'', os.XATTR_CREATE, follow_symlinks=False)
+            assert os.getxattr(self.path, 'user.test') == b''
+            os.setxattr(self.path, b'user.test', b'foo', os.XATTR_REPLACE)
+            assert os.getxattr(self.path, 'user.test', follow_symlinks=False) == b'foo'
+            assert set(os.listxattr(self.path)) == set(
+                init_names + ['user.test'])
+            os.removexattr(self.path, 'user.test', follow_symlinks=False)
+            raises(OSError, os.getxattr, self.path, 'user.test')
+            assert os.listxattr(self.path, follow_symlinks=False) == init_names
+
 
 class AppTestEnvironment(object):
     def setup_class(cls):
@@ -1495,6 +1518,7 @@
             res = os.system(cmd)
             assert res == 0
 
+
 @py.test.fixture
 def check_fsencoding(space, pytestconfig):
     if pytestconfig.getvalue('runappdirect'):
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
@@ -1,4 +1,5 @@
 import sys, os
+import py
 from rpython.tool.udir import udir
 from pypy.module.posix.test import test_posix2
 
@@ -31,6 +32,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 +41,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 +64,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
@@ -89,6 +98,8 @@
         assert d.stat().st_mode & 0o170000 == 0o100000    # S_IFREG
         assert d.stat().st_size == 0
 
+    @py.test.mark.skipif(sys.platform == "win32",
+                         reason="no symlink support so far")
     def test_stat4(self):
         posix = self.posix
         d = next(posix.scandir(self.dir4))
@@ -118,6 +129,8 @@
         assert not d.is_file(follow_symlinks=False)
         assert     d.is_dir(follow_symlinks=False)
 
+    @py.test.mark.skipif(sys.platform == "win32",
+                         reason="no symlink support so far")
     def test_dir3(self):
         posix = self.posix
         d = next(posix.scandir(self.dir3))
@@ -128,6 +141,8 @@
         assert     d.is_file(follow_symlinks=True)
         assert not d.is_file(follow_symlinks=False)
 
+    @py.test.mark.skipif(sys.platform == "win32",
+                         reason="no symlink support so far")
     def test_dir4(self):
         posix = self.posix
         d = next(posix.scandir(self.dir4))
@@ -138,6 +153,8 @@
         assert     d.is_dir(follow_symlinks=True)
         assert not d.is_dir(follow_symlinks=False)
 
+    @py.test.mark.skipif(sys.platform == "win32",
+                         reason="no symlink support so far")
     def test_dir5(self):
         posix = self.posix
         d = next(posix.scandir(self.dir5))
@@ -147,6 +164,8 @@
         assert     d.is_symlink()
         raises(OSError, d.stat)
 
+    @py.test.mark.skipif(sys.platform == "win32",
+                         reason="no symlink support so far")
     def test_dir6(self):
         posix = self.posix
         d = next(posix.scandir(self.dir6))
diff --git a/pypy/module/zipimport/interp_zipimport.py b/pypy/module/zipimport/interp_zipimport.py
--- a/pypy/module/zipimport/interp_zipimport.py
+++ b/pypy/module/zipimport/interp_zipimport.py
@@ -252,6 +252,8 @@
         gets in code_object.co_filename. Something like
         'myfile.zip/mymodule.py'
         """
+        if ZIPSEP != os.path.sep:
+            filename = filename.replace(ZIPSEP, os.path.sep)
         return self.filename + os.path.sep + filename
 
     def load_module(self, space, w_fullname):
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
@@ -17,8 +17,8 @@
     "resource": "_resource_build.py" if sys.platform != "win32" else None,
     "lzma": "_lzma_build.py",
     "_decimal": "_decimal_build.py",
-    "ssl": "_ssl_build.py",
-    # hashlib does not need to be built! It uses API calls from ssl
+    "_ssl": "_ssl_build.py",
+    # hashlib does not need to be built! It uses API calls from _ssl
     "xx": None,    # for testing: 'None' should be completely ignored
     }
 
@@ -28,7 +28,7 @@
     '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',
+    '_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',
@@ -159,6 +159,12 @@
             continue
         if module is None or getattr(options, 'no_' + key, False):
             continue
+        # the key is the module name, has it already been built?
+        status, stdout, stderr = run_subprocess(str(pypy_c), ['-c', 'import %s' % key])
+        if status  == 0:
+            print('*', ' %s already built' % key, file=sys.stderr)
+            continue
+        
         if module.endswith('.py'):
             args = [module]
             cwd = str(join(basedir,'lib_pypy'))
@@ -175,7 +181,7 @@
             shutil.rmtree(destdir, ignore_errors=True)
             os.makedirs(destdir)
 
-            if key == 'ssl' and sys.platform == 'darwin':
+            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 = [
@@ -201,7 +207,7 @@
             env['LDFLAGS'] = \
                 '-L{}/usr/lib {}'.format(destdir, env.get('LDFLAGS', ''))
 
-            if key == 'ssl' and sys.platform == 'darwin':
+            if key == '_ssl' and sys.platform == 'darwin':
                 # needed for our roots patch
                 env['LDFLAGS'] += ' -framework CoreFoundation -framework Security'
 
@@ -237,7 +243,7 @@
                         help='instead of executing sys.executable' \
                              ' 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')
+                        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()
diff --git a/rpython/annotator/binaryop.py b/rpython/annotator/binaryop.py
--- a/rpython/annotator/binaryop.py
+++ b/rpython/annotator/binaryop.py
@@ -381,16 +381,14 @@
 class __extend__(pairtype(SomeChar, SomeUnicodeCodePoint),
                  pairtype(SomeUnicodeCodePoint, SomeChar)):
     def union((uchr1, uchr2)):
-        return SomeUnicodeCodePoint()
+        no_nul = uchr1.no_nul and uchr2.no_nul
+        return SomeUnicodeCodePoint(no_nul=no_nul)
 
 class __extend__(pairtype(SomeUnicodeCodePoint, SomeUnicodeCodePoint)):
     def union((uchr1, uchr2)):
         no_nul = uchr1.no_nul and uchr2.no_nul
         return SomeUnicodeCodePoint(no_nul=no_nul)
 
-    def add((chr1, chr2)):
-        return SomeUnicodeString()
-
 class __extend__(pairtype(SomeString, SomeUnicodeString),
                  pairtype(SomeUnicodeString, SomeString)):
     def mod((str, unistring)):
diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py
--- a/rpython/rlib/rposix.py
+++ b/rpython/rlib/rposix.py
@@ -2574,3 +2574,160 @@
         """Passes offset==NULL; not support on all OSes"""
         res = c_sendfile(out_fd, in_fd, lltype.nullptr(_OFF_PTR_T.TO), count)
         return handle_posix_error('sendfile', res)
+
+# ____________________________________________________________
+# Support for *xattr functions
+
+if sys.platform.startswith('linux'):
+
+    class CConfig:
+        _compilation_info_ = ExternalCompilationInfo(
+            includes=['sys/xattr.h', 'linux/limits.h'],)
+        XATTR_SIZE_MAX = rffi_platform.DefinedConstantInteger('XATTR_SIZE_MAX')
+        XATTR_CREATE = rffi_platform.DefinedConstantInteger('XATTR_CREATE')
+        XATTR_REPLACE = rffi_platform.DefinedConstantInteger('XATTR_REPLACE')
+
+    cConfig = rffi_platform.configure(CConfig)
+    globals().update(cConfig)
+    c_fgetxattr = external('fgetxattr',
+        [rffi.INT, rffi.CCHARP, rffi.VOIDP, rffi.SIZE_T], rffi.SSIZE_T,
+        compilation_info=CConfig._compilation_info_,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+    c_getxattr = external('getxattr',
+        [rffi.CCHARP, rffi.CCHARP, rffi.VOIDP, rffi.SIZE_T], rffi.SSIZE_T,
+        compilation_info=CConfig._compilation_info_,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+    c_lgetxattr = external('lgetxattr',
+        [rffi.CCHARP, rffi.CCHARP, rffi.VOIDP, rffi.SIZE_T], rffi.SSIZE_T,
+        compilation_info=CConfig._compilation_info_,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+    c_fsetxattr = external('fsetxattr',
+        [rffi.INT, rffi.CCHARP, rffi.CCHARP, rffi.SIZE_T, rffi.INT],
+        rffi.INT,
+        compilation_info=CConfig._compilation_info_,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+    c_setxattr = external('setxattr',
+        [rffi.CCHARP, rffi.CCHARP, rffi.CCHARP, rffi.SIZE_T, rffi.INT],
+        rffi.INT,
+        compilation_info=CConfig._compilation_info_,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+    c_lsetxattr = external('lsetxattr',
+        [rffi.CCHARP, rffi.CCHARP, rffi.CCHARP, rffi.SIZE_T, rffi.INT],
+        rffi.INT,
+        compilation_info=CConfig._compilation_info_,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+    c_fremovexattr = external('fremovexattr',
+        [rffi.INT, rffi.CCHARP], rffi.INT,
+        compilation_info=CConfig._compilation_info_,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+    c_removexattr = external('removexattr',
+        [rffi.CCHARP, rffi.CCHARP], rffi.INT,
+        compilation_info=CConfig._compilation_info_,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+    c_lremovexattr = external('lremovexattr',
+        [rffi.CCHARP, rffi.CCHARP], rffi.INT,
+        compilation_info=CConfig._compilation_info_,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+    c_flistxattr = external('flistxattr',
+        [rffi.INT, rffi.CCHARP, rffi.SIZE_T], rffi.SSIZE_T,
+        compilation_info=CConfig._compilation_info_,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+    c_listxattr = external('listxattr',
+        [rffi.CCHARP, rffi.CCHARP, rffi.SIZE_T], rffi.SSIZE_T,
+        compilation_info=CConfig._compilation_info_,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+    c_llistxattr = external('llistxattr',
+        [rffi.CCHARP, rffi.CCHARP, rffi.SIZE_T], rffi.SSIZE_T,
+        compilation_info=CConfig._compilation_info_,
+        save_err=rffi.RFFI_SAVE_ERRNO)
+    buf_sizes = [256, XATTR_SIZE_MAX]
+
+    def fgetxattr(fd, name):
+        for size in buf_sizes:
+            with rffi.scoped_alloc_buffer(size) as buf:
+                void_buf = rffi.cast(rffi.VOIDP, buf.raw)
+                res = c_fgetxattr(fd, name, void_buf, size)
+                if res < 0:
+                    err = get_saved_errno()
+                    if err != errno.ERANGE:
+                        raise OSError(err, 'fgetxattr failed')
+                else:
+                    return buf.str(res)
+        else:
+            raise OSError(errno.ERANGE, 'fgetxattr failed')
+
+    def getxattr(path, name, follow_symlinks=True):
+        for size in buf_sizes:
+            with rffi.scoped_alloc_buffer(size) as buf:
+                void_buf = rffi.cast(rffi.VOIDP, buf.raw)
+                if follow_symlinks:
+                    res = c_getxattr(path, name, void_buf, size)
+                else:
+                    res = c_lgetxattr(path, name, void_buf, size)
+                if res < 0:
+                    err = get_saved_errno()
+                    if err != errno.ERANGE:
+                        c_name = 'getxattr' if follow_symlinks else 'lgetxattr'
+                        raise OSError(err, c_name + 'failed')
+                else:
+                    return buf.str(res)
+        else:
+            c_name = 'getxattr' if follow_symlinks else 'lgetxattr'
+            raise OSError(errno.ERANGE, c_name + 'failed')
+
+    def fsetxattr(fd, name, value, flags=0):
+        return handle_posix_error(
+            'fsetxattr', c_fsetxattr(fd, name, value, len(value), flags))
+
+    def setxattr(path, name, value, flags=0, follow_symlinks=True):
+        if follow_symlinks:
+            return handle_posix_error(
+                'setxattr', c_setxattr(path, name, value, len(value), flags))
+        else:
+            return handle_posix_error(
+                'lsetxattr', c_lsetxattr(path, name, value, len(value), flags))
+
+    def fremovexattr(fd, name):
+        return handle_posix_error('fremovexattr', c_fremovexattr(fd, name))
+
+    def removexattr(path, name, follow_symlinks=True):
+        if follow_symlinks:
+            return handle_posix_error('removexattr', c_removexattr(path, name))
+        else:
+            return handle_posix_error('lremovexattr', c_lremovexattr(path, name))
+
+    def _unpack_attrs(attr_string):
+        result = attr_string.split('\0')
+        del result[-1]
+        return result
+
+    def flistxattr(fd):
+        for size in buf_sizes:
+            with rffi.scoped_alloc_buffer(size) as buf:
+                res = c_flistxattr(fd, buf.raw, size)
+                if res < 0:
+                    err = get_saved_errno()
+                    if err != errno.ERANGE:
+                        raise OSError(err, 'flistxattr failed')
+                else:
+                    return _unpack_attrs(buf.str(res))
+        else:
+            raise OSError(errno.ERANGE, 'flistxattr failed')
+
+    def listxattr(path, follow_symlinks=True):
+        for size in buf_sizes:
+            with rffi.scoped_alloc_buffer(size) as buf:
+                if follow_symlinks:
+                    res = c_listxattr(path, buf.raw, size)
+                else:
+                    res = c_llistxattr(path, buf.raw, size)
+                if res < 0:
+                    err = get_saved_errno()
+                    if err != errno.ERANGE:
+                        c_name = 'listxattr' if follow_symlinks else 'llistxattr'
+                        raise OSError(err, c_name + 'failed')
+                else:
+                    return _unpack_attrs(buf.str(res))
+        else:
+            c_name = 'listxattr' if follow_symlinks else 'llistxattr'
+            raise OSError(errno.ERANGE, c_name + 'failed')
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
@@ -1,56 +1,126 @@
 from rpython.rlib import rposix, rwin32
 from rpython.rlib.objectmodel import specialize
 from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rlib.rarithmetic import intmask
 
 
- 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)
-
 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 closedir(dirp):
+        rposix.c_closedir(dirp)
+
     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 -----
+    import stat
+    from rpython.rlib._os_support import unicode_traits
+    from rpython.rlib.rwin32file import make_win32_traits
+    from rpython.rlib import rposix_stat
+
+    win32traits = make_win32_traits(unicode_traits)
+
+
+    SCANDIRP = lltype.Ptr(lltype.Struct('SCANDIRP',
+        ('filedata', win32traits.WIN32_FIND_DATA),
+        ('hFindFile', rwin32.HANDLE),
+        ('first_time', lltype.Bool),
+        ))
+    NULL_DIRP = lltype.nullptr(SCANDIRP.TO)
+
+
+    # must only be called with unicode!
+    def opendir(path):
+        if len(path) == 0:
+            path = u'.'
+        if path[-1] not in (u'\\', u'/', u':'):
+            mask = path + u'\\*.*'
+        else:
+            mask = path + u'*.*'
+        dirp = lltype.malloc(SCANDIRP.TO, flavor='raw')
+        hFindFile = win32traits.FindFirstFile(mask, dirp.filedata)
+        if hFindFile == rwin32.INVALID_HANDLE_VALUE:
+            error = rwin32.GetLastError_saved()
+            lltype.free(dirp, flavor='raw')
+            raise WindowsError(error,  "FindFirstFileW failed")
+        dirp.hFindFile = hFindFile
+        dirp.first_time = True
+        return dirp
+
+    def closedir(dirp):
+        if dirp.hFindFile != rwin32.INVALID_HANDLE_VALUE:
+            win32traits.FindClose(dirp.hFindFile)
+        lltype.free(dirp, flavor='raw')
+
+    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 NULL 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 lltype.nullptr(win32traits.WIN32_FIND_DATA)
+                raise WindowsError(error,  "FindNextFileW failed")
+        return dirp.filedata
+
+    def get_name_unicode(filedata):
+        return unicode_traits.charp2str(rffi.cast(unicode_traits.CCHARP,
+                                                  filedata.c_cFileName))
+
+    def get_known_type(filedata):
+        attr = filedata.c_dwFileAttributes
+        st_mode = rposix_stat.win32_attributes_to_mode(win32traits, attr)
+        return stat.S_IFMT(st_mode)
+
+    def get_inode(filedata):
+        return None
diff --git a/rpython/rlib/rwin32file.py b/rpython/rlib/rwin32file.py
--- a/rpython/rlib/rwin32file.py
+++ b/rpython/rlib/rwin32file.py
@@ -148,7 +148,7 @@
                                 save_err=rffi.RFFI_SAVE_LASTERROR)
         FindClose = external('FindClose',
                              [rwin32.HANDLE],
-                             rwin32.BOOL)
+                             rwin32.BOOL, releasegil=False)
 
         GetFileAttributes = external(
             'GetFileAttributes' + suffix,
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
@@ -1,3 +1,6 @@
+from hypothesis import given, strategies as st, assume
+import pytest
+
 from rpython.rtyper.test.test_llinterp import interpret
 from rpython.translator.c.test.test_genc import compile
 from rpython.tool.pytest.expecttest import ExpectTest
@@ -8,10 +11,10 @@
 import py
 
 def rposix_requires(funcname):
-    return py.test.mark.skipif(not hasattr(rposix, funcname),
+    return pytest.mark.skipif(not hasattr(rposix, funcname),
         reason="Requires rposix.%s()" % funcname)
 
-win_only = py.test.mark.skipif("os.name != 'nt'")
+win_only = pytest.mark.skipif("os.name != 'nt'")
 
 class TestPosixFunction:
     def test_access(self):
@@ -827,3 +830,61 @@
         rposix.lockf(fd, rposix.F_ULOCK, 4)
     finally:
         os.close(fd)
+
+def check_working_xattr():
+    fname = str(udir.join('xattr_test0.txt'))
+    with open(fname, 'wb'):
+        pass
+    try:
+        rposix.setxattr(fname, 'user.foo', '')
+    except OSError:
+        return False
+    else:
+        return True
+
+ at pytest.mark.skipif(not (hasattr(rposix, 'getxattr') and check_working_xattr()),
+    reason="Requires working rposix.getxattr()")
+ at given(
+    name=st.text(
+        alphabet=st.characters(min_codepoint=1), min_size=1, max_size=10),
+    value=st.binary(max_size=10),
+    follow_symlinks=st.booleans(), use_fd=st.booleans())
+def test_xattr(name, value, follow_symlinks, use_fd):
+    assume(follow_symlinks or not use_fd)
+    name = 'user.' + name.encode('utf-8')
+    fname = str(udir.join('xattr_test.txt'))
+    try:
+        os.unlink(fname)
+    except OSError:
+        pass
+    with open(fname, 'wb'):
+        pass
+    if use_fd:
+        file_id = os.open(fname, os.O_CREAT, 0777)
+        read, write, delete = rposix.fgetxattr, rposix.fsetxattr, rposix.fremovexattr
+        all_names = rposix.flistxattr
+    else:
+        file_id = fname
+        if follow_symlinks:
+            read, write, delete = rposix.getxattr, rposix.setxattr, rposix.removexattr
+            all_names = rposix.listxattr
+        else:
+            read = lambda *args, **kwargs: rposix.getxattr(*args, follow_symlinks=False, **kwargs)
+            write = lambda *args, **kwargs: rposix.setxattr(*args, follow_symlinks=False, **kwargs)
+            delete = lambda *args, **kwargs: rposix.removexattr(*args, follow_symlinks=False, **kwargs)
+            all_names = lambda *args, **kwargs: rposix.listxattr(*args, follow_symlinks=False, **kwargs)
+    try:
+        init_names = all_names(file_id)
+        with pytest.raises(OSError):
+            read(file_id, name)
+        write(file_id, name, value)
+        assert read(file_id, name) == value
+        assert set(all_names(file_id)) == set(init_names + [name])
+        assert '' not in all_names(file_id)
+        delete(file_id, name)
+        with pytest.raises(OSError):
+            read(file_id, name)
+        assert set(all_names(file_id)) == set(init_names)
+    finally:
+        if use_fd:
+            os.close(file_id)


More information about the pypy-commit mailing list