~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/atomicfile.py

[merge] Erik Bågfors: add --revision to bzr pull

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
 
18
18
 
19
 
class AtomicFile:
 
19
from warnings import warn
 
20
from bzrlib.osutils import rename
 
21
import errno
 
22
 
 
23
class AtomicFile(object):
20
24
    """A file that does an atomic-rename to move into place.
21
25
 
22
26
    This also causes hardlinks to break when it's written out.
27
31
    An encoding can be specified; otherwise the default is ascii.
28
32
    """
29
33
 
30
 
    def __init__(self, filename, mode='wb', encoding=None):
 
34
    def __init__(self, filename, mode='wb', encoding=None, new_mode=None):
31
35
        if mode != 'wb' and mode != 'wt':
32
36
            raise ValueError("invalid AtomicFile mode %r" % mode)
33
37
 
36
40
                                             socket.gethostname())
37
41
        self.realfilename = filename
38
42
        
39
 
        self.f = open(self.tmpfilename, mode)
40
 
 
41
43
        if encoding:
42
44
            import codecs
43
 
            self.f = codecs.EncodedFile(self.f, encoding)
44
 
        
 
45
            self.f = codecs.open(self.tmpfilename, mode, encoding)
 
46
        else:
 
47
            self.f = open(self.tmpfilename, mode)
 
48
 
45
49
        self.write = self.f.write
46
 
        self.closed = property(self.f.closed)
 
50
        self.closed = False
 
51
        self._new_mode = new_mode
 
52
 
 
53
 
 
54
    def __repr__(self):
 
55
        return '%s(%r)' % (self.__class__.__name__,
 
56
                           self.realfilename)
 
57
    
47
58
 
48
59
    def commit(self):
49
60
        """Close the file and move to final name."""
50
61
        import sys, os
51
62
        
 
63
        if self.closed:
 
64
            raise Exception('%r is already closed' % self)
 
65
 
 
66
        self.closed = True
52
67
        self.f.close()
53
 
        if sys.platform == 'win32':
54
 
            os.remove(self.realfilename)
55
 
        os.rename(self.tmpfilename, self.realfilename)
 
68
        self.f = None
 
69
        
 
70
        try:
 
71
            if self._new_mode is None:
 
72
                self._new_mode = os.lstat(self.realfilename).st_mode
 
73
        except OSError, e:
 
74
            if e.errno != errno.ENOENT:
 
75
                raise
 
76
        else:
 
77
            os.chmod(self.tmpfilename, self._new_mode)
 
78
 
 
79
        rename(self.tmpfilename, self.realfilename)
 
80
 
56
81
 
57
82
    def abort(self):
58
83
        """Discard temporary file without committing changes."""
59
84
        import os
 
85
 
 
86
        if self.closed:
 
87
            raise Exception('%r is already closed' % self)
 
88
 
 
89
        self.closed = True
60
90
        self.f.close()
 
91
        self.f = None
61
92
        os.remove(self.tmpfilename)
62
93
 
 
94
 
63
95
    def close(self):
64
96
        """Discard the file unless already committed."""
65
97
        if not self.closed:
66
98
            self.abort()
67
 
        
 
99
 
 
100
 
 
101
    def __del__(self):
 
102
        if hasattr(self, 'closed') and not self.closed:
 
103
            warn("%r leaked" % self)
68
104