From 62e68c75c44c3e2c7eee0691c45463a5f5c19fc2 Mon Sep 17 00:00:00 2001 From: fynnbe Date: Thu, 14 Nov 2024 10:25:17 +0100 Subject: [PATCH 1/8] add PipDeps.__gt__() --- bioimageio/spec/conda_env.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bioimageio/spec/conda_env.py b/bioimageio/spec/conda_env.py index 520a4c065..9cb9d4793 100644 --- a/bioimageio/spec/conda_env.py +++ b/bioimageio/spec/conda_env.py @@ -15,6 +15,12 @@ def __lt__(self, other: Any): else: return False + def __gt__(self, other: Any): + if isinstance(other, PipDeps): + return len(self.pip) > len(other.pip) + else: + return False + class CondaEnv(BaseModel): """Represenation of the content of a conda environment.yaml file""" From 66e05901d26852e3009c691558e00351732dedf5 Mon Sep 17 00:00:00 2001 From: fynnbe Date: Thu, 14 Nov 2024 14:09:45 +0100 Subject: [PATCH 2/8] add docstrings --- bioimageio/spec/_description.py | 4 ++++ bioimageio/spec/_internal/common_nodes.py | 2 ++ bioimageio/spec/_io.py | 3 +++ 3 files changed, 9 insertions(+) diff --git a/bioimageio/spec/_description.py b/bioimageio/spec/_description.py index d7b9ecced..29da578a1 100644 --- a/bioimageio/spec/_description.py +++ b/bioimageio/spec/_description.py @@ -185,6 +185,10 @@ def update_format( def ensure_description_is_model( rd: Union[InvalidDescr, ResourceDescr], ) -> AnyModelDescr: + """ + Raises: + ValueError: for invalid or non-model resources + """ if isinstance(rd, InvalidDescr): rd.validation_summary.display() raise ValueError("resource description is invalid") diff --git a/bioimageio/spec/_internal/common_nodes.py b/bioimageio/spec/_internal/common_nodes.py index 443244b53..b414b2a43 100644 --- a/bioimageio/spec/_internal/common_nodes.py +++ b/bioimageio/spec/_internal/common_nodes.py @@ -515,6 +515,8 @@ class InvalidDescr( extra="allow", title="An invalid resource description", ): + """A representation of an invalid resource description""" + type: Any = "unknown" format_version: Any = "unknown" fields_to_set_explicitly: ClassVar[FrozenSet[LiteralString]] = frozenset() diff --git a/bioimageio/spec/_io.py b/bioimageio/spec/_io.py index 2fb66b05d..4473b7d40 100644 --- a/bioimageio/spec/_io.py +++ b/bioimageio/spec/_io.py @@ -81,6 +81,9 @@ def load_model_description( ) -> AnyModelDescr: """same as `load_description`, but addtionally ensures that the loaded description is valid and of type 'model'. + + Raises: + ValueError: for invalid or non-model resources """ rd = load_description( source, From 44307b8b8a2faa456d2f91941d325ce9c74eb034 Mon Sep 17 00:00:00 2001 From: fynnbe Date: Thu, 14 Nov 2024 14:11:58 +0100 Subject: [PATCH 3/8] add suffix to temp file\n\nconda throws an error otherwise --- bioimageio/spec/summary.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bioimageio/spec/summary.py b/bioimageio/spec/summary.py index cf8013bcd..7c5359fbf 100644 --- a/bioimageio/spec/summary.py +++ b/bioimageio/spec/summary.py @@ -155,7 +155,9 @@ def model_post_init(self, __context: Any): if self.recommended_env is not None and self.conda_compare is None: dumped_env = self.recommended_env.model_dump(mode="json") if is_yaml_value(dumped_env): - with NamedTemporaryFile(mode="w", encoding="utf-8") as f: + with NamedTemporaryFile( + mode="w", encoding="utf-8", suffix=".yaml" + ) as f: write_yaml(dumped_env, f) self.conda_compare = subprocess.run( ["conda", "compare", f.name], From 7fac19ae7c697b44d4956b6ee7ce1a4d28d1d79b Mon Sep 17 00:00:00 2001 From: fynnbe Date: Thu, 14 Nov 2024 23:00:59 +0100 Subject: [PATCH 4/8] fix conda compare --- bioimageio/spec/summary.py | 45 +++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/bioimageio/spec/summary.py b/bioimageio/spec/summary.py index 7c5359fbf..e51031829 100644 --- a/bioimageio/spec/summary.py +++ b/bioimageio/spec/summary.py @@ -2,7 +2,7 @@ from io import StringIO from itertools import chain from pathlib import Path -from tempfile import NamedTemporaryFile +from tempfile import TemporaryDirectory from types import MappingProxyType from typing import ( Any, @@ -152,21 +152,30 @@ class ValidationDetail(BaseModel, extra="allow"): def model_post_init(self, __context: Any): """create `conda_compare` default value if needed""" super().model_post_init(__context) - if self.recommended_env is not None and self.conda_compare is None: - dumped_env = self.recommended_env.model_dump(mode="json") - if is_yaml_value(dumped_env): - with NamedTemporaryFile( - mode="w", encoding="utf-8", suffix=".yaml" - ) as f: - write_yaml(dumped_env, f) - self.conda_compare = subprocess.run( - ["conda", "compare", f.name], - capture_output=True, - shell=True, - text=True, - ).stdout - else: - self.conda_compare = "Failed to dump recommended env to valid yaml" + if self.recommended_env is None or self.conda_compare is not None: + return + + dumped_env = self.recommended_env.model_dump(mode="json") + if not is_yaml_value(dumped_env): + self.conda_compare = "Failed to dump recommended env to valid yaml" + return + + with TemporaryDirectory() as d: + path = Path(d) / "env.yaml" + with path.open("w", encoding="utf-8") as f: + write_yaml(dumped_env, f) + + compare_proc = subprocess.run( + ["conda", "compare", str(path)], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + shell=True, + text=True, + ) + self.conda_compare = ( + compare_proc.stdout + or f"conda compare exited with {compare_proc.returncode}" + ) def __str__(self): return f"{self.__class__.__name__}:\n" + self.format() @@ -340,9 +349,9 @@ def format_loc(loc: Loc): ] ) - if d.conda_compare is not None: + if d.conda_compare: details.append( - ["🐍", "actual conda env", d.conda_compare.replace("\n", "
")] + ["🐍", "conda compare", d.conda_compare.replace("\n", "
")] ) for entry in d.errors: From 3426388f3a5bcc452b411c71e652b80785b5c016 Mon Sep 17 00:00:00 2001 From: fynnbe Date: Thu, 14 Nov 2024 23:01:21 +0100 Subject: [PATCH 5/8] fix rendering recommended env --- bioimageio/spec/summary.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bioimageio/spec/summary.py b/bioimageio/spec/summary.py index e51031829..1d25f01dc 100644 --- a/bioimageio/spec/summary.py +++ b/bioimageio/spec/summary.py @@ -345,7 +345,9 @@ def format_loc(loc: Loc): [ "🐍", "recommended conda env", - f"```yaml\n{rec_env.read()}\n```".replace("\n", "
"), + f'
{rec_env.getvalue().replace(
+                            '\n', '
' + )}
', ] ) From 6b3729b46a321506523d8aa968274ccc01bc4245 Mon Sep 17 00:00:00 2001 From: fynnbe Date: Thu, 14 Nov 2024 23:50:07 +0100 Subject: [PATCH 6/8] remove lang=yaml as code is broken up in lines --- bioimageio/spec/summary.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bioimageio/spec/summary.py b/bioimageio/spec/summary.py index 1d25f01dc..a474c0a72 100644 --- a/bioimageio/spec/summary.py +++ b/bioimageio/spec/summary.py @@ -345,8 +345,8 @@ def format_loc(loc: Loc): [ "🐍", "recommended conda env", - f'
{rec_env.getvalue().replace(
-                            '\n', '
' + f'
{rec_env.getvalue().replace(
+                            "\n", "
" )}
', ] ) From 13152685e08ee422a4f55bee2bfbc2c37ea0707a Mon Sep 17 00:00:00 2001 From: fynnbe Date: Thu, 14 Nov 2024 23:54:31 +0100 Subject: [PATCH 7/8] make it py<3.12 compatible --- bioimageio/spec/summary.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bioimageio/spec/summary.py b/bioimageio/spec/summary.py index a474c0a72..8d50ada0e 100644 --- a/bioimageio/spec/summary.py +++ b/bioimageio/spec/summary.py @@ -341,13 +341,12 @@ def format_loc(loc: Loc): json_env = d.recommended_env.model_dump(mode="json") assert is_yaml_value(json_env) write_yaml(json_env, rec_env) + rec_env_code = rec_env.getvalue().replace("\n", "

") details.append( [ "🐍", "recommended conda env", - f'
{rec_env.getvalue().replace(
-                            "\n", "
" - )}
', + f"
{rec_env_code}
", ] ) From ae6480de8de7938e57ee4743a97f36fa9bbcd557 Mon Sep 17 00:00:00 2001 From: fynnbe Date: Thu, 14 Nov 2024 23:58:28 +0100 Subject: [PATCH 8/8] test summary.display() --- tests/test_model/test_v0_5.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_model/test_v0_5.py b/tests/test_model/test_v0_5.py index 643107383..b1ad09c2b 100644 --- a/tests/test_model/test_v0_5.py +++ b/tests/test_model/test_v0_5.py @@ -370,6 +370,7 @@ def test_model(model_data: Dict[str, Any], update: Dict[str, Any]): summary = validate_format( model_data, context=ValidationContext(perform_io_checks=False) ) + summary.display() assert summary.status == "passed", summary.format()