diff --git a/pyiron_base/jobs/flex/pythonfunctioncontainer.py b/pyiron_base/jobs/flex/pythonfunctioncontainer.py index 43cb5c33b..01ad1c6aa 100644 --- a/pyiron_base/jobs/flex/pythonfunctioncontainer.py +++ b/pyiron_base/jobs/flex/pythonfunctioncontainer.py @@ -44,6 +44,7 @@ def __init__(self, project, job_name): super().__init__(project, job_name) self._function = None self._executor_type = None + self._automatically_rename_on_save_using_input = True @property def python_function(self): @@ -64,23 +65,32 @@ def __call__(self, *args, **kwargs): def to_hdf(self, hdf=None, group_name=None): super().to_hdf(hdf=hdf, group_name=group_name) self.project_hdf5["function"] = np.void(cloudpickle.dumps(self._function)) + self.project_hdf5["_automatically_rename_on_save_using_input"] = ( + self._automatically_rename_on_save_using_input + ) def from_hdf(self, hdf=None, group_name=None): super().from_hdf(hdf=hdf, group_name=group_name) self._function = cloudpickle.loads(self.project_hdf5["function"]) + self._automatically_rename_on_save_using_input = bool( + self.project_hdf5["_automatically_rename_on_save_using_input"] + ) def save(self): - job_name = self._function.__name__ + get_hash( - binary=cloudpickle.dumps( - {"fn": self._function, "kwargs": self.input.to_builtin()} + if self._automatically_rename_on_save_using_input: + job_name = self._function.__name__ + get_hash( + binary=cloudpickle.dumps( + {"fn": self._function, "kwargs": self.input.to_builtin()} + ) ) - ) - self.job_name = job_name - if job_name in self.project.list_nodes(): - self.from_hdf() - self.status.finished = True - else: - super().save() + + self.job_name = job_name + + if self.job_name in self.project.list_nodes(): + self.from_hdf() + self.status.finished = True + return # Without saving + super().save() def run_static(self): if ( diff --git a/tests/flex/test_pythonfunctioncontainer.py b/tests/flex/test_pythonfunctioncontainer.py index b08662c72..595498a8c 100644 --- a/tests/flex/test_pythonfunctioncontainer.py +++ b/tests/flex/test_pythonfunctioncontainer.py @@ -55,7 +55,9 @@ def test_with_executor(self): self.assertIsNone(job.server.future.result()) self.assertTrue(job.server.future.done()) - @unittest.skipIf(os.name == "nt", "Starting subprocesses on windows take a long time.") + @unittest.skipIf( + os.name == "nt", "Starting subprocesses on windows take a long time." + ) def test_terminate_job(self): job = self.project.wrap_python_function(my_sleep_funct) job.input["a"] = 5 @@ -100,3 +102,49 @@ def test_with_internal_executor(self): job.run() self.assertEqual(job.output["result"], [6, 8, 10, 12]) self.assertTrue(job.status.finished) + + def test_name_mangling(self): + def make_a_simple_job(): + job = self.project.wrap_python_function(my_function) + job.input["a"] = 1 + job.input["b"] = 2 + return job + + job = make_a_simple_job() + self.assertEqual( + job.job_name, + my_function.__name__, + msg="Sanity check" + ) + try: + job.save() + self.assertNotEqual( + job.job_name, + my_function.__name__, + msg="By default, we expect the wrapped job names to get mangled based " + "on their input so multiple calls to the wrap get unique names" + ) + loaded = self.project.load(job.job_name) + self.assertTrue( + loaded._automatically_rename_on_save_using_input, + msg="The mangling preference should survive saving and loading" + ) + finally: + job.remove() + + job = make_a_simple_job() + job._automatically_rename_on_save_using_input = False + try: + job.save() + self.assertEqual( + job.job_name, + my_function.__name__, + msg="When requested, the job name should retain its original value" + ) + loaded = self.project.load(job.job_name) + self.assertFalse( + loaded._automatically_rename_on_save_using_input, + msg="The mangling preference should survive saving and loading" + ) + finally: + job.remove()