diff --git a/.github/scripts/convert_lintrunner_annotations_to_github.py b/.github/scripts/convert_lintrunner_annotations_to_github.py new file mode 100644 index 000000000000..11901bc300e3 --- /dev/null +++ b/.github/scripts/convert_lintrunner_annotations_to_github.py @@ -0,0 +1,63 @@ +import json +import subprocess +import sys + +from enum import Enum +from pathlib import Path +from typing import NamedTuple, Optional + +# From: https://docs.github.com/en/rest/reference/checks +class GitHubAnnotationLevel(str, Enum): + NOTICE = "notice" + WARNING = "warning" + FAILURE = "failure" + + +class GitHubAnnotation(NamedTuple): + path: str + start_line: int + end_line: int + start_column: Optional[int] + end_column: Optional[int] + annotation_level: GitHubAnnotationLevel + message: str + title: Optional[str] + raw_details: Optional[str] + +PYTORCH_ROOT = Path(subprocess.check_output(['git', 'rev-parse', '--show-toplevel']).decode('ascii').strip()) + +annotations = [] +for line in sys.stdin: + lint_message = json.loads(line) + + path = lint_message.get("path") + line = lint_message.get("line") + + + code = lint_message["code"] + severity = lint_message["severity"] + name = lint_message["name"] + description = lint_message.get("description") + + # These fields are required by the GitHub API, but optional in lintrunner. + # If they don't exist, just skip. + if path is None or line is None: + print(f"No path/line for lint: ({code}) {name}", file=sys.stderr) + continue + + # normalize path relative to git root + path = Path(path).relative_to(PYTORCH_ROOT) + + annotations.append(GitHubAnnotation( + path=str(path), + start_line=int(line), + end_line=int(line), + start_column=None, + end_column=None, + annotation_level=GitHubAnnotationLevel.FAILURE, + message=description, + title=f"({code}) {name}", + raw_details=None, + )._asdict()) + +print(json.dumps(annotations), flush=True) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 228cd84d88d9..6a4dbbf68a92 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,6 +7,72 @@ on: pull_request: jobs: + lintrunner: + runs-on: ubuntu-18.04 + steps: + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.8 + architecture: x64 + + - name: Checkout PyTorch + uses: pytorch/pytorch/.github/actions/checkout-pytorch@master + with: + submodules: false + + - name: Install lintrunner + run: pip install lintrunner==0.5.* + + - name: Initialize lint dependencies + run: lintrunner init + + - name: Do build steps necessary for linters + run: | + python3 -m tools.linter.clang_tidy.generate_build_files + python3 -m tools.generate_torch_version --is_debug=false + python3 -m tools.pyi.gen_pyi \ + --native-functions-path aten/src/ATen/native/native_functions.yaml \ + --deprecated-functions-path "tools/autograd/deprecated.yaml" + + - name: Run lintrunner on all files + if: github.event_name == 'push' + run: lintrunner -vv --paths-cmd='git grep -Il .' --force-color + + - name: Run lintrunner on PR files + if: github.event_name == 'pull_request' + env: + PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + MERGE_BASE=$(git merge-base "$PR_BASE_SHA" "$PR_HEAD_SHA") + lintrunner -vv --force-color --revision "${MERGE_BASE}" + echo "" + echo -e "\e[1m\e[36mYou can reproduce these results locally by using \`lintrunner\`.\e[0m" + echo -e "\e[1m\e[36mSee https://github.com/pytorch/pytorch/wiki/lintrunner for setup instructions.\e[0m" + + - name: Store annotations + # Don't run on forked pull requests + if: failure() && github.event.pull_request.head.repo.full_name == github.repository + run: | + lintrunner --json \ + | python .github/scripts/convert_lintrunner_annotations_to_github.py \ + > annotations.json + + cat annotations.json + + - name: Add annotations + # Don't run on forked pull requests + if: failure() && github.event.pull_request.head.repo.full_name == github.repository + uses: pytorch/add-annotations-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + check_name: 'lintrunner' + linter_output_path: annotations.json + commit_sha: ${{ github.event.pull_request.head.sha }} + mode: json + quick-checks: name: quick-checks runs-on: ubuntu-18.04 @@ -28,70 +94,16 @@ jobs: - name: Install requirements id: requirements run: pip3 install -r requirements.txt --user - - name: Ensure consistent CircleCI YAML config - if: ${{ always() && steps.requirements.outcome == 'success' }} - run: cd .circleci && ./ensure-consistency.py - - name: Lint native_functions.yaml - if: ${{ always() && steps.requirements.outcome == 'success' }} - run: | - pip3 install ruamel.yaml==0.17.4 --user - .github/scripts/lint_native_functions.py - - name: Ensure correct trailing newlines - if: ${{ always() && steps.requirements.outcome == 'success' }} - run: | - (! git --no-pager grep -Il '' -- . ':(exclude)**/contrib/**' ':(exclude)third_party' ':(exclude)**.expect' ':(exclude)**.ipynb' ':(exclude)tools/clang_format_hash' | tools/linter/trailing_newlines.py || (echo "The above files do not have correct trailing newlines; please normalize them"; false)) - - name: Ensure no trailing spaces - if: always() - run: | - (! git --no-pager grep -In '[[:blank:]]$' -- . ':(exclude)**/contrib/**' ':(exclude)**.diff' ':(exclude)third_party' || (echo "The above lines have trailing spaces; please remove them"; false)) - - name: Ensure no tabs - if: always() - run: | - (! git --no-pager grep -In $'\t' -- . ':(exclude)*.svg' ':(exclude)**Makefile' ':(exclude)**/contrib/**' ':(exclude)third_party' ':(exclude).gitattributes' ':(exclude).gitmodules' || (echo "The above lines have tabs; please convert them to spaces"; false)) - name: Ensure no non-breaking spaces if: always() run: | # NB: We use 'printf' below rather than '\u000a' since bash pre-4.2 # does not support the '\u000a' syntax (which is relevant for local linters) (! git --no-pager grep -In "$(printf '\xC2\xA0')" -- . || (echo "The above lines have non-breaking spaces (U+00A0); please convert them to spaces (U+0020)"; false)) - - name: Ensure canonical include - if: always() - run: | - (! git --no-pager grep -In $'#include "' -- ./c10 ./aten ./torch/csrc ':(exclude)aten/src/ATen/native/quantized/cpu/qnnpack/**' ':(exclude)torch/csrc/jit/serialization/mobile_bytecode_generated.h'|| (echo "The above lines have include with quotes; please convert them to #include "; false)) - name: Ensure no versionless Python shebangs if: always() run: | (! git --no-pager grep -In '#!.*python$' -- . || (echo "The above lines have versionless Python shebangs; please specify either python2 or python3"; false)) - - name: Ensure no unqualified noqa - if: always() - run: | - # shellcheck disable=SC2016 - (! git --no-pager grep -InP '# noqa(?!: [A-Z]+\d{3})' -- '**.py' '**.pyi' ':(exclude)caffe2' || (echo 'The above lines have unqualified `noqa`; please convert them to `noqa: XXXX`'; false)) - - name: Ensure no unqualified type ignore - if: always() - run: | - # shellcheck disable=SC2016 - (! git --no-pager grep -InP '# type:\s*ignore(?!\[)' -- '**.py' '**.pyi' ':(exclude)test/test_jit.py' || (echo 'The above lines have unqualified `type: ignore`; please convert them to `type: ignore[xxxx]`'; false)) - - name: Ensure GitHub PyPi dependencies are pinned - if: always() - run: | - (! git --no-pager grep --color=always -InP \ - '(pip|pip3|python -m pip|python3 -m pip|python3 -mpip|python -mpip) install ([a-z][\.a-z-0-9]*+(?!(=|.*\.whl))([[:blank:]]|))+' \ - -- .github \ - ':(exclude)**.rst' \ - ':(exclude)**.py' \ - ':(exclude)**.md' \ - ':(exclude)**.diff' \ - ':(exclude)third_party' || - (echo "The above lines have unpinned PyPi installs; please pin them to a specific version: e.g. 'thepackage==1.2'"; false)) - # note that this next step depends on a clean checkout; - # if you run it locally then it will likely to complain - # about all the generated files in torch/test - - name: Ensure C++ source files are not executable - if: always() - run: | - # shellcheck disable=SC2016 - (! find . \( -path ./third_party -o -path ./.git -o -path ./torch/bin -o -path ./build \) -prune -o -type f -executable -regextype posix-egrep -not -regex '.+(\.(bash|sh|py|so)|git-pre-commit|git-clang-format|gradlew)$' -print | grep . || (echo 'The above files have executable permission; please remove their executable permission by using `chmod -x`'; false)) - name: C++ docs check if: ${{ always() && steps.requirements.outcome == 'success' }} run: | @@ -102,86 +114,12 @@ jobs: run: | set -eux python torch/testing/_check_kernel_launches.py |& tee "${GITHUB_WORKSPACE}"/cuda_kernel_launch_checks.txt - - name: Ensure no direct cub include - if: always() - run: | - (! git --no-pager grep -I -no $'#include commit-sha.txt - - name: Install dependencies - run: | - set -eux - pip3 install typing-extensions==3.10 --user # for tools/linter/translate_annotations.py - pip3 install -r requirements-flake8.txt --user - flake8 --version - - name: Run flake8 - run: | - set -eux - flake8 | tee "${GITHUB_WORKSPACE}"/flake8-output.txt - - name: Translate annotations - if: ${{ github.event_name == 'pull_request' }} - env: - HEAD_SHA: ${{ github.event.pull_request.head.sha }} - run: | - tools/linter/translate_annotations.py \ - --file="${GITHUB_WORKSPACE}"/flake8-output.txt \ - --regex='^(?P.*?):(?P\d+):(?P\d+): (?P\w+\d+) (?P.*)' \ - --commit="$HEAD_SHA" \ - > flake8-output/annotations.json - - name: Fail if there were any warnings - run: | - set -eu - # Re-output flake8 status so GitHub logs show it on the step that actually failed - cat "${GITHUB_WORKSPACE}"/flake8-output.txt - if [ -s "${GITHUB_WORKSPACE}"/flake8-output.txt ]; then - echo 'Please fix the above Flake8 warnings.' - false - fi - - name: Add annotations - # Don't run on forked pull requests - if: ${{ failure() && github.event.pull_request.head.repo.full_name == github.repository }} - uses: pytorch/add-annotations-github-action@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - check_name: 'flake8-py3' - linter_output_path: flake8-output/annotations.json - commit_sha: ${{ github.event.pull_request.head.sha }} - mode: json - - clang-tidy: - name: clang-tidy - runs-on: [self-hosted, linux.2xlarge] - container: - # ubuntu20.04-cuda11.2-py3.8-tidy11 - image: ghcr.io/pytorch/cilint-clang-tidy:d8f0c777964d0dd8a147360de80aed1a13eb613a - steps: - - name: Clean workspace - run: | - rm -rf "${GITHUB_WORKSPACE}" - mkdir "${GITHUB_WORKSPACE}" - # [see note: pytorch repo ref] - # deep clone (fetch-depth 0) to allow tools/linter/clang_tidy.py to do its thing - - name: Checkout PyTorch - uses: pytorch/pytorch/.github/actions/checkout-pytorch@master - with: - no-sudo: true - submodules: false - - name: Prepare output dir with HEAD commit SHA - env: - HEAD_SHA: ${{ github.event.pull_request.head.sha }} - run: | - cd "${GITHUB_WORKSPACE}" - mkdir clang-tidy-output - cd clang-tidy-output - echo "$HEAD_SHA" > commit-sha.txt - - name: Fetch PR diff - if: ${{ github.event_name == 'pull_request' }} - env: - PR_NUMBER: ${{ github.event.pull_request.number }} - run: | - cd "${GITHUB_WORKSPACE}" - wget -O pr.diff "https://patch-diff.githubusercontent.com/raw/pytorch/pytorch/pull/$PR_NUMBER.diff" - - name: Generate build files - run: | - cd "${GITHUB_WORKSPACE}" - python3 -m tools.linter.clang_tidy.generate_build_files - - name: Run PR clang-tidy - if: ${{ github.event_name == 'pull_request' }} - run: | - cd "${GITHUB_WORKSPACE}" - - # The Docker image has our custom build, so we don't need to install it - python3 -m tools.linter.clang_tidy \ - --clang-tidy-exe "$(which clang-tidy)" \ - --diff-file pr.diff \ - --disable-progress-bar 2>&1 | tee "${GITHUB_WORKSPACE}"/clang-tidy-output.txt - - # Run clang-tidy on a smaller subset of the codebase on master until we - # make the repository clang-tidy clean - - name: Run master clang-tidy - run: | - cd "${GITHUB_WORKSPACE}" - - python3 -m tools.linter.clang_tidy \ - --paths \ - torch/csrc/cuda \ - torch/csrc/fx \ - torch/csrc/utils \ - torch/csrc/generic \ - torch/csrc/deploy \ - torch/csrc/onnx \ - torch/csrc/tensor \ - --clang-tidy-exe "$(which clang-tidy)" \ - --disable-progress-bar 2>&1 | tee -a "${GITHUB_WORKSPACE}"/clang-tidy-output.txt - - - name: Annotate output - if: ${{ github.event_name == 'pull_request' }} - env: - HEAD_SHA: ${{ github.event.pull_request.head.sha }} - run: | - cd "${GITHUB_WORKSPACE}" - sed --in-place 's/^\.\.\///g' clang-tidy-output.txt - tools/linter/translate_annotations.py \ - --file=clang-tidy-output.txt \ - --regex='^(?P.*?):(?P\d+):(?P\d+): (?P.*?) \[(?P.*)\]' \ - --commit="$HEAD_SHA" \ - > clang-tidy-output/annotations.json - - name: Check for warnings - run: | - cd "${GITHUB_WORKSPACE}" - set -eu - cat "${GITHUB_WORKSPACE}"/clang-tidy-output.txt - if grep -Fq "Warnings detected!" "${GITHUB_WORKSPACE}"/clang-tidy-output.txt; then - echo 'Please fix the above clang-tidy warnings.' - false - fi - - name: Add annotations - # Don't run on forked pull requests - if: ${{ failure() && github.event.pull_request.head.repo.full_name == github.repository }} - uses: pytorch/add-annotations-github-action@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - check_name: 'clang-tidy' - linter_output_path: clang-tidy/annotations.json - commit_sha: ${{ github.event.pull_request.head.sha }} - mode: json - - cmakelint: - name: cmakelint - runs-on: ubuntu-18.04 - steps: - - name: Setup Python - uses: actions/setup-python@v2 - with: - python-version: 3.x - architecture: x64 - # [see note: pytorch repo ref] - - name: Checkout PyTorch - uses: pytorch/pytorch/.github/actions/checkout-pytorch@master - with: - submodules: false - - name: Install dependencies - run: | - set -eux - pip3 install cmakelint==1.4.1 --user - cmakelint --version - - name: Run cmakelint - run: | - set -eux - git ls-files -z -- bootstrap '*.cmake' '*.cmake.in' '*CMakeLists.txt' | \ - grep -E -z -v '^(cmake/Modules/|cmake/Modules_CUDA_fix/|cmake/Caffe2Config.cmake.in|aten/src/ATen/ATenConfig.cmake.in|cmake/Caffe2ConfigVersion.cmake.in|cmake/TorchConfig.cmake.in|cmake/TorchConfigVersion.cmake.in|cmake/cmake_uninstall.cmake.in)' | \ - xargs -0 cmakelint --config=.cmakelintrc --spaces=2 --quiet - - mypy: - name: mypy - runs-on: ubuntu-18.04 - steps: - - name: Setup Python - uses: actions/setup-python@v2 - with: - python-version: 3.8 - architecture: x64 - # [see note: pytorch repo ref] - - name: Checkout PyTorch - uses: pytorch/pytorch/.github/actions/checkout-pytorch@master - with: - submodules: false - - name: Install dependencies - run: | - set -eux - python3 -mpip install -r requirements.txt --user - python3 -mpip install numpy==1.20 --user # https://github.com/pytorch/pytorch/pull/60472 - python3 -mpip install expecttest==0.1.3 mypy==0.812 --user - # Needed to check tools/render_junit.py - python3 -mpip install junitparser==2.1.1 rich==10.9.0 --user - - name: Run autogen - run: | - set -eux - time python3 -mtools.generate_torch_version --is_debug=false - time python3 -mtools.codegen.gen -s aten/src/ATen -d build/aten/src/ATen - time python3 -mtools.pyi.gen_pyi --native-functions-path aten/src/ATen/native/native_functions.yaml --deprecated-functions-path "tools/autograd/deprecated.yaml" - - name: Run mypy - env: - MYPY_FORCE_COLOR: 1 - TERM: xterm-color - run: | - set -eux - STATUS= - for CONFIG in mypy*.ini; do - if ! python3 -mmypy --config="$CONFIG"; then - STATUS=fail - fi - done - if [ -n "$STATUS" ]; then - echo 'Please fix the above mypy warnings.' - false - fi - test-tools: name: Test tools if: ${{ github.repository == 'pytorch/pytorch' }} @@ -563,6 +267,10 @@ jobs: set -eux python3 -mpip install -r requirements.txt python3 -mpip install boto3==1.16.34 + pip3 install typing-extensions==3.10 --user + pip3 install -r requirements-flake8.txt --user + python3 -mpip install -r requirements.txt --user + python3 -mpip install mypy==0.812 --user make setup_lint - name: Test tools run: | diff --git a/Makefile b/Makefile index e74f5715a820..e35a5dd07df9 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ # This makefile does nothing but delegating the actual building to cmake. PYTHON = python3 +PIP = pip3 all: @mkdir -p build && cd build && cmake .. $(shell $(PYTHON) ./scripts/get_python_cmake_flags.py) && $(MAKE) @@ -51,12 +52,7 @@ shellcheck: --job 'shellcheck' setup_lint: - $(PYTHON) tools/actions_local_runner.py --file .github/workflows/lint.yml \ - --job 'flake8-py3' --step 'Install dependencies' --no-quiet - $(PYTHON) tools/actions_local_runner.py --file .github/workflows/lint.yml \ - --job 'cmakelint' --step 'Install dependencies' --no-quiet - $(PYTHON) tools/actions_local_runner.py --file .github/workflows/lint.yml \ - --job 'mypy' --step 'Install dependencies' --no-quiet + $(PIP) install lintrunner $(PYTHON) tools/actions_local_runner.py --file .github/workflows/lint.yml \ --job 'shellcheck' --step 'Install Jinja2' --no-quiet @@ -71,8 +67,6 @@ setup_lint: --job 'shellcheck' --step 'Install ShellCheck' --no-quiet; \ fi $(PYTHON) -mpip install jinja2 --user - $(PYTHON) -mpip install -r tools/linter/clang_tidy/requirements.txt --user - $(PYTHON) -m tools.linter.install.clang_tidy quick_checks: # TODO: This is broken when 'git config submodule.recurse' is 'true' since the @@ -80,41 +74,7 @@ quick_checks: @$(PYTHON) tools/actions_local_runner.py \ --file .github/workflows/lint.yml \ --job 'quick-checks' \ - --step 'Ensure no trailing spaces' \ - --step 'Ensure no tabs' \ - --step 'Ensure no non-breaking spaces' \ - --step 'Ensure canonical include' \ - --step 'Ensure no versionless Python shebangs' \ - --step 'Ensure no unqualified noqa' \ - --step 'Ensure GitHub PyPi dependencies are pinned' \ - --step 'Ensure no unqualified type ignore' \ - --step 'Ensure no direct cub include' \ - --step 'Ensure correct trailing newlines' \ - --step 'Ensure no raw cuda api calls' - -flake8: - @$(PYTHON) tools/actions_local_runner.py \ - $(CHANGED_ONLY) \ - $(REF_BRANCH) \ - --job 'flake8-py3' - -mypy: - @$(PYTHON) tools/actions_local_runner.py \ - $(CHANGED_ONLY) \ - $(REF_BRANCH) \ - --job 'mypy' - -cmakelint: - @$(PYTHON) tools/actions_local_runner.py \ - --file .github/workflows/lint.yml \ - --job 'cmakelint' \ - --step 'Run cmakelint' - -clang-tidy: - @$(PYTHON) tools/actions_local_runner.py \ - $(CHANGED_ONLY) \ - $(REF_BRANCH) \ - --job 'clang-tidy' + --step 'Ensure no versionless Python shebangs' toc: @$(PYTHON) tools/actions_local_runner.py \ @@ -122,7 +82,9 @@ toc: --job 'toc' \ --step "Regenerate ToCs and check that they didn't change" -lint: flake8 mypy quick_checks cmakelint shellcheck +lint: quick_checks shellcheck + lintrunner quicklint: CHANGED_ONLY=--changed-only -quicklint: mypy flake8 quick_checks cmakelint shellcheck clang-tidy +quicklint: quick_checks shellcheck + lintrunner diff --git a/tools/extract_scripts.py b/tools/extract_scripts.py index 5312ed00da11..1090886f72d2 100755 --- a/tools/extract_scripts.py +++ b/tools/extract_scripts.py @@ -58,7 +58,7 @@ def main() -> None: gha_expressions_found = False for p in Path('.github/workflows').iterdir(): - with open(p) as f: + with open(p, "rb") as f: workflow = yaml.safe_load(f) for job_name, job in workflow['jobs'].items(): diff --git a/tools/linter/adapters/pip_init.py b/tools/linter/adapters/pip_init.py index b4451beac644..10fdcea37ee5 100644 --- a/tools/linter/adapters/pip_init.py +++ b/tools/linter/adapters/pip_init.py @@ -45,7 +45,7 @@ if __name__ == "__main__": "Package {package_name} did not have a version specified. " "Please specify a version to product a consistent linting experience." ) - pip_args = ["pip3", "install", "--user"] + pip_args = ["pip3", "install"] pip_args.extend(args.packages) dry_run = args.dry_run == "1" diff --git a/tools/test/test_actions_local_runner.py b/tools/test/test_actions_local_runner.py index ba4e6fd2cdb9..3ef7c6a3c76c 100644 --- a/tools/test/test_actions_local_runner.py +++ b/tools/test/test_actions_local_runner.py @@ -47,16 +47,6 @@ if sys.version_info >= (3, 8): 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", @@ -73,8 +63,6 @@ if sys.version_info >= (3, 8): 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( @@ -85,9 +73,6 @@ if sys.version_info >= (3, 8): 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"), @@ -147,45 +132,6 @@ if sys.version_info >= (3, 8): 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()