Author: Ronan Lamy <ronan.l...@gmail.com>
Branch: py3k
Changeset: r83781:33f8f738c7a4
Date: 2016-04-19 23:47 +0100
http://bitbucket.org/pypy/pypy/changeset/33f8f738c7a4/

Log:    Merged in follow_symlinks (pull request #428)

        Finish implementing the 3.3 extensions to existing os functions.

diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -1638,9 +1638,14 @@
         return fsdecode(space, w_obj)
 
     def fsencode_w(self, w_obj):
+        from rpython.rlib import rstring
         if self.isinstance_w(w_obj, self.w_unicode):
             w_obj = self.fsencode(w_obj)
-        return self.bytes0_w(w_obj)
+        result = self.bufferstr_w(w_obj, self.BUF_FULL_RO)
+        if '\x00' in result:
+            raise oefmt(self.w_TypeError,
+                        "argument must be a string without NUL characters")
+        return rstring.assert_str0(result)
 
     def fsdecode_w(self, w_obj):
         if self.isinstance_w(w_obj, self.w_bytes):
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
@@ -64,7 +64,7 @@
         self.w_obj = w_obj
 
     def as_bytes(self):
-        return self.space.bytes0_w(self.w_obj)
+        return self.space.fsencode_w(self.w_obj)
 
     def as_unicode(self):
         return self.space.fsdecode_w(self.w_obj)
@@ -83,7 +83,7 @@
             fname = FileEncoder(space, w_fname)
             return func(fname, *args)
         else:
-            fname = space.bytes0_w(w_fname)
+            fname = space.fsencode_w(w_fname)
             return func(fname, *args)
     return dispatch
 
@@ -112,6 +112,57 @@
                 return func(fname1, fname2, *args)
     return dispatch
 
+@specialize.arg(0)
+def call_rposix(func, path, *args):
+    """Call a function that takes a filesystem path as its first argument"""
+    if path.as_unicode is not None:
+        return func(path.as_unicode, *args)
+    else:
+        path_b = path.as_bytes
+        assert path_b is not None
+        return func(path.as_bytes, *args)
+
+
+class Path(object):
+    _immutable_fields_ = ['as_fd', 'as_bytes', 'as_unicode', 'w_path']
+
+    def __init__(self, fd, bytes, unicode, w_path):
+        self.as_fd = fd
+        self.as_bytes = bytes
+        self.as_unicode = unicode
+        self.w_path = w_path
+
+@specialize.arg(2)
+def _unwrap_path(space, w_value, allow_fd=True):
+    if space.is_none(w_value):
+        raise oefmt(space.w_TypeError,
+            "can't specify None for path argument")
+    if _WIN32:
+        try:
+            path_u = space.unicode_w(w_value)
+            return Path(-1, None, path_u, w_value)
+        except OperationError:
+            pass
+    try:
+        path_b = space.fsencode_w(w_value)
+        return Path(-1, path_b, None, w_value)
+    except OperationError:
+        if allow_fd:
+            fd = unwrap_fd(space, w_value, "string, bytes or integer")
+            return Path(fd, None, None, w_value)
+    raise oefmt(space.w_TypeError, "illegal type for path parameter")
+
+class _PathOrFd(Unwrapper):
+    def unwrap(self, space, w_value):
+        return _unwrap_path(space, w_value, allow_fd=True)
+
+class _JustPath(Unwrapper):
+    def unwrap(self, space, w_value):
+        return _unwrap_path(space, w_value, allow_fd=False)
+
+def path_or_fd(allow_fd=True):
+    return _PathOrFd if allow_fd else _JustPath
+
 
 if hasattr(rposix, 'AT_FDCWD'):
     DEFAULT_DIR_FD = rposix.AT_FDCWD
@@ -119,8 +170,20 @@
     DEFAULT_DIR_FD = -100
 DIR_FD_AVAILABLE = False
 
-def unwrap_fd(space, w_value):
-    return space.c_int_w(w_value)
+@specialize.arg(2)
+def unwrap_fd(space, w_value, allowed_types='integer'):
+    try:
+        result = space.c_int_w(w_value)
+    except OperationError as e:
+        if not e.match(space, space.w_OverflowError):
+            raise oefmt(space.w_TypeError,
+                "argument should be %s, not %T", allowed_types, w_value)
+        else:
+            raise
+    if result == -1:
+        # -1 is used as sentinel value for not a fd
+        raise oefmt(space.w_ValueError, "invalid file descriptor: -1")
+    return result
 
 def _unwrap_dirfd(space, w_value):
     if space.is_none(w_value):
@@ -354,8 +417,11 @@
     else:
         return build_stat_result(space, st)
 
-@unwrap_spec(dir_fd=DirFD(available=False), follow_symlinks=kwonly(bool))
-def stat(space, w_path, dir_fd=DEFAULT_DIR_FD, follow_symlinks=True):
+@unwrap_spec(
+    path=path_or_fd(allow_fd=True),
+    dir_fd=DirFD(rposix.HAVE_FSTATAT),
+    follow_symlinks=kwonly(bool))
+def stat(space, path, dir_fd=DEFAULT_DIR_FD, follow_symlinks=True):
     """stat(path, *, dir_fd=None, follow_symlinks=True) -> stat result
 
 Perform a stat system call on the given path.
@@ -371,27 +437,43 @@
   link points to.
 It is an error to use dir_fd or follow_symlinks when specifying path as
   an open file descriptor."""
+    return do_stat(space, "stat", path, dir_fd, follow_symlinks)
+
+@specialize.arg(1)
+def do_stat(space, funcname, path, dir_fd, follow_symlinks):
+    """Common implementation for stat() and lstat()"""
     try:
-        st = dispatch_filename(rposix_stat.stat, 0,
-                               allow_fd_fn=rposix_stat.fstat)(space, w_path)
-    except OSError, e:
-        raise wrap_oserror2(space, e, w_path)
+        if path.as_fd != -1:
+            if dir_fd != DEFAULT_DIR_FD:
+                raise oefmt(space.w_ValueError,
+                    "%s: can't specify both dir_fd and fd", funcname)
+            if not follow_symlinks:
+                raise oefmt(space.w_ValueError,
+                    "%s: cannot use fd and follow_symlinks together", funcname)
+            st = rposix_stat.fstat(path.as_fd)
+        elif follow_symlinks and dir_fd == DEFAULT_DIR_FD:
+            st = call_rposix(rposix_stat.stat, path)
+        elif not follow_symlinks and dir_fd == DEFAULT_DIR_FD:
+            st = call_rposix(rposix_stat.lstat, path)
+        elif rposix.HAVE_FSTATAT:
+            st = call_rposix(rposix_stat.fstatat, path, dir_fd, 
follow_symlinks)
+        else:
+            raise oefmt(space.w_NotImplementedError,
+                "%s: unsupported argument combination", funcname)
+    except OSError as e:
+        raise wrap_oserror2(space, e, path.w_path)
     else:
         return build_stat_result(space, st)
 
-@unwrap_spec(dir_fd=DirFD(available=False))
-def lstat(space, w_path, dir_fd=DEFAULT_DIR_FD):
+@unwrap_spec(
+    path=path_or_fd(allow_fd=False),
+    dir_fd=DirFD(rposix.HAVE_FSTATAT))
+def lstat(space, path, dir_fd=DEFAULT_DIR_FD):
     """lstat(path, *, dir_fd=None) -> stat result
 
 Like stat(), but do not follow symbolic links.
 Equivalent to stat(path, follow_symlinks=False)."""
-
-    try:
-        st = dispatch_filename(rposix_stat.lstat)(space, w_path)
-    except OSError, e:
-        raise wrap_oserror2(space, e, w_path)
-    else:
-        return build_stat_result(space, st)
+    return do_stat(space, "lstat", path, dir_fd, False)
 
 class StatState(object):
     def __init__(self, space):
@@ -432,7 +514,9 @@
 On some platforms, path may also be specified as an open file descriptor.
   If this functionality is unavailable, using it raises an exception."""
     try:
-        st = dispatch_filename(rposix_stat.statvfs)(space, w_path)
+        st = dispatch_filename(
+            rposix_stat.statvfs,
+            allow_fd_fn=rposix_stat.fstatvfs)(space, w_path)
     except OSError as e:
         raise wrap_oserror2(space, e, w_path)
     else:
@@ -1054,8 +1138,10 @@
         raise wrap_oserror(space, e)
 
 
-@unwrap_spec(dir_fd=DirFD(rposix.HAVE_READLINKAT))
-def readlink(space, w_path, dir_fd=DEFAULT_DIR_FD):
+@unwrap_spec(
+    path=path_or_fd(allow_fd=False),
+    dir_fd=DirFD(rposix.HAVE_READLINKAT))
+def readlink(space, path, dir_fd=DEFAULT_DIR_FD):
     """readlink(path, *, dir_fd=None) -> path
 
 Return a string representing the path to which the symbolic link points.
@@ -1064,20 +1150,15 @@
   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."""
-    is_unicode = space.isinstance_w(w_path, space.w_unicode)
-    if is_unicode:
-        path = space.fsencode_w(w_path)
-    else:
-        path = space.bytes0_w(w_path)
     try:
         if dir_fd == DEFAULT_DIR_FD:
-            result = rposix.readlink(path)
+            result = call_rposix(rposix.readlink, path)
         else:
-            result = rposix.readlinkat(path, dir_fd)
-    except OSError, e:
-        raise wrap_oserror2(space, e, w_path)
+            result = call_rposix(rposix.readlinkat, path, dir_fd)
+    except OSError as e:
+        raise wrap_oserror2(space, e, path.w_path)
     w_result = space.wrapbytes(result)
-    if is_unicode:
+    if space.isinstance_w(path.w_path, space.w_unicode):
         return space.fsdecode(w_result)
     return w_result
 
@@ -1258,9 +1339,11 @@
     return space.wrap(ret)
 
 
-@unwrap_spec(w_times=WrappedDefault(None), w_ns=kwonly(WrappedDefault(None)),
+@unwrap_spec(
+    path=path_or_fd(allow_fd=rposix.HAVE_FUTIMENS),
+    w_times=WrappedDefault(None), w_ns=kwonly(WrappedDefault(None)),
     dir_fd=DirFD(rposix.HAVE_UTIMENSAT), follow_symlinks=kwonly(bool))
-def utime(space, w_path, w_times, w_ns, dir_fd=DEFAULT_DIR_FD, 
follow_symlinks=True):
+def utime(space, path, w_times, w_ns, dir_fd=DEFAULT_DIR_FD, 
follow_symlinks=True):
     """utime(path, times=None, *, ns=None, dir_fd=None, follow_symlinks=True)
 
 Set the access and modified time of path.
@@ -1290,47 +1373,11 @@
             not space.is_w(w_ns, space.w_None)):
         raise oefmt(space.w_ValueError,
             "utime: you may specify either 'times' or 'ns' but not both")
-
-    if rposix.HAVE_UTIMENSAT:
-        path = space.fsencode_w(w_path)
-        try:
-            _utimensat(space, path, w_times, w_ns, dir_fd, follow_symlinks)
-            return
-        except OSError, e:
-            raise wrap_oserror2(space, e, w_path)
-
-    if not follow_symlinks:
-        raise argument_unavailable(space, "utime", "follow_symlinks")
-
-    if not space.is_w(w_ns, space.w_None):
-        raise oefmt(space.w_NotImplementedError,
-            "utime: 'ns' unsupported on this platform on PyPy")
-    if space.is_w(w_times, space.w_None):
-        try:
-            dispatch_filename(rposix.utime, 1)(space, w_path, None)
-            return
-        except OSError, e:
-            raise wrap_oserror2(space, e, w_path)
-    try:
-        msg = "utime() arg 2 must be a tuple (atime, mtime) or None"
-        args_w = space.fixedview(w_times)
-        if len(args_w) != 2:
-            raise OperationError(space.w_TypeError, space.wrap(msg))
-        actime = space.float_w(args_w[0], allow_conversion=False)
-        modtime = space.float_w(args_w[1], allow_conversion=False)
-        dispatch_filename(rposix.utime, 2)(space, w_path, (actime, modtime))
-    except OSError, e:
-        raise wrap_oserror2(space, e, w_path)
-    except OperationError, e:
-        if not e.match(space, space.w_TypeError):
-            raise
-        raise OperationError(space.w_TypeError, space.wrap(msg))
-
-
-def _utimensat(space, path, w_times, w_ns, dir_fd, follow_symlinks):
+    utime_now = False
     if space.is_w(w_times, space.w_None) and space.is_w(w_ns, space.w_None):
         atime_s = mtime_s = 0
-        atime_ns = mtime_ns = rposix.UTIME_NOW
+        atime_ns = mtime_ns = 0
+        utime_now = True
     elif not space.is_w(w_times, space.w_None):
         times_w = space.fixedview(w_times)
         if len(times_w) != 2:
@@ -1346,9 +1393,75 @@
         atime_s, atime_ns = convert_ns(space, args_w[0])
         mtime_s, mtime_ns = convert_ns(space, args_w[1])
 
-    rposix.utimensat(
-        path, atime_s, atime_ns, mtime_s, mtime_ns,
-        dir_fd=dir_fd, follow_symlinks=follow_symlinks)
+    if path.as_fd != -1:
+        if dir_fd != DEFAULT_DIR_FD:
+            raise oefmt(space.w_ValueError,
+                        "utime: can't specify both dir_fd and fd")
+        if not follow_symlinks:
+            raise oefmt(space.w_ValueError,
+                        "utime: cannot use fd and follow_symlinks together")
+        if utime_now:
+            atime_ns = mtime_ns = rposix.UTIME_NOW
+        try:
+            rposix.futimens(path.as_fd, atime_s, atime_ns, mtime_s, mtime_ns)
+            return
+        except OSError as e:
+            # CPython's Modules/posixmodule.c::posix_utime() has this comment:
+            # /* Avoid putting the file name into the error here,
+            #    as that may confuse the user into believing that
+            #    something is wrong with the file, when it also
+            #    could be the time stamp that gives a problem. */
+            # so we use wrap_oserror() instead of wrap_oserror2() here
+            raise wrap_oserror(space, e)
+
+    if rposix.HAVE_UTIMENSAT:
+        path_b = path.as_bytes
+        if path_b is None:
+            raise oefmt(space.w_NotImplementedError,
+                        "utime: unsupported value for 'path'")
+        try:
+            if utime_now:
+                rposix.utimensat(
+                    path_b, 0, rposix.UTIME_NOW, 0, rposix.UTIME_NOW,
+                    dir_fd=dir_fd, follow_symlinks=follow_symlinks)
+            else:
+                rposix.utimensat(
+                    path_b, atime_s, atime_ns, mtime_s, mtime_ns,
+                    dir_fd=dir_fd, follow_symlinks=follow_symlinks)
+            return
+        except OSError as e:
+            # see comment above
+            raise wrap_oserror(space, e)
+
+    if not follow_symlinks:
+        raise argument_unavailable(space, "utime", "follow_symlinks")
+
+    if not space.is_w(w_ns, space.w_None):
+        raise oefmt(space.w_NotImplementedError,
+            "utime: 'ns' unsupported on this platform on PyPy")
+    if utime_now:
+        try:
+            call_rposix(rposix.utime, path, None)
+        except OSError as e:
+            # see comment above
+            raise wrap_oserror(space, e)
+    try:
+        msg = "utime() arg 2 must be a tuple (atime, mtime) or None"
+        args_w = space.fixedview(w_times)
+        if len(args_w) != 2:
+            raise OperationError(space.w_TypeError, space.wrap(msg))
+        actime = space.float_w(args_w[0], allow_conversion=False)
+        modtime = space.float_w(args_w[1], allow_conversion=False)
+    except OperationError, e:
+        if not e.match(space, space.w_TypeError):
+            raise
+        raise OperationError(space.w_TypeError, space.wrap(msg))
+    try:
+        call_rposix(rposix.utime, path, (actime, modtime))
+    except OSError as e:
+        # see comment above
+        raise wrap_oserror(space, e)
+
 
 def convert_seconds(space, w_time):
     if space.isinstance_w(w_time, space.w_float):
@@ -1745,13 +1858,19 @@
         raise wrap_oserror(space, e)
     return space.wrap(res)
 
-@unwrap_spec(path='str0')
+@unwrap_spec(path=path_or_fd(allow_fd=hasattr(os, 'fpathconf')))
 def pathconf(space, path, w_name):
     num = confname_w(space, w_name, os.pathconf_names)
-    try:
-        res = os.pathconf(path, num)
-    except OSError, e:
-        raise wrap_oserror(space, e)
+    if path.as_fd != -1:
+        try:
+            res = os.fpathconf(path.as_fd, num)
+        except OSError, e:
+            raise wrap_oserror(space, e)
+    else:
+        try:
+            res = os.pathconf(path.as_bytes, num)
+        except OSError, e:
+            raise wrap_oserror2(space, e, path.w_path)
     return space.wrap(res)
 
 def confstr(space, w_name):
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
@@ -174,6 +174,10 @@
         import stat
         st = self.posix.stat(".")
         assert stat.S_ISDIR(st.st_mode)
+        st = self.posix.stat(b".")
+        assert stat.S_ISDIR(st.st_mode)
+        st = self.posix.stat(bytearray(b"."))
+        assert stat.S_ISDIR(st.st_mode)
         st = self.posix.lstat(".")
         assert stat.S_ISDIR(st.st_mode)
 
@@ -185,6 +189,12 @@
             assert exc.value.errno == errno.ENOENT
             assert exc.value.filename == "nonexistentdir/nonexistentfile"
 
+        excinfo = raises(TypeError, self.posix.stat, None)
+        assert "can't specify None" in str(excinfo.value)
+        excinfo = raises(TypeError, self.posix.stat, 2.)
+        assert "should be string, bytes or integer, not float" in 
str(excinfo.value)
+        raises(ValueError, self.posix.stat, -1)
+
     if hasattr(__import__(os.name), "statvfs"):
         def test_statvfs(self):
             st = self.posix.statvfs(".")
@@ -250,7 +260,7 @@
             try:
                 self.posix.utime('qowieuqw/oeiu', arg)
             except OSError as e:
-                assert e.filename == 'qowieuqw/oeiu'
+                pass
             else:
                 assert 0
 
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to