Skip to content

Commit

Permalink
start linting test files (#3600)
Browse files Browse the repository at this point in the history
* create a whitelist of files that pass pylint

* ignore too many args in dataset_access test

* rename .github/data/ to tests, copy pylintrc for tests

* extended pylintrc disable list for test files

* add disable for a few tests

* workflow file cleanup; add lint and lint-test targets

* add lint disables to pennylane to fix make lint

* fail if new test files are added but not whitelisted

* reorganize files

* be more specific with which files to check

* fake files to test CI

* Revert "fake files to test CI"

This reverts commit 0e53fa7.

* update references to old file locations that I missed
  • Loading branch information
timmysilv committed Jan 9, 2023
1 parent 6c74b0e commit 359130c
Show file tree
Hide file tree
Showing 18 changed files with 138 additions and 13 deletions.
37 changes: 36 additions & 1 deletion .github/workflows/pylint.yml
Expand Up @@ -38,4 +38,39 @@ jobs:
extra-flake8-options: ""
extra-black-options: ""
extra-mypy-options: ""
extra-isort-options: ""
extra-isort-options: ""

- name: Check for new test files that need whitelisting
uses: dorny/paths-filter@v2
id: test_files_added
with:
list-files: shell
filters: |
testsAdded:
- added: 'tests/**/test_*.py'
- added: 'tests/test_*.py'
- name: Ensure new test files are whitelisted if there are any
if: steps.test_files_added.outputs.testsAdded == 'true'
run: ./.github/workflows/scripts/validate_new_tests.sh ${{ steps.test_files_added.outputs.testsAdded_files }}

- name: Read whitelisted test files that pass pylint
id: tests_to_lint
run: echo "testfiles=$(cat tests/tests_passing_pylint.txt | tr '\n' ' ')" >> $GITHUB_OUTPUT

- name: Run pylint for whitelisted tests
uses: ricardochaves/python-lint@v1.4.0
with:
python-root-list: "${{ steps.tests_to_lint.outputs.testfiles }}"
use-pylint: True
use-pycodestyle: false
use-flake8: false
use-black: false
use-mypy: false
use-isort: false
extra-pylint-options: "--rcfile tests/.pylintrc"
extra-pycodestyle-options: ""
extra-flake8-options: ""
extra-black-options: ""
extra-mypy-options: ""
extra-isort-options: ""
20 changes: 20 additions & 0 deletions .github/workflows/scripts/validate_new_tests.sh
@@ -0,0 +1,20 @@
#!/bin/bash
missing_files=()
for file in $@
do
if grep -Fxq $file tests/tests_passing_pylint.txt
then
:
else
missing_files+=($file)
fi
done

if [ ${#missing_files[@]} -eq 0 ]; then
echo "All new test files are already added"
exit 0
fi

echo "Please add the following test files to tests/tests_passing_pylint.txt:"
for file in ${missing_files[@]}; do echo $file; done
exit 1
10 changes: 10 additions & 0 deletions Makefile
Expand Up @@ -17,6 +17,8 @@ help:
@echo " test to run the test suite"
@echo " coverage to generate a coverage report"
@echo " format [check=1] to apply black formatter; use with 'check=1' to check instead of modify (requires black)"
@echo " lint to run pylint on source files"
@echo " lint-test to run pylint on test files"

.PHONY: install
install:
Expand Down Expand Up @@ -72,3 +74,11 @@ ifdef check
else
black -l 100 ./pennylane ./tests
endif

.PHONY: lint
lint:
pylint pennylane --rcfile .pylintrc

.PHONY: lint-test
lint-test:
pylint tests pennylane/devices/tests --rcfile tests/.pylintrc
1 change: 1 addition & 0 deletions pennylane/collections/apply.py
Expand Up @@ -16,6 +16,7 @@
"""


# pylint:disable=unnecessary-lambda-assignment
def apply(func, qnode_collection):
"""Apply a function to the constituent QNodes of a :class:`QNodeCollection`.
Expand Down
2 changes: 1 addition & 1 deletion pennylane/collections/dot.py
Expand Up @@ -14,7 +14,7 @@
"""
Contains functions to implement the dot product between QNode collections
"""
# pylint: disable=too-many-arguments,import-outside-toplevel
# pylint: disable=too-many-arguments,import-outside-toplevel,unnecessary-lambda-assignment


def _get_dot_func(interface, x=None):
Expand Down
2 changes: 2 additions & 0 deletions pennylane/data/dataset.py
Expand Up @@ -157,6 +157,7 @@ def attrs(self):
"""Returns attributes of the dataset."""
return {k: v for k, v in vars(self).items() if k[0] != "_"}

# pylint:disable=c-extension-no-member
@staticmethod
def _read_file(filepath):
"""Read data from a saved file.
Expand Down Expand Up @@ -203,6 +204,7 @@ def read(self, filepath, lazy=False):
data = dict_to_hamiltonian(data["terms"], data["wire_map"])
setattr(self, f"{attribute}", data)

# pylint:disable=c-extension-no-member
@staticmethod
def _write_file(data, filepath, protocol=4):
"""General method to write data to a file."""
Expand Down
4 changes: 1 addition & 3 deletions pennylane/devices/tests/test_gates.py
Expand Up @@ -15,9 +15,7 @@
Tests that application of gates and state preparations
works correctly an a device.
"""
# pylint: disable=no-self-use
# pylint: disable=too-many-arguments
# pylint: disable=pointless-statement
# pylint:disable=no-self-use,too-many-arguments,pointless-statement,unnecessary-lambda-assignment
from cmath import exp
from math import cos, sin, sqrt

Expand Down
2 changes: 1 addition & 1 deletion pennylane/devices/tests/test_properties.py
Expand Up @@ -295,4 +295,4 @@ def circuit(x):
assert pnp.array(dev.access_state()).shape == orig_shape

assert pnp.ndim(res) == 2
assert res.shape[0] == 3
assert res.shape[0] == 3 # pylint:disable=unsubscriptable-object
5 changes: 3 additions & 2 deletions pennylane/math/is_independent.py
Expand Up @@ -74,6 +74,7 @@ def _autograd_is_indep_analytic(func, *args, **kwargs):
return True


# pylint: disable=import-outside-toplevel,unnecessary-lambda-assignment,unnecessary-lambda
def _jax_is_indep_analytic(func, *args, **kwargs):
"""Test analytically whether a function is independent of its arguments
using JAX.
Expand Down Expand Up @@ -107,9 +108,9 @@ def _jax_is_indep_analytic(func, *args, **kwargs):
This is an experimental function and unknown edge
cases may exist to this two-stage test.
"""
import jax # pylint: disable=import-outside-toplevel
import jax

mapped_func = lambda *_args: func(*_args, **kwargs) # pylint: disable=unnecessary-lambda
mapped_func = lambda *_args: func(*_args, **kwargs)
_vjp = jax.vjp(mapped_func, *args)[1]
if _vjp.args[0].args != ((),):
return False
Expand Down
2 changes: 1 addition & 1 deletion pennylane/optimize/adaptive.py
Expand Up @@ -14,7 +14,7 @@
"""Adaptive optimizer"""
import copy

# pylint: disable= no-value-for-parameter, protected-access
# pylint: disable= no-value-for-parameter, protected-access, not-callable
import pennylane as qml
from pennylane import numpy as np

Expand Down
1 change: 1 addition & 0 deletions pennylane/optimize/rotoselect.py
Expand Up @@ -133,6 +133,7 @@ def step(self, objective_fn, x, generators, **kwargs):
"""
x_flat = np.fromiter(_flatten(x), dtype=float)
# wrap the objective function so that it accepts the flattened parameter array
# pylint:disable=unnecessary-lambda-assignment
objective_fn_flat = lambda x_flat, gen: objective_fn(
unflatten(x_flat, x), generators=gen, **kwargs
)
Expand Down
1 change: 1 addition & 0 deletions pennylane/qaoa/cost.py
Expand Up @@ -15,6 +15,7 @@
Methods for generating QAOA cost Hamiltonians corresponding to
different optimization problems.
"""
# pylint: disable=unnecessary-lambda-assignment
from typing import Iterable, Union
import networkx as nx
import retworkx as rx
Expand Down
2 changes: 1 addition & 1 deletion pennylane/qaoa/cycle.py
Expand Up @@ -14,7 +14,7 @@
r"""
Functionality for finding the maximum weighted cycle of directed graphs.
"""
# pylint: disable=unnecessary-comprehension
# pylint: disable=unnecessary-comprehension, unnecessary-lambda-assignment
import itertools
from typing import (
Dict,
Expand Down
1 change: 1 addition & 0 deletions pennylane/qaoa/mixers.py
Expand Up @@ -14,6 +14,7 @@
r"""
Methods for constructing QAOA mixer Hamiltonians.
"""
# pylint: disable=unnecessary-lambda-assignment
import itertools
import functools
from typing import Iterable, Union
Expand Down
4 changes: 2 additions & 2 deletions pennylane/templates/layers/particle_conserving_u2.py
Expand Up @@ -217,8 +217,8 @@ def compute_decomposition(weights, wires, init_state): # pylint: disable=argume

for l in range(n_layers):

for j, _ in enumerate(wires):
op_list.append(qml.RZ(weights[l, j], wires=wires[j]))
for j, wires_ in enumerate(wires):
op_list.append(qml.RZ(weights[l, j], wires=wires_))

for i, wires_ in enumerate(nm_wires):
op_list.extend(u2_ex_gate(weights[l, len(wires) + i], wires=wires_))
Expand Down
51 changes: 51 additions & 0 deletions tests/.pylintrc
@@ -0,0 +1,51 @@
[MASTER]
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
extension-pkg-whitelist=numpy,scipy,autograd,toml,appdir,autograd.numpy,autograd.numpy.linalg,autograd.numpy.builtins,semantic_version,torch,tensorflow,tensorflow.contrib,tensorflow.contrib.eager,LazyLoader,networkx,networkx.dag

[TYPECHECK]

# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis. It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=numpy,scipy,autograd,toml,appdir,autograd.numpy,autograd.numpy.linalg,autograd.numpy.builtins,semantic_version,torch,tensorflow,tensorflow.contrib,tensorflow.contrib.eager,LazyLoader,networkx,networkx.dag,math,pennylane.numpy

# List of classes names for which member attributes should not be checked
# (useful for classes with attributes dynamically set). This supports can work
# with qualified names.
ignored-classes=numpy,scipy,autograd,toml,appdir,autograd.numpy,autograd.numpy.linalg,autograd.numpy.builtins,semantic_version,torch,tensorflow,tensorflow.contrib,tensorflow.contrib.eager,LazyLoader,networkx,networkx.dag,math,pennylane.numpy,pennylane.numpy.random,pennylane.numpy.linalg,pennylane.numpy.builtins,pennylane.operation,retworkx,kahypar

[MESSAGES CONTROL]

# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time.
#enable=

# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once).
# Cyclical import checks are disabled for now as they are frequently used in
# the code base, but this can be removed in the future once cycles are resolved.
disable=
line-too-long,
invalid-name,
too-many-lines,
redefined-builtin,
too-many-locals,
duplicate-code,
cyclic-import,
import-error,
bad-option-value,
import-outside-toplevel,
missing-class-docstring,
missing-function-docstring,
no-self-use

[MISCELLANEOUS]

# List of note tags to take in consideration, separated by a comma.
notes=
4 changes: 3 additions & 1 deletion tests/data/test_dataset_access.py
Expand Up @@ -54,6 +54,7 @@ def httpserver_listen_address():
return ("localhost", 8888)


# pylint:disable=unused-argument
def get_mock(url, timeout=1.0):
"""Return the foldermap or data_struct according to URL"""
resp = MagicMock(ok=True)
Expand All @@ -70,6 +71,7 @@ def submit_download_mock(_self, _fetch_and_save, filename, dest_folder):
qml.data.Dataset._write_file(content, os.path.join(dest_folder, filename))


# pylint:disable=unused-argument
def wait_mock_fixture(_futures, return_when=None):
"""Patch to avoid raising exceptions after collecting threads."""
return MagicMock(done=[])
Expand Down Expand Up @@ -605,7 +607,7 @@ class TestLoadInteractive:
)
def test_load_interactive_success(
self, mock_input, mock_load, mock_sleep, side_effect, data_name, kwargs, sleep_call_count
):
): # pylint:disable=too-many-arguments
"""Test that load_interactive succeeds."""
mock_input.side_effect = side_effect
assert isinstance(qml.data.load_interactive(), qml.data.Dataset)
Expand Down
2 changes: 2 additions & 0 deletions tests/tests_passing_pylint.txt
@@ -0,0 +1,2 @@
tests/data/test_dataset.py
tests/data/test_dataset_access.py

0 comments on commit 359130c

Please sign in to comment.