blob: b008613f03ff85765347ad2887f9046a41c83bf3 [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)
release-please[bot]fd2965c2021-08-11 17:00:21 +0000101#
102# Style Checks
103#
104
105
106def _determine_local_import_names(start_dir: str) -> List[str]:
107 """Determines all import names that should be considered "local".
108
109 This is used when running the linter to insure that import order is
110 properly checked.
111 """
112 file_ext_pairs = [os.path.splitext(path) for path in os.listdir(start_dir)]
113 return [
114 basename
115 for basename, extension in file_ext_pairs
116 if extension == ".py"
117 or os.path.isdir(os.path.join(start_dir, basename))
118 and basename not in ("__pycache__")
119 ]
120
121
122# Linting with flake8.
123#
124# We ignore the following rules:
125# E203: whitespace before ‘:’
126# E266: too many leading ‘#’ for block comment
127# E501: line too long
128# I202: Additional newline in a section of imports
129#
130# We also need to specify the rules which are ignored by default:
131# ['E226', 'W504', 'E126', 'E123', 'W503', 'E24', 'E704', 'E121']
132FLAKE8_COMMON_ARGS = [
133 "--show-source",
134 "--builtin=gettext",
135 "--max-complexity=20",
136 "--import-order-style=google",
137 "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py",
138 "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202",
139 "--max-line-length=88",
140]
141
142
143@nox.session
144def lint(session: nox.sessions.Session) -> None:
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +0000145 if not TEST_CONFIG["enforce_type_hints"]:
release-please[bot]fd2965c2021-08-11 17:00:21 +0000146 session.install("flake8", "flake8-import-order")
147 else:
148 session.install("flake8", "flake8-import-order", "flake8-annotations")
149
150 local_names = _determine_local_import_names(".")
151 args = FLAKE8_COMMON_ARGS + [
152 "--application-import-names",
153 ",".join(local_names),
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +0000154 ".",
release-please[bot]fd2965c2021-08-11 17:00:21 +0000155 ]
156 session.run("flake8", *args)
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +0000157
158
release-please[bot]fd2965c2021-08-11 17:00:21 +0000159#
160# Black
161#
162
163
164@nox.session
165def blacken(session: nox.sessions.Session) -> None:
166 session.install(BLACK_VERSION)
167 python_files = [path for path in os.listdir(".") if path.endswith(".py")]
168
169 session.run("black", *python_files)
170
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +0000171
release-please[bot]fd2965c2021-08-11 17:00:21 +0000172#
173# Sample Tests
174#
175
176
177PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml"]
178
179
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +0000180def _session_tests(
181 session: nox.sessions.Session, post_install: Callable = None
182) -> None:
release-please[bot]fd2965c2021-08-11 17:00:21 +0000183 if TEST_CONFIG["pip_version_override"]:
184 pip_version = TEST_CONFIG["pip_version_override"]
185 session.install(f"pip=={pip_version}")
186 """Runs py.test for a particular project."""
187 if os.path.exists("requirements.txt"):
188 if os.path.exists("constraints.txt"):
189 session.install("-r", "requirements.txt", "-c", "constraints.txt")
190 else:
191 session.install("-r", "requirements.txt")
192
193 if os.path.exists("requirements-test.txt"):
194 if os.path.exists("constraints-test.txt"):
195 session.install("-r", "requirements-test.txt", "-c", "constraints-test.txt")
196 else:
197 session.install("-r", "requirements-test.txt")
198
199 if INSTALL_LIBRARY_FROM_SOURCE:
200 session.install("-e", _get_repo_root())
201
202 if post_install:
203 post_install(session)
204
205 session.run(
206 "pytest",
207 *(PYTEST_COMMON_ARGS + session.posargs),
208 # Pytest will return 5 when no tests are collected. This can happen
209 # on travis where slow and flaky tests are excluded.
210 # See http://doc.pytest.org/en/latest/_modules/_pytest/main.html
211 success_codes=[0, 5],
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +0000212 env=get_pytest_env_vars(),
release-please[bot]fd2965c2021-08-11 17:00:21 +0000213 )
214
215
216@nox.session(python=ALL_VERSIONS)
217def py(session: nox.sessions.Session) -> None:
218 """Runs py.test for a sample using the specified version of Python."""
219 if session.python in TESTED_VERSIONS:
220 _session_tests(session)
221 else:
gcf-owl-bot[bot]167f8602021-09-17 15:36:36 +0000222 session.skip(
223 "SKIPPED: {} tests are disabled for this sample.".format(session.python)
224 )
release-please[bot]fd2965c2021-08-11 17:00:21 +0000225
226
227#
228# Readmegen
229#
230
231
232def _get_repo_root() -> Optional[str]:
233 """ Returns the root folder of the project. """
234 # Get root of this repository. Assume we don't have directories nested deeper than 10 items.
235 p = Path(os.getcwd())
236 for i in range(10):
237 if p is None:
238 break
239 if Path(p / ".git").exists():
240 return str(p)
241 # .git is not available in repos cloned via Cloud Build
242 # setup.py is always in the library's root, so use that instead
243 # https://github.com/googleapis/synthtool/issues/792
244 if Path(p / "setup.py").exists():
245 return str(p)
246 p = p.parent
247 raise Exception("Unable to detect repository root.")
248
249
250GENERATED_READMES = sorted([x for x in Path(".").rglob("*.rst.in")])
251
252
253@nox.session
254@nox.parametrize("path", GENERATED_READMES)
255def readmegen(session: nox.sessions.Session, path: str) -> None:
256 """(Re-)generates the readme for a sample."""
257 session.install("jinja2", "pyyaml")
258 dir_ = os.path.dirname(path)
259
260 if os.path.exists(os.path.join(dir_, "requirements.txt")):
261 session.install("-r", os.path.join(dir_, "requirements.txt"))
262
263 in_file = os.path.join(dir_, "README.rst.in")
264 session.run(
265 "python", _get_repo_root() + "/scripts/readme-gen/readme_gen.py", in_file
266 )