From 523ae9256e7e12a054741c37744aea21e935d0a6 Mon Sep 17 00:00:00 2001 From: Michele Lacchia Date: Tue, 24 Dec 2013 13:26:52 +0100 Subject: [PATCH] Fix #39 Added -i, --ignore option --- radon/cli.py | 29 ++++++++++++++++++----------- radon/tools.py | 37 ++++++++++++++++++++++++------------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/radon/cli.py b/radon/cli.py index bc54c17..b7eb47e 100644 --- a/radon/cli.py +++ b/radon/cli.py @@ -94,16 +94,17 @@ def _print_cc_results(path, results, show_complexity): return average_cc, len(results) -def analyze_cc(paths, exclude, min, max, order_function): +def analyze_cc(paths, exclude, ignore, min, max, order_function): '''Analyze the files located under `paths`. :param paths: A list of paths to analyze. :param exclude: A comma-separated string of fnmatch patterns. + :param ignore: A comma-separated string of patterns to ignore. :param min: The minimum rank to output. :param max: The maximum rank to output. :param order_function: Can be `SCORE`, `LINES` or `ALPHA`, to sort the results respectively by CC score, line number or name.''' - for name in iter_filenames(paths, exclude): + for name in iter_filenames(paths, exclude, ignore): with open(name) as fobj: try: results = sorted_results(cc_visit(fobj.read()), order_function) @@ -115,19 +116,21 @@ def analyze_cc(paths, exclude, min, max, order_function): @program.command -def mi(multi=True, exclude=None, show=False, *paths): +def mi(multi=True, exclude=None, ignore=None, show=False, *paths): '''Analyze the given Python modules and compute the Maintainability Index. The maintainability index (MI) is a compound metric, with the primary aim being to determine how easy it will be to maintain a particular body of code. - :param -e, --exclude : Comma separated list of patterns to exclude - :param -m, --multi: If given, multiline strings are counted as comments - :param -s, --show: If given, the actual MI value is shown in results + :param -e, --exclude : Comma separated list of patterns to exclude. + :param -i, --ignore : Comma separated list of patterns to ignore. + Radon won't even descend into those directories. + :param -m, --multi: If given, multiline strings are counted as comments. + :param -s, --show: If given, the actual MI value is shown in results. :param paths: The modules or packages to analyze. ''' - for name in iter_filenames(paths, exclude): + for name in iter_filenames(paths, exclude, ignore): with open(name) as fobj: try: result = mi_visit(fobj.read(), multi) @@ -146,7 +149,7 @@ def mi(multi=True, exclude=None, show=False, *paths): @program.command def cc(path, min='A', max='F', show_complexity=False, average=False, - exclude=None, order='SCORE', json=False, *more_paths): + exclude=None, ignore=None, order='SCORE', json=False, *more_paths): '''Analyze the given Python modules and compute Cyclomatic Complexity (CC). @@ -158,6 +161,8 @@ def cc(path, min='A', max='F', show_complexity=False, average=False, :param -x, --max : The maximum complexity to display (default to F). :param -e, --exclude : Comma separated list of patterns to exclude. By default hidden directories (those starting with '.') are excluded. + :param -i, --ignore : Comma separated list of patterns to ignore. + Radon won't even descend into them. :param -s, --show_complexity: Whether or not to show the actual complexity score together with the A-F rank. Default to False. :param -a, --average: If True, at the end of the analysis display the average @@ -173,7 +178,7 @@ def cc(path, min='A', max='F', show_complexity=False, average=False, average_cc = .0 analyzed = 0 order_function = getattr(cc_mod, order.upper(), getattr(cc_mod, 'SCORE')) - cc_data = analyze_cc(paths, exclude, min, max, order_function) + cc_data = analyze_cc(paths, exclude, ignore, min, max, order_function) if json: result = {} for key, data in cc_data: @@ -194,7 +199,7 @@ def cc(path, min='A', max='F', show_complexity=False, average=False, @program.command -def raw(exclude=None, summary=False, *paths): +def raw(exclude=None, ignore=None, summary=False, *paths): '''Analyze the given Python modules and compute raw metrics. Raw metrics include: @@ -215,6 +220,8 @@ def raw(exclude=None, summary=False, *paths): :param -e, --exclude : Comma separated list of patterns to exclude. By default hidden directories (those starting with '.') are excluded. + :param -i, --ignore : Comma separated list of patterns to ignore. + Radon won't even descend into those directories. :param -s, --summary: If given, at the end of the analysis display the summary of the gathered metrics. Default to False. :param paths: The modules or packages to analyze. @@ -222,7 +229,7 @@ def raw(exclude=None, summary=False, *paths): headers = ['LOC', 'LLOC', 'SLOC', 'Comments', 'Multi', 'Blank'] sum_metrics = collections.defaultdict(int, zip(headers, [0] * 6)) - for path in iter_filenames(paths, exclude): + for path in iter_filenames(paths, exclude, ignore): with open(path) as fobj: log(path) try: diff --git a/radon/tools.py b/radon/tools.py index 1ce5f99..00ed881 100644 --- a/radon/tools.py +++ b/radon/tools.py @@ -2,38 +2,49 @@ import operator import itertools from functools import reduce -from pathfinder import find_paths, FnmatchFilter, NotFilter, FileFilter +from pathfinder import (find_paths, FnmatchFilter, NotFilter, FileFilter, + DirectoryFilter) from radon.visitors import Function from radon.complexity import cc_rank -def iter_filenames(paths, exclude=None): +def iter_filenames(paths, exclude=None, ignore=None): '''A generator that yields all sub-paths of the ones specified in `paths`. Optional exclude filters can be passed as a comma-separated string of fnmatch patterns.''' - finder = lambda path: build_finder(path, build_filter(exclude)) + finder = lambda path: build_finder(path, build_filter(exclude), + build_ignore(ignore)) return itertools.chain(*map(finder, paths)) -def build_finder(path, filter): +def build_finder(path, filter, ignore): '''Construct a path finder for the specified `path` and with the specified `filter`. Hidden directories are ignored by default.''' if os.path.isfile(path): return (path,) - return find_paths(path, filter=filter, ignore=FnmatchFilter('*/.*')) + return find_paths(path, filter=filter, ignore=ignore) -def build_filter(exclude=None): +def build_filter(exclude): '''Construct a filter from a comma-separated string of fnmatch patterns.''' - excluded = [FnmatchFilter(e) for e in (exclude or '').split(',') if e] - f = FileFilter() & FnmatchFilter('*.py') - if excluded: - f &= NotFilter( - reduce(operator.or_, excluded[1:], excluded[0]) - ) - return f + return build_custom(exclude, FileFilter() & FnmatchFilter('*.py'), + NotFilter) +def build_ignore(ignore): + '''Construct an ignore filter from a comma-separated string of fnmatch + patterns.''' + return build_custom(ignore, DirectoryFilter(), add=[FnmatchFilter('*/.*')]) + + +def build_custom(pattern, start, final=lambda x: x, op=operator.or_, add=[]): + patt = [FnmatchFilter(p) for p in (pattern or '').split(',') if p] + add + if patt: + start &= final( + reduce(op, patt[1:], patt[0]) + ) + return start + def cc_to_dict(obj): '''Convert a list of results into a dictionary. This is meant for JSON dumping.'''