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" ],
42+ 'ignored_versions' : ["2.7" ],
43+
44+ # Old samples are opted out of enforcing Python type hints
45+ # All new samples should feature them
46+ 'enforce_type_hints' : False ,
47+
4148 # An envvar key for determining the project id to use. Change it
4249 # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a
4350 # build specific Cloud project. You can also use your own string
4451 # to use your own Cloud project.
45- " gcloud_project_env" : " GOOGLE_CLOUD_PROJECT" ,
52+ ' gcloud_project_env' : ' GOOGLE_CLOUD_PROJECT' ,
4653 # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT',
54+ # If you need to use a specific version of pip,
55+ # change pip_version_override to the string representation
56+ # of the version number, for example, "20.2.4"
57+ "pip_version_override" : None ,
4758 # A dictionary you want to inject into your test. Don't put any
4859 # secrets here. These values will override predefined values.
49- " envs" : {},
60+ ' envs' : {},
5061}
5162
5263
5364try :
5465 # Ensure we can import noxfile_config in the project's directory.
55- sys .path .append ("." )
66+ sys .path .append ('.' )
5667 from noxfile_config import TEST_CONFIG_OVERRIDE
5768except ImportError as e :
5869 print ("No user noxfile_config found: detail: {}" .format (e ))
6273TEST_CONFIG .update (TEST_CONFIG_OVERRIDE )
6374
6475
65- def get_pytest_env_vars ():
76+ def get_pytest_env_vars () -> Dict [ str , str ] :
6677 """Returns a dict for pytest invocation."""
6778 ret = {}
6879
6980 # Override the GCLOUD_PROJECT and the alias.
70- env_key = TEST_CONFIG [" gcloud_project_env" ]
81+ env_key = TEST_CONFIG [' gcloud_project_env' ]
7182 # This should error out if not set.
72- ret [" GOOGLE_CLOUD_PROJECT" ] = os .environ [env_key ]
83+ ret [' GOOGLE_CLOUD_PROJECT' ] = os .environ [env_key ]
7384
7485 # Apply user supplied envs.
75- ret .update (TEST_CONFIG [" envs" ])
86+ ret .update (TEST_CONFIG [' envs' ])
7687 return ret
7788
7889
7990# DO NOT EDIT - automatically generated.
8091# All versions used to tested samples.
81- ALL_VERSIONS = ["2.7" , "3.6" , "3.7" , "3.8" ]
92+ ALL_VERSIONS = ["2.7" , "3.6" , "3.7" , "3.8" , "3.9" ]
8293
8394# Any default versions that should be ignored.
84- IGNORED_VERSIONS = TEST_CONFIG [" ignored_versions" ]
95+ IGNORED_VERSIONS = TEST_CONFIG [' ignored_versions' ]
8596
8697TESTED_VERSIONS = sorted ([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS ])
8798
88- INSTALL_LIBRARY_FROM_SOURCE = bool ( os .environ .get ("INSTALL_LIBRARY_FROM_SOURCE" , False ))
99+ INSTALL_LIBRARY_FROM_SOURCE = os .environ .get ("INSTALL_LIBRARY_FROM_SOURCE" , False ) in ( "True" , "true" )
89100#
90101# Style Checks
91102#
92103
93104
94- def _determine_local_import_names (start_dir ) :
105+ def _determine_local_import_names (start_dir : str ) -> List [ str ] :
95106 """Determines all import names that should be considered "local".
96107
97108 This is used when running the linter to insure that import order is
@@ -129,17 +140,30 @@ def _determine_local_import_names(start_dir):
129140
130141
131142@nox .session
132- def lint (session ):
133- session .install ("flake8" , "flake8-import-order" )
143+ def lint (session : nox .sessions .Session ) -> None :
144+ if not TEST_CONFIG ['enforce_type_hints' ]:
145+ session .install ("flake8" , "flake8-import-order" )
146+ else :
147+ session .install ("flake8" , "flake8-import-order" , "flake8-annotations" )
134148
135149 local_names = _determine_local_import_names ("." )
136150 args = FLAKE8_COMMON_ARGS + [
137151 "--application-import-names" ,
138152 "," .join (local_names ),
139- "." ,
153+ "."
140154 ]
141155 session .run ("flake8" , * args )
156+ #
157+ # Black
158+ #
159+
142160
161+ @nox .session
162+ def blacken (session : nox .sessions .Session ) -> None :
163+ session .install (BLACK_VERSION )
164+ python_files = [path for path in os .listdir ("." ) if path .endswith (".py" )]
165+
166+ session .run ("black" , * python_files )
143167
144168#
145169# Sample Tests
@@ -149,13 +173,22 @@ def lint(session):
149173PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml" ]
150174
151175
152- def _session_tests (session , post_install = None ):
176+ def _session_tests (session : nox .sessions .Session , post_install : Callable = None ) -> None :
177+ if TEST_CONFIG ["pip_version_override" ]:
178+ pip_version = TEST_CONFIG ["pip_version_override" ]
179+ session .install (f"pip=={ pip_version } " )
153180 """Runs py.test for a particular project."""
154181 if os .path .exists ("requirements.txt" ):
155- session .install ("-r" , "requirements.txt" )
182+ if os .path .exists ("constraints.txt" ):
183+ session .install ("-r" , "requirements.txt" , "-c" , "constraints.txt" )
184+ else :
185+ session .install ("-r" , "requirements.txt" )
156186
157187 if os .path .exists ("requirements-test.txt" ):
158- session .install ("-r" , "requirements-test.txt" )
188+ if os .path .exists ("constraints-test.txt" ):
189+ session .install ("-r" , "requirements-test.txt" , "-c" , "constraints-test.txt" )
190+ else :
191+ session .install ("-r" , "requirements-test.txt" )
159192
160193 if INSTALL_LIBRARY_FROM_SOURCE :
161194 session .install ("-e" , _get_repo_root ())
@@ -175,22 +208,22 @@ def _session_tests(session, post_install=None):
175208
176209
177210@nox .session (python = ALL_VERSIONS )
178- def py (session ) :
211+ def py (session : nox . sessions . Session ) -> None :
179212 """Runs py.test for a sample using the specified version of Python."""
180213 if session .python in TESTED_VERSIONS :
181214 _session_tests (session )
182215 else :
183- session .skip (
184- "SKIPPED: {} tests are disabled for this sample." . format ( session .python )
185- )
216+ session .skip ("SKIPPED: {} tests are disabled for this sample." . format (
217+ session .python
218+ ))
186219
187220
188221#
189222# Readmegen
190223#
191224
192225
193- def _get_repo_root ():
226+ def _get_repo_root () -> Optional [ str ] :
194227 """ Returns the root folder of the project. """
195228 # Get root of this repository. Assume we don't have directories nested deeper than 10 items.
196229 p = Path (os .getcwd ())
@@ -199,6 +232,11 @@ def _get_repo_root():
199232 break
200233 if Path (p / ".git" ).exists ():
201234 return str (p )
235+ # .git is not available in repos cloned via Cloud Build
236+ # setup.py is always in the library's root, so use that instead
237+ # https://github.com/googleapis/synthtool/issues/792
238+ if Path (p / "setup.py" ).exists ():
239+ return str (p )
202240 p = p .parent
203241 raise Exception ("Unable to detect repository root." )
204242
@@ -208,7 +246,7 @@ def _get_repo_root():
208246
209247@nox .session
210248@nox .parametrize ("path" , GENERATED_READMES )
211- def readmegen (session , path ) :
249+ def readmegen (session : nox . sessions . Session , path : str ) -> None :
212250 """(Re-)generates the readme for a sample."""
213251 session .install ("jinja2" , "pyyaml" )
214252 dir_ = os .path .dirname (path )
0 commit comments