Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
424246fb
Commit
424246fb
authored
May 12, 2012
by
Antoine Pitrou
Browse files
Options
Browse Files
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)
...
...
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.
...
...
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'.
...
...
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
):
...
...
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
()
...
...
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
...
...
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