Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix version checks #350

Merged
merged 1 commit into from
Dec 20, 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
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")
Loading