Skip to content

Commit 6a9ecd0

Browse files
authored
Fix version checks (PEtab-dev#350)
Add function for parsing version numbers. Update checks. Fixes PEtab-dev#349.
1 parent 9f11e73 commit 6a9ecd0

File tree

5 files changed

+69
-23
lines changed

5 files changed

+69
-23
lines changed

petab/v1/problem.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
import pandas as pd
1414
from pydantic import AnyUrl, BaseModel, Field, RootModel
1515

16+
from ..versions import get_major_version
1617
from . import (
1718
conditions,
1819
core,
19-
format_version,
2020
mapping,
2121
measurements,
2222
observables,
@@ -290,13 +290,13 @@ def get_path(filename):
290290
"petab.CompositeProblem.from_yaml() instead."
291291
)
292292

293-
if yaml_config[FORMAT_VERSION] not in {"1", 1, "1.0.0", "2.0.0"}:
293+
major_version = get_major_version(yaml_config)
294+
if major_version not in {1, 2}:
294295
raise ValueError(
295296
"Provided PEtab files are of unsupported version "
296-
f"{yaml_config[FORMAT_VERSION]}. Expected "
297-
f"{format_version.__format_version__}."
297+
f"{yaml_config[FORMAT_VERSION]}."
298298
)
299-
if yaml_config[FORMAT_VERSION] == "2.0.0":
299+
if major_version == 2:
300300
warn("Support for PEtab2.0 is experimental!", stacklevel=2)
301301
warn(
302302
"Using petab.v1.Problem with PEtab2.0 is deprecated. "
@@ -321,7 +321,7 @@ def get_path(filename):
321321
if config.parameter_file
322322
else None
323323
)
324-
if config.format_version.root in [1, "1", "1.0.0"]:
324+
if major_version == 1:
325325
if len(problem0.sbml_files) > 1:
326326
# TODO https://github.com/PEtab-dev/libpetab-python/issues/6
327327
raise NotImplementedError(

petab/v1/yaml.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@
1212
import yaml
1313
from pandas.io.common import get_handle
1414

15+
from ..versions import parse_version
1516
from .C import * # noqa: F403
1617

1718
# directory with PEtab yaml schema files
1819
SCHEMA_DIR = Path(__file__).parent.parent / "schemas"
1920
# map of version number to validation schema
2021
SCHEMAS = {
21-
"1": SCHEMA_DIR / "petab_schema.v1.0.0.yaml",
22-
"1.0.0": SCHEMA_DIR / "petab_schema.v1.0.0.yaml",
23-
"2.0.0": SCHEMA_DIR / "petab_schema.v2.0.0.yaml",
22+
(1, 0): SCHEMA_DIR / "petab_schema.v1.0.0.yaml",
23+
(2, 0): SCHEMA_DIR / "petab_schema.v2.0.0.yaml",
2424
}
2525

2626
__all__ = [
@@ -71,14 +71,18 @@ def validate_yaml_syntax(
7171
yaml_config = load_yaml(yaml_config)
7272

7373
if schema is None:
74-
# try get PEtab version from yaml file
74+
# try to get PEtab version from the yaml file
7575
# if this is not the available, the file is not valid anyways,
7676
# but let's still use the latest PEtab schema for full validation
77+
version = yaml_config.get(FORMAT_VERSION, None)
7778
version = (
78-
yaml_config.get(FORMAT_VERSION, None) or list(SCHEMAS.values())[-1]
79+
parse_version(version)[:2]
80+
if version
81+
else list(SCHEMAS.values())[-1]
7982
)
83+
8084
try:
81-
schema = SCHEMAS[str(version)]
85+
schema = SCHEMAS[version]
8286
except KeyError as e:
8387
raise ValueError(
8488
"Unknown PEtab version given in problem "

petab/v2/problem.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from ..v1.problem import ListOfFiles, VersionNumber
2929
from ..v1.yaml import get_path_prefix
3030
from ..v2.C import * # noqa: F403
31+
from ..versions import parse_version
3132
from . import conditions, experiments
3233

3334
if TYPE_CHECKING:
@@ -161,14 +162,15 @@ def get_path(filename):
161162
return filename
162163
return f"{base_path}/{filename}"
163164

164-
if yaml_config[FORMAT_VERSION] not in {"2.0.0"}:
165+
if (format_version := parse_version(yaml_config[FORMAT_VERSION]))[
166+
0
167+
] != 2:
165168
# If we got a path to a v1 yaml file, try to auto-upgrade
166169
from tempfile import TemporaryDirectory
167170

168-
from ..versions import get_major_version
169171
from .petab1to2 import petab1to2
170172

171-
if get_major_version(yaml_config) == 1 and yaml_file:
173+
if format_version[0] == 1 and yaml_file:
172174
logging.debug(
173175
"Auto-upgrading problem from PEtab 1.0 to PEtab 2.0"
174176
)
@@ -185,7 +187,7 @@ def get_path(filename):
185187
)
186188
raise ValueError(
187189
"Provided PEtab files are of unsupported version "
188-
f"{yaml_config[FORMAT_VERSION]}. Expected 2.0.0."
190+
f"{yaml_config[FORMAT_VERSION]}."
189191
)
190192

191193
if yaml.is_composite_problem(yaml_config):

petab/versions.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,62 @@
11
"""Handling of PEtab version numbers."""
22
from __future__ import annotations
33

4+
import re
45
from pathlib import Path
56

67
import petab
7-
from petab.v1 import Problem as V1Problem
8-
from petab.v1.C import FORMAT_VERSION
9-
from petab.v1.yaml import load_yaml
108

119
__all__ = [
1210
"get_major_version",
11+
"parse_version",
1312
]
13+
from . import v1
14+
15+
# version regex pattern
16+
_version_pattern = (
17+
r"(?P<major>\d+)(?:\.(?P<minor>\d+))?"
18+
r"(?:\.(?P<patch>\d+))?(?P<suffix>[\w.]+)?"
19+
)
20+
_version_re = re.compile(_version_pattern)
21+
22+
23+
def parse_version(version: str | int) -> tuple[int, int, int, str]:
24+
"""Parse a version string into a tuple of integers and suffix."""
25+
if isinstance(version, int):
26+
return version, 0, 0, ""
27+
28+
version = str(version)
29+
match = _version_re.match(version)
30+
if match is None:
31+
raise ValueError(f"Invalid version string: {version}")
32+
33+
major = int(match.group("major"))
34+
minor = int(match.group("minor") or 0)
35+
patch = int(match.group("patch") or 0)
36+
suffix = match.group("suffix") or ""
37+
38+
return major, minor, patch, suffix
1439

1540

1641
def get_major_version(
17-
problem: str | dict | Path | V1Problem | petab.v2.Problem,
42+
problem: str | dict | Path | petab.v1.Problem | petab.v2.Problem,
1843
) -> int:
1944
"""Get the major version number of the given problem."""
2045
version = None
2146

2247
if isinstance(problem, str | Path):
48+
from petab.v1.yaml import load_yaml
49+
2350
yaml_config = load_yaml(problem)
24-
version = yaml_config.get(FORMAT_VERSION)
51+
version = yaml_config.get(v1.C.FORMAT_VERSION)
2552
elif isinstance(problem, dict):
26-
version = problem.get(FORMAT_VERSION)
53+
version = problem.get(v1.C.FORMAT_VERSION)
2754

2855
if version is not None:
2956
version = str(version)
3057
return int(version.split(".")[0])
3158

32-
if isinstance(problem, V1Problem):
59+
if isinstance(problem, petab.v1.Problem):
3360
return 1
3461

3562
from . import v2

tests/test_version.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"""Tests related to petab.versions"""
2+
3+
from petab.versions import *
4+
5+
6+
def test_parse_version():
7+
assert parse_version("1.2.3") == (1, 2, 3, "")
8+
assert parse_version("1.2.3a") == (1, 2, 3, "a")
9+
assert parse_version("1.2") == (1, 2, 0, "")
10+
assert parse_version("1") == (1, 0, 0, "")
11+
assert parse_version(1) == (1, 0, 0, "")
12+
assert parse_version("1.2.3.a") == (1, 2, 3, ".a")
13+
assert parse_version("1.2.3.4") == (1, 2, 3, ".4")

0 commit comments

Comments
 (0)