~ubuntu-branches/ubuntu/trusty/rubber/trusty

« back to all changes in this revision

Viewing changes to .pc/04_borken_image_conversion.diff/src/convert.py

  • Committer: Package Import Robot
  • Author(s): Hilmar Preuße
  • Date: 2014-03-27 10:50:03 UTC
  • mfrom: (3.1.6 sid)
  • Revision ID: package-import@ubuntu.com-20140327105003-kflnc7k78idia1ea
Tags: 1.1+20100306-3
* Add patch 04_borken_image_conversion.diff
  (Closes: #684731)
* Add patch 05_rubber-push_vars-bug.diff
  (Closes: #701898)
* Update to Standards-Version 3.9.5 (co changes needed).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
This module contains code for file conversion, including implicit conversion
 
3
rule management.
 
4
"""
 
5
 
 
6
import re, imp, os.path
 
7
from ConfigParser import *
 
8
 
 
9
from rubber import _, msg
 
10
import rubber.converters
 
11
from rubber.util import Variables
 
12
 
 
13
re_variable = re.compile('[a-zA-Z]+')
 
14
 
 
15
def expand_cases (string, vars):
 
16
        """
 
17
        Expand variables and cases in a template string. Variables must occur as
 
18
        $VAR (with only letters in the name) or ${VAR}, their values are taken
 
19
        from the dictionary-like object 'vars'. The "$" character can be written
 
20
        litterally by saying "$$". Cases are written in braces, separated with
 
21
        commas, as in {foo,bar,quux}. Commas at top-level also count as choice
 
22
        separators, as if there was a pair of braces around the whole string.
 
23
        Returns a pair (cases,pos) where 'cases' is the list of expansions of the
 
24
        string and 'pos' is the position of the first unbalanced "}" or the end of
 
25
        the string.
 
26
        """
 
27
        pos = 0   # current position
 
28
        start = 0   # starting point of the current chunk
 
29
        cases = []   # possible expansions from previous cases
 
30
        current = ['']   # possible values for the current case, up to 'start'
 
31
 
 
32
        while pos < len(string):
 
33
 
 
34
                # Cases
 
35
 
 
36
                if string[pos] == ',':
 
37
                        suffix = string[start:pos]
 
38
                        cases.extend([s + suffix for s in current])
 
39
                        current = ['']
 
40
                        start = pos = pos + 1
 
41
 
 
42
                elif string[pos] == '{':
 
43
                        mid = string[start:pos]
 
44
                        next, shift = expand_cases(string[pos + 1:], vars)
 
45
                        current = [left + mid + right for left in current for right in next]
 
46
                        start = pos = pos + shift + 2
 
47
 
 
48
                elif string[pos] == '}':
 
49
                        suffix = string[start:pos]
 
50
                        return cases + [s + suffix for s in current], pos
 
51
 
 
52
                # Variable expansion
 
53
 
 
54
                elif string[pos] == '$' and pos < len(string):
 
55
                        if string[pos + 1] == '{':
 
56
                                end = string.find('}', pos + 2)
 
57
                                if end < 0:
 
58
                                        end = len(string)
 
59
                                name = string[pos+2:end]
 
60
                                suffix = string[start:pos]
 
61
                                if name in vars:
 
62
                                        suffix += vars[name]
 
63
                                current = [s + suffix for s in current]
 
64
                                start = pos = end + 1
 
65
                        elif string[pos + 1] == '$':
 
66
                                suffix = string[start:pos+1]
 
67
                                current = [s + suffix for s in current]
 
68
                                start = pos = pos + 2
 
69
                        else:
 
70
                                m = re_variable.match(string, pos + 1)
 
71
                                if m:
 
72
                                        name = m.group()
 
73
                                        suffix = string[start:pos]
 
74
                                        if name in vars:
 
75
                                                suffix += vars[name]
 
76
                                        current = [s + suffix for s in current]
 
77
                                        start = pos = m.end()
 
78
                                else:
 
79
                                        pos += 1
 
80
 
 
81
                else:
 
82
                        pos += 1
 
83
 
 
84
        suffix = string[start:]
 
85
        return cases + [s + suffix for s in current], pos
 
86
 
 
87
class Rule (Variables):
 
88
        """
 
89
        This class represents a single rule, as described in rules.ini. It is
 
90
        essentially a dictionary, but also includes a compiled form of the regular
 
91
        expression for the target.
 
92
        """
 
93
        def __init__ (self, context, dict):
 
94
                Variables.__init__(self, context, dict)
 
95
                self.cost = dict['cost']
 
96
                self.re_target = re.compile(dict['target'] + '$')
 
97
 
 
98
class Converter (object):
 
99
        """
 
100
        This class represents a set of translation rules that may be used to
 
101
        produce input files. Objects contain a table of rewriting rules to deduce
 
102
        potential source names from the target's name, and each rule has a given
 
103
        cost that indicates how expensive the translation is.
 
104
 
 
105
        Each rule contains a module name. The module is searched for in the
 
106
        package rubber.converters and it is supposed to contain two functions:
 
107
 
 
108
        - check(source, target, context):
 
109
            Returns True if conversion from 'source' to 'target' is possible (i.e.
 
110
            the source file is suitable, all required tools are available, etc.).
 
111
                The 'context' object is a dictionary-like object that contains values
 
112
                from the rule and possibly additional user settings. If the function
 
113
                is absent, conversion is always assumed to be possible.
 
114
 
 
115
        - convert(source, target, context, set):
 
116
            Produce a dependency node in the given 'set' to produce 'target' from
 
117
                'source', using settings from the 'context'.
 
118
        """
 
119
        def __init__ (self, set):
 
120
                """
 
121
                Initialize the converter, associated with a given dependency set, with
 
122
                an empty set of rules.
 
123
                """
 
124
                self.set = set
 
125
                self.modules = {}
 
126
                self.rules = {}
 
127
 
 
128
        def read_ini (self, filename):
 
129
                """
 
130
                Read a set of rules from a file. The file has the form of an INI file,
 
131
                each section describes a rule.
 
132
                """
 
133
                cp = ConfigParser()
 
134
                try:
 
135
                        cp.read(filename)
 
136
                except ParsingError:
 
137
                        msg.error(_("parse error, ignoring this file"), file=filename)
 
138
                        return
 
139
                for name in cp.sections():
 
140
                        dict = { 'name': name }
 
141
                        for key in cp.options(name):
 
142
                                dict[key] = cp.get(name, key)
 
143
                        try:
 
144
                                dict['cost'] = cp.getint(name, 'cost')
 
145
                        except NoOptionError:
 
146
                                msg.warn(_("ignoring rule `%s' (no cost found)") % name,
 
147
                                                file=filename)
 
148
                                continue
 
149
                        except ValueError:
 
150
                                msg.warn(_("ignoring rule `%s' (invalid cost)") % name,
 
151
                                                file=filename)
 
152
                                continue
 
153
                        if 'target' not in dict:
 
154
                                msg.warn(_("ignoring rule `%s' (no target found)") % name,
 
155
                                                file=filename)
 
156
                                continue
 
157
                        if 'rule' not in dict:
 
158
                                msg.warn(_("ignoring rule `%s' (no module found)") % name,
 
159
                                                file=filename)
 
160
                        if not self.load_module(dict['rule']):
 
161
                                msg.warn(_("ignoring rule `%s' (module `%s' not found)") %
 
162
                                                (name, dict['rule']), file=filename)
 
163
                        self.rules[name] = Rule(None, dict)
 
164
 
 
165
        def load_module (self, name):
 
166
                """
 
167
                Check if the module of the given name exists and load it. Returns True
 
168
                if the module was loaded and False otherwise.
 
169
                """
 
170
                if name in self.modules:
 
171
                        return self.modules[name] is not None
 
172
                try:
 
173
                        answer = imp.find_module(name, rubber.converters.__path__)
 
174
                except ImportError:
 
175
                        self.modules[name] = None
 
176
                        return False
 
177
                self.modules[name] = imp.load_module(name, *answer)
 
178
                return True
 
179
 
 
180
        def may_produce (self, name):
 
181
                """
 
182
                Return true if the given filename may be that of a file generated by
 
183
                this converter, i.e. if it matches one of the target regular
 
184
                expressions.
 
185
                """
 
186
                for rule in self.rules.values():
 
187
                        if rule.re_target.match(name):
 
188
                                return True
 
189
                return False
 
190
 
 
191
        def best_rule (self, target, check=None, context=None):
 
192
                """
 
193
                Search for an applicable rule for the given target with the least
 
194
                cost. The returned value is a dictionary that describes the best rule
 
195
                found, or None if no rule is applicable. The optional argument 'check'
 
196
                is a function that takes the rule parameters as arguments (as a
 
197
                dictionary that contains at least 'source' and 'target') and can
 
198
                return false if the rule is refused. The optional argument 'context'
 
199
                is a dictionary that contains additional parameters passed to the
 
200
                converters.
 
201
                """
 
202
                candidates = []
 
203
 
 
204
                for rule in self.rules.values():
 
205
                        match = rule.re_target.match(target)
 
206
                        if not match:
 
207
                                continue
 
208
                        templates, _ = expand_cases(rule['source'], {})
 
209
                        for template in templates:
 
210
                                source = match.expand(template)
 
211
                                if source == target:
 
212
                                        continue
 
213
                                if not os.path.exists(source):
 
214
                                        continue
 
215
                                candidates.append((rule['cost'], source, target, rule))
 
216
 
 
217
                candidates.sort()
 
218
                for cost, source, target, rule in candidates:
 
219
                        instance = Variables(context, rule)
 
220
                        instance['source'] = source
 
221
                        instance['target'] = target
 
222
                        if check is not None and not check(instance):
 
223
                                continue
 
224
                        module = self.modules[rule['rule']]
 
225
                        if hasattr(module, 'check'):
 
226
                                if not module.check(source, target, instance):
 
227
                                        continue
 
228
                        return instance
 
229
 
 
230
                return None
 
231
 
 
232
        def apply (self, instance):
 
233
                """
 
234
                Apply a rule with the variables given in the dictionary passed as
 
235
                argument (as returned from the 'best_rule' method), and return a
 
236
                dependency node for the result.
 
237
                """
 
238
                module = self.modules[instance['rule']]
 
239
                return module.convert(
 
240
                                instance['source'], instance['target'], instance, self.set)