# # Chris Lumens # # Copyright 2005, 2006, 2007 Red Hat, Inc. # # This copyrighted material is made available to anyone wishing to use, modify, # copy, or redistribute it subject to the terms and conditions of the GNU # General Public License v.2. This program is distributed in the hope that it # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., 51 # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat # trademarks that are incorporated in the source code or documentation are not # subject to the GNU General Public License and may only be used or replicated # with the express permission of Red Hat, Inc. # """ Specialized option handling. This module exports two classes: KSOptionParser - A specialized subclass of OptionParser to be used in BaseHandler subclasses. KSOption - A specialized subclass of Option. """ import warnings from copy import copy from optparse import * from constants import * from errors import * from version import * import gettext _ = lambda x: gettext.ldgettext("pykickstart", x) class KSOptionParser(OptionParser): """A specialized subclass of optparse.OptionParser to handle extra option attribute checking, work error reporting into the KickstartParseError framework, and to turn off the default help. """ def exit(self, status=0, msg=None): pass def error(self, msg): if self.lineno != None: raise KickstartParseError, formatErrorMsg(self.lineno, msg=msg) else: raise KickstartParseError, msg def keys(self): retval = [] for opt in self.option_list: if opt not in retval: retval.append(opt.dest) return retval def _init_parsing_state (self): OptionParser._init_parsing_state(self) self.option_seen = {} def check_values (self, values, args): def seen(self, option): return self.option_seen.has_key(option) def usedTooNew(self, option): return option.introduced and option.introduced > self.version def usedDeprecated(self, option): return option.deprecated def usedRemoved(self, option): return option.removed and option.removed <= self.version for option in filter(lambda o: isinstance(o, Option), self.option_list): if option.required and not seen(self, option): raise KickstartValueError, formatErrorMsg(self.lineno, _("Option %s is required") % option) elif seen(self, option) and usedTooNew(self, option): mapping = {"option": option, "intro": versionToString(option.introduced), "version": versionToString(self.version)} self.error(_("The %(option)s option was introduced in version %(intro)s, but you are using kickstart syntax version %(version)s.") % mapping) elif seen(self, option) and usedRemoved(self, option): mapping = {"option": option, "removed": versionToString(option.removed), "version": versionToString(self.version)} if option.removed == self.version: self.error(_("The %(option)s option is no longer supported.") % mapping) else: self.error(_("The %(option)s option was removed in version %(removed)s, but you are using kickstart syntax version %(version)s.") % mapping) elif seen(self, option) and usedDeprecated(self, option): mapping = {"lineno": self.lineno, "option": option} warnings.warn(_("Ignoring deprecated option on line %(lineno)s: The %(option)s option has been deprecated and no longer has any effect. It may be removed from future releases, which will result in a fatal error from kickstart. Please modify your kickstart file to remove this option.") % mapping, DeprecationWarning) return (values, args) def parse_args(self, *args, **kwargs): if kwargs.has_key("lineno"): self.lineno = kwargs.pop("lineno") return OptionParser.parse_args(self, **kwargs) def __init__(self, mapping=None, version=None): """Create a new KSOptionParser instance. Each KickstartCommand subclass should create one instance of KSOptionParser, providing at least the lineno attribute. mapping and version are not required. Instance attributes: mapping -- A mapping from option strings to different values. version -- The version of the kickstart syntax we are checking against. """ OptionParser.__init__(self, option_class=KSOption, add_help_option=False, conflict_handler="resolve") if mapping is None: self.map = {} else: self.map = mapping self.lineno = None self.option_seen = {} self.version = version def _check_ksboolean(option, opt, value): if value.lower() in ("on", "yes", "true", "1"): return True elif value.lower() in ("off", "no", "false", "0"): return False else: mapping = {"opt": opt, "value": value} raise OptionValueError(_("Option %(opt)s: invalid boolean value: %(value)r") % mapping) def _check_string(option, opt, value): if len(value) > 2 and value.startswith("--"): mapping = {"opt": opt, "value": value} raise OptionValueError(_("Option %(opt)s: invalid string value: %(value)r") % mapping) else: return value # Creates a new Option class that supports several new attributes: # - required: any option with this attribute must be supplied or an exception # is thrown # - introduced: the kickstart syntax version that this option first appeared # in - an exception will be raised if the option is used and # the specified syntax version is less than the value of this # attribute # - deprecated: the kickstart syntax version that this option was deprecated # in - a DeprecationWarning will be thrown if the option is # used and the specified syntax version is greater than the # value of this attribute # - removed: the kickstart syntax version that this option was removed in - an # exception will be raised if the option is used and the specified # syntax version is greated than the value of this attribute # Also creates a new type: # - ksboolean: support various kinds of boolean values on an option # And two new actions: # - map : allows you to define an opt -> val mapping such that dest gets val # when opt is seen # - map_extend: allows you to define an opt -> [val1, ... valn] mapping such # that dest gets a list of vals built up when opt is seen class KSOption (Option): ATTRS = Option.ATTRS + ['introduced', 'deprecated', 'removed', 'required'] ACTIONS = Option.ACTIONS + ("map", "map_extend",) STORE_ACTIONS = Option.STORE_ACTIONS + ("map", "map_extend",) TYPES = Option.TYPES + ("ksboolean", "string") TYPE_CHECKER = copy(Option.TYPE_CHECKER) TYPE_CHECKER["ksboolean"] = _check_ksboolean TYPE_CHECKER["string"] = _check_string def _check_required(self): if self.required and not self.takes_value(): raise OptionError(_("Required flag set for option that doesn't take a value"), self) # Make sure _check_required() is called from the constructor! CHECK_METHODS = Option.CHECK_METHODS + [_check_required] def process (self, opt, value, values, parser): Option.process(self, opt, value, values, parser) parser.option_seen[self] = 1 # Override default take_action method to handle our custom actions. def take_action(self, action, dest, opt, value, values, parser): if action == "map": values.ensure_value(dest, parser.map[opt.lstrip('-')]) elif action == "map_extend": values.ensure_value(dest, []).extend(parser.map[opt.lstrip('-')]) else: Option.take_action(self, action, dest, opt, value, values, parser) def takes_value(self): # Deprecated options don't take a value. return Option.takes_value(self) and not self.deprecated def __init__(self, *args, **kwargs): self.deprecated = False self.required = False Option.__init__(self, *args, **kwargs)