blob: 1fd8956fbf019727d7a5002a782f5a425bc16da8 [file] [log] [blame]
release-please[bot]fd2965c2021-08-11 17:00:21 +00001# Copyright 2019 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 print_function
16
17import os
18from pathlib import Path
19import sys
20from typing import Callable, Dict, List, Optional
21
22import nox
23
24
25# WARNING - WARNING - WARNING - WARNING - WARNING
26# WARNING - WARNING - WARNING - WARNING - WARNING
27# DO NOT EDIT THIS FILE EVER!
28# WARNING - WARNING - WARNING - WARNING - WARNING
29# WARNING - WARNING - WARNING - WARNING - WARNING
30
31BLACK_VERSION = "black==19.10b0"
32
33# Copy `noxfile_config.py` to your directory and modify it instead.
34
35# `TEST_CONFIG` dict is a configuration hook that allows users to
36# modify the test configurations. The values here should be in sync
37# with `noxfile_config.py`. Users will copy `noxfile_config.py` into
38# their directory and modify it.
39
40TEST_CONFIG = {
41 # You can opt out from the test for specific Python versions.
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +000042 "ignored_versions": [],
release-please[bot]fd2965c2021-08-11 17:00:21 +000043 # Old samples are opted out of enforcing Python type hints
44 # All new samples should feature them
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +000045 "enforce_type_hints": False,
release-please[bot]fd2965c2021-08-11 17:00:21 +000046 # An envvar key for determining the project id to use. Change it
47 # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a
48 # build specific Cloud project. You can also use your own string
49 # to use your own Cloud project.
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +000050 "gcloud_project_env": "GOOGLE_CLOUD_PROJECT",
release-please[bot]fd2965c2021-08-11 17:00:21 +000051 # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT',
52 # If you need to use a specific version of pip,
53 # change pip_version_override to the string representation
54 # of the version number, for example, "20.2.4"
55 "pip_version_override": None,
56 # A dictionary you want to inject into your test. Don't put any
57 # secrets here. These values will override predefined values.
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +000058 "envs": {},
release-please[bot]fd2965c2021-08-11 17:00:21 +000059}
60
61
62try:
63 # Ensure we can import noxfile_config in the project's directory.
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +000064 sys.path.append(".")
release-please[bot]fd2965c2021-08-11 17:00:21 +000065 from noxfile_config import TEST_CONFIG_OVERRIDE
66except ImportError as e:
67 print("No user noxfile_config found: detail: {}".format(e))
68 TEST_CONFIG_OVERRIDE = {}
69
70# Update the TEST_CONFIG with the user supplied values.
71TEST_CONFIG.update(TEST_CONFIG_OVERRIDE)
72
73
74def get_pytest_env_vars() -> Dict[str, str]:
75 """Returns a dict for pytest invocation."""
76 ret = {}
77
78 # Override the GCLOUD_PROJECT and the alias.
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +000079 env_key = TEST_CONFIG["gcloud_project_env"]
release-please[bot]fd2965c2021-08-11 17:00:21 +000080 # This should error out if not set.
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +000081 ret["GOOGLE_CLOUD_PROJECT"] = os.environ[env_key]
release-please[bot]fd2965c2021-08-11 17:00:21 +000082
83 # Apply user supplied envs.
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +000084 ret.update(TEST_CONFIG["envs"])
release-please[bot]fd2965c2021-08-11 17:00:21 +000085 return ret
86
87
88# DO NOT EDIT - automatically generated.
gcf-owl-bot[bot]34a3c932021-08-13 15:48:19 +000089# All versions used to test samples.
90ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9"]
release-please[bot]fd2965c2021-08-11 17:00:21 +000091
92# Any default versions that should be ignored.
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +000093IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"]
release-please[bot]fd2965c2021-08-11 17:00:21 +000094
95TESTED_VERSIONS = sorted([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS])
96
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +000097INSTALL_LIBRARY_FROM_SOURCE = os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False) in (
98 "True",
99 "true",
100)
gcf-owl-bot[bot]90bd3b52021-09-30 15:44:26 +0000101
102# Error if a python version is missing
103nox.options.error_on_missing_interpreters = True
104
release-please[bot]fd2965c2021-08-11 17:00:21 +0000105#
106# Style Checks
107#
108
109
110def _determine_local_import_names(start_dir: str) -> List[str]:
111 """Determines all import names that should be considered "local".
112
113 This is used when running the linter to insure that import order is
114 properly checked.
115 """
116 file_ext_pairs = [os.path.splitext(path) for path in os.listdir(start_dir)]
117 return [
118 basename
119 for basename, extension in file_ext_pairs
120 if extension == ".py"
121 or os.path.isdir(os.path.join(start_dir, basename))
122 and basename not in ("__pycache__")
123 ]
124
125
126# Linting with flake8.
127#
128# We ignore the following rules:
129# E203: whitespace before ‘:’
130# E266: too many leading ‘#’ for block comment
131# E501: line too long
132# I202: Additional newline in a section of imports
133#
134# We also need to specify the rules which are ignored by default:
135# ['E226', 'W504', 'E126', 'E123', 'W503', 'E24', 'E704', 'E121']
136FLAKE8_COMMON_ARGS = [
137 "--show-source",
138 "--builtin=gettext",
139 "--max-complexity=20",
140 "--import-order-style=google",
141 "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py",
142 "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202",
143 "--max-line-length=88",
144]
145
146
147@nox.session
148def lint(session: nox.sessions.Session) -> None:
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +0000149 if not TEST_CONFIG["enforce_type_hints"]:
release-please[bot]fd2965c2021-08-11 17:00:21 +0000150 session.install("flake8", "flake8-import-order")
151 else:
152 session.install("flake8", "flake8-import-order", "flake8-annotations")
153
154 local_names = _determine_local_import_names(".")
155 args = FLAKE8_COMMON_ARGS + [
156 "--application-import-names",
157 ",".join(local_names),
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +0000158 ".",
release-please[bot]fd2965c2021-08-11 17:00:21 +0000159 ]
160 session.run("flake8", *args)
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +0000161
162
release-please[bot]fd2965c2021-08-11 17:00:21 +0000163#
164# Black
165#
166
167
168@nox.session
169def blacken(session: nox.sessions.Session) -> None:
170 session.install(BLACK_VERSION)
171 python_files = [path for path in os.listdir(".") if path.endswith(".py")]
172
173 session.run("black", *python_files)
174
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +0000175
release-please[bot]fd2965c2021-08-11 17:00:21 +0000176#
177# Sample Tests
178#
179
180
181PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml"]
182
183
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +0000184def _session_tests(
185 session: nox.sessions.Session, post_install: Callable = None
186) -> None:
release-please[bot]fd2965c2021-08-11 17:00:21 +0000187 if TEST_CONFIG["pip_version_override"]:
188 pip_version = TEST_CONFIG["pip_version_override"]
189 session.install(f"pip=={pip_version}")
190 """Runs py.test for a particular project."""
191 if os.path.exists("requirements.txt"):
192 if os.path.exists("constraints.txt"):
193 session.install("-r", "requirements.txt", "-c", "constraints.txt")
194 else:
195 session.install("-r", "requirements.txt")
196
197 if os.path.exists("requirements-test.txt"):
198 if os.path.exists("constraints-test.txt"):
199 session.install("-r", "requirements-test.txt", "-c", "constraints-test.txt")
200 else:
201 session.install("-r", "requirements-test.txt")
202
203 if INSTALL_LIBRARY_FROM_SOURCE:
204 session.install("-e", _get_repo_root())
205
206 if post_install:
207 post_install(session)
208
209 session.run(
210 "pytest",
211 *(PYTEST_COMMON_ARGS + session.posargs),
212 # Pytest will return 5 when no tests are collected. This can happen
213 # on travis where slow and flaky tests are excluded.
214 # See http://doc.pytest.org/en/latest/_modules/_pytest/main.html
215 success_codes=[0, 5],
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +0000216 env=get_pytest_env_vars(),
release-please[bot]fd2965c2021-08-11 17:00:21 +0000217 )
218
219
220@nox.session(python=ALL_VERSIONS)
221def py(session: nox.sessions.Session) -> None:
222 """Runs py.test for a sample using the specified version of Python."""
223 if session.python in TESTED_VERSIONS:
224 _session_tests(session)
225 else:
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +0000226 session.skip(
227 "SKIPPED: {} tests are disabled for this sample.".format(session.python)
228 )
release-please[bot]fd2965c2021-08-11 17:00:21 +0000229
230
231#
232# Readmegen
233#
234
235
236def _get_repo_root() -> Optional[str]:
237 """ Returns the root folder of the project. """
238 # Get root of this repository. Assume we don't have directories nested deeper than 10 items.
239 p = Path(os.getcwd())
240 for i in range(10):
241 if p is None:
242 break
243 if Path(p / ".git").exists():
244 return str(p)
245 # .git is not available in repos cloned via Cloud Build
246 # setup.py is always in the library's root, so use that instead
247 # https://github.com/googleapis/synthtool/issues/792
248 if Path(p / "setup.py").exists():
249 return str(p)
250 p = p.parent
251 raise Exception("Unable to detect repository root.")
252
253
254GENERATED_READMES = sorted([x for x in Path(".").rglob("*.rst.in")])
255
256
257@nox.session
258@nox.parametrize("path", GENERATED_READMES)
259def readmegen(session: nox.sessions.Session, path: str) -> None:
260 """(Re-)generates the readme for a sample."""
261 session.install("jinja2", "pyyaml")
262 dir_ = os.path.dirname(path)
263
264 if os.path.exists(os.path.join(dir_, "requirements.txt")):
265 session.install("-r", os.path.join(dir_, "requirements.txt"))
266
267 in_file = os.path.join(dir_, "README.rst.in")
268 session.run(
269 "python", _get_repo_root() + "/scripts/readme-gen/readme_gen.py", in_file
270 )