1 # This program is free software; you can redistribute it and/or modify
2 # it under the terms of the GNU General Public License as published by
3 # the Free Software Foundation; either version 2 of the License, or
4 # (at your option) any later version.
6 # This program is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # GNU Library General Public License for more details.
11 # You should have received a copy of the GNU General Public License
12 # along with this program; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
14 # Copyright 2002 Duke University
15 # filched from yum - menno smits wrote this - he rocks
23 from ConfigParser
import NoSectionError
, NoOptionError
, ConfigParser
24 from ConfigParser
import ParsingError
27 CONFIG_FILE
= "/etc/certmaster/certmaster.conf"
29 class ConfigError(exceptions
.Exception):
30 def __init__(self
, value
=None):
31 exceptions
.Exception.__init
__(self
)
34 return "%s" %(self
.value
,)
39 This class handles a single Yum configuration file option. Create
40 subclasses for each type of supported configuration option.
42 Python descriptor foo (__get__ and __set__) is used to make option
43 definition easy and consise.
46 def __init__(self
, default
=None):
49 self
.default
= default
51 def _setattrname(self
):
52 '''Calculate the internal attribute name used to store option state in
53 configuration instances.
55 self
._attrname
= '__opt%d' % id(self
)
57 def __get__(self
, obj
, objtype
):
58 '''Called when the option is read (via the descriptor protocol).
60 @param obj: The configuration instance to modify.
61 @param objtype: The type of the config instance (not used).
62 @return: The parsed option value or the default value if the value
63 wasn't set in the configuration file.
68 return getattr(obj
, self
._attrname
, None)
70 def __set__(self
, obj
, value
):
71 '''Called when the option is set (via the descriptor protocol).
73 @param obj: The configuration instance to modify.
74 @param value: The value to set the option to.
77 # Only try to parse if its a string
78 if isinstance(value
, basestring
):
80 value
= self
.parse(value
)
82 # Add the field name onto the error
83 raise ValueError('Error parsing %r: %s' % (value
, str(e
)))
85 setattr(obj
, self
._attrname
, value
)
87 def setup(self
, obj
, name
):
88 '''Initialise the option for a config instance.
89 This must be called before the option can be set or retrieved.
91 @param obj: BaseConfig (or subclass) instance.
92 @param name: Name of the option.
94 setattr(obj
, self
._attrname
, copy
.copy(self
.default
))
97 '''Return a safe copy of this Option instance
104 '''Parse the string value to the Option's native value.
106 @param s: Raw string value to parse.
107 @return: Validated native value.
109 Will raise ValueError if there was a problem parsing the string.
110 Subclasses should override this.
114 def tostring(self
, value
):
115 '''Convert the Option's native value to a string value.
117 @param value: Native option value.
118 @return: String representation of input.
120 This does the opposite of the parse() method above.
121 Subclasses should override this.
126 def Inherit(option_obj
):
127 '''Clone an Option instance for the purposes of inheritance. The returned
128 instance has all the same properties as the input Option and shares items
129 such as the default value. Use this to avoid redefinition of reused
132 @param option_obj: Option instance to inherit.
133 @return: New Option instance inherited from the input.
135 new_option
= option_obj
.clone()
136 new_option
.inherit
= True
140 class ListOption(Option
):
142 def __init__(self
, default
=None):
145 super(ListOption
, self
).__init
__(default
)
148 """Converts a string from the config file to a workable list
150 Commas and spaces are used as separators for the list
152 # we need to allow for the '\n[whitespace]' continuation - easier
153 # to sub the \n with a space and then read the lines
154 s
= s
.replace('\n', ' ')
155 s
= s
.replace(',', ' ')
158 def tostring(self
, value
):
159 return '\n '.join(value
)
162 class UrlOption(Option
):
164 This option handles lists of URLs with validation of the URL scheme.
167 def __init__(self
, default
=None, schemes
=('http', 'ftp', 'file', 'https'),
169 super(UrlOption
, self
).__init
__(default
)
170 self
.schemes
= schemes
171 self
.allow_none
= allow_none
173 def parse(self
, url
):
176 # Handle the "_none_" special case
177 if url
.lower() == '_none_':
181 raise ValueError('"_none_" is not a valid value')
183 # Check that scheme is valid
184 (s
,b
,p
,q
,f
,o
) = urlparse
.urlparse(url
)
185 if s
not in self
.schemes
:
186 raise ValueError('URL must be %s not "%s"' % (self
._schemelist
(), s
))
190 def _schemelist(self
):
191 '''Return a user friendly list of the allowed schemes
193 if len(self
.schemes
) < 1:
195 elif len(self
.schemes
) == 1:
196 return self
.schemes
[0]
198 return '%s or %s' % (', '.join(self
.schemes
[:-1]), self
.schemes
[-1])
201 class UrlListOption(ListOption
):
203 Option for handling lists of URLs with validation of the URL scheme.
206 def __init__(self
, default
=None, schemes
=('http', 'ftp', 'file', 'https')):
207 super(UrlListOption
, self
).__init
__(default
)
209 # Hold a UrlOption instance to assist with parsing
210 self
._urloption
= UrlOption(schemes
=schemes
)
214 for url
in super(UrlListOption
, self
).parse(s
):
215 out
.append(self
._urloption
.parse(url
))
219 class IntOption(Option
):
223 except (ValueError, TypeError), e
:
224 raise ValueError('invalid integer value')
227 class BoolOption(Option
):
230 if s
in ('0', 'no', 'false'):
232 elif s
in ('1', 'yes', 'true'):
235 raise ValueError('invalid boolean value')
237 def tostring(self
, value
):
244 class FloatOption(Option
):
247 return float(s
.strip())
248 except (ValueError, TypeError):
249 raise ValueError('invalid float value')
252 class SelectionOption(Option
):
253 '''Handles string values where only specific values are allowed
255 def __init__(self
, default
=None, allowed
=()):
256 super(SelectionOption
, self
).__init
__(default
)
257 self
._allowed
= allowed
260 if s
not in self
._allowed
:
261 raise ValueError('"%s" is not an allowed value' % s
)
264 class BytesOption(Option
):
266 # Multipliers for unit symbols
274 """Parse a friendly bandwidth option to bytes
276 The input should be a string containing a (possibly floating point)
277 number followed by an optional single character unit. Valid units are
278 'k', 'M', 'G'. Case is ignored.
280 Valid inputs: 100, 123M, 45.6k, 12.4G, 100K, 786.3, 0
281 Invalid inputs: -10, -0.1, 45.6L, 123Mb
283 Return value will always be an integer
287 ValueError will be raised if the option couldn't be parsed.
290 raise ValueError("no value specified")
295 mult
= self
.MULTS
.get(unit
, None)
297 raise ValueError("unknown unit '%s'" % unit
)
305 raise ValueError("couldn't convert '%s' to number" % n
)
308 raise ValueError("bytes value may not be negative")
313 class ThrottleOption(BytesOption
):
316 """Get a throttle option.
318 Input may either be a percentage or a "friendly bandwidth value" as
319 accepted by the BytesOption.
321 Valid inputs: 100, 50%, 80.5%, 123M, 45.6k, 12.4G, 100K, 786.0, 0
322 Invalid inputs: 100.1%, -4%, -500
324 Return value will be a int if a bandwidth value was specified or a
325 float if a percentage was given.
327 ValueError will be raised if input couldn't be parsed.
330 raise ValueError("no value specified")
337 raise ValueError("couldn't convert '%s' to number" % n
)
339 raise ValueError("percentage is out of range")
342 return BytesOption
.parse(self
, s
)
345 class BaseConfig(object):
347 Base class for storing configuration definitions. Subclass when creating
354 for name
in self
.iterkeys():
355 option
= self
.optionobj(name
)
356 option
.setup(self
, name
)
360 out
.append('[%s]' % self
._section
)
361 for name
, value
in self
.iteritems():
362 out
.append('%s: %r' % (name
, value
))
363 return '\n'.join(out
)
365 def populate(self
, parser
, section
, parent
=None):
366 '''Set option values from a INI file section.
368 @param parser: ConfParser instance (or subclass)
369 @param section: INI file section to read use.
370 @param parent: Optional parent BaseConfig (or subclass) instance to use
371 when doing option value inheritance.
374 self
._section
= section
376 for name
in self
.iterkeys():
377 option
= self
.optionobj(name
)
380 value
= parser
.get(section
, name
)
381 except (NoSectionError
, NoOptionError
):
382 # No matching option in this section, try inheriting
383 if parent
and option
.inherit
:
384 value
= getattr(parent
, name
)
386 if value
is not None:
387 setattr(self
, name
, value
)
389 def optionobj(cls
, name
):
390 '''Return the Option instance for the given name
392 obj
= getattr(cls
, name
, None)
393 if isinstance(obj
, Option
):
397 optionobj
= classmethod(optionobj
)
399 def isoption(cls
, name
):
400 '''Return True if the given name refers to a defined option
407 isoption
= classmethod(isoption
)
410 '''Yield the names of all defined options in the instance.
412 for name
, item
in self
.iteritems():
416 '''Yield (name, value) pairs for every option in the instance.
418 The value returned is the parsed, validated option value.
420 # Use dir() so that we see inherited options too
421 for name
in dir(self
):
422 if self
.isoption(name
):
423 yield (name
, getattr(self
, name
))
425 def write(self
, fileobj
, section
=None, always
=()):
426 '''Write out the configuration to a file-like object
428 @param fileobj: File-like object to write to
429 @param section: Section name to use. If not-specified the section name
430 used during parsing will be used.
431 @param always: A sequence of option names to always write out.
432 Options not listed here will only be written out if they are at
433 non-default values. Set to None to dump out all options.
435 # Write section heading
437 if self
._section
is None:
438 raise ValueError("not populated, don't know section")
439 section
= self
._section
441 # Updated the ConfigParser with the changed values
442 cfgOptions
= self
.cfg
.options(section
)
443 for name
,value
in self
.iteritems():
444 option
= self
.optionobj(name
)
445 if always
is None or name
in always
or option
.default
!= value
or name
in cfgOptions
:
446 self
.cfg
.set(section
,name
, option
.tostring(value
))
447 # write the updated ConfigParser to the fileobj.
448 self
.cfg
.write(fileobj
)
450 def getConfigOption(self
, option
, default
=None):
451 warnings
.warn('getConfigOption() will go away in a future version of Yum.\n'
452 'Please access option values as attributes or using getattr().',
454 if hasattr(self
, option
):
455 return getattr(self
, option
)
458 def setConfigOption(self
, option
, value
):
459 warnings
.warn('setConfigOption() will go away in a future version of Yum.\n'
460 'Please set option values as attributes or using setattr().',
462 if hasattr(self
, option
):
463 setattr(self
, option
, value
)
465 raise ConfigError
, 'No such option %s' % option
468 def read_config(config_file
, BaseConfigDerived
):
469 confparser
= ConfigParser()
470 opts
= BaseConfigDerived()
471 if os
.path
.exists(config_file
):
473 confparser
.read(config_file
)
474 except ParsingError
, e
:
475 print >> sys
.stderr
, "Error reading config file: %s" % e
477 opts
.populate(confparser
, 'main')