Read options from config (#1668)
* Read options from config
* Fix assert in test
* Fix tests
* Fix readme to pass rst checks in Travis
* Also support .codespellrc config file
* Fix flake8 error
* CLI args override config args
* Rename tool:codespell to just codespell in config
* Fix typo in readme
* Remove unnecessary check for existance of config files (configparser already handles this case)
diff --git a/README.rst b/README.rst
index d99331e..9058118 100644
--- a/README.rst
+++ b/README.rst
@@ -78,6 +78,28 @@
echo "word" | codespell -
echo "1stword,2ndword" | codespell -
+Using a config file
+-------------------
+
+Command line options can also be specified in a config file.
+
+When running ``codespell``, it will check in the current directory for a file
+named ``setup.cfg`` or ``.codespellrc`` (or a file specified via ``--config``),
+containing an entry named ``[codespell]``. Each command line argument can
+be specified in this file (without the preceding dashes), for example::
+
+ [codespell]
+ skip = *.po,*.ts,./src/3rdParty,./src/Test
+ count =
+ quiet-level = 3
+
+This is equivalent to running::
+
+ codespell --quiet-level 3 --count --skip "*.po,*.ts,./src/3rdParty,./src/Test"
+
+Any options specified in the command line will *override* options from the
+config file.
+
Dictionary format
-----------------
diff --git a/codespell_lib/_codespell.py b/codespell_lib/_codespell.py
index 83ebaa4..7ccff56 100755
--- a/codespell_lib/_codespell.py
+++ b/codespell_lib/_codespell.py
@@ -21,6 +21,7 @@
import argparse
import codecs
+import configparser
import fnmatch
import os
import re
@@ -361,12 +362,39 @@
help='print LINES of leading context')
parser.add_argument('-C', '--context', type=int, metavar='LINES',
help='print LINES of surrounding context')
+ parser.add_argument('--config', type=str,
+ help='path to config file.')
parser.add_argument('files', nargs='*',
help='files or directories to check')
+ # Parse command line options.
options = parser.parse_args(list(args))
+ # Load config files and look for ``codespell`` options.
+ cfg_files = ['setup.cfg', '.codespellrc']
+ if options.config:
+ cfg_files.append(options.config)
+ config = configparser.ConfigParser()
+ config.read(cfg_files)
+
+ if config.has_section('codespell'):
+ # Build a "fake" argv list using option name and value.
+ cfg_args = []
+ for key in config['codespell']:
+ # Add option as arg.
+ cfg_args.append("--%s" % key)
+ # If value is blank, skip.
+ val = config['codespell'][key]
+ if val != "":
+ cfg_args.append(val)
+
+ # Parse config file options.
+ options = parser.parse_args(cfg_args)
+
+ # Re-parse command line options to override config.
+ options = parser.parse_args(list(args), namespace=options)
+
if not options.files:
options.files.append('.')
diff --git a/codespell_lib/tests/test_basic.py b/codespell_lib/tests/test_basic.py
index 876e24c..2e8fd73 100644
--- a/codespell_lib/tests/test_basic.py
+++ b/codespell_lib/tests/test_basic.py
@@ -488,6 +488,39 @@
assert cs.main(f.name, r'--ignore-regex=\Wdonn\W') == 1
+def test_config(tmpdir, capsys):
+ """
+ Tests loading options from a config file.
+ """
+ d = str(tmpdir)
+
+ # Create sample files.
+ with open(op.join(d, 'bad.txt'), 'w') as f:
+ f.write('abandonned donn\n')
+ with open(op.join(d, 'good.txt'), 'w') as f:
+ f.write("good")
+
+ # Create a config file.
+ conffile = op.join(d, 'config.cfg')
+ with open(conffile, 'w') as f:
+ f.write(
+ '[codespell]\n'
+ 'skip = bad.txt\n'
+ 'count = \n'
+ )
+
+ # Should fail when checking both.
+ code, stdout, _ = cs.main(d, count=True, std=True)
+ # Code in this case is not exit code, but count of misspellings.
+ assert code == 2
+ assert 'bad.txt' in stdout
+
+ # Should pass when skipping bad.txt
+ code, stdout, _ = cs.main('--config', conffile, d, count=True, std=True)
+ assert code == 0
+ assert 'bad.txt' not in stdout
+
+
@contextlib.contextmanager
def FakeStdin(text):
if sys.version[0] == '2':