1# -*- Mode: Python -*- 2# pygobject - Python bindings for the GObject library 3# Copyright (C) 2006 Johannes Hoelzl 4# 5# glib/option.py: GOption command line parser 6# 7# This library is free software; you can redistribute it and/or 8# modify it under the terms of the GNU Lesser General Public 9# License as published by the Free Software Foundation; either 10# version 2.1 of the License, or (at your option) any later version. 11# 12# This library is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15# Lesser General Public License for more details. 16# 17# You should have received a copy of the GNU Lesser General Public 18# License along with this library; if not, write to the Free Software 19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 20# USA 21 22"""GOption command line parser 23 24Extends optparse to use the GOptionGroup, GOptionEntry and GOptionContext 25objects. So it is possible to use the gtk, gnome_program and gstreamer command 26line groups and contexts. 27 28Use this interface instead of the raw wrappers of GOptionContext and 29GOptionGroup in glib. 30""" 31 32import sys 33import optparse 34from optparse import OptParseError, OptionError, OptionValueError, \ 35 BadOptionError, OptionConflictError 36 37if sys.version_info >= (3, 0): 38 _basestring = str 39 _bytes = lambda s: s.encode() 40else: 41 _basestring = basestring 42 _bytes = str 43 44import glib 45_glib = sys.modules['glib._glib'] 46 47__all__ = [ 48 "OptParseError", 49 "OptionError", 50 "OptionValueError", 51 "BadOptionError", 52 "OptionConflictError", 53 "Option", 54 "OptionGroup", 55 "OptionParser", 56 "make_option", 57] 58 59class Option(optparse.Option): 60 """Represents a command line option 61 62 To use the extended possibilities of the GOption API Option 63 (and make_option) are extended with new types and attributes. 64 65 Types: 66 filename The supplied arguments are read as filename, GOption 67 parses this type in with the GLib filename encoding. 68 69 Attributes: 70 optional_arg This does not need a arguement, but it can be supplied. 71 hidden The help list does not show this option 72 in_main This option apears in the main group, this should only 73 be used for backwards compatibility. 74 75 Use Option.REMAINING as option name to get all positional arguments. 76 77 NOTE: Every argument to an option is passed as utf-8 coded string, the only 78 exception are options which use the 'filename' type, its arguments 79 are passed as strings in the GLib filename encoding. 80 81 For further help, see optparse.Option. 82 """ 83 TYPES = optparse.Option.TYPES + ( 84 'filename', 85 ) 86 87 ATTRS = optparse.Option.ATTRS + [ 88 'hidden', 89 'in_main', 90 'optional_arg', 91 ] 92 93 REMAINING = '--' + _glib.OPTION_REMAINING 94 95 def __init__(self, *args, **kwargs): 96 optparse.Option.__init__(self, *args, **kwargs) 97 if not self._long_opts: 98 raise ValueError("%s at least one long option name.") 99 100 if len(self._long_opts) < len(self._short_opts): 101 raise ValueError( 102 "%s at least more long option names than short option names.") 103 104 if not self.help: 105 raise ValueError("%s needs a help message.", self._long_opts[0]) 106 107 108 def _set_opt_string(self, opts): 109 if self.REMAINING in opts: 110 self._long_opts.append(self.REMAINING) 111 optparse.Option._set_opt_string(self, opts) 112 if len(self._short_opts) > len(self._long_opts): 113 raise OptionError("goption.Option needs more long option names " 114 "than short option names") 115 116 def _to_goptionentries(self): 117 flags = 0 118 119 if self.hidden: 120 flags |= _glib.OPTION_FLAG_HIDDEN 121 122 if self.in_main: 123 flags |= _glib.OPTION_FLAG_IN_MAIN 124 125 if self.takes_value(): 126 if self.optional_arg: 127 flags |= _glib.OPTION_FLAG_OPTIONAL_ARG 128 else: 129 flags |= _glib.OPTION_FLAG_NO_ARG 130 131 if self.type == 'filename': 132 flags |= _glib.OPTION_FLAG_FILENAME 133 134 for (long_name, short_name) in zip(self._long_opts, self._short_opts): 135 yield (long_name[2:], _bytes(short_name[1]), flags, self.help, self.metavar) 136 137 for long_name in self._long_opts[len(self._short_opts):]: 138 yield (long_name[2:], _bytes('\0'), flags, self.help, self.metavar) 139 140class OptionGroup(optparse.OptionGroup): 141 """A group of command line options. 142 143 Arguements: 144 name: The groups name, used to create the 145 --help-{name} option 146 description: Shown as title of the groups help view 147 help_description: Shown as help to the --help-{name} option 148 option_list: The options used in this group, must be option.Option() 149 defaults: A dicitionary of default values 150 translation_domain: Sets the translation domain for gettext(). 151 152 NOTE: This OptionGroup does not exactly map the optparse.OptionGroup 153 interface. There is no parser object to supply, but it is possible 154 to set default values and option_lists. Also the default values and 155 values are not shared with the OptionParser. 156 157 To pass a OptionGroup into a function which expects a GOptionGroup (e.g. 158 gnome_program_init() ). OptionGroup.get_option_group() can be used. 159 160 For further help, see optparse.OptionGroup. 161 """ 162 def __init__(self, name, description, help_description="", 163 option_list=None, defaults=None, 164 translation_domain=None): 165 optparse.OptionContainer.__init__(self, Option, 'error', description) 166 self.name = name 167 self.parser = None 168 self.help_description = help_description 169 if defaults: 170 self.defaults = defaults 171 172 self.values = None 173 174 self.translation_domain = translation_domain 175 176 if option_list: 177 for option in option_list: 178 self.add_option(option) 179 180 def _create_option_list(self): 181 self.option_list = [] 182 self._create_option_mappings() 183 184 def _to_goptiongroup(self, parser): 185 def callback(option_name, option_value, group): 186 if option_name.startswith('--'): 187 opt = self._long_opt[option_name] 188 else: 189 opt = self._short_opt[option_name] 190 191 try: 192 opt.process(option_name, option_value, self.values, parser) 193 except OptionValueError: 194 error = sys.exc_info()[1] 195 gerror = _glib.GError(str(error)) 196 gerror.domain = _glib.OPTION_ERROR 197 gerror.code = _glib.OPTION_ERROR_BAD_VALUE 198 gerror.message = str(error) 199 raise gerror 200 201 group = _glib.OptionGroup(self.name, self.description, 202 self.help_description, callback) 203 if self.translation_domain: 204 group.set_translation_domain(self.translation_domain) 205 206 entries = [] 207 for option in self.option_list: 208 entries.extend(option._to_goptionentries()) 209 210 group.add_entries(entries) 211 212 return group 213 214 def get_option_group(self, parser = None): 215 """ Returns the corresponding GOptionGroup object. 216 217 Can be used as parameter for gnome_program_init(), gtk_init(). 218 """ 219 self.set_values_to_defaults() 220 return self._to_goptiongroup(parser) 221 222 def set_values_to_defaults(self): 223 for option in self.option_list: 224 default = self.defaults.get(option.dest) 225 if isinstance(default, _basestring): 226 opt_str = option.get_opt_string() 227 self.defaults[option.dest] = option.check_value( 228 opt_str, default) 229 self.values = optparse.Values(self.defaults) 230 231class OptionParser(optparse.OptionParser): 232 """Command line parser with GOption support. 233 234 NOTE: The OptionParser interface is not the exactly the same as the 235 optparse.OptionParser interface. Especially the usage parameter 236 is only used to show the metavar of the arguements. 237 238 Attribues: 239 help_enabled: The --help, --help-all and --help-{group} 240 options are enabled (default). 241 ignore_unknown_options: Do not throw a exception when a option is not 242 knwon, the option will be in the result list. 243 244 OptionParser.add_option_group() does not only accept OptionGroup instances 245 but also glib.OptionGroup, which is returned by gtk_get_option_group(). 246 247 Only glib.option.OptionGroup and glib.option.Option instances should 248 be passed as groups and options. 249 250 For further help, see optparse.OptionParser. 251 """ 252 253 def __init__(self, *args, **kwargs): 254 if 'option_class' not in kwargs: 255 kwargs['option_class'] = Option 256 self.help_enabled = kwargs.pop('help_enabled', True) 257 self.ignore_unknown_options = kwargs.pop('ignore_unknown_options', 258 False) 259 optparse.OptionParser.__init__(self, add_help_option=False, 260 *args, **kwargs) 261 262 def set_usage(self, usage): 263 if usage is None: 264 self.usage = '' 265 elif usage.startswith("%prog"): 266 self.usage = usage[len("%prog"):] 267 else: 268 self.usage = usage 269 270 def _to_goptioncontext(self, values): 271 if self.description: 272 parameter_string = self.usage + " - " + self.description 273 else: 274 parameter_string = self.usage 275 context = _glib.OptionContext(parameter_string) 276 context.set_help_enabled(self.help_enabled) 277 context.set_ignore_unknown_options(self.ignore_unknown_options) 278 279 for option_group in self.option_groups: 280 if isinstance(option_group, _glib.OptionGroup): 281 g_group = option_group 282 else: 283 g_group = option_group.get_option_group(self) 284 context.add_group(g_group) 285 286 def callback(option_name, option_value, group): 287 if option_name.startswith('--'): 288 opt = self._long_opt[option_name] 289 else: 290 opt = self._short_opt[option_name] 291 opt.process(option_name, option_value, values, self) 292 293 main_group = _glib.OptionGroup(None, None, None, callback) 294 main_entries = [] 295 for option in self.option_list: 296 main_entries.extend(option._to_goptionentries()) 297 main_group.add_entries(main_entries) 298 context.set_main_group(main_group) 299 300 return context 301 302 def add_option_group(self, *args, **kwargs): 303 if isinstance(args[0], _basestring): 304 optparse.OptionParser.add_option_group(self, 305 OptionGroup(self, *args, **kwargs)) 306 return 307 elif len(args) == 1 and not kwargs: 308 if isinstance(args[0], OptionGroup): 309 if not args[0].parser: 310 args[0].parser = self 311 if args[0].parser is not self: 312 raise ValueError("invalid OptionGroup (wrong parser)") 313 if isinstance(args[0], _glib.OptionGroup): 314 self.option_groups.append(args[0]) 315 return 316 optparse.OptionParser.add_option_group(self, *args, **kwargs) 317 318 def _get_all_options(self): 319 options = self.option_list[:] 320 for group in self.option_groups: 321 if isinstance(group, optparse.OptionGroup): 322 options.extend(group.option_list) 323 return options 324 325 def _process_args(self, largs, rargs, values): 326 context = self._to_goptioncontext(values) 327 328 # _process_args() returns the remaining parameters in rargs. 329 # The prepended program name is used to all g_set_prgname() 330 # The program name is cut away so it doesn't appear in the result. 331 rargs[:] = context.parse([sys.argv[0]] + rargs)[1:] 332 333 def parse_args(self, args=None, values=None): 334 old_args = args or [] 335 try: 336 options, args = optparse.OptionParser.parse_args( 337 self, args, values) 338 except _glib.GError: 339 error = sys.exc_info()[1] 340 if error.domain != _glib.OPTION_ERROR: 341 raise 342 if error.code == _glib.OPTION_ERROR_BAD_VALUE: 343 raise OptionValueError(error.message) 344 elif error.code == _glib.OPTION_ERROR_UNKNOWN_OPTION: 345 raise BadOptionError(error.message) 346 elif error.code == _glib.OPTION_ERROR_FAILED: 347 raise OptParseError(error.message) 348 else: 349 raise 350 351 for group in self.option_groups: 352 for key, value in group.values.__dict__.items(): 353 options.ensure_value(key, value) 354 355 args = args[2:-len(old_args)] 356 return options, args 357 358make_option = Option 359