diff --git a/doc8/main.py b/doc8/main.py index 5827a25..afc344b 100644 --- a/doc8/main.py +++ b/doc8/main.py @@ -31,14 +31,18 @@ What is checked: import argparse import collections +import logging import os import sys +LOG = logging.getLogger(__name__) + if __name__ == '__main__': # Only useful for when running directly (for dev/debugging). sys.path.insert(0, os.path.abspath(os.getcwd())) sys.path.insert(0, os.path.abspath(os.path.join(os.pardir, os.getcwd()))) +import six from six.moves import configparser from stevedore import extension @@ -97,6 +101,10 @@ def extract_config(args): cfg['sphinx'] = parser.getboolean("doc8", "sphinx") except (configparser.NoSectionError, configparser.NoOptionError): pass + try: + cfg['verbose'] = parser.getboolean("doc8", "verbose") + except (configparser.NoSectionError, configparser.NoOptionError): + pass try: extensions = parser.get("doc8", "extensions") extensions = extensions.split(",") @@ -127,6 +135,15 @@ def fetch_checks(cfg): return base + addons +def setup_logging(verbose): + if verbose: + level = logging.DEBUG + else: + level = logging.ERROR + logging.basicConfig(level=level, + format='%(levelname)s: %(message)s', stream=sys.stdout) + + def main(): parser = argparse.ArgumentParser( prog='doc8', @@ -164,6 +181,8 @@ def main(): help="check file extensions of the given type" " (default: %s)" % ", ".join(FILE_PATTERNS), default=[]) + parser.add_argument("-v", "--verbose", dest="verbose", action='store_true', + help="run in verbose mode", default=False) args = vars(parser.parse_args()) args['ignore'] = merge_sets(args['ignore']) cfg = extract_config(args) @@ -174,21 +193,33 @@ def main(): args.update(cfg) if not args.get('extension'): args['extension'] = list(FILE_PATTERNS) - + setup_logging(args.get('verbose')) files = collections.deque() ignored_paths = [] for path in args.pop('ignore_path', []): ignored_paths.append(os.path.normpath(path)) + print("Scanning...") for filename in utils.find_files(args.pop('paths', []), args.pop('extension', []), ignored_paths): files.append(file_parser.parse(filename)) ignoreables = frozenset(args.pop('ignore', [])) - errors = 0 + error_counts = {} while files: f = files.popleft() + if args.get('verbose'): + print("Validating %s" % f) for c in fetch_checks(args): + try: + # http://legacy.python.org/dev/peps/pep-3155/ + check_name = c.__class__.__qualname__ + except AttributeError: + check_name = ".".join([c.__class__.__module__, + c.__class__.__name__]) + if args.get('verbose'): + print(" Running check '%s'" % check_name) + error_counts.setdefault(check_name, 0) try: reports = set(c.REPORTS) except AttributeError: @@ -196,22 +227,40 @@ def main(): else: reports = reports - ignoreables if not reports: + LOG.debug("Skipping check '%s', determined to only" + " check ignoreable codes", check_name) continue if isinstance(c, checks.ContentCheck): for line_num, code, message in c.report_iter(f): - print('%s:%s: %s %s' - % (f.filename, line_num, code, message)) - errors += 1 + if args.get('verbose'): + print(' - %s:%s: %s %s' + % (f.filename, line_num, code, message)) + else: + print('%s:%s: %s %s' + % (f.filename, line_num, code, message)) + error_counts[check_name] += 1 elif isinstance(c, checks.LineCheck): for line_num, line in enumerate(f.lines_iter(), 1): for code, message in c.report_iter(line): - print('%s:%s: %s %s' - % (f.filename, line_num, code, message)) - errors += 1 + if args.get('verbose'): + print(' - %s:%s: %s %s' + % (f.filename, line_num, code, message)) + else: + print('%s:%s: %s %s' + % (f.filename, line_num, code, message)) + error_counts[check_name] += 1 else: raise TypeError("Unknown check type: %s, %s" % (type(c), c)) - if errors: + total_errors = sum(six.itervalues(error_counts)) + print("=" * 8) + print("Total accumulated errors = %s" % total_errors) + if error_counts: + print("Detailed error counts:") + for check_name in sorted(six.iterkeys(error_counts)): + check_errors = error_counts[check_name] + print(" - %s = %s" % (check_name, check_errors)) + if total_errors: return 1 else: return 0 diff --git a/doc8/parser.py b/doc8/parser.py index 30bb8fa..5963e60 100644 --- a/doc8/parser.py +++ b/doc8/parser.py @@ -99,6 +99,11 @@ class ParsedFile(object): encoding=self.encoding) return self._content + def __str__(self): + return "%s (%s, %s chars, %s lines)" % ( + self.filename, self.encoding, len(self.contents), + len(list(self.lines_iter()))) + def parse(filename, encoding="utf8"): if not os.path.isfile(filename):