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

  • Committer: Robert Collins
  • Date: 2008-01-06 20:04:22 UTC
  • mto: (3221.11.1 StackableBranch)
  • mto: This revision was merged to the branch mainline in revision 3226.
  • Revision ID: robertc@robertcollins.net-20080106200422-x8yz6cxotlzltvwp
The bzrdir format registry now accepts an ``alias`` keyword to
register_metadir, used to indicate that a format name is an alias for
some other format and thus should not be reported when describing the
format. (Robert Collins)
-------------- This line and the fmllowing will be ignored --------------

modified:
  NEWS
  bzrlib/bzrdir.py
  bzrlib/info.py
  bzrlib/tests/test_bzrdir.py
  bzrlib/tests/test_info.py

=== modified file 'NEWS'
--- a/NEWS      2008-01-02 22:30:46 +0000
+++ b/NEWS      2008-01-06 20:04:15 +0000
@@ -135,6 +135,11 @@
     * Patience Diff now supports arbitrary python objects, as long as they
       support ``hash()``. (John Arbash Meinel)
 
+    * The bzrdir format registry now accepts an ``alias`` keyword to
+      register_metadir, used to indicate that a format name is an alias for
+      some other format and thus should not be reported when describing the
+      format. (Robert Collins)
+
   API BREAKS:
 
   TESTING:

=== modified file 'bzrlib/bzrdir.py'
--- a/bzrlib/bzrdir.py  2008-01-02 22:30:46 +0000
+++ b/bzrlib/bzrdir.py  2008-01-06 19:41:29 +0000
@@ -2447,12 +2447,22 @@
     e.g. BzrDirMeta1 with weave repository.  Also, it's more user-oriented.
     """
 
+    def __init__(self):
+        """Create a BzrDirFormatRegistry."""
+        self._aliases = set()
+        super(BzrDirFormatRegistry, self).__init__()
+
+    def aliases(self):
+        """Return a set of the format names which are aliases."""
+        return frozenset(self._aliases)
+
     def register_metadir(self, key,
              repository_format, help, native=True, deprecated=False,
              branch_format=None,
              tree_format=None,
              hidden=False,
-             experimental=False):
+             experimental=False,
+             alias=False):
         """Register a metadir subformat.
 
         These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
@@ -2491,10 +2501,10 @@
                 bd.repository_format = _load(repository_format)
             return bd
         self.register(key, helper, help, native, deprecated, hidden,
-            experimental)
+            experimental, alias)
 
     def register(self, key, factory, help, native=True, deprecated=False,
-                 hidden=False, experimental=False):
+                 hidden=False, experimental=False, alias=False):
         """Register a BzrDirFormat factory.
         
         The factory must be a callable that takes one parameter: the key.
@@ -2505,11 +2515,15 @@
         """
         registry.Registry.register(self, key, factory, help,
             BzrDirFormatInfo(native, deprecated, hidden, experimental))
+        if alias:
+            self._aliases.add(key)
 
     def register_lazy(self, key, module_name, member_name, help, native=True,
-                      deprecated=False, hidden=False, experimental=False):
+        deprecated=False, hidden=False, experimental=False, alias=False):
         registry.Registry.register_lazy(self, key, module_name, member_name,
             help, BzrDirFormatInfo(native, deprecated, hidden, experimental))
+        if alias:
+            self._aliases.add(key)
 
     def set_default(self, key):
         """Set the 'default' key to be a clone of the supplied key.
@@ -2518,6 +2532,7 @@
         """
         registry.Registry.register(self, 'default', self.get(key),
             self.get_help(key), info=self.get_info(key))
+        self._aliases.add('default')
 
     def set_default_repository(self, key):
         """Set the FormatRegistry default and Repository default.
@@ -2670,6 +2685,7 @@
     tree_format='bzrlib.workingtree.WorkingTreeFormat4',
     hidden=False,
     )
+# The following two formats should always just be aliases.
 format_registry.register_metadir('development',
     'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0',
     help='Current development format. Can convert data to and from pack-0.92 '
@@ -2681,6 +2697,7 @@
     branch_format='bzrlib.branch.BzrBranchFormat6',
     tree_format='bzrlib.workingtree.WorkingTreeFormat4',
     experimental=True,
+    alias=True,
     )
 format_registry.register_metadir('development-subtree',
     'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0Subtree',
@@ -2693,7 +2710,9 @@
     branch_format='bzrlib.branch.BzrBranchFormat6',
     tree_format='bzrlib.workingtree.WorkingTreeFormat4',
     experimental=True,
+    alias=True,
     )
+# And the development formats which the will have aliased one of follow:
 format_registry.register_metadir('development0',
     'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0',
     help='Trivial rename of pack-0.92 to provide a development format. '

=== modified file 'bzrlib/info.py'
--- a/bzrlib/info.py    2007-11-06 09:00:25 +0000
+++ b/bzrlib/info.py    2008-01-06 20:01:30 +0000
@@ -440,7 +440,9 @@
         tree.bzrdir.root_transport.base):
         branch = None
         repository = None
-    for key in bzrdir.format_registry.keys():
+    non_aliases = set(bzrdir.format_registry.keys())
+    non_aliases.difference_update(bzrdir.format_registry.aliases())
+    for key in non_aliases:
         format = bzrdir.format_registry.make_bzrdir(key)
         if isinstance(format, bzrdir.BzrDirMetaFormat1):
             if (tree and format.workingtree_format !=
@@ -457,11 +459,12 @@
         candidates.append(key)
     if len(candidates) == 0:
         return 'unnamed'
-    new_candidates = [c for c in candidates if c != 'default']
-    if len(new_candidates) > 0:
-        candidates = new_candidates
+    candidates.sort()
     new_candidates = [c for c in candidates if not
         bzrdir.format_registry.get_info(c).hidden]
     if len(new_candidates) > 0:
+        # If there are any non-hidden formats that match, only return those to
+        # avoid listing hidden formats except when only a hidden format will
+        # do.
         candidates = new_candidates
     return ' or '.join(candidates)

=== modified file 'bzrlib/tests/test_bzrdir.py'
--- a/bzrlib/tests/test_bzrdir.py       2007-12-21 20:32:22 +0000
+++ b/bzrlib/tests/test_bzrdir.py       2008-01-06 19:45:00 +0000
@@ -170,6 +170,16 @@
         finally:
             bzrdir.format_registry.set_default_repository(old_default)
 
+    def test_aliases(self):
+        a_registry = bzrdir.BzrDirFormatRegistry()
+        a_registry.register('weave', bzrdir.BzrDirFormat6,
+            'Pre-0.8 format.  Slower and does not support checkouts or shared'
+            ' repositories', deprecated=True)
+        a_registry.register('weavealias', bzrdir.BzrDirFormat6,
+            'Pre-0.8 format.  Slower and does not support checkouts or shared'
+            ' repositories', deprecated=True, alias=True)
+        self.assertEqual(frozenset(['weavealias']), a_registry.aliases())
+    
 
 class SampleBranch(bzrlib.branch.Branch):
     """A dummy branch for guess what, dummy use."""

=== modified file 'bzrlib/tests/test_info.py'
--- a/bzrlib/tests/test_info.py 2007-11-26 13:55:51 +0000
+++ b/bzrlib/tests/test_info.py 2008-01-06 20:02:10 +0000
@@ -126,16 +126,22 @@
 
     def test_describe_tree_format(self):
         for key in bzrdir.format_registry.keys():
-            if key == 'default':
+            if key in bzrdir.format_registry.aliases():
                 continue
             self.assertTreeDescription(key)
 
     def test_describe_checkout_format(self):
         for key in bzrdir.format_registry.keys():
-            if key in ('default', 'weave', 'experimental'):
-                continue
-            if key.startswith('experimental-'):
-                # these are typically hidden or aliases for other formats
+            if key in bzrdir.format_registry.aliases():
+                # Aliases will not describe correctly in the UI because the
+                # real format is found.
+                continue
+            # legacy: weave does not support checkouts
+            if key == 'weave':
+                continue
+            if bzrdir.format_registry.get_info(key).experimental:
+                # We don't require that experimental formats support checkouts
+                # or describe correctly in the UI.
                 continue
             expected = None
             if key in ('dirstate', 'dirstate-tags', 'dirstate-with-subtree',
@@ -149,7 +155,7 @@
 
     def test_describe_branch_format(self):
         for key in bzrdir.format_registry.keys():
-            if key == 'default':
+            if key in bzrdir.format_registry.aliases():
                 continue
             expected = None
             if key in ('dirstate', 'knit'):
@@ -158,7 +164,7 @@
 
     def test_describe_repo_format(self):
         for key in bzrdir.format_registry.keys():
-            if key == 'default':
+            if key in bzrdir.format_registry.aliases():
                 continue
             expected = None
             if key in ('dirstate', 'knit', 'dirstate-tags'):

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005 Aaron Bentley, Canonical Ltd
 
2
# <aaron.bentley@utoronto.ca>
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
 
 
18
 
 
19
class IterableFileBase(object):
 
20
    """Create a file-like object from any iterable"""
 
21
 
 
22
    def __init__(self, iterable):
 
23
        object.__init__(self)
 
24
        self._iter = iterable.__iter__()
 
25
        self._buffer = ""
 
26
        self.done = False
 
27
 
 
28
    def read_n(self, length):
 
29
        """
 
30
        >>> IterableFileBase(['This ', 'is ', 'a ', 'test.']).read_n(8)
 
31
        'This is '
 
32
        """
 
33
        def test_length(result):
 
34
            if len(result) >= length:
 
35
                return length
 
36
            else:
 
37
                return None
 
38
        return self._read(test_length)
 
39
 
 
40
    def read_to(self, sequence, length=None):
 
41
        """
 
42
        >>> f = IterableFileBase(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.'])
 
43
        >>> f.read_to('\\n')
 
44
        'Th\\n'
 
45
        >>> f.read_to('\\n')
 
46
        'is is \\n'
 
47
        """
 
48
        def test_contents(result):
 
49
            if length is not None:
 
50
                if len(result) >= length:
 
51
                    return length
 
52
            try:
 
53
                return result.index(sequence)+len(sequence)
 
54
            except ValueError:
 
55
                return None
 
56
        return self._read(test_contents)
 
57
 
 
58
    def _read(self, result_length):
 
59
        """
 
60
        Read data until result satisfies the condition result_length.
 
61
        result_length is a callable that returns None until the condition
 
62
        is satisfied, and returns the length of the result to use when
 
63
        the condition is satisfied.  (i.e. it returns the length of the
 
64
        subset of the first condition match.)
 
65
        """
 
66
        result = self._buffer
 
67
        while result_length(result) is None:
 
68
            try:
 
69
                result += self._iter.next()
 
70
            except StopIteration:
 
71
                self.done = True
 
72
                self._buffer = ""
 
73
                return result
 
74
        output_length = result_length(result)
 
75
        self._buffer = result[output_length:]
 
76
        return result[:output_length]
 
77
 
 
78
    def read_all(self):
 
79
        """
 
80
        >>> IterableFileBase(['This ', 'is ', 'a ', 'test.']).read_all()
 
81
        'This is a test.'
 
82
        """
 
83
        def no_stop(result):
 
84
            return None
 
85
        return self._read(no_stop)
 
86
 
 
87
 
 
88
    def push_back(self, contents):
 
89
        """
 
90
        >>> f = IterableFileBase(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.'])
 
91
        >>> f.read_to('\\n')
 
92
        'Th\\n'
 
93
        >>> f.push_back("Sh")
 
94
        >>> f.read_all()
 
95
        'Shis is \\na te\\nst.'
 
96
        """
 
97
        self._buffer = contents + self._buffer
 
98
 
 
99
 
 
100
class IterableFile(object):
 
101
    """This class supplies all File methods that can be implemented cheaply."""
 
102
    def __init__(self, iterable):
 
103
        object.__init__(self)
 
104
        self._file_base = IterableFileBase(iterable)
 
105
        self._iter = self._make_iterator()
 
106
        self._closed = False
 
107
        self.softspace = 0
 
108
 
 
109
    def _make_iterator(self):
 
110
        while not self._file_base.done:
 
111
            self._check_closed()
 
112
            result = self._file_base.read_to('\n')
 
113
            if result != '':
 
114
                yield result
 
115
 
 
116
    def _check_closed(self):
 
117
        if self.closed:
 
118
            raise ValueError("File is closed.")
 
119
 
 
120
    def close(self):
 
121
        """
 
122
        >>> f = IterableFile(['This ', 'is ', 'a ', 'test.'])
 
123
        >>> f.closed
 
124
        False
 
125
        >>> f.close()
 
126
        >>> f.closed
 
127
        True
 
128
        """
 
129
        self._file_base.done = True
 
130
        self._closed = True
 
131
 
 
132
    closed = property(lambda x: x._closed)
 
133
 
 
134
    def flush(self):
 
135
        """No-op for standard compliance.
 
136
        >>> f = IterableFile([])
 
137
        >>> f.close()
 
138
        >>> f.flush()
 
139
        Traceback (most recent call last):
 
140
        ValueError: File is closed.
 
141
        """
 
142
        self._check_closed()
 
143
 
 
144
    def next(self):
 
145
        """Implementation of the iterator protocol's next()
 
146
 
 
147
        >>> f = IterableFile(['This \\n', 'is ', 'a ', 'test.'])
 
148
        >>> f.next()
 
149
        'This \\n'
 
150
        >>> f.close()
 
151
        >>> f.next()
 
152
        Traceback (most recent call last):
 
153
        ValueError: File is closed.
 
154
        >>> f = IterableFile(['This \\n', 'is ', 'a ', 'test.\\n'])
 
155
        >>> f.next()
 
156
        'This \\n'
 
157
        >>> f.next()
 
158
        'is a test.\\n'
 
159
        >>> f.next()
 
160
        Traceback (most recent call last):
 
161
        StopIteration
 
162
        """
 
163
        self._check_closed()
 
164
        return self._iter.next()
 
165
 
 
166
    def __iter__(self):
 
167
        """
 
168
        >>> list(IterableFile(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.']))
 
169
        ['Th\\n', 'is is \\n', 'a te\\n', 'st.']
 
170
        >>> f = IterableFile(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.'])
 
171
        >>> f.close()
 
172
        >>> list(f)
 
173
        Traceback (most recent call last):
 
174
        ValueError: File is closed.
 
175
        """
 
176
        return self
 
177
 
 
178
    def read(self, length=None):
 
179
        """
 
180
        >>> IterableFile(['This ', 'is ', 'a ', 'test.']).read()
 
181
        'This is a test.'
 
182
        >>> f = IterableFile(['This ', 'is ', 'a ', 'test.'])
 
183
        >>> f.read(10)
 
184
        'This is a '
 
185
        >>> f = IterableFile(['This ', 'is ', 'a ', 'test.'])
 
186
        >>> f.close()
 
187
        >>> f.read(10)
 
188
        Traceback (most recent call last):
 
189
        ValueError: File is closed.
 
190
        """
 
191
        self._check_closed()
 
192
        if length is None:
 
193
            return self._file_base.read_all()
 
194
        else:
 
195
            return self._file_base.read_n(length)
 
196
 
 
197
    def read_to(self, sequence, size=None):
 
198
        """
 
199
        Read characters until a sequence is found, with optional max size.
 
200
        The specified sequence, if found, will be included in the result
 
201
 
 
202
        >>> f = IterableFile(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.'])
 
203
        >>> f.read_to('i')
 
204
        'Th\\ni'
 
205
        >>> f.read_to('i')
 
206
        's i'
 
207
        >>> f.close()
 
208
        >>> f.read_to('i')
 
209
        Traceback (most recent call last):
 
210
        ValueError: File is closed.
 
211
        """
 
212
        self._check_closed()
 
213
        return self._file_base.read_to(sequence, size)
 
214
 
 
215
    def readline(self, size=None):
 
216
        """
 
217
        >>> f = IterableFile(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.'])
 
218
        >>> f.readline()
 
219
        'Th\\n'
 
220
        >>> f.readline(4)
 
221
        'is i'
 
222
        >>> f.close()
 
223
        >>> f.readline()
 
224
        Traceback (most recent call last):
 
225
        ValueError: File is closed.
 
226
        """
 
227
        return self.read_to('\n', size)
 
228
 
 
229
    def readlines(self, sizehint=None):
 
230
        """
 
231
        >>> f = IterableFile(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.'])
 
232
        >>> f.readlines()
 
233
        ['Th\\n', 'is is \\n', 'a te\\n', 'st.']
 
234
        >>> f = IterableFile(['Th\\nis ', 'is \\n', 'a ', 'te\\nst.'])
 
235
        >>> f.close()
 
236
        >>> f.readlines()
 
237
        Traceback (most recent call last):
 
238
        ValueError: File is closed.
 
239
        """
 
240
        lines = []
 
241
        while True:
 
242
            line = self.readline()
 
243
            if line == "":
 
244
                return lines
 
245
            if sizehint is None:
 
246
                lines.append(line)
 
247
            elif len(line) < sizehint:
 
248
                lines.append(line)
 
249
                sizehint -= len(line)
 
250
            else:
 
251
                self._file_base.push_back(line)
 
252
                return lines
 
253
 
 
254
        
 
255
if __name__ == "__main__":
 
256
    import doctest
 
257
    doctest.testmod()