Skip to content

Commit

Permalink
Add version module that allows to compare versions in python (#678)
Browse files Browse the repository at this point in the history
* Add version module that allows to compare versions in python

* Make comparison operators from c++ available in python

* Make it an import error if the header cannot be found
  • Loading branch information
tmadlener committed Sep 19, 2024
1 parent 03a75ee commit 3e8f634
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 23 deletions.
8 changes: 3 additions & 5 deletions podioVersion.in.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,9 @@ struct Version {
uint16_t minor{0};
uint16_t patch{0};

#if __cplusplus >= 202002L
auto operator<=>(const Version&) const = default;
#else
// No spaceship yet in c++17
// We explicitly define these here instead of using the default spaceship
// operator because cppyy does not recognize that yet and we want the
// comparisons to work in python as well
#define DEFINE_COMP_OPERATOR(OP) \
constexpr bool operator OP(const Version& o) const noexcept { \
return std::tie(major, minor, patch) OP std::tie(o.major, o.minor, o.patch); \
Expand All @@ -58,7 +57,6 @@ struct Version {
DEFINE_COMP_OPERATOR(!=)

#undef DEFINE_COMP_OPERATOR
#endif

explicit operator std::string() const {
std::stringstream ss;
Expand Down
54 changes: 54 additions & 0 deletions python/podio/test_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/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_parse(self):
"""Make sure that parse works as expected"""
vv = version.parse(1, 2, 3)
self.assertEqual(vv.major, 1)
self.assertEqual(vv.minor, 2)
self.assertEqual(vv.patch, 3)

vv = version.parse(0, 2)
self.assertEqual(vv.major, 0)
self.assertEqual(vv.minor, 2)
self.assertEqual(vv.patch, 0)

vv = version.parse("42.0")
self.assertEqual(vv.major, 42)
self.assertEqual(vv.minor, 0)
self.assertEqual(vv.patch, 0)

vv = version.Version()
self.assertEqual(vv.major, 0)
self.assertEqual(vv.minor, 0)
self.assertEqual(vv.patch, 0)

def test_build_version(self):
"""Make sure that the build version is set consistently (i.e. configured
correctly)"""
self.assertEqual(version.build_version, version.parse(__version__))

def test_version_comparison(self):
"""Make sure that version comparisons work"""
v1 = version.parse(1, 2, 3)
v2 = version.parse("0.4.2")
self.assertTrue(v1 > v2)

v3 = version.parse("1.2.3")
self.assertEqual(v3, v1)

def test_string_representation(self):
"""Make sure the string representation is OK"""
self.assertEqual(f"{version.build_version}", __version__)

vv = version.parse(42)
self.assertEqual(f"{vv}", "42.0.0")
45 changes: 30 additions & 15 deletions python/podio/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,38 @@
import ROOT

# NOTE: It is necessary that this can be found on the ROOT_INCLUDE_PATH
#
# We check whether we can actually load the header to not break python bindings
# in environments with *ancient* podio versions
if ROOT.gInterpreter.LoadFile("podio/podioVersion.h") == 0: # noqa: E402
from ROOT import podio # noqa: E402 # pylint: disable=wrong-import-position
if ROOT.gInterpreter.LoadFile("podio/podioVersion.h") != 0: # noqa: E402
raise ImportError("Cannot find the podio/podioVersion.h header")

build_version = podio.version.build_version
from ROOT import podio # noqa: E402 # pylint: disable=wrong-import-position

Version = podio.version.Version


def _str_dunder(self):
"""Shim to get a more reasonable string representation"""
return f"{self.major}.{self.minor}.{self.patch}"


def version_as_str(ver):
"""Stringify the version into the usual format
Version.__str__ = _str_dunder

Args:
ver (podio.version.Version): A podio version

Returns:
str: A stringified version of the version, in the format
MAJOR.MINOR.PATCH
"""
return f"{ver.major}.{ver.minor}.{ver.patch}"
def parse(*args):
"""Construct a version from either a list of integers or a version string"""
if len(args) == 1:
if isinstance(args[0], podio.version.Version):
return args[0]
if isinstance(args[0], str):
ver_tuple = tuple(int(v) for v in args[0].split("."))
else:
ver_tuple = (int(args[0]),)
else:
ver_tuple = tuple(args)
ver = Version()
for mem, val in zip(("major", "minor", "patch"), ver_tuple):
setattr(ver, mem, val)
return ver


# The version with which podio has been built. Same as __version__
build_version = podio.version.build_version
5 changes: 2 additions & 3 deletions tools/podio-dump
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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}")

Expand Down

0 comments on commit 3e8f634

Please sign in to comment.