~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/filters/__init__.py

  • Committer: Patch Queue Manager
  • Date: 2016-04-21 04:10:52 UTC
  • mfrom: (6616.1.1 fix-en-user-guide)
  • Revision ID: pqm@pqm.ubuntu.com-20160421041052-clcye7ns1qcl2n7w
(richard-wilbur) Ensure build of English use guide always uses English text
 even when user's locale specifies a different language. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2008, 2009, 2011 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
"""Working tree content filtering support.
 
18
 
 
19
A filter consists of a read converter, write converter pair.
 
20
The content in the working tree is called the convenience format
 
21
while the content actually stored is called the canonical format.
 
22
The read converter produces canonical content from convenience
 
23
content while the writer goes the other way.
 
24
 
 
25
Converters have the following signatures::
 
26
 
 
27
    read_converter(chunks) -> chunks
 
28
    write_converter(chunks, context) -> chunks
 
29
 
 
30
where:
 
31
 
 
32
 * chunks is an iterator over a sequence of byte strings
 
33
 
 
34
 * context is an optional ContentFilterContent object (possibly None)
 
35
   providing converters access to interesting information, e.g. the
 
36
   relative path of the file.
 
37
 
 
38
Note that context is currently only supported for write converters.
 
39
"""
 
40
 
 
41
from __future__ import absolute_import
 
42
 
 
43
from cStringIO import StringIO
 
44
from bzrlib.lazy_import import lazy_import
 
45
lazy_import(globals(), """
 
46
from bzrlib import (
 
47
    config,
 
48
    errors,
 
49
    osutils,
 
50
    registry,
 
51
    )
 
52
""")
 
53
from bzrlib.symbol_versioning import (
 
54
    deprecated_function,
 
55
    deprecated_in,
 
56
    )
 
57
 
 
58
 
 
59
class ContentFilter(object):
 
60
 
 
61
    def __init__(self, reader, writer):
 
62
        """Create a filter that converts content while reading and writing.
 
63
 
 
64
        :param reader: function for converting convenience to canonical content
 
65
        :param writer: function for converting canonical to convenience content
 
66
        """
 
67
        self.reader = reader
 
68
        self.writer = writer
 
69
 
 
70
    def __repr__(self):
 
71
        return "reader: %s, writer: %s" % (self.reader,self.writer)
 
72
 
 
73
 
 
74
class ContentFilterContext(object):
 
75
    """Object providing information that filters can use."""
 
76
 
 
77
    def __init__(self, relpath=None, tree=None, entry=None):
 
78
        """Create a context.
 
79
 
 
80
        :param relpath: the relative path or None if this context doesn't
 
81
           support that information.
 
82
        :param tree: the Tree providing this file or None if this context
 
83
           doesn't support that information.
 
84
        :param entry: the InventoryEntry object if it is already known or
 
85
           None if it should be derived if possible
 
86
        """
 
87
        self._relpath = relpath
 
88
        self._tree = tree
 
89
        self._entry = entry
 
90
        # Cached values
 
91
        self._revision_id = None
 
92
        self._revision = None
 
93
 
 
94
    def relpath(self):
 
95
        """Relative path of file to tree-root."""
 
96
        return self._relpath
 
97
 
 
98
    def source_tree(self):
 
99
        """Source Tree object."""
 
100
        return self._tree
 
101
 
 
102
    def file_id(self):
 
103
        """File-id of file."""
 
104
        if self._entry is not None:
 
105
            return self._entry.file_id
 
106
        elif self._tree is None:
 
107
            return None
 
108
        else:
 
109
            return self._tree.path2id(self._relpath)
 
110
 
 
111
    def revision_id(self):
 
112
        """Id of revision that last changed this file."""
 
113
        if self._revision_id is None:
 
114
            if self._entry is not None:
 
115
                self._revision_id = self._entry.revision
 
116
            elif self._tree is not None:
 
117
                file_id = self._tree.path2id(self._relpath)
 
118
                self._entry = self._tree.inventory[file_id]
 
119
                self._revision_id = self._entry.revision
 
120
        return self._revision_id
 
121
 
 
122
    def revision(self):
 
123
        """Revision this variation of the file was introduced in."""
 
124
        if self._revision is None:
 
125
            rev_id = self.revision_id()
 
126
            if rev_id is not None:
 
127
                repo = getattr(self._tree, '_repository', None)
 
128
                if repo is None:
 
129
                    repo = self._tree.branch.repository
 
130
                self._revision = repo.get_revision(rev_id)
 
131
        return self._revision
 
132
 
 
133
 
 
134
def filtered_input_file(f, filters):
 
135
    """Get an input file that converts external to internal content.
 
136
 
 
137
    :param f: the original input file
 
138
    :param filters: the stack of filters to apply
 
139
    :return: a file-like object
 
140
    """
 
141
    if filters:
 
142
        chunks = [f.read()]
 
143
        for filter in filters:
 
144
            if filter.reader is not None:
 
145
                chunks = filter.reader(chunks)
 
146
        return StringIO(''.join(chunks))
 
147
    else:
 
148
        return f
 
149
 
 
150
 
 
151
def filtered_output_bytes(chunks, filters, context=None):
 
152
    """Convert byte chunks from internal to external format.
 
153
 
 
154
    :param chunks: an iterator containing the original content
 
155
    :param filters: the stack of filters to apply
 
156
    :param context: a ContentFilterContext object passed to
 
157
        each filter
 
158
    :return: an iterator containing the content to output
 
159
    """
 
160
    if filters:
 
161
        for filter in reversed(filters):
 
162
            if filter.writer is not None:
 
163
                chunks = filter.writer(chunks, context)
 
164
    return chunks
 
165
 
 
166
 
 
167
def internal_size_sha_file_byname(name, filters):
 
168
    """Get size and sha of internal content given external content.
 
169
 
 
170
    :param name: path to file
 
171
    :param filters: the stack of filters to apply
 
172
    """
 
173
    f = open(name, 'rb', 65000)
 
174
    try:
 
175
        if filters:
 
176
            f = filtered_input_file(f, filters)
 
177
        return osutils.size_sha_file(f)
 
178
    finally:
 
179
        f.close()
 
180
 
 
181
 
 
182
# The registry of filter stacks indexed by name.
 
183
filter_stacks_registry = registry.Registry()
 
184
 
 
185
 
 
186
# Cache of preferences -> stack
 
187
# TODO: make this per branch (say) rather than global
 
188
_stack_cache = {}
 
189
 
 
190
 
 
191
# XXX: This function doesn't have any tests. JRV 2012-03-29
 
192
@deprecated_function(deprecated_in((2, 6, 0)))
 
193
def register_filter_stack_map(name, stack_map_lookup):
 
194
    """Register the filter stacks to use for various preference values.
 
195
 
 
196
    :param name: the preference/filter-stack name
 
197
    :param stack_map_lookup: a callable where
 
198
      the parameter is the preference value to match and
 
199
      the result is the matching stack of filters to use,
 
200
      or None if none.
 
201
    """
 
202
    filter_stacks_registry.register(name, stack_map_lookup)
 
203
 
 
204
 
 
205
# XXX: This function doesn't have any tests. JRV 2012-03-29
 
206
@deprecated_function(deprecated_in((2, 6, 0)))
 
207
def lazy_register_filter_stack_map(name, module_name, member_name):
 
208
    """Lazily register the filter stacks to use for various preference values.
 
209
 
 
210
    :param name: the preference/filter-stack name
 
211
    :param module_name: The python path to the module of the filter stack map.
 
212
    :param member_name: The name of the stack_map_lookup callable
 
213
      in the module.
 
214
    """
 
215
    filter_stacks_registry.register_lazy(name, module_name, member_name)
 
216
 
 
217
 
 
218
def _get_registered_names():
 
219
    """Get the list of names with filters registered."""
 
220
    # Note: We may want to intelligently order these later.
 
221
    # If so, the register_ fn will need to support an optional priority.
 
222
    return filter_stacks_registry.keys()
 
223
 
 
224
 
 
225
def _get_filter_stack_for(preferences):
 
226
    """Get the filter stack given a sequence of preferences.
 
227
 
 
228
    :param preferences: a sequence of (name,value) tuples where
 
229
      name is the preference name and
 
230
      value is the key into the filter stack map registered
 
231
      for that preference.
 
232
    """
 
233
    if preferences is None:
 
234
        return []
 
235
    stack = _stack_cache.get(preferences)
 
236
    if stack is not None:
 
237
        return stack
 
238
    stack = []
 
239
    for k, v in preferences:
 
240
        if v is None:
 
241
            continue
 
242
        try:
 
243
            stack_map_lookup = filter_stacks_registry.get(k)
 
244
        except KeyError:
 
245
            # Some preferences may not have associated filters
 
246
            continue
 
247
        items = stack_map_lookup(v)
 
248
        if items:
 
249
            stack.extend(items)
 
250
    _stack_cache[preferences] = stack
 
251
    return stack
 
252
 
 
253
 
 
254
def _reset_registry(value=None):
 
255
    """Reset the filter stack registry.
 
256
 
 
257
    This function is provided to aid testing. The expected usage is::
 
258
 
 
259
      old = _reset_registry()
 
260
      # run tests
 
261
      _reset_registry(old)
 
262
 
 
263
    :param value: the value to set the registry to or None for an empty one.
 
264
    :return: the existing value before it reset.
 
265
    """
 
266
    global filter_stacks_registry
 
267
    original = filter_stacks_registry
 
268
    if value is None:
 
269
        filter_stacks_registry = registry.Registry()
 
270
    else:
 
271
        filter_stacks_registry = value
 
272
    _stack_cache.clear()
 
273
    return original
 
274
 
 
275
 
 
276
filter_stacks_registry.register_lazy('eol', 'bzrlib.filters.eol', 'eol_lookup')