From 52f99ec865e480b45a441e11fa727f1a130406ad Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Fri, 17 Sep 2021 16:58:04 +0200 Subject: [PATCH 01/31] Added image_pil and image_numpy --- dvclive/data/__init__.py | 4 +++- dvclive/data/image_numpy.py | 19 +++++++++++++++++++ dvclive/data/image_pil.py | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 dvclive/data/image_numpy.py create mode 100644 dvclive/data/image_pil.py diff --git a/dvclive/data/__init__.py b/dvclive/data/__init__.py index 8dc7d5f7..765399ee 100644 --- a/dvclive/data/__init__.py +++ b/dvclive/data/__init__.py @@ -1 +1,3 @@ -from .scalar import Scalar # noqa: F401 +from .image_numpy import ImageNumpy # noqa: F401 +from .image_pil import ImagePIL # noqa: F401 +from .scalar import Scalar # noqa: F401 \ No newline at end of file diff --git a/dvclive/data/image_numpy.py b/dvclive/data/image_numpy.py new file mode 100644 index 00000000..8d2c2c4c --- /dev/null +++ b/dvclive/data/image_numpy.py @@ -0,0 +1,19 @@ +from PIL import Image +from .image_pil import ImagePIL + + +class ImageNumpy(ImagePIL): + + @staticmethod + def could_log(val: object) -> bool: + if val.__class__.__module__ == "numpy": + return True + return False + + def dump(self, val, step) -> None: + val = Image.fromarray(val) + super().dump(val, step) + + @property + def summary(self): + return {} diff --git a/dvclive/data/image_pil.py b/dvclive/data/image_pil.py new file mode 100644 index 00000000..f747a455 --- /dev/null +++ b/dvclive/data/image_pil.py @@ -0,0 +1,35 @@ +from pathlib import Path + +from PIL.Image import Image +from .base import Data + + +class ImagePIL(Data): + subdir = "images" + suffixes = [".jpg", ".jpeg", ".gif", ".png"] + + @staticmethod + def could_log(val: object) -> bool: + if isinstance(val, Image.Image): + return True + return False + + @property + def output_path(self) -> Path: + if self.name.suffix not in self.suffixes: + raise ValueError( + f"Invalid image suffix {self.name.suffix}." + f" Must be one of {self.suffixes}" + ) + return self.output_folder / self.subdir / self.name + + def dump(self, val, step) -> None: + super().dump(val, step) + output_path = Path(str(self.output_path).format(step=step)) + output_path.parent.mkdir(exist_ok=True, parents=True) + + val.save(output_path) + + @property + def summary(self): + return {} From 98803a2f020ed65ceba46c2ac8d1b2b0214386e0 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Fri, 17 Sep 2021 17:01:34 +0200 Subject: [PATCH 02/31] Use DATA_TYPES list in metrics --- dvclive/data/__init__.py | 12 +++++++++--- dvclive/metrics.py | 12 ++++++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/dvclive/data/__init__.py b/dvclive/data/__init__.py index 765399ee..13872b94 100644 --- a/dvclive/data/__init__.py +++ b/dvclive/data/__init__.py @@ -1,3 +1,9 @@ -from .image_numpy import ImageNumpy # noqa: F401 -from .image_pil import ImagePIL # noqa: F401 -from .scalar import Scalar # noqa: F401 \ No newline at end of file +from .image_numpy import ImageNumpy +from .image_pil import ImagePIL +from .scalar import Scalar + +DATA_TYPES = [ + ImageNumpy, + ImagePIL, + Scalar +] \ No newline at end of file diff --git a/dvclive/metrics.py b/dvclive/metrics.py index c6dd8a3d..b5e44e04 100644 --- a/dvclive/metrics.py +++ b/dvclive/metrics.py @@ -5,7 +5,7 @@ from pathlib import Path from typing import Any, Dict, Union -from .data import Scalar +from .data import DATA_TYPES from .dvc import make_checkpoint, make_html from .error import InvalidDataTypeError @@ -115,15 +115,19 @@ def next_step(self): def log(self, name: str, val: Union[int, float]): + data = None if name in self._data: data = self._data[name] - elif Scalar.could_log(val): - data = Scalar(name, self.dir) - self._data[name] = data else: + for data_type in DATA_TYPES: + if data_type.could_log(val): + data = data_type(name, self.dir) + self._data[name] = data + if data is None: raise InvalidDataTypeError(name, type(val)) data.dump(val, self._step) + if self._summary: self.make_summary() From 4cf1df53d1b72e91e117f0aa8ce48354fe1cd6f8 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Fri, 17 Sep 2021 17:09:14 +0200 Subject: [PATCH 03/31] Use subdir structure --- dvclive/data/scalar.py | 3 +++ dvclive/metrics.py | 13 ++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/dvclive/data/scalar.py b/dvclive/data/scalar.py index c70d44a0..b4605817 100644 --- a/dvclive/data/scalar.py +++ b/dvclive/data/scalar.py @@ -10,6 +10,9 @@ class Scalar(Data): + subdir = "linear" + suffixes = [".csv", ".tsv"] + @staticmethod def could_log(val: object) -> bool: if isinstance(val, (int, float)): diff --git a/dvclive/metrics.py b/dvclive/metrics.py index b5e44e04..99d17f06 100644 --- a/dvclive/metrics.py +++ b/dvclive/metrics.py @@ -41,9 +41,12 @@ def __init__( self._init_paths() def _cleanup(self): - - for dvclive_file in Path(self.dir).rglob("*.tsv"): - dvclive_file.unlink() + + for data_type in DATA_TYPES: + subdir = Path(self.dir) / data_type.subdir + data_files = f"*{'|*'.join(data_type.suffixes)}" + for data_file in subdir.rglob(data_files): + data_file.unlink() if os.path.exists(self.summary_path): os.remove(self.summary_path) @@ -92,11 +95,11 @@ def exists(self): @property def summary_path(self): - return self.dir + ".json" + return os.path.join(self.dir, "summary.json") @property def html_path(self): - return self.dir + "_dvc_plots/index.html" + return os.path.join(self.dir, "html") def get_step(self) -> int: return self._step From 60f944dcb37cacd3bde6298011c455ddfe55c24f Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Fri, 17 Sep 2021 17:27:37 +0200 Subject: [PATCH 04/31] Use data subdirs in init_path --- dvclive/data/image_pil.py | 6 +++--- dvclive/metrics.py | 4 ++++ tests/test_main.py | 7 +++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/dvclive/data/image_pil.py b/dvclive/data/image_pil.py index f747a455..b3a43730 100644 --- a/dvclive/data/image_pil.py +++ b/dvclive/data/image_pil.py @@ -10,15 +10,15 @@ class ImagePIL(Data): @staticmethod def could_log(val: object) -> bool: - if isinstance(val, Image.Image): + if isinstance(val, Image): return True return False @property def output_path(self) -> Path: - if self.name.suffix not in self.suffixes: + if Path(self.name).suffix not in self.suffixes: raise ValueError( - f"Invalid image suffix {self.name.suffix}." + f"Invalid image suffix '{Path(self.name).suffix}'" f" Must be one of {self.suffixes}" ) return self.output_folder / self.subdir / self.name diff --git a/dvclive/metrics.py b/dvclive/metrics.py index 99d17f06..1342a027 100644 --- a/dvclive/metrics.py +++ b/dvclive/metrics.py @@ -56,6 +56,10 @@ def _cleanup(self): def _init_paths(self): os.makedirs(self.dir, exist_ok=True) + for data_type in DATA_TYPES: + os.makedirs( + os.path.join(self.dir, data_type.subdir), exist_ok=True) + if self._summary: self.make_summary() diff --git a/tests/test_main.py b/tests/test_main.py index d3c48f26..c7d85387 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -10,6 +10,7 @@ from dvclive import env # pylint: disable=unused-argument +from dvclive.data import DATA_TYPES from dvclive.dvc import SIGNAL_FILE from dvclive.error import ( ConfigMismatchError, @@ -26,7 +27,7 @@ def read_logs(path: str): metric_name = str(metric_file).replace(path + os.path.sep, "") metric_name = metric_name.replace(".tsv", "") history[metric_name] = _parse_tsv(metric_file) - latest = _parse_json(path + ".json") + latest = _parse_json(os.path.join(path + "summary.json")) return history, latest @@ -57,10 +58,12 @@ def _parse_json(path): @pytest.mark.parametrize("path", ["logs", os.path.join("subdir", "logs")]) -def test_create_logs_dir(tmp_dir, path): +def test_init_paths(tmp_dir, path): dvclive.init(path) assert (tmp_dir / path).is_dir() + for data_type in DATA_TYPES: + assert (tmp_dir / path / data_type.subdir).is_dir() @pytest.mark.parametrize("summary", [True, False]) From 062c8a23dc2e44fb2d6a1b11056538b57cb432be Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Mon, 20 Sep 2021 19:54:32 +0200 Subject: [PATCH 05/31] Fix test_logging --- dvclive/data/scalar.py | 5 +++-- dvclive/metrics.py | 1 - tests/test_data/test_image.py | 0 tests/test_main.py | 11 +++++------ 4 files changed, 8 insertions(+), 9 deletions(-) create mode 100644 tests/test_data/test_image.py diff --git a/dvclive/data/scalar.py b/dvclive/data/scalar.py index b4605817..fab49ada 100644 --- a/dvclive/data/scalar.py +++ b/dvclive/data/scalar.py @@ -10,7 +10,7 @@ class Scalar(Data): - subdir = "linear" + subdir = "scalar" suffixes = [".csv", ".tsv"] @staticmethod @@ -21,7 +21,7 @@ def could_log(val: object) -> bool: @property def output_path(self) -> Path: - _path = self.output_folder / self.name + _path = self.output_folder / self.subdir / self.name _path.parent.mkdir(exist_ok=True, parents=True) return _path.with_suffix(".tsv") @@ -33,6 +33,7 @@ def dump(self, val, step): [("timestamp", ts), ("step", self.step), (self.name, self.val)] ) + print(self.output_path) existed = self.output_path.exists() with open(self.output_path, "a") as fobj: writer = csv.DictWriter(fobj, d.keys(), delimiter="\t") diff --git a/dvclive/metrics.py b/dvclive/metrics.py index 1342a027..6ec33c0a 100644 --- a/dvclive/metrics.py +++ b/dvclive/metrics.py @@ -132,7 +132,6 @@ def log(self, name: str, val: Union[int, float]): self._data[name] = data if data is None: raise InvalidDataTypeError(name, type(val)) - data.dump(val, self._step) if self._summary: diff --git a/tests/test_data/test_image.py b/tests/test_data/test_image.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_main.py b/tests/test_main.py index c7d85387..947ede22 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -10,7 +10,7 @@ from dvclive import env # pylint: disable=unused-argument -from dvclive.data import DATA_TYPES +from dvclive.data import DATA_TYPES, Scalar from dvclive.dvc import SIGNAL_FILE from dvclive.error import ( ConfigMismatchError, @@ -27,7 +27,7 @@ def read_logs(path: str): metric_name = str(metric_file).replace(path + os.path.sep, "") metric_name = metric_name.replace(".tsv", "") history[metric_name] = _parse_tsv(metric_file) - latest = _parse_json(os.path.join(path + "summary.json")) + latest = _parse_json(os.path.join(path, "summary.json")) return history, latest @@ -68,13 +68,12 @@ def test_init_paths(tmp_dir, path): @pytest.mark.parametrize("summary", [True, False]) def test_logging(tmp_dir, summary): - dvclive.init("logs", summary=summary) + logger = dvclive.init("logs", summary=summary) dvclive.log("m1", 1) - assert (tmp_dir / "logs").is_dir() - assert (tmp_dir / "logs" / "m1.tsv").is_file() - assert (tmp_dir / "logs.json").is_file() == summary + assert (tmp_dir / "logs" / Scalar.subdir / "m1.tsv").is_file() + assert (tmp_dir / logger.summary_path).is_file() == summary if summary: _, s = read_logs("logs") From 51f67f216d73f4c0acc975120e79f882bc70dc69 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Sep 2021 17:55:31 +0000 Subject: [PATCH 06/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- dvclive/data/__init__.py | 6 +----- dvclive/data/image_numpy.py | 4 ++-- dvclive/data/image_pil.py | 1 + dvclive/metrics.py | 5 +++-- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/dvclive/data/__init__.py b/dvclive/data/__init__.py index 13872b94..2be9d386 100644 --- a/dvclive/data/__init__.py +++ b/dvclive/data/__init__.py @@ -2,8 +2,4 @@ from .image_pil import ImagePIL from .scalar import Scalar -DATA_TYPES = [ - ImageNumpy, - ImagePIL, - Scalar -] \ No newline at end of file +DATA_TYPES = [ImageNumpy, ImagePIL, Scalar] diff --git a/dvclive/data/image_numpy.py b/dvclive/data/image_numpy.py index 8d2c2c4c..6b0b78c3 100644 --- a/dvclive/data/image_numpy.py +++ b/dvclive/data/image_numpy.py @@ -1,9 +1,9 @@ -from PIL import Image +from PIL import Image + from .image_pil import ImagePIL class ImageNumpy(ImagePIL): - @staticmethod def could_log(val: object) -> bool: if val.__class__.__module__ == "numpy": diff --git a/dvclive/data/image_pil.py b/dvclive/data/image_pil.py index b3a43730..e166940a 100644 --- a/dvclive/data/image_pil.py +++ b/dvclive/data/image_pil.py @@ -1,6 +1,7 @@ from pathlib import Path from PIL.Image import Image + from .base import Data diff --git a/dvclive/metrics.py b/dvclive/metrics.py index 6ec33c0a..006b441a 100644 --- a/dvclive/metrics.py +++ b/dvclive/metrics.py @@ -41,7 +41,7 @@ def __init__( self._init_paths() def _cleanup(self): - + for data_type in DATA_TYPES: subdir = Path(self.dir) / data_type.subdir data_files = f"*{'|*'.join(data_type.suffixes)}" @@ -58,7 +58,8 @@ def _init_paths(self): os.makedirs(self.dir, exist_ok=True) for data_type in DATA_TYPES: os.makedirs( - os.path.join(self.dir, data_type.subdir), exist_ok=True) + os.path.join(self.dir, data_type.subdir), exist_ok=True + ) if self._summary: self.make_summary() From ac22e988ffce8fffadfa3b598163f1ab9d1b8af9 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Mon, 20 Sep 2021 20:37:52 +0200 Subject: [PATCH 07/31] Fix tests --- dvclive/data/scalar.py | 1 - dvclive/metrics.py | 11 +++++++---- tests/test_main.py | 26 ++++++++++++-------------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/dvclive/data/scalar.py b/dvclive/data/scalar.py index fab49ada..b28278e0 100644 --- a/dvclive/data/scalar.py +++ b/dvclive/data/scalar.py @@ -33,7 +33,6 @@ def dump(self, val, step): [("timestamp", ts), ("step", self.step), (self.name, self.val)] ) - print(self.output_path) existed = self.output_path.exists() with open(self.output_path, "a") as fobj: writer = csv.DictWriter(fobj, d.keys(), delimiter="\t") diff --git a/dvclive/metrics.py b/dvclive/metrics.py index 006b441a..50a741c7 100644 --- a/dvclive/metrics.py +++ b/dvclive/metrics.py @@ -1,5 +1,6 @@ import json import logging +import shutil import os from collections import OrderedDict from pathlib import Path @@ -44,15 +45,15 @@ def _cleanup(self): for data_type in DATA_TYPES: subdir = Path(self.dir) / data_type.subdir - data_files = f"*{'|*'.join(data_type.suffixes)}" - for data_file in subdir.rglob(data_files): - data_file.unlink() + for suffix in data_type.suffixes: + for data_file in subdir.rglob(f"*{suffix}"): + data_file.unlink() if os.path.exists(self.summary_path): os.remove(self.summary_path) if os.path.exists(self.html_path): - os.remove(self.html_path) + shutil.rmtree(self.html_path, ignore_errors=True) def _init_paths(self): os.makedirs(self.dir, exist_ok=True) @@ -63,6 +64,8 @@ def _init_paths(self): if self._summary: self.make_summary() + if self._html: + os.makedirs(self.html_path, exist_ok=True) @staticmethod def from_env(): diff --git a/tests/test_main.py b/tests/test_main.py index 947ede22..d00c429b 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -21,10 +21,11 @@ def read_logs(path: str): - assert os.path.isdir(path) + path = Path(path) + assert path.is_dir() history = {} - for metric_file in Path(path).rglob("*.tsv"): - metric_name = str(metric_file).replace(path + os.path.sep, "") + for metric_file in (path / Scalar.subdir).rglob("*.tsv"): + metric_name = str(metric_file).replace(str(path / Scalar.subdir) + os.path.sep, "") metric_name = metric_name.replace(".tsv", "") history[metric_name] = _parse_tsv(metric_file) latest = _parse_json(os.path.join(path, "summary.json")) @@ -86,11 +87,9 @@ def test_nested_logging(tmp_dir): dvclive.log("train/m1", 1) dvclive.log("val/val_1/m1", 1) - assert (tmp_dir / "logs").is_dir() - assert (tmp_dir / "logs" / "train").is_dir() - assert (tmp_dir / "logs" / "val" / "val_1").is_dir() - assert (tmp_dir / "logs" / "train" / "m1.tsv").is_file() - assert (tmp_dir / "logs" / "val" / "val_1" / "m1.tsv").is_file() + assert (tmp_dir / "logs" / Scalar.subdir / "val" / "val_1").is_dir() + assert (tmp_dir / "logs" / Scalar.subdir / "train" / "m1.tsv").is_file() + assert (tmp_dir / "logs" / Scalar.subdir / "val" / "val_1" / "m1.tsv").is_file() _, summary = read_logs("logs") @@ -129,22 +128,21 @@ def test_cleanup(tmp_dir, summary, html): logger = dvclive.init("logs", summary=summary) dvclive.log("m1", 1) - html_path = tmp_dir / logger.html_path + html_path = tmp_dir / logger.html_path / "index.html" if html: - html_path.parent.mkdir() html_path.touch() (tmp_dir / "logs" / "some_user_file.txt").touch() - assert (tmp_dir / "logs" / "m1.tsv").is_file() - assert (tmp_dir / "logs.json").is_file() == summary + assert (tmp_dir / "logs" / Scalar.subdir / "m1.tsv").is_file() + assert (tmp_dir / logger.summary_path).is_file() == summary assert html_path.is_file() == html dvclive.init("logs", summary=summary) assert (tmp_dir / "logs" / "some_user_file.txt").is_file() - assert not (tmp_dir / "logs" / "m1.tsv").is_file() - assert (tmp_dir / "logs.json").is_file() == summary + assert not (tmp_dir / "logs" / Scalar.subdir / "m1.tsv").is_file() + assert (tmp_dir / logger.summary_path).is_file() == summary assert not (html_path).is_file() From 23078f5d84e087e878c9e90b4f6ea77239a5af92 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Sep 2021 18:38:17 +0000 Subject: [PATCH 08/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- dvclive/metrics.py | 2 +- tests/test_main.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/dvclive/metrics.py b/dvclive/metrics.py index 50a741c7..137db979 100644 --- a/dvclive/metrics.py +++ b/dvclive/metrics.py @@ -1,7 +1,7 @@ import json import logging -import shutil import os +import shutil from collections import OrderedDict from pathlib import Path from typing import Any, Dict, Union diff --git a/tests/test_main.py b/tests/test_main.py index d00c429b..ebb761c4 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -25,7 +25,9 @@ def read_logs(path: str): assert path.is_dir() history = {} for metric_file in (path / Scalar.subdir).rglob("*.tsv"): - metric_name = str(metric_file).replace(str(path / Scalar.subdir) + os.path.sep, "") + metric_name = str(metric_file).replace( + str(path / Scalar.subdir) + os.path.sep, "" + ) metric_name = metric_name.replace(".tsv", "") history[metric_name] = _parse_tsv(metric_file) latest = _parse_json(os.path.join(path, "summary.json")) @@ -89,7 +91,9 @@ def test_nested_logging(tmp_dir): assert (tmp_dir / "logs" / Scalar.subdir / "val" / "val_1").is_dir() assert (tmp_dir / "logs" / Scalar.subdir / "train" / "m1.tsv").is_file() - assert (tmp_dir / "logs" / Scalar.subdir / "val" / "val_1" / "m1.tsv").is_file() + assert ( + tmp_dir / "logs" / Scalar.subdir / "val" / "val_1" / "m1.tsv" + ).is_file() _, summary = read_logs("logs") @@ -141,7 +145,7 @@ def test_cleanup(tmp_dir, summary, html): dvclive.init("logs", summary=summary) assert (tmp_dir / "logs" / "some_user_file.txt").is_file() - assert not (tmp_dir / "logs" / Scalar.subdir / "m1.tsv").is_file() + assert not (tmp_dir / "logs" / Scalar.subdir / "m1.tsv").is_file() assert (tmp_dir / logger.summary_path).is_file() == summary assert not (html_path).is_file() From 4fa57e050a37a5da73f09827c1bf21e139603bda Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Mon, 20 Sep 2021 21:01:14 +0200 Subject: [PATCH 09/31] Added test_image --- tests/test_data/test_image.py | 44 +++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/test_data/test_image.py b/tests/test_data/test_image.py index e69de29b..ceb6408b 100644 --- a/tests/test_data/test_image.py +++ b/tests/test_data/test_image.py @@ -0,0 +1,44 @@ +import os + +import numpy as np +import pytest +from PIL import Image + +import dvclive +from dvclive.data import ImageNumpy, ImagePIL + + +def test_PIL(tmp_dir): + logger = dvclive.init() + img = Image.new('RGB', (500,500), (250,250,250)) + dvclive.log("image.png", img) + + assert (tmp_dir / logger.dir / ImagePIL.subdir / "image.png").exists() + + +@pytest.mark.parametrize("shape", [ + (500, 500), + (500, 500, 3), + (500, 500, 4) +]) +def test_numpy(tmp_dir, shape): + logger = dvclive.init() + img = np.ones(shape, np.uint8) * 255 + dvclive.log("image.png", img) + + assert (tmp_dir / logger.dir / ImageNumpy.subdir / "image.png").exists() + + +@pytest.mark.parametrize("pattern", [ + "image_{step}.png", + str(os.path.join("{step}", "image.png")) +]) +def test_step_formatting(tmp_dir, pattern): + logger = dvclive.init() + img = np.ones((500, 500, 3), np.uint8) + for _ in range(3): + dvclive.log(pattern, img) + dvclive.next_step() + + for step in range(3): + assert (tmp_dir / logger.dir / ImagePIL.subdir / pattern.format(step=step)).exists() From 979c55ddda0981b6d55c98987d9aeb6656d87f21 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Mon, 20 Sep 2021 21:02:18 +0200 Subject: [PATCH 10/31] pre-commit --- tests/test_data/test_image.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/test_data/test_image.py b/tests/test_data/test_image.py index ceb6408b..36353ba8 100644 --- a/tests/test_data/test_image.py +++ b/tests/test_data/test_image.py @@ -10,17 +10,13 @@ def test_PIL(tmp_dir): logger = dvclive.init() - img = Image.new('RGB', (500,500), (250,250,250)) + img = Image.new("RGB", (500, 500), (250, 250, 250)) dvclive.log("image.png", img) assert (tmp_dir / logger.dir / ImagePIL.subdir / "image.png").exists() -@pytest.mark.parametrize("shape", [ - (500, 500), - (500, 500, 3), - (500, 500, 4) -]) +@pytest.mark.parametrize("shape", [(500, 500), (500, 500, 3), (500, 500, 4)]) def test_numpy(tmp_dir, shape): logger = dvclive.init() img = np.ones(shape, np.uint8) * 255 @@ -29,10 +25,9 @@ def test_numpy(tmp_dir, shape): assert (tmp_dir / logger.dir / ImageNumpy.subdir / "image.png").exists() -@pytest.mark.parametrize("pattern", [ - "image_{step}.png", - str(os.path.join("{step}", "image.png")) -]) +@pytest.mark.parametrize( + "pattern", ["image_{step}.png", str(os.path.join("{step}", "image.png"))] +) def test_step_formatting(tmp_dir, pattern): logger = dvclive.init() img = np.ones((500, 500, 3), np.uint8) @@ -41,4 +36,6 @@ def test_step_formatting(tmp_dir, pattern): dvclive.next_step() for step in range(3): - assert (tmp_dir / logger.dir / ImagePIL.subdir / pattern.format(step=step)).exists() + assert ( + tmp_dir / logger.dir / ImagePIL.subdir / pattern.format(step=step) + ).exists() From c911db299f89d2f619817e13fa9755f9d9abedd1 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Mon, 20 Sep 2021 21:09:19 +0200 Subject: [PATCH 11/31] Fix catalyst and fastai --- tests/test_catalyst.py | 5 +++-- tests/test_fastai.py | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/test_catalyst.py b/tests/test_catalyst.py index f759d26f..5cb6ab17 100644 --- a/tests/test_catalyst.py +++ b/tests/test_catalyst.py @@ -10,6 +10,7 @@ import dvclive from dvclive.catalyst import DvcLiveCallback +from dvclive.data import Scalar # pylint: disable=redefined-outer-name, unused-argument @@ -66,8 +67,8 @@ def test_catalyst_callback(tmp_dir, runner, loaders): assert os.path.exists("dvc_logs") - train_path = tmp_dir / "dvc_logs/train" - valid_path = tmp_dir / "dvc_logs/valid" + train_path = tmp_dir / "dvc_logs" / Scalar.subdir / "train" + valid_path = tmp_dir / "dvc_logs" / Scalar.subdir / "valid" assert train_path.is_dir() assert valid_path.is_dir() diff --git a/tests/test_fastai.py b/tests/test_fastai.py index 6da548bb..c276bade 100644 --- a/tests/test_fastai.py +++ b/tests/test_fastai.py @@ -13,6 +13,7 @@ ) import dvclive +from dvclive.data import Scalar from dvclive.fastai import DvcLiveCallback # pylint: disable=redefined-outer-name, unused-argument @@ -49,12 +50,12 @@ def test_fastai_callback(tmp_dir, data_loader): assert os.path.exists("dvc_logs") - train_path = tmp_dir / "dvc_logs/train" - valid_path = tmp_dir / "dvc_logs/valid" + train_path = tmp_dir / "dvc_logs" / Scalar.subdir / "train" + valid_path = tmp_dir / "dvc_logs" / Scalar.subdir / "valid" assert train_path.is_dir() assert valid_path.is_dir() - assert (tmp_dir / "dvc_logs/accuracy.tsv").exists() + assert (tmp_dir / "dvc_logs" / Scalar.subdir / "accuracy.tsv").exists() def test_fastai_model_file(tmp_dir, data_loader): From 01d10102eac62d9ee73ead657ea3baa29db4c6f7 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Tue, 21 Sep 2021 10:31:10 +0200 Subject: [PATCH 12/31] Make pillow optional dep --- dvclive/data/image_numpy.py | 3 +-- dvclive/data/image_pil.py | 4 +--- setup.py | 3 ++- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/dvclive/data/image_numpy.py b/dvclive/data/image_numpy.py index 6b0b78c3..1deba74b 100644 --- a/dvclive/data/image_numpy.py +++ b/dvclive/data/image_numpy.py @@ -1,5 +1,3 @@ -from PIL import Image - from .image_pil import ImagePIL @@ -11,6 +9,7 @@ def could_log(val: object) -> bool: return False def dump(self, val, step) -> None: + from PIL import Image val = Image.fromarray(val) super().dump(val, step) diff --git a/dvclive/data/image_pil.py b/dvclive/data/image_pil.py index e166940a..b5193a72 100644 --- a/dvclive/data/image_pil.py +++ b/dvclive/data/image_pil.py @@ -1,7 +1,5 @@ from pathlib import Path -from PIL.Image import Image - from .base import Data @@ -11,7 +9,7 @@ class ImagePIL(Data): @staticmethod def could_log(val: object) -> bool: - if isinstance(val, Image): + if val.__class__.__module__ == "PIL.Image": return True return False diff --git a/setup.py b/setup.py index 6b625f74..f69c0b29 100644 --- a/setup.py +++ b/setup.py @@ -43,8 +43,9 @@ def run(self): hugginface = ["transformers", "datasets"] catalyst = ["catalyst"] fastai = ["fastai"] +image = ["pillow"] -all_libs = mmcv + tf + xgb + lgbm + hugginface + catalyst + fastai +all_libs = mmcv + tf + xgb + lgbm + hugginface + catalyst + fastai + image tests_requires = [ "pylint==2.5.3", From 440f8a995f69b5cd78ff39f6f656b67cf7a53f74 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 21 Sep 2021 08:32:34 +0000 Subject: [PATCH 13/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- dvclive/data/image_numpy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dvclive/data/image_numpy.py b/dvclive/data/image_numpy.py index 1deba74b..3f440bef 100644 --- a/dvclive/data/image_numpy.py +++ b/dvclive/data/image_numpy.py @@ -10,6 +10,7 @@ def could_log(val: object) -> bool: def dump(self, val, step) -> None: from PIL import Image + val = Image.fromarray(val) super().dump(val, step) From cb2962ebd4b3661767af2ad5172b37ddbaeeb4ac Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Tue, 21 Sep 2021 11:51:16 +0200 Subject: [PATCH 14/31] Renamed scalar -> scalars --- dvclive/data/scalar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dvclive/data/scalar.py b/dvclive/data/scalar.py index b28278e0..51ddc2c4 100644 --- a/dvclive/data/scalar.py +++ b/dvclive/data/scalar.py @@ -10,7 +10,7 @@ class Scalar(Data): - subdir = "scalar" + subdir = "scalars" suffixes = [".csv", ".tsv"] @staticmethod From c2bda79f6c8df281500284e81d333835a124ea27 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Tue, 21 Sep 2021 17:04:36 +0200 Subject: [PATCH 15/31] Raise exception --- tests/test_data/test_image.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_data/test_image.py b/tests/test_data/test_image.py index 36353ba8..9cee30f2 100644 --- a/tests/test_data/test_image.py +++ b/tests/test_data/test_image.py @@ -16,6 +16,13 @@ def test_PIL(tmp_dir): assert (tmp_dir / logger.dir / ImagePIL.subdir / "image.png").exists() +def test_invalid_extension(tmp_dir): + dvclive.init() + img = Image.new("RGB", (500, 500), (250, 250, 250)) + with pytest.raises(ValueError): + dvclive.log("image.foo", img) + + @pytest.mark.parametrize("shape", [(500, 500), (500, 500, 3), (500, 500, 4)]) def test_numpy(tmp_dir, shape): logger = dvclive.init() From 79f99a0a27b1375782a449375a50665990795a18 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Tue, 21 Sep 2021 20:43:06 +0200 Subject: [PATCH 16/31] Fix pylint --- tests/test_data/test_image.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_data/test_image.py b/tests/test_data/test_image.py index 9cee30f2..6bd5a3cb 100644 --- a/tests/test_data/test_image.py +++ b/tests/test_data/test_image.py @@ -4,6 +4,7 @@ import pytest from PIL import Image +# pylint: disable=unused-argument import dvclive from dvclive.data import ImageNumpy, ImagePIL From 92800156aad3e14a4ddf268a48923aeb380a34ef Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Wed, 22 Sep 2021 21:34:01 +0200 Subject: [PATCH 17/31] Old summary --- dvclive/metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dvclive/metrics.py b/dvclive/metrics.py index 137db979..049a9f9c 100644 --- a/dvclive/metrics.py +++ b/dvclive/metrics.py @@ -103,7 +103,7 @@ def exists(self): @property def summary_path(self): - return os.path.join(self.dir, "summary.json") + return str(self.dir) + ".json" @property def html_path(self): From 523d135f527633de74889844b0ef138a42f4a7df Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Thu, 30 Sep 2021 10:18:17 +0200 Subject: [PATCH 18/31] Removed subdirs --- dvclive/data/image_pil.py | 3 +-- dvclive/data/scalar.py | 3 +-- dvclive/metrics.py | 10 ++-------- tests/test_main.py | 25 +++++++++---------------- 4 files changed, 13 insertions(+), 28 deletions(-) diff --git a/dvclive/data/image_pil.py b/dvclive/data/image_pil.py index b5193a72..96a6483f 100644 --- a/dvclive/data/image_pil.py +++ b/dvclive/data/image_pil.py @@ -4,7 +4,6 @@ class ImagePIL(Data): - subdir = "images" suffixes = [".jpg", ".jpeg", ".gif", ".png"] @staticmethod @@ -20,7 +19,7 @@ def output_path(self) -> Path: f"Invalid image suffix '{Path(self.name).suffix}'" f" Must be one of {self.suffixes}" ) - return self.output_folder / self.subdir / self.name + return self.output_folder / self.name def dump(self, val, step) -> None: super().dump(val, step) diff --git a/dvclive/data/scalar.py b/dvclive/data/scalar.py index 51ddc2c4..d85f6631 100644 --- a/dvclive/data/scalar.py +++ b/dvclive/data/scalar.py @@ -10,7 +10,6 @@ class Scalar(Data): - subdir = "scalars" suffixes = [".csv", ".tsv"] @staticmethod @@ -21,7 +20,7 @@ def could_log(val: object) -> bool: @property def output_path(self) -> Path: - _path = self.output_folder / self.subdir / self.name + _path = self.output_folder / self.name _path.parent.mkdir(exist_ok=True, parents=True) return _path.with_suffix(".tsv") diff --git a/dvclive/metrics.py b/dvclive/metrics.py index 049a9f9c..d5d61f85 100644 --- a/dvclive/metrics.py +++ b/dvclive/metrics.py @@ -44,9 +44,8 @@ def __init__( def _cleanup(self): for data_type in DATA_TYPES: - subdir = Path(self.dir) / data_type.subdir for suffix in data_type.suffixes: - for data_file in subdir.rglob(f"*{suffix}"): + for data_file in Path(self.dir).rglob(f"*{suffix}"): data_file.unlink() if os.path.exists(self.summary_path): @@ -57,11 +56,6 @@ def _cleanup(self): def _init_paths(self): os.makedirs(self.dir, exist_ok=True) - for data_type in DATA_TYPES: - os.makedirs( - os.path.join(self.dir, data_type.subdir), exist_ok=True - ) - if self._summary: self.make_summary() if self._html: @@ -107,7 +101,7 @@ def summary_path(self): @property def html_path(self): - return os.path.join(self.dir, "html") + return str(self.dir) + "_dvc_plots/index.html" def get_step(self) -> int: return self._step diff --git a/tests/test_main.py b/tests/test_main.py index ebb761c4..02e213f7 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -10,7 +10,6 @@ from dvclive import env # pylint: disable=unused-argument -from dvclive.data import DATA_TYPES, Scalar from dvclive.dvc import SIGNAL_FILE from dvclive.error import ( ConfigMismatchError, @@ -24,13 +23,11 @@ def read_logs(path: str): path = Path(path) assert path.is_dir() history = {} - for metric_file in (path / Scalar.subdir).rglob("*.tsv"): - metric_name = str(metric_file).replace( - str(path / Scalar.subdir) + os.path.sep, "" - ) + for metric_file in path.rglob("*.tsv"): + metric_name = str(metric_file).replace(str(path) + os.path.sep, "") metric_name = metric_name.replace(".tsv", "") history[metric_name] = _parse_tsv(metric_file) - latest = _parse_json(os.path.join(path, "summary.json")) + latest = _parse_json(str(path) + ".json") return history, latest @@ -65,8 +62,6 @@ def test_init_paths(tmp_dir, path): dvclive.init(path) assert (tmp_dir / path).is_dir() - for data_type in DATA_TYPES: - assert (tmp_dir / path / data_type.subdir).is_dir() @pytest.mark.parametrize("summary", [True, False]) @@ -75,7 +70,7 @@ def test_logging(tmp_dir, summary): dvclive.log("m1", 1) - assert (tmp_dir / "logs" / Scalar.subdir / "m1.tsv").is_file() + assert (tmp_dir / "logs" / "m1.tsv").is_file() assert (tmp_dir / logger.summary_path).is_file() == summary if summary: @@ -89,11 +84,9 @@ def test_nested_logging(tmp_dir): dvclive.log("train/m1", 1) dvclive.log("val/val_1/m1", 1) - assert (tmp_dir / "logs" / Scalar.subdir / "val" / "val_1").is_dir() - assert (tmp_dir / "logs" / Scalar.subdir / "train" / "m1.tsv").is_file() - assert ( - tmp_dir / "logs" / Scalar.subdir / "val" / "val_1" / "m1.tsv" - ).is_file() + assert (tmp_dir / "logs" / "val" / "val_1").is_dir() + assert (tmp_dir / "logs" / "train" / "m1.tsv").is_file() + assert (tmp_dir / "logs" / "val" / "val_1" / "m1.tsv").is_file() _, summary = read_logs("logs") @@ -138,14 +131,14 @@ def test_cleanup(tmp_dir, summary, html): (tmp_dir / "logs" / "some_user_file.txt").touch() - assert (tmp_dir / "logs" / Scalar.subdir / "m1.tsv").is_file() + assert (tmp_dir / "logs" / "m1.tsv").is_file() assert (tmp_dir / logger.summary_path).is_file() == summary assert html_path.is_file() == html dvclive.init("logs", summary=summary) assert (tmp_dir / "logs" / "some_user_file.txt").is_file() - assert not (tmp_dir / "logs" / Scalar.subdir / "m1.tsv").is_file() + assert not (tmp_dir / "logs" / "m1.tsv").is_file() assert (tmp_dir / logger.summary_path).is_file() == summary assert not (html_path).is_file() From 404beaaa21ce23f385994301a1b2cba69ad659e3 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Thu, 30 Sep 2021 10:53:11 +0200 Subject: [PATCH 19/31] Add image summary --- dvclive/data/image_numpy.py | 4 ---- dvclive/data/image_pil.py | 4 +++- tests/test_data/test_image.py | 10 ++++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dvclive/data/image_numpy.py b/dvclive/data/image_numpy.py index 3f440bef..ed9496bf 100644 --- a/dvclive/data/image_numpy.py +++ b/dvclive/data/image_numpy.py @@ -13,7 +13,3 @@ def dump(self, val, step) -> None: val = Image.fromarray(val) super().dump(val, step) - - @property - def summary(self): - return {} diff --git a/dvclive/data/image_pil.py b/dvclive/data/image_pil.py index 96a6483f..e5dabc7f 100644 --- a/dvclive/data/image_pil.py +++ b/dvclive/data/image_pil.py @@ -30,4 +30,6 @@ def dump(self, val, step) -> None: @property def summary(self): - return {} + return { + self.name: str(self.output_path) + } diff --git a/tests/test_data/test_image.py b/tests/test_data/test_image.py index 6bd5a3cb..5e4a141a 100644 --- a/tests/test_data/test_image.py +++ b/tests/test_data/test_image.py @@ -6,7 +6,7 @@ # pylint: disable=unused-argument import dvclive -from dvclive.data import ImageNumpy, ImagePIL +from tests.test_main import _parse_json def test_PIL(tmp_dir): @@ -14,8 +14,10 @@ def test_PIL(tmp_dir): img = Image.new("RGB", (500, 500), (250, 250, 250)) dvclive.log("image.png", img) - assert (tmp_dir / logger.dir / ImagePIL.subdir / "image.png").exists() + assert (tmp_dir / logger.dir / "image.png").exists() + summary = _parse_json("dvclive.json") + assert summary["image.png"] == os.path.join(logger.dir, "image.png") def test_invalid_extension(tmp_dir): dvclive.init() @@ -30,7 +32,7 @@ def test_numpy(tmp_dir, shape): img = np.ones(shape, np.uint8) * 255 dvclive.log("image.png", img) - assert (tmp_dir / logger.dir / ImageNumpy.subdir / "image.png").exists() + assert (tmp_dir / logger.dir / "image.png").exists() @pytest.mark.parametrize( @@ -45,5 +47,5 @@ def test_step_formatting(tmp_dir, pattern): for step in range(3): assert ( - tmp_dir / logger.dir / ImagePIL.subdir / pattern.format(step=step) + tmp_dir / logger.dir / pattern.format(step=step) ).exists() From 48c8f9c7f40fbc42153f353238f4588a21deffb4 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Thu, 30 Sep 2021 16:53:32 +0200 Subject: [PATCH 20/31] Fix test subdirs --- tests/test_catalyst.py | 5 ++--- tests/test_fastai.py | 7 +++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/test_catalyst.py b/tests/test_catalyst.py index 5cb6ab17..dfd52a4d 100644 --- a/tests/test_catalyst.py +++ b/tests/test_catalyst.py @@ -10,7 +10,6 @@ import dvclive from dvclive.catalyst import DvcLiveCallback -from dvclive.data import Scalar # pylint: disable=redefined-outer-name, unused-argument @@ -67,8 +66,8 @@ def test_catalyst_callback(tmp_dir, runner, loaders): assert os.path.exists("dvc_logs") - train_path = tmp_dir / "dvc_logs" / Scalar.subdir / "train" - valid_path = tmp_dir / "dvc_logs" / Scalar.subdir / "valid" + train_path = tmp_dir / "dvc_logs" / "train" + valid_path = tmp_dir / "dvc_logs" / "valid" assert train_path.is_dir() assert valid_path.is_dir() diff --git a/tests/test_fastai.py b/tests/test_fastai.py index c276bade..2d3c8361 100644 --- a/tests/test_fastai.py +++ b/tests/test_fastai.py @@ -13,7 +13,6 @@ ) import dvclive -from dvclive.data import Scalar from dvclive.fastai import DvcLiveCallback # pylint: disable=redefined-outer-name, unused-argument @@ -50,12 +49,12 @@ def test_fastai_callback(tmp_dir, data_loader): assert os.path.exists("dvc_logs") - train_path = tmp_dir / "dvc_logs" / Scalar.subdir / "train" - valid_path = tmp_dir / "dvc_logs" / Scalar.subdir / "valid" + train_path = tmp_dir / "dvc_logs" / "train" + valid_path = tmp_dir / "dvc_logs" / "valid" assert train_path.is_dir() assert valid_path.is_dir() - assert (tmp_dir / "dvc_logs" / Scalar.subdir / "accuracy.tsv").exists() + assert (tmp_dir / "dvc_logs" / "accuracy.tsv").exists() def test_fastai_model_file(tmp_dir, data_loader): From 5441e449c18ee5dc8ed5faed5c5c1c693ddc7dcc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 30 Sep 2021 14:54:32 +0000 Subject: [PATCH 21/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- dvclive/data/image_pil.py | 4 +--- tests/test_data/test_image.py | 5 ++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/dvclive/data/image_pil.py b/dvclive/data/image_pil.py index e5dabc7f..4150d5e1 100644 --- a/dvclive/data/image_pil.py +++ b/dvclive/data/image_pil.py @@ -30,6 +30,4 @@ def dump(self, val, step) -> None: @property def summary(self): - return { - self.name: str(self.output_path) - } + return {self.name: str(self.output_path)} diff --git a/tests/test_data/test_image.py b/tests/test_data/test_image.py index 5e4a141a..6c20eb4c 100644 --- a/tests/test_data/test_image.py +++ b/tests/test_data/test_image.py @@ -19,6 +19,7 @@ def test_PIL(tmp_dir): assert summary["image.png"] == os.path.join(logger.dir, "image.png") + def test_invalid_extension(tmp_dir): dvclive.init() img = Image.new("RGB", (500, 500), (250, 250, 250)) @@ -46,6 +47,4 @@ def test_step_formatting(tmp_dir, pattern): dvclive.next_step() for step in range(3): - assert ( - tmp_dir / logger.dir / pattern.format(step=step) - ).exists() + assert (tmp_dir / logger.dir / pattern.format(step=step)).exists() From 7295c9f62f537da10a466564b3206832bd8ae7ea Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Thu, 30 Sep 2021 17:04:07 +0200 Subject: [PATCH 22/31] Include step in image summary --- dvclive/data/image_pil.py | 2 +- tests/test_data/test_image.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/dvclive/data/image_pil.py b/dvclive/data/image_pil.py index e5dabc7f..3c284081 100644 --- a/dvclive/data/image_pil.py +++ b/dvclive/data/image_pil.py @@ -31,5 +31,5 @@ def dump(self, val, step) -> None: @property def summary(self): return { - self.name: str(self.output_path) + self.name: str(self.output_path).format(step=self.step) } diff --git a/tests/test_data/test_image.py b/tests/test_data/test_image.py index 5e4a141a..f5ef16eb 100644 --- a/tests/test_data/test_image.py +++ b/tests/test_data/test_image.py @@ -49,3 +49,7 @@ def test_step_formatting(tmp_dir, pattern): assert ( tmp_dir / logger.dir / pattern.format(step=step) ).exists() + + summary = _parse_json("dvclive.json") + + assert summary[pattern] == os.path.join(logger.dir, pattern.format(step=step)) From 2b9cc8062b28b72048d19bdae7b9ffbbc3494c44 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Thu, 30 Sep 2021 17:05:38 +0200 Subject: [PATCH 23/31] lint --- dvclive/data/image_pil.py | 4 +--- tests/test_data/test_image.py | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/dvclive/data/image_pil.py b/dvclive/data/image_pil.py index 3c284081..81ba6131 100644 --- a/dvclive/data/image_pil.py +++ b/dvclive/data/image_pil.py @@ -30,6 +30,4 @@ def dump(self, val, step) -> None: @property def summary(self): - return { - self.name: str(self.output_path).format(step=self.step) - } + return {self.name: str(self.output_path).format(step=self.step)} diff --git a/tests/test_data/test_image.py b/tests/test_data/test_image.py index 05d6cb73..eb72434d 100644 --- a/tests/test_data/test_image.py +++ b/tests/test_data/test_image.py @@ -47,10 +47,10 @@ def test_step_formatting(tmp_dir, pattern): dvclive.next_step() for step in range(3): - assert ( - tmp_dir / logger.dir / pattern.format(step=step) - ).exists() + assert (tmp_dir / logger.dir / pattern.format(step=step)).exists() summary = _parse_json("dvclive.json") - assert summary[pattern] == os.path.join(logger.dir, pattern.format(step=step)) + assert summary[pattern] == os.path.join( + logger.dir, pattern.format(step=step) + ) From af2c270c5a537b46e3ec9268b2ee259027a3457d Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Thu, 30 Sep 2021 17:16:29 +0200 Subject: [PATCH 24/31] Raise Error on lazy PIL import --- dvclive/data/image_numpy.py | 10 ++++++++-- setup.py | 4 +++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/dvclive/data/image_numpy.py b/dvclive/data/image_numpy.py index ed9496bf..ba49972b 100644 --- a/dvclive/data/image_numpy.py +++ b/dvclive/data/image_numpy.py @@ -1,6 +1,6 @@ +from dvclive.error import DvcLiveError from .image_pil import ImagePIL - class ImageNumpy(ImagePIL): @staticmethod def could_log(val: object) -> bool: @@ -9,7 +9,13 @@ def could_log(val: object) -> bool: return False def dump(self, val, step) -> None: - from PIL import Image + try: + from PIL import Image + except ImportError as e: + raise DvcLiveError( + "'pillow' is required for logging images." + " You can install it by running" + " 'pip install pillow'") from e val = Image.fromarray(val) super().dump(val, step) diff --git a/setup.py b/setup.py index f69c0b29..2f4c8b0b 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,7 @@ def run(self): _build_py.run(self) -mmcv = ["mmcv", "torch", "torchvision"] +mmcv = ["mmcv"] tf = ["tensorflow"] xgb = ["xgboost"] lgbm = ["lightgbm"] @@ -74,9 +74,11 @@ def run(self): "tf": tf, "xgb": xgb, "lgbm": lgbm, + "mmcv": mmcv, "huggingface": hugginface, "catalyst": catalyst, "fastai": fastai, + "image": image }, keywords="data-science metrics machine-learning developer-tools ai", python_requires=">=3.6", From 720890c4a97dd19843e8fa32ca3295f29fcd842f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 30 Sep 2021 15:16:43 +0000 Subject: [PATCH 25/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- dvclive/data/image_numpy.py | 5 ++++- setup.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dvclive/data/image_numpy.py b/dvclive/data/image_numpy.py index ba49972b..7b5b9630 100644 --- a/dvclive/data/image_numpy.py +++ b/dvclive/data/image_numpy.py @@ -1,6 +1,8 @@ from dvclive.error import DvcLiveError + from .image_pil import ImagePIL + class ImageNumpy(ImagePIL): @staticmethod def could_log(val: object) -> bool: @@ -15,7 +17,8 @@ def dump(self, val, step) -> None: raise DvcLiveError( "'pillow' is required for logging images." " You can install it by running" - " 'pip install pillow'") from e + " 'pip install pillow'" + ) from e val = Image.fromarray(val) super().dump(val, step) diff --git a/setup.py b/setup.py index 2f4c8b0b..bb085fa8 100644 --- a/setup.py +++ b/setup.py @@ -78,7 +78,7 @@ def run(self): "huggingface": hugginface, "catalyst": catalyst, "fastai": fastai, - "image": image + "image": image, }, keywords="data-science metrics machine-learning developer-tools ai", python_requires=">=3.6", From 18052a943bd4cbbfdacb0cd63ba33f5f44b59254 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Oct 2021 18:16:59 +0000 Subject: [PATCH 26/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 406a132f..72911788 100644 --- a/setup.py +++ b/setup.py @@ -46,7 +46,9 @@ def run(self): pl = ["pytorch_lightning"] image = ["pillow"] -all_libs = mmcv + tf + xgb + lgbm + hugginface + catalyst + fastai + + pl + image +all_libs = ( + mmcv + tf + xgb + lgbm + hugginface + catalyst + fastai + +pl + image +) tests_requires = [ "pylint==2.5.3", From 5751203ad467e4b1fed22774c7f6375966c6da02 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Mon, 4 Oct 2021 20:22:29 +0200 Subject: [PATCH 27/31] Fix setup --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 72911788..28e8a0ef 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ def run(self): image = ["pillow"] all_libs = ( - mmcv + tf + xgb + lgbm + hugginface + catalyst + fastai + +pl + image + mmcv + tf + xgb + lgbm + hugginface + catalyst + fastai + pl + image ) tests_requires = [ From dc6802ceb1e3f0195bf350bb38f078c69fca51eb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Oct 2021 18:22:43 +0000 Subject: [PATCH 28/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- setup.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 28e8a0ef..d84ef395 100644 --- a/setup.py +++ b/setup.py @@ -46,9 +46,7 @@ def run(self): pl = ["pytorch_lightning"] image = ["pillow"] -all_libs = ( - mmcv + tf + xgb + lgbm + hugginface + catalyst + fastai + pl + image -) +all_libs = mmcv + tf + xgb + lgbm + hugginface + catalyst + fastai + pl + image tests_requires = [ "pylint==2.5.3", From 91ba34ee2fc051c891fcfa846b2d0af74d00aba9 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Wed, 27 Oct 2021 21:26:15 +0200 Subject: [PATCH 29/31] Fixed merge --- tests/test_main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_main.py b/tests/test_main.py index 28e3eddb..0403fe92 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -69,7 +69,7 @@ def test_logging(tmp_dir, summary): dvclive.log("m1", 1) assert (tmp_dir / "logs" / "m1.tsv").is_file() - assert (tmp_dir / logger.summary_path).is_file() == summary + assert (tmp_dir / dvclive.summary_path).is_file() == summary if summary: _, s = read_logs("logs") @@ -132,14 +132,14 @@ def test_cleanup(tmp_dir, summary, html): (tmp_dir / "logs" / "some_user_file.txt").touch() assert (tmp_dir / "logs" / "m1.tsv").is_file() - assert (tmp_dir / logger.summary_path).is_file() == summary + assert (tmp_dir / dvclive.summary_path).is_file() == summary assert html_path.is_file() == html dvclive = Live("logs", summary=summary) assert (tmp_dir / "logs" / "some_user_file.txt").is_file() assert not (tmp_dir / "logs" / "m1.tsv").is_file() - assert (tmp_dir / logger.summary_path).is_file() == summary + assert (tmp_dir / dvclive.summary_path).is_file() == summary assert not (html_path).is_file() From 1cb188334b78b6eb836920849ebfb364fd10b848 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Mon, 8 Nov 2021 14:50:24 +0100 Subject: [PATCH 30/31] Fixed tests --- dvclive/live.py | 4 ++-- tests/test_data/test_image.py | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dvclive/live.py b/dvclive/live.py index 4f37eec7..64b83c55 100644 --- a/dvclive/live.py +++ b/dvclive/live.py @@ -56,14 +56,14 @@ def _cleanup(self): os.remove(self.summary_path) if os.path.exists(self.html_path): - shutil.rmtree(self.html_path, ignore_errors=True) + shutil.rmtree(Path(self.html_path).parent, ignore_errors=True) def _init_paths(self): os.makedirs(self.dir, exist_ok=True) if self._summary: self.make_summary() if self._html: - os.makedirs(self.html_path, exist_ok=True) + os.makedirs(Path(self.html_path).parent, exist_ok=True) def init_from_env(self) -> None: from . import env diff --git a/tests/test_data/test_image.py b/tests/test_data/test_image.py index eb72434d..46222546 100644 --- a/tests/test_data/test_image.py +++ b/tests/test_data/test_image.py @@ -5,23 +5,23 @@ from PIL import Image # pylint: disable=unused-argument -import dvclive +from dvclive import Live from tests.test_main import _parse_json def test_PIL(tmp_dir): - logger = dvclive.init() + dvclive = Live() img = Image.new("RGB", (500, 500), (250, 250, 250)) dvclive.log("image.png", img) - assert (tmp_dir / logger.dir / "image.png").exists() + assert (tmp_dir / dvclive.dir / "image.png").exists() summary = _parse_json("dvclive.json") - assert summary["image.png"] == os.path.join(logger.dir, "image.png") + assert summary["image.png"] == os.path.join(dvclive.dir, "image.png") def test_invalid_extension(tmp_dir): - dvclive.init() + dvclive = Live() img = Image.new("RGB", (500, 500), (250, 250, 250)) with pytest.raises(ValueError): dvclive.log("image.foo", img) @@ -29,28 +29,28 @@ def test_invalid_extension(tmp_dir): @pytest.mark.parametrize("shape", [(500, 500), (500, 500, 3), (500, 500, 4)]) def test_numpy(tmp_dir, shape): - logger = dvclive.init() + dvclive = Live() img = np.ones(shape, np.uint8) * 255 dvclive.log("image.png", img) - assert (tmp_dir / logger.dir / "image.png").exists() + assert (tmp_dir / dvclive.dir / "image.png").exists() @pytest.mark.parametrize( "pattern", ["image_{step}.png", str(os.path.join("{step}", "image.png"))] ) def test_step_formatting(tmp_dir, pattern): - logger = dvclive.init() + dvclive = Live() img = np.ones((500, 500, 3), np.uint8) for _ in range(3): dvclive.log(pattern, img) dvclive.next_step() for step in range(3): - assert (tmp_dir / logger.dir / pattern.format(step=step)).exists() + assert (tmp_dir / dvclive.dir / pattern.format(step=step)).exists() summary = _parse_json("dvclive.json") assert summary[pattern] == os.path.join( - logger.dir, pattern.format(step=step) + dvclive.dir, pattern.format(step=step) ) From 69beabc7e82480d4b5fe45a9a5dd2dbf27543ef1 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Mon, 8 Nov 2021 15:01:46 +0100 Subject: [PATCH 31/31] Fixed step formatting --- dvclive/data/image_pil.py | 2 +- tests/test_data/test_image.py | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/dvclive/data/image_pil.py b/dvclive/data/image_pil.py index 81ba6131..3f6d4d93 100644 --- a/dvclive/data/image_pil.py +++ b/dvclive/data/image_pil.py @@ -19,7 +19,7 @@ def output_path(self) -> Path: f"Invalid image suffix '{Path(self.name).suffix}'" f" Must be one of {self.suffixes}" ) - return self.output_folder / self.name + return self.output_folder / "{step}" / self.name def dump(self, val, step) -> None: super().dump(val, step) diff --git a/tests/test_data/test_image.py b/tests/test_data/test_image.py index 46222546..5e7a8792 100644 --- a/tests/test_data/test_image.py +++ b/tests/test_data/test_image.py @@ -14,10 +14,10 @@ def test_PIL(tmp_dir): img = Image.new("RGB", (500, 500), (250, 250, 250)) dvclive.log("image.png", img) - assert (tmp_dir / dvclive.dir / "image.png").exists() + assert (tmp_dir / dvclive.dir / "0" / "image.png").exists() summary = _parse_json("dvclive.json") - assert summary["image.png"] == os.path.join(dvclive.dir, "image.png") + assert summary["image.png"] == os.path.join(dvclive.dir, "0", "image.png") def test_invalid_extension(tmp_dir): @@ -33,24 +33,21 @@ def test_numpy(tmp_dir, shape): img = np.ones(shape, np.uint8) * 255 dvclive.log("image.png", img) - assert (tmp_dir / dvclive.dir / "image.png").exists() + assert (tmp_dir / dvclive.dir / "0" / "image.png").exists() -@pytest.mark.parametrize( - "pattern", ["image_{step}.png", str(os.path.join("{step}", "image.png"))] -) -def test_step_formatting(tmp_dir, pattern): +def test_step_formatting(tmp_dir): dvclive = Live() img = np.ones((500, 500, 3), np.uint8) for _ in range(3): - dvclive.log(pattern, img) + dvclive.log("image.png", img) dvclive.next_step() for step in range(3): - assert (tmp_dir / dvclive.dir / pattern.format(step=step)).exists() + assert (tmp_dir / dvclive.dir / str(step) / "image.png").exists() summary = _parse_json("dvclive.json") - assert summary[pattern] == os.path.join( - dvclive.dir, pattern.format(step=step) + assert summary["image.png"] == os.path.join( + dvclive.dir, str(step), "image.png" )