I am working on below python copy utility that should work on windows & linux, But I am looking for a more efficient approach that could optimize my I/O correction, as my target location is network dependent as well... I have computed the utility execution time factor in the code.
#!/usr/bin/python
"""
Pythonic implementation of multi-target copy (Parallel Copy).
"""
import Queue
import threading
import time
import os, os.path
import sys
import shutil, hashlib
exitFlag = 0
class myThread(threading.Thread):
def __init__(self, threadID, name, queue, idFlag):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.queue = queue
self.idFlag = idFlag
def run(self):
if debugFlag:
print "**** Starting %s" % self.name
process_data(self.name, self.queue, self.idFlag)
if debugFlag:
print "**** Ending %s" % self.name
def copy_files_concurrantly(src_filename, target_filename, idFlag):
"""
"""
sha512_hash = hashlib.sha512()
src_filepath = os.path.join(os.getcwd(), src_filename)
try:
with open(src_filepath, "r") as sf:
statinfo = os.stat(src_filepath)
block_size = 100 * (2 ** 20) # Magic number: 100 * 1MB blocks
nb_blocks = (statinfo.st_size / block_size) + 1
cnt_blocks = 0
l = len(src_filepath.split('\\'))
target_file_path = os.path.join(target_filename, src_filepath.split('\\')[l - 1])
while True:
block = sf.read(block_size)
sha512_hash.update(block) # Todo a blockwise copy
if not block: break
cnt_blocks = cnt_blocks + 1
with open(target_filename, "a") as tf:
tf.write(block)
tf.close()
print "\nCopying %s (to) %s" % (src_filepath, target_filename)
sf.close()
except IOError:
print "Error: cant find or read '%s' file" % (src_filename)
def delete_folder(target_path):
"""
Deletes a folder, if it already exists
@param target_path: Relative path of the directory to delete
"""
if (os.path.exists(target_path) or os.path.isdir(target_path)):
print "Directory %s already exists.. deleting..." % target_path
try:
shutil.rmtree(target_path)
except OSError:
os.remove(target_path)
def process_data(threadName, queue, idFlag):
while not exitFlag:
if not workQueue.empty():
(sfile, dfile) = queue.get()
copy_files_concurrantly(sfile, dfile, idFlag)
time.sleep(0.5)
def queue_mt(argv):
"""
Implementation to do multi-target copy (recursive) of directories
@param argv: Arguments passed at command-line
"""
desc = "Recursively copies the files to destination directories."
syntax = "\nUsage:\n c4.py cp -L -R <src-dir> <target-dir>\n c4.py cp -L -R <src-dir> -t <target-dir1> <target-dir2>..."
options = "\n\n cp\t\t\tCopy operation to perform.\n -L\t\t\tDisplay running logs.(Optional)\n -R\t\t\tRecursively copy source files to target.\n <src-dir>\t\tSpecify source directory to copy.\n <target-dir>\tSpecify target directory to copy."
win = "\n\n Windows: c4.py cp -R d:\src-dir\*.* e:\dst-dir (OR) c4.py cp -R d:\src-dir\*.* -t d:\dst-dir1 e:\dst-dir2"
linux = "\n Linux: c4.py cp -R /src-dir/*.* /dst-dir (OR) c4.py cp -R /src-dir/*.* -t /dst-dir1 /dst-dir2"
cmd_usage = desc + syntax + options + win + linux
# Displays the command-usage incase of incorrect arguments specified
if len(argv) < 4:
print cmd_usage
sys.exit(2)
global threadID, workQueue, debugFlag
threads, threadList, threadID, debugFlag, cnt = [], [], 1, False, 0
stime = time.time()
# Perform single source to single target directory copy
if ((len(argv) == 4) and (("-R" in argv[1]) or ("-r" in argv[1]))) or ((len(argv) == 5) and (("-R" in argv[2]) or ("-r" in argv[2]))):
if (len(argv) == 4):
src_path, dest_path = argv[2], argv[3]
if (len(argv) == 5) and ("-L" in argv[1]):
debugFlag = True
src_path, dest_path = argv[3], argv[4]
if src_path.endswith('/*') or src_path.endswith('\*'):
src_path = src_path[:-2]
if src_path.endswith('/*.*') or src_path.endswith('\*.*'):
src_path = src_path[:-4]
# Computing the file-count recursively traversing the directory
# Excludes the count of number of directories
fcnt = sum([len(f) for r, d, f in os.walk(src_path)])
print "File)s) count in source directory: %d" % fcnt
cnt = fcnt * 1
workQueue = Queue.Queue(cnt)
# Fill the Queue
for root, subfolder, filenames in os.walk(src_path):
newDir = os.path.join(dest_path, root[1 + len(src_path):])
if not os.path.exists(newDir):
os.makedirs(newDir)
else:
delete_folder(newDir)
for filename in filenames:
sfpath = str(os.path.join(root, filename))
dfpath = str(os.path.join(newDir, filename))
workQueue.put((sfpath, dfpath))
if debugFlag:
print "***** Added to Q... %s | %s" % (sfpath, dfpath)
elif ((len(argv) > 4) and (("-t" in argv[3]) or ("-t" in argv[4]))):
if ("-L" in argv[1]):
debugFlag = True
src_path, st = argv[3], 5
else:
src_path, st = argv[2], 4
if src_path.endswith('/*') or src_path.endswith('\*'):
src_path = src_path[:-2]
if src_path.endswith('/*.*') or src_path.endswith('\*.*'):
src_path = src_path[:-4]
# Computing the file-count recursively traversing the directory
# Excludes the count of number of directories
fcnt = sum([len(f) for r, d, f in os.walk(src_path)])
if ("-L" in argv[1]):
dst = (len(argv) - 5)
else:
dst = (len(argv) - 4)
print "File(s) count in source directory:%d | Destination directories count:%s" % (fcnt, dst)
cnt = fcnt * dst
workQueue = Queue.Queue(cnt)
# Fill the Queue
for root, subfolder, filenames in os.walk(src_path):
for i in range(st, (len(argv))):
dest_path = argv[i]
newDir = os.path.join(dest_path, root[1 + len(src_path):])
if not os.path.exists(newDir):
os.makedirs(newDir)
else:
delete_folder(newDir)
for filename in filenames:
sfpath = str(os.path.join(root, filename))
dfpath = str(os.path.join(newDir, filename))
workQueue.put((sfpath, dfpath))
if debugFlag:
print "***** Added to Q... %s | %s" % (sfpath, dfpath)
print "\nGenerating c4id's for source directory files only...\n"
# Create new threads
max_threads = 100
if cnt > max_threads:
cnt = max_threads
for i in range(1, cnt+1):
s = 'Thread'+str(i)
threadList.append(s)
if debugFlag:
print "***** ThreadsList: %s" % str(threadList)
for tName in threadList:
thread = myThread(threadID, tName, workQueue, idFlag=True)
thread.start()
threads.append(thread)
threadID += 1
# Wait for queue to empty
while not workQueue.empty():
pass
# Notify threads its time to exit
global exitFlag
exitFlag = 1
# Wait for all threads to complete
for t in threads:
t.join()
if debugFlag:
print "\nUtility Exec time: %s sec" %(time.time() - stime)
if __name__ == '__main__':
queue_mt(sys.argv[1:])