Skip to content

Commit 86ed43d

Browse files
authored
test(python): use constraints files to check dependency lower bounds (#869)
Use a constraints file when installing dependencies for system and unit tests nox sessions. https://pip.pypa.io/en/stable/user_guide/#constraints-files > Constraints files are requirements files that **only control which version of a requirement is installed, not whether it is installed or not**. Their syntax and contents is nearly identical to Requirements Files. There is one key difference: Including a package in a constraints file does not trigger installation of the package. ``` testing ├── constraints-3.10.txt ├── constraints-3.11.txt ├── constraints-3.6.txt ├── constraints-3.7.txt ├── constraints-3.8.txt └── constraints-3.9.txt ``` Going forward, one constraints file (currently 3.6) will be populated with every library requirement and extra listed in the `setup.py`. The constraints file will pin each requirement to the lower bound. This ensures that library maintainers will see test failures if they forget to update a lower bound on a dependency. See googleapis/python-bigquery#263 for an example
1 parent f5c5904 commit 86ed43d

File tree

2 files changed

+32
-23
lines changed

2 files changed

+32
-23
lines changed

synthtool/gcp/templates/python_library/noxfile.py.j2

+21-12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from __future__ import absolute_import
2020
import os
21+
import pathlib
2122
import shutil
2223

2324
import nox
@@ -30,6 +31,8 @@ DEFAULT_PYTHON_VERSION="{{ default_python_version }}"
3031
SYSTEM_TEST_PYTHON_VERSIONS=[{% for v in system_test_python_versions %}"{{v}}"{% if not loop.last %},{% endif %}{% endfor %}]
3132
UNIT_TEST_PYTHON_VERSIONS=[{% for v in unit_test_python_versions %}"{{v}}"{% if not loop.last %},{% endif %}{% endfor %}]
3233

34+
CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute()
35+
3336
# 'docfx' is excluded since it only needs to run in 'docs-presubmit'
3437
nox.options.sessions = [
3538
"unit",
@@ -88,24 +91,27 @@ def lint_setup_py(session):
8891
def default(session):
8992
# Install all test dependencies, then install this package in-place.
9093

94+
constraints_path = str(
95+
CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt"
96+
)
9197
{%- if microgenerator %}
92-
session.install("asyncmock", "pytest-asyncio")
98+
session.install("asyncmock", "pytest-asyncio", "-c", constraints_path)
9399
{% endif %}
94-
session.install("mock", "pytest", "pytest-cov", {% for d in unit_test_external_dependencies %}"{{d}}"{% if not loop.last %},{% endif %}{% endfor %})
95-
{% for dependency in unit_test_local_dependencies %}session.install("-e", "{{dependency}}")
100+
session.install("mock", "pytest", "pytest-cov", {% for d in unit_test_external_dependencies %}"{{d}}",{% endfor %} "-c", constraints_path)
101+
{% for dependency in unit_test_local_dependencies %}session.install("-e", "{{dependency}}", "-c", constraints_path)
96102
{% endfor %}
97-
{% for dependency in unit_test_dependencies %}session.install("-e", "{{dependency}}"){% endfor %}
103+
{% for dependency in unit_test_dependencies %}session.install("-e", "{{dependency}}", "-c", constraints_path){% endfor %}
98104
{%- if unit_test_extras_by_python %}
99105
{% for extras_python in unit_test_extras_by_python %}
100106
{%- if not loop.first %}el{% endif %}if session.python == "{{extras_python}}":
101107
extras = "[{{",".join(unit_test_extras_by_python[extras_python])}}]"
102108
{% endfor %}else:
103109
extras = "{%- if unit_test_extras %}[{{",".join(unit_test_extras)}}]{% endif %}"
104-
session.install("-e", f".{extras}")
110+
session.install("-e", f".{extras}", "-c", constraints_path)
105111
{% elif unit_test_extras %}
106-
session.install("-e", ".[{{",".join(unit_test_extras)}}]")
112+
session.install("-e", ".[{{",".join(unit_test_extras)}}]", "-c", constraints_path)
107113
{% else %}
108-
session.install("-e", ".")
114+
session.install("-e", ".", "-c", constraints_path)
109115
{% endif %}
110116

111117
# Run py.test against the unit tests.
@@ -132,6 +138,9 @@ def unit(session):
132138
@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS)
133139
def system(session):
134140
"""Run the system test suite."""
141+
constraints_path = str(
142+
CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt"
143+
)
135144
system_test_path = os.path.join("tests", "system.py")
136145
system_test_folder_path = os.path.join("tests", "system")
137146

@@ -156,10 +165,10 @@ def system(session):
156165

157166
# Install all test dependencies, then install this package into the
158167
# virtualenv's dist-packages.
159-
session.install("mock", "pytest", "google-cloud-testutils", {% for d in system_test_external_dependencies %}"{{d}}"{% if not loop.last %},{% endif %}{% endfor %})
168+
session.install("mock", "pytest", "google-cloud-testutils"{% for d in system_test_external_dependencies %}, "{{d}}"{% endfor %}, "-c", constraints_path)
160169

161170
{%- if system_test_local_dependencies %}
162-
{% for dependency in system_test_local_dependencies %}session.install("-e", "{{dependency}}")
171+
{% for dependency in system_test_local_dependencies %}session.install("-e", "{{dependency}}", "-c", constraints_path)
163172
{% endfor %}
164173
{%- endif %}
165174
{%- if system_test_extras_by_python %}
@@ -168,11 +177,11 @@ def system(session):
168177
extras = "[{{",".join(system_test_extras_by_python[extras_python])}}]"
169178
{% endfor %}else:
170179
extras = "{%- if system_test_extras %}[{{",".join(system_test_extras)}}]{% endif %}"
171-
session.install("-e", f".{extras}")
180+
session.install("-e", f".{extras}", "-c", constraints_path)
172181
{% elif system_test_extras %}
173-
session.install("-e", ".[{{",".join(system_test_extras)}}]")
182+
session.install("-e", ".[{{",".join(system_test_extras)}}]", "-c", constraints_path)
174183
{% else %}
175-
session.install("-e", ".")
184+
session.install("-e", ".", "-c", constraints_path)
176185
{% endif %}
177186

178187
# Run py.test against the system tests.

tests/test_python_library.py

+11-11
Original file line numberDiff line numberDiff line change
@@ -27,43 +27,43 @@
2727
@pytest.mark.parametrize(
2828
["template_kwargs", "expected_text"],
2929
[
30-
({}, ["import nox", 'session.install("-e", ".")']),
30+
({}, ["import nox", 'session.install("-e", ".", "-c", constraints_path)']),
3131
(
3232
{"unit_test_local_dependencies": ["../testutils", "../unitutils"]},
3333
[
34-
'session.install("-e", "../testutils")',
35-
'session.install("-e", "../unitutils")',
34+
'session.install("-e", "../testutils", "-c", constraints_path)',
35+
'session.install("-e", "../unitutils", "-c", constraints_path)',
3636
],
3737
),
3838
(
3939
{"system_test_local_dependencies": ["../testutils", "../sysutils"]},
4040
[
41-
'session.install("-e", "../testutils")',
42-
'session.install("-e", "../sysutils")',
41+
'session.install("-e", "../testutils", "-c", constraints_path)',
42+
'session.install("-e", "../sysutils", "-c", constraints_path)',
4343
],
4444
),
4545
(
4646
{"unit_test_extras": ["abc", "def"]},
47-
['session.install("-e", ".[abc,def]")'],
47+
['session.install("-e", ".[abc,def]", "-c", constraints_path)'],
4848
),
4949
(
5050
{"system_test_extras": ["abc", "def"]},
51-
['session.install("-e", ".[abc,def]")'],
51+
['session.install("-e", ".[abc,def]", "-c", constraints_path)'],
5252
),
5353
(
5454
{"unit_test_extras_by_python": {"3.8": ["abc", "def"]}},
5555
[
5656
'if session.python == "3.8":\n extras = "[abc,def]"',
5757
'else:\n extras = ""',
58-
'session.install("-e", f".{extras}")',
58+
'session.install("-e", f".{extras}", "-c", constraints_path)',
5959
],
6060
),
6161
(
6262
{"system_test_extras_by_python": {"3.8": ["abc", "def"]}},
6363
[
6464
'if session.python == "3.8":\n extras = "[abc,def]"',
6565
'else:\n extras = ""',
66-
'session.install("-e", f".{extras}")',
66+
'session.install("-e", f".{extras}", "-c", constraints_path)',
6767
],
6868
),
6969
(
@@ -74,7 +74,7 @@
7474
[
7575
'if session.python == "3.8":\n extras = "[abc,def]"',
7676
'else:\n extras = "[tuv,wxyz]"',
77-
'session.install("-e", f".{extras}")',
77+
'session.install("-e", f".{extras}", "-c", constraints_path)',
7878
],
7979
),
8080
(
@@ -85,7 +85,7 @@
8585
[
8686
'if session.python == "3.8":\n extras = "[abc,def]"',
8787
'else:\n extras = "[tuv,wxyz]"',
88-
'session.install("-e", f".{extras}")',
88+
'session.install("-e", f".{extras}", "-c", constraints_path)',
8989
],
9090
),
9191
],

0 commit comments

Comments
 (0)