/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1185.65.22 by Robert Collins
lockable_files was extracted from branch.py - give it a copyright statement
1
# Copyright (C) 2005 Canonical Ltd
2
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
1185.65.29 by Robert Collins
Implement final review suggestions.
17
from cStringIO import StringIO
18
import codecs
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
19
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
20
import bzrlib
1185.65.27 by Robert Collins
Tweak storage towards mergability.
21
from bzrlib.decorators import *
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
22
import bzrlib.errors as errors
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
23
from bzrlib.errors import LockError, ReadOnlyError
1185.65.27 by Robert Collins
Tweak storage towards mergability.
24
from bzrlib.osutils import file_iterator, safe_unicode
1185.65.29 by Robert Collins
Implement final review suggestions.
25
from bzrlib.symbol_versioning import *
1534.1.15 by Robert Collins
* The internal storage of history, and logical branch identity have now
26
from bzrlib.symbol_versioning import deprecated_method, zero_eight
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
27
from bzrlib.trace import mutter
28
import bzrlib.transactions as transactions
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
29
1185.65.27 by Robert Collins
Tweak storage towards mergability.
30
1185.66.3 by Aaron Bentley
Renamed ControlFiles to LockableFiles
31
class LockableFiles(object):
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
32
    """Object representing a set of files locked within the same scope
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
33
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
34
    _lock_mode
35
        None, or 'r' or 'w'
36
37
    _lock_count
38
        If _lock_mode is true, a positive count of the number of times the
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
39
        lock has been taken *by this process*.  Others may have compatible 
40
        read locks.
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
41
42
    _lock
43
        Lock object from bzrlib.lock.
44
    """
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
45
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
46
    _lock_mode = None
47
    _lock_count = None
48
    _lock = None
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
49
    # If set to False (by a plugin, etc) BzrBranch will not set the
50
    # mode on created files or directories
51
    _set_file_mode = True
52
    _set_dir_mode = True
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
53
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
54
    def __init__(self, transport, lock_name):
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
55
        object.__init__(self)
56
        self._transport = transport
57
        self.lock_name = lock_name
58
        self._transaction = None
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
59
        self._find_modes()
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
60
61
    def __del__(self):
62
        if self._lock_mode or self._lock:
63
            # XXX: This should show something every time, and be suitable for
64
            # headless operation and embedding
1185.67.9 by Aaron Bentley
Fixed most bugs
65
            from warnings import warn
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
66
            warn("file group %r was not explicitly unlocked" % self)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
67
            self._lock.unlock()
68
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
69
    def _escape(self, file_or_path):
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
70
        if not isinstance(file_or_path, basestring):
71
            file_or_path = '/'.join(file_or_path)
72
        if file_or_path == '':
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
73
            return u''
1185.65.27 by Robert Collins
Tweak storage towards mergability.
74
        return bzrlib.transport.urlescape(safe_unicode(file_or_path))
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
75
76
    def _find_modes(self):
77
        """Determine the appropriate modes for files and directories."""
78
        try:
1534.4.28 by Robert Collins
first cut at merge from integration.
79
            st = self._transport.stat('.')
80
        except errors.TransportNotPossible:
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
81
            self._dir_mode = 0755
82
            self._file_mode = 0644
83
        else:
84
            self._dir_mode = st.st_mode & 07777
85
            # Remove the sticky and execute bits for files
86
            self._file_mode = self._dir_mode & ~07111
87
        if not self._set_dir_mode:
88
            self._dir_mode = None
89
        if not self._set_file_mode:
90
            self._file_mode = None
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
91
92
    def controlfilename(self, file_or_path):
93
        """Return location relative to branch."""
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
94
        return self._transport.abspath(self._escape(file_or_path))
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
95
1534.1.15 by Robert Collins
* The internal storage of history, and logical branch identity have now
96
    @deprecated_method(zero_eight)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
97
    def controlfile(self, file_or_path, mode='r'):
98
        """Open a control file for this branch.
99
1185.65.27 by Robert Collins
Tweak storage towards mergability.
100
        There are two classes of file in a lockable directory: text
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
101
        and binary.  binary files are untranslated byte streams.  Text
102
        control files are stored with Unix newlines and in UTF-8, even
103
        if the platform or locale defaults are different.
104
1185.65.27 by Robert Collins
Tweak storage towards mergability.
105
        Such files are not openable in write mode : they are managed via
106
        put and put_utf8 which atomically replace old versions using
107
        atomicfile.
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
108
        """
109
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
110
        relpath = self._escape(file_or_path)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
111
        #TODO: codecs.open() buffers linewise, so it was overloaded with
112
        # a much larger buffer, do we need to do the same for getreader/getwriter?
113
        if mode == 'rb': 
1185.65.29 by Robert Collins
Implement final review suggestions.
114
            return self.get(relpath)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
115
        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.
116
            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
117
        elif mode == 'r':
1185.65.29 by Robert Collins
Implement final review suggestions.
118
            return self.get_utf8(relpath)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
119
        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.
120
            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
121
        else:
122
            raise BzrError("invalid controlfile mode %r" % mode)
123
1185.65.29 by Robert Collins
Implement final review suggestions.
124
    @needs_read_lock
125
    def get(self, relpath):
126
        """Get a file as a bytestream."""
127
        relpath = self._escape(relpath)
128
        return self._transport.get(relpath)
129
130
    @needs_read_lock
131
    def get_utf8(self, relpath):
132
        """Get a file as a unicode stream."""
133
        relpath = self._escape(relpath)
134
        # DO NOT introduce an errors=replace here.
135
        return codecs.getreader('utf-8')(self._transport.get(relpath))
136
1185.65.27 by Robert Collins
Tweak storage towards mergability.
137
    @needs_write_lock
1185.65.12 by Robert Collins
Remove the only-used-once put_controlfiles, and change put_controlfile to put and put_utf8.
138
    def put(self, path, file):
139
        """Write a file.
140
        
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
141
        :param path: The path to put the file, relative to the .bzr control
142
                     directory
143
        :param f: A file-like or string object whose contents should be copied.
144
        """
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
145
        self._transport.put(self._escape(path), file, mode=self._file_mode)
1185.65.12 by Robert Collins
Remove the only-used-once put_controlfiles, and change put_controlfile to put and put_utf8.
146
1185.65.27 by Robert Collins
Tweak storage towards mergability.
147
    @needs_write_lock
1185.65.29 by Robert Collins
Implement final review suggestions.
148
    def put_utf8(self, path, a_string):
149
        """Write a string, encoding as utf-8.
1185.65.12 by Robert Collins
Remove the only-used-once put_controlfiles, and change put_controlfile to put and put_utf8.
150
1185.65.29 by Robert Collins
Implement final review suggestions.
151
        :param path: The path to put the string, relative to the transport root.
152
        :param string: A file-like or string object whose contents should be copied.
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
153
        """
1185.67.7 by Aaron Bentley
Refactored a bit
154
        # IterableFile would not be needed if Transport.put took iterables
155
        # instead of files.  ADHB 2005-12-25
1185.65.17 by Robert Collins
Merge from integration, mode-changes are broken.
156
        # RBC 20060103 surely its not needed anyway, with codecs transcode
157
        # file support ?
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
158
        # JAM 20060103 We definitely don't want encode(..., 'replace')
159
        # these are valuable files which should have exact contents.
1185.65.29 by Robert Collins
Implement final review suggestions.
160
        if not isinstance(a_string, basestring):
161
            raise errors.BzrBadParameterNotString(a_string)
162
        self.put(path, StringIO(a_string.encode('utf-8')))
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
163
164
    def lock_write(self):
1551.2.3 by Aaron Bentley
Removed lock-related log spam [recommit]
165
        # mutter("lock write: %s (%s)", self, self._lock_count)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
166
        # TODO: Upgrade locking to support using a Transport,
167
        # and potentially a remote locking protocol
168
        if self._lock_mode:
169
            if self._lock_mode != 'w':
1185.65.27 by Robert Collins
Tweak storage towards mergability.
170
                raise ReadOnlyError("can't upgrade to a write lock from %r" %
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
171
                                self._lock_mode)
172
            self._lock_count += 1
173
        else:
174
            self._lock = self._transport.lock_write(
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
175
                    self._escape(self.lock_name))
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
176
            self._lock_mode = 'w'
177
            self._lock_count = 1
178
            self._set_transaction(transactions.PassThroughTransaction())
179
180
    def lock_read(self):
1551.2.3 by Aaron Bentley
Removed lock-related log spam [recommit]
181
        # mutter("lock read: %s (%s)", self, self._lock_count)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
182
        if self._lock_mode:
183
            assert self._lock_mode in ('r', 'w'), \
184
                   "invalid lock mode %r" % self._lock_mode
185
            self._lock_count += 1
186
        else:
187
            self._lock = self._transport.lock_read(
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
188
                    self._escape(self.lock_name))
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
189
            self._lock_mode = 'r'
190
            self._lock_count = 1
191
            self._set_transaction(transactions.ReadOnlyTransaction())
192
            # 5K may be excessive, but hey, its a knob.
193
            self.get_transaction().set_cache_size(5000)
194
                        
195
    def unlock(self):
1551.2.3 by Aaron Bentley
Removed lock-related log spam [recommit]
196
        # mutter("unlock: %s (%s)", self, self._lock_count)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
197
        if not self._lock_mode:
198
            raise LockError('branch %r is not locked' % (self))
199
200
        if self._lock_count > 1:
201
            self._lock_count -= 1
202
        else:
203
            self._finish_transaction()
204
            self._lock.unlock()
205
            self._lock = None
206
            self._lock_mode = self._lock_count = None
207
208
    def get_transaction(self):
209
        """Return the current active transaction.
210
211
        If no transaction is active, this returns a passthrough object
212
        for which all data is immediately flushed and no caching happens.
213
        """
214
        if self._transaction is None:
215
            return transactions.PassThroughTransaction()
216
        else:
217
            return self._transaction
218
219
    def _set_transaction(self, new_transaction):
220
        """Set a new active transaction."""
221
        if self._transaction is not None:
222
            raise errors.LockError('Branch %s is in a transaction already.' %
223
                                   self)
224
        self._transaction = new_transaction
225
226
    def _finish_transaction(self):
227
        """Exit the current transaction."""
228
        if self._transaction is None:
229
            raise errors.LockError('Branch %s is not in a transaction' %
230
                                   self)
231
        transaction = self._transaction
232
        self._transaction = None
233
        transaction.finish()