Statistics
| Revision:

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 @ 745

History | View | Annotate | Download (29.5 KB)

1
# 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)