Add incremental pylint make target (#6597)

* Add incremental pylint make target

* add tox.ini and fetch/compare to origin/HEAD

* commands

* opportunistic caching

* Add small tool to avoid shell utils

* import pylint not sub-process. clean examples

* blacken few extra files

* just pylint

* update contributing.md

* tweak error msg, tox.ini

* fixup makefile, tox.ini

* command line tweaks

* add tools to lint-incr target

* Update tox.ini

* Revert "Update tox.ini"

Tox doesn't support globbing so this change wasn't valid that line was
commented on purpose. This reverts the previous tox.ini update and adds
a comment about the commented line to explain the intent.

This reverts commit c33c2745cf.

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
This commit is contained in:
Lev Bishop 2021-07-22 20:06:55 -04:00 committed by GitHub
parent fbf389fca6
commit 105cba366d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 143 additions and 14 deletions

View File

@ -379,6 +379,12 @@ run `tox -eblack` to automatically update the code formatting to conform to
the style. However, if `pylint` returns any error you will have to fix these the style. However, if `pylint` returns any error you will have to fix these
issues by manually updating your code. issues by manually updating your code.
Because `pylint` analysis can be slow, there is also a `tox -elint-incr` target, which only applies
`pylint` to files which have changed from the source github. On rare occasions this will miss some
issues that would have been caught by checking the complete source tree, but makes up for this by
being much faster (and those rare oversights will still be caught by the CI after you open a pull
request).
## Development Cycle ## Development Cycle
The development cycle for qiskit-terra is all handled in the open using The development cycle for qiskit-terra is all handled in the open using

View File

@ -32,7 +32,9 @@ else
CONCURRENCY := $(shell echo "$(NPROCS) 2" | awk '{printf "%.0f", $$1 / $$2}') CONCURRENCY := $(shell echo "$(NPROCS) 2" | awk '{printf "%.0f", $$1 / $$2}')
endif endif
.PHONY: env lint test test_ci .PHONY: default env lint lint-incr style black test test_randomized pytest pytest_randomized test_ci coverage coverage_erase clean
default: style lint-incr test ;
# Dependencies need to be installed on the Anaconda virtual environment. # Dependencies need to be installed on the Anaconda virtual environment.
env: env:
@ -47,7 +49,15 @@ env:
lint: lint:
pylint -rn qiskit test tools pylint -rn qiskit test tools
tools/verify_headers.py qiskit test tools examples tools/verify_headers.py qiskit test tools examples
pylint -rn --disable='C0103, C0114, W0621' examples/python/*.py pylint -rn --disable='invalid-name, missing-module-docstring, redefined-outer-name' examples/python/*.py
tools/find_optional_imports.py
# Only pylint on files that have changed from origin/main. Also parallelize (disables cyclic-import check)
lint-incr:
-git fetch -q https://github.com/Qiskit/qiskit-terra.git :lint_incr_latest
tools/pylint_incr.py -j4 -rn -sn --paths :/qiskit/*.py :/test/*.py :/tools/*.py
tools/pylint_incr.py -j4 -rn -sn --disable='invalid-name, missing-module-docstring, redefined-outer-name' --paths ':(glob,top)examples/python/*.py'
tools/verify_headers.py qiskit test tools examples
tools/find_optional_imports.py tools/find_optional_imports.py
style: style:

97
tools/pylint_incr.py Executable file
View File

@ -0,0 +1,97 @@
#!/usr/bin/env python3
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2018.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""Run pylint incrementally on only changed files"""
import subprocess
import argparse
import os
import sys
from pylint import lint
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
def _minimal_ext_cmd(cmd):
# construct minimal environment
env = {}
for k in ["SYSTEMROOT", "PATH"]:
v = os.environ.get(k)
if v is not None:
env[k] = v
# LANGUAGE is used on win32
env["LANGUAGE"] = "C"
env["LANG"] = "C"
env["LC_ALL"] = "C"
with subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
cwd=os.path.join(os.path.dirname(ROOT_DIR)),
) as proc:
stdout, stderr = proc.communicate()
return proc.returncode, stdout, stderr
def _run_pylint(ref, paths, pylint_args):
code, stdout, stderr = _minimal_ext_cmd(
[
"git",
"diff-index",
"--name-only",
"--diff-filter=d",
"--merge-base",
"-z",
ref,
"--",
*paths,
]
)
if code != 0:
print(
f"{__file__}: unable to get list of changed files. Git returncode: {code}\n"
f"Git must be installed, and you need to be in a git tree with a ref `{ref}`\n"
f"{stderr.strip().decode('ascii')}"
)
sys.exit(128)
changed_paths = [path.decode("ascii") for path in stdout.split(b"\x00") if len(path) > 0]
if len(changed_paths) == 0:
print(f"No changed files in {' '.join(paths)}")
sys.exit(0)
changed_paths_pretty = "\n ".join(changed_paths)
print(f"Running pylint on {len(changed_paths)} changed files:\n {changed_paths_pretty}")
lint.Run([*pylint_args, "--", *changed_paths])
def _main():
parser = argparse.ArgumentParser(
description="Incremental pylint.",
epilog="Unknown arguments passed through to pylint",
allow_abbrev=False,
)
parser.add_argument(
"--paths",
required=True,
type=str,
nargs="+",
help="Git <pathspec>s to resolve (and pass any changed files to pylint)",
)
args, pylint_args = parser.parse_known_args()
_run_pylint("lint_incr_latest", args.paths, pylint_args)
if __name__ == "__main__":
_main()

18
tox.ini
View File

@ -1,6 +1,6 @@
[tox] [tox]
minversion = 2.1 minversion = 2.1
envlist = py36, py37, py38, lint envlist = py36, py37, py38, lint-incr
skipsdist = True skipsdist = True
[testenv] [testenv]
@ -24,6 +24,22 @@ basepython = python3
commands = commands =
black --check {posargs} qiskit test tools examples setup.py black --check {posargs} qiskit test tools examples setup.py
pylint -rn qiskit test tools pylint -rn qiskit test tools
# This line is commented out until #6649 merges. We can't run this currently
# via tox because tox doesn't support globbing
# pylint -rn --disable='invalid-name,missing-module-docstring,redefined-outer-name' examples/python/*.py
{toxinidir}/tools/verify_headers.py qiskit test tools examples
{toxinidir}/tools/find_optional_imports.py
reno lint
[testenv:lint-incr]
envdir = .tox/lint
basepython = python3
allowlist_externals = git
commands =
black --check {posargs} qiskit test tools examples setup.py
-git fetch -q https://github.com/Qiskit/qiskit-terra.git :lint_incr_latest
{toxinidir}/tools/pylint_incr.py -rn -j4 -sn --paths :/qiskit/*.py :/test/*.py :/tools/*.py
{toxinidir}/tools/pylint_incr.py -rn -j4 -sn --disable='invalid-name,missing-module-docstring,redefined-outer-name' --paths :(glob,top)examples/python/*.py
{toxinidir}/tools/verify_headers.py qiskit test tools examples {toxinidir}/tools/verify_headers.py qiskit test tools examples
{toxinidir}/tools/find_optional_imports.py {toxinidir}/tools/find_optional_imports.py
reno lint reno lint