diff --git a/pyiron_workflow/node.py b/pyiron_workflow/node.py index a5517c24..ad741ee4 100644 --- a/pyiron_workflow/node.py +++ b/pyiron_workflow/node.py @@ -834,7 +834,11 @@ def save_checkpoint(self, backend: str | StorageInterface = "pickle"): self.graph_root.save(backend=backend) def load( - self, backend: str | StorageInterface = "pickle", only_requested=False, **kwargs + self, + backend: str | StorageInterface = "pickle", + only_requested=False, + filename: str | Path | None = None, + **kwargs, ): """ @@ -846,6 +850,9 @@ def load( only_requested (bool): Whether to _only_ try loading from the specified backend, or to loop through all available backends. (Default is False, try to load whatever you can find.) + filename (str | Path | None): The name of the file (without extensions) at + which to save the node. (Default is None, which uses the node's + semantic path.) **kwargs: back end-specific arguments (only likely to work in combination with :param:`only_requested`, otherwise there's nothing to be specific _to_.) @@ -857,7 +864,9 @@ def load( for backend in available_backends( backend=backend, only_requested=only_requested ): - inst = backend.load(node=self, **kwargs) + inst = backend.load( + node=self if filename is None else None, filename=filename, **kwargs + ) if inst is not None: break if inst is None: @@ -877,6 +886,7 @@ def delete_storage( self, backend: Literal["pickle"] | StorageInterface | None = None, only_requested: bool = False, + filename: str | Path | None = None, **kwargs, ): """ @@ -888,6 +898,9 @@ def delete_storage( only_requested (bool): Whether to _only_ try loading from the specified backend, or to loop through all available backends. (Default is False, try to load whatever you can find.) + filename (str | Path | None): The name of the file (without extensions) at + which to save the node. (Default is None, which uses the node's + semantic path.) **kwargs: back end-specific arguments (only likely to work in combination with :param:`only_requested`, otherwise there's nothing to be specific _to_.) @@ -895,12 +908,15 @@ def delete_storage( for backend in available_backends( backend=backend, only_requested=only_requested ): - backend.delete(node=self, **kwargs) + backend.delete( + node=self if filename is None else None, filename=filename, **kwargs + ) def has_saved_content( self, backend: Literal["pickle"] | StorageInterface | None = None, only_requested: bool = False, + filename: str | Path | None = None, **kwargs, ): """ @@ -912,6 +928,9 @@ def has_saved_content( only_requested (bool): Whether to _only_ try loading from the specified backend, or to loop through all available backends. (Default is False, try to load whatever you can find.) + filename (str | Path | None): The name of the file (without extensions) at + which to save the node. (Default is None, which uses the node's + semantic path.) **kwargs: back end-specific arguments (only likely to work in combination with :param:`only_requested`, otherwise there's nothing to be specific _to_.) @@ -920,7 +939,9 @@ def has_saved_content( bool: Whether any save files were found """ return any( - be.has_saved_content(self, **kwargs) + be.has_saved_content( + node=self if filename is None else None, filename=filename, **kwargs + ) for be in available_backends(backend=backend, only_requested=only_requested) ) diff --git a/tests/unit/test_node.py b/tests/unit/test_node.py index 4fdebc98..a6fa135f 100644 --- a/tests/unit/test_node.py +++ b/tests/unit/test_node.py @@ -1,4 +1,5 @@ from concurrent.futures import Future, ProcessPoolExecutor +import pathlib import unittest from pyiron_workflow.channels import InputData, NOT_DATA @@ -452,6 +453,33 @@ def test_storage(self): self.n1.delete_storage(backend) hard_input.delete_storage(backend) + def test_storage_to_filename(self): + y = self.n1() + fname = "foo" + + for backend in available_backends(): + with self.subTest(backend): + try: + self.n1.save(backend=backend, filename=fname) + self.assertFalse( + self.n1.has_saved_content(backend=backend), + msg="There should be no content at the default location" + ) + self.assertTrue( + self.n1.has_saved_content(backend=backend, filename=fname), + msg="There should be content at the specified file location" + ) + new = ANode() + new.load(filename=fname) + self.assertEqual(new.label, self.n1.label) + self.assertEqual(new.outputs.y.value, y) + finally: + self.n1.delete_storage(backend=backend, filename=fname) + self.assertFalse( + self.n1.has_saved_content(backend=backend, filename=fname), + msg="Deleting storage should have cleaned up the file" + ) + def test_checkpoint(self): for backend in available_backends(): with self.subTest(backend):