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

  • Committer: John Arbash Meinel
  • Date: 2011-04-20 14:27:19 UTC
  • mto: This revision was merged to the branch mainline in revision 5837.
  • Revision ID: john@arbash-meinel.com-20110420142719-advs1k5vztqzbrgv
Fix bug #767177. Be more agressive with file.close() calls.

Our test suite gets a number of thread leaks and failures because it happens to get async
SFTPFile.close() calls. (if an SFTPFile closes due to __del__ it is done as an async request,
while if you call SFTPFile.close() it is done as a synchronous request.)
We have a couple other cases, probably. Namely SFTPTransport.get() also does an async
prefetch of the content, so if you don't .read() you'll also leak threads that think they
are doing work that you want.

The biggest change here, though, is using a try/finally in a generator, which is not 
python2.4 compatible.

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
 
18
 
import re
19
 
 
20
18
from bzrlib.lazy_import import lazy_import
21
19
lazy_import(globals(), """
22
20
import bisect
23
21
import datetime
 
22
 
 
23
from bzrlib import (
 
24
    branch as _mod_branch,
 
25
    osutils,
 
26
    revision,
 
27
    symbol_versioning,
 
28
    workingtree,
 
29
    )
24
30
""")
25
31
 
26
32
from bzrlib import (
27
33
    errors,
28
 
    osutils,
 
34
    lazy_regex,
29
35
    registry,
30
 
    revision,
31
 
    symbol_versioning,
32
36
    trace,
33
37
    )
34
38
 
113
117
        return RevisionInfo(branch, revno, revision_id)
114
118
 
115
119
 
116
 
_revno_regex = None
117
 
 
118
 
 
119
120
class RevisionSpec(object):
120
121
    """A parsed revision specification."""
121
122
 
166
167
                         spectype.__name__, spec)
167
168
            return spectype(spec, _internal=True)
168
169
        else:
169
 
            for spectype in SPEC_TYPES:
170
 
                if spec.startswith(spectype.prefix):
171
 
                    trace.mutter('Returning RevisionSpec %s for %s',
172
 
                                 spectype.__name__, spec)
173
 
                    return spectype(spec, _internal=True)
174
170
            # Otherwise treat it as a DWIM, build the RevisionSpec object and
175
171
            # wait for _match_on to be called.
176
172
            return RevisionSpec_dwim(spec, _internal=True)
302
298
    # each revspec we try.
303
299
    wants_revision_history = False
304
300
 
 
301
    _revno_regex = lazy_regex.lazy_compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
 
302
 
 
303
    # The revspecs to try
 
304
    _possible_revspecs = []
 
305
 
305
306
    def _try_spectype(self, rstype, branch):
306
307
        rs = rstype(self.spec, _internal=True)
307
308
        # Hit in_history to find out if it exists, or we need to try the
312
313
        """Run the lookup and see what we can get."""
313
314
 
314
315
        # First, see if it's a revno
315
 
        global _revno_regex
316
 
        if _revno_regex is None:
317
 
            _revno_regex = re.compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
318
 
        if _revno_regex.match(self.spec) is not None:
 
316
        if self._revno_regex.match(self.spec) is not None:
319
317
            try:
320
318
                return self._try_spectype(RevisionSpec_revno, branch)
321
319
            except RevisionSpec_revno.dwim_catchable_exceptions:
322
320
                pass
323
321
 
324
322
        # Next see what has been registered
 
323
        for objgetter in self._possible_revspecs:
 
324
            rs_class = objgetter.get_obj()
 
325
            try:
 
326
                return self._try_spectype(rs_class, branch)
 
327
            except rs_class.dwim_catchable_exceptions:
 
328
                pass
 
329
 
 
330
        # Try the old (deprecated) dwim list:
325
331
        for rs_class in dwim_revspecs:
326
332
            try:
327
333
                return self._try_spectype(rs_class, branch)
333
339
        # really relevant.
334
340
        raise errors.InvalidRevisionSpec(self.spec, branch)
335
341
 
 
342
    @classmethod
 
343
    def append_possible_revspec(cls, revspec):
 
344
        """Append a possible DWIM revspec.
 
345
 
 
346
        :param revspec: Revision spec to try.
 
347
        """
 
348
        cls._possible_revspecs.append(registry._ObjectGetter(revspec))
 
349
 
 
350
    @classmethod
 
351
    def append_possible_lazy_revspec(cls, module_name, member_name):
 
352
        """Append a possible lazily loaded DWIM revspec.
 
353
 
 
354
        :param module_name: Name of the module with the revspec
 
355
        :param member_name: Name of the revspec within the module
 
356
        """
 
357
        cls._possible_revspecs.append(
 
358
            registry._LazyObjectGetter(module_name, member_name))
 
359
 
336
360
 
337
361
class RevisionSpec_revno(RevisionSpec):
338
362
    """Selects a revision using a number."""
444
468
 
445
469
 
446
470
 
447
 
class RevisionSpec_revid(RevisionSpec):
 
471
class RevisionIDSpec(RevisionSpec):
 
472
 
 
473
    def _match_on(self, branch, revs):
 
474
        revision_id = self.as_revision_id(branch)
 
475
        return RevisionInfo.from_revision_id(branch, revision_id, revs)
 
476
 
 
477
 
 
478
class RevisionSpec_revid(RevisionIDSpec):
448
479
    """Selects a revision using the revision id."""
449
480
 
450
481
    help_txt = """Selects a revision using the revision id.
459
490
 
460
491
    prefix = 'revid:'
461
492
 
462
 
    def _match_on(self, branch, revs):
 
493
    def _as_revision_id(self, context_branch):
463
494
        # self.spec comes straight from parsing the command line arguments,
464
495
        # so we expect it to be a Unicode string. Switch it to the internal
465
496
        # representation.
466
 
        revision_id = osutils.safe_revision_id(self.spec, warn=False)
467
 
        return RevisionInfo.from_revision_id(branch, revision_id, revs)
468
 
 
469
 
    def _as_revision_id(self, context_branch):
470
497
        return osutils.safe_revision_id(self.spec, warn=False)
471
498
 
472
499
 
658
685
                                   August 14th, 2006 at 5:10pm.
659
686
    """
660
687
    prefix = 'date:'
661
 
    _date_re = re.compile(
 
688
    _date_regex = lazy_regex.lazy_compile(
662
689
            r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
663
690
            r'(,|T)?\s*'
664
691
            r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
682
709
        elif self.spec.lower() == 'tomorrow':
683
710
            dt = today + datetime.timedelta(days=1)
684
711
        else:
685
 
            m = self._date_re.match(self.spec)
 
712
            m = self._date_regex.match(self.spec)
686
713
            if not m or (not m.group('date') and not m.group('time')):
687
714
                raise errors.InvalidRevisionSpec(self.user_spec,
688
715
                                                 branch, 'invalid date')
813
840
        revision_b = other_branch.last_revision()
814
841
        if revision_b in (None, revision.NULL_REVISION):
815
842
            raise errors.NoCommits(other_branch)
816
 
        # pull in the remote revisions so we can diff
817
 
        branch.fetch(other_branch, revision_b)
 
843
        if branch is None:
 
844
            branch = other_branch
 
845
        else:
 
846
            try:
 
847
                # pull in the remote revisions so we can diff
 
848
                branch.fetch(other_branch, revision_b)
 
849
            except errors.ReadOnlyError:
 
850
                branch = other_branch
818
851
        try:
819
852
            revno = branch.revision_id_to_revno(revision_b)
820
853
        except errors.NoSuchRevision:
840
873
            raise errors.NoCommits(other_branch)
841
874
        return other_branch.repository.revision_tree(last_revision)
842
875
 
 
876
    def needs_branch(self):
 
877
        return False
 
878
 
 
879
    def get_branch(self):
 
880
        return self.spec
 
881
 
843
882
 
844
883
 
845
884
class RevisionSpec_submit(RevisionSpec_ancestor):
884
923
            self._get_submit_location(context_branch))
885
924
 
886
925
 
 
926
class RevisionSpec_annotate(RevisionIDSpec):
 
927
 
 
928
    prefix = 'annotate:'
 
929
 
 
930
    help_txt = """Select the revision that last modified the specified line.
 
931
 
 
932
    Select the revision that last modified the specified line.  Line is
 
933
    specified as path:number.  Path is a relative path to the file.  Numbers
 
934
    start at 1, and are relative to the current version, not the last-
 
935
    committed version of the file.
 
936
    """
 
937
 
 
938
    def _raise_invalid(self, numstring, context_branch):
 
939
        raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
 
940
            'No such line: %s' % numstring)
 
941
 
 
942
    def _as_revision_id(self, context_branch):
 
943
        path, numstring = self.spec.rsplit(':', 1)
 
944
        try:
 
945
            index = int(numstring) - 1
 
946
        except ValueError:
 
947
            self._raise_invalid(numstring, context_branch)
 
948
        tree, file_path = workingtree.WorkingTree.open_containing(path)
 
949
        tree.lock_read()
 
950
        try:
 
951
            file_id = tree.path2id(file_path)
 
952
            if file_id is None:
 
953
                raise errors.InvalidRevisionSpec(self.user_spec,
 
954
                    context_branch, "File '%s' is not versioned." %
 
955
                    file_path)
 
956
            revision_ids = [r for (r, l) in tree.annotate_iter(file_id)]
 
957
        finally:
 
958
            tree.unlock()
 
959
        try:
 
960
            revision_id = revision_ids[index]
 
961
        except IndexError:
 
962
            self._raise_invalid(numstring, context_branch)
 
963
        if revision_id == revision.CURRENT_REVISION:
 
964
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
 
965
                'Line %s has not been committed.' % numstring)
 
966
        return revision_id
 
967
 
 
968
 
 
969
class RevisionSpec_mainline(RevisionIDSpec):
 
970
 
 
971
    help_txt = """Select mainline revision that merged the specified revision.
 
972
 
 
973
    Select the revision that merged the specified revision into mainline.
 
974
    """
 
975
 
 
976
    prefix = 'mainline:'
 
977
 
 
978
    def _as_revision_id(self, context_branch):
 
979
        revspec = RevisionSpec.from_string(self.spec)
 
980
        if revspec.get_branch() is None:
 
981
            spec_branch = context_branch
 
982
        else:
 
983
            spec_branch = _mod_branch.Branch.open(revspec.get_branch())
 
984
        revision_id = revspec.as_revision_id(spec_branch)
 
985
        graph = context_branch.repository.get_graph()
 
986
        result = graph.find_lefthand_merger(revision_id,
 
987
                                            context_branch.last_revision())
 
988
        if result is None:
 
989
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
 
990
        return result
 
991
 
 
992
 
887
993
# The order in which we want to DWIM a revision spec without any prefix.
888
994
# revno is always tried first and isn't listed here, this is used by
889
995
# RevisionSpec_dwim._match_on
890
 
dwim_revspecs = [
891
 
    RevisionSpec_tag, # Let's try for a tag
892
 
    RevisionSpec_revid, # Maybe it's a revid?
893
 
    RevisionSpec_date, # Perhaps a date?
894
 
    RevisionSpec_branch, # OK, last try, maybe it's a branch
895
 
    ]
 
996
dwim_revspecs = symbol_versioning.deprecated_list(
 
997
    symbol_versioning.deprecated_in((2, 4, 0)), "dwim_revspecs", [])
896
998
 
 
999
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_tag)
 
1000
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_revid)
 
1001
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_date)
 
1002
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_branch)
897
1003
 
898
1004
revspec_registry = registry.Registry()
899
1005
def _register_revspec(revspec):
908
1014
_register_revspec(RevisionSpec_ancestor)
909
1015
_register_revspec(RevisionSpec_branch)
910
1016
_register_revspec(RevisionSpec_submit)
911
 
 
912
 
# classes in this list should have a "prefix" attribute, against which
913
 
# string specs are matched
914
 
SPEC_TYPES = symbol_versioning.deprecated_list(
915
 
    symbol_versioning.deprecated_in((1, 12, 0)), "SPEC_TYPES", [])
 
1017
_register_revspec(RevisionSpec_annotate)
 
1018
_register_revspec(RevisionSpec_mainline)