OLD | NEW |
| (Empty) |
1 # Copyright (C) 2013 Google Inc. All rights reserved. | |
2 # | |
3 # Redistribution and use in source and binary forms, with or without | |
4 # modification, are permitted provided that the following conditions are | |
5 # met: | |
6 # | |
7 # * Redistributions of source code must retain the above copyright | |
8 # notice, this list of conditions and the following disclaimer. | |
9 # * Redistributions in binary form must reproduce the above | |
10 # copyright notice, this list of conditions and the following disclaimer | |
11 # in the documentation and/or other materials provided with the | |
12 # distribution. | |
13 # * Neither the name of Google Inc. nor the names of its | |
14 # contributors may be used to endorse or promote products derived from | |
15 # this software without specific prior written permission. | |
16 # | |
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | |
29 import copy | |
30 import os | |
31 | |
32 # NOTE: This has only been used to parse | |
33 # core/page/RuntimeEnabledFeatures.in and may not be capable | |
34 # of parsing other .in files correctly. | |
35 | |
36 # .in file format is: | |
37 # // comment | |
38 # name1 arg=value, arg2=value2, arg2=value3 | |
39 # | |
40 # InFile must be passed a dictionary of default values | |
41 # with which to validate arguments against known names. | |
42 # Sequence types as default values will produce sequences | |
43 # as parse results. | |
44 # Bare arguments (no '=') are treated as names with value True. | |
45 # The first field will always be labeled 'name'. | |
46 # | |
47 # InFile.load_from_files(['file.in'], {'arg': None, 'arg2': []}) | |
48 # | |
49 # Parsing produces an array of dictionaries: | |
50 # [ { 'name' : 'name1', 'arg' :' value', arg2=['value2', 'value3'] } | |
51 | |
52 def _is_comment(line): | |
53 return line.startswith("//") or line.startswith("#") | |
54 | |
55 class InFile(object): | |
56 def __init__(self, lines, defaults, valid_values=None, default_parameters=No
ne): | |
57 self.name_dictionaries = [] | |
58 self.parameters = copy.deepcopy(default_parameters if default_parameters
else {}) | |
59 self._defaults = defaults | |
60 self._valid_values = copy.deepcopy(valid_values if valid_values else {}) | |
61 self._parse(map(str.strip, lines)) | |
62 | |
63 @classmethod | |
64 def load_from_files(self, file_paths, defaults, valid_values, default_parame
ters): | |
65 lines = [] | |
66 for path in file_paths: | |
67 with open(os.path.abspath(path)) as in_file: | |
68 lines += in_file.readlines() | |
69 return InFile(lines, defaults, valid_values, default_parameters) | |
70 | |
71 def _is_sequence(self, arg): | |
72 return (not hasattr(arg, "strip") | |
73 and hasattr(arg, "__getitem__") | |
74 or hasattr(arg, "__iter__")) | |
75 | |
76 def _parse(self, lines): | |
77 parsing_parameters = True | |
78 indices = {} | |
79 for line in lines: | |
80 if _is_comment(line): | |
81 continue | |
82 if not line: | |
83 parsing_parameters = False | |
84 continue | |
85 if parsing_parameters: | |
86 self._parse_parameter(line) | |
87 else: | |
88 entry = self._parse_line(line) | |
89 name = entry['name'] | |
90 if name in indices: | |
91 entry = self._merge_entries(entry, self.name_dictionaries[in
dices[name]]) | |
92 entry['name'] = name | |
93 self.name_dictionaries[indices[name]] = entry | |
94 else: | |
95 indices[name] = len(self.name_dictionaries) | |
96 self.name_dictionaries.append(entry) | |
97 | |
98 | |
99 def _merge_entries(self, one, two): | |
100 merged = {} | |
101 for key in one: | |
102 if key not in two: | |
103 self._fatal("Expected key '%s' not found in entry: %s" % (key, t
wo)) | |
104 if one[key] and two[key]: | |
105 val_one = one[key] | |
106 val_two = two[key] | |
107 if isinstance(val_one, list) and isinstance(val_two, list): | |
108 val = val_one + val_two | |
109 elif isinstance(val_one, list): | |
110 val = val_one + [val_two] | |
111 elif isinstance(val_two, list): | |
112 val = [val_one] + val_two | |
113 else: | |
114 val = [val_one, val_two] | |
115 merged[key] = val | |
116 elif one[key]: | |
117 merged[key] = one[key] | |
118 else: | |
119 merged[key] = two[key] | |
120 return merged | |
121 | |
122 | |
123 def _parse_parameter(self, line): | |
124 if '=' in line: | |
125 name, value = line.split('=') | |
126 else: | |
127 name, value = line, True | |
128 if not name in self.parameters: | |
129 self._fatal("Unknown parameter: '%s' in line:\n%s\nKnown parameters:
%s" % (name, line, self.parameters.keys())) | |
130 self.parameters[name] = value | |
131 | |
132 def _parse_line(self, line): | |
133 args = copy.deepcopy(self._defaults) | |
134 parts = line.split(' ') | |
135 args['name'] = parts[0] | |
136 # re-join the rest of the line and split on ',' | |
137 args_list = ' '.join(parts[1:]).strip().split(',') | |
138 for arg_string in args_list: | |
139 arg_string = arg_string.strip() | |
140 if not arg_string: # Ignore empty args | |
141 continue | |
142 if '=' in arg_string: | |
143 arg_name, arg_value = arg_string.split('=') | |
144 else: | |
145 arg_name, arg_value = arg_string, True | |
146 if arg_name not in self._defaults: | |
147 self._fatal("Unknown argument: '%s' in line:\n%s\nKnown argument
s: %s" % (arg_name, line, self._defaults.keys())) | |
148 valid_values = self._valid_values.get(arg_name) | |
149 if valid_values and arg_value not in valid_values: | |
150 self._fatal("Unknown value: '%s' in line:\n%s\nKnown values: %s"
% (arg_value, line, valid_values)) | |
151 if self._is_sequence(args[arg_name]): | |
152 args[arg_name].append(arg_value) | |
153 else: | |
154 args[arg_name] = arg_value | |
155 return args | |
156 | |
157 def _fatal(self, message): | |
158 # FIXME: This should probably raise instead of exit(1) | |
159 print message | |
160 exit(1) | |
OLD | NEW |