5752.3.8
by John Arbash Meinel
Merge bzr.dev 5764 to resolve release-notes (aka NEWS) conflicts |
1 |
# Copyright (C) 2008, 2009, 2011 Canonical Ltd
|
3368.2.1
by Ian Clatworthy
first cut at working tree content filtering |
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
|
|
4183.7.1
by Sabin Iacob
update FSF mailing address |
15 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
3368.2.1
by Ian Clatworthy
first cut at working tree content filtering |
16 |
|
17 |
||
3368.2.6
by Ian Clatworthy
make filter API explicit wrt chunks and add context parameter |
18 |
"""Working tree content filtering support.
|
19 |
||
3368.2.22
by Ian Clatworthy
more changes from abentley's review |
20 |
A filter consists of a read converter, write converter pair.
|
3368.2.25
by Ian Clatworthy
feedback from poolie's review |
21 |
The content in the working tree is called the convenience format
|
22 |
while the content actually stored in called the canonical format.
|
|
23 |
The read converter produces canonical content from convenience
|
|
24 |
content while the writer goes the other way.
|
|
25 |
||
3368.2.22
by Ian Clatworthy
more changes from abentley's review |
26 |
Converters have the following signatures::
|
3368.2.6
by Ian Clatworthy
make filter API explicit wrt chunks and add context parameter |
27 |
|
3368.2.22
by Ian Clatworthy
more changes from abentley's review |
28 |
read_converter(chunks) -> chunks
|
29 |
write_converter(chunks, context) -> chunks
|
|
3368.2.6
by Ian Clatworthy
make filter API explicit wrt chunks and add context parameter |
30 |
|
31 |
where:
|
|
32 |
||
3368.2.22
by Ian Clatworthy
more changes from abentley's review |
33 |
* chunks is an iterator over a sequence of byte strings
|
34 |
||
35 |
* context is an optional ContentFilterContent object (possibly None)
|
|
36 |
providing converters access to interesting information, e.g. the
|
|
37 |
relative path of the file.
|
|
38 |
||
39 |
Note that context is currently only supported for write converters.
|
|
3368.2.6
by Ian Clatworthy
make filter API explicit wrt chunks and add context parameter |
40 |
"""
|
41 |
||
4183.1.1
by Vincent Ladeuil
Fix python2.6 obsoleted import. |
42 |
from cStringIO import StringIO |
3368.2.1
by Ian Clatworthy
first cut at working tree content filtering |
43 |
from bzrlib.lazy_import import lazy_import |
44 |
lazy_import(globals(), """ |
|
45 |
from bzrlib import (
|
|
3368.2.39
by Ian Clatworthy
add config & caching to ContentFilterContext |
46 |
config,
|
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
47 |
errors,
|
48 |
osutils,
|
|
3368.2.19
by Ian Clatworthy
first round of changes from abentley's review |
49 |
registry,
|
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
50 |
)
|
3368.2.1
by Ian Clatworthy
first cut at working tree content filtering |
51 |
""") |
52 |
||
53 |
||
54 |
class ContentFilter(object): |
|
55 |
||
56 |
def __init__(self, reader, writer): |
|
57 |
"""Create a filter that converts content while reading and writing.
|
|
3368.2.47
by Ian Clatworthy
merge bzr.dev r4042 |
58 |
|
3368.2.25
by Ian Clatworthy
feedback from poolie's review |
59 |
:param reader: function for converting convenience to canonical content
|
60 |
:param writer: function for converting canonical to convenience content
|
|
3368.2.1
by Ian Clatworthy
first cut at working tree content filtering |
61 |
"""
|
62 |
self.reader = reader |
|
63 |
self.writer = writer |
|
64 |
||
3368.2.30
by Ian Clatworthy
add -Dfilters support |
65 |
def __repr__(self): |
66 |
return "reader: %s, writer: %s" % (self.reader,self.writer) |
|
67 |
||
3368.2.1
by Ian Clatworthy
first cut at working tree content filtering |
68 |
|
3368.2.6
by Ian Clatworthy
make filter API explicit wrt chunks and add context parameter |
69 |
class ContentFilterContext(object): |
3368.2.33
by Ian Clatworthy
expand filter context to support interesting stuff |
70 |
"""Object providing information that filters can use."""
|
3368.2.9
by Ian Clatworthy
default context should only support relpath |
71 |
|
3368.2.33
by Ian Clatworthy
expand filter context to support interesting stuff |
72 |
def __init__(self, relpath=None, tree=None, entry=None): |
3368.2.9
by Ian Clatworthy
default context should only support relpath |
73 |
"""Create a context.
|
74 |
||
75 |
:param relpath: the relative path or None if this context doesn't
|
|
76 |
support that information.
|
|
3368.2.33
by Ian Clatworthy
expand filter context to support interesting stuff |
77 |
:param tree: the Tree providing this file or None if this context
|
78 |
doesn't support that information.
|
|
79 |
:param entry: the InventoryEntry object if it is already known or
|
|
80 |
None if it should be derived if possible
|
|
3368.2.9
by Ian Clatworthy
default context should only support relpath |
81 |
"""
|
82 |
self._relpath = relpath |
|
3368.2.33
by Ian Clatworthy
expand filter context to support interesting stuff |
83 |
self._tree = tree |
84 |
self._entry = entry |
|
3368.2.39
by Ian Clatworthy
add config & caching to ContentFilterContext |
85 |
# Cached values
|
86 |
self._revision_id = None |
|
87 |
self._revision = None |
|
88 |
self._config = None |
|
3368.2.6
by Ian Clatworthy
make filter API explicit wrt chunks and add context parameter |
89 |
|
90 |
def relpath(self): |
|
91 |
"""Relative path of file to tree-root."""
|
|
3368.2.19
by Ian Clatworthy
first round of changes from abentley's review |
92 |
return self._relpath |
3368.2.6
by Ian Clatworthy
make filter API explicit wrt chunks and add context parameter |
93 |
|
3368.2.33
by Ian Clatworthy
expand filter context to support interesting stuff |
94 |
def source_tree(self): |
95 |
"""Source Tree object."""
|
|
96 |
return self._tree |
|
97 |
||
3368.2.35
by Ian Clatworthy
add revision_id to filter context |
98 |
def file_id(self): |
3368.2.33
by Ian Clatworthy
expand filter context to support interesting stuff |
99 |
"""File-id of file."""
|
100 |
if self._entry is not None: |
|
101 |
return self._entry.file_id |
|
102 |
elif self._tree is None: |
|
103 |
return None |
|
104 |
else: |
|
3368.2.35
by Ian Clatworthy
add revision_id to filter context |
105 |
return self._tree.path2id(self._relpath) |
106 |
||
107 |
def revision_id(self): |
|
108 |
"""Id of revision that last changed this file."""
|
|
3368.2.39
by Ian Clatworthy
add config & caching to ContentFilterContext |
109 |
if self._revision_id is None: |
110 |
if self._entry is not None: |
|
111 |
self._revision_id = self._entry.revision |
|
112 |
elif self._tree is not None: |
|
113 |
file_id = self._tree.path2id(self._relpath) |
|
114 |
self._entry = self._tree.inventory[file_id] |
|
115 |
self._revision_id = self._entry.revision |
|
116 |
return self._revision_id |
|
3368.2.33
by Ian Clatworthy
expand filter context to support interesting stuff |
117 |
|
118 |
def revision(self): |
|
3368.2.35
by Ian Clatworthy
add revision_id to filter context |
119 |
"""Revision this variation of the file was introduced in."""
|
3368.2.39
by Ian Clatworthy
add config & caching to ContentFilterContext |
120 |
if self._revision is None: |
121 |
rev_id = self.revision_id() |
|
122 |
if rev_id is not None: |
|
123 |
repo = getattr(self._tree, '_repository', None) |
|
124 |
if repo is None: |
|
125 |
repo = self._tree.branch.repository |
|
126 |
self._revision = repo.get_revision(rev_id) |
|
127 |
return self._revision |
|
128 |
||
129 |
def config(self): |
|
130 |
"""The Config object to search for configuration settings."""
|
|
131 |
if self._config is None: |
|
132 |
branch = getattr(self._tree, 'branch', None) |
|
133 |
if branch is not None: |
|
134 |
self._config = branch.get_config() |
|
135 |
else: |
|
136 |
self._config = config.GlobalConfig() |
|
137 |
return self._config |
|
3368.2.33
by Ian Clatworthy
expand filter context to support interesting stuff |
138 |
|
3368.2.6
by Ian Clatworthy
make filter API explicit wrt chunks and add context parameter |
139 |
|
3368.2.10
by Ian Clatworthy
don't support context for read filters |
140 |
def filtered_input_file(f, filters): |
3368.2.1
by Ian Clatworthy
first cut at working tree content filtering |
141 |
"""Get an input file that converts external to internal content.
|
3368.2.47
by Ian Clatworthy
merge bzr.dev r4042 |
142 |
|
3368.2.1
by Ian Clatworthy
first cut at working tree content filtering |
143 |
:param f: the original input file
|
144 |
:param filters: the stack of filters to apply
|
|
145 |
:return: a file-like object
|
|
146 |
"""
|
|
147 |
if filters: |
|
3368.2.6
by Ian Clatworthy
make filter API explicit wrt chunks and add context parameter |
148 |
chunks = [f.read()] |
3368.2.1
by Ian Clatworthy
first cut at working tree content filtering |
149 |
for filter in filters: |
3368.2.10
by Ian Clatworthy
don't support context for read filters |
150 |
if filter.reader is not None: |
151 |
chunks = filter.reader(chunks) |
|
4183.1.1
by Vincent Ladeuil
Fix python2.6 obsoleted import. |
152 |
return StringIO(''.join(chunks)) |
3368.2.1
by Ian Clatworthy
first cut at working tree content filtering |
153 |
else: |
154 |
return f |
|
155 |
||
156 |
||
3368.2.19
by Ian Clatworthy
first round of changes from abentley's review |
157 |
def filtered_output_bytes(chunks, filters, context=None): |
158 |
"""Convert byte chunks from internal to external format.
|
|
3368.2.47
by Ian Clatworthy
merge bzr.dev r4042 |
159 |
|
3368.2.6
by Ian Clatworthy
make filter API explicit wrt chunks and add context parameter |
160 |
:param chunks: an iterator containing the original content
|
3368.2.1
by Ian Clatworthy
first cut at working tree content filtering |
161 |
:param filters: the stack of filters to apply
|
3368.2.6
by Ian Clatworthy
make filter API explicit wrt chunks and add context parameter |
162 |
:param context: a ContentFilterContext object passed to
|
163 |
each filter
|
|
3368.2.3
by Ian Clatworthy
tests for ..input_file and ..output_lines |
164 |
:return: an iterator containing the content to output
|
3368.2.1
by Ian Clatworthy
first cut at working tree content filtering |
165 |
"""
|
166 |
if filters: |
|
167 |
for filter in reversed(filters): |
|
3368.2.10
by Ian Clatworthy
don't support context for read filters |
168 |
if filter.writer is not None: |
169 |
chunks = filter.writer(chunks, context) |
|
3368.2.6
by Ian Clatworthy
make filter API explicit wrt chunks and add context parameter |
170 |
return chunks |
3368.2.1
by Ian Clatworthy
first cut at working tree content filtering |
171 |
|
172 |
||
3368.2.19
by Ian Clatworthy
first round of changes from abentley's review |
173 |
def internal_size_sha_file_byname(name, filters): |
174 |
"""Get size and sha of internal content given external content.
|
|
3368.2.47
by Ian Clatworthy
merge bzr.dev r4042 |
175 |
|
3368.2.6
by Ian Clatworthy
make filter API explicit wrt chunks and add context parameter |
176 |
:param name: path to file
|
177 |
:param filters: the stack of filters to apply
|
|
178 |
"""
|
|
3368.2.19
by Ian Clatworthy
first round of changes from abentley's review |
179 |
f = open(name, 'rb', 65000) |
180 |
try: |
|
181 |
if filters: |
|
182 |
f = filtered_input_file(f, filters) |
|
3368.2.49
by Ian Clatworthy
added osutils.size_sha_file() with tests |
183 |
return osutils.size_sha_file(f) |
3368.2.19
by Ian Clatworthy
first round of changes from abentley's review |
184 |
finally: |
185 |
f.close() |
|
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
186 |
|
187 |
||
188 |
# The registry of filter stacks indexed by name.
|
|
3368.2.26
by Ian Clatworthy
more feedback tweaks |
189 |
# See register_filter_stack_map for details on the registered values.
|
3368.2.19
by Ian Clatworthy
first round of changes from abentley's review |
190 |
_filter_stacks_registry = registry.Registry() |
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
191 |
|
192 |
||
193 |
# Cache of preferences -> stack
|
|
3368.2.25
by Ian Clatworthy
feedback from poolie's review |
194 |
# TODO: make this per branch (say) rather than global
|
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
195 |
_stack_cache = {} |
196 |
||
197 |
||
4257.1.1
by Ian Clatworthy
register content filters using a callable, not a dict+callable |
198 |
def register_filter_stack_map(name, stack_map_lookup): |
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
199 |
"""Register the filter stacks to use for various preference values.
|
200 |
||
201 |
:param name: the preference/filter-stack name
|
|
4257.1.1
by Ian Clatworthy
register content filters using a callable, not a dict+callable |
202 |
:param stack_map_lookup: a callable where
|
203 |
the parameter is the preference value to match and
|
|
204 |
the result is the matching stack of filters to use,
|
|
205 |
or None if none.
|
|
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
206 |
"""
|
3368.2.19
by Ian Clatworthy
first round of changes from abentley's review |
207 |
if name in _filter_stacks_registry: |
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
208 |
raise errors.BzrError( |
209 |
"filter stack for %s already installed" % name) |
|
4257.1.1
by Ian Clatworthy
register content filters using a callable, not a dict+callable |
210 |
_filter_stacks_registry.register(name, stack_map_lookup) |
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
211 |
|
212 |
||
4182.1.1
by Jelmer Vernooij
Add wrapper function for lazily registering filter stack maps. |
213 |
def lazy_register_filter_stack_map(name, module_name, member_name): |
214 |
"""Lazily register the filter stacks to use for various preference values.
|
|
215 |
||
216 |
:param name: the preference/filter-stack name
|
|
217 |
:param module_name: The python path to the module of the filter stack map.
|
|
4257.1.1
by Ian Clatworthy
register content filters using a callable, not a dict+callable |
218 |
:param member_name: The name of the stack_map_lookup callable
|
4210.3.2
by Ian Clatworthy
tweak docstring for lazy content filter registration |
219 |
in the module.
|
4182.1.1
by Jelmer Vernooij
Add wrapper function for lazily registering filter stack maps. |
220 |
"""
|
221 |
if name in _filter_stacks_registry: |
|
222 |
raise errors.BzrError( |
|
223 |
"filter stack for %s already installed" % name) |
|
224 |
_filter_stacks_registry.register_lazy(name, module_name, member_name) |
|
225 |
||
226 |
||
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
227 |
def _get_registered_names(): |
228 |
"""Get the list of names with filters registered."""
|
|
229 |
# Note: We may want to intelligently order these later.
|
|
230 |
# If so, the register_ fn will need to support an optional priority.
|
|
231 |
return _filter_stacks_registry.keys() |
|
232 |
||
233 |
||
234 |
def _get_filter_stack_for(preferences): |
|
235 |
"""Get the filter stack given a sequence of preferences.
|
|
3368.2.47
by Ian Clatworthy
merge bzr.dev r4042 |
236 |
|
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
237 |
:param preferences: a sequence of (name,value) tuples where
|
238 |
name is the preference name and
|
|
3368.2.25
by Ian Clatworthy
feedback from poolie's review |
239 |
value is the key into the filter stack map registered
|
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
240 |
for that preference.
|
241 |
"""
|
|
3368.2.16
by Ian Clatworthy
add real implementation of Tree.get_filter_stack |
242 |
if preferences is None: |
243 |
return [] |
|
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
244 |
stack = _stack_cache.get(preferences) |
245 |
if stack is not None: |
|
246 |
return stack |
|
247 |
stack = [] |
|
248 |
for k, v in preferences: |
|
4423.2.1
by Ian Clatworthy
fix bug #379370 |
249 |
if v is None: |
250 |
continue
|
|
3368.2.19
by Ian Clatworthy
first round of changes from abentley's review |
251 |
try: |
4257.1.1
by Ian Clatworthy
register content filters using a callable, not a dict+callable |
252 |
stack_map_lookup = _filter_stacks_registry.get(k) |
3368.2.19
by Ian Clatworthy
first round of changes from abentley's review |
253 |
except KeyError: |
3368.2.26
by Ian Clatworthy
more feedback tweaks |
254 |
# Some preferences may not have associated filters
|
3368.2.19
by Ian Clatworthy
first round of changes from abentley's review |
255 |
continue
|
4257.1.1
by Ian Clatworthy
register content filters using a callable, not a dict+callable |
256 |
items = stack_map_lookup(v) |
3368.2.19
by Ian Clatworthy
first round of changes from abentley's review |
257 |
if items: |
258 |
stack.extend(items) |
|
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
259 |
_stack_cache[preferences] = stack |
260 |
return stack |
|
261 |
||
262 |
||
263 |
def _reset_registry(value=None): |
|
264 |
"""Reset the filter stack registry.
|
|
265 |
||
266 |
This function is provided to aid testing. The expected usage is::
|
|
267 |
||
268 |
old = _reset_registry()
|
|
269 |
# run tests
|
|
270 |
_reset_registry(old)
|
|
271 |
||
272 |
:param value: the value to set the registry to or None for an empty one.
|
|
273 |
:return: the existing value before it reset.
|
|
274 |
"""
|
|
275 |
global _filter_stacks_registry |
|
3368.2.19
by Ian Clatworthy
first round of changes from abentley's review |
276 |
original = _filter_stacks_registry |
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
277 |
if value is None: |
3368.2.19
by Ian Clatworthy
first round of changes from abentley's review |
278 |
_filter_stacks_registry = registry.Registry() |
3368.2.15
by Ian Clatworthy
add filter stack registry code and tests |
279 |
else: |
280 |
_filter_stacks_registry = value |
|
281 |
_stack_cache.clear() |
|
282 |
return original |
|
4275.1.1
by Ian Clatworthy
fix eol content filter loading |
283 |
|
284 |
||
285 |
# Register the standard filters
|
|
286 |
from bzrlib.filters import eol |
|
287 |
eol.register_eol_content_filter() |