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

  • Committer: Jelmer Vernooij
  • Date: 2017-06-10 00:21:41 UTC
  • mto: This revision was merged to the branch mainline in revision 6675.
  • Revision ID: jelmer@jelmer.uk-20170610002141-m1z5k7fs8laesa65
Fix import.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 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
51
51
 
52
52
"""
53
53
 
 
54
from __future__ import absolute_import
54
55
 
55
 
from cStringIO import StringIO
56
 
from itertools import izip
57
56
import operator
58
57
import os
59
 
import sys
60
58
 
61
 
from bzrlib.lazy_import import lazy_import
 
59
from .lazy_import import lazy_import
62
60
lazy_import(globals(), """
63
 
from bzrlib import (
64
 
    annotate,
 
61
import gzip
 
62
 
 
63
from breezy import (
65
64
    debug,
66
65
    diff,
67
66
    graph as _mod_graph,
68
67
    index as _mod_index,
69
 
    lru_cache,
70
68
    pack,
71
 
    progress,
 
69
    patiencediff,
72
70
    static_tuple,
73
71
    trace,
74
72
    tsort,
75
73
    tuned_gzip,
76
74
    ui,
77
75
    )
 
76
 
 
77
from breezy.repofmt import pack_repo
 
78
from breezy.i18n import gettext
78
79
""")
79
 
from bzrlib import (
 
80
from . import (
 
81
    annotate,
80
82
    errors,
81
83
    osutils,
82
 
    patiencediff,
83
84
    )
84
 
from bzrlib.errors import (
85
 
    FileExists,
 
85
from .errors import (
86
86
    NoSuchFile,
87
 
    KnitError,
88
87
    InvalidRevisionId,
89
88
    KnitCorrupt,
90
89
    KnitHeaderError,
91
90
    RevisionNotPresent,
92
 
    RevisionAlreadyPresent,
93
91
    SHA1KnitCorrupt,
94
92
    )
95
 
from bzrlib.osutils import (
 
93
from .osutils import (
96
94
    contains_whitespace,
97
 
    contains_linebreaks,
98
95
    sha_string,
99
96
    sha_strings,
100
97
    split_lines,
101
98
    )
102
 
from bzrlib.versionedfile import (
 
99
from .sixish import (
 
100
    BytesIO,
 
101
    range,
 
102
    viewitems,
 
103
    viewvalues,
 
104
    )
 
105
from .versionedfile import (
 
106
    _KeyRefs,
103
107
    AbsentContentFactory,
104
108
    adapter_registry,
105
109
    ConstantMapper,
106
110
    ContentFactory,
107
 
    ChunkedContentFactory,
108
111
    sort_groupcompress,
109
 
    VersionedFile,
110
 
    VersionedFiles,
 
112
    VersionedFilesWithFallbacks,
111
113
    )
112
114
 
113
115
 
192
194
        delta = self._annotate_factory.parse_line_delta(contents, rec[1],
193
195
            plain=True)
194
196
        compression_parent = factory.parents[0]
195
 
        basis_entry = self._basis_vf.get_record_stream(
196
 
            [compression_parent], 'unordered', True).next()
 
197
        basis_entry = next(self._basis_vf.get_record_stream(
 
198
            [compression_parent], 'unordered', True))
197
199
        if basis_entry.storage_kind == 'absent':
198
200
            raise errors.RevisionNotPresent(compression_parent, self._basis_vf)
199
201
        basis_chunks = basis_entry.get_bytes_as('chunked')
228
230
        delta = self._plain_factory.parse_line_delta(contents, rec[1])
229
231
        compression_parent = factory.parents[0]
230
232
        # XXX: string splitting overhead.
231
 
        basis_entry = self._basis_vf.get_record_stream(
232
 
            [compression_parent], 'unordered', True).next()
 
233
        basis_entry = next(self._basis_vf.get_record_stream(
 
234
            [compression_parent], 'unordered', True))
233
235
        if basis_entry.storage_kind == 'absent':
234
236
            raise errors.RevisionNotPresent(compression_parent, self._basis_vf)
235
237
        basis_chunks = basis_entry.get_bytes_as('chunked')
412
414
class KnitContent(object):
413
415
    """Content of a knit version to which deltas can be applied.
414
416
 
415
 
    This is always stored in memory as a list of lines with \n at the end,
 
417
    This is always stored in memory as a list of lines with \\n at the end,
416
418
    plus a flag saying if the final ending is really there or not, because that
417
419
    corresponds to the on-disk knit representation.
418
420
    """
470
472
 
471
473
    def __init__(self, lines):
472
474
        KnitContent.__init__(self)
473
 
        self._lines = lines
 
475
        self._lines = list(lines)
474
476
 
475
477
    def annotate(self):
476
478
        """Return a list of (origin, text) for each content line."""
491
493
    def text(self):
492
494
        try:
493
495
            lines = [text for origin, text in self._lines]
494
 
        except ValueError, e:
 
496
        except ValueError as e:
495
497
            # most commonly (only?) caused by the internal form of the knit
496
498
            # missing annotation information because of a bug - see thread
497
499
            # around 20071015
503
505
        return lines
504
506
 
505
507
    def copy(self):
506
 
        return AnnotatedKnitContent(self._lines[:])
 
508
        return AnnotatedKnitContent(self._lines)
507
509
 
508
510
 
509
511
class PlainKnitContent(KnitContent):
598
600
        #       but the code itself doesn't really depend on that.
599
601
        #       Figure out a way to not require the overhead of turning the
600
602
        #       list back into tuples.
601
 
        lines = [tuple(line.split(' ', 1)) for line in content]
 
603
        lines = (tuple(line.split(' ', 1)) for line in content)
602
604
        return AnnotatedKnitContent(lines)
603
605
 
604
606
    def parse_line_delta_iter(self, lines):
620
622
        """
621
623
        result = []
622
624
        lines = iter(lines)
623
 
        next = lines.next
624
625
 
625
626
        cache = {}
626
627
        def cache_and_return(line):
633
634
        if plain:
634
635
            for header in lines:
635
636
                start, end, count = [int(n) for n in header.split(',')]
636
 
                contents = [next().split(' ', 1)[1] for i in xrange(count)]
 
637
                contents = [next(lines).split(' ', 1)[1] for _ in range(count)]
637
638
                result.append((start, end, count, contents))
638
639
        else:
639
640
            for header in lines:
640
641
                start, end, count = [int(n) for n in header.split(',')]
641
 
                contents = [tuple(next().split(' ', 1)) for i in xrange(count)]
 
642
                contents = [tuple(next(lines).split(' ', 1))
 
643
                    for _ in range(count)]
642
644
                result.append((start, end, count, contents))
643
645
        return result
644
646
 
653
655
        Only the actual content lines.
654
656
        """
655
657
        lines = iter(lines)
656
 
        next = lines.next
657
658
        for header in lines:
658
659
            header = header.split(',')
659
660
            count = int(header[2])
660
 
            for i in xrange(count):
661
 
                origin, text = next().split(' ', 1)
 
661
            for _ in range(count):
 
662
                origin, text = next(lines).split(' ', 1)
662
663
                yield text
663
664
 
664
665
    def lower_fulltext(self, content):
686
687
        content = knit._get_content(key)
687
688
        # adjust for the fact that serialised annotations are only key suffixes
688
689
        # for this factory.
689
 
        if type(key) is tuple:
 
690
        if isinstance(key, tuple):
690
691
            prefix = key[:-1]
691
692
            origins = content.annotate()
692
693
            result = []
739
740
        Only the actual content lines.
740
741
        """
741
742
        lines = iter(lines)
742
 
        next = lines.next
743
743
        for header in lines:
744
744
            header = header.split(',')
745
745
            count = int(header[2])
746
 
            for i in xrange(count):
747
 
                yield next()
 
746
            for _ in range(count):
 
747
                yield next(lines)
748
748
 
749
749
    def lower_fulltext(self, content):
750
750
        return content.text()
805
805
        writer.begin()
806
806
        index = _KnitGraphIndex(graph_index, lambda:True, parents=parents,
807
807
            deltas=delta, add_callback=graph_index.add_nodes)
808
 
        access = _DirectPackAccess({})
 
808
        access = pack_repo._DirectPackAccess({})
809
809
        access.set_writer(writer, graph_index, (transport, 'newpack'))
810
810
        result = KnitVersionedFiles(index, access,
811
811
            max_delta_chain=max_delta_chain)
845
845
            if compression_parent not in all_build_index_memos:
846
846
                next_keys.add(compression_parent)
847
847
        build_keys = next_keys
848
 
    return sum([index_memo[2] for index_memo
849
 
                in all_build_index_memos.itervalues()])
850
 
 
851
 
 
852
 
class KnitVersionedFiles(VersionedFiles):
 
848
    return sum(index_memo[2]
 
849
               for index_memo in viewvalues(all_build_index_memos))
 
850
 
 
851
 
 
852
class KnitVersionedFiles(VersionedFilesWithFallbacks):
853
853
    """Storage for many versioned files using knit compression.
854
854
 
855
855
    Backend storage is managed by indices and data objects.
873
873
            stored during insertion.
874
874
        :param reload_func: An function that can be called if we think we need
875
875
            to reload the pack listing and try again. See
876
 
            'bzrlib.repofmt.pack_repo.AggregateIndex' for the signature.
 
876
            'breezy.repofmt.pack_repo.AggregateIndex' for the signature.
877
877
        """
878
878
        self._index = index
879
879
        self._access = data_access
882
882
            self._factory = KnitAnnotateFactory()
883
883
        else:
884
884
            self._factory = KnitPlainFactory()
885
 
        self._fallback_vfs = []
 
885
        self._immediate_fallback_vfs = []
886
886
        self._reload_func = reload_func
887
887
 
888
888
    def __repr__(self):
891
891
            self._index,
892
892
            self._access)
893
893
 
 
894
    def without_fallbacks(self):
 
895
        """Return a clone of this object without any fallbacks configured."""
 
896
        return KnitVersionedFiles(self._index, self._access,
 
897
            self._max_delta_chain, self._factory.annotated,
 
898
            self._reload_func)
 
899
 
894
900
    def add_fallback_versioned_files(self, a_versioned_files):
895
901
        """Add a source of texts for texts not present in this knit.
896
902
 
897
903
        :param a_versioned_files: A VersionedFiles object.
898
904
        """
899
 
        self._fallback_vfs.append(a_versioned_files)
 
905
        self._immediate_fallback_vfs.append(a_versioned_files)
900
906
 
901
907
    def add_lines(self, key, parents, lines, parent_texts=None,
902
908
        left_matching_blocks=None, nostore_sha=None, random_id=False,
992
998
            lines = osutils.split_lines(line_bytes)
993
999
 
994
1000
        for element in key[:-1]:
995
 
            if type(element) is not str:
 
1001
            if not isinstance(element, str):
996
1002
                raise TypeError("key contains non-strings: %r" % (key,))
997
1003
        if key[-1] is None:
998
1004
            key = key[:-1] + ('sha1:' + digest,)
999
 
        elif type(key[-1]) is not str:
 
1005
        elif not isinstance(key[-1], str):
1000
1006
                raise TypeError("key contains non-strings: %r" % (key,))
1001
1007
        # Knit hunks are still last-element only
1002
1008
        version_id = key[-1]
1069
1075
                    raise errors.KnitCorrupt(self,
1070
1076
                        "Missing basis parent %s for %s" % (
1071
1077
                        compression_parent, key))
1072
 
        for fallback_vfs in self._fallback_vfs:
 
1078
        for fallback_vfs in self._immediate_fallback_vfs:
1073
1079
            fallback_vfs.check()
1074
1080
 
1075
1081
    def _check_add(self, key, lines, random_id, check_content):
1114
1120
        """
1115
1121
        delta_size = 0
1116
1122
        fulltext_size = None
1117
 
        for count in xrange(self._max_delta_chain):
 
1123
        for count in range(self._max_delta_chain):
1118
1124
            try:
1119
1125
                # Note that this only looks in the index of this particular
1120
1126
                # KnitVersionedFiles, not in the fallbacks.  This ensures that
1122
1128
                # boundaries.
1123
1129
                build_details = self._index.get_build_details([parent])
1124
1130
                parent_details = build_details[parent]
1125
 
            except (RevisionNotPresent, KeyError), e:
 
1131
            except (RevisionNotPresent, KeyError) as e:
1126
1132
                # Some basis is not locally present: always fulltext
1127
1133
                return False
1128
1134
            index_memo, compression_parent, _, _ = parent_details
1153
1159
 
1154
1160
        A dict of key to (record_details, index_memo, next, parents) is
1155
1161
        returned.
1156
 
        method is the way referenced data should be applied.
1157
 
        index_memo is the handle to pass to the data access to actually get the
1158
 
            data
1159
 
        next is the build-parent of the version, or None for fulltexts.
1160
 
        parents is the version_ids of the parents of this version
1161
 
 
1162
 
        :param allow_missing: If True do not raise an error on a missing component,
1163
 
            just ignore it.
 
1162
 
 
1163
        * method is the way referenced data should be applied.
 
1164
        * index_memo is the handle to pass to the data access to actually get
 
1165
          the data
 
1166
        * next is the build-parent of the version, or None for fulltexts.
 
1167
        * parents is the version_ids of the parents of this version
 
1168
 
 
1169
        :param allow_missing: If True do not raise an error on a missing
 
1170
            component, just ignore it.
1164
1171
        """
1165
1172
        component_data = {}
1166
1173
        pending_components = keys
1168
1175
            build_details = self._index.get_build_details(pending_components)
1169
1176
            current_components = set(pending_components)
1170
1177
            pending_components = set()
1171
 
            for key, details in build_details.iteritems():
 
1178
            for key, details in viewitems(build_details):
1172
1179
                (index_memo, compression_parent, parents,
1173
1180
                 record_details) = details
1174
1181
                method = record_details[0]
1192
1199
        generator = _VFContentMapGenerator(self, [key])
1193
1200
        return generator._get_content(key)
1194
1201
 
1195
 
    def get_known_graph_ancestry(self, keys):
1196
 
        """Get a KnownGraph instance with the ancestry of keys."""
1197
 
        parent_map, missing_keys = self._index.find_ancestry(keys)
1198
 
        for fallback in self._fallback_vfs:
1199
 
            if not missing_keys:
1200
 
                break
1201
 
            (f_parent_map, f_missing_keys) = fallback._index.find_ancestry(
1202
 
                                                missing_keys)
1203
 
            parent_map.update(f_parent_map)
1204
 
            missing_keys = f_missing_keys
1205
 
        kg = _mod_graph.KnownGraph(parent_map)
1206
 
        return kg
1207
 
 
1208
1202
    def get_parent_map(self, keys):
1209
1203
        """Get a map of the graph parents of keys.
1210
1204
 
1225
1219
            and so on.
1226
1220
        """
1227
1221
        result = {}
1228
 
        sources = [self._index] + self._fallback_vfs
 
1222
        sources = [self._index] + self._immediate_fallback_vfs
1229
1223
        source_results = []
1230
1224
        missing = set(keys)
1231
1225
        for source in sources:
1241
1235
        """Produce a dictionary of knit records.
1242
1236
 
1243
1237
        :return: {key:(record, record_details, digest, next)}
1244
 
            record
1245
 
                data returned from read_records (a KnitContentobject)
1246
 
            record_details
1247
 
                opaque information to pass to parse_record
1248
 
            digest
1249
 
                SHA1 digest of the full text after all steps are done
1250
 
            next
1251
 
                build-parent of the version, i.e. the leftmost ancestor.
 
1238
 
 
1239
            * record: data returned from read_records (a KnitContentobject)
 
1240
            * record_details: opaque information to pass to parse_record
 
1241
            * digest: SHA1 digest of the full text after all steps are done
 
1242
            * next: build-parent of the version, i.e. the leftmost ancestor.
1252
1243
                Will be None if the record is not a delta.
 
1244
 
1253
1245
        :param keys: The keys to build a map for
1254
1246
        :param allow_missing: If some records are missing, rather than
1255
1247
            error, just return the data that could be generated.
1290
1282
                # key = component_id, r = record_details, i_m = index_memo,
1291
1283
                # n = next
1292
1284
                records = [(key, i_m) for key, (r, i_m, n)
1293
 
                                       in position_map.iteritems()]
 
1285
                                       in viewitems(position_map)]
1294
1286
                # Sort by the index memo, so that we request records from the
1295
1287
                # same pack file together, and in forward-sorted order
1296
1288
                records.sort(key=operator.itemgetter(1))
1299
1291
                    (record_details, index_memo, next) = position_map[key]
1300
1292
                    raw_record_map[key] = data, record_details, next
1301
1293
                return raw_record_map
1302
 
            except errors.RetryWithNewPacks, e:
 
1294
            except errors.RetryWithNewPacks as e:
1303
1295
                self._access.reload_or_raise(e)
1304
1296
 
1305
1297
    @classmethod
1408
1400
                    remaining_keys.discard(content_factory.key)
1409
1401
                    yield content_factory
1410
1402
                return
1411
 
            except errors.RetryWithNewPacks, e:
 
1403
            except errors.RetryWithNewPacks as e:
1412
1404
                self._access.reload_or_raise(e)
1413
1405
 
1414
1406
    def _get_remaining_record_stream(self, keys, ordering,
1421
1413
            # map from key to
1422
1414
            # (record_details, access_memo, compression_parent_key)
1423
1415
            positions = dict((key, self._build_details_to_components(details))
1424
 
                for key, details in build_details.iteritems())
 
1416
                for key, details in viewitems(build_details))
1425
1417
        absent_keys = keys.difference(set(positions))
1426
1418
        # There may be more absent keys : if we're missing the basis component
1427
1419
        # and are trying to include the delta closure.
1525
1517
                        yield KnitContentFactory(key, global_map[key],
1526
1518
                            record_details, None, raw_data, self._factory.annotated, None)
1527
1519
                else:
1528
 
                    vf = self._fallback_vfs[parent_maps.index(source) - 1]
 
1520
                    vf = self._immediate_fallback_vfs[parent_maps.index(source) - 1]
1529
1521
                    for record in vf.get_record_stream(keys, ordering,
1530
1522
                        include_delta_closure):
1531
1523
                        yield record
1535
1527
        missing = set(keys)
1536
1528
        record_map = self._get_record_map(missing, allow_missing=True)
1537
1529
        result = {}
1538
 
        for key, details in record_map.iteritems():
 
1530
        for key, details in viewitems(record_map):
1539
1531
            if key not in missing:
1540
1532
                continue
1541
1533
            # record entry 2 is the 'digest'.
1542
1534
            result[key] = details[2]
1543
1535
        missing.difference_update(set(result))
1544
 
        for source in self._fallback_vfs:
 
1536
        for source in self._immediate_fallback_vfs:
1545
1537
            if not missing:
1546
1538
                break
1547
1539
            new_result = source.get_sha1s(missing)
1572
1564
        else:
1573
1565
            # self is not annotated, but we can strip annotations cheaply.
1574
1566
            annotated = ""
1575
 
            convertibles = set(["knit-annotated-ft-gz"])
 
1567
            convertibles = {"knit-annotated-ft-gz"}
1576
1568
            if self._max_delta_chain:
1577
1569
                delta_types.add("knit-annotated-delta-gz")
1578
1570
                convertibles.add("knit-annotated-delta-gz")
1618
1610
                raise RevisionNotPresent([record.key], self)
1619
1611
            elif ((record.storage_kind in knit_types)
1620
1612
                  and (compression_parent is None
1621
 
                       or not self._fallback_vfs
1622
 
                       or self._index.has_key(compression_parent)
1623
 
                       or not self.has_key(compression_parent))):
 
1613
                       or not self._immediate_fallback_vfs
 
1614
                       or compression_parent in self._index
 
1615
                       or compression_parent not in self)):
1624
1616
                # we can insert the knit record literally if either it has no
1625
1617
                # compression parent OR we already have its basis in this kvf
1626
1618
                # OR the basis is not present even in the fallbacks.  In the
1628
1620
                # will be well, or it won't turn up at all and we'll raise an
1629
1621
                # error at the end.
1630
1622
                #
1631
 
                # TODO: self.has_key is somewhat redundant with
1632
 
                # self._index.has_key; we really want something that directly
 
1623
                # TODO: self.__contains__ is somewhat redundant with
 
1624
                # self._index.__contains__; we really want something that directly
1633
1625
                # asks if it's only present in the fallbacks. -- mbp 20081119
1634
1626
                if record.storage_kind not in native_types:
1635
1627
                    try:
1668
1660
                    #
1669
1661
                    # They're required to be physically in this
1670
1662
                    # KnitVersionedFiles, not in a fallback.
1671
 
                    if not self._index.has_key(compression_parent):
 
1663
                    if compression_parent not in self._index:
1672
1664
                        pending = buffered_index_entries.setdefault(
1673
1665
                            compression_parent, [])
1674
1666
                        pending.append(index_entry)
1767
1759
                # we need key, position, length
1768
1760
                key_records = []
1769
1761
                build_details = self._index.get_build_details(keys)
1770
 
                for key, details in build_details.iteritems():
 
1762
                for key, details in viewitems(build_details):
1771
1763
                    if key in keys:
1772
1764
                        key_records.append((key, details[0]))
1773
1765
                records_iter = enumerate(self._read_records_iter(key_records))
1774
1766
                for (key_idx, (key, data, sha_value)) in records_iter:
1775
 
                    pb.update('Walking content', key_idx, total)
 
1767
                    pb.update(gettext('Walking content'), key_idx, total)
1776
1768
                    compression_parent = build_details[key][1]
1777
1769
                    if compression_parent is None:
1778
1770
                        # fulltext
1790
1782
                    for line in line_iterator:
1791
1783
                        yield line, key
1792
1784
                done = True
1793
 
            except errors.RetryWithNewPacks, e:
 
1785
            except errors.RetryWithNewPacks as e:
1794
1786
                self._access.reload_or_raise(e)
1795
1787
        # If there are still keys we've not yet found, we look in the fallback
1796
1788
        # vfs, and hope to find them there.  Note that if the keys are found
1797
1789
        # but had no changes or no content, the fallback may not return
1798
1790
        # anything.
1799
 
        if keys and not self._fallback_vfs:
 
1791
        if keys and not self._immediate_fallback_vfs:
1800
1792
            # XXX: strictly the second parameter is meant to be the file id
1801
1793
            # but it's not easily accessible here.
1802
1794
            raise RevisionNotPresent(keys, repr(self))
1803
 
        for source in self._fallback_vfs:
 
1795
        for source in self._immediate_fallback_vfs:
1804
1796
            if not keys:
1805
1797
                break
1806
1798
            source_keys = set()
1808
1800
                source_keys.add(key)
1809
1801
                yield line, key
1810
1802
            keys.difference_update(source_keys)
1811
 
        pb.update('Walking content', total, total)
 
1803
        pb.update(gettext('Walking content'), total, total)
1812
1804
 
1813
1805
    def _make_line_delta(self, delta_seq, new_content):
1814
1806
        """Generate a line delta from delta_seq and new_content."""
1879
1871
        :return: the header and the decompressor stream.
1880
1872
                 as (stream, header_record)
1881
1873
        """
1882
 
        df = tuned_gzip.GzipFile(mode='rb', fileobj=StringIO(raw_data))
 
1874
        df = gzip.GzipFile(mode='rb', fileobj=BytesIO(raw_data))
1883
1875
        try:
1884
1876
            # Current serialise
1885
1877
            rec = self._check_header(key, df.readline())
1886
 
        except Exception, e:
 
1878
        except Exception as e:
1887
1879
            raise KnitCorrupt(self,
1888
1880
                              "While reading {%s} got %s(%s)"
1889
1881
                              % (key, e.__class__.__name__, str(e)))
1894
1886
        # 4168 calls in 2880 217 internal
1895
1887
        # 4168 calls to _parse_record_header in 2121
1896
1888
        # 4168 calls to readlines in 330
1897
 
        df = tuned_gzip.GzipFile(mode='rb', fileobj=StringIO(data))
 
1889
        df = gzip.GzipFile(mode='rb', fileobj=BytesIO(data))
1898
1890
        try:
1899
1891
            record_contents = df.readlines()
1900
 
        except Exception, e:
 
1892
        except Exception as e:
1901
1893
            raise KnitCorrupt(self, "Corrupt compressed record %r, got %s(%s)" %
1902
1894
                (data, e.__class__.__name__, str(e)))
1903
1895
        header = record_contents.pop(0)
1922
1914
        The result will be returned in whatever is the fastest to read.
1923
1915
        Not by the order requested. Also, multiple requests for the same
1924
1916
        record will only yield 1 response.
 
1917
 
1925
1918
        :param records: A list of (key, access_memo) entries
1926
1919
        :return: Yields (key, contents, digest) in the order
1927
1920
                 read, not the order requested
1939
1932
        raw_data = self._access.get_raw_records(
1940
1933
            [index_memo for key, index_memo in needed_records])
1941
1934
 
1942
 
        for (key, index_memo), data in \
1943
 
                izip(iter(needed_records), raw_data):
 
1935
        for (key, index_memo), data in zip(needed_records, raw_data):
1944
1936
            content, digest = self._parse_record(key[-1], data)
1945
1937
            yield key, content, digest
1946
1938
 
1976
1968
            raw_records = self._access.get_raw_records(needed_offsets)
1977
1969
 
1978
1970
        for key, index_memo in records:
1979
 
            data = raw_records.next()
 
1971
            data = next(raw_records)
1980
1972
            yield key, data
1981
1973
 
1982
1974
    def _record_to_data(self, key, digest, lines, dense_lines=None):
1985
1977
        :param key: The key of the record. Currently keys are always serialised
1986
1978
            using just the trailing component.
1987
1979
        :param dense_lines: The bytes of lines but in a denser form. For
1988
 
            instance, if lines is a list of 1000 bytestrings each ending in \n,
1989
 
            dense_lines may be a list with one line in it, containing all the
1990
 
            1000's lines and their \n's. Using dense_lines if it is already
1991
 
            known is a win because the string join to create bytes in this
1992
 
            function spends less time resizing the final string.
1993
 
        :return: (len, a StringIO instance with the raw data ready to read.)
 
1980
            instance, if lines is a list of 1000 bytestrings each ending in
 
1981
            \\n, dense_lines may be a list with one line in it, containing all
 
1982
            the 1000's lines and their \\n's. Using dense_lines if it is
 
1983
            already known is a win because the string join to create bytes in
 
1984
            this function spends less time resizing the final string.
 
1985
        :return: (len, a BytesIO instance with the raw data ready to read.)
1994
1986
        """
1995
1987
        chunks = ["version %s %d %s\n" % (key[-1], len(lines), digest)]
1996
1988
        chunks.extend(dense_lines or lines)
1997
1989
        chunks.append("end %s\n" % key[-1])
1998
1990
        for chunk in chunks:
1999
 
            if type(chunk) is not str:
 
1991
            if not isinstance(chunk, str):
2000
1992
                raise AssertionError(
2001
1993
                    'data must be plain bytes was %s' % type(chunk))
2002
1994
        if lines and lines[-1][-1] != '\n':
2015
2007
        """See VersionedFiles.keys."""
2016
2008
        if 'evil' in debug.debug_flags:
2017
2009
            trace.mutter_callsite(2, "keys scales with size of history")
2018
 
        sources = [self._index] + self._fallback_vfs
 
2010
        sources = [self._index] + self._immediate_fallback_vfs
2019
2011
        result = set()
2020
2012
        for source in sources:
2021
2013
            result.update(source.keys())
2033
2025
        # Note that _get_content is only called when the _ContentMapGenerator
2034
2026
        # has been constructed with just one key requested for reconstruction.
2035
2027
        if key in self.nonlocal_keys:
2036
 
            record = self.get_record_stream().next()
 
2028
            record = next(self.get_record_stream())
2037
2029
            # Create a content object on the fly
2038
2030
            lines = osutils.chunks_to_lines(record.get_bytes_as('chunked'))
2039
2031
            return PlainKnitContent(lines, record.key)
2061
2053
 
2062
2054
        missing_keys = set(nonlocal_keys)
2063
2055
        # Read from remote versioned file instances and provide to our caller.
2064
 
        for source in self.vf._fallback_vfs:
 
2056
        for source in self.vf._immediate_fallback_vfs:
2065
2057
            if not missing_keys:
2066
2058
                break
2067
2059
            # Loop over fallback repositories asking them for texts - ignore
2175
2167
        # one line with next ('' for None)
2176
2168
        # one line with byte count of the record bytes
2177
2169
        # the record bytes
2178
 
        for key, (record_bytes, (method, noeol), next) in \
2179
 
            self._raw_record_map.iteritems():
 
2170
        for key, (record_bytes, (method, noeol), next) in viewitems(
 
2171
                self._raw_record_map):
2180
2172
            key_bytes = '\x00'.join(key)
2181
2173
            parents = self.global_map.get(key, None)
2182
2174
            if parents is None:
2446
2438
                    line = "\n%s %s %s %s %s :" % (
2447
2439
                        key[-1], ','.join(options), pos, size,
2448
2440
                        self._dictionary_compress(parents))
2449
 
                    if type(line) is not str:
 
2441
                    if not isinstance(line, str):
2450
2442
                        raise AssertionError(
2451
2443
                            'data must be utf8 was %s' % type(line))
2452
2444
                    lines.append(line)
2646
2638
        entry = self._kndx_cache[prefix][0][suffix]
2647
2639
        return key, entry[2], entry[3]
2648
2640
 
2649
 
    has_key = _mod_index._has_key_from_parent_map
 
2641
    __contains__ = _mod_index._has_key_from_parent_map
2650
2642
 
2651
2643
    def _init_index(self, path, extra_lines=[]):
2652
2644
        """Initialize an index."""
2653
 
        sio = StringIO()
 
2645
        sio = BytesIO()
2654
2646
        sio.write(self.HEADER)
2655
2647
        sio.writelines(extra_lines)
2656
2648
        sio.seek(0)
2668
2660
        result = set()
2669
2661
        # Identify all key prefixes.
2670
2662
        # XXX: A bit hacky, needs polish.
2671
 
        if type(self._mapper) is ConstantMapper:
 
2663
        if isinstance(self._mapper, ConstantMapper):
2672
2664
            prefixes = [()]
2673
2665
        else:
2674
2666
            relpaths = set()
2706
2698
                    del self._history
2707
2699
                except NoSuchFile:
2708
2700
                    self._kndx_cache[prefix] = ({}, [])
2709
 
                    if type(self._mapper) is ConstantMapper:
 
2701
                    if isinstance(self._mapper, ConstantMapper):
2710
2702
                        # preserve behaviour for revisions.kndx etc.
2711
2703
                        self._init_index(path)
2712
2704
                    del self._cache
2786
2778
        return key[:-1], key[-1]
2787
2779
 
2788
2780
 
2789
 
class _KeyRefs(object):
2790
 
 
2791
 
    def __init__(self, track_new_keys=False):
2792
 
        # dict mapping 'key' to 'set of keys referring to that key'
2793
 
        self.refs = {}
2794
 
        if track_new_keys:
2795
 
            # set remembering all new keys
2796
 
            self.new_keys = set()
2797
 
        else:
2798
 
            self.new_keys = None
2799
 
 
2800
 
    def clear(self):
2801
 
        if self.refs:
2802
 
            self.refs.clear()
2803
 
        if self.new_keys:
2804
 
            self.new_keys.clear()
2805
 
 
2806
 
    def add_references(self, key, refs):
2807
 
        # Record the new references
2808
 
        for referenced in refs:
2809
 
            try:
2810
 
                needed_by = self.refs[referenced]
2811
 
            except KeyError:
2812
 
                needed_by = self.refs[referenced] = set()
2813
 
            needed_by.add(key)
2814
 
        # Discard references satisfied by the new key
2815
 
        self.add_key(key)
2816
 
 
2817
 
    def get_new_keys(self):
2818
 
        return self.new_keys
2819
 
    
2820
 
    def get_unsatisfied_refs(self):
2821
 
        return self.refs.iterkeys()
2822
 
 
2823
 
    def _satisfy_refs_for_key(self, key):
2824
 
        try:
2825
 
            del self.refs[key]
2826
 
        except KeyError:
2827
 
            # No keys depended on this key.  That's ok.
2828
 
            pass
2829
 
 
2830
 
    def add_key(self, key):
2831
 
        # satisfy refs for key, and remember that we've seen this key.
2832
 
        self._satisfy_refs_for_key(key)
2833
 
        if self.new_keys is not None:
2834
 
            self.new_keys.add(key)
2835
 
 
2836
 
    def satisfy_refs_for_keys(self, keys):
2837
 
        for key in keys:
2838
 
            self._satisfy_refs_for_key(key)
2839
 
 
2840
 
    def get_referrers(self):
2841
 
        result = set()
2842
 
        for referrers in self.refs.itervalues():
2843
 
            result.update(referrers)
2844
 
        return result
2845
 
 
2846
 
 
2847
2781
class _KnitGraphIndex(object):
2848
2782
    """A KnitVersionedFiles index layered on GraphIndex."""
2849
2783
 
2851
2785
        add_callback=None, track_external_parent_refs=False):
2852
2786
        """Construct a KnitGraphIndex on a graph_index.
2853
2787
 
2854
 
        :param graph_index: An implementation of bzrlib.index.GraphIndex.
 
2788
        :param graph_index: An implementation of breezy.index.GraphIndex.
2855
2789
        :param is_locked: A callback to check whether the object should answer
2856
2790
            queries.
2857
2791
        :param deltas: Allow delta-compressed records.
2958
2892
                del keys[key]
2959
2893
        result = []
2960
2894
        if self._parents:
2961
 
            for key, (value, node_refs) in keys.iteritems():
 
2895
            for key, (value, node_refs) in viewitems(keys):
2962
2896
                result.append((key, value, node_refs))
2963
2897
        else:
2964
 
            for key, (value, node_refs) in keys.iteritems():
 
2898
            for key, (value, node_refs) in viewitems(keys):
2965
2899
                result.append((key, value))
2966
2900
        self._add_callback(result)
2967
2901
        if missing_compression_parents:
3156
3090
        node = self._get_node(key)
3157
3091
        return self._node_to_position(node)
3158
3092
 
3159
 
    has_key = _mod_index._has_key_from_parent_map
 
3093
    __contains__ = _mod_index._has_key_from_parent_map
3160
3094
 
3161
3095
    def keys(self):
3162
3096
        """Get all the keys in the collection.
3221
3155
            opaque index memo. For _KnitKeyAccess the memo is (key, pos,
3222
3156
            length), where the key is the record key.
3223
3157
        """
3224
 
        if type(raw_data) is not str:
 
3158
        if not isinstance(raw_data, str):
3225
3159
            raise AssertionError(
3226
3160
                'data must be plain bytes was %s' % type(raw_data))
3227
3161
        result = []
3278
3212
                yield data
3279
3213
 
3280
3214
 
3281
 
class _DirectPackAccess(object):
3282
 
    """Access to data in one or more packs with less translation."""
3283
 
 
3284
 
    def __init__(self, index_to_packs, reload_func=None, flush_func=None):
3285
 
        """Create a _DirectPackAccess object.
3286
 
 
3287
 
        :param index_to_packs: A dict mapping index objects to the transport
3288
 
            and file names for obtaining data.
3289
 
        :param reload_func: A function to call if we determine that the pack
3290
 
            files have moved and we need to reload our caches. See
3291
 
            bzrlib.repo_fmt.pack_repo.AggregateIndex for more details.
3292
 
        """
3293
 
        self._container_writer = None
3294
 
        self._write_index = None
3295
 
        self._indices = index_to_packs
3296
 
        self._reload_func = reload_func
3297
 
        self._flush_func = flush_func
3298
 
 
3299
 
    def add_raw_records(self, key_sizes, raw_data):
3300
 
        """Add raw knit bytes to a storage area.
3301
 
 
3302
 
        The data is spooled to the container writer in one bytes-record per
3303
 
        raw data item.
3304
 
 
3305
 
        :param sizes: An iterable of tuples containing the key and size of each
3306
 
            raw data segment.
3307
 
        :param raw_data: A bytestring containing the data.
3308
 
        :return: A list of memos to retrieve the record later. Each memo is an
3309
 
            opaque index memo. For _DirectPackAccess the memo is (index, pos,
3310
 
            length), where the index field is the write_index object supplied
3311
 
            to the PackAccess object.
3312
 
        """
3313
 
        if type(raw_data) is not str:
3314
 
            raise AssertionError(
3315
 
                'data must be plain bytes was %s' % type(raw_data))
3316
 
        result = []
3317
 
        offset = 0
3318
 
        for key, size in key_sizes:
3319
 
            p_offset, p_length = self._container_writer.add_bytes_record(
3320
 
                raw_data[offset:offset+size], [])
3321
 
            offset += size
3322
 
            result.append((self._write_index, p_offset, p_length))
3323
 
        return result
3324
 
 
3325
 
    def flush(self):
3326
 
        """Flush pending writes on this access object.
3327
 
 
3328
 
        This will flush any buffered writes to a NewPack.
3329
 
        """
3330
 
        if self._flush_func is not None:
3331
 
            self._flush_func()
3332
 
            
3333
 
    def get_raw_records(self, memos_for_retrieval):
3334
 
        """Get the raw bytes for a records.
3335
 
 
3336
 
        :param memos_for_retrieval: An iterable containing the (index, pos,
3337
 
            length) memo for retrieving the bytes. The Pack access method
3338
 
            looks up the pack to use for a given record in its index_to_pack
3339
 
            map.
3340
 
        :return: An iterator over the bytes of the records.
3341
 
        """
3342
 
        # first pass, group into same-index requests
3343
 
        request_lists = []
3344
 
        current_index = None
3345
 
        for (index, offset, length) in memos_for_retrieval:
3346
 
            if current_index == index:
3347
 
                current_list.append((offset, length))
3348
 
            else:
3349
 
                if current_index is not None:
3350
 
                    request_lists.append((current_index, current_list))
3351
 
                current_index = index
3352
 
                current_list = [(offset, length)]
3353
 
        # handle the last entry
3354
 
        if current_index is not None:
3355
 
            request_lists.append((current_index, current_list))
3356
 
        for index, offsets in request_lists:
3357
 
            try:
3358
 
                transport, path = self._indices[index]
3359
 
            except KeyError:
3360
 
                # A KeyError here indicates that someone has triggered an index
3361
 
                # reload, and this index has gone missing, we need to start
3362
 
                # over.
3363
 
                if self._reload_func is None:
3364
 
                    # If we don't have a _reload_func there is nothing that can
3365
 
                    # be done
3366
 
                    raise
3367
 
                raise errors.RetryWithNewPacks(index,
3368
 
                                               reload_occurred=True,
3369
 
                                               exc_info=sys.exc_info())
3370
 
            try:
3371
 
                reader = pack.make_readv_reader(transport, path, offsets)
3372
 
                for names, read_func in reader.iter_records():
3373
 
                    yield read_func(None)
3374
 
            except errors.NoSuchFile:
3375
 
                # A NoSuchFile error indicates that a pack file has gone
3376
 
                # missing on disk, we need to trigger a reload, and start over.
3377
 
                if self._reload_func is None:
3378
 
                    raise
3379
 
                raise errors.RetryWithNewPacks(transport.abspath(path),
3380
 
                                               reload_occurred=False,
3381
 
                                               exc_info=sys.exc_info())
3382
 
 
3383
 
    def set_writer(self, writer, index, transport_packname):
3384
 
        """Set a writer to use for adding data."""
3385
 
        if index is not None:
3386
 
            self._indices[index] = transport_packname
3387
 
        self._container_writer = writer
3388
 
        self._write_index = index
3389
 
 
3390
 
    def reload_or_raise(self, retry_exc):
3391
 
        """Try calling the reload function, or re-raise the original exception.
3392
 
 
3393
 
        This should be called after _DirectPackAccess raises a
3394
 
        RetryWithNewPacks exception. This function will handle the common logic
3395
 
        of determining when the error is fatal versus being temporary.
3396
 
        It will also make sure that the original exception is raised, rather
3397
 
        than the RetryWithNewPacks exception.
3398
 
 
3399
 
        If this function returns, then the calling function should retry
3400
 
        whatever operation was being performed. Otherwise an exception will
3401
 
        be raised.
3402
 
 
3403
 
        :param retry_exc: A RetryWithNewPacks exception.
3404
 
        """
3405
 
        is_error = False
3406
 
        if self._reload_func is None:
3407
 
            is_error = True
3408
 
        elif not self._reload_func():
3409
 
            # The reload claimed that nothing changed
3410
 
            if not retry_exc.reload_occurred:
3411
 
                # If there wasn't an earlier reload, then we really were
3412
 
                # expecting to find changes. We didn't find them, so this is a
3413
 
                # hard error
3414
 
                is_error = True
3415
 
        if is_error:
3416
 
            exc_class, exc_value, exc_traceback = retry_exc.exc_info
3417
 
            raise exc_class, exc_value, exc_traceback
3418
 
 
3419
 
 
3420
 
# Deprecated, use PatienceSequenceMatcher instead
3421
 
KnitSequenceMatcher = patiencediff.PatienceSequenceMatcher
3422
 
 
3423
 
 
3424
3215
def annotate_knit(knit, revision_id):
3425
3216
    """Annotate a knit with no cached annotations.
3426
3217
 
3469
3260
            passing to read_records_iter to start reading in the raw data from
3470
3261
            the pack file.
3471
3262
        """
3472
 
        pending = set([key])
 
3263
        pending = {key}
3473
3264
        records = []
3474
3265
        ann_keys = set()
3475
3266
        self._num_needed_children[key] = 1
3480
3271
            self._all_build_details.update(build_details)
3481
3272
            # new_nodes = self._vf._index._get_entries(this_iteration)
3482
3273
            pending = set()
3483
 
            for key, details in build_details.iteritems():
 
3274
            for key, details in viewitems(build_details):
3484
3275
                (index_memo, compression_parent, parent_keys,
3485
3276
                 record_details) = details
3486
3277
                self._parent_map[key] = parent_keys
3501
3292
                    else:
3502
3293
                        self._num_compression_children[compression_parent] = 1
3503
3294
 
3504
 
            missing_versions = this_iteration.difference(build_details.keys())
 
3295
            missing_versions = this_iteration.difference(build_details)
3505
3296
            if missing_versions:
3506
3297
                for key in missing_versions:
3507
3298
                    if key in self._parent_map and key in self._text_cache:
3524
3315
        return records, ann_keys
3525
3316
 
3526
3317
    def _get_needed_texts(self, key, pb=None):
3527
 
        # if True or len(self._vf._fallback_vfs) > 0:
3528
 
        if len(self._vf._fallback_vfs) > 0:
 
3318
        # if True or len(self._vf._immediate_fallback_vfs) > 0:
 
3319
        if len(self._vf._immediate_fallback_vfs) > 0:
3529
3320
            # If we have fallbacks, go to the generic path
3530
3321
            for v in annotate.Annotator._get_needed_texts(self, key, pb=pb):
3531
3322
                yield v
3536
3327
                for idx, (sub_key, text, num_lines) in enumerate(
3537
3328
                                                self._extract_texts(records)):
3538
3329
                    if pb is not None:
3539
 
                        pb.update('annotating', idx, len(records))
 
3330
                        pb.update(gettext('annotating'), idx, len(records))
3540
3331
                    yield sub_key, text, num_lines
3541
3332
                for sub_key in ann_keys:
3542
3333
                    text = self._text_cache[sub_key]
3543
3334
                    num_lines = len(text) # bad assumption
3544
3335
                    yield sub_key, text, num_lines
3545
3336
                return
3546
 
            except errors.RetryWithNewPacks, e:
 
3337
            except errors.RetryWithNewPacks as e:
3547
3338
                self._vf._access.reload_or_raise(e)
3548
3339
                # The cached build_details are no longer valid
3549
3340
                self._all_build_details.clear()
3708
3499
                    to_process.extend(self._process_pending(key))
3709
3500
 
3710
3501
try:
3711
 
    from bzrlib._knit_load_data_pyx import _load_data_c as _load_data
3712
 
except ImportError, e:
 
3502
    from ._knit_load_data_pyx import _load_data_c as _load_data
 
3503
except ImportError as e:
3713
3504
    osutils.failed_to_load_extension(e)
3714
 
    from bzrlib._knit_load_data_py import _load_data_py as _load_data
 
3505
    from ._knit_load_data_py import _load_data_py as _load_data