1717import os
1818from pathlib import Path
1919import sys
20+ from typing import Callable , Dict , List , Optional
2021
2122import nox
2223
2728# WARNING - WARNING - WARNING - WARNING - WARNING
2829# WARNING - WARNING - WARNING - WARNING - WARNING
2930
30- # Copy `noxfile_config.py` to your directory and modify it instead.
31+ BLACK_VERSION = "black==19.10b0"
3132
33+ # Copy `noxfile_config.py` to your directory and modify it instead.
3234
3335# `TEST_CONFIG` dict is a configuration hook that allows users to
3436# modify the test configurations. The values here should be in sync
3739
3840TEST_CONFIG = {
3941 # You can opt out from the test for specific Python versions.
40- 'ignored_versions' : ["2.7" ],
41-
42+ "ignored_versions" : [],
43+ # Old samples are opted out of enforcing Python type hints
44+ # All new samples should feature them
45+ "enforce_type_hints" : False ,
4246 # An envvar key for determining the project id to use. Change it
4347 # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a
4448 # build specific Cloud project. You can also use your own string
4549 # to use your own Cloud project.
46- ' gcloud_project_env' : ' GOOGLE_CLOUD_PROJECT' ,
50+ " gcloud_project_env" : " GOOGLE_CLOUD_PROJECT" ,
4751 # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT',
48-
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 ,
4956 # A dictionary you want to inject into your test. Don't put any
5057 # secrets here. These values will override predefined values.
51- ' envs' : {},
58+ " envs" : {},
5259}
5360
5461
5562try :
5663 # Ensure we can import noxfile_config in the project's directory.
57- sys .path .append ('.' )
64+ sys .path .append ("." )
5865 from noxfile_config import TEST_CONFIG_OVERRIDE
5966except ImportError as e :
6067 print ("No user noxfile_config found: detail: {}" .format (e ))
6471TEST_CONFIG .update (TEST_CONFIG_OVERRIDE )
6572
6673
67- def get_pytest_env_vars ():
74+ def get_pytest_env_vars () -> Dict [ str , str ] :
6875 """Returns a dict for pytest invocation."""
6976 ret = {}
7077
7178 # Override the GCLOUD_PROJECT and the alias.
72- env_key = TEST_CONFIG [' gcloud_project_env' ]
79+ env_key = TEST_CONFIG [" gcloud_project_env" ]
7380 # This should error out if not set.
74- ret [' GOOGLE_CLOUD_PROJECT' ] = os .environ [env_key ]
81+ ret [" GOOGLE_CLOUD_PROJECT" ] = os .environ [env_key ]
7582
7683 # Apply user supplied envs.
77- ret .update (TEST_CONFIG [' envs' ])
84+ ret .update (TEST_CONFIG [" envs" ])
7885 return ret
7986
8087
8188# DO NOT EDIT - automatically generated.
82- # All versions used to tested samples.
83- ALL_VERSIONS = ["2. 7" , "3.6 " , "3.7 " , "3.8 " ]
89+ # All versions used to test samples.
90+ ALL_VERSIONS = ["3.6" , "3. 7" , "3.8 " , "3.9 " , "3.10 " ]
8491
8592# Any default versions that should be ignored.
86- IGNORED_VERSIONS = TEST_CONFIG [' ignored_versions' ]
93+ IGNORED_VERSIONS = TEST_CONFIG [" ignored_versions" ]
8794
8895TESTED_VERSIONS = sorted ([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS ])
8996
90- INSTALL_LIBRARY_FROM_SOURCE = bool (os .environ .get ("INSTALL_LIBRARY_FROM_SOURCE" , False ))
97+ INSTALL_LIBRARY_FROM_SOURCE = os .environ .get ("INSTALL_LIBRARY_FROM_SOURCE" , False ) in (
98+ "True" ,
99+ "true" ,
100+ )
101+
102+ # Error if a python version is missing
103+ nox .options .error_on_missing_interpreters = True
104+
91105#
92106# Style Checks
93107#
94108
95109
96- def _determine_local_import_names (start_dir ) :
110+ def _determine_local_import_names (start_dir : str ) -> List [ str ] :
97111 """Determines all import names that should be considered "local".
98112
99113 This is used when running the linter to insure that import order is
@@ -131,18 +145,34 @@ def _determine_local_import_names(start_dir):
131145
132146
133147@nox .session
134- def lint (session ):
135- session .install ("flake8" , "flake8-import-order" )
148+ def lint (session : nox .sessions .Session ) -> None :
149+ if not TEST_CONFIG ["enforce_type_hints" ]:
150+ session .install ("flake8" , "flake8-import-order" )
151+ else :
152+ session .install ("flake8" , "flake8-import-order" , "flake8-annotations" )
136153
137154 local_names = _determine_local_import_names ("." )
138155 args = FLAKE8_COMMON_ARGS + [
139156 "--application-import-names" ,
140157 "," .join (local_names ),
141- "."
158+ "." ,
142159 ]
143160 session .run ("flake8" , * args )
144161
145162
163+ #
164+ # Black
165+ #
166+
167+
168+ @nox .session
169+ def 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+
175+
146176#
147177# Sample Tests
148178#
@@ -151,13 +181,24 @@ def lint(session):
151181PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml" ]
152182
153183
154- def _session_tests (session , post_install = None ):
184+ def _session_tests (
185+ session : nox .sessions .Session , post_install : Callable = None
186+ ) -> None :
187+ if TEST_CONFIG ["pip_version_override" ]:
188+ pip_version = TEST_CONFIG ["pip_version_override" ]
189+ session .install (f"pip=={ pip_version } " )
155190 """Runs py.test for a particular project."""
156191 if os .path .exists ("requirements.txt" ):
157- session .install ("-r" , "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" )
158196
159197 if os .path .exists ("requirements-test.txt" ):
160- session .install ("-r" , "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" )
161202
162203 if INSTALL_LIBRARY_FROM_SOURCE :
163204 session .install ("-e" , _get_repo_root ())
@@ -172,27 +213,27 @@ def _session_tests(session, post_install=None):
172213 # on travis where slow and flaky tests are excluded.
173214 # See http://doc.pytest.org/en/latest/_modules/_pytest/main.html
174215 success_codes = [0 , 5 ],
175- env = get_pytest_env_vars ()
216+ env = get_pytest_env_vars (),
176217 )
177218
178219
179220@nox .session (python = ALL_VERSIONS )
180- def py (session ) :
221+ def py (session : nox . sessions . Session ) -> None :
181222 """Runs py.test for a sample using the specified version of Python."""
182223 if session .python in TESTED_VERSIONS :
183224 _session_tests (session )
184225 else :
185- session .skip ("SKIPPED: {} tests are disabled for this sample." . format (
186- session .python
187- ))
226+ session .skip (
227+ "SKIPPED: {} tests are disabled for this sample." . format ( session .python )
228+ )
188229
189230
190231#
191232# Readmegen
192233#
193234
194235
195- def _get_repo_root ():
236+ def _get_repo_root () -> Optional [ str ] :
196237 """ Returns the root folder of the project. """
197238 # Get root of this repository. Assume we don't have directories nested deeper than 10 items.
198239 p = Path (os .getcwd ())
@@ -201,6 +242,11 @@ def _get_repo_root():
201242 break
202243 if Path (p / ".git" ).exists ():
203244 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 )
204250 p = p .parent
205251 raise Exception ("Unable to detect repository root." )
206252
@@ -210,7 +256,7 @@ def _get_repo_root():
210256
211257@nox .session
212258@nox .parametrize ("path" , GENERATED_READMES )
213- def readmegen (session , path ) :
259+ def readmegen (session : nox . sessions . Session , path : str ) -> None :
214260 """(Re-)generates the readme for a sample."""
215261 session .install ("jinja2" , "pyyaml" )
216262 dir_ = os .path .dirname (path )
0 commit comments