| OLD | NEW |
| 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: |
| 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) |
| OLD | NEW |