| Left: | ||
| Right: |
| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # coding: utf-8 | |
| 2 | |
| 3 # This file is part of the Adblock Plus build tools, | |
| 4 # Copyright (C) 2006-2012 Eyeo GmbH | |
| 5 # | |
| 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 | |
| 8 # published by the Free Software Foundation. | |
| 9 # | |
| 10 # Adblock Plus is distributed in the hope that it will be useful, | |
| 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 # GNU General Public License for more details. | |
| 14 # | |
| 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/>. | |
| 17 | |
| 18 # Note: These are the base functions common to all packagers, the actual | |
| 19 # packagers are implemented in packagerGecko and packagerChrome. | |
| 20 | |
| 21 import os, codecs, ConfigParser | |
| 22 | |
| 23 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 """ | |
| 25 This class provides essentially the same interfaces as SafeConfigParser but | |
| 26 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 | |
| 28 a config file needs to contain the following option: | |
| 29 | |
| 30 [default] | |
| 31 inherit = foo/bar.config | |
| 32 | |
| 33 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, | |
| 35 longer chains are disallowed to deal with circular references. | |
| 36 | |
| 37 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 | |
| 39 encoded as UTF-8. Also, ChainedConfigParser data is read-only and the | |
| 40 options are case-sensitive. | |
| 41 """ | |
| 42 | |
| 43 def __init__(self, path): | |
| 44 self.chain = [] | |
| 45 self.read_path(path) | |
| 46 | |
| 47 def read_path(self, path): | |
| 48 if len(self.chain) >= 5: | |
| 49 raise Exception('Too much inheritance in config files') | |
| 50 | |
| 51 config = ConfigParser.SafeConfigParser() | |
| 52 config.optionxform = str | |
| 53 handle = codecs.open(path, 'rb', encoding='utf-8') | |
| 54 config.readfp(handle) | |
| 55 handle.close() | |
| 56 self.chain.append(config) | |
| 57 | |
| 58 if config.has_section('default') and config.has_option('default', 'inherit') : | |
| 59 parts = config.get('default', 'inherit').split('/') | |
| 60 defaults_path = os.path.join(os.path.dirname(path), *parts) | |
| 61 self.read_path(defaults_path) | |
| 62 | |
| 63 def defaults(self): | |
| 64 result = {} | |
| 65 for config in reverse(self.chain): | |
| 66 for key, value in config.defaults().iteritems(): | |
| 67 result[key] = value | |
| 68 return result | |
| 69 | |
| 70 def sections(self): | |
| 71 result = set() | |
| 72 for config in self.chain: | |
| 73 for section in config.sections(): | |
| 74 result.add(section) | |
| 75 return list(result) | |
| 76 | |
| 77 def has_section(self, section): | |
| 78 for config in self.chain: | |
| 79 if config.has_section(section): | |
| 80 return True | |
| 81 return False | |
| 82 | |
| 83 def options(self, section): | |
| 84 result = set() | |
| 85 for config in self.chain: | |
| 86 if config.has_section(section): | |
| 87 for option in config.options(section): | |
| 88 result.add(option) | |
| 89 return list(result) | |
| 90 | |
| 91 def has_option(self, section, option): | |
| 92 for config in self.chain: | |
| 93 if config.has_section(section) and config.has_option(section, option): | |
| 94 return True | |
| 95 return False | |
| 96 | |
| 97 def get(self, section, option): | |
| 98 for config in self.chain: | |
| 99 if config.has_section(section) and config.has_option(section, option): | |
| 100 return config.get(section, option) | |
| 101 raise ConfigParser.NoOptionError(option, section) | |
| 102 | |
| 103 def items(self, section): | |
| 104 seen = set() | |
| 105 result = [] | |
| 106 for config in self.chain: | |
| 107 if config.has_section(section): | |
| 108 for name, value in config.items(section): | |
| 109 if name not in seen: | |
| 110 seen.add(name) | |
| 111 result.append((name, value)) | |
| 112 return result | |
| OLD | NEW |