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
9097# todo(kolea2): temporary workaround to install pinned dep version
9198INSTALL_LIBRARY_FROM_SOURCE = False
99+
100+ # Error if a python version is missing
101+ nox .options .error_on_missing_interpreters = True
102+
92103#
93104# Style Checks
94105#
95106
96107
97- def _determine_local_import_names (start_dir ) :
108+ def _determine_local_import_names (start_dir : str ) -> List [ str ] :
98109 """Determines all import names that should be considered "local".
99110
100111 This is used when running the linter to insure that import order is
@@ -132,18 +143,34 @@ def _determine_local_import_names(start_dir):
132143
133144
134145@nox .session
135- def lint (session ):
136- session .install ("flake8" , "flake8-import-order" )
146+ def lint (session : nox .sessions .Session ) -> None :
147+ if not TEST_CONFIG ["enforce_type_hints" ]:
148+ session .install ("flake8" , "flake8-import-order" )
149+ else :
150+ session .install ("flake8" , "flake8-import-order" , "flake8-annotations" )
137151
138152 local_names = _determine_local_import_names ("." )
139153 args = FLAKE8_COMMON_ARGS + [
140154 "--application-import-names" ,
141155 "," .join (local_names ),
142- "."
156+ "." ,
143157 ]
144158 session .run ("flake8" , * args )
145159
146160
161+ #
162+ # Black
163+ #
164+
165+
166+ @nox .session
167+ def blacken (session : nox .sessions .Session ) -> None :
168+ session .install (BLACK_VERSION )
169+ python_files = [path for path in os .listdir ("." ) if path .endswith (".py" )]
170+
171+ session .run ("black" , * python_files )
172+
173+
147174#
148175# Sample Tests
149176#
@@ -152,13 +179,24 @@ def lint(session):
152179PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml" ]
153180
154181
155- def _session_tests (session , post_install = None ):
182+ def _session_tests (
183+ session : nox .sessions .Session , post_install : Callable = None
184+ ) -> None :
185+ if TEST_CONFIG ["pip_version_override" ]:
186+ pip_version = TEST_CONFIG ["pip_version_override" ]
187+ session .install (f"pip=={ pip_version } " )
156188 """Runs py.test for a particular project."""
157189 if os .path .exists ("requirements.txt" ):
158- session .install ("-r" , "requirements.txt" )
190+ if os .path .exists ("constraints.txt" ):
191+ session .install ("-r" , "requirements.txt" , "-c" , "constraints.txt" )
192+ else :
193+ session .install ("-r" , "requirements.txt" )
159194
160195 if os .path .exists ("requirements-test.txt" ):
161- session .install ("-r" , "requirements-test.txt" )
196+ if os .path .exists ("constraints-test.txt" ):
197+ session .install ("-r" , "requirements-test.txt" , "-c" , "constraints-test.txt" )
198+ else :
199+ session .install ("-r" , "requirements-test.txt" )
162200
163201 if INSTALL_LIBRARY_FROM_SOURCE :
164202 session .install ("-e" , _get_repo_root ())
@@ -173,27 +211,27 @@ def _session_tests(session, post_install=None):
173211 # on travis where slow and flaky tests are excluded.
174212 # See http://doc.pytest.org/en/latest/_modules/_pytest/main.html
175213 success_codes = [0 , 5 ],
176- env = get_pytest_env_vars ()
214+ env = get_pytest_env_vars (),
177215 )
178216
179217
180218@nox .session (python = ALL_VERSIONS )
181- def py (session ) :
219+ def py (session : nox . sessions . Session ) -> None :
182220 """Runs py.test for a sample using the specified version of Python."""
183221 if session .python in TESTED_VERSIONS :
184222 _session_tests (session )
185223 else :
186- session .skip ("SKIPPED: {} tests are disabled for this sample." . format (
187- session .python
188- ))
224+ session .skip (
225+ "SKIPPED: {} tests are disabled for this sample." . format ( session .python )
226+ )
189227
190228
191229#
192230# Readmegen
193231#
194232
195233
196- def _get_repo_root ():
234+ def _get_repo_root () -> Optional [ str ] :
197235 """ Returns the root folder of the project. """
198236 # Get root of this repository. Assume we don't have directories nested deeper than 10 items.
199237 p = Path (os .getcwd ())
@@ -202,6 +240,11 @@ def _get_repo_root():
202240 break
203241 if Path (p / ".git" ).exists ():
204242 return str (p )
243+ # .git is not available in repos cloned via Cloud Build
244+ # setup.py is always in the library's root, so use that instead
245+ # https://github.com/googleapis/synthtool/issues/792
246+ if Path (p / "setup.py" ).exists ():
247+ return str (p )
205248 p = p .parent
206249 raise Exception ("Unable to detect repository root." )
207250
@@ -211,7 +254,7 @@ def _get_repo_root():
211254
212255@nox .session
213256@nox .parametrize ("path" , GENERATED_READMES )
214- def readmegen (session , path ) :
257+ def readmegen (session : nox . sessions . Session , path : str ) -> None :
215258 """(Re-)generates the readme for a sample."""
216259 session .install ("jinja2" , "pyyaml" )
217260 dir_ = os .path .dirname (path )
0 commit comments