/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
1
import bzrlib
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
2
import bzrlib.errors as errors
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
3
from bzrlib.errors import LockError, ReadOnlyError
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
4
from bzrlib.trace import mutter
5
import bzrlib.transactions as transactions
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
6
1185.66.3 by Aaron Bentley
Renamed ControlFiles to LockableFiles
7
class LockableFiles(object):
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
8
    """Object representing a set of lockable files
9
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
10
    _lock_mode
11
        None, or 'r' or 'w'
12
13
    _lock_count
14
        If _lock_mode is true, a positive count of the number of times the
15
        lock has been taken.
16
17
    _lock
18
        Lock object from bzrlib.lock.
19
    """
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
20
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
21
    _lock_mode = None
22
    _lock_count = None
23
    _lock = None
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
24
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
25
    def __init__(self, transport, lock_name):
26
        object.__init__(self)
27
        self._transport = transport
28
        self.lock_name = lock_name
29
        self._transaction = None
30
31
    def __del__(self):
32
        if self._lock_mode or self._lock:
33
            # XXX: This should show something every time, and be suitable for
34
            # headless operation and embedding
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
35
            warn("file group %r was not explicitly unlocked" % self)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
36
            self._lock.unlock()
37
38
    def _rel_controlfilename(self, file_or_path):
39
        if not isinstance(file_or_path, basestring):
40
            file_or_path = '/'.join(file_or_path)
41
        if file_or_path == '':
1185.65.15 by Robert Collins
Merge from integration.
42
            return unicode(bzrlib.BZRDIR)
43
        return bzrlib.transport.urlescape(unicode(bzrlib.BZRDIR + '/' + file_or_path))
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
44
45
    def controlfilename(self, file_or_path):
46
        """Return location relative to branch."""
47
        return self._transport.abspath(self._rel_controlfilename(file_or_path))
48
49
    def controlfile(self, file_or_path, mode='r'):
50
        """Open a control file for this branch.
51
52
        There are two classes of file in the control directory: text
53
        and binary.  binary files are untranslated byte streams.  Text
54
        control files are stored with Unix newlines and in UTF-8, even
55
        if the platform or locale defaults are different.
56
57
        Controlfiles should almost never be opened in write mode but
58
        rather should be atomically copied and replaced using atomicfile.
59
        """
60
        import codecs
61
62
        relpath = self._rel_controlfilename(file_or_path)
63
        #TODO: codecs.open() buffers linewise, so it was overloaded with
64
        # a much larger buffer, do we need to do the same for getreader/getwriter?
65
        if mode == 'rb': 
66
            return self._transport.get(relpath)
67
        elif mode == 'wb':
1185.65.12 by Robert Collins
Remove the only-used-once put_controlfiles, and change put_controlfile to put and put_utf8.
68
            raise BzrError("Branch.controlfile(mode='wb') is not supported, use put[_utf8]")
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
69
        elif mode == 'r':
70
            # XXX: Do we really want errors='replace'?   Perhaps it should be
71
            # an error, or at least reported, if there's incorrectly-encoded
72
            # data inside a file.
73
            # <https://launchpad.net/products/bzr/+bug/3823>
74
            return codecs.getreader('utf-8')(self._transport.get(relpath), errors='replace')
75
        elif mode == 'w':
1185.65.12 by Robert Collins
Remove the only-used-once put_controlfiles, and change put_controlfile to put and put_utf8.
76
            raise BzrError("Branch.controlfile(mode='w') is not supported, use put[_utf8]")
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
77
        else:
78
            raise BzrError("invalid controlfile mode %r" % mode)
79
1185.65.12 by Robert Collins
Remove the only-used-once put_controlfiles, and change put_controlfile to put and put_utf8.
80
    def put(self, path, file):
81
        """Write a file.
82
        
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
83
        :param path: The path to put the file, relative to the .bzr control
84
                     directory
85
        :param f: A file-like or string object whose contents should be copied.
86
        """
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
87
        if not self._lock_mode == 'w':
88
            raise ReadOnlyError()
1185.65.12 by Robert Collins
Remove the only-used-once put_controlfiles, and change put_controlfile to put and put_utf8.
89
        self._transport.put(self._rel_controlfilename(path), file)
90
91
    def put_utf8(self, path, file):
92
        """Write a file, encoding as utf-8.
93
94
        :param path: The path to put the file, relative to the .bzr control
95
                     directory
96
        :param f: A file-like or string object whose contents should be copied.
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
97
        """
98
        import codecs
1185.67.6 by Aaron Bentley
Added tests and fixes for LockableFiles.put_utf8(); imported IterableFile
99
        from iterablefile import IterableFile
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
100
        ctrl_files = []
1185.67.6 by Aaron Bentley
Added tests and fixes for LockableFiles.put_utf8(); imported IterableFile
101
        def file_iterator(unicode_file, bufsize=32768):
102
            while True:
103
                b = unicode_file.read(bufsize)
104
                if len(b) == 0:
105
                    break
106
                yield b
107
        if hasattr(file, 'read'):
108
            iterator = file_iterator(file)
1185.65.12 by Robert Collins
Remove the only-used-once put_controlfiles, and change put_controlfile to put and put_utf8.
109
        else:
1185.67.6 by Aaron Bentley
Added tests and fixes for LockableFiles.put_utf8(); imported IterableFile
110
            iterator = file
111
        encoded_file = IterableFile(b.encode('utf-8', 'replace') for b in 
112
                                    iterator)
113
        self.put(path, encoded_file)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
114
115
    def lock_write(self):
116
        mutter("lock write: %s (%s)", self, self._lock_count)
117
        # TODO: Upgrade locking to support using a Transport,
118
        # and potentially a remote locking protocol
119
        if self._lock_mode:
120
            if self._lock_mode != 'w':
121
                raise LockError("can't upgrade to a write lock from %r" %
122
                                self._lock_mode)
123
            self._lock_count += 1
124
        else:
125
            self._lock = self._transport.lock_write(
126
                    self._rel_controlfilename(self.lock_name))
127
            self._lock_mode = 'w'
128
            self._lock_count = 1
129
            self._set_transaction(transactions.PassThroughTransaction())
130
131
    def lock_read(self):
132
        mutter("lock read: %s (%s)", self, self._lock_count)
133
        if self._lock_mode:
134
            assert self._lock_mode in ('r', 'w'), \
135
                   "invalid lock mode %r" % self._lock_mode
136
            self._lock_count += 1
137
        else:
138
            self._lock = self._transport.lock_read(
139
                    self._rel_controlfilename('branch-lock'))
140
            self._lock_mode = 'r'
141
            self._lock_count = 1
142
            self._set_transaction(transactions.ReadOnlyTransaction())
143
            # 5K may be excessive, but hey, its a knob.
144
            self.get_transaction().set_cache_size(5000)
145
                        
146
    def unlock(self):
147
        mutter("unlock: %s (%s)", self, self._lock_count)
148
        if not self._lock_mode:
149
            raise LockError('branch %r is not locked' % (self))
150
151
        if self._lock_count > 1:
152
            self._lock_count -= 1
153
        else:
154
            self._finish_transaction()
155
            self._lock.unlock()
156
            self._lock = None
157
            self._lock_mode = self._lock_count = None
158
159
    def make_transport(self, relpath):
160
        return self._transport.clone(relpath)
161
162
    def get_transaction(self):
163
        """Return the current active transaction.
164
165
        If no transaction is active, this returns a passthrough object
166
        for which all data is immediately flushed and no caching happens.
167
        """
168
        if self._transaction is None:
169
            return transactions.PassThroughTransaction()
170
        else:
171
            return self._transaction
172
173
    def _set_transaction(self, new_transaction):
174
        """Set a new active transaction."""
175
        if self._transaction is not None:
176
            raise errors.LockError('Branch %s is in a transaction already.' %
177
                                   self)
178
        self._transaction = new_transaction
179
180
    def _finish_transaction(self):
181
        """Exit the current transaction."""
182
        if self._transaction is None:
183
            raise errors.LockError('Branch %s is not in a transaction' %
184
                                   self)
185
        transaction = self._transaction
186
        self._transaction = None
187
        transaction.finish()