1
# Copyright (C) 2005, 2006 Canonical Ltd
1
# Copyright (C) 2005, 2006, 2008 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
102
107
def __del__(self):
103
108
if self.is_locked():
104
# XXX: This should show something every time, and be suitable for
105
# headless operation and embedding
106
from warnings import warn
107
warn("file group %r was not explicitly unlocked" % self)
109
# do not automatically unlock; there should have been a
110
# try/finally to unlock this.
111
warn("%r was gc'd while locked" % self)
113
def break_lock(self):
114
"""Break the lock of this lockable files group if it is held.
116
The current ui factory will be used to prompt for user conformation.
118
self._lock.break_lock()
110
120
def _escape(self, file_or_path):
111
121
if not isinstance(file_or_path, basestring):
112
122
file_or_path = '/'.join(file_or_path)
113
123
if file_or_path == '':
115
return bzrlib.transport.urlescape(safe_unicode(file_or_path))
125
return urlutils.escape(safe_unicode(file_or_path))
117
127
def _find_modes(self):
118
"""Determine the appropriate modes for files and directories."""
128
"""Determine the appropriate modes for files and directories.
130
:deprecated: Replaced by BzrDir._find_modes.
120
133
st = self._transport.stat('.')
121
134
except errors.TransportNotPossible:
122
135
self._dir_mode = 0755
123
136
self._file_mode = 0644
125
self._dir_mode = st.st_mode & 07777
138
# Check the directory mode, but also make sure the created
139
# directories and files are read-write for this user. This is
140
# mostly a workaround for filesystems which lie about being able to
141
# write to a directory (cygwin & win32)
142
self._dir_mode = (st.st_mode & 07777) | 00700
126
143
# Remove the sticky and execute bits for files
127
144
self._file_mode = self._dir_mode & ~07111
128
if not self._set_dir_mode:
129
self._dir_mode = None
130
if not self._set_file_mode:
131
self._file_mode = None
146
@deprecated_method(deprecated_in((1, 6, 0)))
133
147
def controlfilename(self, file_or_path):
134
"""Return location relative to branch."""
148
"""Return location relative to branch.
150
:deprecated: Use Transport methods instead.
135
152
return self._transport.abspath(self._escape(file_or_path))
137
@deprecated_method(zero_eight)
138
def controlfile(self, file_or_path, mode='r'):
139
"""Open a control file for this branch.
141
There are two classes of file in a lockable directory: text
142
and binary. binary files are untranslated byte streams. Text
143
control files are stored with Unix newlines and in UTF-8, even
144
if the platform or locale defaults are different.
146
Such files are not openable in write mode : they are managed via
147
put and put_utf8 which atomically replace old versions using
151
relpath = self._escape(file_or_path)
152
# TODO: codecs.open() buffers linewise, so it was overloaded with
153
# a much larger buffer, do we need to do the same for getreader/getwriter?
155
return self.get(relpath)
157
raise BzrError("Branch.controlfile(mode='wb') is not supported, use put[_utf8]")
159
return self.get_utf8(relpath)
161
raise BzrError("Branch.controlfile(mode='w') is not supported, use put[_utf8]")
163
raise BzrError("invalid controlfile mode %r" % mode)
155
@deprecated_method(deprecated_in((1, 5, 0)))
166
156
def get(self, relpath):
167
"""Get a file as a bytestream."""
157
"""Get a file as a bytestream.
159
:deprecated: Use a Transport instead of LockableFiles.
168
161
relpath = self._escape(relpath)
169
162
return self._transport.get(relpath)
165
@deprecated_method(deprecated_in((1, 5, 0)))
172
166
def get_utf8(self, relpath):
173
"""Get a file as a unicode stream."""
167
"""Get a file as a unicode stream.
169
:deprecated: Use a Transport instead of LockableFiles.
174
171
relpath = self._escape(relpath)
175
172
# DO NOT introduce an errors=replace here.
176
173
return codecs.getreader('utf-8')(self._transport.get(relpath))
178
175
@needs_write_lock
176
@deprecated_method(deprecated_in((1, 6, 0)))
179
177
def put(self, path, file):
182
180
:param path: The path to put the file, relative to the .bzr control
184
:param f: A file-like or string object whose contents should be copied.
186
self._transport.put(self._escape(path), file, mode=self._file_mode)
182
:param file: A file-like or string object whose contents should be copied.
184
:deprecated: Use Transport methods instead.
186
self._transport.put_file(self._escape(path), file, mode=self._file_mode)
189
@deprecated_method(deprecated_in((1, 6, 0)))
190
def put_bytes(self, path, a_string):
191
"""Write a string of bytes.
193
:param path: The path to put the bytes, relative to the transport root.
194
:param a_string: A string object, whose exact bytes are to be copied.
196
:deprecated: Use Transport methods instead.
198
self._transport.put_bytes(self._escape(path), a_string,
199
mode=self._file_mode)
202
@deprecated_method(deprecated_in((1, 6, 0)))
189
203
def put_utf8(self, path, a_string):
190
204
"""Write a string, encoding as utf-8.
192
206
:param path: The path to put the string, relative to the transport root.
193
:param string: A file-like or string object whose contents should be copied.
207
:param string: A string or unicode object whose contents should be copied.
209
:deprecated: Use Transport methods instead.
195
211
# IterableFile would not be needed if Transport.put took iterables
196
212
# instead of files. ADHB 2005-12-25
200
216
# these are valuable files which should have exact contents.
201
217
if not isinstance(a_string, basestring):
202
218
raise errors.BzrBadParameterNotString(a_string)
203
self.put(path, StringIO(a_string.encode('utf-8')))
205
def lock_write(self):
219
self.put_bytes(path, a_string.encode('utf-8'))
221
def leave_in_place(self):
222
"""Set this LockableFiles to not clear the physical lock on unlock."""
223
self._lock.leave_in_place()
225
def dont_leave_in_place(self):
226
"""Set this LockableFiles to clear the physical lock on unlock."""
227
self._lock.dont_leave_in_place()
229
def lock_write(self, token=None):
230
"""Lock this group of files for writing.
232
:param token: if this is already locked, then lock_write will fail
233
unless the token matches the existing lock.
234
:returns: a token if this instance supports tokens, otherwise None.
235
:raises TokenLockingNotSupported: when a token is given but this
236
instance doesn't support using token locks.
237
:raises MismatchedToken: if the specified token doesn't match the token
238
of the existing lock.
240
A token should be passed in if you know that you have locked the object
241
some other way, and need to synchronise this object's state with that
206
244
# mutter("lock write: %s (%s)", self, self._lock_count)
207
245
# TODO: Upgrade locking to support using a Transport,
208
246
# and potentially a remote locking protocol
209
247
if self._lock_mode:
210
248
if self._lock_mode != 'w' or not self.get_transaction().writeable():
211
raise ReadOnlyError(self)
249
raise errors.ReadOnlyError(self)
250
self._lock.validate_token(token)
212
251
self._lock_count += 1
252
return self._token_from_lock
214
self._lock.lock_write()
254
token_from_lock = self._lock.lock_write(token=token)
215
255
#note('write locking %s', self)
216
256
#traceback.print_stack()
217
257
self._lock_mode = 'w'
218
258
self._lock_count = 1
219
259
self._set_transaction(transactions.WriteTransaction())
260
self._token_from_lock = token_from_lock
261
return token_from_lock
221
263
def lock_read(self):
222
264
# mutter("lock read: %s (%s)", self, self._lock_count)
223
265
if self._lock_mode:
224
assert self._lock_mode in ('r', 'w'), \
225
"invalid lock mode %r" % self._lock_mode
266
if self._lock_mode not in ('r', 'w'):
267
raise ValueError("invalid lock mode %r" % (self._lock_mode,))
226
268
self._lock_count += 1
228
270
self._lock.lock_read()