Call parent class tests first.
[pykickstart.git] / pykickstart / options.py
blob5e3ffd442450801ad1bf014a508de2d3c8354d0d
2 # Chris Lumens <clumens@redhat.com>
4 # Copyright 2005, 2006, 2007 Red Hat, Inc.
6 # This copyrighted material is made available to anyone wishing to use, modify,
7 # copy, or redistribute it subject to the terms and conditions of the GNU
8 # General Public License v.2. This program is distributed in the hope that it
9 # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10 # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 # See the GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License along with
14 # this program; if not, write to the Free Software Foundation, Inc., 51
15 # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16 # trademarks that are incorporated in the source code or documentation are not
17 # subject to the GNU General Public License and may only be used or replicated
18 # with the express permission of Red Hat, Inc.
20 """
21 Specialized option handling.
23 This module exports two classes:
25 KSOptionParser - A specialized subclass of OptionParser to be used
26 in BaseHandler subclasses.
28 KSOption - A specialized subclass of Option.
29 """
30 import warnings
31 from copy import copy
32 from optparse import *
34 from constants import *
35 from errors import *
36 from version import *
38 import gettext
39 _ = lambda x: gettext.ldgettext("pykickstart", x)
41 class KSOptionParser(OptionParser):
42 """A specialized subclass of optparse.OptionParser to handle extra option
43 attribute checking, work error reporting into the KickstartParseError
44 framework, and to turn off the default help.
45 """
46 def exit(self, status=0, msg=None):
47 pass
49 def error(self, msg):
50 if self.lineno != None:
51 raise KickstartParseError, formatErrorMsg(self.lineno, msg=msg)
52 else:
53 raise KickstartParseError, msg
55 def keys(self):
56 retval = []
58 for opt in self.option_list:
59 if opt not in retval:
60 retval.append(opt.dest)
62 return retval
64 def _init_parsing_state (self):
65 OptionParser._init_parsing_state(self)
66 self.option_seen = {}
68 def check_values (self, values, args):
69 def seen(self, option):
70 return self.option_seen.has_key(option)
72 def usedTooNew(self, option):
73 return option.introduced and option.introduced > self.version
75 def usedDeprecated(self, option):
76 return option.deprecated
78 def usedRemoved(self, option):
79 return option.removed and option.removed <= self.version
81 for option in filter(lambda o: isinstance(o, Option), self.option_list):
82 if option.required and not seen(self, option):
83 raise KickstartValueError, formatErrorMsg(self.lineno, _("Option %s is required") % option)
84 elif seen(self, option) and usedTooNew(self, option):
85 mapping = {"option": option, "intro": versionToString(option.introduced),
86 "version": versionToString(self.version)}
87 self.error(_("The %(option)s option was introduced in version %(intro)s, but you are using kickstart syntax version %(version)s.") % mapping)
88 elif seen(self, option) and usedRemoved(self, option):
89 mapping = {"option": option, "removed": versionToString(option.removed),
90 "version": versionToString(self.version)}
92 if option.removed == self.version:
93 self.error(_("The %(option)s option is no longer supported.") % mapping)
94 else:
95 self.error(_("The %(option)s option was removed in version %(removed)s, but you are using kickstart syntax version %(version)s.") % mapping)
96 elif seen(self, option) and usedDeprecated(self, option):
97 mapping = {"lineno": self.lineno, "option": option}
98 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)
100 return (values, args)
102 def parse_args(self, *args, **kwargs):
103 if kwargs.has_key("lineno"):
104 self.lineno = kwargs.pop("lineno")
106 return OptionParser.parse_args(self, **kwargs)
108 def __init__(self, map={}, version=None):
109 """Create a new KSOptionParser instance. Each KickstartCommand
110 subclass should create one instance of KSOptionParser, providing
111 at least the lineno attribute. map and version are not required.
112 Instance attributes:
114 map -- A mapping from option strings to different values.
115 version -- The version of the kickstart syntax we are checking
116 against.
118 OptionParser.__init__(self, option_class=KSOption,
119 add_help_option=False,
120 conflict_handler="resolve")
121 self.map = map
122 self.version = version
124 def _check_ksboolean(option, opt, value):
125 if value.lower() in ("on", "yes", "true", "1"):
126 return True
127 elif value.lower() in ("off", "no", "false", "0"):
128 return False
129 else:
130 mapping = {"opt": opt, "value": value}
131 raise OptionValueError(_("Option %(opt)s: invalid boolean value: %(value)r") % mapping)
133 def _check_string(option, opt, value):
134 if len(value) > 2 and value.startswith("--"):
135 mapping = {"opt": opt, "value": value}
136 raise OptionValueError(_("Option %(opt)s: invalid string value: %(value)r") % mapping)
137 else:
138 return value
140 # Creates a new Option class that supports several new attributes:
141 # - required: any option with this attribute must be supplied or an exception
142 # is thrown
143 # - introduced: the kickstart syntax version that this option first appeared
144 # in - an exception will be raised if the option is used and
145 # the specified syntax version is less than the value of this
146 # attribute
147 # - deprecated: the kickstart syntax version that this option was deprecated
148 # in - a DeprecationWarning will be thrown if the option is
149 # used and the specified syntax version is greater than the
150 # value of this attribute
151 # - removed: the kickstart syntax version that this option was removed in - an
152 # exception will be raised if the option is used and the specified
153 # syntax version is greated than the value of this attribute
154 # Also creates a new type:
155 # - ksboolean: support various kinds of boolean values on an option
156 # And two new actions:
157 # - map : allows you to define an opt -> val mapping such that dest gets val
158 # when opt is seen
159 # - map_extend: allows you to define an opt -> [val1, ... valn] mapping such
160 # that dest gets a list of vals built up when opt is seen
161 class KSOption (Option):
162 ATTRS = Option.ATTRS + ['introduced', 'deprecated', 'removed', 'required']
163 ACTIONS = Option.ACTIONS + ("map", "map_extend",)
164 STORE_ACTIONS = Option.STORE_ACTIONS + ("map", "map_extend",)
166 TYPES = Option.TYPES + ("ksboolean",)
167 TYPE_CHECKER = copy(Option.TYPE_CHECKER)
169 def _check_required(self):
170 if self.required and not self.takes_value():
171 raise OptionError(_("Required flag set for option that doesn't take a value"), self)
173 # Make sure _check_required() is called from the constructor!
174 CHECK_METHODS = Option.CHECK_METHODS + [_check_required]
175 TYPE_CHECKER.update({"ksboolean": _check_ksboolean, "string": _check_string})
177 def process (self, opt, value, values, parser):
178 Option.process(self, opt, value, values, parser)
179 parser.option_seen[self] = 1
181 # Override default take_action method to handle our custom actions.
182 def take_action(self, action, dest, opt, value, values, parser):
183 if action == "map":
184 values.ensure_value(dest, parser.map[opt.lstrip('-')])
185 elif action == "map_extend":
186 values.ensure_value(dest, []).extend(parser.map[opt.lstrip('-')])
187 else:
188 Option.take_action(self, action, dest, opt, value, values, parser)
190 def __init__(self, *args, **kwargs):
191 self.required = False
192 Option.__init__(self, *args, **kwargs)