@ -248,15 +248,38 @@ def _ReportErrorFileAndLine(filename, line_num, dummy_line):
return ' %s : %s ' % ( filename , line_num )
return ' %s : %s ' % ( filename , line_num )
def _FindNewViolationsOfRule ( callable_rule , input_api , source_file_filter = None ,
def _GenerateAffectedFileExtList ( input_api , source_file_filter ) :
error_formatter = _ReportErrorFileAndLine ) :
""" Generate a list of (file, extension) tuples from affected files.
The result can be fed to _FindNewViolationsOfRule ( ) directly , or
could be filtered before doing that .
Args :
input_api : object to enumerate the affected files .
source_file_filter : a filter to be passed to the input api .
Yields :
A list of ( file , extension ) tuples , where | file | is an affected
file , and | extension | its file path extension .
"""
for f in input_api . AffectedFiles (
include_deletes = False , file_filter = source_file_filter ) :
extension = str ( f . LocalPath ( ) ) . rsplit ( ' . ' , 1 ) [ - 1 ]
yield ( f , extension )
def _FindNewViolationsOfRuleForList ( callable_rule ,
file_ext_list ,
error_formatter = _ReportErrorFileAndLine ) :
""" Find all newly introduced violations of a per-line rule (a callable).
""" Find all newly introduced violations of a per-line rule (a callable).
Prefer calling _FindNewViolationsOfRule ( ) instead of this function , unless
the list of affected files need to be filtered in a special way .
Arguments :
Arguments :
callable_rule : a callable taking a file extension and line of input and
callable_rule : a callable taking a file extension and line of input and
returning True if the rule is satisfied and False if there was a problem .
returning True if the rule is satisfied and False if there was a problem .
input_api : object to enumerate the affected files .
file_ext_list: a list of input ( file , extension ) tuples , as returned by
source_file_filter : a filter to be passed to the input api .
_GenerateAffectedFileExtList ( ) .
error_formatter : a callable taking ( filename , line_number , line ) and
error_formatter : a callable taking ( filename , line_number , line ) and
returning a formatted error string .
returning a formatted error string .
@ -264,13 +287,11 @@ def _FindNewViolationsOfRule(callable_rule, input_api, source_file_filter=None,
A list of the newly - introduced violations reported by the rule .
A list of the newly - introduced violations reported by the rule .
"""
"""
errors = [ ]
errors = [ ]
for f in input_api . AffectedFiles ( include_deletes = False ,
for f , extension in file_ext_list :
file_filter = source_file_filter ) :
# For speed, we do two passes, checking first the full file. Shelling out
# For speed, we do two passes, checking first the full file. Shelling out
# to the SCM to determine the changed region can be quite expensive on
# to the SCM to determine the changed region can be quite expensive on
# Win32. Assuming that most files will be kept problem-free, we can
# Win32. Assuming that most files will be kept problem-free, we can
# skip the SCM operations most of the time.
# skip the SCM operations most of the time.
extension = str ( f . LocalPath ( ) ) . rsplit ( ' . ' , 1 ) [ - 1 ]
if all ( callable_rule ( extension , line ) for line in f . NewContents ( ) ) :
if all ( callable_rule ( extension , line ) for line in f . NewContents ( ) ) :
continue # No violation found in full text: can skip considering diff.
continue # No violation found in full text: can skip considering diff.
@ -281,6 +302,28 @@ def _FindNewViolationsOfRule(callable_rule, input_api, source_file_filter=None,
return errors
return errors
def _FindNewViolationsOfRule ( callable_rule ,
input_api ,
source_file_filter = None ,
error_formatter = _ReportErrorFileAndLine ) :
""" Find all newly introduced violations of a per-line rule (a callable).
Arguments :
callable_rule : a callable taking a file extension and line of input and
returning True if the rule is satisfied and False if there was a problem .
input_api : object to enumerate the affected files .
source_file_filter : a filter to be passed to the input api .
error_formatter : a callable taking ( filename , line_number , line ) and
returning a formatted error string .
Returns :
A list of the newly - introduced violations reported by the rule .
"""
return _FindNewViolationsOfRuleForList (
callable_rule , _GenerateAffectedFileExtList (
input_api , source_file_filter ) , error_formatter )
def CheckChangeHasNoTabs ( input_api , output_api , source_file_filter = None ) :
def CheckChangeHasNoTabs ( input_api , output_api , source_file_filter = None ) :
""" Checks that there are no tab characters in any of the text files to be
""" Checks that there are no tab characters in any of the text files to be
submitted .
submitted .
@ -385,11 +428,6 @@ def CheckLongLines(input_api, output_api, maxlen, source_file_filter=None):
if any ( ( url in line ) for url in ( ' file:// ' , ' http:// ' , ' https:// ' ) ) :
if any ( ( url in line ) for url in ( ' file:// ' , ' http:// ' , ' https:// ' ) ) :
return True
return True
# If 'line-too-long' is explicitly suppressed for the line, any length is
# acceptable.
if ' pylint: disable=line-too-long ' in line and file_extension == ' py ' :
return True
if line_len > extra_maxlen :
if line_len > extra_maxlen :
return False
return False
@ -402,12 +440,64 @@ def CheckLongLines(input_api, output_api, maxlen, source_file_filter=None):
return input_api . re . match (
return input_api . re . match (
r ' .*[A-Za-z][A-Za-z_0-9] { %d ,}.* ' % long_symbol , line )
r ' .*[A-Za-z][A-Za-z_0-9] { %d ,}.* ' % long_symbol , line )
def is_global_pylint_directive ( line , pos ) :
""" True iff the pylint directive starting at line[pos] is global. """
# Any character before |pos| that is not whitespace or '#' indidcates
# this is a local directive.
return not any ( [ c not in " \t # " for c in line [ : pos ] ] )
def check_python_long_lines ( affected_files , error_formatter ) :
errors = [ ]
global_check_enabled = True
for f in affected_files :
file_path = f . LocalPath ( )
for idx , line in enumerate ( f . NewContents ( ) ) :
line_num = idx + 1
line_is_short = no_long_lines ( PY_FILE_EXTS [ 0 ] , line )
pos = line . find ( ' pylint: disable=line-too-long ' )
if pos > = 0 :
if is_global_pylint_directive ( line , pos ) :
global_check_enabled = False # Global disable
else :
continue # Local disable.
do_check = global_check_enabled
pos = line . find ( ' pylint: enable=line-too-long ' )
if pos > = 0 :
if is_global_pylint_directive ( line , pos ) :
global_check_enabled = True # Global enable
do_check = True # Ensure it applies to current line as well.
else :
do_check = True # Local enable
if do_check and not line_is_short :
errors . append ( error_formatter ( file_path , line_num , line ) )
return errors
def format_error ( filename , line_num , line ) :
def format_error ( filename , line_num , line ) :
return ' %s , line %s , %s chars ' % ( filename , line_num , len ( line ) )
return ' %s , line %s , %s chars ' % ( filename , line_num , len ( line ) )
errors = _FindNewViolationsOfRule ( no_long_lines , input_api ,
file_ext_list = list (
source_file_filter ,
_GenerateAffectedFileExtList ( input_api , source_file_filter ) )
error_formatter = format_error )
errors = [ ]
# For non-Python files, a simple line-based rule check is enough.
non_py_file_ext_list = [ x for x in file_ext_list if x [ 1 ] not in PY_FILE_EXTS ]
if non_py_file_ext_list :
errors + = _FindNewViolationsOfRuleForList (
no_long_lines , non_py_file_ext_list , error_formatter = format_error )
# However, Python files need more sophisticated checks that need parsing
# the whole source file.
py_file_list = [ x [ 0 ] for x in file_ext_list if x [ 1 ] in PY_FILE_EXTS ]
if py_file_list :
errors + = check_python_long_lines (
py_file_list , error_formatter = format_error )
if errors :
if errors :
msg = ' Found lines longer than %s characters (first 5 shown). ' % maxlen
msg = ' Found lines longer than %s characters (first 5 shown). ' % maxlen
return [ output_api . PresubmitPromptWarning ( msg , items = errors [ : 5 ] ) ]
return [ output_api . PresubmitPromptWarning ( msg , items = errors [ : 5 ] ) ]