# HG changeset patch # User cmlenz # Date 1183468356 0 # Node ID 52746440c300dc3b29277e5b0fa13dd42d6a0d15 # Parent 93a922d31ecad31fd31123c012ebf0d25715dcde Fix for #36: avoid corrupting the catalog on update when there's an error in the writing process. diff --git a/babel/messages/frontend.py b/babel/messages/frontend.py --- a/babel/messages/frontend.py +++ b/babel/messages/frontend.py @@ -22,8 +22,10 @@ from optparse import OptionParser import os import re +import shutil from StringIO import StringIO import sys +import tempfile from babel import __version__ as VERSION from babel import Locale, localedata @@ -511,13 +513,33 @@ finally: infile.close() - rest = catalog.update(template) + catalog.update(template) - outfile = open(filename, 'w') + tmpname = os.path.join(os.path.dirname(filename), + tempfile.gettempprefix() + + os.path.basename(filename)) + tmpfile = open(tmpname, 'w') try: - write_po(outfile, catalog, ignore_obsolete=self.ignore_obsolete) - finally: - outfile.close() + try: + write_po(tmpfile, catalog, + ignore_obsolete=self.ignore_obsolete) + finally: + tmpfile.close() + except: + os.remove(tmpname) + raise + + try: + os.rename(tmpname, filename) + except OSError: + # We're probably on Windows, which doesn't support atomic + # renames, at least not through Python + # If the error is in fact due to a permissions problem, that + # same error is going to be raised from one of the following + # operations + os.remove(filename) + shutil.copy(tmpname, filename) + os.remove(tmpname) class CommandLineInterface(object): @@ -915,14 +937,35 @@ finally: infile.close() - rest = catalog.update(template) + catalog.update(template) - outfile = open(filename, 'w') + catalog.update(template) + + tmpname = os.path.join(os.path.dirname(filename), + tempfile.gettempprefix() + + os.path.basename(filename)) + tmpfile = open(tmpname, 'w') try: - write_po(outfile, catalog, - ignore_obsolete=options.ignore_obsolete) - finally: - outfile.close() + try: + write_po(tmpfile, catalog, + ignore_obsolete=options.ignore_obsolete) + finally: + tmpfile.close() + except: + os.remove(tmpname) + raise + + try: + os.rename(tmpname, filename) + except OSError: + # We're probably on Windows, which doesn't support atomic + # renames, at least not through Python + # If the error is in fact due to a permissions problem, that + # same error is going to be raised from one of the following + # operations + os.remove(filename) + shutil.copy(tmpname, filename) + os.remove(tmpname) def main():