1# Copyright 2016 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15from __future__ import absolute_import 16import os 17import pathlib 18import shutil 19 20# https://github.com/google/importlab/issues/25 21import nox # pytype: disable=import-error 22 23 24BLACK_VERSION = "black==19.10b0" 25BLACK_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] 26# Black and flake8 clash on the syntax for ignoring flake8's F401 in this file. 27BLACK_EXCLUDES = ["--exclude", "^/google/api_core/operations_v1/__init__.py"] 28 29DEFAULT_PYTHON_VERSION = "3.7" 30CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() 31 32# 'docfx' is excluded since it only needs to run in 'docs-presubmit' 33nox.options.sessions = [ 34 "unit", 35 "unit_grpc_gcp", 36 "unit_wo_grpc", 37 "cover", 38 "pytype", 39 "mypy", 40 "lint", 41 "lint_setup_py", 42 "blacken", 43 "docs", 44] 45 46 47def _greater_or_equal_than_36(version_string): 48 tokens = version_string.split(".") 49 for i, token in enumerate(tokens): 50 try: 51 tokens[i] = int(token) 52 except ValueError: 53 pass 54 return tokens >= [3, 6] 55 56 57@nox.session(python=DEFAULT_PYTHON_VERSION) 58def lint(session): 59 """Run linters. 60 61 Returns a failure if the linters find linting errors or sufficiently 62 serious code quality issues. 63 """ 64 session.install("flake8", "flake8-import-order", BLACK_VERSION) 65 session.install(".") 66 session.run( 67 "black", "--check", *BLACK_EXCLUDES, *BLACK_PATHS, 68 ) 69 session.run("flake8", "google", "tests") 70 71 72@nox.session(python=DEFAULT_PYTHON_VERSION) 73def blacken(session): 74 """Run black. 75 76 Format code to uniform standard. 77 """ 78 session.install(BLACK_VERSION) 79 session.run("black", *BLACK_EXCLUDES, *BLACK_PATHS) 80 81 82def default(session, install_grpc=True): 83 """Default unit test session. 84 85 This is intended to be run **without** an interpreter set, so 86 that the current ``python`` (on the ``PATH``) or the version of 87 Python corresponding to the ``nox`` binary the ``PATH`` can 88 run the tests. 89 """ 90 constraints_path = str( 91 CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" 92 ) 93 94 # Install all test dependencies, then install this package in-place. 95 session.install("mock", "pytest", "pytest-cov") 96 if install_grpc: 97 session.install("-e", ".[grpc]", "-c", constraints_path) 98 else: 99 session.install("-e", ".", "-c", constraints_path) 100 101 pytest_args = [ 102 "python", 103 "-m", 104 "py.test", 105 "--quiet", 106 "--cov=google.api_core", 107 "--cov=tests.unit", 108 "--cov-append", 109 "--cov-config=.coveragerc", 110 "--cov-report=", 111 "--cov-fail-under=0", 112 os.path.join("tests", "unit"), 113 ] 114 pytest_args.extend(session.posargs) 115 116 # Inject AsyncIO content and proto-plus, if version >= 3.6. 117 # proto-plus is needed for a field mask test in test_protobuf_helpers.py 118 if _greater_or_equal_than_36(session.python): 119 session.install("asyncmock", "pytest-asyncio", "proto-plus") 120 121 pytest_args.append("--cov=tests.asyncio") 122 pytest_args.append(os.path.join("tests", "asyncio")) 123 session.run(*pytest_args) 124 else: 125 # Run py.test against the unit tests. 126 session.run(*pytest_args) 127 128 129@nox.session(python=["3.6", "3.7", "3.8", "3.9", "3.10"]) 130def unit(session): 131 """Run the unit test suite.""" 132 default(session) 133 134 135@nox.session(python=["3.6", "3.7", "3.8", "3.9"]) 136def unit_grpc_gcp(session): 137 """Run the unit test suite with grpcio-gcp installed.""" 138 constraints_path = str( 139 CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" 140 ) 141 # Install grpcio-gcp 142 session.install("-e", ".[grpcgcp]", "-c", constraints_path) 143 144 default(session) 145 146 147@nox.session(python=["3.6", "3.10"]) 148def unit_wo_grpc(session): 149 """Run the unit test suite w/o grpcio installed""" 150 default(session, install_grpc=False) 151 152 153@nox.session(python="3.6") 154def lint_setup_py(session): 155 """Verify that setup.py is valid (including RST check).""" 156 157 session.install("docutils", "Pygments") 158 session.run("python", "setup.py", "check", "--restructuredtext", "--strict") 159 160 161# No 3.7 because pytype supports up to 3.6 only. 162@nox.session(python="3.6") 163def pytype(session): 164 """Run type-checking.""" 165 session.install(".[grpc, grpcgcp]", "pytype >= 2019.3.21") 166 session.run("pytype") 167 168 169@nox.session(python=DEFAULT_PYTHON_VERSION) 170def mypy(session): 171 """Run type-checking.""" 172 session.install(".[grpc, grpcgcp]", "mypy") 173 session.install( 174 "types-setuptools", "types-requests", "types-protobuf", "types-mock" 175 ) 176 session.run("mypy", "google", "tests") 177 178 179@nox.session(python="3.6") 180def cover(session): 181 """Run the final coverage report. 182 183 This outputs the coverage report aggregating coverage from the unit 184 test runs (not system test runs), and then erases coverage data. 185 """ 186 session.install("coverage", "pytest-cov") 187 session.run("coverage", "report", "--show-missing", "--fail-under=100") 188 session.run("coverage", "erase") 189 190 191@nox.session(python="3.8") 192def docs(session): 193 """Build the docs for this library.""" 194 195 session.install("-e", ".[grpc, grpcgcp]") 196 session.install("sphinx==4.0.1", "alabaster", "recommonmark") 197 198 shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) 199 session.run( 200 "sphinx-build", 201 "-W", # warnings as errors 202 "-T", # show full traceback on exception 203 "-N", # no colors 204 "-b", 205 "html", 206 "-d", 207 os.path.join("docs", "_build", "doctrees", ""), 208 os.path.join("docs", ""), 209 os.path.join("docs", "_build", "html", ""), 210 ) 211 212 213@nox.session(python="3.8") 214def docfx(session): 215 """Build the docfx yaml files for this library.""" 216 217 session.install("-e", ".") 218 session.install( 219 "sphinx==4.0.1", "alabaster", "recommonmark", "gcp-sphinx-docfx-yaml" 220 ) 221 222 shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) 223 session.run( 224 "sphinx-build", 225 "-T", # show full traceback on exception 226 "-N", # no colors 227 "-D", 228 ( 229 "extensions=sphinx.ext.autodoc," 230 "sphinx.ext.autosummary," 231 "docfx_yaml.extension," 232 "sphinx.ext.intersphinx," 233 "sphinx.ext.coverage," 234 "sphinx.ext.napoleon," 235 "sphinx.ext.todo," 236 "sphinx.ext.viewcode," 237 "recommonmark" 238 ), 239 "-b", 240 "html", 241 "-d", 242 os.path.join("docs", "_build", "doctrees", ""), 243 os.path.join("docs", ""), 244 os.path.join("docs", "_build", "html", ""), 245 ) 246