Author: Ronan Lamy <[email protected]>
Branch: py3.5
Changeset: r93498:fa25c452dbb5
Date: 2017-12-19 17:24 +0000
http://bitbucket.org/pypy/pypy/changeset/fa25c452dbb5/
Log: Merged in py3.5-xattr (pull request #586)
Implement posix.*xattr functions
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
@@ -229,7 +229,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 +246,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):
@@ -2283,7 +2283,9 @@
This function will not follow symbolic links.
Equivalent to chflags(path, flags, follow_symlinks=False)."""
-def getxattr():
+@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 +2294,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, eintr_retry=False)
+ else:
+ try:
+ result = rposix.getxattr(path.as_bytes, attribute.as_bytes,
+ follow_symlinks=follow_symlinks)
+ except OSError as e:
+ raise wrap_oserror(space, e, eintr_retry=False)
+ return space.newbytes(result)
-def setxattr():
+@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 +2322,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, eintr_retry=False)
+ else:
+ try:
+ rposix.setxattr(path.as_bytes, attribute.as_bytes, value,
+ follow_symlinks=follow_symlinks)
+ except OSError as e:
+ raise wrap_oserror(space, e, eintr_retry=False)
-def removexattr():
+@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 +2349,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, eintr_retry=False)
+ else:
+ try:
+ rposix.removexattr(path.as_bytes, attribute.as_bytes,
+ follow_symlinks=follow_symlinks)
+ except OSError as e:
+ raise wrap_oserror(space, e, eintr_retry=False)
-def listxattr():
+
+@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 +2376,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, eintr_retry=False)
+ return space.newlist([space.newbytes(attr) for attr in result])
have_functions = []
@@ -2449,8 +2517,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 +2532,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 +2545,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/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
@@ -1350,7 +1350,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 +1441,24 @@
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)
+ raises(OSError, os.getxattr, self.path, 'user.test')
+ 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, '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 + [b'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 +1513,7 @@
res = os.system(cmd)
assert res == 0
+
@py.test.fixture
def check_fsencoding(space, pytestconfig):
if pytestconfig.getvalue('runappdirect'):
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/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
+
[email protected](not (hasattr(rposix, 'getxattr') and
check_working_xattr()),
+ reason="Requires working rposix.getxattr()")
+@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)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit