22
22
tree.views.lookup_view()
34
_VIEWS_FORMAT_MARKER_RE = re.compile(r'Bazaar views format (\d+)')
35
_VIEWS_FORMAT1_MARKER = "Bazaar views format 1\n"
33
_VIEWS_FORMAT_MARKER_RE = re.compile(b'Bazaar views format (\\d+)')
34
_VIEWS_FORMAT1_MARKER = b"Bazaar views format 1\n"
37
class NoSuchView(errors.BzrError):
38
"""A view does not exist.
41
_fmt = u"No such view: %(view_name)s."
43
def __init__(self, view_name):
44
self.view_name = view_name
47
class ViewsNotSupported(errors.BzrError):
48
"""Views are not supported by a tree format.
51
_fmt = ("Views are not supported by %(tree)s;"
52
" use 'brz upgrade' to change your tree to a later format.")
54
def __init__(self, tree):
58
class FileOutsideView(errors.BzrError):
60
_fmt = ('Specified file "%(file_name)s" is outside the current view: '
63
def __init__(self, file_name, view_files):
64
self.file_name = file_name
65
self.view_str = ", ".join(view_files)
38
68
class _Views(object):
94
124
:param views: a map from view name to list of files/directories
96
126
if current is not None and current not in views:
97
raise errors.NoSuchView(current)
98
self.tree.lock_write()
127
raise NoSuchView(current)
128
with self.tree.lock_write():
100
129
self._current = current
101
130
self._views = views
102
131
self._save_view_info()
106
133
def lookup_view(self, view_name=None):
107
134
"""Return the contents of a view.
119
146
return self._views[view_name]
121
raise errors.NoSuchView(view_name)
148
raise NoSuchView(view_name)
123
150
def set_view(self, view_name, view_files, make_current=True):
124
151
"""Add or update a view definition.
127
154
:param view_files: the list of files/directories in the view
128
155
:param make_current: make this view the current one or not
130
self.tree.lock_write()
157
with self.tree.lock_write():
132
158
self._load_view_info()
133
159
self._views[view_name] = view_files
135
161
self._current = view_name
136
162
self._save_view_info()
140
164
def delete_view(self, view_name):
141
165
"""Delete a view definition.
143
167
If the view deleted is the current one, the current view is reset.
145
self.tree.lock_write()
169
with self.tree.lock_write():
147
170
self._load_view_info()
149
172
del self._views[view_name]
151
raise errors.NoSuchView(view_name)
174
raise NoSuchView(view_name)
152
175
if view_name == self._current:
153
176
self._current = None
154
177
self._save_view_info()
158
179
def _save_view_info(self):
159
180
"""Save the current view and all view definitions.
161
182
Be sure to have initialised self._current and self._views before
162
183
calling this method.
164
self.tree.lock_write()
185
with self.tree.lock_write():
166
186
if self._current is None:
169
189
keywords = {'current': self._current}
170
self.tree._transport.put_bytes('views',
171
self._serialize_view_content(keywords, self._views))
190
self.tree._transport.put_bytes(
191
'views', self._serialize_view_content(keywords, self._views))
175
193
def _load_view_info(self):
176
194
"""Load the current view and dictionary of view definitions."""
177
195
if not self._loaded:
178
self.tree.lock_read()
196
with self.tree.lock_read():
181
198
view_content = self.tree._transport.get_bytes('views')
182
except errors.NoSuchFile, e:
199
except errors.NoSuchFile:
183
200
self._current, self._views = None, {}
185
202
keywords, self._views = \
186
203
self._deserialize_view_content(view_content)
187
204
self._current = keywords.get('current')
190
205
self._loaded = True
192
207
def _serialize_view_content(self, keywords, view_dict):
193
208
"""Convert view keywords and a view dictionary into a stream."""
194
209
lines = [_VIEWS_FORMAT1_MARKER]
195
210
for key in keywords:
196
line = "%s=%s\n" % (key,keywords[key])
211
line = "%s=%s\n" % (key, keywords[key])
197
212
lines.append(line.encode('utf-8'))
199
214
lines.append("views:\n".encode('utf-8'))
200
215
for view in sorted(view_dict):
201
216
view_data = "%s\0%s\n" % (view, "\0".join(view_dict[view]))
202
217
lines.append(view_data.encode('utf-8'))
203
return "".join(lines)
218
return b"".join(lines)
205
220
def _deserialize_view_content(self, view_content):
206
221
"""Convert a stream into view keywords and a dictionary of views."""
207
222
# as a special case to make initialization easy, an empty definition
208
223
# maps to no current view and an empty view dictionary
209
if view_content == '':
224
if view_content == b'':
211
226
lines = view_content.splitlines()
212
227
match = _VIEWS_FORMAT_MARKER_RE.match(lines[0])
214
229
raise ValueError(
215
230
"format marker missing from top of views file")
216
elif match.group(1) != '1':
231
elif match.group(1) != b'1':
217
232
raise ValueError(
218
233
"cannot decode views format %s" % match.group(1))
257
272
def _not_supported(self, *a, **k):
258
raise errors.ViewsNotSupported(self.tree)
273
raise ViewsNotSupported(self.tree)
260
275
get_view_info = _not_supported
261
276
set_view_info = _not_supported
280
295
"""If a working tree has a view enabled, check the path is within it."""
281
296
if tree.supports_views():
282
297
view_files = tree.views.lookup_view()
283
if view_files and not osutils.is_inside_any(view_files, relpath):
284
raise errors.FileOutsideView(relpath, view_files)
298
if view_files and not osutils.is_inside_any(view_files, relpath):
299
raise FileOutsideView(relpath, view_files)