[pypy-commit] pypy default: Merge branch 'rposix-for-3'
rlamy
pypy.commits at gmail.com
Wed Apr 6 11:03:08 EDT 2016
Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch:
Changeset: r83540:70a0c992daf3
Date: 2016-04-06 15:58 +0100
http://bitbucket.org/pypy/pypy/changeset/70a0c992daf3/
Log: Merge branch 'rposix-for-3'
diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py
--- a/rpython/rlib/rposix.py
+++ b/rpython/rlib/rposix.py
@@ -22,21 +22,6 @@
from rpython.rlib import rwin32
from rpython.rlib.rwin32file import make_win32_traits
-class CConfig:
- _compilation_info_ = ExternalCompilationInfo(
- includes=['sys/stat.h',
- 'unistd.h',
- 'fcntl.h'],
- )
- for _name in """fchdir fchmod fchmodat fchown fchownat fexecve fdopendir
- fpathconf fstat fstatat fstatvfs ftruncate futimens futimes
- futimesat linkat lchflags lchmod lchown lstat lutimes
- mkdirat mkfifoat mknodat openat readlinkat renameat
- symlinkat unlinkat utimensat""".split():
- locals()['HAVE_%s' % _name.upper()] = rffi_platform.Has(_name)
-cConfig = rffi_platform.configure(CConfig)
-globals().update(cConfig)
-
class CConstantErrno(CConstant):
# these accessors are used when calling get_errno() or set_errno()
@@ -618,14 +603,44 @@
config = rffi_platform.configure(CConfig)
DIRENT = config['DIRENT']
DIRENTP = lltype.Ptr(DIRENT)
- c_opendir = external('opendir', [rffi.CCHARP], DIRP,
- save_err=rffi.RFFI_SAVE_ERRNO)
+ c_opendir = external('opendir',
+ [rffi.CCHARP], DIRP, save_err=rffi.RFFI_SAVE_ERRNO)
+ c_fdopendir = external('fdopendir',
+ [rffi.INT], DIRP, save_err=rffi.RFFI_SAVE_ERRNO)
# XXX macro=True is hack to make sure we get the correct kind of
# dirent struct (which depends on defines)
c_readdir = external('readdir', [DIRP], DIRENTP,
macro=True, save_err=rffi.RFFI_FULL_ERRNO_ZERO)
c_closedir = external('closedir', [DIRP], rffi.INT)
+def _listdir(dirp):
+ result = []
+ while True:
+ direntp = c_readdir(dirp)
+ if not direntp:
+ error = get_saved_errno()
+ break
+ namep = rffi.cast(rffi.CCHARP, direntp.c_d_name)
+ name = rffi.charp2str(namep)
+ if name != '.' and name != '..':
+ result.append(name)
+ c_closedir(dirp)
+ if error:
+ raise OSError(error, "readdir failed")
+ return result
+
+def fdlistdir(dirfd):
+ """
+ Like listdir(), except that the directory is specified as an open
+ file descriptor.
+
+ Note: fdlistdir() closes the file descriptor.
+ """
+ dirp = c_fdopendir(dirfd)
+ if not dirp:
+ raise OSError(get_saved_errno(), "opendir failed")
+ return _listdir(dirp)
+
@replace_os_function('listdir')
@specialize.argtype(0)
def listdir(path):
@@ -634,20 +649,7 @@
dirp = c_opendir(path)
if not dirp:
raise OSError(get_saved_errno(), "opendir failed")
- result = []
- while True:
- direntp = c_readdir(dirp)
- if not direntp:
- error = get_saved_errno()
- break
- namep = rffi.cast(rffi.CCHARP, direntp.c_d_name)
- name = rffi.charp2str(namep)
- if name != '.' and name != '..':
- result.append(name)
- c_closedir(dirp)
- if error:
- raise OSError(error, "readdir failed")
- return result
+ return _listdir(dirp)
else: # _WIN32 case
traits = _preferred_traits(path)
win32traits = make_win32_traits(traits)
@@ -1739,3 +1741,259 @@
def getcontroller(self):
from rpython.rlib.rposix_environ import OsEnvironController
return OsEnvironController()
+
+
+# ____________________________________________________________
+# Support for f... and ...at families of POSIX functions
+
+class CConfig:
+ _compilation_info_ = ExternalCompilationInfo(
+ includes=['sys/stat.h',
+ 'unistd.h',
+ 'fcntl.h'],
+ )
+ for _name in """faccessat fchdir fchmod fchmodat fchown fchownat fexecve
+ fdopendir fpathconf fstat fstatat fstatvfs ftruncate
+ futimens futimes futimesat linkat chflags lchflags lchmod lchown
+ lstat lutimes mkdirat mkfifoat mknodat openat readlinkat renameat
+ symlinkat unlinkat utimensat""".split():
+ locals()['HAVE_%s' % _name.upper()] = rffi_platform.Has(_name)
+cConfig = rffi_platform.configure(CConfig)
+globals().update(cConfig)
+
+if not _WIN32:
+ class CConfig:
+ _compilation_info_ = ExternalCompilationInfo(
+ includes=['sys/stat.h',
+ 'unistd.h',
+ 'fcntl.h'],
+ )
+ AT_FDCWD = rffi_platform.DefinedConstantInteger('AT_FDCWD')
+ AT_SYMLINK_NOFOLLOW = rffi_platform.DefinedConstantInteger('AT_SYMLINK_NOFOLLOW')
+ AT_EACCESS = rffi_platform.DefinedConstantInteger('AT_EACCESS')
+ AT_REMOVEDIR = rffi_platform.DefinedConstantInteger('AT_REMOVEDIR')
+ AT_EMPTY_PATH = rffi_platform.DefinedConstantInteger('AT_EMPTY_PATH')
+ UTIME_NOW = rffi_platform.DefinedConstantInteger('UTIME_NOW')
+ UTIME_OMIT = rffi_platform.DefinedConstantInteger('UTIME_OMIT')
+ TIMESPEC = rffi_platform.Struct('struct timespec', [
+ ('tv_sec', rffi.TIME_T),
+ ('tv_nsec', rffi.LONG)])
+
+ cConfig = rffi_platform.configure(CConfig)
+ globals().update(cConfig)
+ TIMESPEC2P = rffi.CArrayPtr(TIMESPEC)
+
+if HAVE_FACCESSAT:
+ c_faccessat = external('faccessat',
+ [rffi.INT, rffi.CCHARP, rffi.INT, rffi.INT], rffi.INT)
+
+ def faccessat(pathname, mode, dir_fd=AT_FDCWD,
+ effective_ids=False, follow_symlinks=True):
+ """Thin wrapper around faccessat(2) with an interface simlar to
+ Python3's os.access().
+ """
+ flags = 0
+ if not follow_symlinks:
+ flags |= AT_SYMLINK_NOFOLLOW
+ if effective_ids:
+ flags |= AT_EACCESS
+ error = c_faccessat(dir_fd, pathname, mode, flags)
+ return error == 0
+
+if HAVE_FCHMODAT:
+ c_fchmodat = external('fchmodat',
+ [rffi.INT, rffi.CCHARP, rffi.INT, rffi.INT], rffi.INT,
+ save_err=rffi.RFFI_SAVE_ERRNO,)
+
+ def fchmodat(path, mode, dir_fd=AT_FDCWD, follow_symlinks=True):
+ if follow_symlinks:
+ flag = 0
+ else:
+ flag = AT_SYMLINK_NOFOLLOW
+ error = c_fchmodat(dir_fd, path, mode, flag)
+ handle_posix_error('fchmodat', error)
+
+if HAVE_FCHOWNAT:
+ c_fchownat = external('fchownat',
+ [rffi.INT, rffi.CCHARP, rffi.INT, rffi.INT, rffi.INT], rffi.INT,
+ save_err=rffi.RFFI_SAVE_ERRNO,)
+
+ def fchownat(path, owner, group, dir_fd=AT_FDCWD,
+ follow_symlinks=True, empty_path=False):
+ flag = 0
+ if not follow_symlinks:
+ flag |= AT_SYMLINK_NOFOLLOW
+ if empty_path:
+ flag |= AT_EMPTY_PATH
+ error = c_fchownat(dir_fd, path, owner, group, flag)
+ handle_posix_error('fchownat', error)
+
+if HAVE_FEXECVE:
+ c_fexecve = external('fexecve',
+ [rffi.INT, rffi.CCHARPP, rffi.CCHARPP], rffi.INT,
+ save_err=rffi.RFFI_SAVE_ERRNO)
+
+ def fexecve(fd, args, env):
+ envstrs = []
+ for item in env.iteritems():
+ envstr = "%s=%s" % item
+ envstrs.append(envstr)
+
+ # This list conversion already takes care of NUL bytes.
+ l_args = rffi.ll_liststr2charpp(args)
+ l_env = rffi.ll_liststr2charpp(envstrs)
+ c_fexecve(fd, l_args, l_env)
+
+ rffi.free_charpp(l_env)
+ rffi.free_charpp(l_args)
+ raise OSError(get_saved_errno(), "execve failed")
+
+if HAVE_LINKAT:
+ c_linkat = external('linkat',
+ [rffi.INT, rffi.CCHARP, rffi.INT, rffi.CCHARP, rffi.INT], rffi.INT)
+
+ def linkat(src, dst, src_dir_fd=AT_FDCWD, dst_dir_fd=AT_FDCWD,
+ follow_symlinks=True):
+ """Thin wrapper around linkat(2) with an interface similar to
+ Python3's os.link()
+ """
+ if follow_symlinks:
+ flag = 0
+ else:
+ flag = AT_SYMLINK_NOFOLLOW
+ error = c_linkat(src_dir_fd, src, dst_dir_fd, dst, flag)
+ handle_posix_error('linkat', error)
+
+if HAVE_FUTIMENS:
+ c_futimens = external('futimens', [rffi.INT, TIMESPEC2P], rffi.INT)
+
+ def futimens(fd, atime, atime_ns, mtime, mtime_ns):
+ l_times = lltype.malloc(TIMESPEC2P.TO, 2, flavor='raw')
+ rffi.setintfield(l_times[0], 'c_tv_sec', atime)
+ rffi.setintfield(l_times[0], 'c_tv_nsec', atime_ns)
+ rffi.setintfield(l_times[1], 'c_tv_sec', mtime)
+ rffi.setintfield(l_times[1], 'c_tv_nsec', mtime_ns)
+ error = c_futimens(fd, l_times)
+ lltype.free(l_times, flavor='raw')
+ handle_posix_error('futimens', error)
+
+if HAVE_UTIMENSAT:
+ c_utimensat = external('utimensat',
+ [rffi.INT, rffi.CCHARP, TIMESPEC2P, rffi.INT], rffi.INT)
+
+ def utimensat(pathname, atime, atime_ns, mtime, mtime_ns,
+ dir_fd=AT_FDCWD, follow_symlinks=True):
+ """Wrapper around utimensat(2)
+
+ To set access time to the current time, pass atime_ns=UTIME_NOW,
+ atime is then ignored.
+
+ To set modification time to the current time, pass mtime_ns=UTIME_NOW,
+ mtime is then ignored.
+ """
+ l_times = lltype.malloc(TIMESPEC2P.TO, 2, flavor='raw')
+ rffi.setintfield(l_times[0], 'c_tv_sec', atime)
+ rffi.setintfield(l_times[0], 'c_tv_nsec', atime_ns)
+ rffi.setintfield(l_times[1], 'c_tv_sec', mtime)
+ rffi.setintfield(l_times[1], 'c_tv_nsec', mtime_ns)
+ if follow_symlinks:
+ flag = 0
+ else:
+ flag = AT_SYMLINK_NOFOLLOW
+ error = c_utimensat(dir_fd, pathname, l_times, flag)
+ lltype.free(l_times, flavor='raw')
+ handle_posix_error('utimensat', error)
+
+if HAVE_MKDIRAT:
+ c_mkdirat = external('mkdirat',
+ [rffi.INT, rffi.CCHARP, rffi.INT], rffi.INT,
+ save_err=rffi.RFFI_SAVE_ERRNO)
+
+ def mkdirat(pathname, mode, dir_fd=AT_FDCWD):
+ error = c_mkdirat(dir_fd, pathname, mode)
+ handle_posix_error('mkdirat', error)
+
+if HAVE_UNLINKAT:
+ c_unlinkat = external('unlinkat',
+ [rffi.INT, rffi.CCHARP, rffi.INT], rffi.INT,
+ save_err=rffi.RFFI_SAVE_ERRNO)
+
+ def unlinkat(pathname, dir_fd=AT_FDCWD, removedir=False):
+ flag = AT_REMOVEDIR if removedir else 0
+ error = c_unlinkat(dir_fd, pathname, flag)
+ handle_posix_error('unlinkat', error)
+
+if HAVE_READLINKAT:
+ c_readlinkat = external(
+ 'readlinkat',
+ [rffi.INT, rffi.CCHARP, rffi.CCHARP, rffi.SIZE_T], rffi.SSIZE_T,
+ save_err=rffi.RFFI_SAVE_ERRNO)
+
+ def readlinkat(pathname, dir_fd=AT_FDCWD):
+ pathname = _as_bytes0(pathname)
+ bufsize = 1023
+ while True:
+ buf = lltype.malloc(rffi.CCHARP.TO, bufsize, flavor='raw')
+ res = widen(c_readlinkat(dir_fd, pathname, buf, bufsize))
+ if res < 0:
+ lltype.free(buf, flavor='raw')
+ error = get_saved_errno() # failed
+ raise OSError(error, "readlinkat failed")
+ elif res < bufsize:
+ break # ok
+ else:
+ # buf too small, try again with a larger buffer
+ lltype.free(buf, flavor='raw')
+ bufsize *= 4
+ # convert the result to a string
+ result = rffi.charp2strn(buf, res)
+ lltype.free(buf, flavor='raw')
+ return result
+
+if HAVE_RENAMEAT:
+ c_renameat = external(
+ 'renameat',
+ [rffi.INT, rffi.CCHARP, rffi.INT, rffi.CCHARP], rffi.INT,
+ save_err=rffi.RFFI_SAVE_ERRNO)
+
+ def renameat(src, dst, src_dir_fd=AT_FDCWD, dst_dir_fd=AT_FDCWD):
+ error = c_renameat(src_dir_fd, src, dst_dir_fd, dst)
+ handle_posix_error('renameat', error)
+
+
+if HAVE_SYMLINKAT:
+ c_symlinkat = external('symlinkat',
+ [rffi.CCHARP, rffi.INT, rffi.CCHARP], rffi.INT,
+ save_err=rffi.RFFI_SAVE_ERRNO)
+
+ def symlinkat(src, dst, dir_fd=AT_FDCWD):
+ error = c_symlinkat(src, dir_fd, dst)
+ handle_posix_error('symlinkat', error)
+
+if HAVE_OPENAT:
+ c_openat = external('openat',
+ [rffi.INT, rffi.CCHARP, rffi.INT, rffi.MODE_T], rffi.INT,
+ save_err=rffi.RFFI_SAVE_ERRNO)
+
+ @enforceargs(s_Str0, int, int, int, typecheck=False)
+ def openat(path, flags, mode, dir_fd=AT_FDCWD):
+ fd = c_openat(dir_fd, path, flags, mode)
+ return handle_posix_error('open', fd)
+
+if HAVE_MKFIFOAT:
+ c_mkfifoat = external('mkfifoat',
+ [rffi.INT, rffi.CCHARP, rffi.MODE_T], rffi.INT,
+ save_err=rffi.RFFI_SAVE_ERRNO)
+
+ def mkfifoat(path, mode, dir_fd=AT_FDCWD):
+ error = c_mkfifoat(dir_fd, path, mode)
+ handle_posix_error('mkfifoat', error)
+
+if HAVE_MKNODAT:
+ c_mknodat = external('mknodat',
+ [rffi.INT, rffi.CCHARP, rffi.MODE_T, rffi.INT], rffi.INT,
+ save_err=rffi.RFFI_SAVE_ERRNO)
+
+ def mknodat(path, mode, device, dir_fd=AT_FDCWD):
+ error = c_mknodat(dir_fd, path, mode, device)
+ handle_posix_error('mknodat', error)
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
@@ -7,6 +7,12 @@
import errno
import py
+def rposix_requires(funcname):
+ return py.test.mark.skipif(not hasattr(rposix, funcname),
+ reason="Requires rposix.%s()" % funcname)
+
+win_only = py.test.mark.skipif("os.name != 'nt'")
+
class TestPosixFunction:
def test_access(self):
filename = str(udir.join('test_access.txt'))
@@ -29,9 +35,8 @@
for value in times:
assert isinstance(value, float)
+ @py.test.mark.skipif("not hasattr(os, 'getlogin')")
def test_getlogin(self):
- if not hasattr(os, 'getlogin'):
- py.test.skip('posix specific function')
try:
expected = os.getlogin()
except OSError, e:
@@ -39,9 +44,8 @@
data = rposix.getlogin()
assert data == expected
+ @win_only
def test_utimes(self):
- if os.name != 'nt':
- py.test.skip('Windows specific feature')
# Windows support centiseconds
def f(fname, t1):
os.utime(fname, (t1, t1))
@@ -51,15 +55,12 @@
t1 = 1159195039.25
compile(f, (str, float))(str(fname), t1)
assert t1 == os.stat(str(fname)).st_mtime
- if sys.version_info < (2, 7):
- py.test.skip('requires Python 2.7')
t1 = 5000000000.0
compile(f, (str, float))(str(fname), t1)
assert t1 == os.stat(str(fname)).st_mtime
+ @win_only
def test__getfullpathname(self):
- if os.name != 'nt':
- py.test.skip('nt specific function')
posix = __import__(os.name)
sysdrv = os.getenv('SystemDrive', 'C:')
stuff = sysdrv + 'stuff'
@@ -99,11 +100,25 @@
def test_mkdir(self):
filename = str(udir.join('test_mkdir.dir'))
rposix.mkdir(filename, 0)
- exc = py.test.raises(OSError, rposix.mkdir, filename, 0)
- assert exc.value.errno == errno.EEXIST
+ with py.test.raises(OSError) as excinfo:
+ rposix.mkdir(filename, 0)
+ assert excinfo.value.errno == errno.EEXIST
if sys.platform == 'win32':
assert exc.type is WindowsError
+ @rposix_requires('mkdirat')
+ def test_mkdirat(self):
+ relpath = 'test_mkdirat.dir'
+ filename = str(udir.join(relpath))
+ dirfd = os.open(os.path.dirname(filename), os.O_RDONLY)
+ try:
+ rposix.mkdirat(relpath, 0, dir_fd=dirfd)
+ with py.test.raises(OSError) as excinfo:
+ rposix.mkdirat(relpath, 0, dir_fd=dirfd)
+ assert excinfo.value.errno == errno.EEXIST
+ finally:
+ os.close(dirfd)
+
def test_strerror(self):
assert rposix.strerror(2) == os.strerror(2)
@@ -116,10 +131,8 @@
os.unlink(filename)
+ @py.test.mark.skipif("os.name != 'posix'")
def test_execve(self):
- if os.name != 'posix':
- py.test.skip('posix specific function')
-
EXECVE_ENV = {"foo": "bar", "baz": "quux"}
def run_execve(program, args=None, env=None, do_path_lookup=False):
@@ -258,11 +271,8 @@
assert rposix.isatty(-1) is False
+ at py.test.mark.skipif("not hasattr(os, 'ttyname')")
class TestOsExpect(ExpectTest):
- def setup_class(cls):
- if not hasattr(os, 'ttyname'):
- py.test.skip("no ttyname")
-
def test_ttyname(self):
def f():
import os
@@ -426,9 +436,8 @@
except Exception:
pass
+ @win_only
def test_is_valid_fd(self):
- if os.name != 'nt':
- py.test.skip('relevant for windows only')
assert rposix.is_valid_fd(0) == 1
fid = open(str(udir.join('validate_test.txt')), 'w')
fd = fid.fileno()
@@ -448,6 +457,59 @@
def _get_filename(self):
return str(udir.join('test_open_ascii'))
+ @rposix_requires('openat')
+ def test_openat(self):
+ def f(dirfd):
+ try:
+ fd = rposix.openat('test_open_ascii', os.O_RDONLY, 0777, dirfd)
+ try:
+ text = os.read(fd, 50)
+ return text
+ finally:
+ os.close(fd)
+ except OSError:
+ return ''
+
+ dirfd = os.open(os.path.dirname(self.ufilename), os.O_RDONLY)
+ try:
+ assert ll_to_string(interpret(f, [dirfd])) == "test"
+ finally:
+ os.close(dirfd)
+
+ @rposix_requires('unlinkat')
+ def test_unlinkat(self):
+ def f(dirfd):
+ return rposix.unlinkat('test_open_ascii', dir_fd=dirfd)
+
+ dirfd = os.open(os.path.dirname(self.ufilename), os.O_RDONLY)
+ try:
+ interpret(f, [dirfd])
+ finally:
+ os.close(dirfd)
+ assert not os.path.exists(self.ufilename)
+
+ def test_utimensat(self):
+ def f(dirfd):
+ return rposix.utimensat('test_open_ascii',
+ 0, rposix.UTIME_NOW, 0, rposix.UTIME_NOW, dir_fd=dirfd)
+
+ dirfd = os.open(os.path.dirname(self.ufilename), os.O_RDONLY)
+ try:
+ interpret(f, [dirfd]) # does not crash
+ finally:
+ os.close(dirfd)
+
+ def test_fchmodat(self):
+ def f(dirfd):
+ return rposix.fchmodat('test_open_ascii', 0777, dirfd)
+
+ dirfd = os.open(os.path.dirname(self.ufilename), os.O_RDONLY)
+ try:
+ interpret(f, [dirfd]) # does not crash
+ finally:
+ os.close(dirfd)
+
+
class TestPosixUnicode(BasePosixUnicodeOrAscii):
def _get_filename(self):
return (unicode(udir.join('test_open')) +
@@ -465,3 +527,30 @@
os.open('/tmp/t', 0, 0)
os.open(u'/tmp/t', 0, 0)
compile(f, ())
+
+
+def test_fdlistdir(tmpdir):
+ tmpdir.join('file').write('text')
+ dirfd = os.open(str(tmpdir), os.O_RDONLY)
+ result = rposix.fdlistdir(dirfd)
+ # Note: fdlistdir() always closes dirfd
+ assert result == ['file']
+
+def test_symlinkat(tmpdir):
+ tmpdir.join('file').write('text')
+ dirfd = os.open(str(tmpdir), os.O_RDONLY)
+ try:
+ rposix.symlinkat('file', 'link', dir_fd=dirfd)
+ assert os.readlink(str(tmpdir.join('link'))) == 'file'
+ finally:
+ os.close(dirfd)
+
+def test_renameat(tmpdir):
+ tmpdir.join('file').write('text')
+ dirfd = os.open(str(tmpdir), os.O_RDONLY)
+ try:
+ rposix.renameat('file', 'file2', src_dir_fd=dirfd, dst_dir_fd=dirfd)
+ finally:
+ os.close(dirfd)
+ assert tmpdir.join('file').check(exists=False)
+ assert tmpdir.join('file2').check(exists=True)
More information about the pypy-commit
mailing list