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

  • Committer: Alexander Belchenko
  • Date: 2006-06-29 08:41:31 UTC
  • mto: (1860.1.1 win32.installer)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060629084131-3ea4d44e3204e36f
win32 installer for bzr.dev.0.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008 Canonical Ltd
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
"""View management.
18
 
 
19
 
Views are contained within a working tree and normally constructed
20
 
when first accessed.  Clients should do, for example, ...
21
 
 
22
 
  tree.views.lookup_view()
23
 
"""
24
 
 
25
 
from __future__ import absolute_import
26
 
 
27
 
import re
28
 
 
29
 
from . import (
30
 
    errors,
31
 
    osutils,
32
 
    )
33
 
 
34
 
 
35
 
_VIEWS_FORMAT_MARKER_RE = re.compile(b'Bazaar views format (\\d+)')
36
 
_VIEWS_FORMAT1_MARKER = b"Bazaar views format 1\n"
37
 
 
38
 
 
39
 
class NoSuchView(errors.BzrError):
40
 
    """A view does not exist.
41
 
    """
42
 
 
43
 
    _fmt = u"No such view: %(view_name)s."
44
 
 
45
 
    def __init__(self, view_name):
46
 
        self.view_name = view_name
47
 
 
48
 
 
49
 
class ViewsNotSupported(errors.BzrError):
50
 
    """Views are not supported by a tree format.
51
 
    """
52
 
 
53
 
    _fmt = ("Views are not supported by %(tree)s;"
54
 
            " use 'brz upgrade' to change your tree to a later format.")
55
 
 
56
 
    def __init__(self, tree):
57
 
        self.tree = tree
58
 
 
59
 
 
60
 
class FileOutsideView(errors.BzrError):
61
 
 
62
 
    _fmt = ('Specified file "%(file_name)s" is outside the current view: '
63
 
            '%(view_str)s')
64
 
 
65
 
    def __init__(self, file_name, view_files):
66
 
        self.file_name = file_name
67
 
        self.view_str = ", ".join(view_files)
68
 
 
69
 
 
70
 
class _Views(object):
71
 
    """Base class for View managers."""
72
 
 
73
 
    def supports_views(self):
74
 
        raise NotImplementedError(self.supports_views)
75
 
 
76
 
 
77
 
class PathBasedViews(_Views):
78
 
    """View storage in an unversioned tree control file.
79
 
 
80
 
    Views are stored in terms of paths relative to the tree root.
81
 
 
82
 
    The top line of the control file is a format marker in the format:
83
 
 
84
 
      Bazaar views format X
85
 
 
86
 
    where X is an integer number. After this top line, version 1 format is
87
 
    stored as follows:
88
 
 
89
 
     * optional name-values pairs in the format 'name=value'
90
 
 
91
 
     * optional view definitions, one per line in the format
92
 
 
93
 
       views:
94
 
       name file1 file2 ...
95
 
       name file1 file2 ...
96
 
 
97
 
    where the fields are separated by a nul character (\0). The views file
98
 
    is encoded in utf-8. The only supported keyword in version 1 is
99
 
    'current' which stores the name of the current view, if any.
100
 
    """
101
 
 
102
 
    def __init__(self, tree):
103
 
        self.tree = tree
104
 
        self._loaded = False
105
 
        self._current = None
106
 
        self._views = {}
107
 
 
108
 
    def supports_views(self):
109
 
        return True
110
 
 
111
 
    def get_view_info(self):
112
 
        """Get the current view and dictionary of views.
113
 
 
114
 
        :return: current, views where
115
 
          current = the name of the current view or None if no view is enabled
116
 
          views = a map from view name to list of files/directories
117
 
        """
118
 
        self._load_view_info()
119
 
        return self._current, self._views
120
 
 
121
 
    def set_view_info(self, current, views):
122
 
        """Set the current view and dictionary of views.
123
 
 
124
 
        :param current: the name of the current view or None if no view is
125
 
          enabled
126
 
        :param views: a map from view name to list of files/directories
127
 
        """
128
 
        if current is not None and current not in views:
129
 
            raise NoSuchView(current)
130
 
        with self.tree.lock_write():
131
 
            self._current = current
132
 
            self._views = views
133
 
            self._save_view_info()
134
 
 
135
 
    def lookup_view(self, view_name=None):
136
 
        """Return the contents of a view.
137
 
 
138
 
        :param view_Name: name of the view or None to lookup the current view
139
 
        :return: the list of files/directories in the requested view
140
 
        """
141
 
        self._load_view_info()
142
 
        try:
143
 
            if view_name is None:
144
 
                if self._current:
145
 
                    view_name = self._current
146
 
                else:
147
 
                    return []
148
 
            return self._views[view_name]
149
 
        except KeyError:
150
 
            raise NoSuchView(view_name)
151
 
 
152
 
    def set_view(self, view_name, view_files, make_current=True):
153
 
        """Add or update a view definition.
154
 
 
155
 
        :param view_name: the name of the view
156
 
        :param view_files: the list of files/directories in the view
157
 
        :param make_current: make this view the current one or not
158
 
        """
159
 
        with self.tree.lock_write():
160
 
            self._load_view_info()
161
 
            self._views[view_name] = view_files
162
 
            if make_current:
163
 
                self._current = view_name
164
 
            self._save_view_info()
165
 
 
166
 
    def delete_view(self, view_name):
167
 
        """Delete a view definition.
168
 
 
169
 
        If the view deleted is the current one, the current view is reset.
170
 
        """
171
 
        with self.tree.lock_write():
172
 
            self._load_view_info()
173
 
            try:
174
 
                del self._views[view_name]
175
 
            except KeyError:
176
 
                raise NoSuchView(view_name)
177
 
            if view_name == self._current:
178
 
                self._current = None
179
 
            self._save_view_info()
180
 
 
181
 
    def _save_view_info(self):
182
 
        """Save the current view and all view definitions.
183
 
 
184
 
        Be sure to have initialised self._current and self._views before
185
 
        calling this method.
186
 
        """
187
 
        with self.tree.lock_write():
188
 
            if self._current is None:
189
 
                keywords = {}
190
 
            else:
191
 
                keywords = {'current': self._current}
192
 
            self.tree._transport.put_bytes(
193
 
                'views', self._serialize_view_content(keywords, self._views))
194
 
 
195
 
    def _load_view_info(self):
196
 
        """Load the current view and dictionary of view definitions."""
197
 
        if not self._loaded:
198
 
            with self.tree.lock_read():
199
 
                try:
200
 
                    view_content = self.tree._transport.get_bytes('views')
201
 
                except errors.NoSuchFile:
202
 
                    self._current, self._views = None, {}
203
 
                else:
204
 
                    keywords, self._views = \
205
 
                        self._deserialize_view_content(view_content)
206
 
                    self._current = keywords.get('current')
207
 
            self._loaded = True
208
 
 
209
 
    def _serialize_view_content(self, keywords, view_dict):
210
 
        """Convert view keywords and a view dictionary into a stream."""
211
 
        lines = [_VIEWS_FORMAT1_MARKER]
212
 
        for key in keywords:
213
 
            line = "%s=%s\n" % (key, keywords[key])
214
 
            lines.append(line.encode('utf-8'))
215
 
        if view_dict:
216
 
            lines.append("views:\n".encode('utf-8'))
217
 
            for view in sorted(view_dict):
218
 
                view_data = "%s\0%s\n" % (view, "\0".join(view_dict[view]))
219
 
                lines.append(view_data.encode('utf-8'))
220
 
        return b"".join(lines)
221
 
 
222
 
    def _deserialize_view_content(self, view_content):
223
 
        """Convert a stream into view keywords and a dictionary of views."""
224
 
        # as a special case to make initialization easy, an empty definition
225
 
        # maps to no current view and an empty view dictionary
226
 
        if view_content == b'':
227
 
            return {}, {}
228
 
        lines = view_content.splitlines()
229
 
        match = _VIEWS_FORMAT_MARKER_RE.match(lines[0])
230
 
        if not match:
231
 
            raise ValueError(
232
 
                "format marker missing from top of views file")
233
 
        elif match.group(1) != b'1':
234
 
            raise ValueError(
235
 
                "cannot decode views format %s" % match.group(1))
236
 
        try:
237
 
            keywords = {}
238
 
            views = {}
239
 
            in_views = False
240
 
            for line in lines[1:]:
241
 
                text = line.decode('utf-8')
242
 
                if in_views:
243
 
                    parts = text.split('\0')
244
 
                    view = parts.pop(0)
245
 
                    views[view] = parts
246
 
                elif text == 'views:':
247
 
                    in_views = True
248
 
                    continue
249
 
                elif text.find('=') >= 0:
250
 
                    # must be a name-value pair
251
 
                    keyword, value = text.split('=', 1)
252
 
                    keywords[keyword] = value
253
 
                else:
254
 
                    raise ValueError("failed to deserialize views line %s",
255
 
                                     text)
256
 
            return keywords, views
257
 
        except ValueError as e:
258
 
            raise ValueError("failed to deserialize views content %r: %s"
259
 
                             % (view_content, e))
260
 
 
261
 
 
262
 
class DisabledViews(_Views):
263
 
    """View storage that refuses to store anything.
264
 
 
265
 
    This is used by older formats that can't store views.
266
 
    """
267
 
 
268
 
    def __init__(self, tree):
269
 
        self.tree = tree
270
 
 
271
 
    def supports_views(self):
272
 
        return False
273
 
 
274
 
    def _not_supported(self, *a, **k):
275
 
        raise ViewsNotSupported(self.tree)
276
 
 
277
 
    get_view_info = _not_supported
278
 
    set_view_info = _not_supported
279
 
    lookup_view = _not_supported
280
 
    set_view = _not_supported
281
 
    delete_view = _not_supported
282
 
 
283
 
 
284
 
def view_display_str(view_files, encoding=None):
285
 
    """Get the display string for a list of view files.
286
 
 
287
 
    :param view_files: the list of file names
288
 
    :param encoding: the encoding to display the files in
289
 
    """
290
 
    if encoding is None:
291
 
        return ", ".join(view_files)
292
 
    else:
293
 
        return ", ".join([v.encode(encoding, 'replace') for v in view_files])
294
 
 
295
 
 
296
 
def check_path_in_view(tree, relpath):
297
 
    """If a working tree has a view enabled, check the path is within it."""
298
 
    if tree.supports_views():
299
 
        view_files = tree.views.lookup_view()
300
 
        if view_files and not osutils.is_inside_any(view_files, relpath):
301
 
            raise FileOutsideView(relpath, view_files)