/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/lockdir.py

  • Committer: Jelmer Vernooij
  • Date: 2018-02-18 21:42:57 UTC
  • mto: This revision was merged to the branch mainline in revision 6859.
  • Revision ID: jelmer@jelmer.uk-20180218214257-jpevutp1wa30tz3v
Update TODO to reference Breezy, not Bazaar.

Show diffs side-by-side

added added

removed removed

Lines of Context:
98
98
but helps protect against colliding host names.
99
99
"""
100
100
 
 
101
from __future__ import absolute_import
 
102
 
101
103
 
102
104
# TODO: We sometimes have the problem that our attempt to rename '1234' to
103
105
# 'held' fails because the transport server moves into an existing directory,
122
124
    )
123
125
from .decorators import only_raises
124
126
from .errors import (
125
 
    DirectoryNotEmpty,
126
 
    FileExists,
127
 
    LockBreakMismatch,
128
 
    LockBroken,
129
 
    LockContention,
130
 
    LockCorrupt,
131
 
    LockFailed,
132
 
    LockNotHeld,
133
 
    NoSuchFile,
134
 
    PathError,
135
 
    ResourceBusy,
136
 
    TransportError,
137
 
    )
 
127
        DirectoryNotEmpty,
 
128
        FileExists,
 
129
        LockBreakMismatch,
 
130
        LockBroken,
 
131
        LockContention,
 
132
        LockCorrupt,
 
133
        LockFailed,
 
134
        LockNotHeld,
 
135
        NoSuchFile,
 
136
        PathError,
 
137
        ResourceBusy,
 
138
        TransportError,
 
139
        )
138
140
from .i18n import gettext
139
141
from .osutils import format_delta, rand_chars, get_host_name
 
142
from .sixish import (
 
143
    PY3,
 
144
    text_type,
 
145
    )
140
146
from .trace import mutter, note
141
147
 
142
148
 
165
171
 
166
172
    __INFO_NAME = '/info'
167
173
 
168
 
    def __init__(self, transport, path, file_modebits=0o644,
169
 
                 dir_modebits=0o755, extra_holder_info=None):
 
174
    def __init__(self, transport, path, file_modebits=0o644, dir_modebits=0o755,
 
175
        extra_holder_info=None):
170
176
        """Create a new LockDir object.
171
177
 
172
178
        The LockDir is initially unlocked - this just creates the object.
245
251
                self._trace("other holder is %r" % other_holder)
246
252
                try:
247
253
                    self._handle_lock_contention(other_holder)
248
 
                except BaseException:
 
254
                except:
249
255
                    self._remove_pending_dir(tmpname)
250
256
                    raise
251
257
            except Exception as e:
267
273
        self._trace("after locking, info=%r", info)
268
274
        if info is None:
269
275
            raise LockFailed(self, "lock was renamed into place, but "
270
 
                             "now is missing!")
 
276
                "now is missing!")
271
277
        if info.get('nonce') != self.nonce:
272
278
            self._trace("rename succeeded, "
273
 
                        "but lock is still held by someone else")
 
279
                "but lock is still held by someone else")
274
280
            raise LockContention(self)
275
281
        self._lock_held = True
276
282
        self._trace("... lock succeeded after %dms",
277
 
                    (time.time() - start_time) * 1000)
 
283
                (time.time() - start_time) * 1000)
278
284
        return self.nonce
279
285
 
280
286
    def _handle_lock_contention(self, other_holder):
296
302
                    ui.ui_factory.show_user_warning(
297
303
                        'locks_steal_dead',
298
304
                        lock_url=urlutils.join(self.transport.base, self.path),
299
 
                        other_holder_info=str(other_holder))
 
305
                        other_holder_info=text_type(other_holder))
300
306
                    self.force_break(other_holder)
301
307
                    self._trace("stole lock from dead holder")
302
308
                    return
334
340
        # We'll rename the whole directory into place to get atomic
335
341
        # properties
336
342
        self.transport.put_bytes_non_atomic(tmpname + self.__INFO_NAME,
337
 
                                            info.to_bytes())
 
343
            info.to_bytes())
338
344
        return tmpname
339
345
 
340
346
    @only_raises(LockNotHeld, LockBroken)
363
369
            self.transport.delete(tmpname + self.__INFO_NAME)
364
370
            try:
365
371
                self.transport.rmdir(tmpname)
366
 
            except DirectoryNotEmpty:
 
372
            except DirectoryNotEmpty as e:
367
373
                # There might have been junk left over by a rename that moved
368
374
                # another locker within the 'held' directory.  do a slower
369
375
                # deletion where we list the directory and remove everything
370
376
                # within it.
 
377
                #
 
378
                # Maybe this should be broader to allow for ftp servers with
 
379
                # non-specific error messages?
371
380
                self._trace("doing recursive deletion of non-empty directory "
372
 
                            "%s", tmpname)
 
381
                        "%s", tmpname)
373
382
                self.transport.delete_tree(tmpname)
374
383
            self._trace("... unlock succeeded after %dms",
375
 
                        (time.time() - start_time) * 1000)
 
384
                    (time.time() - start_time) * 1000)
376
385
            result = lock.LockResult(self.transport.abspath(self.path),
377
386
                                     old_nonce)
378
387
            for hook in self.hooks['lock_released']:
400
409
            if ui.ui_factory.confirm_action(
401
410
                u"Break %(lock_info)s",
402
411
                'breezy.lockdir.break',
403
 
                    dict(lock_info=str(holder_info))):
 
412
                dict(lock_info=text_type(holder_info))):
404
413
                result = self.force_break(holder_info)
405
414
                ui.ui_factory.show_message(
406
415
                    "Broke lock %s" % result.lock_url)
523
532
            info = self._read_info_file(self._held_info_path)
524
533
            self._trace("peek -> held")
525
534
            return info
526
 
        except NoSuchFile:
 
535
        except NoSuchFile as e:
527
536
            self._trace("peek -> not held")
528
537
 
529
538
    def _prepare_info(self):
543
552
            raise LockContention(self)
544
553
        result = self._attempt_lock()
545
554
        hook_result = lock.LockResult(self.transport.abspath(self.path),
546
 
                                      self.nonce)
 
555
                self.nonce)
547
556
        for hook in self.hooks['lock_acquired']:
548
557
            hook(hook_result)
549
558
        return result
610
619
                    start = gettext('Lock owner changed for')
611
620
                last_info = new_info
612
621
                msg = gettext('{0} lock {1} {2}.').format(start, lock_url,
613
 
                                                          new_info)
 
622
                                                                    new_info)
614
623
                if deadline_str is None:
615
624
                    deadline_str = time.strftime('%H:%M:%S',
616
 
                                                 time.localtime(deadline))
 
625
                                                    time.localtime(deadline))
617
626
                if timeout > 0:
618
627
                    msg += '\n' + gettext(
619
 
                        'Will continue to try until %s, unless '
620
 
                        'you press Ctrl-C.') % deadline_str
 
628
                             'Will continue to try until %s, unless '
 
629
                             'you press Ctrl-C.') % deadline_str
621
630
                msg += '\n' + gettext('See "brz help break-lock" for more.')
622
631
                self._report_function(msg)
623
632
            if (max_attempts is not None) and (attempt_count >= max_attempts):
675
684
        # we can't rely on that remotely.  Once this is cleaned up,
676
685
        # reenable this warning to prevent it coming back in
677
686
        # -- mbp 20060303
678
 
        # warn("LockDir.lock_read falls back to write lock")
 
687
        ## warn("LockDir.lock_read falls back to write lock")
679
688
        if self._lock_held or self._fake_read_lock:
680
689
            raise LockContention(self)
681
690
        self._fake_read_lock = True
724
733
        """Return a debugging representation of this object."""
725
734
        return "%s(%r)" % (self.__class__.__name__, self.info_dict)
726
735
 
727
 
    def __str__(self):
 
736
    def __unicode__(self):
728
737
        """Return a user-oriented description of this object."""
729
738
        d = self.to_readable_dict()
730
 
        return (gettext(
 
739
        return ( gettext(
731
740
            u'held by %(user)s on %(hostname)s (process #%(pid)s), '
732
741
            u'acquired %(time_ago)s') % d)
733
742
 
 
743
    if PY3:
 
744
        __str__ = __unicode__
 
745
 
734
746
    def to_readable_dict(self):
735
747
        """Turn the holder info into a dict of human-readable attributes.
736
748
 
766
778
        info = dict(
767
779
            hostname=get_host_name(),
768
780
            pid=str(os.getpid()),
769
 
            nonce=rand_chars(20).encode('ascii'),
 
781
            nonce=rand_chars(20),
770
782
            start_time=str(int(time.time())),
771
783
            user=get_username_for_lock_info(),
772
784
            )
787
799
        except ValueError as e:
788
800
            mutter('Corrupt lock info file: %r', lines)
789
801
            raise LockCorrupt("could not parse lock info file: " + str(e),
790
 
                              lines)
 
802
                lines)
791
803
        if stanza is None:
792
804
            # see bug 185013; we fairly often end up with the info file being
793
805
            # empty after an interruption; we could log a message here but
794
806
            # there may not be much we can say
795
807
            return cls({})
796
808
        else:
797
 
            ret = stanza.as_dict()
798
 
            ret['nonce'] = ret['nonce'].encode('ascii')
799
 
            return cls(ret)
 
809
            return cls(stanza.as_dict())
800
810
 
801
811
    def __hash__(self):
802
812
        return id(self)
846
856
            pid = int(pid_str)
847
857
        except ValueError:
848
858
            mutter("can't parse pid %r from %r"
849
 
                   % (pid_str, self))
 
859
                % (pid_str, self))
850
860
            return False
851
861
        return osutils.is_local_pid_dead(pid)
852
862
 
859
869
    """
860
870
    try:
861
871
        return config.GlobalStack().get('email')
862
 
    except errors.NoWhoami:
 
872
    except config.NoWhoami:
863
873
        return osutils.getuser_unicode()