/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 breezy/lock.py

  • Committer: Jelmer Vernooij
  • Date: 2018-05-06 11:48:54 UTC
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: jelmer@jelmer.uk-20180506114854-h4qd9ojaqy8wxjsd
Move .mailmap to root.

Show diffs side-by-side

added added

removed removed

Lines of Context:
50
50
from .hooks import Hooks
51
51
from .i18n import gettext
52
52
 
53
 
 
54
53
class LockHooks(Hooks):
55
54
 
56
55
    def __init__(self):
57
56
        Hooks.__init__(self, "breezy.lock", "Lock.hooks")
58
 
        self.add_hook(
59
 
            'lock_acquired',
 
57
        self.add_hook('lock_acquired',
60
58
            "Called with a breezy.lock.LockResult when a physical lock is "
61
59
            "acquired.", (1, 8))
62
 
        self.add_hook(
63
 
            'lock_released',
 
60
        self.add_hook('lock_released',
64
61
            "Called with a breezy.lock.LockResult when a physical lock is "
65
62
            "released.", (1, 8))
66
 
        self.add_hook(
67
 
            'lock_broken',
 
63
        self.add_hook('lock_broken',
68
64
            "Called with a breezy.lock.LockResult when a physical lock is "
69
65
            "broken.", (1, 15))
70
66
 
91
87
 
92
88
    def __repr__(self):
93
89
        return '%s(%s, %s)' % (self.__class__.__name__,
94
 
                               self.lock_url, self.details)
 
90
                             self.lock_url, self.details)
95
91
 
96
92
 
97
93
class LogicalLockResult(object):
114
110
        # If there was an error raised, prefer the original one
115
111
        try:
116
112
            self.unlock()
117
 
        except BaseException:
 
113
        except:
118
114
            if exc_type is None:
119
115
                raise
120
116
        return False
123
119
def cant_unlock_not_held(locked_object):
124
120
    """An attempt to unlock failed because the object was not locked.
125
121
 
126
 
    This provides a policy point from which we can generate either a warning or
127
 
    an exception.
 
122
    This provides a policy point from which we can generate either a warning 
 
123
    or an exception.
128
124
    """
129
125
    # This is typically masking some other error and called from a finally
130
126
    # block, so it's useful to have the option not to generate a new error
132
128
    # raise LockNotHeld.
133
129
    if 'unlock' in debug.debug_flags:
134
130
        warnings.warn("%r is already unlocked" % (locked_object,),
135
 
                      stacklevel=3)
 
131
            stacklevel=3)
136
132
    else:
137
133
        raise errors.LockNotHeld(locked_object)
138
134
 
143
139
except ImportError:
144
140
    have_fcntl = False
145
141
 
 
142
have_pywin32 = False
146
143
have_ctypes_win32 = False
147
144
if sys.platform == 'win32':
148
145
    import msvcrt
149
146
    try:
 
147
        import win32file, pywintypes, winerror
 
148
        have_pywin32 = True
 
149
    except ImportError:
 
150
        pass
 
151
 
 
152
    try:
150
153
        import ctypes
151
154
        have_ctypes_win32 = True
152
155
    except ImportError:
197
200
            fcntl.lockf(self.f, fcntl.LOCK_UN)
198
201
            self._clear_f()
199
202
 
 
203
 
200
204
    class _fcntl_WriteLock(_fcntl_FileLock):
201
205
 
202
206
        _open_locks = set()
237
241
            _fcntl_WriteLock._open_locks.remove(self.filename)
238
242
            self._unlock()
239
243
 
 
244
 
240
245
    class _fcntl_ReadLock(_fcntl_FileLock):
241
246
 
242
247
        _open_locks = {}
284
289
            """
285
290
            if self.filename in _fcntl_WriteLock._open_locks:
286
291
                raise AssertionError('file already locked: %r'
287
 
                                     % (self.filename,))
 
292
                    % (self.filename,))
288
293
            try:
289
294
                wlock = _fcntl_TemporaryWriteLock(self)
290
295
            except errors.LockError:
292
297
                return False, self
293
298
            return True, wlock
294
299
 
 
300
 
295
301
    class _fcntl_TemporaryWriteLock(_OSLock):
296
302
        """A token used when grabbing a temporary_write_lock.
297
303
 
311
317
 
312
318
            if self.filename in _fcntl_WriteLock._open_locks:
313
319
                raise AssertionError('file already locked: %r'
314
 
                                     % (self.filename,))
 
320
                    % (self.filename,))
315
321
 
316
322
            # See if we can open the file for writing. Another process might
317
323
            # have a read lock. We don't use self._open() because we don't want
336
342
 
337
343
        def restore_read_lock(self):
338
344
            """Restore the original ReadLock."""
339
 
            # For fcntl, since we never released the read lock, just release
340
 
            # the write lock, and return the original lock.
 
345
            # For fcntl, since we never released the read lock, just release the
 
346
            # write lock, and return the original lock.
341
347
            fcntl.lockf(self.f, fcntl.LOCK_UN)
342
348
            self._clear_f()
343
349
            _fcntl_WriteLock._open_locks.remove(self.filename)
346
352
            self._read_lock = None
347
353
            return read_lock
348
354
 
 
355
 
349
356
    _lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
350
357
 
351
358
 
 
359
if have_pywin32 and sys.platform == 'win32':
 
360
    win32file_CreateFile = win32file.CreateFileW
 
361
 
 
362
    class _w32c_FileLock(_OSLock):
 
363
 
 
364
        def _open(self, filename, access, share, cflags, pymode):
 
365
            self.filename = osutils.realpath(filename)
 
366
            try:
 
367
                self._handle = win32file_CreateFile(filename, access, share,
 
368
                    None, win32file.OPEN_ALWAYS,
 
369
                    win32file.FILE_ATTRIBUTE_NORMAL, None)
 
370
            except pywintypes.error as e:
 
371
                if e.args[0] == winerror.ERROR_ACCESS_DENIED:
 
372
                    raise errors.LockFailed(filename, e)
 
373
                if e.args[0] == winerror.ERROR_SHARING_VIOLATION:
 
374
                    raise errors.LockContention(filename, e)
 
375
                raise
 
376
            fd = win32file._open_osfhandle(self._handle, cflags)
 
377
            self.f = os.fdopen(fd, pymode)
 
378
            return self.f
 
379
 
 
380
        def unlock(self):
 
381
            self._clear_f()
 
382
            self._handle = None
 
383
 
 
384
 
 
385
    class _w32c_ReadLock(_w32c_FileLock):
 
386
        def __init__(self, filename):
 
387
            super(_w32c_ReadLock, self).__init__()
 
388
            self._open(filename, win32file.GENERIC_READ,
 
389
                win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
 
390
 
 
391
        def temporary_write_lock(self):
 
392
            """Try to grab a write lock on the file.
 
393
 
 
394
            On platforms that support it, this will upgrade to a write lock
 
395
            without unlocking the file.
 
396
            Otherwise, this will release the read lock, and try to acquire a
 
397
            write lock.
 
398
 
 
399
            :return: A token which can be used to switch back to a read lock.
 
400
            """
 
401
            # I can't find a way to upgrade a read lock to a write lock without
 
402
            # unlocking first. So here, we do just that.
 
403
            self.unlock()
 
404
            try:
 
405
                wlock = _w32c_WriteLock(self.filename)
 
406
            except errors.LockError:
 
407
                return False, _w32c_ReadLock(self.filename)
 
408
            return True, wlock
 
409
 
 
410
 
 
411
    class _w32c_WriteLock(_w32c_FileLock):
 
412
        def __init__(self, filename):
 
413
            super(_w32c_WriteLock, self).__init__()
 
414
            self._open(filename,
 
415
                win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
 
416
                os.O_RDWR, "rb+")
 
417
 
 
418
        def restore_read_lock(self):
 
419
            """Restore the original ReadLock."""
 
420
            # For win32 we had to completely let go of the original lock, so we
 
421
            # just unlock and create a new read lock.
 
422
            self.unlock()
 
423
            return _w32c_ReadLock(self.filename)
 
424
 
 
425
 
 
426
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
 
427
 
 
428
 
352
429
if have_ctypes_win32:
353
 
    from ctypes.wintypes import DWORD, LPWSTR
354
 
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p  # used as NULL no need to declare
355
 
    HANDLE = ctypes.c_int  # rather than unsigned as in ctypes.wintypes
 
430
    from ctypes.wintypes import DWORD, LPCSTR, LPCWSTR
 
431
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p # used as NULL no need to declare
 
432
    HANDLE = ctypes.c_int # rather than unsigned as in ctypes.wintypes
356
433
    _function_name = "CreateFileW"
357
434
 
358
435
    # CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
359
436
    _CreateFile = ctypes.WINFUNCTYPE(
360
 
        HANDLE,                # return value
361
 
        LPWSTR,                # lpFileName
362
 
        DWORD,                 # dwDesiredAccess
363
 
        DWORD,                 # dwShareMode
364
 
        LPSECURITY_ATTRIBUTES,  # lpSecurityAttributes
365
 
        DWORD,                 # dwCreationDisposition
366
 
        DWORD,                 # dwFlagsAndAttributes
367
 
        HANDLE                 # hTemplateFile
 
437
            HANDLE,                # return value
 
438
            LPWSTR,                # lpFileName
 
439
            DWORD,                 # dwDesiredAccess
 
440
            DWORD,                 # dwShareMode
 
441
            LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
 
442
            DWORD,                 # dwCreationDisposition
 
443
            DWORD,                 # dwFlagsAndAttributes
 
444
            HANDLE                 # hTemplateFile
368
445
        )((_function_name, ctypes.windll.kernel32))
369
446
 
370
447
    INVALID_HANDLE_VALUE = -1
383
460
        def _open(self, filename, access, share, cflags, pymode):
384
461
            self.filename = osutils.realpath(filename)
385
462
            handle = _CreateFile(filename, access, share, None, OPEN_ALWAYS,
386
 
                                 FILE_ATTRIBUTE_NORMAL, 0)
 
463
                FILE_ATTRIBUTE_NORMAL, 0)
387
464
            if handle in (INVALID_HANDLE_VALUE, 0):
388
465
                e = ctypes.WinError()
389
466
                if e.args[0] == ERROR_ACCESS_DENIED:
398
475
        def unlock(self):
399
476
            self._clear_f()
400
477
 
 
478
 
401
479
    class _ctypes_ReadLock(_ctypes_FileLock):
402
480
        def __init__(self, filename):
403
481
            super(_ctypes_ReadLock, self).__init__()
404
482
            self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
405
 
                       "rb")
 
483
                "rb")
406
484
 
407
485
        def temporary_write_lock(self):
408
486
            """Try to grab a write lock on the file.
427
505
        def __init__(self, filename):
428
506
            super(_ctypes_WriteLock, self).__init__()
429
507
            self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
430
 
                       "rb+")
 
508
                "rb+")
431
509
 
432
510
        def restore_read_lock(self):
433
511
            """Restore the original ReadLock."""
436
514
            self.unlock()
437
515
            return _ctypes_ReadLock(self.filename)
438
516
 
 
517
 
439
518
    _lock_classes.append(('ctypes', _ctypes_WriteLock, _ctypes_ReadLock))
440
519
 
441
520
 
442
521
if len(_lock_classes) == 0:
443
522
    raise NotImplementedError(
444
 
        "We must have one of fcntl or ctypes available"
 
523
        "We must have one of fcntl, pywin32, or ctypes available"
445
524
        " to support OS locking."
446
525
        )
447
526
 
470
549
            trace.note(gettext('{0!r} was {1} locked again'), self, type_name)
471
550
        self._prev_lock = lock_type
472
551
 
473
 
 
474
552
@contextlib.contextmanager
475
553
def write_locked(lockable):
476
554
    lockable.lock_write()