Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add version module that allows to compare versions in python #678

Merged
merged 3 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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