Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 # coding: utf-8 | 1 # coding: utf-8 |
2 | 2 |
3 # This file is part of the Adblock Plus build tools, | 3 # This file is part of the Adblock Plus build tools, |
4 # Copyright (C) 2006-2012 Eyeo GmbH | 4 # Copyright (C) 2006-2012 Eyeo GmbH |
5 # | 5 # |
6 # Adblock Plus is free software: you can redistribute it and/or modify | 6 # Adblock Plus is free software: you can redistribute it and/or modify |
7 # it under the terms of the GNU General Public License version 3 as | 7 # it under the terms of the GNU General Public License version 3 as |
8 # published by the Free Software Foundation. | 8 # published by the Free Software Foundation. |
9 # | 9 # |
10 # Adblock Plus is distributed in the hope that it will be useful, | 10 # Adblock Plus is distributed in the hope that it will be useful, |
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 # GNU General Public License for more details. | 13 # GNU General Public License for more details. |
14 # | 14 # |
15 # You should have received a copy of the GNU General Public License | 15 # You should have received a copy of the GNU General Public License |
16 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 16 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
17 | 17 |
18 # Note: These are the base functions common to all packagers, the actual | 18 # Note: These are the base functions common to all packagers, the actual |
19 # packagers are implemented in packagerGecko and packagerChrome. | 19 # packagers are implemented in packagerGecko and packagerChrome. |
20 | 20 |
21 import os, codecs, ConfigParser | 21 import os, codecs, ConfigParser |
22 | 22 |
23 class Item(tuple): | |
24 def __new__(cls, name, value, source): | |
25 result = super(Item, cls).__new__(cls, (name, value)) | |
26 result.source = source | |
27 return result | |
28 | |
23 class ChainedConfigParser: | 29 class ChainedConfigParser: |
Sebastian Noack
2013/11/05 12:19:38
I find this approach slightly overkill. I would pr
Wladimir Palant
2013/11/05 12:45:38
That was a consideration of course. However, we ne
Sebastian Noack
2013/11/05 13:13:25
Fair enough, LGTM.
| |
24 """ | 30 """ |
25 This class provides essentially the same interfaces as SafeConfigParser but | 31 This class provides essentially the same interfaces as SafeConfigParser but |
26 allows chaining configuration files so that one config file provides the | 32 allows chaining configuration files so that one config file provides the |
27 default values for the other. To specify the config file to inherit from | 33 default values for the other. To specify the config file to inherit from |
28 a config file needs to contain the following option: | 34 a config file needs to contain the following option: |
29 | 35 |
30 [default] | 36 [default] |
31 inherit = foo/bar.config | 37 inherit = foo/bar.config |
32 | 38 |
33 The value of the inherit option has to be a relative path with forward | 39 The value of the inherit option has to be a relative path with forward |
34 slashes as delimiters. Up to 5 configuration files can be chained this way, | 40 slashes as delimiters. Up to 5 configuration files can be chained this way, |
35 longer chains are disallowed to deal with circular references. | 41 longer chains are disallowed to deal with circular references. |
36 | 42 |
37 A main API difference to SafeConfigParser is the way a class instance is | 43 A main API difference to SafeConfigParser is the way a class instance is |
38 constructed: a file path has to be passed, this file is assumed to be | 44 constructed: a file path has to be passed, this file is assumed to be |
39 encoded as UTF-8. Also, ChainedConfigParser data is read-only and the | 45 encoded as UTF-8. Also, ChainedConfigParser data is read-only and the |
40 options are case-sensitive. | 46 options are case-sensitive. An additional option_source(section, option) |
47 method is provided to get the path of the configuration file defining this | |
48 option (for relative paths). Items returned by the items() function also | |
49 have a source attribute serving the same purpose. | |
41 """ | 50 """ |
42 | 51 |
43 def __init__(self, path): | 52 def __init__(self, path): |
44 self.chain = [] | 53 self.chain = [] |
45 self.read_path(path) | 54 self.read_path(path) |
46 | 55 |
47 def read_path(self, path): | 56 def read_path(self, path): |
48 if len(self.chain) >= 5: | 57 if len(self.chain) >= 5: |
49 raise Exception('Too much inheritance in config files') | 58 raise Exception('Too much inheritance in config files') |
50 | 59 |
51 config = ConfigParser.SafeConfigParser() | 60 config = ConfigParser.SafeConfigParser() |
52 config.optionxform = str | 61 config.optionxform = str |
62 config.source_path = path | |
53 handle = codecs.open(path, 'rb', encoding='utf-8') | 63 handle = codecs.open(path, 'rb', encoding='utf-8') |
54 config.readfp(handle) | 64 config.readfp(handle) |
55 handle.close() | 65 handle.close() |
56 self.chain.append(config) | 66 self.chain.append(config) |
57 | 67 |
58 if config.has_section('default') and config.has_option('default', 'inherit') : | 68 if config.has_section('default') and config.has_option('default', 'inherit') : |
59 parts = config.get('default', 'inherit').split('/') | 69 parts = config.get('default', 'inherit').split('/') |
60 defaults_path = os.path.join(os.path.dirname(path), *parts) | 70 defaults_path = os.path.join(os.path.dirname(path), *parts) |
61 self.read_path(defaults_path) | 71 self.read_path(defaults_path) |
62 | 72 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
101 raise ConfigParser.NoOptionError(option, section) | 111 raise ConfigParser.NoOptionError(option, section) |
102 | 112 |
103 def items(self, section): | 113 def items(self, section): |
104 seen = set() | 114 seen = set() |
105 result = [] | 115 result = [] |
106 for config in self.chain: | 116 for config in self.chain: |
107 if config.has_section(section): | 117 if config.has_section(section): |
108 for name, value in config.items(section): | 118 for name, value in config.items(section): |
109 if name not in seen: | 119 if name not in seen: |
110 seen.add(name) | 120 seen.add(name) |
111 result.append((name, value)) | 121 result.append(Item(name, value, config.source_path)) |
112 return result | 122 return result |
123 | |
124 def option_source(self, section, option): | |
125 for config in self.chain: | |
126 if config.has_section(section) and config.has_option(section, option): | |
127 return config.source_path | |
128 raise ConfigParser.NoOptionError(option, section) | |
LEFT | RIGHT |