1212# See the License for the specific language governing permissions and
1313# limitations under the License.
1414
15+ from __future__ import annotations
1516from __future__ import print_function
1617
18+ from collections .abc import Callable
1719import glob
1820import os
1921from pathlib import Path
2022import sys
21- from typing import Callable , Dict , Optional
2223
2324import nox
2425
26+
2527# WARNING - WARNING - WARNING - WARNING - WARNING
2628# WARNING - WARNING - WARNING - WARNING - WARNING
27- # DO NOT EDIT THIS FILE EVER !
29+ # BE CAREFUL WHEN EDITING THIS FILE!
2830# WARNING - WARNING - WARNING - WARNING - WARNING
2931# WARNING - WARNING - WARNING - WARNING - WARNING
3032
31- BLACK_VERSION = "black==22.3.0"
32- ISORT_VERSION = "isort==5.10.1"
33-
3433# Copy `noxfile_config.py` to your directory and modify it instead.
3534
35+
3636# `TEST_CONFIG` dict is a configuration hook that allows users to
3737# modify the test configurations. The values here should be in sync
3838# with `noxfile_config.py`. Users will copy `noxfile_config.py` into
3939# their directory and modify it.
4040
4141TEST_CONFIG = {
4242 # You can opt out from the test for specific Python versions.
43- "ignored_versions" : [],
43+ "ignored_versions" : ["2.7" , "3.7" , "3.9" , "3.10" , "3.11" ],
4444 # Old samples are opted out of enforcing Python type hints
4545 # All new samples should feature them
4646 "enforce_type_hints" : False ,
7272TEST_CONFIG .update (TEST_CONFIG_OVERRIDE )
7373
7474
75- def get_pytest_env_vars () -> Dict [str , str ]:
75+ def get_pytest_env_vars () -> dict [str , str ]:
7676 """Returns a dict for pytest invocation."""
7777 ret = {}
7878
7979 # Override the GCLOUD_PROJECT and the alias.
8080 env_key = TEST_CONFIG ["gcloud_project_env" ]
8181 # This should error out if not set.
8282 ret ["GOOGLE_CLOUD_PROJECT" ] = os .environ [env_key ]
83+ ret ["GCLOUD_PROJECT" ] = os .environ [env_key ] # deprecated
8384
8485 # Apply user supplied envs.
8586 ret .update (TEST_CONFIG ["envs" ])
8687 return ret
8788
8889
89- # DO NOT EDIT - automatically generated.
90- # All versions used to test samples.
91- ALL_VERSIONS = ["3.7" , "3.8" , "3.9" , "3.10" , "3.11" ]
90+ # All versions used to tested samples.
91+ ALL_VERSIONS = ["2.7" , "3.8" , "3.9" , "3.10" , "3.11" , "3.12" ]
9292
9393# Any default versions that should be ignored.
9494IGNORED_VERSIONS = TEST_CONFIG ["ignored_versions" ]
9595
9696TESTED_VERSIONS = sorted ([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS ])
9797
98- INSTALL_LIBRARY_FROM_SOURCE = os .environ .get ("INSTALL_LIBRARY_FROM_SOURCE" , False ) in (
99- "True" ,
100- "true" ,
101- )
98+ INSTALL_LIBRARY_FROM_SOURCE = bool (os .environ .get ("INSTALL_LIBRARY_FROM_SOURCE" , False ))
10299
103100# Error if a python version is missing
104101nox .options .error_on_missing_interpreters = True
@@ -108,9 +105,26 @@ def get_pytest_env_vars() -> Dict[str, str]:
108105#
109106
110107
108+ def _determine_local_import_names (start_dir : str ) -> list [str ]:
109+ """Determines all import names that should be considered "local".
110+
111+ This is used when running the linter to ensure that import order is
112+ properly checked.
113+ """
114+ file_ext_pairs = [os .path .splitext (path ) for path in os .listdir (start_dir )]
115+ return [
116+ basename
117+ for basename , extension in file_ext_pairs
118+ if extension == ".py"
119+ or os .path .isdir (os .path .join (start_dir , basename ))
120+ and basename not in ("__pycache__" )
121+ ]
122+
123+
111124# Linting with flake8.
112125#
113126# We ignore the following rules:
127+ # ANN101: missing type annotation for self in method
114128# E203: whitespace before ‘:’
115129# E266: too many leading ‘#’ for block comment
116130# E501: line too long
@@ -122,20 +136,24 @@ def get_pytest_env_vars() -> Dict[str, str]:
122136 "--show-source" ,
123137 "--builtin=gettext" ,
124138 "--max-complexity=20" ,
139+ "--import-order-style=google" ,
125140 "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py" ,
126- "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202" ,
141+ "--ignore=ANN101, E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202" ,
127142 "--max-line-length=88" ,
128143]
129144
130145
131146@nox .session
132147def lint (session : nox .sessions .Session ) -> None :
133148 if not TEST_CONFIG ["enforce_type_hints" ]:
134- session .install ("flake8" )
149+ session .install ("flake8" , "flake8-import-order" )
135150 else :
136- session .install ("flake8" , "flake8-annotations" )
151+ session .install ("flake8" , "flake8-import-order" , "flake8- annotations" )
137152
153+ local_names = _determine_local_import_names ("." )
138154 args = FLAKE8_COMMON_ARGS + [
155+ "--application-import-names" ,
156+ "," .join (local_names ),
139157 "." ,
140158 ]
141159 session .run ("flake8" , * args )
@@ -148,30 +166,9 @@ def lint(session: nox.sessions.Session) -> None:
148166
149167@nox .session
150168def blacken (session : nox .sessions .Session ) -> None :
151- """Run black. Format code to uniform standard."""
152- session .install (BLACK_VERSION )
153- python_files = [path for path in os .listdir ("." ) if path .endswith (".py" )]
154-
155- session .run ("black" , * python_files )
156-
157-
158- #
159- # format = isort + black
160- #
161-
162-
163- @nox .session
164- def format (session : nox .sessions .Session ) -> None :
165- """
166- Run isort to sort imports. Then run black
167- to format code to uniform standard.
168- """
169- session .install (BLACK_VERSION , ISORT_VERSION )
169+ session .install ("black" )
170170 python_files = [path for path in os .listdir ("." ) if path .endswith (".py" )]
171171
172- # Use the --fss option to sort imports using strict alphabetical order.
173- # See https://pycqa.github.io/isort/docs/configuration/options.html#force-sort-within-sections
174- session .run ("isort" , "--fss" , * python_files )
175172 session .run ("black" , * python_files )
176173
177174
@@ -187,10 +184,8 @@ def _session_tests(
187184 session : nox .sessions .Session , post_install : Callable = None
188185) -> None :
189186 # check for presence of tests
190- test_list = glob .glob ("**/*_test.py" , recursive = True ) + glob .glob (
191- "**/test_*.py" , recursive = True
192- )
193- test_list .extend (glob .glob ("**/tests" , recursive = True ))
187+ test_list = glob .glob ("*_test.py" ) + glob .glob ("test_*.py" )
188+ test_list .extend (glob .glob ("tests" ))
194189
195190 if len (test_list ) == 0 :
196191 print ("No tests found, skipping directory." )
@@ -199,23 +194,42 @@ def _session_tests(
199194 if TEST_CONFIG ["pip_version_override" ]:
200195 pip_version = TEST_CONFIG ["pip_version_override" ]
201196 session .install (f"pip=={ pip_version } " )
197+ else :
198+ session .install ("--upgrade" , "pip" )
199+
202200 """Runs py.test for a particular project."""
203201 concurrent_args = []
204202 if os .path .exists ("requirements.txt" ):
205- if os .path .exists ("constraints.txt" ):
206- session .install ("-r" , "requirements.txt" , "-c" , "constraints.txt" )
207- else :
208- session .install ("-r" , "requirements.txt" )
209203 with open ("requirements.txt" ) as rfile :
210204 packages = rfile .read ()
205+ if os .path .exists ("constraints.txt" ):
206+ session .install (
207+ "-r" ,
208+ "requirements.txt" ,
209+ "-c" ,
210+ "constraints.txt" ,
211+ "--only-binary" ,
212+ ":all" ,
213+ )
214+ elif "pyspark" in packages :
215+ session .install ("-r" , "requirements.txt" , "--use-pep517" )
216+ else :
217+ session .install ("-r" , "requirements.txt" , "--only-binary" , ":all" )
211218
212219 if os .path .exists ("requirements-test.txt" ):
213- if os .path .exists ("constraints-test.txt" ):
214- session .install ("-r" , "requirements-test.txt" , "-c" , "constraints-test.txt" )
215- else :
216- session .install ("-r" , "requirements-test.txt" )
217220 with open ("requirements-test.txt" ) as rtfile :
218221 packages += rtfile .read ()
222+ if os .path .exists ("constraints-test.txt" ):
223+ session .install (
224+ "-r" ,
225+ "requirements-test.txt" ,
226+ "-c" ,
227+ "constraints-test.txt" ,
228+ "--only-binary" ,
229+ ":all" ,
230+ )
231+ else :
232+ session .install ("-r" , "requirements-test.txt" , "--only-binary" , ":all" )
219233
220234 if INSTALL_LIBRARY_FROM_SOURCE :
221235 session .install ("-e" , _get_repo_root ())
@@ -255,20 +269,16 @@ def py(session: nox.sessions.Session) -> None:
255269#
256270
257271
258- def _get_repo_root () -> Optional [ str ] :
272+ def _get_repo_root () -> str | None :
259273 """Returns the root folder of the project."""
260- # Get root of this repository. Assume we don't have directories nested deeper than 10 items.
274+ # Get root of this repository.
275+ # Assume we don't have directories nested deeper than 10 items.
261276 p = Path (os .getcwd ())
262277 for i in range (10 ):
263278 if p is None :
264279 break
265280 if Path (p / ".git" ).exists ():
266281 return str (p )
267- # .git is not available in repos cloned via Cloud Build
268- # setup.py is always in the library's root, so use that instead
269- # https://github.com/googleapis/synthtool/issues/792
270- if Path (p / "setup.py" ).exists ():
271- return str (p )
272282 p = p .parent
273283 raise Exception ("Unable to detect repository root." )
274284
0 commit comments