def mi(paths, min='A', max='C', multi=True, exclude=None, ignore=None, show=False, json=False, sort=False): '''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 paths: The paths where to find modules or packages to analyze. More than one path is allowed. :param -n, --min <str>: The minimum MI to display (default to A). :param -x, --max <str>: The maximum MI to display (default to C). :param -e, --exclude <str>: Exclude files only when their path matches one of these glob patterns. Usually needs quoting at the command line. :param -i, --ignore <str>: Ignore directories when their name matches one of these glob patterns: radon won't even descend into them. By default, hidden directories (starting with '.') are ignored. :param -m, --multi: If given, multiline strings are not counted as comments. :param -s, --show: If given, the actual MI value is shown in results. :param -j, --json: Format results in JSON. :param --sort: If given, results are sorted in ascending order. ''' config = Config( min=min.upper(), max=max.upper(), exclude=exclude, ignore=ignore, multi=multi, show=show, sort=sort, ) harvester = MIHarvester(paths, config) log_result(harvester, json=json)
def radon_mi(self, f, config): result = True for mir in MIHarvester([f], config).results: ''' Не допускается Maintainability Index ниже 30% ''' if mir[1]['mi'] < 30: print('%s: %u%%' % (mir[0], mir[1]['mi'])) result = False return result
def radon_mi(self, filename, config): result = True for mir in MIHarvester([filename], config).results: ''' Не допускается Maintainability Index ниже 50% ''' if mir[1]['mi'] < 50: print('%s: Low maintainability index - %u%%' % (mir[0], mir[1]['mi'])) result = False return result
def mi( paths, min=_cfg.get_value('mi_min', str, 'A'), max=_cfg.get_value('mi_max', str, 'C'), multi=_cfg.get_value('multi', bool, True), exclude=_cfg.get_value('exclude', str, None), ignore=_cfg.get_value('ignore', str, None), show=_cfg.get_value('show_mi', bool, False), json=False, sort=False, output_file=_cfg.get_value('output_file', str, None), include_ipynb=_cfg.get_value('include_ipynb', bool, False), ipynb_cells=_cfg.get_value('ipynb_cells', bool, False), ): '''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 paths: The paths where to find modules or packages to analyze. More than one path is allowed. :param -n, --min <str>: The minimum MI to display (default to A). :param -x, --max <str>: The maximum MI to display (default to C). :param -e, --exclude <str>: Exclude files only when their path matches one of these glob patterns. Usually needs quoting at the command line. :param -i, --ignore <str>: Ignore directories when their name matches one of these glob patterns: radon won't even descend into them. By default, hidden directories (starting with '.') are ignored. :param -m, --multi: If given, multiline strings are not counted as comments. :param -s, --show: If given, the actual MI value is shown in results. :param -j, --json: Format results in JSON. :param --sort: If given, results are sorted in ascending order. :param -O, --output-file <str>: The output file (default to stdout). :param --include-ipynb: Include IPython Notebook files :param --ipynb-cells: Include reports for individual IPYNB cells ''' config = Config( min=min.upper(), max=max.upper(), exclude=exclude, ignore=ignore, multi=multi, show=show, sort=sort, include_ipynb=include_ipynb, ipynb_cells=ipynb_cells, ) harvester = MIHarvester(paths, config) with outstream(output_file) as stream: log_result(harvester, json=json, stream=stream)
def get_files_maintainability_data(paths, ignore): config = Config( min="A", max="C", exclude=ignore, ignore=ignore, multi=True, show=True, ) harvester = MIHarvester(paths, config) data = [] for filename, mi_data in harvester.results: if not mi_data: continue data.append((filename, mi_data['mi'])) sorted_scores = sorted(data, key=operator.itemgetter(1)) return OrderedDict(sorted_scores)
def raw(code_path, results): """ Compute raw metrics such as number of lines of code, documentation rate or complexity metrics :param code_path: Path to the source code :param results: Dictionary to which the results are appended. """ # Lines h = RawHarvester([code_path], Config(exclude=None, ignore=None, summary=True)) file_metrics = dict(h.results) # Maintainability h = MIHarvester([code_path], Config(min='A', max='C', multi=True, exclude=None, ignore=None, show=False, json=False, sort=False)) mi_metrics = dict(h.results) always_merger.merge(file_metrics, mi_metrics) # Create a summary for the total of the code summary = dict() summation_keys = ['loc', 'lloc', 'sloc', 'comments', 'multi', 'blank', 'single_comments'] for k in summation_keys: summary[k] = sum([metrics[k] for metrics in file_metrics.values()]) # Weighted average summaries averaging_keys = {'mi': 'sloc'} for key_index, weight_index in averaging_keys.items(): if summary[weight_index] == 0.0: summary[weight_index] = 0.0 else: summary[key_index] = sum([metrics[key_index] * metrics[weight_index] for metrics in file_metrics.values()]) / summary[weight_index] # Export results results[LINES_OF_CODE] = summary.get('lloc', 0) results[COMMENT_RATE] = (float(summary.get('comments', 0)) + float(summary.get('multi', 0))) / (max(1, float(summary.get('loc', 1)))) results[MAINTAINABILITY_INDEX] = summary.get('mi', 0.0) / 100.0
def analyse(paths): #Setup the Configuration for each Harvester config = Config( exclude=[], ignore=[], order=SCORE, no_assert= False, show_closures=False, min='A', max='F', ) config2 = Config( exclude=[], ignore=[], by_function= False ) config3 = Config( min= 'A', max= 'C', exclude=[], ignore=[], multi=True, show=True, ) config4 = Config( exclude=[], ignore=[], summary=False, json=True, ) """ ---------------------- Cyclomatic Complexity --------------------- Cyclomatic Complexity corresponds to the number of decisions a block of code contains plus 1. This number (also called McCabe number) is equal to the number of linearly independent paths through the code. This number can be used as a guide when testing conditional logic in blocks.Radon analyzes the AST tree of a Python program to compute Cyclomatic Complexity. Statements have the following effects on Cyclomatic Complexity: """ h = CCHarvester(paths, config) ccResults = h._to_dicts() # print(ccResults) numOfFunctions = 0 complexity = 0 # for result in ccResults.values(): # numOfFunctions += 1 # complexity += result['complexity'] if isinstance(result, dict) else 0 for path in paths: for i in ccResults.get(path, []): numOfFunctions += 1 complexity += i["complexity"] if isinstance(i, dict) else 0 cc = complexity/numOfFunctions if numOfFunctions != 0 else 0 """ ------------------- Halstead's Metrics ------------------ Halstead’s goal was to identify measurable properties of software, and the relations between them. These numbers are statically computed from the source code: Effort, Bugs, Length, Difficulty, Time, Vocabulary , Volume """ i = HCHarvester(paths, config2) hcResults = i._to_dicts() halsteadEffort = 0 halsteadBugs = 0 halsteadLength = 0 halsteadDifficulty = 0 halsteadTime = 0 halsteadVocabulary = 0 halsteadVolume = 0 numberOfFiles = 0 for result in hcResults.values(): if 'total' in result: halsteadEffort += result["total"][9] halsteadBugs += result["total"][11] halsteadLength += result["total"][5] halsteadDifficulty += result["total"][8] halsteadTime += result["total"][10] halsteadVocabulary += result["total"][4] halsteadVolume += result["total"][7] numberOfFiles += 1 avgHalsteadEffort = halsteadEffort/numberOfFiles avgHalsteadBugs = halsteadBugs/numberOfFiles avgHalsteadLength = halsteadLength /numberOfFiles avgHalsteadDifficulty = halsteadDifficulty/numberOfFiles avgHalsteadTime = halsteadTime/numberOfFiles avgHalsteadVocabulary = halsteadVocabulary/numberOfFiles avgHalsteadVolume = halsteadVolume/numberOfFiles """ --------------------------------------- MI Harvester for Maintainability index -------------------------------------- Maintainability Index is a software metric which measures how maintainable (easy to support and change) the source code is. The maintainability index is calculated as a factored formula consisting of SLOC (Source Lines Of Code), Cyclomatic Complexity and Halstead volume. It is used in several automated software metric tools, including the Microsoft Visual Studio 2010 development environment, which uses a shifted scale (0 to 100) derivative. """ j = MIHarvester(paths, config3) miResults = dict(j.filtered_results) miVal = 0 numOfFiles = 0 for result in miResults.values(): if 'mi' in result: miVal += result["mi"] numOfFiles += 1 mi = miVal/numOfFiles """ ------------ Raw Metrics ----------- The following are the definitions employed by Radon: - LOC: The total number of lines of code. It does not necessarily correspond to the number of lines in the file. - LLOC: The number of logical lines of code. Every logical line of code contains exactly one statement. - SLOC: The number of source lines of code - not necessarily corresponding to the LLOC. [sloc] - Comments: The number of comment lines. Multi-line strings are not counted as comment since, to the Python interpreter, they are just strings. - Multi: The number of lines which represent multi-line strings. [multi] - Blanks: The number of blank lines (or whitespace-only ones). [blank] """ k = RawHarvester(paths, config4) rawResults = (dict(k.results)) comments = 0 lloc = 0 loc = 0 for result in rawResults.values(): if 'comments' in result: comments += result['comments'] if 'lloc' in result: lloc += result['lloc'] if 'loc' in result: loc += result['loc'] data = { "numberOfFiles" : len(paths), "numberOfLines" : loc, "numberOfLogicalLines" : lloc, "numberOfComments" : comments, "cyclomaticComplexity" : cc, "maintainabilityIndex" : mi, "halsteadEffort" : avgHalsteadEffort, "halsteadBugs" : avgHalsteadBugs, "halsteadLength" :avgHalsteadLength, "halsteadDifficulty" : avgHalsteadDifficulty, "halsteadTime" : avgHalsteadTime, "halsteadVocabulary" : avgHalsteadVocabulary, "halsteadVolume" : avgHalsteadVolume } return data