/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: 2017-06-08 23:30:31 UTC
  • mto: This revision was merged to the branch mainline in revision 6690.
  • Revision ID: jelmer@jelmer.uk-20170608233031-3qavls2o7a1pqllj
Update imports.

Show diffs side-by-side

added added

removed removed

Lines of Context:
33
33
unlock() method.
34
34
"""
35
35
 
 
36
from __future__ import absolute_import
 
37
 
36
38
import contextlib
37
39
import errno
38
40
import os
48
50
from .hooks import Hooks
49
51
from .i18n import gettext
50
52
 
51
 
 
52
53
class LockHooks(Hooks):
53
54
 
54
55
    def __init__(self):
55
56
        Hooks.__init__(self, "breezy.lock", "Lock.hooks")
56
 
        self.add_hook(
57
 
            'lock_acquired',
 
57
        self.add_hook('lock_acquired',
58
58
            "Called with a breezy.lock.LockResult when a physical lock is "
59
59
            "acquired.", (1, 8))
60
 
        self.add_hook(
61
 
            'lock_released',
 
60
        self.add_hook('lock_released',
62
61
            "Called with a breezy.lock.LockResult when a physical lock is "
63
62
            "released.", (1, 8))
64
 
        self.add_hook(
65
 
            'lock_broken',
 
63
        self.add_hook('lock_broken',
66
64
            "Called with a breezy.lock.LockResult when a physical lock is "
67
65
            "broken.", (1, 15))
68
66
 
89
87
 
90
88
    def __repr__(self):
91
89
        return '%s(%s, %s)' % (self.__class__.__name__,
92
 
                               self.lock_url, self.details)
 
90
                             self.lock_url, self.details)
93
91
 
94
92
 
95
93
class LogicalLockResult(object):
98
96
    :ivar unlock: A callable which will unlock the lock.
99
97
    """
100
98
 
101
 
    def __init__(self, unlock, token=None):
 
99
    def __init__(self, unlock):
102
100
        self.unlock = unlock
103
 
        self.token = token
104
101
 
105
102
    def __repr__(self):
106
103
        return "LogicalLockResult(%s)" % (self.unlock)
107
104
 
108
 
    def __enter__(self):
109
 
        return self
110
 
 
111
 
    def __exit__(self, exc_type, exc_val, exc_tb):
112
 
        # If there was an error raised, prefer the original one
113
 
        try:
114
 
            self.unlock()
115
 
        except BaseException:
116
 
            if exc_type is None:
117
 
                raise
118
 
        return False
119
105
 
120
106
 
121
107
def cant_unlock_not_held(locked_object):
122
108
    """An attempt to unlock failed because the object was not locked.
123
109
 
124
 
    This provides a policy point from which we can generate either a warning or
125
 
    an exception.
 
110
    This provides a policy point from which we can generate either a warning 
 
111
    or an exception.
126
112
    """
127
113
    # This is typically masking some other error and called from a finally
128
114
    # block, so it's useful to have the option not to generate a new error
130
116
    # raise LockNotHeld.
131
117
    if 'unlock' in debug.debug_flags:
132
118
        warnings.warn("%r is already unlocked" % (locked_object,),
133
 
                      stacklevel=3)
 
119
            stacklevel=3)
134
120
    else:
135
121
        raise errors.LockNotHeld(locked_object)
136
122
 
141
127
except ImportError:
142
128
    have_fcntl = False
143
129
 
 
130
have_pywin32 = False
144
131
have_ctypes_win32 = False
145
132
if sys.platform == 'win32':
146
133
    import msvcrt
147
134
    try:
 
135
        import win32file, pywintypes, winerror
 
136
        have_pywin32 = True
 
137
    except ImportError:
 
138
        pass
 
139
 
 
140
    try:
148
141
        import ctypes
149
142
        have_ctypes_win32 = True
150
143
    except ImportError:
195
188
            fcntl.lockf(self.f, fcntl.LOCK_UN)
196
189
            self._clear_f()
197
190
 
 
191
 
198
192
    class _fcntl_WriteLock(_fcntl_FileLock):
199
193
 
200
194
        _open_locks = set()
235
229
            _fcntl_WriteLock._open_locks.remove(self.filename)
236
230
            self._unlock()
237
231
 
 
232
 
238
233
    class _fcntl_ReadLock(_fcntl_FileLock):
239
234
 
240
235
        _open_locks = {}
282
277
            """
283
278
            if self.filename in _fcntl_WriteLock._open_locks:
284
279
                raise AssertionError('file already locked: %r'
285
 
                                     % (self.filename,))
 
280
                    % (self.filename,))
286
281
            try:
287
282
                wlock = _fcntl_TemporaryWriteLock(self)
288
283
            except errors.LockError:
290
285
                return False, self
291
286
            return True, wlock
292
287
 
 
288
 
293
289
    class _fcntl_TemporaryWriteLock(_OSLock):
294
290
        """A token used when grabbing a temporary_write_lock.
295
291
 
309
305
 
310
306
            if self.filename in _fcntl_WriteLock._open_locks:
311
307
                raise AssertionError('file already locked: %r'
312
 
                                     % (self.filename,))
 
308
                    % (self.filename,))
313
309
 
314
310
            # See if we can open the file for writing. Another process might
315
311
            # have a read lock. We don't use self._open() because we don't want
334
330
 
335
331
        def restore_read_lock(self):
336
332
            """Restore the original ReadLock."""
337
 
            # For fcntl, since we never released the read lock, just release
338
 
            # the write lock, and return the original lock.
 
333
            # For fcntl, since we never released the read lock, just release the
 
334
            # write lock, and return the original lock.
339
335
            fcntl.lockf(self.f, fcntl.LOCK_UN)
340
336
            self._clear_f()
341
337
            _fcntl_WriteLock._open_locks.remove(self.filename)
344
340
            self._read_lock = None
345
341
            return read_lock
346
342
 
 
343
 
347
344
    _lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
348
345
 
349
346
 
 
347
if have_pywin32 and sys.platform == 'win32':
 
348
    win32file_CreateFile = win32file.CreateFileW
 
349
 
 
350
    class _w32c_FileLock(_OSLock):
 
351
 
 
352
        def _open(self, filename, access, share, cflags, pymode):
 
353
            self.filename = osutils.realpath(filename)
 
354
            try:
 
355
                self._handle = win32file_CreateFile(filename, access, share,
 
356
                    None, win32file.OPEN_ALWAYS,
 
357
                    win32file.FILE_ATTRIBUTE_NORMAL, None)
 
358
            except pywintypes.error as e:
 
359
                if e.args[0] == winerror.ERROR_ACCESS_DENIED:
 
360
                    raise errors.LockFailed(filename, e)
 
361
                if e.args[0] == winerror.ERROR_SHARING_VIOLATION:
 
362
                    raise errors.LockContention(filename, e)
 
363
                raise
 
364
            fd = win32file._open_osfhandle(self._handle, cflags)
 
365
            self.f = os.fdopen(fd, pymode)
 
366
            return self.f
 
367
 
 
368
        def unlock(self):
 
369
            self._clear_f()
 
370
            self._handle = None
 
371
 
 
372
 
 
373
    class _w32c_ReadLock(_w32c_FileLock):
 
374
        def __init__(self, filename):
 
375
            super(_w32c_ReadLock, self).__init__()
 
376
            self._open(filename, win32file.GENERIC_READ,
 
377
                win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
 
378
 
 
379
        def temporary_write_lock(self):
 
380
            """Try to grab a write lock on the file.
 
381
 
 
382
            On platforms that support it, this will upgrade to a write lock
 
383
            without unlocking the file.
 
384
            Otherwise, this will release the read lock, and try to acquire a
 
385
            write lock.
 
386
 
 
387
            :return: A token which can be used to switch back to a read lock.
 
388
            """
 
389
            # I can't find a way to upgrade a read lock to a write lock without
 
390
            # unlocking first. So here, we do just that.
 
391
            self.unlock()
 
392
            try:
 
393
                wlock = _w32c_WriteLock(self.filename)
 
394
            except errors.LockError:
 
395
                return False, _w32c_ReadLock(self.filename)
 
396
            return True, wlock
 
397
 
 
398
 
 
399
    class _w32c_WriteLock(_w32c_FileLock):
 
400
        def __init__(self, filename):
 
401
            super(_w32c_WriteLock, self).__init__()
 
402
            self._open(filename,
 
403
                win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
 
404
                os.O_RDWR, "rb+")
 
405
 
 
406
        def restore_read_lock(self):
 
407
            """Restore the original ReadLock."""
 
408
            # For win32 we had to completely let go of the original lock, so we
 
409
            # just unlock and create a new read lock.
 
410
            self.unlock()
 
411
            return _w32c_ReadLock(self.filename)
 
412
 
 
413
 
 
414
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
 
415
 
 
416
 
350
417
if have_ctypes_win32:
351
 
    from ctypes.wintypes import DWORD, LPWSTR
352
 
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p  # used as NULL no need to declare
353
 
    HANDLE = ctypes.c_int  # rather than unsigned as in ctypes.wintypes
 
418
    from ctypes.wintypes import DWORD, LPCSTR, LPCWSTR
 
419
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p # used as NULL no need to declare
 
420
    HANDLE = ctypes.c_int # rather than unsigned as in ctypes.wintypes
354
421
    _function_name = "CreateFileW"
355
422
 
356
423
    # CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
357
424
    _CreateFile = ctypes.WINFUNCTYPE(
358
 
        HANDLE,                # return value
359
 
        LPWSTR,                # lpFileName
360
 
        DWORD,                 # dwDesiredAccess
361
 
        DWORD,                 # dwShareMode
362
 
        LPSECURITY_ATTRIBUTES,  # lpSecurityAttributes
363
 
        DWORD,                 # dwCreationDisposition
364
 
        DWORD,                 # dwFlagsAndAttributes
365
 
        HANDLE                 # hTemplateFile
 
425
            HANDLE,                # return value
 
426
            LPWSTR,                # lpFileName
 
427
            DWORD,                 # dwDesiredAccess
 
428
            DWORD,                 # dwShareMode
 
429
            LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
 
430
            DWORD,                 # dwCreationDisposition
 
431
            DWORD,                 # dwFlagsAndAttributes
 
432
            HANDLE                 # hTemplateFile
366
433
        )((_function_name, ctypes.windll.kernel32))
367
434
 
368
435
    INVALID_HANDLE_VALUE = -1
381
448
        def _open(self, filename, access, share, cflags, pymode):
382
449
            self.filename = osutils.realpath(filename)
383
450
            handle = _CreateFile(filename, access, share, None, OPEN_ALWAYS,
384
 
                                 FILE_ATTRIBUTE_NORMAL, 0)
 
451
                FILE_ATTRIBUTE_NORMAL, 0)
385
452
            if handle in (INVALID_HANDLE_VALUE, 0):
386
453
                e = ctypes.WinError()
387
454
                if e.args[0] == ERROR_ACCESS_DENIED:
396
463
        def unlock(self):
397
464
            self._clear_f()
398
465
 
 
466
 
399
467
    class _ctypes_ReadLock(_ctypes_FileLock):
400
468
        def __init__(self, filename):
401
469
            super(_ctypes_ReadLock, self).__init__()
402
470
            self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
403
 
                       "rb")
 
471
                "rb")
404
472
 
405
473
        def temporary_write_lock(self):
406
474
            """Try to grab a write lock on the file.
425
493
        def __init__(self, filename):
426
494
            super(_ctypes_WriteLock, self).__init__()
427
495
            self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
428
 
                       "rb+")
 
496
                "rb+")
429
497
 
430
498
        def restore_read_lock(self):
431
499
            """Restore the original ReadLock."""
434
502
            self.unlock()
435
503
            return _ctypes_ReadLock(self.filename)
436
504
 
 
505
 
437
506
    _lock_classes.append(('ctypes', _ctypes_WriteLock, _ctypes_ReadLock))
438
507
 
439
508
 
440
509
if len(_lock_classes) == 0:
441
510
    raise NotImplementedError(
442
 
        "We must have one of fcntl or ctypes available"
 
511
        "We must have one of fcntl, pywin32, or ctypes available"
443
512
        " to support OS locking."
444
513
        )
445
514
 
468
537
            trace.note(gettext('{0!r} was {1} locked again'), self, type_name)
469
538
        self._prev_lock = lock_type
470
539
 
471
 
 
472
540
@contextlib.contextmanager
473
541
def write_locked(lockable):
474
542
    lockable.lock_write()