Skip to content

Commit 04ace01

Browse files
committed
fix(tests): fix e2e tests that are failing for release and not running all tests durring regression
1 parent 1e39fa7 commit 04ace01

6 files changed

Lines changed: 141 additions & 36 deletions

File tree

.detect-secrets.scan.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,5 +139,5 @@
139139
}
140140
]
141141
},
142-
"generated_at": "2026-06-12T09:44:07Z"
142+
"generated_at": "2026-06-12T10:03:55Z"
143143
}

.github/actions/python/tests/action.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ inputs:
2424
required: false
2525
default: "functional"
2626
test-category:
27-
description: "Test category: smoke, sanity, regression, or all (supports combinations\
28-
\ like 'smoke or sanity')"
27+
description: "Test category: smoke, sanity, regression, or all (regression/all runs all\
28+
\ tests, other categories filter using pytest keyword matching -k)"
2929
required: false
3030
default: "all"
3131
force-run:
@@ -71,8 +71,8 @@ runs:
7171
env:
7272
TEST_CATEGORY: ${{ inputs.test-category }}
7373
run: |-
74-
if [ "$TEST_CATEGORY" != "all" ] && [ -n "$TEST_CATEGORY" ]; then
75-
echo "args=-m \"$TEST_CATEGORY\"" >> "$GITHUB_OUTPUT"
74+
if [ "$TEST_CATEGORY" != "all" ] && [ "$TEST_CATEGORY" != "regression" ] && [ -n "$TEST_CATEGORY" ]; then
75+
echo "args=-k \"$TEST_CATEGORY\"" >> "$GITHUB_OUTPUT"
7676
else
7777
echo "args=" >> "$GITHUB_OUTPUT"
7878
fi

pyproject.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,22 @@ builder = true
261261
template = "python"
262262
python = "3.10"
263263

264+
[tool.hatch.envs.py311]
265+
template = "python"
266+
python = "3.11"
267+
268+
[tool.hatch.envs.py312]
269+
template = "python"
270+
python = "3.12"
271+
272+
[tool.hatch.envs.py313]
273+
template = "python"
274+
python = "3.13"
275+
276+
[tool.hatch.envs.py314]
277+
template = "python"
278+
python = "3.14"
279+
264280
[tool.hatch.envs.python.scripts]
265281
lint = [
266282
"ruff check {args:{env:PYTHON_TARGETS}}",

src/gitversioned/compat.py

Lines changed: 97 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
1-
"""
2-
Compatibility abstractions for optional dependencies.
3-
4-
This module centralizes fallback logic for safely importing optional dependencies
5-
like ``psutil``, ``opentelemetry``, and TOML parsers. It provides standardized
6-
access points for these modules, avoiding scattered ``try-except`` blocks across
7-
the codebase. Maintainers should import optional dependencies from this module
8-
rather than attempting direct imports elsewhere.
1+
"""Compatibility abstractions for optional dependencies and packaging errors.
2+
3+
This module centralizes fallback logic for safely importing optional
4+
dependencies such as ``psutil``, ``opentelemetry``, and TOML parsers.
5+
By providing standardized module-level variables that resolve to either
6+
the imported module or ``None``, it prevents scattered ``try-except``
7+
blocks and import errors across the library.
8+
9+
In addition to optional dependencies, it abstracts differences in
10+
packaging-related setup exception classes. It resolves and exposes
11+
tools to check or raise setup errors compatible with both legacy
12+
``distutils`` and modern ``setuptools``.
913
"""
1014

1115
from __future__ import annotations
1216

1317
import importlib
1418
import types
15-
from typing import Annotated
19+
from typing import Annotated, NoReturn
1620

17-
__all__ = ["DistutilsSetupError", "maturin", "opentelemetry_trace", "psutil", "tomllib"]
21+
__all__ = [
22+
"is_distutils_setup_error",
23+
"maturin",
24+
"opentelemetry_trace",
25+
"psutil",
26+
"raise_distutils_setup_error",
27+
"tomllib",
28+
]
1829

1930
_maturin: types.ModuleType | None = None
2031
try:
@@ -45,18 +56,86 @@
4556
except ImportError:
4657
_psutil = None
4758

48-
_DistutilsSetupError: type[Exception]
49-
try:
50-
import setuptools.errors
5159

52-
_DistutilsSetupError = setuptools.errors.SetupError
53-
except ImportError:
60+
def raise_distutils_setup_error(
61+
message: str, from_exception: Exception | None = None
62+
) -> NoReturn:
63+
"""Dynamically resolve and raise the appropriate setup error class.
64+
65+
Resolves the setup error class by attempting to import
66+
``setuptools.errors.SetupError``, falling back to
67+
``distutils.errors.DistutilsSetupError``, and ultimately using the
68+
builtin ``Exception`` if neither is available. This avoids import-time
69+
monkeypatching race conditions with ``setuptools``.
70+
71+
Example:
72+
>>> try:
73+
... # Perform some custom setup or build operation
74+
... pass
75+
... except Exception as err:
76+
... raise_distutils_setup_error("Setup failed", from_exception=err)
77+
78+
:param message: The descriptive error message to associate with the
79+
raised exception.
80+
:param from_exception: An optional exception instance to chain via
81+
``raise ... from``.
82+
:raises Exception: The resolved setup error or a standard base
83+
``Exception``.
84+
"""
5485
try:
55-
import distutils.errors
86+
import setuptools.errors # noqa: PLC0415
5687

57-
_DistutilsSetupError = distutils.errors.DistutilsSetupError
88+
exc_class = setuptools.errors.SetupError
5889
except ImportError:
59-
_DistutilsSetupError = Exception
90+
try:
91+
import distutils.errors # noqa: PLC0415
92+
93+
exc_class = distutils.errors.DistutilsSetupError
94+
except ImportError:
95+
exc_class = Exception
96+
97+
if from_exception is not None:
98+
raise exc_class(message) from from_exception
99+
raise exc_class(message)
100+
101+
102+
def is_distutils_setup_error(error: Exception) -> bool:
103+
"""Verify if an exception instance matches the resolved SetupError.
104+
105+
Checks the given exception against ``setuptools.errors.SetupError`` and
106+
``distutils.errors.DistutilsSetupError`` if their respective modules
107+
can be imported.
108+
109+
Example:
110+
>>> try:
111+
... # Run packaging tool logic
112+
... pass
113+
... except Exception as err:
114+
... if is_distutils_setup_error(err):
115+
... print("Packaging setup error detected")
116+
117+
:param error: The exception instance to evaluate.
118+
:returns: ``True`` if the error is a setup error instance; otherwise
119+
``False``.
120+
"""
121+
try:
122+
import setuptools.errors # noqa: PLC0415
123+
124+
if isinstance(error, setuptools.errors.SetupError):
125+
return True
126+
except ImportError:
127+
pass
128+
129+
try:
130+
import distutils.errors # noqa: PLC0415
131+
132+
if isinstance(error, distutils.errors.DistutilsSetupError):
133+
return True
134+
except ImportError:
135+
pass
136+
137+
return False
138+
60139

61140
maturin: Annotated[
62141
types.ModuleType | None,
@@ -83,8 +162,3 @@
83162
"``pyproject.toml``. Provides the standard library ``tomllib``, the "
84163
"third-party ``tomli``, or ``None``.",
85164
] = _tomllib
86-
87-
DistutilsSetupError: Annotated[
88-
type[Exception],
89-
"The SetupError exception class resolved from setuptools or distutils.",
90-
] = _DistutilsSetupError

src/gitversioned/plugins/setuptools_plugin.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from packaging.utils import canonicalize_name
2525
from setuptools import Distribution
2626

27-
from gitversioned.compat import DistutilsSetupError
27+
from gitversioned.compat import is_distutils_setup_error, raise_distutils_setup_error
2828
from gitversioned.logging import autolog, configure_logger, logger
2929
from gitversioned.settings import Settings
3030
from gitversioned.utils import BuildEnvironment, GitRepository
@@ -72,11 +72,11 @@ def setup_keywords(distribution: Distribution, attribute: str, value: Any) -> No
7272

7373
if attribute != "gitversioned":
7474
logger.error(f"Unknown keyword argument: {attribute}")
75-
raise DistutilsSetupError(f"Unknown keyword argument: {attribute}")
75+
raise_distutils_setup_error(f"Unknown keyword argument: {attribute}")
7676

7777
if not isinstance(value, (dict, bool)):
7878
logger.error("gitversioned keyword argument must be a dict or bool")
79-
raise DistutilsSetupError("gitversioned must be a dict or bool")
79+
raise_distutils_setup_error("gitversioned must be a dict or bool")
8080

8181
cast("Any", distribution).gitversioned_config = (
8282
{} if isinstance(value, bool) else value
@@ -116,7 +116,7 @@ def finalize_distribution_options(distribution: Distribution) -> None:
116116

117117
project_root, source_root, package_name = _resolve_project_context(distribution)
118118
if not package_name:
119-
raise DistutilsSetupError("Could not determine package name.")
119+
raise_distutils_setup_error("Could not determine package name.")
120120

121121
# Check for an established version to avoid redundant Git resolution
122122
established_version = _extract_established_version(distribution, project_root)
@@ -162,10 +162,12 @@ def finalize_distribution_options(distribution: Distribution) -> None:
162162
)
163163

164164
except Exception as error:
165-
if isinstance(error, DistutilsSetupError):
165+
if is_distutils_setup_error(error):
166166
raise
167167
logger.exception("Unexpected failure during version resolution")
168-
raise DistutilsSetupError(f"Failed to resolve version: {error}") from error
168+
raise_distutils_setup_error(
169+
f"Failed to resolve version: {error}", from_exception=error
170+
)
169171

170172

171173
@autolog

tests/python/unit/test_compat.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,18 @@ def restore_compat_module() -> Any:
4444
def test_smoke_module_exports() -> None:
4545
"""Verify that compat exposes the expected variables and matches expected types."""
4646
assert compat.__all__ == [
47-
"DistutilsSetupError",
47+
"is_distutils_setup_error",
4848
"maturin",
4949
"opentelemetry_trace",
5050
"psutil",
51+
"raise_distutils_setup_error",
5152
"tomllib",
5253
]
53-
assert hasattr(compat, "DistutilsSetupError")
54+
assert hasattr(compat, "is_distutils_setup_error")
5455
assert hasattr(compat, "maturin")
5556
assert hasattr(compat, "opentelemetry_trace")
5657
assert hasattr(compat, "psutil")
58+
assert hasattr(compat, "raise_distutils_setup_error")
5759
assert hasattr(compat, "tomllib")
5860

5961

@@ -142,3 +144,14 @@ def test_regression_tomllib_all_unavailable(monkeypatch: pytest.MonkeyPatch) ->
142144
monkeypatch.setitem(sys.modules, "tomli", cast("Any", None))
143145
importlib.reload(compat)
144146
assert compat.tomllib is None
147+
148+
149+
@pytest.mark.sanity
150+
def test_raise_and_check_distutils_setup_error() -> None:
151+
"""Verify raising and checking of distutils setup errors works dynamically."""
152+
with pytest.raises(Exception) as exc_info:
153+
compat.raise_distutils_setup_error("Test setup error")
154+
155+
assert compat.is_distutils_setup_error(exc_info.value) is True
156+
assert str(exc_info.value) == "Test setup error"
157+
assert compat.is_distutils_setup_error(ValueError("Not a setup error")) is False

0 commit comments

Comments
 (0)