shutil.copy()
doesn't offer any options to track the progress, no. At most you could monitor the size of the destination file (using os.*
functions on the target filename).
The alternative would be to implement your own copy function. The implementation is really quite simple; shutil.copy()
is basically a shutil.copyfile()
plus shutil.copymode()
call; shutil.copyfile()
in turn delegates the real work to shutil.copyfileobj()
* (links to the Python 3.8.2 source code).
Implementing your own shutil.copyfileobj()
to include progress should be trivial; inject support for a callback function to report inform your program each time another block has copied:
import os
import shutil
def copyfileobj(fsrc, fdst, callback, length=0):
try:
# check for optimisation opportunity
if "b" in fsrc.mode and "b" in fdst.mode and fsrc.readinto:
return _copyfileobj_readinto(fsrc, fdst, callback, length)
except AttributeError:
# one or both file objects do not support a .mode or .readinto attribute
pass
if not length:
length = shutil.COPY_BUFSIZE
fsrc_read = fsrc.read
fdst_write = fdst.write
copied = 0
while True:
buf = fsrc_read(length)
if not buf:
break
fdst_write(buf)
copied += len(buf)
callback(copied)
# differs from shutil.COPY_BUFSIZE on platforms != Windows
READINTO_BUFSIZE = 1024 * 1024
def _copyfileobj_readinto(fsrc, fdst, callback, length=0):
"""readinto()/memoryview() based variant of copyfileobj().
*fsrc* must support readinto() method and both files must be
open in binary mode.
"""
fsrc_readinto = fsrc.readinto
fdst_write = fdst.write
if not length:
try:
file_size = os.stat(fsrc.fileno()).st_size
except OSError:
file_size = READINTO_BUFSIZE
length = min(file_size, READINTO_BUFSIZE)
copied = 0
with memoryview(bytearray(length)) as mv:
while True:
n = fsrc_readinto(mv)
if not n:
break
elif n < length:
with mv[:n] as smv:
fdst.write(smv)
else:
fdst_write(mv)
copied += n
callback(copied)
and then, in the callback, compare the copied
size with the file size.
Note that in the above implementation we look for the opportunity to use a different method for binary files, where you can use fileobj.readinto()
and a memoryview
object to avoid redundant data copying; see the original _copyfileobj_readinto()
implementation for comparison.
* footnote to … delegates the real work to shutil.copyfileobj()
: As of Python 3.8, on OS X and Linux the copyfile()
implementation delegates file copying to OS-specific, optimised system calls (to fcopyfile()
and sendfile()
, respectively) but these calls have no hooks whatsoever to track progress, and so if you need to track progress you'd want to disable these delegation paths anyway. On Windows the code uses the aforementioned _copyfileobj_readinto()
function.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…