mirror of
https://github.com/saymrwulf/pytorch.git
synced 2026-05-14 20:57:59 +00:00
Summary: Pull Request resolved: https://github.com/pytorch/pytorch/pull/58798 In #58623 there was a bug in `make quicklint` where ShellCheck would run on the entire repo when there were no files. This PR fixes that by refactoring out common stuff (like skipping quicklint when there are no files, let checks do their own file filtering) and pushes the logic into a runner class. Test Plan: Imported from OSS Reviewed By: samestep Differential Revision: D28649889 Pulled By: driazati fbshipit-source-id: b19f32cdb63396c806cb689b2f6daf97e1724d44
191 lines
7.4 KiB
Python
191 lines
7.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import textwrap
|
|
import unittest
|
|
import sys
|
|
import contextlib
|
|
import io
|
|
import os
|
|
import subprocess
|
|
import multiprocessing
|
|
from typing import List, Dict, Any
|
|
|
|
from tools import actions_local_runner
|
|
|
|
|
|
if sys.version_info >= (3, 8):
|
|
# actions_local_runner uses asyncio features not available in 3.6, and
|
|
# IsolatedAsyncioTestCase was added in 3.8, so skip testing on
|
|
# unsupported systems
|
|
class TestRunner(unittest.IsolatedAsyncioTestCase):
|
|
def run(self, *args: List[Any], **kwargs: List[Dict[str, Any]]) -> Any:
|
|
return super().run(*args, **kwargs)
|
|
|
|
def test_step_extraction(self) -> None:
|
|
fake_job = {
|
|
"steps": [
|
|
{"name": "test1", "run": "echo hi"},
|
|
{"name": "test2", "run": "echo hi"},
|
|
{"name": "test3", "run": "echo hi"},
|
|
]
|
|
}
|
|
|
|
actual = actions_local_runner.grab_specific_steps(["test2"], fake_job)
|
|
expected = [
|
|
{"name": "test2", "run": "echo hi"},
|
|
]
|
|
self.assertEqual(actual, expected)
|
|
|
|
async def test_runner(self) -> None:
|
|
fake_step = {"name": "say hello", "run": "echo hi"}
|
|
f = io.StringIO()
|
|
with contextlib.redirect_stdout(f):
|
|
await actions_local_runner.YamlStep(fake_step, "test", True).run()
|
|
|
|
result = f.getvalue()
|
|
self.assertIn("say hello", result)
|
|
|
|
class TestEndToEnd(unittest.TestCase):
|
|
expected = [
|
|
"cmakelint: Run cmakelint",
|
|
"quick-checks: Ensure no direct cub include",
|
|
"quick-checks: Ensure no unqualified type ignore",
|
|
"quick-checks: Ensure no unqualified noqa",
|
|
"quick-checks: Ensure canonical include",
|
|
"quick-checks: Ensure no non-breaking spaces",
|
|
"quick-checks: Ensure no tabs",
|
|
"flake8",
|
|
"quick-checks: Ensure correct trailing newlines",
|
|
"quick-checks: Ensure no trailing spaces",
|
|
"shellcheck: Regenerate workflows",
|
|
"shellcheck: Assert that regenerating the workflows didn't change them",
|
|
"shellcheck: Extract scripts from GitHub Actions workflows",
|
|
"shellcheck: Run ShellCheck",
|
|
]
|
|
|
|
def test_lint(self):
|
|
cmd = ["make", "lint", "-j", str(multiprocessing.cpu_count())]
|
|
proc = subprocess.run(
|
|
cmd, cwd=actions_local_runner.REPO_ROOT, stdout=subprocess.PIPE
|
|
)
|
|
stdout = proc.stdout.decode()
|
|
|
|
for line in self.expected:
|
|
self.assertIn(line, stdout)
|
|
|
|
self.assertIn("mypy", stdout)
|
|
|
|
def test_quicklint(self):
|
|
cmd = ["make", "quicklint", "-j", str(multiprocessing.cpu_count())]
|
|
proc = subprocess.run(
|
|
cmd, cwd=actions_local_runner.REPO_ROOT, stdout=subprocess.PIPE
|
|
)
|
|
stdout = proc.stdout.decode()
|
|
|
|
for line in self.expected:
|
|
self.assertIn(line, stdout)
|
|
|
|
# TODO: See https://github.com/pytorch/pytorch/issues/57967
|
|
self.assertIn("mypy (skipped typestub generation)", stdout)
|
|
|
|
class TestQuicklint(unittest.IsolatedAsyncioTestCase):
|
|
test_files = [
|
|
os.path.join("caffe2", "some_cool_file.py"),
|
|
os.path.join("torch", "some_cool_file.py"),
|
|
os.path.join("aten", "some_cool_file.py"),
|
|
os.path.join("torch", "some_stubs.pyi"),
|
|
os.path.join("test.sh"),
|
|
]
|
|
test_py_files = [
|
|
f for f in test_files if f.endswith(".py") or f.endswith(".pyi")
|
|
]
|
|
test_sh_files = [f for f in test_files if f.endswith(".sh")]
|
|
maxDiff = None
|
|
|
|
def setUp(self, *args, **kwargs):
|
|
for name in self.test_files:
|
|
bad_code = textwrap.dedent(
|
|
"""
|
|
some_variable = '2'
|
|
some_variable = None
|
|
some_variable = 11.2
|
|
"""
|
|
).rstrip("\n")
|
|
|
|
with open(name, "w") as f:
|
|
f.write(bad_code)
|
|
|
|
def tearDown(self, *args, **kwargs):
|
|
for name in self.test_files:
|
|
os.remove(name)
|
|
|
|
def test_file_selection(self):
|
|
files = actions_local_runner.find_changed_files()
|
|
for name in self.test_files:
|
|
self.assertIn(name, files)
|
|
|
|
async def test_flake8(self):
|
|
f = io.StringIO()
|
|
with contextlib.redirect_stdout(f):
|
|
await actions_local_runner.Flake8(self.test_py_files, True).run()
|
|
|
|
# Should exclude the caffe2/ file
|
|
expected = textwrap.dedent(
|
|
"""
|
|
x flake8
|
|
torch/some_cool_file.py:4:21: W292 no newline at end of file
|
|
aten/some_cool_file.py:4:21: W292 no newline at end of file
|
|
"""
|
|
).lstrip("\n")
|
|
self.assertEqual(expected, f.getvalue())
|
|
|
|
async def test_shellcheck(self):
|
|
f = io.StringIO()
|
|
with contextlib.redirect_stdout(f):
|
|
await actions_local_runner.ShellCheck(self.test_sh_files, True).run()
|
|
|
|
self.assertIn("SC2148: Tips depend on target shell", f.getvalue())
|
|
self.assertIn("SC2283: Remove spaces around = to assign", f.getvalue())
|
|
|
|
async def test_mypy(self):
|
|
self.maxDiff = None
|
|
f = io.StringIO()
|
|
with contextlib.redirect_stdout(f):
|
|
# Quicklint assumes this has been run already and doesn't work
|
|
# without it
|
|
_, _, _ = await actions_local_runner.shell_cmd(
|
|
[
|
|
f"{sys.executable}",
|
|
"tools/actions_local_runner.py",
|
|
"--job",
|
|
"mypy",
|
|
"--file",
|
|
".github/workflows/lint.yml",
|
|
"--step",
|
|
"Run autogen",
|
|
],
|
|
redirect=True,
|
|
)
|
|
|
|
await actions_local_runner.Mypy(self.test_py_files, True).run()
|
|
|
|
# Should exclude the aten/ file; also, apparently mypy
|
|
# typechecks files in reverse order
|
|
expected = textwrap.dedent(
|
|
"""
|
|
x mypy (skipped typestub generation)
|
|
torch/some_stubs.pyi:3:17: error: Incompatible types in assignment (expression has type "None", variable has type "str") [assignment]
|
|
torch/some_stubs.pyi:4:17: error: Incompatible types in assignment (expression has type "float", variable has type "str") [assignment]
|
|
torch/some_cool_file.py:3:17: error: Incompatible types in assignment (expression has type "None", variable has type "str") [assignment]
|
|
torch/some_cool_file.py:4:17: error: Incompatible types in assignment (expression has type "float", variable has type "str") [assignment]
|
|
caffe2/some_cool_file.py:3:17: error: Incompatible types in assignment (expression has type "None", variable has type "str") [assignment]
|
|
caffe2/some_cool_file.py:4:17: error: Incompatible types in assignment (expression has type "float", variable has type "str") [assignment]
|
|
""" # noqa: B950
|
|
).lstrip(
|
|
"\n"
|
|
)
|
|
self.assertEqual(expected, f.getvalue())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|