# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution # Copyright (C) 2004-2010 Tiny SPRL (). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # ############################################################################## import os from tools.translate import _ from base_module_quality import base_module_quality class quality_test(base_module_quality.abstract_quality_check): def __init__(self): super(quality_test, self).__init__() self.name = _("PEP-8 Test") self.note = _(""" PEP-8 Test , copyright of py files check, method can not call from loops """) self.bool_installed_only = False self.bad_standard = 0 self.good_standard = 0 self.result_py = {} self.min_score = 40 return None def run_test(self, cr, uid, module_path): list_files = os.listdir(module_path) for i in list_files: path = os.path.join(module_path, i) if os.path.isdir(path): for j in os.listdir(path): list_files.append(os.path.join(i, j)) py_list = [] for file_py in list_files: if file_py.split('.')[-1] == 'py' and not file_py.endswith('__init__.py') and not file_py.endswith('__openerp__.py'): file_path = os.path.join(module_path, file_py) py_list.append(file_path) open_files = map(lambda x: open(x, 'r'), py_list) if not py_list: self.error = True self.result = _("No python file found") return None #below functions check: #1. Imports should usually be on separate lines #2. Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants self.check_import(open_files) #1. there should be a one space after , : ; self.check_space(open_files) #1. Have all the .py files a copyright? self.check_licence(open_files) #1. Does the module avoid unecessary queries like when we put a browse into a loop? self.check_loop(open_files) #1.More than one space around an assignment (or other) operator to align it with another. # self.check_space_operator(open_files) #1. For sequences, (strings, lists, tuples), use the fact that empty sequences are false #for e.g : if seq: => good & if len(seq): => not good self.check_len(open_files) # below function checks # 1. Don't compare boolean values to True or False using == and != self.check_boolean(open_files) self.score = self.good_standard and float(self.good_standard) / float(self.good_standard + self.bad_standard) if self.score*100 < self.min_score: self.message = 'Score is below than minimal score(%s%%)' % self.min_score self.result = self.get_result({ module_path: [int(self.score * 100)]}) self.result_details += self.get_result_general(self.result_py) return None def check_import(self, open_files): for py in open_files: py.seek(0) class_or_def = False line_counter = 0 file_name = py.name.split('/')[-1] while True: line_counter += 1 line = py.readline() if not line: break if ((line.find('class') > -1) or (line.find('def') > -1)): class_or_def = True import_found = line.find('import') comment_found = line.find('#') if comment_found == -1 and import_found != -1: self.good_standard += 1 if (class_or_def): self.bad_standard += 1 self.result_py[file_name + str(line_counter)] = [file_name, line_counter, 'Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants'] if (line.find('from') < 0 and line.find(',') != -1): self.bad_standard += 1 self.result_py[file_name + str(line_counter)] = [file_name, line_counter, 'Imports should usually be on separate lines'] def check_licence(self, open_files): for py in open_files: py.seek(0) bad_position = False copyright_found = False gnu_found = False license_found = False gnu_website_found = False line_counter = 0 file_name = py.name.split('/')[-1] while True: declaration = False flag = False line_counter += 1 line = py.readline() if not line: break if ((line.find('class') > -1) or (line.find('def') > -1) or (line.find('import') > -1)): bad_position = True comment_found = line.find('#') copyright_found = line.find('Copyright') gnu_found = line.find('GNU') license_found = line.find('License') gnu_website_found = line.find('www.gnu.org/licenses') if ((copyright_found > -1) or (gnu_found > -1) or (license_found > -1) or (gnu_website_found > -1)): self.good_standard += 1 declaration = True flag = True break if (comment_found > -1) and bad_position and declaration: self.bad_standard += 1 self.result_py[file_name + str(line_counter)] = [file_name, line_counter, 'Declaration of copyright must be at the top of file'] break if bad_position and (not flag): self.bad_standard += 1 self.result_py[file_name] = [file_name, '--', 'File is not copyright'] def check_loop(self, open_files): for py in open_files: py.seek(0) methods = ['browse', 'search', 'read', 'copy', 'unlink'] place_for = 1000 file_name = py.name.split('/')[-1] line_counter = 0 counter = 0 while True: line_counter += 1 line = py.readline() if not line: break place_method = 0 for i in line : if (i == ' '): place_method += 1 elif (i != ' '): break elif (place_method > 100): break if (line.find('for') > -1): place_for = place_method if (place_for < place_method): counter += 1 for method in methods: got = line.find(method) if(got > -1): self.bad_standard += 1 self.result_py[file_name + str(line_counter)] = [file_name, line_counter, 'puting method inside loop is not good'] self.good_standard += counter def check_space(self, open_files): for py in open_files: py.seek(0) counter_line = 0 file_name = py.name.split('/')[-1] counter = 0 while True: counter_line += 1 line = py.readline() if not line: break pos_comma = line.find(',') pos_semicolon = line.find(';') pos_colon = line.find(':') space_find = -1 if (pos_comma != -1 or pos_semicolon != -1 or pos_colon != -1): counter += 1 for i in line: space_find += 1 if (i == ' '): if ((space_find + 1) == pos_comma) or ((space_find + 1) == pos_semicolon) or ((space_find + 1) == pos_colon): self.bad_standard += 1 self.result_py[file_name + str(counter_line)] = [file_name, counter_line, 'You should not have space before (: ; ,)'] self.good_standard += counter # to be check def check_space_operator(self, open_files): for py in open_files: py.seek(0) space_counter = 0 eq_found = False operator_found = False line_counter = 0 file_name = py.name.split('/')[-1] while True: line_counter += 1 line = py.readline() if not line: break for counter in line: if (counter == ' '): space_counter += 1 else: if (space_counter > 1): if counter in ['=', '<', '>', '!', '+', '-', '*', '/', '^', '%'] or operator_found: self.bad_standard += 1 self.result_py[file_name + str(line_counter)] = [file_name, line_counter, 'More than one space around an assignment (or other) operator to align it with another'] operator_found = False space_counter = 0 if counter in ['=', '<', '>', '!', '+', '-', '*', '/', '^', '%']: self.good_standard += 1 operator_found = True def check_len(self, open_files): for py in open_files: py.seek(0) line_counter = 0 file_name = py.name.split('/')[-1] while True: line_counter += 1 line = py.readline() if not line: break if (line.find('if') > -1) and (line.find('len(') > -1) and (line.find(')') > -1): self.good_standard += 1 if (line.find(':') > -1) and not line.find('<') > -1 and not line.find('>') > -1 and not line.find('=') > -1 and not line.find('!') > -1 : self.bad_standard += 1 self.result_py[file_name + str(line_counter)] = [file_name, line_counter, ' For sequences, (strings, lists, tuples), use the fact that empty sequences are false'] def check_boolean(self, open_files): for py in open_files: py.seek(0) line_counter = 0 file_name = py.name.split('/')[-1] while True: line_counter += 1 line = py.readline() if not line: break if (line.find('if') > -1): self.good_standard += 1 if ((line.find('==') > -1) or (line.find('!=') > -1)) and ((line.find('True') > -1) or (line.find('False') > -1)): self.bad_standard += 1 self.result_py[file_name + str(line_counter)] = [file_name, line_counter, "Don't compare boolean values to True or False using == or !="] def get_result(self, dict_obj): header = ('{| border="1" cellspacing="0" cellpadding="5" align="left" \n! %-40s \n', [_('Result of pep8_test in %')]) if not self.error: return self.format_table(header, data_list=dict_obj) return "" def get_result_general(self, dict_obj): str_html = '''Result%s'''%(self.get_style()) header = ('', [_('Object Name'), _('Line number'), _('Suggestion')]) if not self.error: res = str_html + self.format_html_table(header, data_list=dict_obj) + '
%s%s%s
' res = res.replace('''