diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml new file mode 100644 index 00000000..b6a2f6ec --- /dev/null +++ b/.github/workflows/mypy.yml @@ -0,0 +1,19 @@ +name: Type Check + +on: [push, pull_request] + +jobs: + mypy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v1 + with: + python-version: '3.8' + architecture: 'x64' + - name: Install mypy + run: | + python -m pip install --upgrade pip + pip install mypy + - name: Type check + run: dev_tools/mypy || true diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml new file mode 100644 index 00000000..c59b6d03 --- /dev/null +++ b/.github/workflows/pylint.yml @@ -0,0 +1,19 @@ +name: Pylint + +on: [push, pull_request] + +jobs: + pylint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v1 + with: + python-version: '3.8' + architecture: 'x64' + - name: Install Pylint + run: | + python -m pip install --upgrade pip + pip install pylint + - name: Pylint check + run: dev_tools/pylint diff --git a/dev_tools/.pylintrc b/dev_tools/.pylintrc new file mode 100644 index 00000000..aeb6b8a8 --- /dev/null +++ b/dev_tools/.pylintrc @@ -0,0 +1,73 @@ +[MASTER] +load-plugins=pylint.extensions.docstyle,pylint.extensions.docparams,pylint_copyright_checker +max-line-length=100 +disable=all +#ignore-paths=cirq-google/cirq_google/cloud/.* +ignore-patterns=.*_pb2\.py +output-format=colorized +score=no +reports=no +enable= + anomalous-backslash-in-string, + assert-on-tuple, + bad-indentation, + bad-option-value, + bad-reversed-sequence, + bad-super-call, + consider-merging-isinstance, + continue-in-finally, + dangerous-default-value, + docstyle, + duplicate-argument-name, + expression-not-assigned, + function-redefined, + inconsistent-mro, + init-is-generator, + line-too-long, + lost-exception, + missing-kwoa, + missing-param-doc, + missing-raises-doc, + mixed-line-endings, + no-value-for-parameter, + nonexistent-operator, + not-in-loop, + pointless-statement, + redefined-builtin, + return-arg-in-generator, + return-in-init, + return-outside-function, + simplifiable-if-statement, + singleton-comparison, + syntax-error, + too-many-function-args, + trailing-whitespace, + undefined-variable, + unexpected-keyword-arg, + unhashable-dict-key, + unnecessary-pass, + unreachable, + unrecognized-inline-option, + unused-import, + unnecessary-semicolon, + unused-variable, + unused-wildcard-import, + wildcard-import, + wrong-import-order, + wrong-import-position, + yield-outside-function + +# Ignore long lines containing urls or pylint directives. +ignore-long-lines=^(.*#\w*pylint: disable.*|\s*(# )?[<\[\(]?https?://\S+[>\]\)]?)$ + +[TYPECHECK] + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members=numpy.* + + +#[IMPORTS] +# Force import order to recognize a module as part of a third party library. +#known-third-party=cirq,cirq_google,cirq_aqt,cirq_ionq diff --git a/dev_tools/mypy b/dev_tools/mypy new file mode 100755 index 00000000..b804ed14 --- /dev/null +++ b/dev_tools/mypy @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +################################################################################ +# Runs mypy on the repository using a preconfigured mypy.ini file. +# +# Usage: +# check/mypy [--flags] +################################################################################ + +# Get the working directory to the repo root. +cd "$(dirname "${BASH_SOURCE[0]}")" +cd "$(git rev-parse --show-toplevel)" + + +echo -e -n "\033[31m" +mypy --config-file=dev_tools/mypy.ini "$@" . +result=$? +echo -e -n "\033[0m" + +exit ${result} diff --git a/dev_tools/mypy.ini b/dev_tools/mypy.ini new file mode 100644 index 00000000..46349a2e --- /dev/null +++ b/dev_tools/mypy.ini @@ -0,0 +1,16 @@ +[mypy] +show_error_codes = true + +[mypy-__main__] +follow_imports = silent +ignore_missing_imports = true + +# 3rd-party libs for which we don't have stubs +[mypy-apiclient.*,matplotlib.*,mpl_toolkits,oauth2client.*,pandas.*,proto.*,pytest.*,scipy.*,setuptools.*,networkx.*,_pytest.*,codeowners.*,pylint.*,numpy.*,cirq.*,flask.*,numpy.*,cirq_google.*] +follow_imports = silent +ignore_missing_imports = true + +# There was no type information before numpy 1.20, so there are numpy mypy issues in the codebase +[mypy-numpy.*] +follow_imports = skip +follow_imports_for_stubs = true diff --git a/dev_tools/pylint b/dev_tools/pylint new file mode 100755 index 00000000..3d88f809 --- /dev/null +++ b/dev_tools/pylint @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +################################################################################ +# Runs pylint on the repository using a preconfigured .pylintrc file. +# +# Usage: +# check/pylint [--flags for pylint] +################################################################################ + +# Get the working directory to the repo root. +cd "$(dirname "${BASH_SOURCE[0]}")" +cd "$(git rev-parse --show-toplevel)" + +# Add dev_tools to $PYTHONPATH so that pylint can find custom checkers +env PYTHONPATH=dev_tools pylint --jobs=0 --rcfile=dev_tools/.pylintrc "$@" . diff --git a/requirements.txt b/requirements.txt index 3a379bfa..047c3bc8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,14 @@ cirq-core>=0.14.0 cirq-google>=0.14.0 # When changing Cirq requirements be sure to update dev_tools/write-ci-requirements.py -seaborn -sphinx +numpy +sympy + +# dev tools ipython black +mypy +pylint +pytest +pytest-cov +coverage<=6.2 diff --git a/unitary/__init__.py b/unitary/__init__.py index 3e0d2855..a6ce2123 100644 --- a/unitary/__init__.py +++ b/unitary/__init__.py @@ -20,7 +20,3 @@ get_sampler_by_name, execute_in_queue, ) - -from unitary.documentation_utils import ( - display_markdown_docstring, -) diff --git a/unitary/documentation_utils.py b/unitary/documentation_utils.py deleted file mode 100644 index 313a1e7e..00000000 --- a/unitary/documentation_utils.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2020 Google -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import inspect -import os -import re -import tarfile -import urllib.request - -from sphinx.ext.napoleon import Config, GoogleDocstring - - -class GoogleDocstringToMarkdown(GoogleDocstring): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.markdown_lines = [] - - def _parse_attributes_section(self, section): - lines = ['#### Attributes'] - for _name, _type, _desc in self._consume_fields(): - desc = ' '.join(_desc) - lines += [f' - `{_name}`: {desc}'] - - lines += [''] - return lines - - def _parse_see_also_section(self, section): - lines = self._consume_to_next_section() - lines = [line.strip() for line in lines] - return ['#### See Also', ' '.join(lines), ''] - - -def display_markdown_docstring(cls): - config = Config() - gds = GoogleDocstringToMarkdown(inspect.cleandoc(cls.__doc__), - config=config, what='class') - gds_lines = [f'### {cls.__name__}'] + gds.lines() - gds_lines = [re.sub(r':py:func:`(\w+)`', r'`\1`', line) - for line in gds_lines] - - from IPython.display import Markdown - return Markdown('\n'.join(gds_lines))