Skip to content

Commit 67f3d07

Browse files
authored
chore: microgen - add noxfile for the microgenerator (#2285)
* chore: removes old proof of concept * removes old __init__.py * Adds two utility files to handle basic tasks * Adds a configuration file for the microgenerator * Removes unused comment * chore: adds noxfile.py for the microgenerator
1 parent f8f6872 commit 67f3d07

File tree

1 file changed

+382
-0
lines changed

1 file changed

+382
-0
lines changed

scripts/microgenerator/noxfile.py

Lines changed: 382 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,382 @@
1+
# Copyright 2016 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+
15+
from __future__ import absolute_import
16+
17+
from functools import wraps
18+
import pathlib
19+
import os
20+
import shutil
21+
import nox
22+
import time
23+
24+
25+
MYPY_VERSION = "mypy==1.6.1"
26+
PYTYPE_VERSION = "pytype==2024.9.13"
27+
BLACK_VERSION = "black==23.7.0"
28+
BLACK_PATHS = (".",)
29+
30+
DEFAULT_PYTHON_VERSION = "3.9"
31+
SYSTEM_TEST_PYTHON_VERSIONS = ["3.9", "3.11", "3.12", "3.13"]
32+
UNIT_TEST_PYTHON_VERSIONS = ["3.9", "3.11", "3.12", "3.13"]
33+
CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute()
34+
35+
36+
def _calculate_duration(func):
37+
"""This decorator prints the execution time for the decorated function."""
38+
39+
@wraps(func)
40+
def wrapper(*args, **kwargs):
41+
start = time.monotonic()
42+
result = func(*args, **kwargs)
43+
end = time.monotonic()
44+
total_seconds = round(end - start)
45+
hours = total_seconds // 3600 # Integer division to get hours
46+
remaining_seconds = total_seconds % 3600 # Modulo to find remaining seconds
47+
minutes = remaining_seconds // 60
48+
seconds = remaining_seconds % 60
49+
human_time = f"{hours:}:{minutes:0>2}:{seconds:0>2}"
50+
print(f"Session ran in {total_seconds} seconds ({human_time})")
51+
return result
52+
53+
return wrapper
54+
55+
56+
# 'docfx' is excluded since it only needs to run in 'docs-presubmit'
57+
nox.options.sessions = [
58+
"unit",
59+
"system",
60+
"cover",
61+
"lint",
62+
"lint_setup_py",
63+
"blacken",
64+
"mypy",
65+
"pytype",
66+
"docs",
67+
]
68+
69+
70+
def default(session, install_extras=True):
71+
"""Default unit test session.
72+
73+
This is intended to be run **without** an interpreter set, so
74+
that the current ``python`` (on the ``PATH``) or the version of
75+
Python corresponding to the ``nox`` binary the ``PATH`` can
76+
run the tests.
77+
"""
78+
79+
constraints_path = str(
80+
CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt"
81+
)
82+
83+
# Install all test dependencies, then install local packages in-place.
84+
session.install(
85+
"pytest",
86+
"google-cloud-testutils",
87+
"pytest-cov",
88+
"pytest-xdist",
89+
"freezegun",
90+
"-c",
91+
constraints_path,
92+
)
93+
# We have logic in the magics.py file that checks for whether 'bigquery_magics'
94+
# is imported OR not. If yes, we use a context object from that library.
95+
# If no, we use our own context object from magics.py. In order to exercise
96+
# that logic (and the associated tests) we avoid installing the [ipython] extra
97+
# which has a downstream effect of then avoiding installing bigquery_magics.
98+
if install_extras and session.python == UNIT_TEST_PYTHON_VERSIONS[0]:
99+
install_target = ".[bqstorage,pandas,ipywidgets,geopandas,matplotlib,tqdm,opentelemetry,bigquery_v2]"
100+
elif install_extras: # run against all other UNIT_TEST_PYTHON_VERSIONS
101+
install_target = ".[all]"
102+
else:
103+
install_target = "."
104+
session.install("-e", install_target, "-c", constraints_path)
105+
106+
# Test with some broken "extras" in case the user didn't install the extra
107+
# directly. For example, pandas-gbq is recommended for pandas features, but
108+
# we want to test that we fallback to the previous behavior. For context,
109+
# see internal document go/pandas-gbq-and-bigframes-redundancy.
110+
if session.python == UNIT_TEST_PYTHON_VERSIONS[0]:
111+
session.run("python", "-m", "pip", "uninstall", "pandas-gbq", "-y")
112+
113+
session.run("python", "-m", "pip", "freeze")
114+
115+
# Run py.test against the unit tests.
116+
session.run(
117+
"py.test",
118+
"-n=8",
119+
"--quiet",
120+
"-W default::PendingDeprecationWarning",
121+
"--cov=google/cloud/bigquery",
122+
"--cov=tests/unit",
123+
"--cov-append",
124+
"--cov-config=.coveragerc",
125+
"--cov-report=",
126+
"--cov-fail-under=0",
127+
os.path.join("tests", "unit"),
128+
*session.posargs,
129+
)
130+
131+
132+
@nox.session(python=UNIT_TEST_PYTHON_VERSIONS)
133+
@_calculate_duration
134+
def unit(session):
135+
"""Run the unit test suite."""
136+
137+
default(session)
138+
139+
140+
@nox.session(python=DEFAULT_PYTHON_VERSION)
141+
@_calculate_duration
142+
def mypy(session):
143+
"""Run type checks with mypy."""
144+
145+
session.install("-e", ".[all]")
146+
session.install(MYPY_VERSION)
147+
148+
# Just install the dependencies' type info directly, since "mypy --install-types"
149+
# might require an additional pass.
150+
session.install(
151+
"types-protobuf",
152+
"types-python-dateutil",
153+
"types-requests",
154+
"types-setuptools",
155+
)
156+
session.run("python", "-m", "pip", "freeze")
157+
session.run("mypy", "-p", "google", "--show-traceback")
158+
159+
160+
@nox.session(python=DEFAULT_PYTHON_VERSION)
161+
@_calculate_duration
162+
def pytype(session):
163+
"""Run type checks with pytype."""
164+
# An indirect dependecy attrs==21.1.0 breaks the check, and installing a less
165+
# recent version avoids the error until a possibly better fix is found.
166+
# https://github.com/googleapis/python-bigquery/issues/655
167+
168+
session.install("attrs==20.3.0")
169+
session.install("-e", ".[all]")
170+
session.install(PYTYPE_VERSION)
171+
session.run("python", "-m", "pip", "freeze")
172+
# See https://github.com/google/pytype/issues/464
173+
session.run("pytype", "-P", ".", "google/cloud/bigquery")
174+
175+
176+
@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS)
177+
@_calculate_duration
178+
def system(session):
179+
"""Run the system test suite."""
180+
181+
constraints_path = str(
182+
CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt"
183+
)
184+
185+
# Sanity check: Only run system tests if the environment variable is set.
186+
if not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS", ""):
187+
session.skip("Credentials must be set via environment variable.")
188+
189+
# Use pre-release gRPC for system tests.
190+
# Exclude version 1.49.0rc1 which has a known issue.
191+
# See https://github.com/grpc/grpc/pull/30642
192+
session.install("--pre", "grpcio!=1.49.0rc1", "-c", constraints_path)
193+
194+
# Install all test dependencies, then install local packages in place.
195+
session.install(
196+
"pytest",
197+
"psutil",
198+
"pytest-xdist",
199+
"google-cloud-testutils",
200+
"-c",
201+
constraints_path,
202+
)
203+
if os.environ.get("GOOGLE_API_USE_CLIENT_CERTIFICATE", "") == "true":
204+
# mTLS test requires pyopenssl and latest google-cloud-storage
205+
session.install("google-cloud-storage", "pyopenssl")
206+
else:
207+
session.install("google-cloud-storage", "-c", constraints_path)
208+
209+
# Data Catalog needed for the column ACL test with a real Policy Tag.
210+
session.install("google-cloud-datacatalog", "-c", constraints_path)
211+
212+
# Resource Manager needed for test with a real Resource Tag.
213+
session.install("google-cloud-resource-manager", "-c", constraints_path)
214+
215+
if session.python in ["3.11", "3.12"]:
216+
extras = "[bqstorage,ipywidgets,pandas,tqdm,opentelemetry]"
217+
else:
218+
extras = "[all]"
219+
session.install("-e", f".{extras}", "-c", constraints_path)
220+
221+
# Test with some broken "extras" in case the user didn't install the extra
222+
# directly. For example, pandas-gbq is recommended for pandas features, but
223+
# we want to test that we fallback to the previous behavior. For context,
224+
# see internal document go/pandas-gbq-and-bigframes-redundancy.
225+
if session.python == SYSTEM_TEST_PYTHON_VERSIONS[0]:
226+
session.run("python", "-m", "pip", "uninstall", "pandas-gbq", "-y")
227+
228+
# print versions of all dependencies
229+
session.run("python", "-m", "pip", "freeze")
230+
231+
# Run py.test against the system tests.
232+
session.run(
233+
"py.test",
234+
"-n=auto",
235+
"--quiet",
236+
"-W default::PendingDeprecationWarning",
237+
os.path.join("tests", "system"),
238+
*session.posargs,
239+
)
240+
241+
242+
@nox.session(python=DEFAULT_PYTHON_VERSION)
243+
@_calculate_duration
244+
def cover(session):
245+
"""Run the final coverage report.
246+
247+
This outputs the coverage report aggregating coverage from the unit
248+
test runs (not system test runs), and then erases coverage data.
249+
"""
250+
251+
session.install("coverage", "pytest-cov")
252+
session.run("python", "-m", "pip", "freeze")
253+
session.run("coverage", "report", "--show-missing", "--fail-under=100")
254+
session.run("coverage", "erase")
255+
256+
257+
@nox.session(python=DEFAULT_PYTHON_VERSION)
258+
@_calculate_duration
259+
def lint(session):
260+
"""Run linters.
261+
262+
Returns a failure if the linters find linting errors or sufficiently
263+
serious code quality issues.
264+
"""
265+
266+
session.install("flake8", BLACK_VERSION)
267+
session.install("-e", ".")
268+
session.run("python", "-m", "pip", "freeze")
269+
session.run("flake8", os.path.join("google", "cloud", "bigquery"))
270+
session.run("flake8", "tests")
271+
session.run("flake8", os.path.join("docs", "samples"))
272+
session.run("flake8", os.path.join("docs", "snippets.py"))
273+
session.run("flake8", "benchmark")
274+
session.run("black", "--check", *BLACK_PATHS)
275+
276+
277+
@nox.session(python=DEFAULT_PYTHON_VERSION)
278+
@_calculate_duration
279+
def lint_setup_py(session):
280+
"""Verify that setup.py is valid (including RST check)."""
281+
282+
session.install("docutils", "Pygments")
283+
session.run("python", "-m", "pip", "freeze")
284+
session.run("python", "setup.py", "check", "--restructuredtext", "--strict")
285+
286+
287+
@nox.session(python=DEFAULT_PYTHON_VERSION)
288+
@_calculate_duration
289+
def blacken(session):
290+
"""Run black.
291+
Format code to uniform standard.
292+
"""
293+
294+
session.install(BLACK_VERSION)
295+
session.run("python", "-m", "pip", "freeze")
296+
session.run("black", *BLACK_PATHS)
297+
298+
299+
@nox.session(python="3.10")
300+
@_calculate_duration
301+
def docs(session):
302+
"""Build the docs."""
303+
304+
session.install(
305+
# We need to pin to specific versions of the `sphinxcontrib-*` packages
306+
# which still support sphinx 4.x.
307+
# See https://github.com/googleapis/sphinx-docfx-yaml/issues/344
308+
# and https://github.com/googleapis/sphinx-docfx-yaml/issues/345.
309+
"sphinxcontrib-applehelp==1.0.4",
310+
"sphinxcontrib-devhelp==1.0.2",
311+
"sphinxcontrib-htmlhelp==2.0.1",
312+
"sphinxcontrib-qthelp==1.0.3",
313+
"sphinxcontrib-serializinghtml==1.1.5",
314+
"sphinx==4.5.0",
315+
"alabaster",
316+
"recommonmark",
317+
)
318+
session.install("google-cloud-storage")
319+
session.install("-e", ".[all]")
320+
321+
shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True)
322+
session.run("python", "-m", "pip", "freeze")
323+
session.run(
324+
"sphinx-build",
325+
"-W", # warnings as errors
326+
"-T", # show full traceback on exception
327+
"-N", # no colors
328+
"-b",
329+
"html",
330+
"-d",
331+
os.path.join("docs", "_build", "doctrees", ""),
332+
os.path.join("docs", ""),
333+
os.path.join("docs", "_build", "html", ""),
334+
)
335+
336+
337+
@nox.session(python="3.10")
338+
@_calculate_duration
339+
def docfx(session):
340+
"""Build the docfx yaml files for this library."""
341+
342+
session.install("-e", ".")
343+
session.install(
344+
# We need to pin to specific versions of the `sphinxcontrib-*` packages
345+
# which still support sphinx 4.x.
346+
# See https://github.com/googleapis/sphinx-docfx-yaml/issues/344
347+
# and https://github.com/googleapis/sphinx-docfx-yaml/issues/345.
348+
"sphinxcontrib-applehelp==1.0.4",
349+
"sphinxcontrib-devhelp==1.0.2",
350+
"sphinxcontrib-htmlhelp==2.0.1",
351+
"sphinxcontrib-qthelp==1.0.3",
352+
"sphinxcontrib-serializinghtml==1.1.5",
353+
"gcp-sphinx-docfx-yaml",
354+
"alabaster",
355+
"recommonmark",
356+
)
357+
358+
shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True)
359+
session.run("python", "-m", "pip", "freeze")
360+
session.run(
361+
"sphinx-build",
362+
"-T", # show full traceback on exception
363+
"-N", # no colors
364+
"-D",
365+
(
366+
"extensions=sphinx.ext.autodoc,"
367+
"sphinx.ext.autosummary,"
368+
"docfx_yaml.extension,"
369+
"sphinx.ext.intersphinx,"
370+
"sphinx.ext.coverage,"
371+
"sphinx.ext.napoleon,"
372+
"sphinx.ext.todo,"
373+
"sphinx.ext.viewcode,"
374+
"recommonmark"
375+
),
376+
"-b",
377+
"html",
378+
"-d",
379+
os.path.join("docs", "_build", "doctrees", ""),
380+
os.path.join("docs", ""),
381+
os.path.join("docs", "_build", "html", ""),
382+
)

0 commit comments

Comments
 (0)