gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / pylint / config.py @ 1026
History | View | Annotate | Download (29.5 KB)
1 | 745 | jjdelcerro | # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
|
---|---|---|---|
2 | # This program is free software; you can redistribute it and/or modify it under
|
||
3 | # the terms of the GNU General Public License as published by the Free Software
|
||
4 | # Foundation; either version 2 of the License, or (at your option) any later
|
||
5 | # version.
|
||
6 | #
|
||
7 | # This program is distributed in the hope that it will be useful, but WITHOUT
|
||
8 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||
9 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
|
||
10 | #
|
||
11 | # You should have received a copy of the GNU General Public License along with
|
||
12 | # this program; if not, write to the Free Software Foundation, Inc.,
|
||
13 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
14 | """utilities for Pylint configuration :
|
||
15 |
|
||
16 | * pylintrc
|
||
17 | * pylint.d (PYLINTHOME)
|
||
18 | """
|
||
19 | from __future__ import print_function |
||
20 | |||
21 | # TODO(cpopa): this module contains the logic for the
|
||
22 | # configuration parser and for the command line parser,
|
||
23 | # but it's really coupled to optparse's internals.
|
||
24 | # The code was copied almost verbatim from logilab.common,
|
||
25 | # in order to not depend on it anymore and it will definitely
|
||
26 | # need a cleanup. It could be completely reengineered as well.
|
||
27 | |||
28 | import contextlib |
||
29 | import copy |
||
30 | import optparse |
||
31 | import os |
||
32 | import pickle |
||
33 | import re |
||
34 | import sys |
||
35 | import time |
||
36 | |||
37 | from six.moves import configparser |
||
38 | from six.moves import range |
||
39 | |||
40 | from pylint import utils |
||
41 | |||
42 | |||
43 | USER_HOME = os.path.expanduser('~')
|
||
44 | if 'PYLINTHOME' in os.environ: |
||
45 | PYLINT_HOME = os.environ['PYLINTHOME']
|
||
46 | if USER_HOME == '~': |
||
47 | USER_HOME = os.path.dirname(PYLINT_HOME) |
||
48 | elif USER_HOME == '~': |
||
49 | PYLINT_HOME = ".pylint.d"
|
||
50 | else:
|
||
51 | PYLINT_HOME = os.path.join(USER_HOME, '.pylint.d')
|
||
52 | |||
53 | |||
54 | def _get_pdata_path(base_name, recurs): |
||
55 | base_name = base_name.replace(os.sep, '_')
|
||
56 | return os.path.join(PYLINT_HOME, "%s%s%s"%(base_name, recurs, '.stats')) |
||
57 | |||
58 | |||
59 | def load_results(base): |
||
60 | data_file = _get_pdata_path(base, 1)
|
||
61 | try:
|
||
62 | with open(data_file, _PICK_LOAD) as stream: |
||
63 | return pickle.load(stream)
|
||
64 | except Exception: # pylint: disable=broad-except |
||
65 | return {}
|
||
66 | |||
67 | if sys.version_info < (3, 0): |
||
68 | _PICK_DUMP, _PICK_LOAD = 'w', 'r' |
||
69 | else:
|
||
70 | _PICK_DUMP, _PICK_LOAD = 'wb', 'rb' |
||
71 | |||
72 | def save_results(results, base): |
||
73 | if not os.path.exists(PYLINT_HOME): |
||
74 | try:
|
||
75 | os.mkdir(PYLINT_HOME) |
||
76 | except OSError: |
||
77 | print('Unable to create directory %s' % PYLINT_HOME, file=sys.stderr)
|
||
78 | data_file = _get_pdata_path(base, 1)
|
||
79 | try:
|
||
80 | with open(data_file, _PICK_DUMP) as stream: |
||
81 | pickle.dump(results, stream) |
||
82 | except (IOError, OSError) as ex: |
||
83 | print('Unable to create file %s: %s' % (data_file, ex), file=sys.stderr)
|
||
84 | |||
85 | |||
86 | def find_pylintrc(): |
||
87 | """search the pylint rc file and return its path if it find it, else None
|
||
88 | """
|
||
89 | # is there a pylint rc file in the current directory ?
|
||
90 | if os.path.exists('pylintrc'): |
||
91 | return os.path.abspath('pylintrc') |
||
92 | if os.path.exists('.pylintrc'): |
||
93 | return os.path.abspath('.pylintrc') |
||
94 | if os.path.isfile('__init__.py'): |
||
95 | curdir = os.path.abspath(os.getcwd()) |
||
96 | while os.path.isfile(os.path.join(curdir, '__init__.py')): |
||
97 | curdir = os.path.abspath(os.path.join(curdir, '..'))
|
||
98 | if os.path.isfile(os.path.join(curdir, 'pylintrc')): |
||
99 | return os.path.join(curdir, 'pylintrc') |
||
100 | if os.path.isfile(os.path.join(curdir, '.pylintrc')): |
||
101 | return os.path.join(curdir, '.pylintrc') |
||
102 | if 'PYLINTRC' in os.environ and os.path.exists(os.environ['PYLINTRC']): |
||
103 | pylintrc = os.environ['PYLINTRC']
|
||
104 | else:
|
||
105 | user_home = os.path.expanduser('~')
|
||
106 | if user_home == '~' or user_home == '/root': |
||
107 | pylintrc = ".pylintrc"
|
||
108 | else:
|
||
109 | pylintrc = os.path.join(user_home, '.pylintrc')
|
||
110 | if not os.path.isfile(pylintrc): |
||
111 | pylintrc = os.path.join(user_home, '.config', 'pylintrc') |
||
112 | if not os.path.isfile(pylintrc): |
||
113 | if os.path.isfile('/etc/pylintrc'): |
||
114 | pylintrc = '/etc/pylintrc'
|
||
115 | else:
|
||
116 | pylintrc = None
|
||
117 | return pylintrc
|
||
118 | |||
119 | PYLINTRC = find_pylintrc() |
||
120 | |||
121 | ENV_HELP = '''
|
||
122 | The following environment variables are used:
|
||
123 | * PYLINTHOME
|
||
124 | Path to the directory where the persistent for the run will be stored. If
|
||
125 | not found, it defaults to ~/.pylint.d/ or .pylint.d (in the current working
|
||
126 | directory).
|
||
127 | * PYLINTRC
|
||
128 | Path to the configuration file. See the documentation for the method used
|
||
129 | to search for configuration file.
|
||
130 | ''' % globals() |
||
131 | |||
132 | |||
133 | class UnsupportedAction(Exception): |
||
134 | """raised by set_option when it doesn't know what to do for an action"""
|
||
135 | |||
136 | |||
137 | def _choice_validator(optdict, name, value): |
||
138 | if value not in optdict['choices']: |
||
139 | msg = "option %s: invalid value: %r, should be in %s"
|
||
140 | raise optparse.OptionValueError(msg % (name, value, optdict['choices'])) |
||
141 | return value
|
||
142 | |||
143 | |||
144 | def _multiple_choice_validator(optdict, name, value): |
||
145 | choices = optdict['choices']
|
||
146 | values = utils._check_csv(value) |
||
147 | for value in values: |
||
148 | if value not in choices: |
||
149 | msg = "option %s: invalid value: %r, should be in %s"
|
||
150 | raise optparse.OptionValueError(msg % (name, value, choices))
|
||
151 | return values
|
||
152 | |||
153 | |||
154 | # pylint: disable=unused-argument
|
||
155 | def _csv_validator(_, name, value): |
||
156 | return utils._check_csv(value)
|
||
157 | |||
158 | |||
159 | # pylint: disable=unused-argument
|
||
160 | def _regexp_validator(_, name, value): |
||
161 | if hasattr(value, 'pattern'): |
||
162 | return value
|
||
163 | return re.compile(value)
|
||
164 | |||
165 | |||
166 | def _yn_validator(opt, _, value): |
||
167 | if isinstance(value, int): |
||
168 | return bool(value) |
||
169 | if value in ('y', 'yes'): |
||
170 | return True |
||
171 | if value in ('n', 'no'): |
||
172 | return False |
||
173 | msg = "option %s: invalid yn value %r, should be in (y, yes, n, no)"
|
||
174 | raise optparse.OptionValueError(msg % (opt, value))
|
||
175 | |||
176 | |||
177 | VALIDATORS = { |
||
178 | 'string': utils._unquote,
|
||
179 | 'int': int, |
||
180 | 'regexp': re.compile,
|
||
181 | 'csv': _csv_validator,
|
||
182 | 'yn': _yn_validator,
|
||
183 | 'choice': _choice_validator,
|
||
184 | 'multiple_choice': _multiple_choice_validator,
|
||
185 | } |
||
186 | |||
187 | def _call_validator(opttype, optdict, option, value): |
||
188 | if opttype not in VALIDATORS: |
||
189 | raise Exception('Unsupported type "%s"' % opttype) |
||
190 | try:
|
||
191 | return VALIDATORS[opttype](optdict, option, value)
|
||
192 | except TypeError: |
||
193 | try:
|
||
194 | return VALIDATORS[opttype](value)
|
||
195 | except Exception: |
||
196 | raise optparse.OptionValueError('%s value (%r) should be of type %s' % |
||
197 | (option, value, opttype)) |
||
198 | |||
199 | |||
200 | def _validate(value, optdict, name=''): |
||
201 | """return a validated value for an option according to its type
|
||
202 |
|
||
203 | optional argument name is only used for error message formatting
|
||
204 | """
|
||
205 | try:
|
||
206 | _type = optdict['type']
|
||
207 | except KeyError: |
||
208 | # FIXME
|
||
209 | return value
|
||
210 | return _call_validator(_type, optdict, name, value)
|
||
211 | |||
212 | |||
213 | def _level_options(group, outputlevel): |
||
214 | return [option for option in group.option_list |
||
215 | if (getattr(option, 'level', 0) or 0) <= outputlevel |
||
216 | and option.help is not optparse.SUPPRESS_HELP] |
||
217 | |||
218 | |||
219 | def _expand_default(self, option): |
||
220 | """Patch OptionParser.expand_default with custom behaviour
|
||
221 |
|
||
222 | This will handle defaults to avoid overriding values in the
|
||
223 | configuration file.
|
||
224 | """
|
||
225 | if self.parser is None or not self.default_tag: |
||
226 | return option.help
|
||
227 | optname = option._long_opts[0][2:] |
||
228 | try:
|
||
229 | provider = self.parser.options_manager._all_options[optname]
|
||
230 | except KeyError: |
||
231 | value = None
|
||
232 | else:
|
||
233 | optdict = provider.get_option_def(optname) |
||
234 | optname = provider.option_attrname(optname, optdict) |
||
235 | value = getattr(provider.config, optname, optdict)
|
||
236 | value = utils._format_option_value(optdict, value) |
||
237 | if value is optparse.NO_DEFAULT or not value: |
||
238 | value = self.NO_DEFAULT_VALUE
|
||
239 | return option.help.replace(self.default_tag, str(value)) |
||
240 | |||
241 | |||
242 | @contextlib.contextmanager
|
||
243 | def _patch_optparse(): |
||
244 | orig_default = optparse.HelpFormatter |
||
245 | try:
|
||
246 | optparse.HelpFormatter.expand_default = _expand_default |
||
247 | yield
|
||
248 | finally:
|
||
249 | optparse.HelpFormatter.expand_default = orig_default |
||
250 | |||
251 | |||
252 | class Option(optparse.Option): |
||
253 | TYPES = optparse.Option.TYPES + ('regexp', 'csv', 'yn', 'multiple_choice') |
||
254 | ATTRS = optparse.Option.ATTRS + ['hide', 'level'] |
||
255 | TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER) |
||
256 | TYPE_CHECKER['regexp'] = _regexp_validator
|
||
257 | TYPE_CHECKER['csv'] = _csv_validator
|
||
258 | TYPE_CHECKER['yn'] = _yn_validator
|
||
259 | TYPE_CHECKER['multiple_choice'] = _multiple_choice_validator
|
||
260 | |||
261 | def __init__(self, *opts, **attrs): |
||
262 | optparse.Option.__init__(self, *opts, **attrs)
|
||
263 | if hasattr(self, "hide") and self.hide: |
||
264 | self.help = optparse.SUPPRESS_HELP
|
||
265 | |||
266 | def _check_choice(self): |
||
267 | if self.type in ("choice", "multiple_choice"): |
||
268 | if self.choices is None: |
||
269 | raise optparse.OptionError(
|
||
270 | "must supply a list of choices for type 'choice'", self) |
||
271 | elif not isinstance(self.choices, (tuple, list)): |
||
272 | raise optparse.OptionError(
|
||
273 | "choices must be a list of strings ('%s' supplied)"
|
||
274 | % str(type(self.choices)).split("'")[1], self) |
||
275 | elif self.choices is not None: |
||
276 | raise optparse.OptionError(
|
||
277 | "must not supply choices for type %r" % self.type, self) |
||
278 | optparse.Option.CHECK_METHODS[2] = _check_choice
|
||
279 | |||
280 | def process(self, opt, value, values, parser): |
||
281 | # First, convert the value(s) to the right type. Howl if any
|
||
282 | # value(s) are bogus.
|
||
283 | value = self.convert_value(opt, value)
|
||
284 | if self.type == 'named': |
||
285 | existant = getattr(values, self.dest) |
||
286 | if existant:
|
||
287 | existant.update(value) |
||
288 | value = existant |
||
289 | # And then take whatever action is expected of us.
|
||
290 | # This is a separate method to make life easier for
|
||
291 | # subclasses to add new actions.
|
||
292 | return self.take_action( |
||
293 | self.action, self.dest, opt, value, values, parser) |
||
294 | |||
295 | |||
296 | class OptionParser(optparse.OptionParser): |
||
297 | |||
298 | def __init__(self, option_class=Option, *args, **kwargs): |
||
299 | optparse.OptionParser.__init__(self, option_class=Option, *args, **kwargs)
|
||
300 | |||
301 | def format_option_help(self, formatter=None): |
||
302 | if formatter is None: |
||
303 | formatter = self.formatter
|
||
304 | outputlevel = getattr(formatter, 'output_level', 0) |
||
305 | formatter.store_option_strings(self)
|
||
306 | result = [] |
||
307 | result.append(formatter.format_heading("Options"))
|
||
308 | formatter.indent() |
||
309 | if self.option_list: |
||
310 | result.append(optparse.OptionContainer.format_option_help(self, formatter))
|
||
311 | result.append("\n")
|
||
312 | for group in self.option_groups: |
||
313 | if group.level <= outputlevel and ( |
||
314 | group.description or _level_options(group, outputlevel)):
|
||
315 | result.append(group.format_help(formatter)) |
||
316 | result.append("\n")
|
||
317 | formatter.dedent() |
||
318 | # Drop the last "\n", or the header if no options or option groups:
|
||
319 | return "".join(result[:-1]) |
||
320 | |||
321 | def _match_long_opt(self, opt): |
||
322 | """Disable abbreviations."""
|
||
323 | if opt not in self._long_opt: |
||
324 | raise optparse.BadOptionError(opt)
|
||
325 | return opt
|
||
326 | |||
327 | |||
328 | # pylint: disable=abstract-method; by design?
|
||
329 | class _ManHelpFormatter(optparse.HelpFormatter): |
||
330 | |||
331 | def __init__(self, indent_increment=0, max_help_position=24, |
||
332 | width=79, short_first=0): |
||
333 | optparse.HelpFormatter.__init__( |
||
334 | self, indent_increment, max_help_position, width, short_first)
|
||
335 | |||
336 | def format_heading(self, heading): |
||
337 | return '.SH %s\n' % heading.upper() |
||
338 | |||
339 | def format_description(self, description): |
||
340 | return description
|
||
341 | |||
342 | def format_option(self, option): |
||
343 | try:
|
||
344 | optstring = option.option_strings |
||
345 | except AttributeError: |
||
346 | optstring = self.format_option_strings(option)
|
||
347 | if option.help:
|
||
348 | help_text = self.expand_default(option)
|
||
349 | help = ' '.join([l.strip() for l in help_text.splitlines()]) |
||
350 | else:
|
||
351 | help = ''
|
||
352 | return '''.IP "%s" |
||
353 | %s
|
||
354 | ''' % (optstring, help)
|
||
355 | |||
356 | def format_head(self, optparser, pkginfo, section=1): |
||
357 | long_desc = ""
|
||
358 | try:
|
||
359 | pgm = optparser._get_prog_name() |
||
360 | except AttributeError: |
||
361 | # py >= 2.4.X (dunno which X exactly, at least 2)
|
||
362 | pgm = optparser.get_prog_name() |
||
363 | short_desc = self.format_short_description(pgm, pkginfo.description)
|
||
364 | if hasattr(pkginfo, "long_desc"): |
||
365 | long_desc = self.format_long_description(pgm, pkginfo.long_desc)
|
||
366 | return '%s\n%s\n%s\n%s' % (self.format_title(pgm, section), |
||
367 | short_desc, self.format_synopsis(pgm),
|
||
368 | long_desc) |
||
369 | |||
370 | @staticmethod
|
||
371 | def format_title(pgm, section): |
||
372 | date = '-'.join(str(num) for num in time.localtime()[:3]) |
||
373 | return '.TH %s %s "%s" %s' % (pgm, section, date, pgm) |
||
374 | |||
375 | @staticmethod
|
||
376 | def format_short_description(pgm, short_desc): |
||
377 | return '''.SH NAME |
||
378 | .B %s
|
||
379 | \- %s
|
||
380 | ''' % (pgm, short_desc.strip())
|
||
381 | |||
382 | @staticmethod
|
||
383 | def format_synopsis(pgm): |
||
384 | return '''.SH SYNOPSIS |
||
385 | .B %s
|
||
386 | [
|
||
387 | .I OPTIONS
|
||
388 | ] [
|
||
389 | .I <arguments>
|
||
390 | ]
|
||
391 | ''' % pgm
|
||
392 | |||
393 | @staticmethod
|
||
394 | def format_long_description(pgm, long_desc): |
||
395 | long_desc = '\n'.join(line.lstrip()
|
||
396 | for line in long_desc.splitlines()) |
||
397 | long_desc = long_desc.replace('\n.\n', '\n\n') |
||
398 | if long_desc.lower().startswith(pgm):
|
||
399 | long_desc = long_desc[len(pgm):]
|
||
400 | return '''.SH DESCRIPTION |
||
401 | .B %s
|
||
402 | %s
|
||
403 | ''' % (pgm, long_desc.strip())
|
||
404 | |||
405 | @staticmethod
|
||
406 | def format_tail(pkginfo): |
||
407 | tail = '''.SH SEE ALSO
|
||
408 | /usr/share/doc/pythonX.Y-%s/
|
||
409 |
|
||
410 | .SH BUGS
|
||
411 | Please report bugs on the project\'s mailing list:
|
||
412 | %s
|
||
413 |
|
||
414 | .SH AUTHOR
|
||
415 | %s <%s>
|
||
416 | ''' % (getattr(pkginfo, 'debian_name', pkginfo.modname), |
||
417 | pkginfo.mailinglist, pkginfo.author, pkginfo.author_email) |
||
418 | |||
419 | if hasattr(pkginfo, "copyright"): |
||
420 | tail += '''
|
||
421 | .SH COPYRIGHT
|
||
422 | %s
|
||
423 | ''' % pkginfo.copyright
|
||
424 | |||
425 | return tail
|
||
426 | |||
427 | |||
428 | class OptionsManagerMixIn(object): |
||
429 | """Handle configuration from both a configuration file and command line options"""
|
||
430 | |||
431 | def __init__(self, usage, config_file=None, version=None, quiet=0): |
||
432 | self.config_file = config_file
|
||
433 | self.reset_parsers(usage, version=version)
|
||
434 | # list of registered options providers
|
||
435 | self.options_providers = []
|
||
436 | # dictionary associating option name to checker
|
||
437 | self._all_options = {}
|
||
438 | self._short_options = {}
|
||
439 | self._nocallback_options = {}
|
||
440 | self._mygroups = {}
|
||
441 | # verbosity
|
||
442 | self.quiet = quiet
|
||
443 | self._maxlevel = 0 |
||
444 | |||
445 | def reset_parsers(self, usage='', version=None): |
||
446 | # configuration file parser
|
||
447 | self.cfgfile_parser = configparser.ConfigParser()
|
||
448 | # command line parser
|
||
449 | self.cmdline_parser = OptionParser(usage=usage, version=version)
|
||
450 | self.cmdline_parser.options_manager = self |
||
451 | self._optik_option_attrs = set(self.cmdline_parser.option_class.ATTRS) |
||
452 | |||
453 | def register_options_provider(self, provider, own_group=True): |
||
454 | """register an options provider"""
|
||
455 | assert provider.priority <= 0, "provider's priority can't be >= 0" |
||
456 | for i in range(len(self.options_providers)): |
||
457 | if provider.priority > self.options_providers[i].priority: |
||
458 | self.options_providers.insert(i, provider)
|
||
459 | break
|
||
460 | else:
|
||
461 | self.options_providers.append(provider)
|
||
462 | non_group_spec_options = [option for option in provider.options |
||
463 | if 'group' not in option[1]] |
||
464 | groups = getattr(provider, 'option_groups', ()) |
||
465 | if own_group and non_group_spec_options: |
||
466 | self.add_option_group(provider.name.upper(), provider.__doc__,
|
||
467 | non_group_spec_options, provider) |
||
468 | else:
|
||
469 | for opt, optdict in non_group_spec_options: |
||
470 | self.add_optik_option(provider, self.cmdline_parser, opt, optdict) |
||
471 | for gname, gdoc in groups: |
||
472 | gname = gname.upper() |
||
473 | goptions = [option for option in provider.options |
||
474 | if option[1].get('group', '').upper() == gname] |
||
475 | self.add_option_group(gname, gdoc, goptions, provider)
|
||
476 | |||
477 | def add_option_group(self, group_name, _, options, provider): |
||
478 | # add option group to the command line parser
|
||
479 | if group_name in self._mygroups: |
||
480 | group = self._mygroups[group_name]
|
||
481 | else:
|
||
482 | group = optparse.OptionGroup(self.cmdline_parser,
|
||
483 | title=group_name.capitalize()) |
||
484 | self.cmdline_parser.add_option_group(group)
|
||
485 | group.level = provider.level |
||
486 | self._mygroups[group_name] = group
|
||
487 | # add section to the config file
|
||
488 | if group_name != "DEFAULT": |
||
489 | self.cfgfile_parser.add_section(group_name)
|
||
490 | # add provider's specific options
|
||
491 | for opt, optdict in options: |
||
492 | self.add_optik_option(provider, group, opt, optdict)
|
||
493 | |||
494 | def add_optik_option(self, provider, optikcontainer, opt, optdict): |
||
495 | args, optdict = self.optik_option(provider, opt, optdict)
|
||
496 | option = optikcontainer.add_option(*args, **optdict) |
||
497 | self._all_options[opt] = provider
|
||
498 | self._maxlevel = max(self._maxlevel, option.level or 0) |
||
499 | |||
500 | def optik_option(self, provider, opt, optdict): |
||
501 | """get our personal option definition and return a suitable form for
|
||
502 | use with optik/optparse
|
||
503 | """
|
||
504 | optdict = copy.copy(optdict) |
||
505 | if 'action' in optdict: |
||
506 | self._nocallback_options[provider] = opt
|
||
507 | else:
|
||
508 | optdict['action'] = 'callback' |
||
509 | optdict['callback'] = self.cb_set_provider_option |
||
510 | # default is handled here and *must not* be given to optik if you
|
||
511 | # want the whole machinery to work
|
||
512 | if 'default' in optdict: |
||
513 | if ('help' in optdict |
||
514 | and optdict.get('default') is not None |
||
515 | and optdict['action'] not in ('store_true', 'store_false')): |
||
516 | optdict['help'] += ' [current: %default]' |
||
517 | del optdict['default'] |
||
518 | args = ['--' + str(opt)] |
||
519 | if 'short' in optdict: |
||
520 | self._short_options[optdict['short']] = opt |
||
521 | args.append('-' + optdict['short']) |
||
522 | del optdict['short'] |
||
523 | # cleanup option definition dict before giving it to optik
|
||
524 | for key in list(optdict.keys()): |
||
525 | if key not in self._optik_option_attrs: |
||
526 | optdict.pop(key) |
||
527 | return args, optdict
|
||
528 | |||
529 | def cb_set_provider_option(self, option, opt, value, parser): |
||
530 | """optik callback for option setting"""
|
||
531 | if opt.startswith('--'): |
||
532 | # remove -- on long option
|
||
533 | opt = opt[2:]
|
||
534 | else:
|
||
535 | # short option, get its long equivalent
|
||
536 | opt = self._short_options[opt[1:]] |
||
537 | # trick since we can't set action='store_true' on options
|
||
538 | if value is None: |
||
539 | value = 1
|
||
540 | self.global_set_option(opt, value)
|
||
541 | |||
542 | def global_set_option(self, opt, value): |
||
543 | """set option on the correct option provider"""
|
||
544 | self._all_options[opt].set_option(opt, value)
|
||
545 | |||
546 | def generate_config(self, stream=None, skipsections=(), encoding=None): |
||
547 | """write a configuration file according to the current configuration
|
||
548 | into the given stream or stdout
|
||
549 | """
|
||
550 | options_by_section = {} |
||
551 | sections = [] |
||
552 | for provider in self.options_providers: |
||
553 | for section, options in provider.options_by_section(): |
||
554 | if section is None: |
||
555 | section = provider.name |
||
556 | if section in skipsections: |
||
557 | continue
|
||
558 | options = [(n, d, v) for (n, d, v) in options |
||
559 | if d.get('type') is not None |
||
560 | and not d.get('deprecated')] |
||
561 | if not options: |
||
562 | continue
|
||
563 | if section not in sections: |
||
564 | sections.append(section) |
||
565 | alloptions = options_by_section.setdefault(section, []) |
||
566 | alloptions += options |
||
567 | stream = stream or sys.stdout
|
||
568 | encoding = utils._get_encoding(encoding, stream) |
||
569 | printed = False
|
||
570 | for section in sections: |
||
571 | if printed:
|
||
572 | print('\n', file=stream)
|
||
573 | utils.format_section(stream, section.upper(), |
||
574 | options_by_section[section], |
||
575 | encoding) |
||
576 | printed = True
|
||
577 | |||
578 | def generate_manpage(self, pkginfo, section=1, stream=None): |
||
579 | with _patch_optparse():
|
||
580 | _generate_manpage(self.cmdline_parser, pkginfo,
|
||
581 | section, stream=stream or sys.stdout,
|
||
582 | level=self._maxlevel)
|
||
583 | |||
584 | def load_provider_defaults(self): |
||
585 | """initialize configuration using default values"""
|
||
586 | for provider in self.options_providers: |
||
587 | provider.load_defaults() |
||
588 | |||
589 | def read_config_file(self, config_file=None): |
||
590 | """read the configuration file but do not load it (i.e. dispatching
|
||
591 | values to each options provider)
|
||
592 | """
|
||
593 | helplevel = 1
|
||
594 | while helplevel <= self._maxlevel: |
||
595 | opt = '-'.join(['long'] * helplevel) + '-help' |
||
596 | if opt in self._all_options: |
||
597 | break # already processed |
||
598 | # pylint: disable=unused-argument
|
||
599 | def helpfunc(option, opt, val, p, level=helplevel): |
||
600 | print(self.help(level))
|
||
601 | sys.exit(0)
|
||
602 | helpmsg = '%s verbose help.' % ' '.join(['more'] * helplevel) |
||
603 | optdict = {'action': 'callback', 'callback': helpfunc, |
||
604 | 'help': helpmsg}
|
||
605 | provider = self.options_providers[0] |
||
606 | self.add_optik_option(provider, self.cmdline_parser, opt, optdict) |
||
607 | provider.options += ((opt, optdict),) |
||
608 | helplevel += 1
|
||
609 | if config_file is None: |
||
610 | config_file = self.config_file
|
||
611 | if config_file is not None: |
||
612 | config_file = os.path.expanduser(config_file) |
||
613 | if config_file and os.path.exists(config_file): |
||
614 | parser = self.cfgfile_parser
|
||
615 | parser.read([config_file]) |
||
616 | # normalize sections'title
|
||
617 | for sect, values in list(parser._sections.items()): |
||
618 | if not sect.isupper() and values: |
||
619 | parser._sections[sect.upper()] = values |
||
620 | elif not self.quiet: |
||
621 | msg = 'No config file found, using default configuration'
|
||
622 | #print(msg, file=sys.stderr)
|
||
623 | return
|
||
624 | |||
625 | def load_config_file(self): |
||
626 | """dispatch values previously read from a configuration file to each
|
||
627 | options provider)
|
||
628 | """
|
||
629 | parser = self.cfgfile_parser
|
||
630 | for section in parser.sections(): |
||
631 | for option, value in parser.items(section): |
||
632 | try:
|
||
633 | self.global_set_option(option, value)
|
||
634 | except (KeyError, optparse.OptionError): |
||
635 | # TODO handle here undeclared options appearing in the config file
|
||
636 | continue
|
||
637 | |||
638 | def load_configuration(self, **kwargs): |
||
639 | """override configuration according to given parameters"""
|
||
640 | for opt, opt_value in kwargs.items(): |
||
641 | opt = opt.replace('_', '-') |
||
642 | provider = self._all_options[opt]
|
||
643 | provider.set_option(opt, opt_value) |
||
644 | |||
645 | def load_command_line_configuration(self, args=None): |
||
646 | """Override configuration according to command line parameters
|
||
647 |
|
||
648 | return additional arguments
|
||
649 | """
|
||
650 | with _patch_optparse():
|
||
651 | if args is None: |
||
652 | args = sys.argv[1:]
|
||
653 | else:
|
||
654 | args = list(args)
|
||
655 | (options, args) = self.cmdline_parser.parse_args(args=args)
|
||
656 | for provider in self._nocallback_options.keys(): |
||
657 | config = provider.config |
||
658 | for attr in config.__dict__.keys(): |
||
659 | value = getattr(options, attr, None) |
||
660 | if value is None: |
||
661 | continue
|
||
662 | setattr(config, attr, value)
|
||
663 | return args
|
||
664 | |||
665 | def add_help_section(self, title, description, level=0): |
||
666 | """add a dummy option section for help purpose """
|
||
667 | group = optparse.OptionGroup(self.cmdline_parser,
|
||
668 | title=title.capitalize(), |
||
669 | description=description) |
||
670 | group.level = level |
||
671 | self._maxlevel = max(self._maxlevel, level) |
||
672 | self.cmdline_parser.add_option_group(group)
|
||
673 | |||
674 | def help(self, level=0): |
||
675 | """return the usage string for available options """
|
||
676 | self.cmdline_parser.formatter.output_level = level
|
||
677 | with _patch_optparse():
|
||
678 | return self.cmdline_parser.format_help() |
||
679 | |||
680 | |||
681 | class OptionsProviderMixIn(object): |
||
682 | """Mixin to provide options to an OptionsManager"""
|
||
683 | |||
684 | # those attributes should be overridden
|
||
685 | priority = -1
|
||
686 | name = 'default'
|
||
687 | options = () |
||
688 | level = 0
|
||
689 | |||
690 | def __init__(self): |
||
691 | self.config = optparse.Values()
|
||
692 | self.load_defaults()
|
||
693 | |||
694 | def load_defaults(self): |
||
695 | """initialize the provider using default values"""
|
||
696 | for opt, optdict in self.options: |
||
697 | action = optdict.get('action')
|
||
698 | if action != 'callback': |
||
699 | # callback action have no default
|
||
700 | if optdict is None: |
||
701 | optdict = self.get_option_def(opt)
|
||
702 | default = optdict.get('default')
|
||
703 | self.set_option(opt, default, action, optdict)
|
||
704 | |||
705 | def option_attrname(self, opt, optdict=None): |
||
706 | """get the config attribute corresponding to opt"""
|
||
707 | if optdict is None: |
||
708 | optdict = self.get_option_def(opt)
|
||
709 | return optdict.get('dest', opt.replace('-', '_')) |
||
710 | |||
711 | def option_value(self, opt): |
||
712 | """get the current value for the given option"""
|
||
713 | return getattr(self.config, self.option_attrname(opt), None) |
||
714 | |||
715 | def set_option(self, opt, value, action=None, optdict=None): |
||
716 | """method called to set an option (registered in the options list)"""
|
||
717 | if optdict is None: |
||
718 | optdict = self.get_option_def(opt)
|
||
719 | if value is not None: |
||
720 | value = _validate(value, optdict, opt) |
||
721 | if action is None: |
||
722 | action = optdict.get('action', 'store') |
||
723 | if action == 'store': |
||
724 | setattr(self.config, self.option_attrname(opt, optdict), value) |
||
725 | elif action in ('store_true', 'count'): |
||
726 | setattr(self.config, self.option_attrname(opt, optdict), 0) |
||
727 | elif action == 'store_false': |
||
728 | setattr(self.config, self.option_attrname(opt, optdict), 1) |
||
729 | elif action == 'append': |
||
730 | opt = self.option_attrname(opt, optdict)
|
||
731 | _list = getattr(self.config, opt, None) |
||
732 | if _list is None: |
||
733 | if isinstance(value, (list, tuple)): |
||
734 | _list = value |
||
735 | elif value is not None: |
||
736 | _list = [] |
||
737 | _list.append(value) |
||
738 | setattr(self.config, opt, _list) |
||
739 | elif isinstance(_list, tuple): |
||
740 | setattr(self.config, opt, _list + (value,)) |
||
741 | else:
|
||
742 | _list.append(value) |
||
743 | elif action == 'callback': |
||
744 | optdict['callback'](None, opt, value, None) |
||
745 | else:
|
||
746 | raise UnsupportedAction(action)
|
||
747 | |||
748 | def get_option_def(self, opt): |
||
749 | """return the dictionary defining an option given its name"""
|
||
750 | assert self.options |
||
751 | for option in self.options: |
||
752 | if option[0] == opt: |
||
753 | return option[1] |
||
754 | raise optparse.OptionError('no such option %s in section %r' |
||
755 | % (opt, self.name), opt)
|
||
756 | |||
757 | def options_by_section(self): |
||
758 | """return an iterator on options grouped by section
|
||
759 |
|
||
760 | (section, [list of (optname, optdict, optvalue)])
|
||
761 | """
|
||
762 | sections = {} |
||
763 | for optname, optdict in self.options: |
||
764 | sections.setdefault(optdict.get('group'), []).append(
|
||
765 | (optname, optdict, self.option_value(optname)))
|
||
766 | if None in sections: |
||
767 | yield None, sections.pop(None) |
||
768 | for section, options in sorted(sections.items()): |
||
769 | yield section.upper(), options
|
||
770 | |||
771 | def options_and_values(self, options=None): |
||
772 | if options is None: |
||
773 | options = self.options
|
||
774 | for optname, optdict in options: |
||
775 | yield (optname, optdict, self.option_value(optname)) |
||
776 | |||
777 | |||
778 | class ConfigurationMixIn(OptionsManagerMixIn, OptionsProviderMixIn): |
||
779 | """basic mixin for simple configurations which don't need the
|
||
780 | manager / providers model
|
||
781 | """
|
||
782 | def __init__(self, *args, **kwargs): |
||
783 | if not args: |
||
784 | kwargs.setdefault('usage', '') |
||
785 | kwargs.setdefault('quiet', 1) |
||
786 | OptionsManagerMixIn.__init__(self, *args, **kwargs)
|
||
787 | OptionsProviderMixIn.__init__(self)
|
||
788 | if not getattr(self, 'option_groups', None): |
||
789 | self.option_groups = []
|
||
790 | for _, optdict in self.options: |
||
791 | try:
|
||
792 | gdef = (optdict['group'].upper(), '') |
||
793 | except KeyError: |
||
794 | continue
|
||
795 | if gdef not in self.option_groups: |
||
796 | self.option_groups.append(gdef)
|
||
797 | self.register_options_provider(self, own_group=False) |
||
798 | |||
799 | |||
800 | def _generate_manpage(optparser, pkginfo, section=1, |
||
801 | stream=sys.stdout, level=0):
|
||
802 | formatter = _ManHelpFormatter() |
||
803 | formatter.output_level = level |
||
804 | formatter.parser = optparser |
||
805 | print(formatter.format_head(optparser, pkginfo, section), file=stream) |
||
806 | print(optparser.format_option_help(formatter), file=stream) |
||
807 | print(formatter.format_tail(pkginfo), file=stream) |