From 010a3059ff0916c055e9bc6b8c0fcb5ec1168c91 Mon Sep 17 00:00:00 2001 From: Thomas Lazarus Date: Mon, 27 Mar 2023 21:09:48 -0500 Subject: [PATCH 01/12] WIP copy files to path_in_repo --- skops/card/_model_card.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/skops/card/_model_card.py b/skops/card/_model_card.py index 1e54fc0e..74cd3c0d 100644 --- a/skops/card/_model_card.py +++ b/skops/card/_model_card.py @@ -2,6 +2,7 @@ import json import re +import shutil import sys import textwrap import zipfile @@ -1003,6 +1004,7 @@ def add_plot( description: str | None = None, alt_text: str | None = None, folded=False, + path_in_repo: str | None = None, **kwargs: str | Path, ) -> Self: """Add plots to the model card. @@ -1032,6 +1034,11 @@ def add_plot( to show the content. This option is useful if the added plot is large. + path_in_repo: str or None (default=None) + If a string is passed as ``path_in_repo``, it is used as the path to + copy the plot to in the repository. If this argument is ``None``, + the plot will not be copied. + **kwargs : dict The arguments should be of the form ``name=plot_path``, where ``name`` is the name of the plot and section, and ``plot_path`` is @@ -1057,6 +1064,9 @@ def add_plot( folded=folded, ) self._add_single(section_name, section) + + if path_in_repo is not None: + shutil.copy(plot_path, path_in_repo) return self def add_table( @@ -1168,6 +1178,7 @@ def add_permutation_importances( plot_name: str = "Permutation Importances", overwrite: bool = False, description: str | None = None, + path_in_repo: str | None = None, ) -> Self: """Plots permutation importance and saves it to model card. @@ -1214,7 +1225,12 @@ def add_permutation_importances( ax.set_title(plot_name) ax.set_xlabel("Decrease in Score") plt.savefig(plot_file) - self.add_plot(description=description, alt_text=None, **{plot_name: plot_file}) + self.add_plot( + description=description, + alt_text=None, + path_in_repo=path_in_repo, + **{plot_name: plot_file}, + ) return self From 83c041af0248f746d951d40f7ab0f4b4f02b0431 Mon Sep 17 00:00:00 2001 From: Thomas Lazarus Date: Mon, 3 Apr 2023 19:32:12 -0500 Subject: [PATCH 02/12] Revert "WIP copy files to path_in_repo" This reverts commit 010a3059ff0916c055e9bc6b8c0fcb5ec1168c91. --- skops/card/_model_card.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/skops/card/_model_card.py b/skops/card/_model_card.py index 74cd3c0d..1e54fc0e 100644 --- a/skops/card/_model_card.py +++ b/skops/card/_model_card.py @@ -2,7 +2,6 @@ import json import re -import shutil import sys import textwrap import zipfile @@ -1004,7 +1003,6 @@ def add_plot( description: str | None = None, alt_text: str | None = None, folded=False, - path_in_repo: str | None = None, **kwargs: str | Path, ) -> Self: """Add plots to the model card. @@ -1034,11 +1032,6 @@ def add_plot( to show the content. This option is useful if the added plot is large. - path_in_repo: str or None (default=None) - If a string is passed as ``path_in_repo``, it is used as the path to - copy the plot to in the repository. If this argument is ``None``, - the plot will not be copied. - **kwargs : dict The arguments should be of the form ``name=plot_path``, where ``name`` is the name of the plot and section, and ``plot_path`` is @@ -1064,9 +1057,6 @@ def add_plot( folded=folded, ) self._add_single(section_name, section) - - if path_in_repo is not None: - shutil.copy(plot_path, path_in_repo) return self def add_table( @@ -1178,7 +1168,6 @@ def add_permutation_importances( plot_name: str = "Permutation Importances", overwrite: bool = False, description: str | None = None, - path_in_repo: str | None = None, ) -> Self: """Plots permutation importance and saves it to model card. @@ -1225,12 +1214,7 @@ def add_permutation_importances( ax.set_title(plot_name) ax.set_xlabel("Decrease in Score") plt.savefig(plot_file) - self.add_plot( - description=description, - alt_text=None, - path_in_repo=path_in_repo, - **{plot_name: plot_file}, - ) + self.add_plot(description=description, alt_text=None, **{plot_name: plot_file}) return self From 147629433cb40739f4e6afb8b89767fbeb8ae9e9 Mon Sep 17 00:00:00 2001 From: Thomas Lazarus Date: Tue, 4 Apr 2023 20:23:31 -0500 Subject: [PATCH 03/12] Moves copying plots to Card.save() --- skops/card/_model_card.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/skops/card/_model_card.py b/skops/card/_model_card.py index 1e54fc0e..610b206e 100644 --- a/skops/card/_model_card.py +++ b/skops/card/_model_card.py @@ -2,6 +2,7 @@ import json import re +import shutil import sys import textwrap import zipfile @@ -1399,7 +1400,15 @@ def _generate_card(self) -> Iterator[str]: # add an empty line add the end yield "" - def save(self, path: str | Path) -> None: + def _copy_plots(self, data, plot_path: str): + """Copy the plots to the specified path.""" + for section in data.values(): + self._copy_plots(section.subsections, plot_path) + + if hasattr(section, "path"): + shutil.copy(section.path, plot_path + "/" + section.path) + + def save(self, path: str | Path, plot_path: None) -> None: """Save the model card. This method renders the model card in markdown format and then saves it @@ -1410,6 +1419,9 @@ def save(self, path: str | Path) -> None: path: str, or Path Filepath to save your card. + plot_path: str + Filepath to save the plots. + Notes ----- The keys in model card metadata can be seen `here @@ -1418,6 +1430,9 @@ def save(self, path: str | Path) -> None: with open(path, "w", encoding="utf-8") as f: f.write("\n".join(self._generate_card())) + if plot_path is not None: + self._copy_plots(self._data, plot_path) + def render(self) -> str: """Render the final model card as a string. From ba77226c94f2bbbacaf68c395bde53119d4e1ddb Mon Sep 17 00:00:00 2001 From: Thomas Lazarus Date: Tue, 4 Apr 2023 20:45:06 -0500 Subject: [PATCH 04/12] Adds test --- skops/card/_model_card.py | 8 ++++---- skops/card/tests/test_card.py | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/skops/card/_model_card.py b/skops/card/_model_card.py index 610b206e..8ec6343c 100644 --- a/skops/card/_model_card.py +++ b/skops/card/_model_card.py @@ -1400,13 +1400,13 @@ def _generate_card(self) -> Iterator[str]: # add an empty line add the end yield "" - def _copy_plots(self, data, plot_path: str): + def _copy_plots(self, data, parent_path: str, plot_path: str) -> None: """Copy the plots to the specified path.""" for section in data.values(): - self._copy_plots(section.subsections, plot_path) + self._copy_plots(section.subsections, plot_path, parent_path) if hasattr(section, "path"): - shutil.copy(section.path, plot_path + "/" + section.path) + shutil.copy(parent_path / section.path, plot_path + "/" + section.path) def save(self, path: str | Path, plot_path: None) -> None: """Save the model card. @@ -1431,7 +1431,7 @@ def save(self, path: str | Path, plot_path: None) -> None: f.write("\n".join(self._generate_card())) if plot_path is not None: - self._copy_plots(self._data, plot_path) + self._copy_plots(self._data, path.parent, plot_path) def render(self) -> str: """Render the final model card as a string. diff --git a/skops/card/tests/test_card.py b/skops/card/tests/test_card.py index 7a3dfb27..f9a60a4d 100644 --- a/skops/card/tests/test_card.py +++ b/skops/card/tests/test_card.py @@ -1887,3 +1887,18 @@ def test_toc_with_invisible_section(self, card): ] assert toc == "\n".join(exptected_toc) + + +class TestCardSaveWithPlots: + def test_copy_plots(self, destination_path, model_card): + import matplotlib.pyplot as plt + + plt.plot([4, 5, 6, 7]) + plt.savefig(Path(destination_path) / "fig1.png") + model_card = model_card.add_plot(fig1="fig1.png") + + with tempfile.TemporaryDirectory(prefix="skops-test-plots") as plot_path: + model_card.save(Path(destination_path) / "README.md", plot_path=plot_path) + + assert (Path(destination_path) / "README.md").exists() + assert (Path(plot_path) / "fig1.png").exists() From b4fd7308a394ed52ba1745990bd5e6e303ffe860 Mon Sep 17 00:00:00 2001 From: Thomas Lazarus Date: Tue, 4 Apr 2023 20:54:11 -0500 Subject: [PATCH 05/12] Makes parameter optional --- skops/card/_model_card.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/skops/card/_model_card.py b/skops/card/_model_card.py index 8ec6343c..64822001 100644 --- a/skops/card/_model_card.py +++ b/skops/card/_model_card.py @@ -1400,15 +1400,15 @@ def _generate_card(self) -> Iterator[str]: # add an empty line add the end yield "" - def _copy_plots(self, data, parent_path: str, plot_path: str) -> None: + def _copy_plots(self, data, parent_path: str | Path, plot_path: str | Path) -> None: """Copy the plots to the specified path.""" for section in data.values(): self._copy_plots(section.subsections, plot_path, parent_path) if hasattr(section, "path"): - shutil.copy(parent_path / section.path, plot_path + "/" + section.path) + shutil.copy(parent_path / section.path, plot_path / section.path) - def save(self, path: str | Path, plot_path: None) -> None: + def save(self, path: str | Path, plot_path=None) -> None: """Save the model card. This method renders the model card in markdown format and then saves it @@ -1431,6 +1431,8 @@ def save(self, path: str | Path, plot_path: None) -> None: f.write("\n".join(self._generate_card())) if plot_path is not None: + if not isinstance(path, Path): + path = Path(path) self._copy_plots(self._data, path.parent, plot_path) def render(self) -> str: From 92dda94b9340d5fd8ab4dda41b30dbc3e1799edd Mon Sep 17 00:00:00 2001 From: Thomas Lazarus Date: Tue, 4 Apr 2023 20:58:13 -0500 Subject: [PATCH 06/12] Updates changelog --- docs/changes.rst | 2 ++ skops/card/tests/test_card.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/changes.rst b/docs/changes.rst index 11bb032b..624a09c4 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -12,6 +12,8 @@ skops Changelog v0.7 ---- +- Add ability to copy plots on :meth:`.Card.save` so that they can be + referenced in the model card. :pr:`330` by :user:`Thomas Lazarus `. v0.6 diff --git a/skops/card/tests/test_card.py b/skops/card/tests/test_card.py index f9a60a4d..6adc471c 100644 --- a/skops/card/tests/test_card.py +++ b/skops/card/tests/test_card.py @@ -1898,7 +1898,9 @@ def test_copy_plots(self, destination_path, model_card): model_card = model_card.add_plot(fig1="fig1.png") with tempfile.TemporaryDirectory(prefix="skops-test-plots") as plot_path: - model_card.save(Path(destination_path) / "README.md", plot_path=plot_path) + model_card.save( + Path(destination_path) / "README.md", plot_path=Path(plot_path) + ) assert (Path(destination_path) / "README.md").exists() assert (Path(plot_path) / "fig1.png").exists() From ebe246f5c1df5e46cc73cbb321a4f32b43fc00e8 Mon Sep 17 00:00:00 2001 From: Thomas Lazarus Date: Thu, 6 Apr 2023 19:21:15 -0500 Subject: [PATCH 07/12] Moves copying to `_generate_content` --- skops/card/_model_card.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/skops/card/_model_card.py b/skops/card/_model_card.py index 64822001..32c150e3 100644 --- a/skops/card/_model_card.py +++ b/skops/card/_model_card.py @@ -1301,7 +1301,7 @@ def _generate_metadata(self, metadata: ModelCardData) -> Iterator[str]: yield aRepr.repr(f"metadata.{key}={val},").strip('"').strip("'") def _generate_content( - self, data: dict[str, Section], depth: int = 1 + self, data: dict[str, Section], depth: int = 1, parent_path=None, plot_path=None ) -> Iterator[str]: """Yield title and (formatted) contents. @@ -1319,8 +1319,20 @@ def _generate_content( yield section.format() + if ( + parent_path is not None + and plot_path is not None + and isinstance(section, PlotSection) + ): + shutil.copy(parent_path.parent / section.path, plot_path / section.path) + if section.subsections: - yield from self._generate_content(section.subsections, depth=depth + 1) + yield from self._generate_content( + section.subsections, + depth=depth + 1, + parent_path=parent_path, + plot_path=plot_path, + ) def _iterate_content( self, data: dict[str, Section], parent_section: str = "" @@ -1388,26 +1400,20 @@ def __repr__(self) -> str: complete_repr += ")" return complete_repr - def _generate_card(self) -> Iterator[str]: + def _generate_card(self, parent_path=None, plot_path=None) -> Iterator[str]: """Yield sections of the model card, including the metadata.""" if self.metadata.to_dict(): yield f"---\n{self.metadata.to_yaml()}\n---" - for line in self._generate_content(self._data): + for line in self._generate_content( + self._data, parent_path=parent_path, plot_path=plot_path + ): if line: yield "\n" + line # add an empty line add the end yield "" - def _copy_plots(self, data, parent_path: str | Path, plot_path: str | Path) -> None: - """Copy the plots to the specified path.""" - for section in data.values(): - self._copy_plots(section.subsections, plot_path, parent_path) - - if hasattr(section, "path"): - shutil.copy(parent_path / section.path, plot_path / section.path) - def save(self, path: str | Path, plot_path=None) -> None: """Save the model card. @@ -1428,12 +1434,7 @@ def save(self, path: str | Path, plot_path=None) -> None: `__. """ with open(path, "w", encoding="utf-8") as f: - f.write("\n".join(self._generate_card())) - - if plot_path is not None: - if not isinstance(path, Path): - path = Path(path) - self._copy_plots(self._data, path.parent, plot_path) + f.write("\n".join(self._generate_card(path, plot_path))) def render(self) -> str: """Render the final model card as a string. From 10c92acb961e1bc1165ee09716b6ba00f98dae73 Mon Sep 17 00:00:00 2001 From: Thomas Lazarus Date: Sat, 15 Apr 2023 20:18:59 -0500 Subject: [PATCH 08/12] Copies plots to the same dir as README --- skops/card/_model_card.py | 28 ++++++++++++++++++---------- skops/card/tests/test_card.py | 14 ++++++++------ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/skops/card/_model_card.py b/skops/card/_model_card.py index 32c150e3..736e42a8 100644 --- a/skops/card/_model_card.py +++ b/skops/card/_model_card.py @@ -1301,7 +1301,11 @@ def _generate_metadata(self, metadata: ModelCardData) -> Iterator[str]: yield aRepr.repr(f"metadata.{key}={val},").strip('"').strip("'") def _generate_content( - self, data: dict[str, Section], depth: int = 1, parent_path=None, plot_path=None + self, + data: dict[str, Section], + depth: int = 1, + path: str | Path = "", + copy_files: bool = False, ) -> Iterator[str]: """Yield title and (formatted) contents. @@ -1320,18 +1324,18 @@ def _generate_content( yield section.format() if ( - parent_path is not None - and plot_path is not None + path is not None + and copy_files is not False and isinstance(section, PlotSection) ): - shutil.copy(parent_path.parent / section.path, plot_path / section.path) + shutil.copy(Path(path) / section.path, section.path) if section.subsections: yield from self._generate_content( section.subsections, depth=depth + 1, - parent_path=parent_path, - plot_path=plot_path, + path=path, + copy_files=copy_files, ) def _iterate_content( @@ -1400,13 +1404,15 @@ def __repr__(self) -> str: complete_repr += ")" return complete_repr - def _generate_card(self, parent_path=None, plot_path=None) -> Iterator[str]: + def _generate_card( + self, path: str | Path = "", copy_files: bool = False + ) -> Iterator[str]: """Yield sections of the model card, including the metadata.""" if self.metadata.to_dict(): yield f"---\n{self.metadata.to_yaml()}\n---" for line in self._generate_content( - self._data, parent_path=parent_path, plot_path=plot_path + self._data, path=path, copy_files=copy_files ): if line: yield "\n" + line @@ -1414,7 +1420,7 @@ def _generate_card(self, parent_path=None, plot_path=None) -> Iterator[str]: # add an empty line add the end yield "" - def save(self, path: str | Path, plot_path=None) -> None: + def save(self, path: str | Path, copy_files: bool = False) -> None: """Save the model card. This method renders the model card in markdown format and then saves it @@ -1434,7 +1440,9 @@ def save(self, path: str | Path, plot_path=None) -> None: `__. """ with open(path, "w", encoding="utf-8") as f: - f.write("\n".join(self._generate_card(path, plot_path))) + if not isinstance(path, Path): + path = Path(path) + f.write("\n".join(self._generate_card(path.parent, copy_files))) def render(self) -> str: """Render the final model card as a string. diff --git a/skops/card/tests/test_card.py b/skops/card/tests/test_card.py index 6adc471c..55ebfb89 100644 --- a/skops/card/tests/test_card.py +++ b/skops/card/tests/test_card.py @@ -1897,10 +1897,12 @@ def test_copy_plots(self, destination_path, model_card): plt.savefig(Path(destination_path) / "fig1.png") model_card = model_card.add_plot(fig1="fig1.png") - with tempfile.TemporaryDirectory(prefix="skops-test-plots") as plot_path: - model_card.save( - Path(destination_path) / "README.md", plot_path=Path(plot_path) - ) + plt.plot([7, 6, 5, 4]) + plt.savefig(Path(destination_path) / "fig2.png") + model_card = model_card.add_plot(fig2="fig2.png") + + model_card.save(Path(destination_path) / "README.md") - assert (Path(destination_path) / "README.md").exists() - assert (Path(plot_path) / "fig1.png").exists() + assert (Path(destination_path) / "README.md").exists() + assert (Path(destination_path) / "fig1.png").exists() + assert (Path(destination_path) / "fig2.png").exists() From d0778de7fb5ccd3748f424ed343af12b9264fb94 Mon Sep 17 00:00:00 2001 From: Thomas Lazarus Date: Thu, 27 Apr 2023 16:27:14 -0500 Subject: [PATCH 09/12] Updated the test based off of feedback Still need to get the copying working with absolute paths. --- skops/card/_model_card.py | 12 ++++++------ skops/card/tests/test_card.py | 21 ++++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/skops/card/_model_card.py b/skops/card/_model_card.py index 736e42a8..d842b535 100644 --- a/skops/card/_model_card.py +++ b/skops/card/_model_card.py @@ -1304,7 +1304,7 @@ def _generate_content( self, data: dict[str, Section], depth: int = 1, - path: str | Path = "", + destination_path: str | Path = "", copy_files: bool = False, ) -> Iterator[str]: """Yield title and (formatted) contents. @@ -1324,17 +1324,17 @@ def _generate_content( yield section.format() if ( - path is not None + destination_path is not None and copy_files is not False and isinstance(section, PlotSection) ): - shutil.copy(Path(path) / section.path, section.path) + shutil.copy(section.path, Path(destination_path)) if section.subsections: yield from self._generate_content( section.subsections, depth=depth + 1, - path=path, + destination_path=destination_path, copy_files=copy_files, ) @@ -1405,14 +1405,14 @@ def __repr__(self) -> str: return complete_repr def _generate_card( - self, path: str | Path = "", copy_files: bool = False + self, destination_path: str | Path = "", copy_files: bool = False ) -> Iterator[str]: """Yield sections of the model card, including the metadata.""" if self.metadata.to_dict(): yield f"---\n{self.metadata.to_yaml()}\n---" for line in self._generate_content( - self._data, path=path, copy_files=copy_files + self._data, destination_path=destination_path, copy_files=copy_files ): if line: yield "\n" + line diff --git a/skops/card/tests/test_card.py b/skops/card/tests/test_card.py index 55ebfb89..ad7dd3c2 100644 --- a/skops/card/tests/test_card.py +++ b/skops/card/tests/test_card.py @@ -1893,15 +1893,18 @@ class TestCardSaveWithPlots: def test_copy_plots(self, destination_path, model_card): import matplotlib.pyplot as plt - plt.plot([4, 5, 6, 7]) - plt.savefig(Path(destination_path) / "fig1.png") - model_card = model_card.add_plot(fig1="fig1.png") - - plt.plot([7, 6, 5, 4]) - plt.savefig(Path(destination_path) / "fig2.png") - model_card = model_card.add_plot(fig2="fig2.png") - - model_card.save(Path(destination_path) / "README.md") + with tempfile.TemporaryDirectory(prefix="skops-test-plots") as plot_path: + plt.plot([4, 5, 6, 7]) + fig_1_path = Path(plot_path) / "fig1.png" + plt.savefig(fig_1_path) + model_card = model_card.add_plot(fig1=fig_1_path) + + plt.plot([7, 6, 5, 4]) + fig_2_path = "fig2.png" + plt.savefig(fig_2_path) + model_card = model_card.add_plot(fig2=fig_2_path) + + model_card.save(Path(destination_path) / "README.md", copy_files=True) assert (Path(destination_path) / "README.md").exists() assert (Path(destination_path) / "fig1.png").exists() From f7f82d0621d0f6b9581900f2c2f88a81bda4a1f3 Mon Sep 17 00:00:00 2001 From: Thomas Lazarus Date: Tue, 2 May 2023 19:05:28 -0500 Subject: [PATCH 10/12] Fixes test for absolute path --- skops/card/tests/test_card.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skops/card/tests/test_card.py b/skops/card/tests/test_card.py index ad7dd3c2..0ddce241 100644 --- a/skops/card/tests/test_card.py +++ b/skops/card/tests/test_card.py @@ -1904,7 +1904,7 @@ def test_copy_plots(self, destination_path, model_card): plt.savefig(fig_2_path) model_card = model_card.add_plot(fig2=fig_2_path) - model_card.save(Path(destination_path) / "README.md", copy_files=True) + model_card.save(Path(destination_path) / "README.md", copy_files=True) assert (Path(destination_path) / "README.md").exists() assert (Path(destination_path) / "fig1.png").exists() From f7c2cf0e4b3f45ee3574612404ab7eb860153fc9 Mon Sep 17 00:00:00 2001 From: Thomas Lazarus Date: Thu, 4 May 2023 19:41:52 -0500 Subject: [PATCH 11/12] Removes needing to pass `copy_files` down between methods. --- skops/card/_model_card.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/skops/card/_model_card.py b/skops/card/_model_card.py index d842b535..d3687f58 100644 --- a/skops/card/_model_card.py +++ b/skops/card/_model_card.py @@ -1304,8 +1304,7 @@ def _generate_content( self, data: dict[str, Section], depth: int = 1, - destination_path: str | Path = "", - copy_files: bool = False, + destination_path: str | Path | None = None, ) -> Iterator[str]: """Yield title and (formatted) contents. @@ -1323,11 +1322,7 @@ def _generate_content( yield section.format() - if ( - destination_path is not None - and copy_files is not False - and isinstance(section, PlotSection) - ): + if destination_path is not None and isinstance(section, PlotSection): shutil.copy(section.path, Path(destination_path)) if section.subsections: @@ -1335,7 +1330,6 @@ def _generate_content( section.subsections, depth=depth + 1, destination_path=destination_path, - copy_files=copy_files, ) def _iterate_content( @@ -1405,14 +1399,14 @@ def __repr__(self) -> str: return complete_repr def _generate_card( - self, destination_path: str | Path = "", copy_files: bool = False + self, destination_path: str | Path | None = None ) -> Iterator[str]: """Yield sections of the model card, including the metadata.""" if self.metadata.to_dict(): yield f"---\n{self.metadata.to_yaml()}\n---" for line in self._generate_content( - self._data, destination_path=destination_path, copy_files=copy_files + self._data, destination_path=destination_path ): if line: yield "\n" + line @@ -1442,7 +1436,7 @@ def save(self, path: str | Path, copy_files: bool = False) -> None: with open(path, "w", encoding="utf-8") as f: if not isinstance(path, Path): path = Path(path) - f.write("\n".join(self._generate_card(path.parent, copy_files))) + f.write("\n".join(self._generate_card(path.parent if copy_files else None))) def render(self) -> str: """Render the final model card as a string. From 1872155a987b345115c9104edca40700029de21a Mon Sep 17 00:00:00 2001 From: Thomas Lazarus Date: Tue, 16 May 2023 19:20:51 -0500 Subject: [PATCH 12/12] Updates doc string and cleans up parameter types --- skops/card/_model_card.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/skops/card/_model_card.py b/skops/card/_model_card.py index cde5feec..32ba9b8c 100644 --- a/skops/card/_model_card.py +++ b/skops/card/_model_card.py @@ -1322,7 +1322,7 @@ def _generate_content( self, data: dict[str, Section], depth: int = 1, - destination_path: str | Path | None = None, + destination_path: Path | None = None, ) -> Iterator[str]: """Yield title and (formatted) contents. @@ -1341,7 +1341,7 @@ def _generate_content( yield section.format() if destination_path is not None and isinstance(section, PlotSection): - shutil.copy(section.path, Path(destination_path)) + shutil.copy(section.path, destination_path) if section.subsections: yield from self._generate_content( @@ -1416,9 +1416,7 @@ def __repr__(self) -> str: complete_repr += ")" return complete_repr - def _generate_card( - self, destination_path: str | Path | None = None - ) -> Iterator[str]: + def _generate_card(self, destination_path: Path | None = None) -> Iterator[str]: """Yield sections of the model card, including the metadata.""" if self.metadata.to_dict(): yield f"---\n{self.metadata.to_yaml()}\n---" @@ -1440,11 +1438,13 @@ def save(self, path: str | Path, copy_files: bool = False) -> None: Parameters ---------- - path: str, or Path + path: Path Filepath to save your card. plot_path: str - Filepath to save the plots. + Filepath to save the plots. Use this when saving the model card before creating the + repository. Without this path the README will have an absolute path to the plot that + won't exist in the repository. Notes ----- @@ -1454,7 +1454,8 @@ def save(self, path: str | Path, copy_files: bool = False) -> None: with open(path, "w", encoding="utf-8") as f: if not isinstance(path, Path): path = Path(path) - f.write("\n".join(self._generate_card(path.parent if copy_files else None))) + destination_path = path.parent if copy_files else None + f.write("\n".join(self._generate_card(destination_path=destination_path))) def render(self) -> str: """Render the final model card as a string.