Skip to content

Commit

Permalink
Fix version checks (#350)
Browse files Browse the repository at this point in the history
Add function for parsing version numbers. Update checks.

Fixes #349.
  • Loading branch information
dweindl authored Dec 20, 2024
1 parent 9f11e73 commit 6a9ecd0
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 23 deletions.
12 changes: 6 additions & 6 deletions petab/v1/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
import pandas as pd
from pydantic import AnyUrl, BaseModel, Field, RootModel

from ..versions import get_major_version
from . import (
conditions,
core,
format_version,
mapping,
measurements,
observables,
Expand Down Expand Up @@ -290,13 +290,13 @@ def get_path(filename):
"petab.CompositeProblem.from_yaml() instead."
)

if yaml_config[FORMAT_VERSION] not in {"1", 1, "1.0.0", "2.0.0"}:
major_version = get_major_version(yaml_config)
if major_version not in {1, 2}:
raise ValueError(
"Provided PEtab files are of unsupported version "
f"{yaml_config[FORMAT_VERSION]}. Expected "
f"{format_version.__format_version__}."
f"{yaml_config[FORMAT_VERSION]}."
)
if yaml_config[FORMAT_VERSION] == "2.0.0":
if major_version == 2:
warn("Support for PEtab2.0 is experimental!", stacklevel=2)
warn(
"Using petab.v1.Problem with PEtab2.0 is deprecated. "
Expand All @@ -321,7 +321,7 @@ def get_path(filename):
if config.parameter_file
else None
)
if config.format_version.root in [1, "1", "1.0.0"]:
if major_version == 1:
if len(problem0.sbml_files) > 1:
# TODO https://github.com/PEtab-dev/libpetab-python/issues/6
raise NotImplementedError(
Expand Down
16 changes: 10 additions & 6 deletions petab/v1/yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
import yaml
from pandas.io.common import get_handle

from ..versions import parse_version
from .C import * # noqa: F403

# directory with PEtab yaml schema files
SCHEMA_DIR = Path(__file__).parent.parent / "schemas"
# map of version number to validation schema
SCHEMAS = {
"1": SCHEMA_DIR / "petab_schema.v1.0.0.yaml",
"1.0.0": SCHEMA_DIR / "petab_schema.v1.0.0.yaml",
"2.0.0": SCHEMA_DIR / "petab_schema.v2.0.0.yaml",
(1, 0): SCHEMA_DIR / "petab_schema.v1.0.0.yaml",
(2, 0): SCHEMA_DIR / "petab_schema.v2.0.0.yaml",
}

__all__ = [
Expand Down Expand Up @@ -71,14 +71,18 @@ def validate_yaml_syntax(
yaml_config = load_yaml(yaml_config)

if schema is None:
# try get PEtab version from yaml file
# try to get PEtab version from the yaml file
# if this is not the available, the file is not valid anyways,
# but let's still use the latest PEtab schema for full validation
version = yaml_config.get(FORMAT_VERSION, None)
version = (
yaml_config.get(FORMAT_VERSION, None) or list(SCHEMAS.values())[-1]
parse_version(version)[:2]
if version
else list(SCHEMAS.values())[-1]
)

try:
schema = SCHEMAS[str(version)]
schema = SCHEMAS[version]
except KeyError as e:
raise ValueError(
"Unknown PEtab version given in problem "
Expand Down
10 changes: 6 additions & 4 deletions petab/v2/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from ..v1.problem import ListOfFiles, VersionNumber
from ..v1.yaml import get_path_prefix
from ..v2.C import * # noqa: F403
from ..versions import parse_version
from . import conditions, experiments

if TYPE_CHECKING:
Expand Down Expand Up @@ -161,14 +162,15 @@ def get_path(filename):
return filename
return f"{base_path}/{filename}"

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

from ..versions import get_major_version
from .petab1to2 import petab1to2

if get_major_version(yaml_config) == 1 and yaml_file:
if format_version[0] == 1 and yaml_file:
logging.debug(
"Auto-upgrading problem from PEtab 1.0 to PEtab 2.0"
)
Expand All @@ -185,7 +187,7 @@ def get_path(filename):
)
raise ValueError(
"Provided PEtab files are of unsupported version "
f"{yaml_config[FORMAT_VERSION]}. Expected 2.0.0."
f"{yaml_config[FORMAT_VERSION]}."
)

if yaml.is_composite_problem(yaml_config):
Expand Down
41 changes: 34 additions & 7 deletions petab/versions.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,62 @@
"""Handling of PEtab version numbers."""
from __future__ import annotations

import re
from pathlib import Path

import petab
from petab.v1 import Problem as V1Problem
from petab.v1.C import FORMAT_VERSION
from petab.v1.yaml import load_yaml

__all__ = [
"get_major_version",
"parse_version",
]
from . import v1

# version regex pattern
_version_pattern = (
r"(?P<major>\d+)(?:\.(?P<minor>\d+))?"
r"(?:\.(?P<patch>\d+))?(?P<suffix>[\w.]+)?"
)
_version_re = re.compile(_version_pattern)


def parse_version(version: str | int) -> tuple[int, int, int, str]:
"""Parse a version string into a tuple of integers and suffix."""
if isinstance(version, int):
return version, 0, 0, ""

version = str(version)
match = _version_re.match(version)
if match is None:
raise ValueError(f"Invalid version string: {version}")

major = int(match.group("major"))
minor = int(match.group("minor") or 0)
patch = int(match.group("patch") or 0)
suffix = match.group("suffix") or ""

return major, minor, patch, suffix


def get_major_version(
problem: str | dict | Path | V1Problem | petab.v2.Problem,
problem: str | dict | Path | petab.v1.Problem | petab.v2.Problem,
) -> int:
"""Get the major version number of the given problem."""
version = None

if isinstance(problem, str | Path):
from petab.v1.yaml import load_yaml

yaml_config = load_yaml(problem)
version = yaml_config.get(FORMAT_VERSION)
version = yaml_config.get(v1.C.FORMAT_VERSION)
elif isinstance(problem, dict):
version = problem.get(FORMAT_VERSION)
version = problem.get(v1.C.FORMAT_VERSION)

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

if isinstance(problem, V1Problem):
if isinstance(problem, petab.v1.Problem):
return 1

from . import v2
Expand Down
13 changes: 13 additions & 0 deletions tests/test_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Tests related to petab.versions"""

from petab.versions import *


def test_parse_version():
assert parse_version("1.2.3") == (1, 2, 3, "")
assert parse_version("1.2.3a") == (1, 2, 3, "a")
assert parse_version("1.2") == (1, 2, 0, "")
assert parse_version("1") == (1, 0, 0, "")
assert parse_version(1) == (1, 0, 0, "")
assert parse_version("1.2.3.a") == (1, 2, 3, ".a")
assert parse_version("1.2.3.4") == (1, 2, 3, ".4")

0 comments on commit 6a9ecd0

Please sign in to comment.