Read user configuration and local configuration. Closes #22.
authorFlorent Xicluna <florent.xicluna@gmail.com>
Sat, 2 Jun 2012 11:41:17 +0000 (13:41 +0200)
committerFlorent Xicluna <florent.xicluna@gmail.com>
Sat, 2 Jun 2012 11:41:17 +0000 (13:41 +0200)
1  2 
CHANGES.txt
pep8.py

diff --cc CHANGES.txt
index a21474ca6819ea6f88666900bb0dd604c798d93e,4934a468e58cc5b17414f38362c784322312f5ea..52d2b0ca11b975d3ef8cf891bedae3a4b2d87d3f
@@@ -2,93 -2,6 +2,96 @@@ Changelo
  =========
  
  
 +1.3 (UNRELEASED)
 +----------------
 +
++* Read user configuration from ``~/.config/pep8``
++  and local configuration from ``./.pep8``. (Issue #22)
++
 +* Suggest less error-prone alternatives for E712 errors.
 +
 +* Rewrite E27 checks to run faster.
 +
 +
 +1.2 (2012-06-01)
 +----------------
 +
 +* Add E121 through E128 for continuation line indentation.  These
 +  checks are disabled by default.  If you want to force all checks,
 +  use switch ``--select=E,W``.  Patch by Sam Vilain. (Issue #64)
 +
 +* Add E721 for direct type comparisons. (Issue #47)
 +
 +* Add E711 and E712 for comparisons to singletons. (Issue #46)
 +
 +* Fix spurious E225 and E701 for function annotations. (Issue #29)
 +
 +* Add E502 for explicit line join between brackets.
 +
 +* Fix E901 when printing source with ``--show-source``.
 +
 +* Report all errors for each checker, instead of reporting only the
 +  first occurence for each line.
 +
 +* Option ``--show-pep8`` implies ``--first``.
 +
 +
 +1.1 (2012-05-24)
 +----------------
 +
 +* Add E901 for syntax errors. (Issues #63 and #30)
 +
 +* Add E271, E272, E273 and E274 for extraneous whitespace around
 +  keywords. (Issue #57)
 +
 +* Add ``tox.ini`` configuration file for tests. (Issue #61)
 +
 +* Add ``.travis.yml`` configuration file for continuous integration.
 +  (Issue #62)
 +
 +
 +1.0.1 (2012-04-06)
 +------------------
 +
 +* Fix inconsistent version numbers.
 +
 +
 +1.0 (2012-04-04)
 +----------------
 +
 +* Fix W602 ``raise`` to handle multi-char names. (Issue #53)
 +
 +
 +0.7.0 (2012-03-26)
 +------------------
 +
 +* Now ``--first`` prints only the first occurence of each error.
 +  The ``--repeat`` flag becomes obsolete because it is the default
 +  behaviour. (Issue #6)
 +
 +* Allow to specify ``--max-line-length``. (Issue #36)
 +
 +* Make the shebang more flexible. (Issue #26)
 +
 +* Add testsuite to the bundle. (Issue #25)
 +
 +* Fixes for Jython. (Issue #49)
 +
 +* Add PyPI classifiers. (Issue #43)
 +
 +* Fix the ``--exclude`` option. (Issue #48)
 +
 +* Fix W602, accept ``raise`` with 3 arguments. (Issue #34)
 +
 +* Correctly select all tests if ``DEFAULT_IGNORE == ''``.
 +
 +
 +0.6.1 (2010-10-03)
 +------------------
 +
 +* Fix inconsistent version numbers. (Issue #21)
 +
 +
  0.6.0 (2010-09-19)
  ------------------
  
diff --cc pep8.py
index b9144517a19292b55f29730393a4851c9b700e9a,5f196ce637f02ebd3d31ef009f8f88431e42f7fb..cb80e64daa38f8a8646112fa8c6530c91e9db77c
+++ b/pep8.py
@@@ -107,15 -107,17 +107,22 @@@ from fnmatch import fnmatc
  try:
      frozenset
  except NameError:
 -    from sets import ImmutableSet as frozenset
 +    from sets import Set as set, ImmutableSet as frozenset
++try:
++    from configparser import RawConfigParser
++except ImportError:
++    from ConfigParser import RawConfigParser
  
  
  DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git'
 -DEFAULT_IGNORE = 'E24'
 +DEFAULT_IGNORE = 'E12,E24'
++DEFAULT_CONFIG = os.path.join(
++    os.getenv("XDG_CONFIG_HOME") or
++    os.path.join(os.getenv("HOME"), ".config"), "pep8")
  MAX_LINE_LENGTH = 79
  
 -DEFAULT_CONFIG = os.path.expanduser('~/.pep8')
 -DEFAULT_OPTION_LIST = ('verbose', 'quiet', 'repeat', 'exclude', 'filename',
 -    'select', 'ignore', 'show-source', 'show-pep8', 'statistics',
 -    'count', 'benchmark', 'testsuite', 'doctest', 'config')
 +SINGLETONS = frozenset(['False', 'None', 'True'])
 +KEYWORDS = frozenset(keyword.kwlist + ['print']) - SINGLETONS
  
  INDENT_REGEX = re.compile(r'([ \t]*)')
  RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*(,)')
@@@ -1572,6 -1256,6 +1579,52 @@@ def selftest()
              print("Test passed.")
  
  
++def read_config(options, args, arglist, parser):
++    config = RawConfigParser()
++
++    user_conf = options.config
++    if os.path.isfile(user_conf):
++        if options.verbose:
++            print('user configuration: %s' % user_conf)
++        config.read(user_conf)
++
++    if len(args) == 1:
++        parent, tail = os.path.abspath(args[0]), True
++        while tail:
++            local_conf = os.path.join(parent, '.pep8')
++            if os.path.isfile(local_conf):
++                if options.verbose:
++                    print('local configuration: %s' % local_conf)
++                config.read(local_conf)
++                break
++            parent, tail = os.path.split(parent)
++
++    if config.has_section('pep8'):
++        option_list = dict([(o.dest, o.type or o.action)
++                            for o in parser.option_list if o.dest])
++
++        # First, read the defaut values
++        options, _ = parser.parse_args([])
++
++        # Second, parse the configuration
++        for opt in config.options('pep8'):
++            opt_type = option_list.get(opt)
++            if not opt_type:
++                print('Unknown option: %s' % opt)
++            elif opt_type in ('int', 'count'):
++                value = config.getint('pep8', opt)
++            elif opt_type == 'string':
++                value = config.get('pep8', opt)
++            else:
++                assert opt_type in ('store_true', 'store_false')
++                value = config.getboolean('pep8', opt)
++            setattr(options, opt, value)
++
++        # Third, overwrite with the command-line options
++        options, _ = parser.parse_args(arglist, values=options)
++
++    return options
++
  def process_options(arglist=None):
      """
      Process options passed either via arglist or via command line args.
                        help="measure processing speed")
      parser.add_option('--testsuite', metavar='dir',
                        help="run regression tests from dir")
 +    parser.add_option('--max-line-length', type='int', metavar='n',
 +                      default=MAX_LINE_LENGTH,
 +                      help="set maximum allowed line length (default: %d)" %
 +                      MAX_LINE_LENGTH)
      parser.add_option('--doctest', action='store_true',
                        help="run doctest on myself")
 -    parser.add_option('--config', metavar='config-file',
 -                      default=DEFAULT_CONFIG,
++    parser.add_option('--config', metavar='path', default=DEFAULT_CONFIG,
+                       help='config file location')
 +
      options, args = parser.parse_args(arglist)
 +    if options.show_pep8:
 +        options.repeat = False
      if options.testsuite:
          args.append(options.testsuite)
--    if not args and not options.doctest:
--        parser.error('input not specified')
 -
 -    if options.config and os.path.isfile(options.config):
 -        config = ConfigParser.RawConfigParser()
 -        config.read(options.config)
 -        for opt in DEFAULT_OPTION_LIST:
 -            p_opt = opt.replace('-', '_')
 -            try:
 -                options.__dict__[p_opt] = config.get('pep8', opt)
 -            except ConfigParser.NoOptionError:
 -                try:
 -                    options.__dict__[p_opt] = config.get('pep8', p_opt)
 -                except ConfigParser.NoOptionError:
 -                    pass
++    elif not options.doctest:
++        if not args:
++            if os.path.exists('.pep8'):
++                args = ['.']
++            else:
++                parser.error('input not specified')
++        options = read_config(options, args, arglist, parser)
      options.prog = os.path.basename(sys.argv[0])
      options.exclude = options.exclude.split(',')
 -    for index in range(len(options.exclude)):
 -        options.exclude[index] = options.exclude[index].rstrip('/')
 +    for index, value in enumerate(options.exclude):
 +        options.exclude[index] = value.rstrip('/')
      if options.filename:
          options.filename = options.filename.split(',')
 +
      if options.select:
          options.select = options.select.split(',')
      else: