Skip to content

Commit 6fff855

Browse files
authored
Fix validation for remote files (#287)
Actually load files instead of just checking `exists`, which failed on remote files. Closes #286
1 parent 6299139 commit 6fff855

File tree

3 files changed

+58
-11
lines changed

3 files changed

+58
-11
lines changed

petab/v1/problem.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import tempfile
66
from collections.abc import Iterable
77
from math import nan
8-
from pathlib import Path
8+
from pathlib import Path, PurePosixPath
99
from typing import TYPE_CHECKING
1010
from warnings import warn
1111

@@ -485,7 +485,7 @@ def to_files_generic(
485485

486486
if prefix_path is None:
487487
return filenames["yaml_file"]
488-
return str(prefix_path / filenames["yaml_file"])
488+
return str(PurePosixPath(prefix_path, filenames["yaml_file"]))
489489

490490
def to_files(
491491
self,

petab/v1/yaml.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import jsonschema
1010
import numpy as np
11+
import pandas as pd
1112
import yaml
1213
from pandas.io.common import get_handle
1314

@@ -110,25 +111,46 @@ def validate_yaml_semantics(
110111
"""
111112
if not path_prefix:
112113
if isinstance(yaml_config, str | Path):
113-
path_prefix = os.path.dirname(str(yaml_config))
114+
path_prefix = get_path_prefix(yaml_config)
114115
else:
115116
path_prefix = ""
116117

117118
yaml_config = load_yaml(yaml_config)
118119

119120
def _check_file(_filename: str, _field: str):
120-
if not os.path.isfile(_filename):
121+
# this could be a regular path or some local or remote URL
122+
# the simplest check is just trying to load the respective table or
123+
# sbml model
124+
if _field == SBML_FILES:
125+
from ..models.sbml_model import SbmlModel
126+
127+
try:
128+
SbmlModel.from_file(_filename)
129+
except Exception as e:
130+
raise AssertionError(
131+
f"Failed to read '{_filename}' provided as '{_field}'."
132+
) from e
133+
return
134+
135+
try:
136+
pd.read_csv(_filename, sep="\t")
137+
except pd.errors.EmptyDataError:
138+
# at this stage, we don't care about the content
139+
pass
140+
except Exception as e:
121141
raise AssertionError(
122-
f"File '{_filename}' provided as '{_field}' " "does not exist."
123-
)
142+
f"Failed to read '{_filename}' provided as '{_field}'."
143+
) from e
124144

125145
# Handles both a single parameter file, and a parameter file that has been
126146
# split into multiple subset files.
127147
for parameter_subset_file in list(
128148
np.array(yaml_config[PARAMETER_FILE]).flat
129149
):
130150
_check_file(
131-
os.path.join(path_prefix, parameter_subset_file),
151+
f"{path_prefix}/{parameter_subset_file}"
152+
if path_prefix
153+
else parameter_subset_file,
132154
parameter_subset_file,
133155
)
134156

@@ -142,7 +164,12 @@ def _check_file(_filename: str, _field: str):
142164
]:
143165
if field in problem_config:
144166
for filename in problem_config[field]:
145-
_check_file(os.path.join(path_prefix, filename), field)
167+
_check_file(
168+
f"{path_prefix}/{filename}"
169+
if path_prefix
170+
else filename,
171+
field,
172+
)
146173

147174

148175
def load_yaml(yaml_config: dict | Path | str) -> dict:

tests/v1/test_yaml.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ def test_create_problem_yaml():
3737
observable_file = Path(outdir, "observables.tsv")
3838
yaml_file = Path(outdir, "problem.yaml")
3939
visualization_file = Path(outdir, "visualization.tsv")
40+
41+
_create_dummy_sbml_model(sbml_file)
42+
4043
for file in (
41-
sbml_file,
4244
condition_file,
4345
measurement_file,
4446
parameter_file,
@@ -65,13 +67,14 @@ def test_create_problem_yaml():
6567
observable_file2 = Path(outdir, "observables2.tsv")
6668
yaml_file2 = Path(outdir, "problem2.yaml")
6769
for file in (
68-
sbml_file2,
6970
condition_file2,
7071
measurement_file2,
7172
observable_file2,
7273
):
7374
file.touch()
7475

76+
_create_dummy_sbml_model(sbml_file2)
77+
7578
sbml_files = [sbml_file, sbml_file2]
7679
condition_files = [condition_file, condition_file2]
7780
measurement_files = [measurement_file, measurement_file2]
@@ -87,10 +90,27 @@ def test_create_problem_yaml():
8790
validate(yaml_file2)
8891

8992

90-
def test_get_path_prefix_local():
93+
def test_get_path_prefix():
9194
assert get_path_prefix("/some/dir/file.yaml") == str(Path("/some/dir"))
9295
assert get_path_prefix("some/dir/file.yaml") == str(Path("some/dir"))
9396
assert (
9497
get_path_prefix("https://petab.rocks/dir/file.yaml")
9598
== "https://petab.rocks/dir"
9699
)
100+
101+
102+
def test_validate_remote():
103+
yaml_url = (
104+
"https://raw.githubusercontent.com/PEtab-dev/petab_test_suite"
105+
"/main/petabtests/cases/v1.0.0/sbml/0001/_0001.yaml"
106+
)
107+
108+
validate(yaml_url)
109+
110+
111+
def _create_dummy_sbml_model(sbml_file: Path | str):
112+
import libsbml
113+
114+
sbml_doc = libsbml.SBMLDocument()
115+
sbml_doc.createModel()
116+
libsbml.writeSBMLToFile(sbml_doc, str(sbml_file))

0 commit comments

Comments
 (0)