comparison mercurial/util.py @ 15057:774da7121fc9

atomictempfile: make close() consistent with other file-like objects. The usual contract is that close() makes your writes permanent, so atomictempfile's use of close() to *discard* writes (and rename() to keep them) is rather unexpected. Thus, change it so close() makes things permanent and add a new discard() method to throw them away. discard() is only used internally, in __del__(), to ensure that writes are discarded when an atomictempfile object goes out of scope. I audited mercurial.*, hgext.*, and ~80 third-party extensions, and found no one using the existing semantics of close() to discard writes, so this should be safe.
author Greg Ward <greg@gerg.ca>
date Thu, 25 Aug 2011 20:21:04 -0400
parents ff3e93686306
children 81f33be0ea79
comparison
equal deleted inserted replaced
15056:8413916df816 15057:774da7121fc9
743 743
744 class atomictempfile(object): 744 class atomictempfile(object):
745 '''writeable file object that atomically updates a file 745 '''writeable file object that atomically updates a file
746 746
747 All writes will go to a temporary copy of the original file. Call 747 All writes will go to a temporary copy of the original file. Call
748 rename() when you are done writing, and atomictempfile will rename 748 close() when you are done writing, and atomictempfile will rename
749 the temporary copy to the original name, making the changes visible. 749 the temporary copy to the original name, making the changes
750 750 visible. If the object is destroyed without being closed, all your
751 Unlike other file-like objects, close() discards your writes by 751 writes are discarded.
752 simply deleting the temporary file.
753 ''' 752 '''
754 def __init__(self, name, mode='w+b', createmode=None): 753 def __init__(self, name, mode='w+b', createmode=None):
755 self.__name = name # permanent name 754 self.__name = name # permanent name
756 self._tempname = mktempcopy(name, emptyok=('w' in mode), 755 self._tempname = mktempcopy(name, emptyok=('w' in mode),
757 createmode=createmode) 756 createmode=createmode)
759 758
760 # delegated methods 759 # delegated methods
761 self.write = self._fp.write 760 self.write = self._fp.write
762 self.fileno = self._fp.fileno 761 self.fileno = self._fp.fileno
763 762
764 def rename(self): 763 def close(self):
765 if not self._fp.closed: 764 if not self._fp.closed:
766 self._fp.close() 765 self._fp.close()
767 rename(self._tempname, localpath(self.__name)) 766 rename(self._tempname, localpath(self.__name))
768 767
769 def close(self): 768 def discard(self):
770 if not self._fp.closed: 769 if not self._fp.closed:
771 try: 770 try:
772 os.unlink(self._tempname) 771 os.unlink(self._tempname)
773 except OSError: 772 except OSError:
774 pass 773 pass
775 self._fp.close() 774 self._fp.close()
776 775
777 def __del__(self): 776 def __del__(self):
778 if safehasattr(self, '_fp'): # constructor actually did something 777 if safehasattr(self, '_fp'): # constructor actually did something
779 self.close() 778 self.discard()
780 779
781 def makedirs(name, mode=None): 780 def makedirs(name, mode=None):
782 """recursive directory creation with parent mode inheritance""" 781 """recursive directory creation with parent mode inheritance"""
783 parent = os.path.abspath(os.path.dirname(name)) 782 parent = os.path.abspath(os.path.dirname(name))
784 try: 783 try: