Edgewall Software
close Warning: No coverage annotation found for /trunk/bitten/build/config.py for revision range [1001:1001].

source: trunk/bitten/build/config.py @ 1001

Last change on this file since 1001 was 910, checked in by osimons, 13 years ago

Updated copyright to 2010.

  • Property svn:eol-style set to native
File size: 7.7 KB
CovLine 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
4# Copyright (C) 2007-2010 Edgewall Software
5# All rights reserved.
6#
7# This software is licensed as described in the file COPYING, which
8# you should have received as part of this distribution. The terms
9# are also available at http://bitten.edgewall.org/wiki/License.
10
11"""Support for build slave configuration."""
12
13from ConfigParser import SafeConfigParser
14import logging
15import os
16import platform
17import re
18from string import Template
19
20log = logging.getLogger('bitten.config')
21
22__docformat__ = 'restructuredtext en'
23
24class ConfigFileNotFound(Exception):
25    pass
26
27class Configuration(object):
28    """Encapsulates the configuration of a build machine.
29   
30    Configuration values can be provided through a configuration file (in INI
31    format) or through command-line parameters (properties). In addition to
32    explicitly defined properties, this class automatically collects platform
33    information and stores them as properties. These defaults can be
34    overridden (useful for cross-compilation).
35    """
36
37    def __init__(self, filename=None, properties=None):
38        """Create the configuration object.
39       
40        :param filename: the path to the configuration file, if any
41        :param properties: a dictionary of the configuration properties
42                           provided on the command-line
43        """
44        self.properties = {}
45        self.packages = {}
46        parser = SafeConfigParser()
47        if filename:
48            if not (os.path.isfile(filename) or os.path.islink(filename)):
49                raise ConfigFileNotFound(
50                            "Configuration file %r not found." % filename)
51            parser.read(filename)
52        self._merge_sysinfo(parser, properties)
53        self._merge_packages(parser, properties)
54
55    def _merge_sysinfo(self, parser, properties):
56        """Merge the platform information properties into the configuration."""
57        system, _, release, version, machine, processor = platform.uname()
58        system, release, version = platform.system_alias(system, release,
59                                                         version)
60        self.properties['machine'] = machine
61        self.properties['processor'] = processor
62        self.properties['os'] = system
63        self.properties['family'] = os.name
64        self.properties['version'] = release
65
66        mapping = {'machine': ('machine', 'name'),
67                   'processor': ('machine', 'processor'),
68                   'os': ('os', 'name'),
69                   'family': ('os', 'family'),
70                   'version': ('os', 'version')}
71        for key, (section, option) in mapping.items():
72            if parser.has_option(section, option):
73                value = parser.get(section, option)
74                if value is not None:
75                    self.properties[key] = value
76
77        if properties:
78            for key, value in properties.items():
79                if key in mapping:
80                    self.properties[key] = value
81
82    def _merge_packages(self, parser, properties):
83        """Merge package information into the configuration."""
84        for section in parser.sections():
85            if section in ('os', 'machine', 'maintainer'):
86                continue
87            package = {}
88            for option in parser.options(section):
89                if option == 'name':
90                    log.warning("Reserved configuration option 'name' used "
91                            "for package/section '%s'. Skipping." % section)
92                    continue
93                package[option] = parser.get(section, option)
94            self.packages[section] = package
95
96        if properties:
97            for key, value in properties.items():
98                if '.' in key:
99                    package, propname = key.split('.', 1)
100                    if propname == 'name':
101                        log.warning("Reserved configuration option 'name' "
102                                "used for property '%s'. Skipping." % key)
103                        continue
104                    if package not in self.packages:
105                        self.packages[package] = {}
106                    self.packages[package][propname] = value
107
108    def __contains__(self, key):
109        """Return whether the configuration contains a value for the specified
110        key.
111       
112        :param key: name of the configuration option using dotted notation
113                    (for example, "python.path")
114        """
115        if '.' in key:
116            package, propname = key.split('.', 1)
117            return propname in self.packages.get(package, {})
118        return key in self.properties
119
120    def __getitem__(self, key):
121        """Return the value for the specified configuration key.
122       
123        :param key: name of the configuration option using dotted notation
124                    (for example, "python.path")
125        """
126        if '.' in key:
127            package, propname = key.split('.', 1)
128            return self.packages.get(package, {}).get(propname)
129        return self.properties.get(key)
130
131    def __str__(self):
132        return str({'properties': self.properties, 'packages': self.packages})
133
134    def get_dirpath(self, key):
135        """Return the value of the specified configuration key, but verify that
136        the value refers to the path of an existing directory.
137       
138        If the value does not exist, or is not a directory path, return `None`.
139
140        :param key: name of the configuration option using dotted notation
141                    (for example, "ant.home")
142        """
143        dirpath = self[key]
144        if dirpath:
145            if os.path.isdir(dirpath):
146                return dirpath
147            log.warning('Invalid %s: %s is not a directory', key, dirpath)
148        return None
149
150    def get_filepath(self, key):
151        """Return the value of the specified configuration key, but verify that
152        the value refers to the path of an existing file.
153       
154        If the value does not exist, or is not a file path, return `None`.
155
156        :param key: name of the configuration option using dotted notation
157                    (for example, "python.path")
158        """
159        filepath = self[key]
160        if filepath:
161            if os.path.isfile(filepath):
162                return filepath
163            log.warning('Invalid %s: %s is not a file', key, filepath)
164        return None
165
166    _VAR_RE = re.compile(r'\$\{(?P<ref>\w[\w.]*?\w)(?:\:(?P<def>.+))?\}')
167
168    def interpolate(self, text, **vars):
169        """Interpolate configuration and environment properties into a string.
170       
171        Configuration properties can be referenced in the text using the notation
172        ``${property.name}``. A default value can be provided by appending it to
173        the property name separated by a colon, for example
174        ``${property.name:defaultvalue}``. This value will be used when there's
175        no such property in the configuration. Otherwise, if no default is
176        provided, the reference is not replaced at all.
177       
178        Environment properties substitute from environment variables, supporting
179        the common notations of ``$VAR`` and ``${VAR}``.
180
181        :param text: the string containing variable references
182        :param vars: extra variables to use for the interpolation
183        """
184        def _replace(m):
185            refname = m.group('ref')
186            if refname in self:
187                return self[refname]
188            elif refname in vars:
189                return vars[refname]
190            elif m.group('def'):
191                return m.group('def')
192            else:
193                return m.group(0)
194        return Template(self._VAR_RE.sub(_replace, text)
195                                                ).safe_substitute(os.environ)
Note: See TracBrowser for help on using the repository browser.