Repository: glotzerlab/signac
Pull request #125 (2019-02-16 01:25:48)

Resolve issue #120 by allowing job methods to be chained.
See issue #120 for description.

I also looked at the possibility of chaining calls to `Project`, but there were no candidate methods that I thought would be good to chain.

bdice: @csadorf This should be ready for review.

Patch:
diff --git a/changelog.txt b/changelog.txt
index 7c598d41..87f0e3d7 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -29,6 +29,7 @@ Added
  - Add automatic cast of numpy arrays to lists when storing them within a `JSONDict`, e.g., a `job.statepoint` or `job.document`.
  - Enable `Collection` class to manage collections stored in gzip files.
  - Enable deleting of `JSONDict` keys through the attribute interface, e.g., `del job.doc.foo`.
+ - The `Job.init()` method returns the job to allow one-line job creation and initialization.
 
 Fixed
 +++++
diff --git a/signac/contrib/job.py b/signac/contrib/job.py
index beb986fa..8c71c3ec 100644
--- a/signac/contrib/job.py
+++ b/signac/contrib/job.py
@@ -123,7 +123,7 @@ class Job(object):
 
         .. danger::
 
-            Use this function with caution! Resetting a job's state point,
+            Use this function with caution! Resetting a job's state point
             may sometimes be necessary, but can possibly lead to incoherent
             data spaces.
 
@@ -344,11 +344,17 @@ class Job(object):
         This function will do nothing if the directory and
         the job manifest already exist.
 
+        Returns the calling job.
+
         :param force:
-                Overwrite any existing state point's manifest
-                files, e.g., to repair them when they got corrupted.
+            Overwrite any existing state point's manifest
+            files, e.g., to repair them when they got corrupted.
         :type force:
-                bool
+            bool
+        :return:
+            The job handle.
+        :rtype:
+            :class:`~.Job`
         """
         try:
             self._init(force=force)
@@ -356,6 +362,7 @@ class Job(object):
             logger.error(
                 "State point manifest file of job '{}' appears to be corrupted.".format(self._id))
             raise
+        return self
 
     def clear(self):
         """Remove all job data, but not the job itself.


Test patch:
diff --git a/tests/test_job.py b/tests/test_job.py
index 0f2fc2ff..dc5f80b6 100644
--- a/tests/test_job.py
+++ b/tests/test_job.py
@@ -377,6 +377,15 @@ class JobOpenAndClosingTest(BaseJobTest):
         self.assertTrue(os.path.isdir(job.ws))
         self.assertTrue(os.path.exists(os.path.join(job.workspace(), job.FN_MANIFEST)))
 
+    def test_chained_init(self):
+        job = self.open_job(test_token)
+        self.assertFalse(os.path.isdir(job.workspace()))
+        job = self.open_job(test_token).init()
+        self.assertEqual(job.workspace(), job.ws)
+        self.assertTrue(os.path.isdir(job.workspace()))
+        self.assertTrue(os.path.isdir(job.ws))
+        self.assertTrue(os.path.exists(os.path.join(job.workspace(), job.FN_MANIFEST)))
+
     def test_construction(self):
         job = self.open_job(test_token)
         job2 = eval(repr(job))