Commit 09d967df authored by ben's avatar ben

Added resuming bug fixes


git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup@114 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
parent c5f707de
...@@ -338,8 +338,10 @@ class PipeConnection(LowLevelPipeConnection): ...@@ -338,8 +338,10 @@ class PipeConnection(LowLevelPipeConnection):
def extract_exception(self): def extract_exception(self):
"""Return active exception""" """Return active exception"""
Log("Sending back exception: \n" + if Log.verbosity >= 5 or Log.term_verbosity >= 5:
"".join(traceback.format_tb(sys.exc_info()[2])), 2) Log("Sending back exception %s of type %s: \n%s" %
(sys.exc_info()[1], sys.exc_info()[0],
"".join(traceback.format_tb(sys.exc_info()[2]))), 5)
return sys.exc_info()[1] return sys.exc_info()[1]
def Server(self): def Server(self):
......
#!/usr/bin/env python #!/usr/bin/env python
# #
# rdiff-backup -- Mirror files while keeping incremental changes # rdiff-backup -- Mirror files while keeping incremental changes
# Version 0.7.5.4 released May 29, 2002 # Version 0.7.6 released May 31, 2002
# Copyright (C) 2001, 2002 Ben Escoto <bescoto@stanford.edu> # Copyright (C) 2001, 2002 Ben Escoto <bescoto@stanford.edu>
# #
# This program is licensed under the GNU General Public License (GPL). # This program is licensed under the GNU General Public License (GPL).
...@@ -14,6 +14,6 @@ ...@@ -14,6 +14,6 @@
# bugs or have any suggestions. # bugs or have any suggestions.
from __future__ import nested_scopes, generators from __future__ import nested_scopes, generators
import os, stat, time, sys, getopt, re, cPickle, types, shutil, sha, marshal, traceback, popen2, tempfile, gzip, UserList, errno import os, stat, time, sys, getopt, re, cPickle, types, shutil, sha, marshal, traceback, popen2, tempfile, gzip, UserList, errno, signal
...@@ -207,13 +207,18 @@ class HLDestinationStruct: ...@@ -207,13 +207,18 @@ class HLDestinationStruct:
else: else:
iitr = IncrementITR(inc_rpath) iitr = IncrementITR(inc_rpath)
iitr.override_changed() iitr.override_changed()
Globals.ITR = iitr
iitr.Errors = 0
return iitr return iitr
def get_MirrorITR(cls, inc_rpath): def get_MirrorITR(cls, inc_rpath):
"""Return MirrorITR, starting from state if available""" """Return MirrorITR, starting from state if available"""
if cls._session_info and cls._session_info.ITR: if cls._session_info and cls._session_info.ITR:
return cls._session_info.ITR return cls._session_info.ITR
else: return MirrorITR(inc_rpath) ITR = MirrorITR(inc_rpath)
Globals.ITR = ITR
ITR.Errors = 0
return ITR
def patch_and_finalize(cls, dest_rpath, diffs): def patch_and_finalize(cls, dest_rpath, diffs):
"""Apply diffs and finalize""" """Apply diffs and finalize"""
...@@ -244,7 +249,7 @@ class HLDestinationStruct: ...@@ -244,7 +249,7 @@ class HLDestinationStruct:
collated = RORPIter.CollateIterators(diffs, cls.initial_dsiter2) collated = RORPIter.CollateIterators(diffs, cls.initial_dsiter2)
finalizer, ITR = cls.get_finalizer(), cls.get_MirrorITR(inc_rpath) finalizer, ITR = cls.get_finalizer(), cls.get_MirrorITR(inc_rpath)
Stats.open_dir_stats_file() Stats.open_dir_stats_file()
dsrp = None dsrp, finished_dsrp = None, None
try: try:
for indexed_tuple in collated: for indexed_tuple in collated:
...@@ -255,9 +260,10 @@ class HLDestinationStruct: ...@@ -255,9 +260,10 @@ class HLDestinationStruct:
ITR(dsrp.index, diff_rorp, dsrp) ITR(dsrp.index, diff_rorp, dsrp)
finalizer(dsrp.index, dsrp) finalizer(dsrp.index, dsrp)
SaveState.checkpoint(ITR, finalizer, dsrp) SaveState.checkpoint(ITR, finalizer, dsrp)
finished_dsrp = dsrp
ITR.Finish() ITR.Finish()
finalizer.Finish() finalizer.Finish()
except: cls.handle_last_error(dsrp, finalizer, ITR) except: cls.handle_last_error(finished_dsrp, finalizer, ITR)
if Globals.preserve_hardlinks: Hardlink.final_writedata() if Globals.preserve_hardlinks: Hardlink.final_writedata()
Stats.close_dir_stats_file() Stats.close_dir_stats_file()
...@@ -269,7 +275,7 @@ class HLDestinationStruct: ...@@ -269,7 +275,7 @@ class HLDestinationStruct:
collated = RORPIter.CollateIterators(diffs, cls.initial_dsiter2) collated = RORPIter.CollateIterators(diffs, cls.initial_dsiter2)
finalizer, ITR = cls.get_finalizer(), cls.get_ITR(inc_rpath) finalizer, ITR = cls.get_finalizer(), cls.get_ITR(inc_rpath)
Stats.open_dir_stats_file() Stats.open_dir_stats_file()
dsrp = None dsrp, finished_dsrp = None, None
try: try:
for indexed_tuple in collated: for indexed_tuple in collated:
...@@ -281,9 +287,10 @@ class HLDestinationStruct: ...@@ -281,9 +287,10 @@ class HLDestinationStruct:
ITR(index, diff_rorp, dsrp) ITR(index, diff_rorp, dsrp)
finalizer(index, dsrp) finalizer(index, dsrp)
SaveState.checkpoint(ITR, finalizer, dsrp) SaveState.checkpoint(ITR, finalizer, dsrp)
finished_dsrp = dsrp
ITR.Finish() ITR.Finish()
finalizer.Finish() finalizer.Finish()
except: cls.handle_last_error(dsrp, finalizer, ITR) except: cls.handle_last_error(finished_dsrp, finalizer, ITR)
if Globals.preserve_hardlinks: Hardlink.final_writedata() if Globals.preserve_hardlinks: Hardlink.final_writedata()
Stats.close_dir_stats_file() Stats.close_dir_stats_file()
......
import tempfile, errno import tempfile, errno, signal
execfile("hardlink.py") execfile("hardlink.py")
####################################################################### #######################################################################
...@@ -79,7 +79,7 @@ class RobustAction: ...@@ -79,7 +79,7 @@ class RobustAction:
class Robust: class Robust:
"""Contains various file operations made safer using tempfiles""" """Contains various methods designed to make things safer"""
null_action = RobustAction(None, None, None) null_action = RobustAction(None, None, None)
def chain(*robust_action_list): def chain(*robust_action_list):
"""Return chain tying together a number of robust actions """Return chain tying together a number of robust actions
...@@ -122,9 +122,9 @@ class Robust: ...@@ -122,9 +122,9 @@ class Robust:
final_vals.append(ra.final_func(init_val)) final_vals.append(ra.final_func(init_val))
return final_vals return final_vals
def error(exc, ran_init, init_val): def error(exc, ran_init, init_val):
for ra, init_val in zip(ras_with_started_inits, init_return_vals): for ra, init_val in zip(ras_with_started_inits, init_vals):
ra.error_handler(exc, 1, init_val) ra.error_handler(exc, 1, init_val)
for ra in ras_with_started_inits[len(init_return_vals):]: for ra in ras_with_started_inits[len(init_vals):]:
ra.error_handler(exc, None, None) ra.error_handler(exc, None, None)
return RobustAction(init, final, error) return RobustAction(init, final, error)
...@@ -251,6 +251,10 @@ class Robust: ...@@ -251,6 +251,10 @@ class Robust:
'ENOTDIR', 'ENAMETOOLONG', 'EINTR', 'ENOTEMPTY', 'ENOTDIR', 'ENAMETOOLONG', 'EINTR', 'ENOTEMPTY',
'EIO', 'ETXTBSY', 'ESRCH', 'EINVAL'])): 'EIO', 'ETXTBSY', 'ESRCH', 'EINVAL'])):
Log.exception() Log.exception()
conn = Globals.backup_writer
if conn is not None: # increment error count
ITR_exists = conn.Globals.is_not_None('ITR')
if ITR_exists: conn.Globals.ITR.increment_stat('Errors')
if error_handler: return error_handler(exc, *args) if error_handler: return error_handler(exc, *args)
else: else:
Log.exception(1, 2) Log.exception(1, 2)
...@@ -265,9 +269,23 @@ class Robust: ...@@ -265,9 +269,23 @@ class Robust:
dir_listing.sort() dir_listing.sort()
return dir_listing return dir_listing
def signal_handler(signum, frame):
"""This is called when signal signum is caught"""
raise SignalException(signum)
def install_signal_handlers():
"""Install signal handlers on current connection"""
for signum in [signal.SIGQUIT, signal.SIGHUP, signal.SIGTERM]:
signal.signal(signum, Robust.signal_handler)
MakeStatic(Robust) MakeStatic(Robust)
class SignalException(Exception):
"""SignalException(signum) means signal signum has been received"""
pass
class TracebackArchive: class TracebackArchive:
"""Save last 10 caught exceptions, so they can be printed if fatal""" """Save last 10 caught exceptions, so they can be printed if fatal"""
_traceback_strings = [] _traceback_strings = []
......
...@@ -275,7 +275,7 @@ probably isn't what you meant.""" % ...@@ -275,7 +275,7 @@ probably isn't what you meant.""" %
if include is None: continue # later line may match if include is None: continue # later line may match
return include return include
selection_function.exclude = something_excluded selection_function.exclude = something_excluded or inc_default == 0
selection_function.name = "Filelist: " + filelist_name selection_function.name = "Filelist: " + filelist_name
return selection_function return selection_function
......
...@@ -17,8 +17,10 @@ class StatsObj: ...@@ -17,8 +17,10 @@ class StatsObj:
'ChangedFiles', 'ChangedFiles',
'ChangedSourceSize', 'ChangedMirrorSize', 'ChangedSourceSize', 'ChangedMirrorSize',
'IncrementFiles', 'IncrementFileSize') 'IncrementFiles', 'IncrementFileSize')
stat_misc_attrs = ('Errors',)
stat_time_attrs = ('StartTime', 'EndTime', 'ElapsedTime') stat_time_attrs = ('StartTime', 'EndTime', 'ElapsedTime')
stat_attrs = ('Filename',) + stat_time_attrs + stat_file_attrs stat_attrs = (('Filename',) + stat_time_attrs +
stat_misc_attrs + stat_file_attrs)
# Below, the second value in each pair is true iff the value # Below, the second value in each pair is true iff the value
# indicates a number of bytes # indicates a number of bytes
...@@ -50,6 +52,10 @@ class StatsObj: ...@@ -50,6 +52,10 @@ class StatsObj:
"""Set attribute to given value""" """Set attribute to given value"""
self.__dict__[attr] = value self.__dict__[attr] = value
def increment_stat(self, attr):
"""Add 1 to value of attribute"""
self.__dict__[attr] = self.get_stat(attr) + 1
def get_stats_line(self, index): def get_stats_line(self, index):
"""Return one line abbreviated version of full stats string""" """Return one line abbreviated version of full stats string"""
file_attrs = map(lambda attr: str(self.get_stat(attr)), file_attrs = map(lambda attr: str(self.get_stat(attr)),
...@@ -95,6 +101,8 @@ class StatsObj: ...@@ -95,6 +101,8 @@ class StatsObj:
self.ElapsedTime = self.EndTime - self.StartTime self.ElapsedTime = self.EndTime - self.StartTime
timelist.append("ElapsedTime %.2f (%s)\n" % timelist.append("ElapsedTime %.2f (%s)\n" %
(self.ElapsedTime, Time.inttopretty(self.ElapsedTime))) (self.ElapsedTime, Time.inttopretty(self.ElapsedTime)))
if self.Errors is not None:
timelist.append("Errors %d\n" % self.Errors)
return "".join(timelist) return "".join(timelist)
def get_filestats_string(self): def get_filestats_string(self):
......
...@@ -338,8 +338,10 @@ class PipeConnection(LowLevelPipeConnection): ...@@ -338,8 +338,10 @@ class PipeConnection(LowLevelPipeConnection):
def extract_exception(self): def extract_exception(self):
"""Return active exception""" """Return active exception"""
Log("Sending back exception: \n" + if Log.verbosity >= 5 or Log.term_verbosity >= 5:
"".join(traceback.format_tb(sys.exc_info()[2])), 2) Log("Sending back exception %s of type %s: \n%s" %
(sys.exc_info()[1], sys.exc_info()[0],
"".join(traceback.format_tb(sys.exc_info()[2]))), 5)
return sys.exc_info()[1] return sys.exc_info()[1]
def Server(self): def Server(self):
......
...@@ -8,7 +8,7 @@ import re, os ...@@ -8,7 +8,7 @@ import re, os
class Globals: class Globals:
# The current version of rdiff-backup # The current version of rdiff-backup
version = "0.7.5.4" version = "0.7.6"
# If this is set, use this value in seconds as the current time # If this is set, use this value in seconds as the current time
# instead of reading it from the clock. # instead of reading it from the clock.
...@@ -157,11 +157,20 @@ class Globals: ...@@ -157,11 +157,20 @@ class Globals:
# replaced by the source and mirror Select objects respectively. # replaced by the source and mirror Select objects respectively.
select_source, select_mirror = None, None select_source, select_mirror = None, None
# On the backup writer connection, holds the main incrementing
# function. Access is provided to increment error counts.
ITR = None
def get(cls, name): def get(cls, name):
"""Return the value of something in this class""" """Return the value of something in this class"""
return cls.__dict__[name] return cls.__dict__[name]
get = classmethod(get) get = classmethod(get)
def is_not_None(cls, name):
"""Returns true if value is not None"""
return cls.__dict__[name] is not None
is_not_None = classmethod(is_not_None)
def set(cls, name, val): def set(cls, name, val):
"""Set the value of something in this class """Set the value of something in this class
......
#!/usr/bin/env python #!/usr/bin/env python
# #
# rdiff-backup -- Mirror files while keeping incremental changes # rdiff-backup -- Mirror files while keeping incremental changes
# Version 0.7.5.4 released May 29, 2002 # Version 0.7.6 released May 31, 2002
# Copyright (C) 2001, 2002 Ben Escoto <bescoto@stanford.edu> # Copyright (C) 2001, 2002 Ben Escoto <bescoto@stanford.edu>
# #
# This program is licensed under the GNU General Public License (GPL). # This program is licensed under the GNU General Public License (GPL).
...@@ -14,6 +14,6 @@ ...@@ -14,6 +14,6 @@
# bugs or have any suggestions. # bugs or have any suggestions.
from __future__ import nested_scopes, generators from __future__ import nested_scopes, generators
import os, stat, time, sys, getopt, re, cPickle, types, shutil, sha, marshal, traceback, popen2, tempfile, gzip, UserList, errno import os, stat, time, sys, getopt, re, cPickle, types, shutil, sha, marshal, traceback, popen2, tempfile, gzip, UserList, errno, signal
...@@ -207,13 +207,18 @@ class HLDestinationStruct: ...@@ -207,13 +207,18 @@ class HLDestinationStruct:
else: else:
iitr = IncrementITR(inc_rpath) iitr = IncrementITR(inc_rpath)
iitr.override_changed() iitr.override_changed()
Globals.ITR = iitr
iitr.Errors = 0
return iitr return iitr
def get_MirrorITR(cls, inc_rpath): def get_MirrorITR(cls, inc_rpath):
"""Return MirrorITR, starting from state if available""" """Return MirrorITR, starting from state if available"""
if cls._session_info and cls._session_info.ITR: if cls._session_info and cls._session_info.ITR:
return cls._session_info.ITR return cls._session_info.ITR
else: return MirrorITR(inc_rpath) ITR = MirrorITR(inc_rpath)
Globals.ITR = ITR
ITR.Errors = 0
return ITR
def patch_and_finalize(cls, dest_rpath, diffs): def patch_and_finalize(cls, dest_rpath, diffs):
"""Apply diffs and finalize""" """Apply diffs and finalize"""
...@@ -244,7 +249,7 @@ class HLDestinationStruct: ...@@ -244,7 +249,7 @@ class HLDestinationStruct:
collated = RORPIter.CollateIterators(diffs, cls.initial_dsiter2) collated = RORPIter.CollateIterators(diffs, cls.initial_dsiter2)
finalizer, ITR = cls.get_finalizer(), cls.get_MirrorITR(inc_rpath) finalizer, ITR = cls.get_finalizer(), cls.get_MirrorITR(inc_rpath)
Stats.open_dir_stats_file() Stats.open_dir_stats_file()
dsrp = None dsrp, finished_dsrp = None, None
try: try:
for indexed_tuple in collated: for indexed_tuple in collated:
...@@ -255,9 +260,10 @@ class HLDestinationStruct: ...@@ -255,9 +260,10 @@ class HLDestinationStruct:
ITR(dsrp.index, diff_rorp, dsrp) ITR(dsrp.index, diff_rorp, dsrp)
finalizer(dsrp.index, dsrp) finalizer(dsrp.index, dsrp)
SaveState.checkpoint(ITR, finalizer, dsrp) SaveState.checkpoint(ITR, finalizer, dsrp)
finished_dsrp = dsrp
ITR.Finish() ITR.Finish()
finalizer.Finish() finalizer.Finish()
except: cls.handle_last_error(dsrp, finalizer, ITR) except: cls.handle_last_error(finished_dsrp, finalizer, ITR)
if Globals.preserve_hardlinks: Hardlink.final_writedata() if Globals.preserve_hardlinks: Hardlink.final_writedata()
Stats.close_dir_stats_file() Stats.close_dir_stats_file()
...@@ -269,7 +275,7 @@ class HLDestinationStruct: ...@@ -269,7 +275,7 @@ class HLDestinationStruct:
collated = RORPIter.CollateIterators(diffs, cls.initial_dsiter2) collated = RORPIter.CollateIterators(diffs, cls.initial_dsiter2)
finalizer, ITR = cls.get_finalizer(), cls.get_ITR(inc_rpath) finalizer, ITR = cls.get_finalizer(), cls.get_ITR(inc_rpath)
Stats.open_dir_stats_file() Stats.open_dir_stats_file()
dsrp = None dsrp, finished_dsrp = None, None
try: try:
for indexed_tuple in collated: for indexed_tuple in collated:
...@@ -281,9 +287,10 @@ class HLDestinationStruct: ...@@ -281,9 +287,10 @@ class HLDestinationStruct:
ITR(index, diff_rorp, dsrp) ITR(index, diff_rorp, dsrp)
finalizer(index, dsrp) finalizer(index, dsrp)
SaveState.checkpoint(ITR, finalizer, dsrp) SaveState.checkpoint(ITR, finalizer, dsrp)
finished_dsrp = dsrp
ITR.Finish() ITR.Finish()
finalizer.Finish() finalizer.Finish()
except: cls.handle_last_error(dsrp, finalizer, ITR) except: cls.handle_last_error(finished_dsrp, finalizer, ITR)
if Globals.preserve_hardlinks: Hardlink.final_writedata() if Globals.preserve_hardlinks: Hardlink.final_writedata()
Stats.close_dir_stats_file() Stats.close_dir_stats_file()
......
...@@ -174,6 +174,8 @@ class Main: ...@@ -174,6 +174,8 @@ class Main:
Globals.postset_regexp('no_compression_regexp', Globals.postset_regexp('no_compression_regexp',
Globals.no_compression_regexp_string) Globals.no_compression_regexp_string)
for conn in Globals.connections: Robust.install_signal_handlers()
def take_action(self, rps): def take_action(self, rps):
"""Do whatever self.action says""" """Do whatever self.action says"""
if self.action == "server": if self.action == "server":
......
import tempfile, errno import tempfile, errno, signal
execfile("hardlink.py") execfile("hardlink.py")
####################################################################### #######################################################################
...@@ -79,7 +79,7 @@ class RobustAction: ...@@ -79,7 +79,7 @@ class RobustAction:
class Robust: class Robust:
"""Contains various file operations made safer using tempfiles""" """Contains various methods designed to make things safer"""
null_action = RobustAction(None, None, None) null_action = RobustAction(None, None, None)
def chain(*robust_action_list): def chain(*robust_action_list):
"""Return chain tying together a number of robust actions """Return chain tying together a number of robust actions
...@@ -122,9 +122,9 @@ class Robust: ...@@ -122,9 +122,9 @@ class Robust:
final_vals.append(ra.final_func(init_val)) final_vals.append(ra.final_func(init_val))
return final_vals return final_vals
def error(exc, ran_init, init_val): def error(exc, ran_init, init_val):
for ra, init_val in zip(ras_with_started_inits, init_return_vals): for ra, init_val in zip(ras_with_started_inits, init_vals):
ra.error_handler(exc, 1, init_val) ra.error_handler(exc, 1, init_val)
for ra in ras_with_started_inits[len(init_return_vals):]: for ra in ras_with_started_inits[len(init_vals):]:
ra.error_handler(exc, None, None) ra.error_handler(exc, None, None)
return RobustAction(init, final, error) return RobustAction(init, final, error)
...@@ -251,6 +251,10 @@ class Robust: ...@@ -251,6 +251,10 @@ class Robust:
'ENOTDIR', 'ENAMETOOLONG', 'EINTR', 'ENOTEMPTY', 'ENOTDIR', 'ENAMETOOLONG', 'EINTR', 'ENOTEMPTY',
'EIO', 'ETXTBSY', 'ESRCH', 'EINVAL'])): 'EIO', 'ETXTBSY', 'ESRCH', 'EINVAL'])):
Log.exception() Log.exception()
conn = Globals.backup_writer
if conn is not None: # increment error count
ITR_exists = conn.Globals.is_not_None('ITR')
if ITR_exists: conn.Globals.ITR.increment_stat('Errors')
if error_handler: return error_handler(exc, *args) if error_handler: return error_handler(exc, *args)
else: else:
Log.exception(1, 2) Log.exception(1, 2)
...@@ -265,9 +269,23 @@ class Robust: ...@@ -265,9 +269,23 @@ class Robust:
dir_listing.sort() dir_listing.sort()
return dir_listing return dir_listing
def signal_handler(signum, frame):
"""This is called when signal signum is caught"""
raise SignalException(signum)
def install_signal_handlers():
"""Install signal handlers on current connection"""
for signum in [signal.SIGQUIT, signal.SIGHUP, signal.SIGTERM]:
signal.signal(signum, Robust.signal_handler)
MakeStatic(Robust) MakeStatic(Robust)
class SignalException(Exception):
"""SignalException(signum) means signal signum has been received"""
pass
class TracebackArchive: class TracebackArchive:
"""Save last 10 caught exceptions, so they can be printed if fatal""" """Save last 10 caught exceptions, so they can be printed if fatal"""
_traceback_strings = [] _traceback_strings = []
......
...@@ -275,7 +275,7 @@ probably isn't what you meant.""" % ...@@ -275,7 +275,7 @@ probably isn't what you meant.""" %
if include is None: continue # later line may match if include is None: continue # later line may match
return include return include
selection_function.exclude = something_excluded selection_function.exclude = something_excluded or inc_default == 0
selection_function.name = "Filelist: " + filelist_name selection_function.name = "Filelist: " + filelist_name
return selection_function return selection_function
......
...@@ -17,8 +17,10 @@ class StatsObj: ...@@ -17,8 +17,10 @@ class StatsObj:
'ChangedFiles', 'ChangedFiles',
'ChangedSourceSize', 'ChangedMirrorSize', 'ChangedSourceSize', 'ChangedMirrorSize',
'IncrementFiles', 'IncrementFileSize') 'IncrementFiles', 'IncrementFileSize')
stat_misc_attrs = ('Errors',)
stat_time_attrs = ('StartTime', 'EndTime', 'ElapsedTime') stat_time_attrs = ('StartTime', 'EndTime', 'ElapsedTime')
stat_attrs = ('Filename',) + stat_time_attrs + stat_file_attrs stat_attrs = (('Filename',) + stat_time_attrs +
stat_misc_attrs + stat_file_attrs)
# Below, the second value in each pair is true iff the value # Below, the second value in each pair is true iff the value
# indicates a number of bytes # indicates a number of bytes
...@@ -50,6 +52,10 @@ class StatsObj: ...@@ -50,6 +52,10 @@ class StatsObj:
"""Set attribute to given value""" """Set attribute to given value"""
self.__dict__[attr] = value self.__dict__[attr] = value
def increment_stat(self, attr):
"""Add 1 to value of attribute"""
self.__dict__[attr] = self.get_stat(attr) + 1
def get_stats_line(self, index): def get_stats_line(self, index):
"""Return one line abbreviated version of full stats string""" """Return one line abbreviated version of full stats string"""
file_attrs = map(lambda attr: str(self.get_stat(attr)), file_attrs = map(lambda attr: str(self.get_stat(attr)),
...@@ -95,6 +101,8 @@ class StatsObj: ...@@ -95,6 +101,8 @@ class StatsObj:
self.ElapsedTime = self.EndTime - self.StartTime self.ElapsedTime = self.EndTime - self.StartTime
timelist.append("ElapsedTime %.2f (%s)\n" % timelist.append("ElapsedTime %.2f (%s)\n" %
(self.ElapsedTime, Time.inttopretty(self.ElapsedTime))) (self.ElapsedTime, Time.inttopretty(self.ElapsedTime)))
if self.Errors is not None:
timelist.append("Errors %d\n" % self.Errors)
return "".join(timelist) return "".join(timelist)
def get_filestats_string(self): def get_filestats_string(self):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment