Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Open sidebar
Kirill Smelkov
cpython
Commits
424246fb
Commit
424246fb
authored
12 years ago
by
Antoine Pitrou
Browse files
Options
Download
Email Patches
Plain Diff
Issue #14082: shutil.copy2() now copies extended attributes, if possible.
Patch by Hynek Schlawack.
parent
4d688e32
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
141 additions
and
22 deletions
+141
-22
Doc/library/shutil.rst
Doc/library/shutil.rst
+3
-3
Lib/shutil.py
Lib/shutil.py
+31
-0
Lib/test/support.py
Lib/test/support.py
+29
-0
Lib/test/test_os.py
Lib/test/test_os.py
+1
-19
Lib/test/test_shutil.py
Lib/test/test_shutil.py
+74
-0
Misc/NEWS
Misc/NEWS
+3
-0
No files found.
Doc/library/shutil.rst
View file @
424246fb
...
...
@@ -102,14 +102,14 @@ Directory and files operations
.. function:: copy2(src, dst[, symlinks=False])
Similar to :func:`shutil.copy`, but metadata is copied as well -- in fact,
this is just :func:`shutil.copy` followed by :func:`copystat`. This is
Similar to :func:`shutil.copy`, but metadata is copied as well. This is
similar to the Unix command :program:`cp -p`. If *symlinks* is true,
symbolic links won't be followed but recreated instead -- this resembles
GNU's :program:`cp -P`.
.. versionchanged:: 3.3
Added *symlinks* argument.
Added *symlinks* argument, try to copy extended file system attributes
too (currently Linux only).
.. function:: ignore_patterns(\*patterns)
...
...
This diff is collapsed.
Click to expand it.
Lib/shutil.py
View file @
424246fb
...
...
@@ -166,6 +166,36 @@ def copystat(src, dst, symlinks=False):
else
:
raise
if
hasattr
(
os
,
'listxattr'
):
def
_copyxattr
(
src
,
dst
,
symlinks
=
False
):
"""Copy extended filesystem attributes from `src` to `dst`.
Overwrite existing attributes.
If the optional flag `symlinks` is set, symlinks won't be followed.
"""
if
symlinks
:
listxattr
=
os
.
llistxattr
removexattr
=
os
.
lremovexattr
setxattr
=
os
.
lsetxattr
getxattr
=
os
.
lgetxattr
else
:
listxattr
=
os
.
listxattr
removexattr
=
os
.
removexattr
setxattr
=
os
.
setxattr
getxattr
=
os
.
getxattr
for
attr
in
listxattr
(
src
):
try
:
setxattr
(
dst
,
attr
,
getxattr
(
src
,
attr
))
except
OSError
as
e
:
if
e
.
errno
not
in
(
errno
.
EPERM
,
errno
.
ENOTSUP
,
errno
.
ENODATA
):
raise
else
:
def
_copyxattr
(
*
args
,
**
kwargs
):
pass
def
copy
(
src
,
dst
,
symlinks
=
False
):
"""Copy data and mode bits ("cp src dst").
...
...
@@ -193,6 +223,7 @@ def copy2(src, dst, symlinks=False):
dst
=
os
.
path
.
join
(
dst
,
os
.
path
.
basename
(
src
))
copyfile
(
src
,
dst
,
symlinks
=
symlinks
)
copystat
(
src
,
dst
,
symlinks
=
symlinks
)
_copyxattr
(
src
,
dst
,
symlinks
=
symlinks
)
def
ignore_patterns
(
*
patterns
):
"""Function that can be used as copytree() ignore parameter.
...
...
This diff is collapsed.
Click to expand it.
Lib/test/support.py
View file @
424246fb
...
...
@@ -1696,6 +1696,35 @@ def skip_unless_symlink(test):
msg
=
"Requires functional symlink implementation"
return
test
if
ok
else
unittest
.
skip
(
msg
)(
test
)
_can_xattr
=
None
def
can_xattr
():
global
_can_xattr
if
_can_xattr
is
not
None
:
return
_can_xattr
if
not
hasattr
(
os
,
"setxattr"
):
can
=
False
else
:
try
:
with
open
(
TESTFN
,
"wb"
)
as
fp
:
try
:
os
.
fsetxattr
(
fp
.
fileno
(),
b
"user.test"
,
b
""
)
# Kernels < 2.6.39 don't respect setxattr flags.
kernel_version
=
platform
.
release
()
m
=
re
.
match
(
"2.6.(\d{1,2})"
,
kernel_version
)
can
=
m
is
None
or
int
(
m
.
group
(
1
))
>=
39
except
OSError
:
can
=
False
finally
:
unlink
(
TESTFN
)
_can_xattr
=
can
return
can
def
skip_unless_xattr
(
test
):
"""Skip decorator for tests that require functional extended attributes"""
ok
=
can_xattr
()
msg
=
"no non-broken extended attribute support"
return
test
if
ok
else
unittest
.
skip
(
msg
)(
test
)
def
patch
(
test_instance
,
object_to_patch
,
attr_name
,
new_value
):
"""Override 'object_to_patch'.'attr_name' with 'new_value'.
...
...
This diff is collapsed.
Click to expand it.
Lib/test/test_os.py
View file @
424246fb
...
...
@@ -1810,25 +1810,7 @@ class TestSendfile(unittest.TestCase):
raise
def
supports_extended_attributes
():
if
not
hasattr
(
os
,
"setxattr"
):
return
False
try
:
with
open
(
support
.
TESTFN
,
"wb"
)
as
fp
:
try
:
os
.
fsetxattr
(
fp
.
fileno
(),
b
"user.test"
,
b
""
)
except
OSError
:
return
False
finally
:
support
.
unlink
(
support
.
TESTFN
)
# Kernels < 2.6.39 don't respect setxattr flags.
kernel_version
=
platform
.
release
()
m
=
re
.
match
(
"2.6.(\d{1,2})"
,
kernel_version
)
return
m
is
None
or
int
(
m
.
group
(
1
))
>=
39
@
unittest
.
skipUnless
(
supports_extended_attributes
(),
"no non-broken extended attribute support"
)
@
support
.
skip_unless_xattr
class
ExtendedAttributeTests
(
unittest
.
TestCase
):
def
tearDown
(
self
):
...
...
This diff is collapsed.
Click to expand it.
Lib/test/test_shutil.py
View file @
424246fb
...
...
@@ -311,6 +311,67 @@ class TestShutil(unittest.TestCase):
finally
:
os
.
chflags
=
old_chflags
@
support
.
skip_unless_xattr
def
test_copyxattr
(
self
):
tmp_dir
=
self
.
mkdtemp
()
src
=
os
.
path
.
join
(
tmp_dir
,
'foo'
)
write_file
(
src
,
'foo'
)
dst
=
os
.
path
.
join
(
tmp_dir
,
'bar'
)
write_file
(
dst
,
'bar'
)
# no xattr == no problem
shutil
.
_copyxattr
(
src
,
dst
)
# common case
os
.
setxattr
(
src
,
'user.foo'
,
b
'42'
)
os
.
setxattr
(
src
,
'user.bar'
,
b
'43'
)
shutil
.
_copyxattr
(
src
,
dst
)
self
.
assertEqual
(
os
.
listxattr
(
src
),
os
.
listxattr
(
dst
))
self
.
assertEqual
(
os
.
getxattr
(
src
,
'user.foo'
),
os
.
getxattr
(
dst
,
'user.foo'
))
# check errors don't affect other attrs
os
.
remove
(
dst
)
write_file
(
dst
,
'bar'
)
os_error
=
OSError
(
errno
.
EPERM
,
'EPERM'
)
def
_raise_on_user_foo
(
fname
,
attr
,
val
):
if
attr
==
'user.foo'
:
raise
os_error
else
:
orig_setxattr
(
fname
,
attr
,
val
)
try
:
orig_setxattr
=
os
.
setxattr
os
.
setxattr
=
_raise_on_user_foo
shutil
.
_copyxattr
(
src
,
dst
)
self
.
assertEqual
([
'user.bar'
],
os
.
listxattr
(
dst
))
finally
:
os
.
setxattr
=
orig_setxattr
@
support
.
skip_unless_symlink
@
support
.
skip_unless_xattr
@
unittest
.
skipUnless
(
hasattr
(
os
,
'geteuid'
)
and
os
.
geteuid
()
==
0
,
'root privileges required'
)
def
test_copyxattr_symlinks
(
self
):
# On Linux, it's only possible to access non-user xattr for symlinks;
# which in turn require root privileges. This test should be expanded
# as soon as other platforms gain support for extended attributes.
tmp_dir
=
self
.
mkdtemp
()
src
=
os
.
path
.
join
(
tmp_dir
,
'foo'
)
src_link
=
os
.
path
.
join
(
tmp_dir
,
'baz'
)
write_file
(
src
,
'foo'
)
os
.
symlink
(
src
,
src_link
)
os
.
setxattr
(
src
,
'trusted.foo'
,
b
'42'
)
os
.
lsetxattr
(
src_link
,
'trusted.foo'
,
b
'43'
)
dst
=
os
.
path
.
join
(
tmp_dir
,
'bar'
)
dst_link
=
os
.
path
.
join
(
tmp_dir
,
'qux'
)
write_file
(
dst
,
'bar'
)
os
.
symlink
(
dst
,
dst_link
)
shutil
.
_copyxattr
(
src_link
,
dst_link
,
symlinks
=
True
)
self
.
assertEqual
(
os
.
lgetxattr
(
dst_link
,
'trusted.foo'
),
b
'43'
)
self
.
assertRaises
(
OSError
,
os
.
getxattr
,
dst
,
'trusted.foo'
)
shutil
.
_copyxattr
(
src_link
,
dst
,
symlinks
=
True
)
self
.
assertEqual
(
os
.
getxattr
(
dst
,
'trusted.foo'
),
b
'43'
)
@
support
.
skip_unless_symlink
def
test_copy_symlinks
(
self
):
tmp_dir
=
self
.
mkdtemp
()
...
...
@@ -369,6 +430,19 @@ class TestShutil(unittest.TestCase):
if
hasattr
(
os
,
'lchflags'
)
and
hasattr
(
src_link_stat
,
'st_flags'
):
self
.
assertEqual
(
src_link_stat
.
st_flags
,
dst_stat
.
st_flags
)
@
support
.
skip_unless_xattr
def
test_copy2_xattr
(
self
):
tmp_dir
=
self
.
mkdtemp
()
src
=
os
.
path
.
join
(
tmp_dir
,
'foo'
)
dst
=
os
.
path
.
join
(
tmp_dir
,
'bar'
)
write_file
(
src
,
'foo'
)
os
.
setxattr
(
src
,
'user.foo'
,
b
'42'
)
shutil
.
copy2
(
src
,
dst
)
self
.
assertEqual
(
os
.
getxattr
(
src
,
'user.foo'
),
os
.
getxattr
(
dst
,
'user.foo'
))
os
.
remove
(
dst
)
@
support
.
skip_unless_symlink
def
test_copyfile_symlinks
(
self
):
tmp_dir
=
self
.
mkdtemp
()
...
...
This diff is collapsed.
Click to expand it.
Misc/NEWS
View file @
424246fb
...
...
@@ -23,6 +23,9 @@ Core and Builtins
Library
-------
- Issue #14082: shutil.copy2() now copies extended attributes, if possible.
Patch by Hynek Schlawack.
- Issue #13959: Make importlib.abc.FileLoader.load_module()/get_filename() and
importlib.machinery.ExtensionFileLoader.load_module() have their single
argument be optional. Allows for the replacement (and thus deprecation) of
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment