From ee88c3dbda171c70f05393f19047c828da018f53 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 18 Aug 2021 14:09:16 -0400 Subject: [PATCH 1/5] chore(python): refactor unit / system test dependency install Closes #1185. --- .../templates/python_library/noxfile.py.j2 | 147 +++++++++++++----- tests/test_python_library.py | 80 +++++++--- 2 files changed, 165 insertions(+), 62 deletions(-) diff --git a/synthtool/gcp/templates/python_library/noxfile.py.j2 b/synthtool/gcp/templates/python_library/noxfile.py.j2 index cb6672d4b..20e9187f0 100644 --- a/synthtool/gcp/templates/python_library/noxfile.py.j2 +++ b/synthtool/gcp/templates/python_library/noxfile.py.j2 @@ -23,13 +23,58 @@ import shutil import nox - BLACK_VERSION = "black==19.10b0" BLACK_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] DEFAULT_PYTHON_VERSION="{{ default_python_version }}" -SYSTEM_TEST_PYTHON_VERSIONS=[{% for v in system_test_python_versions %}"{{v}}"{% if not loop.last %},{% endif %}{% endfor %}] -UNIT_TEST_PYTHON_VERSIONS=[{% for v in unit_test_python_versions %}"{{v}}"{% if not loop.last %},{% endif %}{% endfor %}] + +UNIT_TEST_PYTHON_VERSIONS=[{% for v in unit_test_python_versions %}"{{v}}"{% if not loop.last %}, {% endif %}{% endfor %}] +UNIT_TEST_STANDARD_DEPENDENCIES = [ + "mock", + "asyncmock", + "pytest", + "pytest-cov", + "pytest-asyncio", +] +UNIT_TEST_EXTERNAL_DEPENDENCIES = [{% for v in unit_test_external_dependencies %} + "{{v}}",{% endfor %} +] +UNIT_TEST_LOCAL_DEPENDENCIES = [{% for v in unit_test_local_dependencies %} + "{{v}}",{% endfor %} +] +UNIT_TEST_DEPENDENCIES = [{% for v in unit_test_dependencies %} + "{{v}}",{% endfor %} +] +UNIT_TEST_EXTRAS = [{% for v in unit_test_extras %} + "{{v}}",{% endfor %} +] +UNIT_TEST_EXTRAS_BY_PYTHON = {{ '{' }}{% if unit_test_extras_by_python %}{% for python_version, extras in unit_test_extras_by_python.items() %} + "{{python_version}}": [{% for v in extras %} + "{{v}}",{% endfor %} + ],{% endfor %}{% endif %} +} + +SYSTEM_TEST_PYTHON_VERSIONS=[{% for v in system_test_python_versions %}"{{v}}"{% if not loop.last %}, {% endif %}{% endfor %}] +SYSTEM_TEST_STANDARD_DEPENDENCIES = [ + "mock", "pytest", "google-cloud-testutils", +] +SYSTEM_TEST_EXTERNAL_DEPENDENCIES = [{% for v in system_test_external_dependencies %} + "{{v}}",{% endfor %} +] +SYSTEM_TEST_LOCAL_DEPENDENCIES = [{% for v in system_test_local_dependencies %} + "{{v}}",{% endfor %} +] +SYSTEM_TEST_DEPENDENCIES = [{% for v in system_test_dependencies %} + "{{v}}",{% endfor %} +] +SYSTEM_TEST_EXTRAS = [{% for v in system_test_extras %} + "{{v}}",{% endfor %} +] +SYSTEM_TEST_EXTRAS_BY_PYTHON = {{ '{' }}{% if system_test_extras_by_python %}{% for python_version, extras in system_test_extras_by_python.items() %} + "{{python_version}}": [{% for v in extras %} + "{{v}}",{% endfor %} + ],{% endfor %}{% endif %} +} CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() @@ -81,29 +126,38 @@ def lint_setup_py(session): session.run("python", "setup.py", "check", "--restructuredtext", "--strict") +def install_unittest_dependencies(session, *constraints): + session.install(*UNIT_TEST_STANDARD_DEPENDENCIES, *constraints) + + if UNIT_TEST_EXTERNAL_DEPENDENCIES: + session.install(*UNIT_TEST_EXTERNAL_DEPENDENCIES, *constraints) + + if UNIT_TEST_LOCAL_DEPENDENCIES: + session.install(*UNIT_TEST_LOCAL_DEPENDENCIES, *constraints) + + if UNIT_TEST_DEPENDENCIES: + session.install(*UNIT_TEST_DEPENDENCIES, *constraints) + + if UNIT_TEST_EXTRAS_BY_PYTHON: + extras = UNIT_TEST_EXTRAS_BY_PYTHON.get(session.python, []) + elif UNIT_TEST_EXTRAS: + extras = UNIT_TEST_EXTRAS + else: + extras = [] + + if extras: + session.install("-e", f".[{','.join(extras)}]", *constraints) + else: + session.install("-e", ".", *constraints) + + def default(session): # Install all test dependencies, then install this package in-place. constraints_path = str( CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" ) - session.install("mock", "asyncmock", "pytest", "pytest-cov", "pytest-asyncio", "-c", constraints_path) - {% for d in unit_test_external_dependencies %}session.install("{{d}}", "-c", constraints_path){% endfor %} - {% for dependency in unit_test_local_dependencies %}session.install("-e", "{{dependency}}", "-c", constraints_path) - {% endfor %} - {% for dependency in unit_test_dependencies %}session.install("-e", "{{dependency}}", "-c", constraints_path){% endfor %} - {%- if unit_test_extras_by_python %} - {% for extras_python in unit_test_extras_by_python %} - {%- if not loop.first %}el{% endif %}if session.python == "{{extras_python}}": - extras = "[{{",".join(unit_test_extras_by_python[extras_python])}}]" - {% endfor %}else: - extras = "{%- if unit_test_extras %}[{{",".join(unit_test_extras)}}]{% endif %}" - session.install("-e", f".{extras}", "-c", constraints_path) - {% elif unit_test_extras %} - session.install("-e", ".[{{",".join(unit_test_extras)}}]", "-c", constraints_path) - {% else %} - session.install("-e", ".", "-c", constraints_path) - {% endif %} + install_unittest_dependencies(session, "-c", constraints_path) # Run py.test against the unit tests. session.run( @@ -126,6 +180,35 @@ def unit(session): default(session) +def install_systemtest_dependencies(session, *constraints): + + # Use pre-release gRPC for system tests. + session.install("--pre", "grpcio") + + session.install(*SYSTEM_TEST_STANDARD_DEPENDENCIES, *constraints) + + if SYSTEM_TEST_EXTERNAL_DEPENDENCIES: + session.install(*SYSTEM_TEST_EXTERNAL_DEPENDENCIES, *constraints) + + if SYSTEM_TEST_LOCAL_DEPENDENCIES: + session.install(*SYSTEM_TEST_LOCAL_DEPENDENCIES, *constraints) + + if SYSTEM_TEST_DEPENDENCIES: + session.install(*SYSTEM_TEST_DEPENDENCIES, *constraints) + + if SYSTEM_TEST_EXTRAS_BY_PYTHON: + extras = SYSTEM_TEST_EXTRAS_BY_PYTHON.get(session.python, []) + elif SYSTEM_TEST_EXTRAS: + extras = SYSTEM_TEST_EXTRAS + else: + extras = [] + + if extras: + session.install("-e", f".[{','.join(extras)}]", *constraints) + else: + session.install("-e", ".", *constraints) + + @nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS) def system(session): """Run the system test suite.""" @@ -148,29 +231,7 @@ def system(session): if not system_test_exists and not system_test_folder_exists: session.skip("System tests were not found") - # Use pre-release gRPC for system tests. - session.install("--pre", "grpcio") - - # Install all test dependencies, then install this package into the - # virtualenv's dist-packages. - session.install("mock", "pytest", "google-cloud-testutils"{% for d in system_test_external_dependencies %}, "{{d}}"{% endfor %}, "-c", constraints_path) - - {%- if system_test_local_dependencies %} - {% for dependency in system_test_local_dependencies %}session.install("-e", "{{dependency}}", "-c", constraints_path) - {% endfor %} - {%- endif %} - {%- if system_test_extras_by_python %} - {% for extras_python in system_test_extras_by_python %} - {%- if not loop.first %}el{% endif %}if session.python == "{{extras_python}}": - extras = "[{{",".join(system_test_extras_by_python[extras_python])}}]" - {% endfor %}else: - extras = "{%- if system_test_extras %}[{{",".join(system_test_extras)}}]{% endif %}" - session.install("-e", f".{extras}", "-c", constraints_path) - {% elif system_test_extras %} - session.install("-e", ".[{{",".join(system_test_extras)}}]", "-c", constraints_path) - {% else %} - session.install("-e", ".", "-c", constraints_path) - {% endif %} + install_systemtest_dependencies(session, "-c", constraints_path) # Run py.test against the system tests. if system_test_exists: diff --git a/tests/test_python_library.py b/tests/test_python_library.py index 1fe8ff9c4..2d953cca9 100644 --- a/tests/test_python_library.py +++ b/tests/test_python_library.py @@ -28,43 +28,67 @@ @pytest.mark.parametrize( ["template_kwargs", "expected_text"], [ - ({}, ["import nox", 'session.install("-e", ".", "-c", constraints_path)']), + ({}, ["import nox", 'session.install("-e", ".", *constraints)']), ( {"unit_test_local_dependencies": ["../testutils", "../unitutils"]}, [ - 'session.install("-e", "../testutils", "-c", constraints_path)', - 'session.install("-e", "../unitutils", "-c", constraints_path)', + """\ +UNIT_TEST_LOCAL_DEPENDENCIES = [ + "../testutils", + "../unitutils", +]""", ], ), ( {"system_test_local_dependencies": ["../testutils", "../sysutils"]}, [ - 'session.install("-e", "../testutils", "-c", constraints_path)', - 'session.install("-e", "../sysutils", "-c", constraints_path)', + """\ +SYSTEM_TEST_LOCAL_DEPENDENCIES = [ + "../testutils", + "../sysutils", +]""", ], ), ( {"unit_test_extras": ["abc", "def"]}, - ['session.install("-e", ".[abc,def]", "-c", constraints_path)'], + [ + """\ +UNIT_TEST_EXTRAS = [ + "abc", + "def", +]""", + ], ), ( {"system_test_extras": ["abc", "def"]}, - ['session.install("-e", ".[abc,def]", "-c", constraints_path)'], + """\ +SYSTEM_TEST_EXTRAS = [ + "abc", + "def", +]""", ), ( {"unit_test_extras_by_python": {"3.8": ["abc", "def"]}}, [ - 'if session.python == "3.8":\n extras = "[abc,def]"', - 'else:\n extras = ""', - 'session.install("-e", f".{extras}", "-c", constraints_path)', + """\ +UNIT_TEST_EXTRAS_BY_PYTHON = { + "3.8": [ + "abc", + "def", + ], +}""", ], ), ( {"system_test_extras_by_python": {"3.8": ["abc", "def"]}}, [ - 'if session.python == "3.8":\n extras = "[abc,def]"', - 'else:\n extras = ""', - 'session.install("-e", f".{extras}", "-c", constraints_path)', + """\ +SYSTEM_TEST_EXTRAS_BY_PYTHON = { + "3.8": [ + "abc", + "def", + ], +}""", ], ), ( @@ -73,9 +97,18 @@ "unit_test_extras_by_python": {"3.8": ["abc", "def"]}, }, [ - 'if session.python == "3.8":\n extras = "[abc,def]"', - 'else:\n extras = "[tuv,wxyz]"', - 'session.install("-e", f".{extras}", "-c", constraints_path)', + """\ +UNIT_TEST_EXTRAS = [ + "tuv", + "wxyz", +]""", + """\ +UNIT_TEST_EXTRAS_BY_PYTHON = { + "3.8": [ + "abc", + "def", + ], +}""", ], ), ( @@ -84,9 +117,18 @@ "system_test_extras_by_python": {"3.8": ["abc", "def"]}, }, [ - 'if session.python == "3.8":\n extras = "[abc,def]"', - 'else:\n extras = "[tuv,wxyz]"', - 'session.install("-e", f".{extras}", "-c", constraints_path)', + """\ +SYSTEM_TEST_EXTRAS = [ + "tuv", + "wxyz", +]""", + """\ +SYSTEM_TEST_EXTRAS_BY_PYTHON = { + "3.8": [ + "abc", + "def", + ], +}""", ], ), ], From be04805d067fc8fa37c9874461a78a54289ee438 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 18 Aug 2021 15:23:58 -0400 Subject: [PATCH 2/5] chore: use editable installs for local deps --- synthtool/gcp/templates/python_library/noxfile.py.j2 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/synthtool/gcp/templates/python_library/noxfile.py.j2 b/synthtool/gcp/templates/python_library/noxfile.py.j2 index 20e9187f0..0cffccf42 100644 --- a/synthtool/gcp/templates/python_library/noxfile.py.j2 +++ b/synthtool/gcp/templates/python_library/noxfile.py.j2 @@ -133,10 +133,10 @@ def install_unittest_dependencies(session, *constraints): session.install(*UNIT_TEST_EXTERNAL_DEPENDENCIES, *constraints) if UNIT_TEST_LOCAL_DEPENDENCIES: - session.install(*UNIT_TEST_LOCAL_DEPENDENCIES, *constraints) + session.install("-e", *UNIT_TEST_LOCAL_DEPENDENCIES, *constraints) if UNIT_TEST_DEPENDENCIES: - session.install(*UNIT_TEST_DEPENDENCIES, *constraints) + session.install("-e", *UNIT_TEST_DEPENDENCIES, *constraints) if UNIT_TEST_EXTRAS_BY_PYTHON: extras = UNIT_TEST_EXTRAS_BY_PYTHON.get(session.python, []) @@ -191,10 +191,10 @@ def install_systemtest_dependencies(session, *constraints): session.install(*SYSTEM_TEST_EXTERNAL_DEPENDENCIES, *constraints) if SYSTEM_TEST_LOCAL_DEPENDENCIES: - session.install(*SYSTEM_TEST_LOCAL_DEPENDENCIES, *constraints) + session.install("-e", *SYSTEM_TEST_LOCAL_DEPENDENCIES, *constraints) if SYSTEM_TEST_DEPENDENCIES: - session.install(*SYSTEM_TEST_DEPENDENCIES, *constraints) + session.install("-e", *SYSTEM_TEST_DEPENDENCIES, *constraints) if SYSTEM_TEST_EXTRAS_BY_PYTHON: extras = SYSTEM_TEST_EXTRAS_BY_PYTHON.get(session.python, []) From d274ce3655d1b76d3156fd3d4a096e4d31f86f8e Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 30 Nov 2021 12:33:39 -0500 Subject: [PATCH 3/5] chore: don't install deps using '-e' --- synthtool/gcp/templates/python_library/noxfile.py.j2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/synthtool/gcp/templates/python_library/noxfile.py.j2 b/synthtool/gcp/templates/python_library/noxfile.py.j2 index 68681373a..cce7fe650 100644 --- a/synthtool/gcp/templates/python_library/noxfile.py.j2 +++ b/synthtool/gcp/templates/python_library/noxfile.py.j2 @@ -133,10 +133,10 @@ def install_unittest_dependencies(session, *constraints): session.install(*UNIT_TEST_EXTERNAL_DEPENDENCIES, *constraints) if UNIT_TEST_LOCAL_DEPENDENCIES: - session.install("-e", *UNIT_TEST_LOCAL_DEPENDENCIES, *constraints) + session.install(*UNIT_TEST_LOCAL_DEPENDENCIES, *constraints) if UNIT_TEST_DEPENDENCIES: - session.install("-e", *UNIT_TEST_DEPENDENCIES, *constraints) + session.install(*UNIT_TEST_DEPENDENCIES, *constraints) if UNIT_TEST_EXTRAS_BY_PYTHON: extras = UNIT_TEST_EXTRAS_BY_PYTHON.get(session.python, []) From 3ed578313978ff3b9299b1ae1a7c5ada75089323 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 30 Nov 2021 12:56:52 -0500 Subject: [PATCH 4/5] chore: deprecate 'unit_test_external_dependencies' --- synthtool/gcp/templates/python_library/noxfile.py.j2 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/synthtool/gcp/templates/python_library/noxfile.py.j2 b/synthtool/gcp/templates/python_library/noxfile.py.j2 index cce7fe650..1ed30b97b 100644 --- a/synthtool/gcp/templates/python_library/noxfile.py.j2 +++ b/synthtool/gcp/templates/python_library/noxfile.py.j2 @@ -20,6 +20,7 @@ from __future__ import absolute_import import os import pathlib import shutil +import warnings import nox @@ -130,6 +131,11 @@ def install_unittest_dependencies(session, *constraints): session.install(*UNIT_TEST_STANDARD_DEPENDENCIES, *constraints) if UNIT_TEST_EXTERNAL_DEPENDENCIES: + warnings.warn( + "'unit_test_external_dependencies' is deprecated. Instead, please " + "use 'unit_test_dependencies' or 'unit_test_local_dependencies'.", + DeprecationWarning, + ) session.install(*UNIT_TEST_EXTERNAL_DEPENDENCIES, *constraints) if UNIT_TEST_LOCAL_DEPENDENCIES: From 2fdef3597ac5d05158d945a7878d7b3c53c16546 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Fri, 3 Dec 2021 11:03:35 -0500 Subject: [PATCH 5/5] fix: install standard + main unit test deps together FBO pip resolver. --- synthtool/gcp/templates/python_library/noxfile.py.j2 | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/synthtool/gcp/templates/python_library/noxfile.py.j2 b/synthtool/gcp/templates/python_library/noxfile.py.j2 index 1ed30b97b..51a12ff6f 100644 --- a/synthtool/gcp/templates/python_library/noxfile.py.j2 +++ b/synthtool/gcp/templates/python_library/noxfile.py.j2 @@ -128,7 +128,8 @@ def lint_setup_py(session): def install_unittest_dependencies(session, *constraints): - session.install(*UNIT_TEST_STANDARD_DEPENDENCIES, *constraints) + standard_deps = UNIT_TEST_STANDARD_DEPENDENCIES + UNIT_TEST_DEPENDENCIES + session.install(*standard_deps, *constraints) if UNIT_TEST_EXTERNAL_DEPENDENCIES: warnings.warn( @@ -141,9 +142,6 @@ def install_unittest_dependencies(session, *constraints): if UNIT_TEST_LOCAL_DEPENDENCIES: session.install(*UNIT_TEST_LOCAL_DEPENDENCIES, *constraints) - if UNIT_TEST_DEPENDENCIES: - session.install(*UNIT_TEST_DEPENDENCIES, *constraints) - if UNIT_TEST_EXTRAS_BY_PYTHON: extras = UNIT_TEST_EXTRAS_BY_PYTHON.get(session.python, []) elif UNIT_TEST_EXTRAS: