Allow ignore specific error codes in files

Instead of only ignoring full files or ignoring full
codes for all files, allow for ignoring specific errors
for certain files.

Change-Id: I0ebfbe1d2dc68990c84bc0b47d872e44e19b5b2a
This commit is contained in:
Joshua Harlow 2015-03-09 15:35:51 -07:00
parent 4d82c269ab
commit b2adee882b
2 changed files with 62 additions and 24 deletions

View File

@ -27,7 +27,8 @@ Command line usage
$ doc8 -h $ doc8 -h
usage: doc8 [-h] [--config path] [--allow-long-titles] [--ignore code] usage: doc8 [-h] [--config path] [--allow-long-titles] [--ignore code]
[--no-sphinx] [--ignore-path path] [--default-extension extension] [--no-sphinx] [--ignore-path path] [--ignore-path-errors path]
[--default-extension extension] [--file-encoding encoding]
[--max-line-length int] [-e extension] [-v] [--version] [--max-line-length int] [-e extension] [-v] [--version]
[path [path ...]] [path [path ...]]
@ -46,17 +47,20 @@ Command line usage
- no newline at end of file - D005 - no newline at end of file - D005
positional arguments: positional arguments:
path path to scan for doc files (default: os.getcwd()) path Path to scan for doc files (default: current
directory).
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
--config path user config file location (default: doc8.ini, tox.ini, --config path User config file location (default: doc8.ini, tox.ini,
pep8.ini, setup.cfg) pep8.ini, setup.cfg).
--allow-long-titles allow long section titles (default: False) --allow-long-titles Allow long section titles (default: False).
--ignore code ignore the given errors code/codes --ignore code Ignore the given error code(s).
--no-sphinx do not ignore sphinx specific false positives --no-sphinx Do not ignore sphinx specific false positives.
--ignore-path path ignore the given directory or file (globs are --ignore-path path Ignore the given directory or file (globs are
supported) supported).
--ignore-path-errors path
Ignore the given specific errors in the provided file.
--default-extension extension --default-extension extension
Default file extension to use when a file is found Default file extension to use when a file is found
without a file extension. without a file extension.
@ -65,11 +69,11 @@ Command line usage
an input files text encoding (providing this avoids an input files text encoding (providing this avoids
using `chardet` to automatically detect encoding/s) using `chardet` to automatically detect encoding/s)
--max-line-length int --max-line-length int
maximum allowed line length (default: 79) Maximum allowed line length (default: 79).
-e extension, --extension extension -e extension, --extension extension
check file extensions of the given type (default: Check file extensions of the given type (default:
.rst, .txt) .rst, .txt).
-v, --verbose run in verbose mode -v, --verbose Run in verbose mode.
--version Show the version and exit. --version Show the version and exit.
Ini file usage Ini file usage
@ -90,14 +94,14 @@ An example section that can be placed into one of these files::
[doc8] [doc8]
ignore_path=/tmp/stuff,/tmp/other_stuff ignore-path=/tmp/stuff,/tmp/other_stuff
max_line_length=99 max-line-length=99
verbose=1 verbose=1
ignore-path-errors=/tmp/other_thing.rst;D001;D002
**Note:** The option names are the same as the command line ones but instead **Note:** The option names are the same as the command line ones (with the
of dashes underscores are used instead (with the only variation of this being only variation of this being the ``no-sphinx`` option which from
the ``no-sphinx`` option which from configuration file will be ``sphinx`` configuration file will be ``sphinx`` instead).
instead).
Option conflict resolution Option conflict resolution
************************** **************************
@ -110,6 +114,7 @@ of conflicts.
Option Overrides Merges Option Overrides Merges
===================== =========== ======== ===================== =========== ========
``allow-long-titles`` Yes No ``allow-long-titles`` Yes No
``ignore-path-errors`` No Yes
``default-extension`` Yes No ``default-extension`` Yes No
``extension`` No Yes ``extension`` No Yes
``ignore-path`` No Yes ``ignore-path`` No Yes

View File

@ -60,8 +60,8 @@ CONFIG_FILENAMES = [
] ]
def split_set_type(text): def split_set_type(text, delimiter=","):
return set([i.strip() for i in text.split(",") if i.strip()]) return set([i.strip() for i in text.split(delimiter) if i.strip()])
def merge_sets(sets): def merge_sets(sets):
@ -71,6 +71,16 @@ def merge_sets(sets):
return m return m
def parse_ignore_path_errors(entries):
ignore_path_errors = collections.defaultdict(set)
for path in entries:
path, ignored_errors = path.split(";", 1)
path = os.path.abspath(path.strip())
ignored_errors = split_set_type(ignored_errors, delimiter=";")
ignore_path_errors[path].update(ignored_errors)
return dict(ignore_path_errors)
def extract_config(args): def extract_config(args):
parser = configparser.RawConfigParser() parser = configparser.RawConfigParser()
read_files = [] read_files = []
@ -97,6 +107,13 @@ def extract_config(args):
"ignore-path")) "ignore-path"))
except (configparser.NoSectionError, configparser.NoOptionError): except (configparser.NoSectionError, configparser.NoOptionError):
pass pass
try:
ignore_path_errors = parser.get("doc8", "ignore-path-errors")
ignore_path_errors = split_set_type(ignore_path_errors)
ignore_path_errors = parse_ignore_path_errors(ignore_path_errors)
cfg['ignore_path_errors'] = ignore_path_errors
except (configparser.NoSectionError, configparser.NoOptionError):
pass
try: try:
cfg['allow_long_titles'] = parser.getboolean("doc8", cfg['allow_long_titles'] = parser.getboolean("doc8",
"allow-long-titles") "allow-long-titles")
@ -186,10 +203,13 @@ def validate(cfg, files):
print("Validating...") print("Validating...")
error_counts = {} error_counts = {}
ignoreables = frozenset(cfg.get('ignore', [])) ignoreables = frozenset(cfg.get('ignore', []))
ignore_targeted = cfg.get('ignore_path_errors', {})
while files: while files:
f = files.popleft() f = files.popleft()
if cfg.get('verbose'): if cfg.get('verbose'):
print("Validating %s" % f) print("Validating %s" % f)
targeted_ignoreables = set(ignore_targeted.get(f.filename, set()))
targeted_ignoreables.update(ignoreables)
for c in fetch_checks(cfg): for c in fetch_checks(cfg):
try: try:
# http://legacy.python.org/dev/peps/pep-3155/ # http://legacy.python.org/dev/peps/pep-3155/
@ -214,7 +234,7 @@ def validate(cfg, files):
except AttributeError: except AttributeError:
pass pass
else: else:
reports = reports - ignoreables reports = reports - targeted_ignoreables
if not reports: if not reports:
if cfg.get('verbose'): if cfg.get('verbose'):
print(" Skipping check '%s', determined to only" print(" Skipping check '%s', determined to only"
@ -224,7 +244,7 @@ def validate(cfg, files):
print(" Running check '%s'" % check_name) print(" Running check '%s'" % check_name)
if isinstance(c, checks.ContentCheck): if isinstance(c, checks.ContentCheck):
for line_num, code, message in c.report_iter(f): for line_num, code, message in c.report_iter(f):
if code in ignoreables: if code in targeted_ignoreables:
continue continue
if cfg.get('verbose'): if cfg.get('verbose'):
print(' - %s:%s: %s %s' print(' - %s:%s: %s %s'
@ -236,7 +256,7 @@ def validate(cfg, files):
elif isinstance(c, checks.LineCheck): elif isinstance(c, checks.LineCheck):
for line_num, line in enumerate(f.lines_iter(), 1): for line_num, line in enumerate(f.lines_iter(), 1):
for code, message in c.report_iter(line): for code, message in c.report_iter(line):
if code in ignoreables: if code in targeted_ignoreables:
continue continue
if cfg.get('verbose'): if cfg.get('verbose'):
print(' - %s:%s: %s %s' print(' - %s:%s: %s %s'
@ -278,6 +298,9 @@ def main():
parser.add_argument("--ignore-path", action="append", default=[], parser.add_argument("--ignore-path", action="append", default=[],
help="Ignore the given directory or file (globs" help="Ignore the given directory or file (globs"
" are supported).", metavar='path') " are supported).", metavar='path')
parser.add_argument("--ignore-path-errors", action="append", default=[],
help="Ignore the given specific errors in the"
" provided file.", metavar='path')
parser.add_argument("--default-extension", action="store", parser.add_argument("--default-extension", action="store",
help="Default file extension to use when a file is" help="Default file extension to use when a file is"
" found without a file extension.", " found without a file extension.",
@ -315,6 +338,16 @@ def main():
args['sphinx'] = cfg.pop("sphinx") args['sphinx'] = cfg.pop("sphinx")
args['extension'].extend(cfg.pop('extension', [])) args['extension'].extend(cfg.pop('extension', []))
args['ignore_path'].extend(cfg.pop('ignore_path', [])) args['ignore_path'].extend(cfg.pop('ignore_path', []))
cfg.setdefault('ignore_path_errors', {})
for tmp_ignore_path_error in args.pop('ignore_path_errors', []):
tmp_ignores = parse_ignore_path_errors(tmp_ignore_path_error)
for path, ignores in six.iteritems(tmp_ignores):
if path in cfg['ignore_path_errors']:
cfg['ignore_path_errors'][path].update(ignores)
else:
cfg['ignore_path_errors'][path] = set(ignores)
args.update(cfg) args.update(cfg)
setup_logging(args.get('verbose')) setup_logging(args.get('verbose'))