/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/lockable_files.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-04-06 06:59:03 UTC
  • mfrom: (5051.5.1 subunit)
  • Revision ID: pqm@pqm.ubuntu.com-20100406065903-y9dxgwmog1pmw7dz
Use subunit when running tests in PQM.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2008, 2009 Canonical Ltd
2
2
#
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
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
from __future__ import absolute_import
 
17
from cStringIO import StringIO
18
18
 
19
 
from .lazy_import import lazy_import
 
19
from bzrlib.lazy_import import lazy_import
20
20
lazy_import(globals(), """
 
21
import codecs
21
22
import warnings
22
23
 
23
 
from breezy import (
 
24
from bzrlib import (
24
25
    counted_lock,
25
26
    errors,
26
27
    lock,
30
31
    )
31
32
""")
32
33
 
33
 
from .decorators import (
 
34
from bzrlib.decorators import (
34
35
    only_raises,
35
36
    )
 
37
from bzrlib.symbol_versioning import (
 
38
    deprecated_in,
 
39
    deprecated_method,
 
40
    )
 
41
 
 
42
 
 
43
# XXX: The tracking here of lock counts and whether the lock is held is
 
44
# somewhat redundant with what's done in LockDir; the main difference is that
 
45
# LockableFiles permits reentrancy.
 
46
 
 
47
class _LockWarner(object):
 
48
    """Hold a counter for a lock and warn if GCed while the count is >= 1.
 
49
 
 
50
    This is separate from LockableFiles because putting a __del__ on
 
51
    LockableFiles can result in uncollectable cycles.
 
52
    """
 
53
 
 
54
    def __init__(self, repr):
 
55
        self.lock_count = 0
 
56
        self.repr = repr
 
57
 
 
58
    def __del__(self):
 
59
        if self.lock_count >= 1:
 
60
            # There should have been a try/finally to unlock this.
 
61
            warnings.warn("%r was gc'd while locked" % self.repr)
36
62
 
37
63
 
38
64
class LockableFiles(object):
49
75
    This class is now deprecated; code should move to using the Transport
50
76
    directly for file operations and using the lock or CountedLock for
51
77
    locking.
52
 
 
 
78
    
53
79
    :ivar _lock: The real underlying lock (e.g. a LockDir)
54
 
    :ivar _lock_count: If _lock_mode is true, a positive count of the number
55
 
        of times the lock has been taken (and not yet released) *by this
56
 
        process*, through this particular object instance.
57
 
    :ivar _lock_mode: None, or 'r' or 'w'
 
80
    :ivar _counted_lock: A lock decorated with a semaphore, so that it 
 
81
        can be re-entered.
58
82
    """
59
83
 
 
84
    # _lock_mode: None, or 'r' or 'w'
 
85
 
 
86
    # _lock_count: If _lock_mode is true, a positive count of the number of
 
87
    # times the lock has been taken *by this process*.
 
88
 
60
89
    def __init__(self, transport, lock_name, lock_class):
61
90
        """Create a LockableFiles group
62
91
 
70
99
        self.lock_name = lock_name
71
100
        self._transaction = None
72
101
        self._lock_mode = None
73
 
        self._lock_count = 0
 
102
        self._lock_warner = _LockWarner(repr(self))
74
103
        self._find_modes()
75
104
        esc_name = self._escape(lock_name)
76
105
        self._lock = lock_class(transport, esc_name,
89
118
    def __repr__(self):
90
119
        return '%s(%r)' % (self.__class__.__name__,
91
120
                           self._transport)
92
 
 
93
121
    def __str__(self):
94
122
        return 'LockableFiles(%s, %s)' % (self.lock_name, self._transport.base)
95
123
 
102
130
 
103
131
    def _escape(self, file_or_path):
104
132
        """DEPRECATED: Do not use outside this class"""
 
133
        if not isinstance(file_or_path, basestring):
 
134
            file_or_path = '/'.join(file_or_path)
105
135
        if file_or_path == '':
106
136
            return u''
107
 
        return urlutils.escape(file_or_path)
 
137
        return urlutils.escape(osutils.safe_unicode(file_or_path))
108
138
 
109
139
    def _find_modes(self):
110
140
        """Determine the appropriate modes for files and directories.
117
147
        try:
118
148
            st = self._transport.stat('.')
119
149
        except errors.TransportNotPossible:
120
 
            self._dir_mode = 0o755
121
 
            self._file_mode = 0o644
 
150
            self._dir_mode = 0755
 
151
            self._file_mode = 0644
122
152
        else:
123
153
            # Check the directory mode, but also make sure the created
124
154
            # directories and files are read-write for this user. This is
125
155
            # mostly a workaround for filesystems which lie about being able to
126
156
            # write to a directory (cygwin & win32)
127
 
            self._dir_mode = (st.st_mode & 0o7777) | 0o0700
 
157
            self._dir_mode = (st.st_mode & 07777) | 00700
128
158
            # Remove the sticky and execute bits for files
129
 
            self._file_mode = self._dir_mode & ~0o7111
 
159
            self._file_mode = self._dir_mode & ~07111
130
160
 
131
161
    def leave_in_place(self):
132
162
        """Set this LockableFiles to not clear the physical lock on unlock."""
151
181
        some other way, and need to synchronise this object's state with that
152
182
        fact.
153
183
        """
 
184
        # TODO: Upgrade locking to support using a Transport,
 
185
        # and potentially a remote locking protocol
154
186
        if self._lock_mode:
155
 
            if (self._lock_mode != 'w'
156
 
                or not self.get_transaction().writeable()):
 
187
            if self._lock_mode != 'w' or not self.get_transaction().writeable():
157
188
                raise errors.ReadOnlyError(self)
158
189
            self._lock.validate_token(token)
159
 
            self._lock_count += 1
 
190
            self._lock_warner.lock_count += 1
160
191
            return self._token_from_lock
161
192
        else:
162
193
            token_from_lock = self._lock.lock_write(token=token)
163
194
            #traceback.print_stack()
164
195
            self._lock_mode = 'w'
165
 
            self._lock_count = 1
 
196
            self._lock_warner.lock_count = 1
166
197
            self._set_write_transaction()
167
198
            self._token_from_lock = token_from_lock
168
199
            return token_from_lock
171
202
        if self._lock_mode:
172
203
            if self._lock_mode not in ('r', 'w'):
173
204
                raise ValueError("invalid lock mode %r" % (self._lock_mode,))
174
 
            self._lock_count += 1
 
205
            self._lock_warner.lock_count += 1
175
206
        else:
176
207
            self._lock.lock_read()
177
208
            #traceback.print_stack()
178
209
            self._lock_mode = 'r'
179
 
            self._lock_count = 1
 
210
            self._lock_warner.lock_count = 1
180
211
            self._set_read_transaction()
181
212
 
182
213
    def _set_read_transaction(self):
193
224
    def unlock(self):
194
225
        if not self._lock_mode:
195
226
            return lock.cant_unlock_not_held(self)
196
 
        if self._lock_count > 1:
197
 
            self._lock_count -= 1
 
227
        if self._lock_warner.lock_count > 1:
 
228
            self._lock_warner.lock_count -= 1
198
229
        else:
199
230
            #traceback.print_stack()
200
231
            self._finish_transaction()
201
232
            try:
202
233
                self._lock.unlock()
203
234
            finally:
204
 
                self._lock_count = 0
205
 
                self._lock_mode = None
 
235
                self._lock_mode = self._lock_warner.lock_count = None
 
236
 
 
237
    @property
 
238
    def _lock_count(self):
 
239
        return self._lock_warner.lock_count
206
240
 
207
241
    def is_locked(self):
208
242
        """Return true if this LockableFiles group is locked"""
209
 
        return self._lock_count >= 1
 
243
        return self._lock_warner.lock_count >= 1
210
244
 
211
245
    def get_physical_lock_status(self):
212
246
        """Return physical lock status.
292
326
    def create(self, mode=None):
293
327
        """Create lock mechanism"""
294
328
        # for old-style locks, create the file now
295
 
        self._transport.put_bytes(self._escaped_name, b'',
 
329
        self._transport.put_bytes(self._escaped_name, '',
296
330
                            mode=self._file_modebits)
297
331
 
298
332
    def validate_token(self, token):
299
333
        if token is not None:
300
334
            raise errors.TokenLockingNotSupported(self)
 
335