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