From 3e8b383e5367d34bfc2f619cfb8e923e47b87141 Mon Sep 17 00:00:00 2001 From: tmadlener Date: Wed, 18 Sep 2024 13:49:40 +0200 Subject: [PATCH] Add version module that allows to compare versions in python --- python/podio/base_reader.py | 5 +-- python/podio/test_version.py | 25 ++++++++++++++ python/podio/version.py | 63 ++++++++++++++++++++++++++++++------ tools/podio-dump | 5 ++- 4 files changed, 83 insertions(+), 15 deletions(-) create mode 100644 python/podio/test_version.py diff --git a/python/podio/base_reader.py b/python/podio/base_reader.py index e078aa4ad..6127499b0 100644 --- a/python/podio/base_reader.py +++ b/python/podio/base_reader.py @@ -4,6 +4,7 @@ from podio.frame_iterator import FrameCategoryIterator +from podio.version import Version class BaseReaderMixin: @@ -101,11 +102,11 @@ def current_file_version(self, edm_name=None): version is requested """ if edm_name is None: - return self._reader.currentFileVersion() + return Version(self._reader.currentFileVersion()) if self._is_legacy: raise RuntimeError("Legacy readers do not store any version info") maybe_version = self._reader.currentFileVersion(edm_name) if maybe_version.has_value(): - return maybe_version.value() + return Version(maybe_version.value()) raise KeyError(f"No version information available for '{edm_name}'") diff --git a/python/podio/test_version.py b/python/podio/test_version.py new file mode 100644 index 000000000..7d7f42db1 --- /dev/null +++ b/python/podio/test_version.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +"""Unittests for python version module""" + +import unittest + +from podio import version +from podio import __version__ + + +class TestVersion(unittest.TestCase): + """General unittests for the python bindings of Version""" + + def test_build_version(self): + """Make sure that the build version is set consistently (i.e. configured + correctly)""" + self.assertEqual(version.build_version, version.Version(__version__)) + + def test_version_comparison(self): + """Make sure that version comparisons work""" + v1 = version.Version(1, 2, 3) + v2 = version.Version("0.4.2") + self.assertTrue(v1 > v2) + + v3 = version.Version("1.2.3") + self.assertEqual(v3, v1) diff --git a/python/podio/version.py b/python/podio/version.py index 9707e2df1..02441413d 100644 --- a/python/podio/version.py +++ b/python/podio/version.py @@ -1,6 +1,9 @@ #!/usr/bin/env python3 """Module that facilitates working with the podio::version::Version""" +from functools import total_ordering +from packaging import version + import ROOT # NOTE: It is necessary that this can be found on the ROOT_INCLUDE_PATH @@ -10,17 +13,57 @@ if ROOT.gInterpreter.LoadFile("podio/podioVersion.h") == 0: # noqa: E402 from ROOT import podio # noqa: E402 # pylint: disable=wrong-import-position -build_version = podio.version.build_version +@total_ordering +class Version: + """A dedicated version class for podio versions, as they are returned by + the c++ interface. -def version_as_str(ver): - """Stringify the version into the usual format + We make this so that version comparisons work as expected in python as well + """ - Args: - ver (podio.version.Version): A podio version + def __init__(self, *args): + """Construct a python version from its c++ counterpart - Returns: - str: A stringified version of the version, in the format - MAJOR.MINOR.PATCH - """ - return f"{ver.major}.{ver.minor}.{ver.patch}" + Takes either a podio.version.Version, up to three numbers which are + interpreted as major, minor, patch or a valid (python) version string + """ + # We don't do a lot of validation here, we just convert everything into + # a string and then let the version parsing fail in case the inputs are + # wrong + ver = args[0] + if len(args) == 1: + if isinstance(ver, podio.version.Version): + ver = f"{ver.major}.{ver.minor}.{ver.patch}" + else: + ver = ".".join(str(v) for v in args) + + self.version = version.Version(ver) + + def __getattr__(self, attr): + """Delegate attribute retrieval to the underlying version""" + return getattr(self.version, attr) + + def __str__(self): + return str(self.version) + + def __repr__(self): + return f"podio.Version({self.version!r})" + + def __eq__(self, other): + if isinstance(other, Version): + return self.version == other.version + if isinstance(other, version.Version): + return self.version == other + return NotImplemented + + def __lt__(self, other): + if isinstance(other, Version): + return self.version < other.version + if isinstance(other, version.Version): + return self.version < other + return NotImplemented + + +# The version with which podio has been built. Same as __version__ +build_version = Version(podio.version.build_version) diff --git a/tools/podio-dump b/tools/podio-dump index f85387711..ea8143762 100755 --- a/tools/podio-dump +++ b/tools/podio-dump @@ -8,7 +8,6 @@ import yaml from tabulate import tabulate from podio_version import __version__ -from podio.version import version_as_str def print_general_info(reader, filename): @@ -25,14 +24,14 @@ def print_general_info(reader, filename): print( f"input file: {filename}{legacy_text}\n" " (written with podio version: " - f"{version_as_str(reader.current_file_version())})\n" + f"{reader.current_file_version()})\n" ) print("datamodel model definitions stored in this file: ") for edm_name in reader.datamodel_definitions: try: edm_version = reader.current_file_version(edm_name) - print(f" - {edm_name} ({version_as_str(edm_version)})") + print(f" - {edm_name} ({edm_version})") except KeyError: print(f" - {edm_name}")