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