diff --git a/src/_pytest/__init__.py b/src/_pytest/__init__.py index 46c7827ed5e..f7765ca66ed 100644 --- a/src/_pytest/__init__.py +++ b/src/_pytest/__init__.py @@ -1,8 +1,47 @@ __all__ = ["__version__"] +import re +from typing import Tuple + try: from ._version import version as __version__ except ImportError: # broken installation, we don't even try # unknown only works because we do poor mans version compare __version__ = "unknown" + + +def version_tuple() -> Tuple[int, int, int, str, str]: + """ + Return a tuple containing the components of this pytest version: + (*major*, *minor*, *patch*, *release candidate*, *dev hash*). + + Useful for plugins to handle multiple pytest versions when there are incompatibilities + at the plugin level, without parsing the version manually. + """ + return parse_version(__version__) + + +_VERSION_RE = re.compile( + r""" + (?P\d+) + \. + (?P\d+) + \. + (?P\d+) + (?Prc\d+)? + (\.(?P.*))? + """, + re.VERBOSE, +) + + +def parse_version(version: str) -> Tuple[int, int, int, str, str]: + m = _VERSION_RE.match(version) + assert m is not None, "internal error parsing version {}".format(version) + major = int(m.group("major")) + minor = int(m.group("minor")) + patch = int(m.group("patch")) + rc = m.group("rc") or "" + dev = m.group("dev") or "" + return major, minor, patch, rc, dev diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index c4c28191877..5ea63bdc13d 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -2,6 +2,7 @@ """pytest: unit and functional testing with Python.""" from . import collect from _pytest import __version__ +from _pytest import version_tuple from _pytest.assertion import register_assert_rewrite from _pytest.config import cmdline from _pytest.config import console_main @@ -50,6 +51,7 @@ __all__ = [ "__version__", + "version_tuple", "_fillfuncargs", "approx", "Class", diff --git a/testing/test_meta.py b/testing/test_meta.py index 7ab8951a015..5f3f161d44d 100644 --- a/testing/test_meta.py +++ b/testing/test_meta.py @@ -33,3 +33,21 @@ def test_no_warnings(module: str) -> None: "-c", "__import__({!r})".format(module), )) # fmt: on + + +def test_version_tuple(): + fields = pytest.__version__.split(".") + assert pytest.version_tuple()[:2] == (int(fields[0]), int(fields[1])) + + +@pytest.mark.parametrize( + "v, expected", + [ + ("6.0.0", (6, 0, 0, "", "")), + ("6.0.0rc1", (6, 0, 0, "rc1", "")), + ("6.23.1.dev39+ga", (6, 23, 1, "", "dev39+ga")), + ("6.23.1rc2.dev39+ga", (6, 23, 1, "rc2", "dev39+ga")), + ], +) +def test_passe_version_tuple(v, expected): + assert _pytest.parse_version(v) == expected