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