pylint2: drop Python 2 support

Distros aren't carrying Python 2 anymore, let alone pylint that works
with Python 2 code, so drop the pylint2 builtin.  This will let us
update the pylint configs to assume newer versions.

Bug: 382098607
Test: unittests
Change-Id: Ic0d2a2c11b6d71bd07615924e84223b1f549d371
Reviewed-on: https://android-review.googlesource.com/c/platform/tools/repohooks/+/3427939
Performance: CrystalBall Performance Presubmit <[email protected]>
Presubmit-Verified: Treehugger Robot <[email protected]>
Open-Source-Licensing: Lint 🤖 <[email protected]>
Reviewed-by: Greg Edelston <[email protected]>
Lint: Lint 🤖 <[email protected]>
diff --git a/README.md b/README.md
index 5cfb289..9747c5d 100644
--- a/README.md
+++ b/README.md
@@ -207,7 +207,7 @@
   --include-dirs, which if specified will limit enforcement to only files under
   the specified directories.
 * `pylint`: Alias of `pylint3`.
-* `pylint2`: Run Python code through `pylint` using Python 2.
+* `pylint2`: Ignored for compatibility with old configs.
 * `pylint3`: Run Python code through `pylint` using Python 3.
 * `rustfmt`: Run Rust code through `rustfmt`.
 * `xmllint`: Run XML code through `xmllint`.
diff --git a/rh/hooks.py b/rh/hooks.py
index e31cc48..ec8b69c 100644
--- a/rh/hooks.py
+++ b/rh/hooks.py
@@ -975,15 +975,22 @@
 
 
 def check_pylint2(project, commit, desc, diff, options=None):
-    """Run pylint through Python 2."""
-    return _check_pylint(project, commit, desc, diff, options=options)
+    """Run pylint through Python 2.
+
+    This hook is not supported anymore, but we keep it registered to avoid
+    breaking in older branches with old configs that still have it.
+    """
+    del desc, diff, options
+    return [rh.results.HookResult(
+        'pylint2', project, commit,
+        ('The pylint2 check is no longer supported.  '
+         'Please delete from PREUPLOAD.cfg.'),
+        warning=True)]
 
 
 def check_pylint3(project, commit, desc, diff, options=None):
     """Run pylint through Python 3."""
-    return _check_pylint(project, commit, desc, diff,
-                         extra_args=['--py3'],
-                         options=options)
+    return _check_pylint(project, commit, desc, diff, options=options)
 
 
 def check_rustfmt(project, commit, _desc, diff, options=None):
diff --git a/rh/hooks_unittest.py b/rh/hooks_unittest.py
index a54e24f..bed3745 100755
--- a/rh/hooks_unittest.py
+++ b/rh/hooks_unittest.py
@@ -911,13 +911,15 @@
 
     def test_pylint(self, mock_check, _mock_run):
         """Verify the pylint builtin hook."""
-        self._test_file_filter(mock_check, rh.hooks.check_pylint2,
+        self._test_file_filter(mock_check, rh.hooks.check_pylint3,
                                ('foo.py',))
 
     def test_pylint2(self, mock_check, _mock_run):
         """Verify the pylint2 builtin hook."""
-        self._test_file_filter(mock_check, rh.hooks.check_pylint2,
-                               ('foo.py',))
+        ret = rh.hooks.check_pylint2(
+            self.project, 'commit', 'desc', (), options=self.options)
+        self.assertEqual(len(ret), 1)
+        self.assertTrue(ret[0].is_warning())
 
     def test_pylint3(self, mock_check, _mock_run):
         """Verify the pylint3 builtin hook."""
diff --git a/rh/results.py b/rh/results.py
index 65e0052..236387e 100644
--- a/rh/results.py
+++ b/rh/results.py
@@ -27,8 +27,16 @@
 class HookResult(object):
     """A single hook result."""
 
-    def __init__(self, hook, project, commit, error, files=(),
-                 fixup_cmd: Optional[List[str]] = None):
+    def __init__(
+        self,
+        hook,
+        project,
+        commit,
+        error,
+        warning: bool = False,
+        files=(),
+        fixup_cmd: Optional[List[str]] = None,
+    ):
         """Initialize.
 
         Args:
@@ -37,6 +45,7 @@
           commit: The git commit sha.
           error: A string representation of the hook's result.  Empty on
               success.
+          warning: Whether this result is a warning, not an error.
           files: The list of files that were involved in the hook execution.
           fixup_cmd: A command that can automatically fix errors found in the
               hook's execution.  Can be None if the hook does not support
@@ -46,16 +55,17 @@
         self.project = project
         self.commit = commit
         self.error = error
+        self._warning = warning
         self.files = files
         self.fixup_cmd = fixup_cmd
 
     def __bool__(self):
         """Whether this result is an error."""
-        return bool(self.error)
+        return bool(self.error) and not self._warning
 
     def is_warning(self):
         """Whether this result is a non-fatal warning."""
-        return False
+        return self._warning
 
 
 class HookCommandResult(HookResult):
diff --git a/tools/pylint.py b/tools/pylint.py
index d692d83..dff0007 100755
--- a/tools/pylint.py
+++ b/tools/pylint.py
@@ -32,37 +32,6 @@
     os.path.dirname(os.path.realpath(__file__)), 'pylintrc')
 
 
-def is_pylint3(pylint):
-    """See whether |pylint| supports Python 3."""
-    # Make sure pylint is using Python 3.
-    result = subprocess.run([pylint, '--version'], stdout=subprocess.PIPE,
-                            check=True)
-    if b'Python 3' not in result.stdout:
-        print(f'{__file__}: unable to locate a Python 3 version of pylint; '
-              'Python 3 support cannot be guaranteed', file=sys.stderr)
-        return False
-
-    return True
-
-
-def find_pylint3():
-    """Figure out the name of the pylint tool for Python 3.
-
-    It keeps changing with Python 2->3 migrations.  Fun.
-    """
-    # Prefer pylint3 as that's what we want.
-    if shutil.which('pylint3'):
-        return 'pylint3'
-
-    # If there's no pylint, give up.
-    if not shutil.which('pylint'):
-        print(f'{__file__}: unable to locate pylint; please install:\n'
-              'sudo apt-get install pylint', file=sys.stderr)
-        sys.exit(1)
-
-    return 'pylint'
-
-
 def run_lint(pylint: str, unknown: Optional[List[str]],
              files: Optional[List[str]], init_hook: str,
              pylintrc: Optional[str] = None) -> bool:
@@ -181,9 +150,7 @@
     """Return a command line parser."""
     parser = argparse.ArgumentParser(description=__doc__)
     parser.add_argument('--init-hook', help='Init hook commands to run.')
-    parser.add_argument('--py3', action='store_true',
-                        help='Force Python 3 mode')
-    parser.add_argument('--executable-path',
+    parser.add_argument('--executable-path', default='pylint',
                         help='The path of the pylint executable.')
     parser.add_argument('--no-rcfile', dest='use_default_conf',
                         help='Specify to use the executable\'s default '
@@ -200,16 +167,6 @@
     ret = 0
 
     pylint = opts.executable_path
-    if pylint is None:
-        if opts.py3:
-            pylint = find_pylint3()
-        else:
-            pylint = 'pylint'
-
-    # Make sure pylint is using Python 3.
-    if opts.py3:
-        is_pylint3(pylint)
-
     if not opts.use_default_conf:
         pylintrc_map = map_pyfiles_to_pylintrc(opts.files)
         first = True