|
20 | 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21 | 21 | # THE SOFTWARE.
|
22 | 22 | import datetime
|
| 23 | +import json |
| 24 | +import pathlib |
23 | 25 | import re
|
| 26 | +from tempfile import TemporaryDirectory |
| 27 | + |
| 28 | +from pylint import epylint as linter |
24 | 29 |
|
25 | 30 | import requests
|
26 | 31 |
|
| 32 | +import sh |
| 33 | +from sh.contrib import git |
| 34 | + |
27 | 35 | from adabot import github_requests as github
|
28 | 36 | from adabot import pypi_requests as pypi
|
29 | 37 | from adabot.lib import common_funcs
|
|
99 | 107 | ERROR_NOT_ON_PYPI = "Not listed on PyPi for CPython use"
|
100 | 108 | ERROR_PYLINT_VERSION_NOT_FIXED = "PyLint version not fixed"
|
101 | 109 | ERROR_PYLINT_VERSION_NOT_LATEST = "PyLint version not latest"
|
| 110 | +ERROR_PYLINT_FAILED_LINTING = "Failed PyLint checks" |
102 | 111 | ERROR_NEW_REPO_IN_WORK = "New repo(s) currently in work, and unreleased"
|
103 | 112 |
|
104 | 113 | # Temp category for GitHub Actions migration.
|
@@ -1041,3 +1050,51 @@ def validate_labels(self, repo):
|
1041 | 1050 | errors.append(ERROR_MISSING_STANDARD_LABELS)
|
1042 | 1051 |
|
1043 | 1052 | return errors
|
| 1053 | + |
| 1054 | + def validate_passes_linting(self, repo): |
| 1055 | + """ Clones the repo and runs pylint on the Python files""" |
| 1056 | + if not repo["name"].startswith("Adafruit_CircuitPython"): |
| 1057 | + return [] |
| 1058 | + |
| 1059 | + ignored_py_files = ["setup.py", "conf.py"] |
| 1060 | + |
| 1061 | + with TemporaryDirectory() as tempdir: |
| 1062 | + repo_dir = pathlib.Path(tempdir) / repo["name"] |
| 1063 | + try: |
| 1064 | + git.clone("--depth=1", repo["git_url"], repo_dir) |
| 1065 | + except sh.ErrorReturnCode as err: |
| 1066 | + self.output_file_data.append( |
| 1067 | + f"Failed to clone repo for linting: {repo['full_name']}\n {err.stderr}" |
| 1068 | + ) |
| 1069 | + return [ERROR_OUTPUT_HANDLER] |
| 1070 | + |
| 1071 | + for file in repo_dir.rglob("*.py"): |
| 1072 | + if not file.name in ignored_py_files and not str(file.parent).endswith("examples"): |
| 1073 | + py_run_args = f"{file} --output-format=json" |
| 1074 | + if (repo_dir / '.pylintrc').exists(): |
| 1075 | + py_run_args += ( |
| 1076 | + f" --rcfile={str(repo_dir / '.pylintrc')}" |
| 1077 | + ) |
| 1078 | + |
| 1079 | + pylint_stdout, pylint_stderr = linter.py_run( |
| 1080 | + py_run_args, |
| 1081 | + return_std=True |
| 1082 | + ) |
| 1083 | + |
| 1084 | + if pylint_stderr.getvalue(): |
| 1085 | + self.output_file_data.append( |
| 1086 | + f"PyLint error ({repo['name']}): '{pylint_stderr.getvalue()}'" |
| 1087 | + ) |
| 1088 | + return [ERROR_OUTPUT_HANDLER] |
| 1089 | + |
| 1090 | + try: |
| 1091 | + pylint_result = json.loads(pylint_stdout.getvalue()) |
| 1092 | + except json.JSONDecodeError as json_err: |
| 1093 | + self.output_file_data.append( |
| 1094 | + f"PyLint output JSONDecodeError: {json_err.msg}" |
| 1095 | + ) |
| 1096 | + return [ERROR_OUTPUT_HANDLER] |
| 1097 | + |
| 1098 | + if pylint_result: |
| 1099 | + return [ERROR_PYLINT_FAILED_LINTING] |
| 1100 | + return [] |
0 commit comments