<commit_before># Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.


"""Presubmit checks for the buildbot code."""


import subprocess


def _MakeFileFilter(input_api, include_extensions=None,
                    exclude_extensions=None):
  """Return a filter to pass to AffectedSourceFiles.

  The filter will include all files with a file extension in include_extensions,
  and will ignore all files with a file extension in exclude_extensions.

  If include_extensions is empty, all files, even those without any extension,
  are included.
  """
  white_list = [input_api.re.compile(r'.+')]
  if include_extensions:
    white_list = [input_api.re.compile(r'.+\.%s$' % ext)
                  for ext in include_extensions]
  # If black_list is empty, the InputApi default is used, so always include at
  # least one regexp.
  black_list = [input_api.re.compile(r'^$')]
  if exclude_extensions:
    black_list = [input_api.re.compile(r'.+\.%s$' % ext)
                  for ext in exclude_extensions]
  return lambda x: input_api.FilterSourceFile(x, white_list=white_list,
                                              black_list=black_list)

def _CheckNonAscii(input_api, output_api):
  """Check for non-ASCII characters and throw warnings if any are found."""
  results = []
  files_with_unicode_lines = []
  # We keep track of the longest file (in line count) so that we can pad the
  # numbers when displaying output. This makes it easier to see the indention.
  max_lines_in_any_file = 0
  FILE_EXTENSIONS = ['bat', 'cfg', 'cmd', 'conf', 'css', 'gyp', 'gypi', 'htm',
                     'html', 'js', 'json', 'ps1', 'py', 'sh', 'tac', 'yaml']
  file_filter = _MakeFileFilter(input_api, FILE_EXTENSIONS)
  for affected_file in input_api.AffectedSourceFiles(file_filter):
    affected_filepath = affected_file.LocalPath()
    unicode_lines = []
    with open(affected_filepath, 'r+b') as f:
      total_lines = 0
      for line in f:
        total_lines += 1
        try:
          line.decode('ascii')
        except UnicodeDecodeError:
          unicode_lines.append((total_lines, line.rstrip()))
    if unicode_lines:
      files_with_unicode_lines.append((affected_filepath, unicode_lines))
      if total_lines > max_lines_in_any_file:
        max_lines_in_any_file = total_lines

  if files_with_unicode_lines:
    padding = len(str(max_lines_in_any_file))
    long_text = 'The following files contain non-ASCII characters:\n'
    for filename, unicode_lines in files_with_unicode_lines:
      long_text += '  %s\n' % filename
      for line_num, line in unicode_lines:
        long_text += '    %s: %s\n' % (str(line_num).rjust(padding), line)
      long_text += '\n'
    results.append(output_api.PresubmitPromptWarning(
        message='Some files contain non-ASCII characters.',
        long_text=long_text))

  return results


def _CheckBannedGoAPIs(input_api, output_api):
  """Check go source code for functions and packages that should not be used."""
  # TODO(benjaminwagner): A textual search is easy, but it would be more
  #   accurate to parse and analyze the source due to package aliases.
  # A list of tuples of a regex to match an API and a suggested replacement for
  # that API.
  banned_replacements = [
    (r'\breflect\.DeepEqual\b', 'DeepEqual in go.skia.org/infra/go/testutils'),
    (r'\bgithub\.com/golang/glog\b', 'go.skia.org/infra/go/sklog'),
    (r'\bgithub\.com/skia-dev/glog\b', 'go.skia.org/infra/go/sklog'),
    (r'\bhttp\.Get\b', 'NewTimeoutClient in go.skia.org/infra/go/httputils'),
    (r'\bhttp\.Head\b', 'NewTimeoutClient in go.skia.org/infra/go/httputils'),
    (r'\bhttp\.Post\b', 'NewTimeoutClient in go.skia.org/infra/go/httputils'),
    (r'\bhttp\.PostForm\b',
        'NewTimeoutClient in go.skia.org/infra/go/httputils'),
    (r'\bos\.Interrupt\b', 'AtExit in go.skia.org/go/cleanup'),
    (r'\bsignal\.Notify\b', 'AtExit in go.skia.org/go/cleanup'),
    (r'\bsyscall.SIGINT\b', 'AtExit in go.skia.org/go/cleanup'),
    (r'\bsyscall.SIGTERM\b', 'AtExit in go.skia.org/go/cleanup'),
  ]

  compiled_replacements = []
  for (re, replacement) in banned_replacements:
    compiled_re = input_api.re.compile(re)
    compiled_replacements.append((compiled_re, replacement))

  errors = []
  file_filter = _MakeFileFilter(input_api, ['go'])
  for affected_file in input_api.AffectedSourceFiles(file_filter):
    affected_filepath = affected_file.LocalPath()
    for (line_num, line) in affected_file.ChangedContents():
      for (re, replacement) in compiled_replacements:
        match = re.search(line)
        if match:
          errors.append('%s:%s: Instead of %s, please use %s.' % (
              affected_filepath, line_num, match.group(), replacement))

  if errors:
    return [output_api.PresubmitPromptWarning('\n'.join(errors))]

  return []


def CheckChange(input_api, output_api):
  """Presubmit checks for the change on upload or commit.

  The presubmit checks have been handpicked from the list of canned checks
  here:
  https://chromium.googlesource.com/chromium/tools/depot_tools/+/master/presubmit_canned_checks.py

  The following are the presubmit checks:
  * Pylint is run if the change contains any .py files.
  * Enforces max length for all lines is 100.
  * Checks that the user didn't add TODO(name) without an owner.
  * Checks that there is no stray whitespace at source lines end.
  * Checks that there are no tab characters in any of the text files.
  """
  results = []

  pylint_disabled_warnings = (
      'F0401',  # Unable to import.
      'E0611',  # No name in module.
      'W0232',  # Class has no __init__ method.
      'E1002',  # Use of super on an old style class.
      'W0403',  # Relative import used.
      'R0201',  # Method could be a function.
      'E1003',  # Using class name in super.
      'W0613',  # Unused argument.
  )
  # Run Pylint on only the modified python files. Unfortunately it still runs
  # Pylint on the whole file instead of just the modified lines.
  file_filter = _MakeFileFilter(input_api, ['py'])
  affected_python_files = (f.LocalPath()
                           for f in input_api.AffectedSourceFiles(file_filter))<commit_msg>Skip pylint for recipes.py in presubmit<commit_after># Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.


"""Presubmit checks for the buildbot code."""


import subprocess


def _MakeFileFilter(input_api, include_extensions=None,
                    exclude_extensions=None):
  """Return a filter to pass to AffectedSourceFiles.

  The filter will include all files with a file extension in include_extensions,
  and will ignore all files with a file extension in exclude_extensions.

  If include_extensions is empty, all files, even those without any extension,
  are included.
  """
  white_list = [input_api.re.compile(r'.+')]
  if include_extensions:
    white_list = [input_api.re.compile(r'.+\.%s$' % ext)
                  for ext in include_extensions]
  # If black_list is empty, the InputApi default is used, so always include at
  # least one regexp.
  black_list = [input_api.re.compile(r'^$')]
  if exclude_extensions:
    black_list = [input_api.re.compile(r'.+\.%s$' % ext)
                  for ext in exclude_extensions]
  return lambda x: input_api.FilterSourceFile(x, white_list=white_list,
                                              black_list=black_list)

def _CheckNonAscii(input_api, output_api):
  """Check for non-ASCII characters and throw warnings if any are found."""
  results = []
  files_with_unicode_lines = []
  # We keep track of the longest file (in line count) so that we can pad the
  # numbers when displaying output. This makes it easier to see the indention.
  max_lines_in_any_file = 0
  FILE_EXTENSIONS = ['bat', 'cfg', 'cmd', 'conf', 'css', 'gyp', 'gypi', 'htm',
                     'html', 'js', 'json', 'ps1', 'py', 'sh', 'tac', 'yaml']
  file_filter = _MakeFileFilter(input_api, FILE_EXTENSIONS)
  for affected_file in input_api.AffectedSourceFiles(file_filter):
    affected_filepath = affected_file.LocalPath()
    unicode_lines = []
    with open(affected_filepath, 'r+b') as f:
      total_lines = 0
      for line in f:
        total_lines += 1
        try:
          line.decode('ascii')
        except UnicodeDecodeError:
          unicode_lines.append((total_lines, line.rstrip()))
    if unicode_lines:
      files_with_unicode_lines.append((affected_filepath, unicode_lines))
      if total_lines > max_lines_in_any_file:
        max_lines_in_any_file = total_lines

  if files_with_unicode_lines:
    padding = len(str(max_lines_in_any_file))
    long_text = 'The following files contain non-ASCII characters:\n'
    for filename, unicode_lines in files_with_unicode_lines:
      long_text += '  %s\n' % filename
      for line_num, line in unicode_lines:
        long_text += '    %s: %s\n' % (str(line_num).rjust(padding), line)
      long_text += '\n'
    results.append(output_api.PresubmitPromptWarning(
        message='Some files contain non-ASCII characters.',
        long_text=long_text))

  return results


def _CheckBannedGoAPIs(input_api, output_api):
  """Check go source code for functions and packages that should not be used."""
  # TODO(benjaminwagner): A textual search is easy, but it would be more
  #   accurate to parse and analyze the source due to package aliases.
  # A list of tuples of a regex to match an API and a suggested replacement for
  # that API.
  banned_replacements = [
    (r'\breflect\.DeepEqual\b', 'DeepEqual in go.skia.org/infra/go/testutils'),
    (r'\bgithub\.com/golang/glog\b', 'go.skia.org/infra/go/sklog'),
    (r'\bgithub\.com/skia-dev/glog\b', 'go.skia.org/infra/go/sklog'),
    (r'\bhttp\.Get\b', 'NewTimeoutClient in go.skia.org/infra/go/httputils'),
    (r'\bhttp\.Head\b', 'NewTimeoutClient in go.skia.org/infra/go/httputils'),
    (r'\bhttp\.Post\b', 'NewTimeoutClient in go.skia.org/infra/go/httputils'),
    (r'\bhttp\.PostForm\b',
        'NewTimeoutClient in go.skia.org/infra/go/httputils'),
    (r'\bos\.Interrupt\b', 'AtExit in go.skia.org/go/cleanup'),
    (r'\bsignal\.Notify\b', 'AtExit in go.skia.org/go/cleanup'),
    (r'\bsyscall.SIGINT\b', 'AtExit in go.skia.org/go/cleanup'),
    (r'\bsyscall.SIGTERM\b', 'AtExit in go.skia.org/go/cleanup'),
  ]

  compiled_replacements = []
  for (re, replacement) in banned_replacements:
    compiled_re = input_api.re.compile(re)
    compiled_replacements.append((compiled_re, replacement))

  errors = []
  file_filter = _MakeFileFilter(input_api, ['go'])
  for affected_file in input_api.AffectedSourceFiles(file_filter):
    affected_filepath = affected_file.LocalPath()
    for (line_num, line) in affected_file.ChangedContents():
      for (re, replacement) in compiled_replacements:
        match = re.search(line)
        if match:
          errors.append('%s:%s: Instead of %s, please use %s.' % (
              affected_filepath, line_num, match.group(), replacement))

  if errors:
    return [output_api.PresubmitPromptWarning('\n'.join(errors))]

  return []


def CheckChange(input_api, output_api):
  """Presubmit checks for the change on upload or commit.

  The presubmit checks have been handpicked from the list of canned checks
  here:
  https://chromium.googlesource.com/chromium/tools/depot_tools/+/master/presubmit_canned_checks.py

  The following are the presubmit checks:
  * Pylint is run if the change contains any .py files.
  * Enforces max length for all lines is 100.
  * Checks that the user didn't add TODO(name) without an owner.
  * Checks that there is no stray whitespace at source lines end.
  * Checks that there are no tab characters in any of the text files.
  """
  results = []

  pylint_disabled_files = (
      'infra/bots/recipes.py',
  )
  pylint_disabled_warnings = (
      'F0401',  # Unable to import.
      'E0611',  # No name in module.
      'W0232',  # Class has no __init__ method.
      'E1002',  # Use of super on an old style class.
      'W0403',  # Relative import used.
      'R0201',  # Method could be a function.
      'E1003',  # Using class name in super.
      'W0613',  # Unused argument.
  )
  # Run Pylint on only the modified python files. Unfortunately it still runs
  # Pylint on the whole file instead of just the modified lines.
  file_filter = _MakeFileFilter(input_api, ['py'])
  affected_python_files = (
    f.LocalPath() for f in input_api.AffectedSourceFiles(file_filter)
    if f.LocalPath() not in pylint_disabled_files)
{'fraction_non_alphanumeric': 0.11140939597315436, 'fraction_numerical': 0.006790367153572839, 'mean_word_length': 3.3066984019041143, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1295198', 'n_tokens_mistral': 4249, 'n_tokens_neox': 4023, 'n_words': 1165}