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
9b9c708d
Commit
9b9c708d
authored
Dec 29, 2011
by
Benjamin Peterson
Browse files
Options
Browse Files
Download
Plain Diff
merge heads
parents
3ab85886
78091e63
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
333 additions
and
38 deletions
+333
-38
Doc/library/shutil.rst
Doc/library/shutil.rst
+34
-12
Lib/shutil.py
Lib/shutil.py
+75
-26
Lib/test/test_shutil.py
Lib/test/test_shutil.py
+219
-0
Misc/NEWS
Misc/NEWS
+5
-0
No files found.
Doc/library/shutil.rst
View file @
9b9c708d
...
@@ -45,7 +45,7 @@ Directory and files operations
...
@@ -45,7 +45,7 @@ Directory and files operations
be copied.
be copied.
.. function:: copyfile(src, dst)
.. function:: copyfile(src, dst
[, symlinks=False]
)
Copy the contents (no metadata) of the file named *src* to a file named *dst*.
Copy the contents (no metadata) of the file named *src* to a file named *dst*.
*dst* must be the complete target file name; look at :func:`copy` for a copy that
*dst* must be the complete target file name; look at :func:`copy` for a copy that
...
@@ -56,37 +56,56 @@ Directory and files operations
...
@@ -56,37 +56,56 @@ Directory and files operations
such as character or block devices and pipes cannot be copied with this
such as character or block devices and pipes cannot be copied with this
function. *src* and *dst* are path names given as strings.
function. *src* and *dst* are path names given as strings.
If *symlinks* is true and *src* is a symbolic link, a new symbolic link will
be created instead of copying the file *src* points to.
.. versionchanged:: 3.3
.. versionchanged:: 3.3
:exc:`IOError` used to be raised instead of :exc:`OSError`.
:exc:`IOError` used to be raised instead of :exc:`OSError`.
Added *symlinks* argument.
.. function:: copymode(src, dst)
.. function:: copymode(src, dst
[, symlinks=False]
)
Copy the permission bits from *src* to *dst*. The file contents, owner, and
Copy the permission bits from *src* to *dst*. The file contents, owner, and
group are unaffected. *src* and *dst* are path names given as strings.
group are unaffected. *src* and *dst* are path names given as strings. If
*symlinks* is true, *src* a symbolic link and the operating system supports
modes for symbolic links (for example BSD-based ones), the mode of the link
will be copied.
.. versionchanged:: 3.3
Added *symlinks* argument.
.. function:: copystat(src, dst)
.. function:: copystat(src, dst
[, symlinks=False]
)
Copy the permission bits, last access time, last modification time, and flags
Copy the permission bits, last access time, last modification time, and flags
from *src* to *dst*. The file contents, owner, and group are unaffected. *src*
from *src* to *dst*. The file contents, owner, and group are unaffected. *src*
and *dst* are path names given as strings.
and *dst* are path names given as strings. If *src* and *dst* are both
symbolic links and *symlinks* true, the stats of the link will be copied as
far as the platform allows.
.. versionchanged:: 3.3
Added *symlinks* argument.
.. function:: copy(src, dst)
.. function:: copy(src, dst
[, symlinks=False])
)
Copy the file *src* to the file or directory *dst*. If *dst* is a directory, a
Copy the file *src* to the file or directory *dst*. If *dst* is a directory, a
file with the same basename as *src* is created (or overwritten) in the
file with the same basename as *src* is created (or overwritten) in the
directory specified. Permission bits are copied. *src* and *dst* are path
directory specified. Permission bits are copied. *src* and *dst* are path
names given as strings.
names given as strings. 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.
.. function:: copy2(src, dst)
.. function:: copy2(src, dst
[, symlinks=False]
)
Similar to :func:`copy`, but metadata is copied as well -- in fact, this is just
Similar to :func:`copy`, but metadata is copied as well -- in fact, this is just
:func:`copy` followed by :func:`copystat`. This is similar to the
:func:`copy` followed by :func:`copystat`. This is similar to the
Unix command :program:`cp -p`.
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.
.. function:: ignore_patterns(\*patterns)
.. function:: ignore_patterns(\*patterns)
...
@@ -104,9 +123,9 @@ Directory and files operations
...
@@ -104,9 +123,9 @@ Directory and files operations
:func:`copy2`.
:func:`copy2`.
If *symlinks* is true, symbolic links in the source tree are represented as
If *symlinks* is true, symbolic links in the source tree are represented as
symbolic links in the new tree
, but the metadata of the original links is NOT
symbolic links in the new tree
and the metadata of the original links will
copied; if false or omitted, the contents and metadata of the linked file
s
be copied as far as the platform allows; if false or omitted, the content
s
are copied to the new tree.
a
nd metadata of the linked files a
re copied to the new tree.
When *symlinks* is false, if the file pointed by the symlink doesn't
When *symlinks* is false, if the file pointed by the symlink doesn't
exist, a exception will be added in the list of errors raised in
exist, a exception will be added in the list of errors raised in
...
@@ -140,6 +159,9 @@ Directory and files operations
...
@@ -140,6 +159,9 @@ Directory and files operations
Added the *ignore_dangling_symlinks* argument to silent dangling symlinks
Added the *ignore_dangling_symlinks* argument to silent dangling symlinks
errors when *symlinks* is false.
errors when *symlinks* is false.
.. versionchanged:: 3.3
Copy metadata when *symlinks* is false.
.. function:: rmtree(path, ignore_errors=False, onerror=None)
.. function:: rmtree(path, ignore_errors=False, onerror=None)
...
...
Lib/shutil.py
View file @
9b9c708d
...
@@ -82,8 +82,13 @@ def _samefile(src, dst):
...
@@ -82,8 +82,13 @@ def _samefile(src, dst):
return
(
os
.
path
.
normcase
(
os
.
path
.
abspath
(
src
))
==
return
(
os
.
path
.
normcase
(
os
.
path
.
abspath
(
src
))
==
os
.
path
.
normcase
(
os
.
path
.
abspath
(
dst
)))
os
.
path
.
normcase
(
os
.
path
.
abspath
(
dst
)))
def
copyfile
(
src
,
dst
):
def
copyfile
(
src
,
dst
,
symlinks
=
False
):
"""Copy data from src to dst"""
"""Copy data from src to dst.
If optional flag `symlinks` is set and `src` is a symbolic link, a new
symlink will be created instead of copying the file it points to.
"""
if
_samefile
(
src
,
dst
):
if
_samefile
(
src
,
dst
):
raise
Error
(
"`%s` and `%s` are the same file"
%
(
src
,
dst
))
raise
Error
(
"`%s` and `%s` are the same file"
%
(
src
,
dst
))
...
@@ -98,54 +103,94 @@ def copyfile(src, dst):
...
@@ -98,54 +103,94 @@ def copyfile(src, dst):
if
stat
.
S_ISFIFO
(
st
.
st_mode
):
if
stat
.
S_ISFIFO
(
st
.
st_mode
):
raise
SpecialFileError
(
"`%s` is a named pipe"
%
fn
)
raise
SpecialFileError
(
"`%s` is a named pipe"
%
fn
)
if
symlinks
and
os
.
path
.
islink
(
src
):
os
.
symlink
(
os
.
readlink
(
src
),
dst
)
else
:
with
open
(
src
,
'rb'
)
as
fsrc
:
with
open
(
src
,
'rb'
)
as
fsrc
:
with
open
(
dst
,
'wb'
)
as
fdst
:
with
open
(
dst
,
'wb'
)
as
fdst
:
copyfileobj
(
fsrc
,
fdst
)
copyfileobj
(
fsrc
,
fdst
)
def
copymode
(
src
,
dst
):
def
copymode
(
src
,
dst
,
symlinks
=
False
):
"""Copy mode bits from src to dst"""
"""Copy mode bits from src to dst.
if
hasattr
(
os
,
'chmod'
):
st
=
os
.
stat
(
src
)
If the optional flag `symlinks` is set, symlinks aren't followed if and
mode
=
stat
.
S_IMODE
(
st
.
st_mode
)
only if both `src` and `dst` are symlinks. If `lchmod` isn't available (eg.
os
.
chmod
(
dst
,
mode
)
Linux), in these cases, this method does nothing.
"""
if
symlinks
and
os
.
path
.
islink
(
src
)
and
os
.
path
.
islink
(
dst
):
if
hasattr
(
os
,
'lchmod'
):
stat_func
,
chmod_func
=
os
.
lstat
,
os
.
lchmod
else
:
return
elif
hasattr
(
os
,
'chmod'
):
stat_func
,
chmod_func
=
os
.
stat
,
os
.
chmod
else
:
return
def
copystat
(
src
,
dst
):
st
=
stat_func
(
src
)
"""Copy all stat info (mode bits, atime, mtime, flags) from src to dst"""
chmod_func
(
dst
,
stat
.
S_IMODE
(
st
.
st_mode
))
st
=
os
.
stat
(
src
)
def
copystat
(
src
,
dst
,
symlinks
=
False
):
"""Copy all stat info (mode bits, atime, mtime, flags) from src to dst.
If the optional flag `symlinks` is set, symlinks aren't followed if and
only if both `src` and `dst` are symlinks.
"""
def
_nop
(
*
args
):
pass
if
symlinks
and
os
.
path
.
islink
(
src
)
and
os
.
path
.
islink
(
dst
):
stat_func
=
os
.
lstat
utime_func
=
os
.
lutimes
if
hasattr
(
os
,
'lutimes'
)
else
_nop
chmod_func
=
os
.
lchmod
if
hasattr
(
os
,
'lchmod'
)
else
_nop
chflags_func
=
os
.
lchflags
if
hasattr
(
os
,
'lchflags'
)
else
_nop
else
:
stat_func
=
os
.
stat
utime_func
=
os
.
utime
if
hasattr
(
os
,
'utime'
)
else
_nop
chmod_func
=
os
.
chmod
if
hasattr
(
os
,
'chmod'
)
else
_nop
chflags_func
=
os
.
chflags
if
hasattr
(
os
,
'chflags'
)
else
_nop
st
=
stat_func
(
src
)
mode
=
stat
.
S_IMODE
(
st
.
st_mode
)
mode
=
stat
.
S_IMODE
(
st
.
st_mode
)
if
hasattr
(
os
,
'utime'
):
utime_func
(
dst
,
(
st
.
st_atime
,
st
.
st_mtime
))
os
.
utime
(
dst
,
(
st
.
st_atime
,
st
.
st_mtime
))
chmod_func
(
dst
,
mode
)
if
hasattr
(
os
,
'chmod'
):
if
hasattr
(
st
,
'st_flags'
):
os
.
chmod
(
dst
,
mode
)
if
hasattr
(
os
,
'chflags'
)
and
hasattr
(
st
,
'st_flags'
):
try
:
try
:
os
.
chflags
(
dst
,
st
.
st_flags
)
chflags_func
(
dst
,
st
.
st_flags
)
except
OSError
as
why
:
except
OSError
as
why
:
if
(
not
hasattr
(
errno
,
'EOPNOTSUPP'
)
or
if
(
not
hasattr
(
errno
,
'EOPNOTSUPP'
)
or
why
.
errno
!=
errno
.
EOPNOTSUPP
):
why
.
errno
!=
errno
.
EOPNOTSUPP
):
raise
raise
def
copy
(
src
,
dst
):
def
copy
(
src
,
dst
,
symlinks
=
False
):
"""Copy data and mode bits ("cp src dst").
"""Copy data and mode bits ("cp src dst").
The destination may be a directory.
The destination may be a directory.
If the optional flag `symlinks` is set, symlinks won't be followed. This
resembles GNU's "cp -P src dst".
"""
"""
if
os
.
path
.
isdir
(
dst
):
if
os
.
path
.
isdir
(
dst
):
dst
=
os
.
path
.
join
(
dst
,
os
.
path
.
basename
(
src
))
dst
=
os
.
path
.
join
(
dst
,
os
.
path
.
basename
(
src
))
copyfile
(
src
,
dst
)
copyfile
(
src
,
dst
,
symlinks
=
symlinks
)
copymode
(
src
,
dst
)
copymode
(
src
,
dst
,
symlinks
=
symlinks
)
def
copy2
(
src
,
dst
):
def
copy2
(
src
,
dst
,
symlinks
=
False
):
"""Copy data and all stat info ("cp -p src dst").
"""Copy data and all stat info ("cp -p src dst").
The destination may be a directory.
The destination may be a directory.
If the optional flag `symlinks` is set, symlinks won't be followed. This
resembles GNU's "cp -P src dst".
"""
"""
if
os
.
path
.
isdir
(
dst
):
if
os
.
path
.
isdir
(
dst
):
dst
=
os
.
path
.
join
(
dst
,
os
.
path
.
basename
(
src
))
dst
=
os
.
path
.
join
(
dst
,
os
.
path
.
basename
(
src
))
copyfile
(
src
,
dst
)
copyfile
(
src
,
dst
,
symlinks
=
symlinks
)
copystat
(
src
,
dst
)
copystat
(
src
,
dst
,
symlinks
=
symlinks
)
def
ignore_patterns
(
*
patterns
):
def
ignore_patterns
(
*
patterns
):
"""Function that can be used as copytree() ignore parameter.
"""Function that can be used as copytree() ignore parameter.
...
@@ -212,7 +257,11 @@ def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2,
...
@@ -212,7 +257,11 @@ def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2,
if
os
.
path
.
islink
(
srcname
):
if
os
.
path
.
islink
(
srcname
):
linkto
=
os
.
readlink
(
srcname
)
linkto
=
os
.
readlink
(
srcname
)
if
symlinks
:
if
symlinks
:
# We can't just leave it to `copy_function` because legacy
# code with a custom `copy_function` may rely on copytree
# doing the right thing.
os
.
symlink
(
linkto
,
dstname
)
os
.
symlink
(
linkto
,
dstname
)
copystat
(
srcname
,
dstname
,
symlinks
=
symlinks
)
else
:
else
:
# ignore dangling symlink if the flag is on
# ignore dangling symlink if the flag is on
if
not
os
.
path
.
exists
(
linkto
)
and
ignore_dangling_symlinks
:
if
not
os
.
path
.
exists
(
linkto
)
and
ignore_dangling_symlinks
:
...
...
Lib/test/test_shutil.py
View file @
9b9c708d
...
@@ -164,6 +164,197 @@ class TestShutil(unittest.TestCase):
...
@@ -164,6 +164,197 @@ class TestShutil(unittest.TestCase):
self
.
assertTrue
(
issubclass
(
exc
[
0
],
OSError
))
self
.
assertTrue
(
issubclass
(
exc
[
0
],
OSError
))
self
.
errorState
=
2
self
.
errorState
=
2
@
unittest
.
skipUnless
(
hasattr
(
os
,
'chmod'
),
'requires os.chmod'
)
@
support
.
skip_unless_symlink
def
test_copymode_follow_symlinks
(
self
):
tmp_dir
=
self
.
mkdtemp
()
src
=
os
.
path
.
join
(
tmp_dir
,
'foo'
)
dst
=
os
.
path
.
join
(
tmp_dir
,
'bar'
)
src_link
=
os
.
path
.
join
(
tmp_dir
,
'baz'
)
dst_link
=
os
.
path
.
join
(
tmp_dir
,
'quux'
)
write_file
(
src
,
'foo'
)
write_file
(
dst
,
'foo'
)
os
.
symlink
(
src
,
src_link
)
os
.
symlink
(
dst
,
dst_link
)
os
.
chmod
(
src
,
stat
.
S_IRWXU
|
stat
.
S_IRWXG
)
# file to file
os
.
chmod
(
dst
,
stat
.
S_IRWXO
)
self
.
assertNotEqual
(
os
.
stat
(
src
).
st_mode
,
os
.
stat
(
dst
).
st_mode
)
shutil
.
copymode
(
src
,
dst
)
self
.
assertEqual
(
os
.
stat
(
src
).
st_mode
,
os
.
stat
(
dst
).
st_mode
)
# follow src link
os
.
chmod
(
dst
,
stat
.
S_IRWXO
)
shutil
.
copymode
(
src_link
,
dst
)
self
.
assertEqual
(
os
.
stat
(
src
).
st_mode
,
os
.
stat
(
dst
).
st_mode
)
# follow dst link
os
.
chmod
(
dst
,
stat
.
S_IRWXO
)
shutil
.
copymode
(
src
,
dst_link
)
self
.
assertEqual
(
os
.
stat
(
src
).
st_mode
,
os
.
stat
(
dst
).
st_mode
)
# follow both links
os
.
chmod
(
dst
,
stat
.
S_IRWXO
)
shutil
.
copymode
(
src_link
,
dst
)
self
.
assertEqual
(
os
.
stat
(
src
).
st_mode
,
os
.
stat
(
dst
).
st_mode
)
@
unittest
.
skipUnless
(
hasattr
(
os
,
'lchmod'
),
'requires os.lchmod'
)
@
support
.
skip_unless_symlink
def
test_copymode_symlink_to_symlink
(
self
):
tmp_dir
=
self
.
mkdtemp
()
src
=
os
.
path
.
join
(
tmp_dir
,
'foo'
)
dst
=
os
.
path
.
join
(
tmp_dir
,
'bar'
)
src_link
=
os
.
path
.
join
(
tmp_dir
,
'baz'
)
dst_link
=
os
.
path
.
join
(
tmp_dir
,
'quux'
)
write_file
(
src
,
'foo'
)
write_file
(
dst
,
'foo'
)
os
.
symlink
(
src
,
src_link
)
os
.
symlink
(
dst
,
dst_link
)
os
.
chmod
(
src
,
stat
.
S_IRWXU
|
stat
.
S_IRWXG
)
os
.
chmod
(
dst
,
stat
.
S_IRWXU
)
os
.
lchmod
(
src_link
,
stat
.
S_IRWXO
|
stat
.
S_IRWXG
)
# link to link
os
.
lchmod
(
dst_link
,
stat
.
S_IRWXO
)
shutil
.
copymode
(
src_link
,
dst_link
,
symlinks
=
True
)
self
.
assertEqual
(
os
.
lstat
(
src_link
).
st_mode
,
os
.
lstat
(
dst_link
).
st_mode
)
self
.
assertNotEqual
(
os
.
stat
(
src
).
st_mode
,
os
.
stat
(
dst
).
st_mode
)
# src link - use chmod
os
.
lchmod
(
dst_link
,
stat
.
S_IRWXO
)
shutil
.
copymode
(
src_link
,
dst
,
symlinks
=
True
)
self
.
assertEqual
(
os
.
stat
(
src
).
st_mode
,
os
.
stat
(
dst
).
st_mode
)
# dst link - use chmod
os
.
lchmod
(
dst_link
,
stat
.
S_IRWXO
)
shutil
.
copymode
(
src
,
dst_link
,
symlinks
=
True
)
self
.
assertEqual
(
os
.
stat
(
src
).
st_mode
,
os
.
stat
(
dst
).
st_mode
)
@
unittest
.
skipIf
(
hasattr
(
os
,
'lchmod'
),
'requires os.lchmod to be missing'
)
@
support
.
skip_unless_symlink
def
test_copymode_symlink_to_symlink_wo_lchmod
(
self
):
tmp_dir
=
self
.
mkdtemp
()
src
=
os
.
path
.
join
(
tmp_dir
,
'foo'
)
dst
=
os
.
path
.
join
(
tmp_dir
,
'bar'
)
src_link
=
os
.
path
.
join
(
tmp_dir
,
'baz'
)
dst_link
=
os
.
path
.
join
(
tmp_dir
,
'quux'
)
write_file
(
src
,
'foo'
)
write_file
(
dst
,
'foo'
)
os
.
symlink
(
src
,
src_link
)
os
.
symlink
(
dst
,
dst_link
)
shutil
.
copymode
(
src_link
,
dst_link
,
symlinks
=
True
)
# silent fail
@
support
.
skip_unless_symlink
def
test_copystat_symlinks
(
self
):
tmp_dir
=
self
.
mkdtemp
()
src
=
os
.
path
.
join
(
tmp_dir
,
'foo'
)
dst
=
os
.
path
.
join
(
tmp_dir
,
'bar'
)
src_link
=
os
.
path
.
join
(
tmp_dir
,
'baz'
)
dst_link
=
os
.
path
.
join
(
tmp_dir
,
'qux'
)
write_file
(
src
,
'foo'
)
src_stat
=
os
.
stat
(
src
)
os
.
utime
(
src
,
(
src_stat
.
st_atime
,
src_stat
.
st_mtime
-
42.0
))
# ensure different mtimes
write_file
(
dst
,
'bar'
)
self
.
assertNotEqual
(
os
.
stat
(
src
).
st_mtime
,
os
.
stat
(
dst
).
st_mtime
)
os
.
symlink
(
src
,
src_link
)
os
.
symlink
(
dst
,
dst_link
)
if
hasattr
(
os
,
'lchmod'
):
os
.
lchmod
(
src_link
,
stat
.
S_IRWXO
)
if
hasattr
(
os
,
'lchflags'
)
and
hasattr
(
stat
,
'UF_NODUMP'
):
os
.
lchflags
(
src_link
,
stat
.
UF_NODUMP
)
src_link_stat
=
os
.
lstat
(
src_link
)
# follow
if
hasattr
(
os
,
'lchmod'
):
shutil
.
copystat
(
src_link
,
dst_link
,
symlinks
=
False
)
self
.
assertNotEqual
(
src_link_stat
.
st_mode
,
os
.
stat
(
dst
).
st_mode
)
# don't follow
shutil
.
copystat
(
src_link
,
dst_link
,
symlinks
=
True
)
dst_link_stat
=
os
.
lstat
(
dst_link
)
if
hasattr
(
os
,
'lutimes'
):
for
attr
in
'st_atime'
,
'st_mtime'
:
# The modification times may be truncated in the new file.
self
.
assertLessEqual
(
getattr
(
src_link_stat
,
attr
),
getattr
(
dst_link_stat
,
attr
)
+
1
)
if
hasattr
(
os
,
'lchmod'
):
self
.
assertEqual
(
src_link_stat
.
st_mode
,
dst_link_stat
.
st_mode
)
if
hasattr
(
os
,
'lchflags'
)
and
hasattr
(
src_link_stat
,
'st_flags'
):
self
.
assertEqual
(
src_link_stat
.
st_flags
,
dst_link_stat
.
st_flags
)
# tell to follow but dst is not a link
shutil
.
copystat
(
src_link
,
dst
,
symlinks
=
True
)
self
.
assertTrue
(
abs
(
os
.
stat
(
src
).
st_mtime
-
os
.
stat
(
dst
).
st_mtime
)
<
00000.1
)
@
support
.
skip_unless_symlink
def
test_copy_symlinks
(
self
):
tmp_dir
=
self
.
mkdtemp
()
src
=
os
.
path
.
join
(
tmp_dir
,
'foo'
)
dst
=
os
.
path
.
join
(
tmp_dir
,
'bar'
)
src_link
=
os
.
path
.
join
(
tmp_dir
,
'baz'
)
write_file
(
src
,
'foo'
)
os
.
symlink
(
src
,
src_link
)
if
hasattr
(
os
,
'lchmod'
):
os
.
lchmod
(
src_link
,
stat
.
S_IRWXU
|
stat
.
S_IRWXO
)
# don't follow
shutil
.
copy
(
src_link
,
dst
,
symlinks
=
False
)
self
.
assertFalse
(
os
.
path
.
islink
(
dst
))
self
.
assertEqual
(
read_file
(
src
),
read_file
(
dst
))
os
.
remove
(
dst
)
# follow
shutil
.
copy
(
src_link
,
dst
,
symlinks
=
True
)
self
.
assertTrue
(
os
.
path
.
islink
(
dst
))
self
.
assertEqual
(
os
.
readlink
(
dst
),
os
.
readlink
(
src_link
))
if
hasattr
(
os
,
'lchmod'
):
self
.
assertEqual
(
os
.
lstat
(
src_link
).
st_mode
,
os
.
lstat
(
dst
).
st_mode
)
@
support
.
skip_unless_symlink
def
test_copy2_symlinks
(
self
):
tmp_dir
=
self
.
mkdtemp
()
src
=
os
.
path
.
join
(
tmp_dir
,
'foo'
)
dst
=
os
.
path
.
join
(
tmp_dir
,
'bar'
)
src_link
=
os
.
path
.
join
(
tmp_dir
,
'baz'
)
write_file
(
src
,
'foo'
)
os
.
symlink
(
src
,
src_link
)
if
hasattr
(
os
,
'lchmod'
):
os
.
lchmod
(
src_link
,
stat
.
S_IRWXU
|
stat
.
S_IRWXO
)
if
hasattr
(
os
,
'lchflags'
)
and
hasattr
(
stat
,
'UF_NODUMP'
):
os
.
lchflags
(
src_link
,
stat
.
UF_NODUMP
)
src_stat
=
os
.
stat
(
src
)
src_link_stat
=
os
.
lstat
(
src_link
)
# follow
shutil
.
copy2
(
src_link
,
dst
,
symlinks
=
False
)
self
.
assertFalse
(
os
.
path
.
islink
(
dst
))
self
.
assertEqual
(
read_file
(
src
),
read_file
(
dst
))
os
.
remove
(
dst
)
# don't follow
shutil
.
copy2
(
src_link
,
dst
,
symlinks
=
True
)
self
.
assertTrue
(
os
.
path
.
islink
(
dst
))
self
.
assertEqual
(
os
.
readlink
(
dst
),
os
.
readlink
(
src_link
))
dst_stat
=
os
.
lstat
(
dst
)
if
hasattr
(
os
,
'lutimes'
):
for
attr
in
'st_atime'
,
'st_mtime'
:
# The modification times may be truncated in the new file.
self
.
assertLessEqual
(
getattr
(
src_link_stat
,
attr
),
getattr
(
dst_stat
,
attr
)
+
1
)
if
hasattr
(
os
,
'lchmod'
):
self
.
assertEqual
(
src_link_stat
.
st_mode
,
dst_stat
.
st_mode
)
self
.
assertNotEqual
(
src_stat
.
st_mode
,
dst_stat
.
st_mode
)
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_symlink
def
test_copyfile_symlinks
(
self
):
tmp_dir
=
self
.
mkdtemp
()
src
=
os
.
path
.
join
(
tmp_dir
,
'src'
)
dst
=
os
.
path
.
join
(
tmp_dir
,
'dst'
)
dst_link
=
os
.
path
.
join
(
tmp_dir
,
'dst_link'
)
link
=
os
.
path
.
join
(
tmp_dir
,
'link'
)
write_file
(
src
,
'foo'
)
os
.
symlink
(
src
,
link
)
# don't follow
shutil
.
copyfile
(
link
,
dst_link
,
symlinks
=
True
)
self
.
assertTrue
(
os
.
path
.
islink
(
dst_link
))
self
.
assertEqual
(
os
.
readlink
(
link
),
os
.
readlink
(
dst_link
))
# follow
shutil
.
copyfile
(
link
,
dst
)
self
.
assertFalse
(
os
.
path
.
islink
(
dst
))
def
test_rmtree_dont_delete_file
(
self
):
def
test_rmtree_dont_delete_file
(
self
):
# When called on a file instead of a directory, don't delete it.
# When called on a file instead of a directory, don't delete it.
handle
,
path
=
tempfile
.
mkstemp
()
handle
,
path
=
tempfile
.
mkstemp
()
...
@@ -190,6 +381,34 @@ class TestShutil(unittest.TestCase):
...
@@ -190,6 +381,34 @@ class TestShutil(unittest.TestCase):
actual
=
read_file
((
dst_dir
,
'test_dir'
,
'test.txt'
))
actual
=
read_file
((
dst_dir
,
'test_dir'
,
'test.txt'
))
self
.
assertEqual
(
actual
,
'456'
)
self
.
assertEqual
(
actual
,
'456'
)
@
support
.
skip_unless_symlink
def
test_copytree_symlinks
(
self
):
tmp_dir
=
self
.
mkdtemp
()
src_dir
=
os
.
path
.
join
(
tmp_dir
,
'src'
)
dst_dir
=
os
.
path
.
join
(
tmp_dir
,
'dst'
)
sub_dir
=
os
.
path
.
join
(
src_dir
,
'sub'
)
os
.
mkdir
(
src_dir
)
os
.
mkdir
(
sub_dir
)
write_file
((
src_dir
,
'file.txt'
),
'foo'
)
src_link
=
os
.
path
.
join
(
sub_dir
,
'link'
)
dst_link
=
os
.
path
.
join
(
dst_dir
,
'sub/link'
)
os
.
symlink
(
os
.
path
.
join
(
src_dir
,
'file.txt'
),
src_link
)
if
hasattr
(
os
,
'lchmod'
):
os
.
lchmod
(
src_link
,
stat
.
S_IRWXU
|
stat
.
S_IRWXO
)
if
hasattr
(
os
,
'lchflags'
)
and
hasattr
(
stat
,
'UF_NODUMP'
):
os
.
lchflags
(
src_link
,
stat
.
UF_NODUMP
)
src_stat
=
os
.
lstat
(
src_link
)
shutil
.
copytree
(
src_dir
,
dst_dir
,
symlinks
=
True
)
self
.
assertTrue
(
os
.
path
.
islink
(
os
.
path
.
join
(
dst_dir
,
'sub'
,
'link'
)))
self
.
assertEqual
(
os
.
readlink
(
os
.
path
.
join
(
dst_dir
,
'sub'
,
'link'
)),
os
.
path
.
join
(
src_dir
,
'file.txt'
))
dst_stat
=
os
.
lstat
(
dst_link
)
if
hasattr
(
os
,
'lchmod'
):
self
.
assertEqual
(
dst_stat
.
st_mode
,
src_stat
.
st_mode
)
if
hasattr
(
os
,
'lchflags'
):
self
.
assertEqual
(
dst_stat
.
st_flags
,
src_stat
.
st_flags
)
def
test_copytree_with_exclude
(
self
):
def
test_copytree_with_exclude
(
self
):
# creating data
# creating data
join
=
os
.
path
.
join
join
=
os
.
path
.
join
...
...
Misc/NEWS
View file @
9b9c708d
...
@@ -422,6 +422,11 @@ Core and Builtins
...
@@ -422,6 +422,11 @@ Core and Builtins
Library
Library
-------
-------
-
Issue
#
12715
:
Add
an
optional
symlinks
argument
to
shutil
functions
(
copyfile
,
copymode
,
copystat
,
copy
,
copy2
).
When
that
parameter
is
true
,
symlinks
aren
't dereferenced and the operation instead acts on the
symlink itself (or creates one, if relevant). Patch by Hynek Schlawack.
- Add a flags parameter to select.epoll.
- Add a flags parameter to select.epoll.
- Issue #12798: Updated the mimetypes documentation.
- Issue #12798: Updated the mimetypes documentation.
...
...
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