# Buggy code
def checkpoint(self):
    """Checkpoint the dfk incrementally to a checkpoint file.

    When called, every task that has been completed yet not
    checkpointed is checkpointed to a file.

    .. note::
        Checkpointing only works if memoization is enabled

    Returns:
        Checkpoint dir if checkpoints were written successfully.
        By default the checkpoints are written to the RUNDIR of the current
        run under RUNDIR/checkpoints/{tasks.pkl, dfk.pkl}
    """
    logger.info("Checkpointing.. ")

    checkpoint_dir = "{0}/checkpoint".format(self.rundir)
    checkpoint_dfk = checkpoint_dir + "/dfk.pkl"
    checkpoint_tasks = checkpoint_dir + "/tasks.pkl"

    if not os.path.exists(checkpoint_dir):
        try:
            os.makedirs(checkpoint_dir)
        except FileExistsError as e:
            pass

    with open(checkpoint_dfk, "wb") as f:
        state = {
            "config": self.config,
            "rundir": self.rundir,
            "task_count": self.task_count,
        }
        pickle.dump(state, f)

    count = 0
    with open(checkpoint_tasks, "ab") as f:
        for task_id in self.tasks:
            if (
                self.tasks[task_id]["app_fu"].done()
                and not self.tasks[task_id]["checkpoint"]
            ):
                hashsum = self.tasks[task_id]["hashsum"]
                if not hashsum:
                    continue
                t = {"hash": hashsum, "exception": None, "result": None}
                try:
                    # Asking for the result will raise an exception if
                    # the app had failed. Should we even checkpoint these?
                    # TODO : Resolve this question ?
                    r = self.memoizer.hash_lookup(hashsum).result()
                except Exception as e:
                    t["exception"] = e
                else:
                    t["result"] = r

                # We are using pickle here since pickle dumps to a file in 'ab'
                # mode behave like a incremental log.
                pickle.dump(t, f)
                count += 1
                self.tasks[task_id]["checkpoint"] = True
                logger.debug("Task {} checkpointed".format(task_id))

    self.checkpointed_tasks += count

    if count == 0:
        if self.checkpointed_tasks == 0:
            logger.warn("No tasks checkpointed, please ensure caching is enabled")
        else:
            logger.debug("No tasks checkpointed")
    else:
        logger.info("Done checkpointing {} tasks".format(count))

    return checkpoint_dir


# Traceback (AttributeError)
Traceback (most recent call last):
File "test_regression_233.py", line 38, in <module>
test_checkpoint_availability()
File "test_regression_233.py", line 27, in test_checkpoint_availability
original = run_checkpointed([])
File "test_regression_233.py", line 18, in run_checkpointed
x = cached_rand(i)
File "/home/yadu/src/parsl/parsl/app/app_factory.py", line 76, in __call__
return app_obj(*args, **kwargs)
File "/home/yadu/src/parsl/parsl/app/python_app.py", line 46, in __call__
**kwargs)
File "/home/yadu/src/parsl/parsl/dataflow/dflow.py", line 499, in submit
task_id, func, *new_args, **kwargs)
File "/home/yadu/src/parsl/parsl/dataflow/dflow.py", line 322, in launch_task
exec_fu.add_done_callback(partial(self.handle_update, task_id))
File "/usr/lib/python3.5/concurrent/futures/_base.py", line 376, in add_done_callback
fn(self)
File "/home/yadu/src/parsl/parsl/dataflow/dflow.py", line 218, in handle_update
self.checkpoint(tasks=[task_id])
File "/home/yadu/src/parsl/parsl/dataflow/dflow.py", line 617, in checkpoint
if self.tasks[task_id]['app_fu'].done() and \
AttributeError: 'NoneType' object has no attribute 'done'

# Fixed code
def checkpoint(self, tasks=None):
    """Checkpoint the dfk incrementally to a checkpoint file.

    When called, every task that has been completed yet not
    checkpointed is checkpointed to a file.

    Kwargs:
        - tasks (List of task ids) : List of task ids to checkpoint. Default=None
                                     if set to None, we iterate over all tasks held by the DFK.

    .. note::
        Checkpointing only works if memoization is enabled

    Returns:
        Checkpoint dir if checkpoints were written successfully.
        By default the checkpoints are written to the RUNDIR of the current
        run under RUNDIR/checkpoints/{tasks.pkl, dfk.pkl}
    """

    checkpoint_queue = None
    if tasks:
        checkpoint_queue = tasks
    else:
        checkpoint_queue = self.tasks

    checkpoint_dir = "{0}/checkpoint".format(self.rundir)
    checkpoint_dfk = checkpoint_dir + "/dfk.pkl"
    checkpoint_tasks = checkpoint_dir + "/tasks.pkl"

    if not os.path.exists(checkpoint_dir):
        try:
            os.makedirs(checkpoint_dir)
        except FileExistsError as e:
            pass

    with open(checkpoint_dfk, "wb") as f:
        state = {
            "config": self.config,
            "rundir": self.rundir,
            "task_count": self.task_count,
        }
        pickle.dump(state, f)

    count = 0

    with open(checkpoint_tasks, "ab") as f:
        for task_id in checkpoint_queue:
            if (
                not self.tasks[task_id]["checkpoint"]
                and self.tasks[task_id]["status"] == States.done
            ):
                hashsum = self.tasks[task_id]["hashsum"]
                if not hashsum:
                    continue
                t = {"hash": hashsum, "exception": None, "result": None}
                try:
                    # Asking for the result will raise an exception if
                    # the app had failed. Should we even checkpoint these?
                    # TODO : Resolve this question ?
                    r = self.memoizer.hash_lookup(hashsum).result()
                except Exception as e:
                    t["exception"] = e
                else:
                    t["result"] = r

                # We are using pickle here since pickle dumps to a file in 'ab'
                # mode behave like a incremental log.
                pickle.dump(t, f)
                count += 1
                self.tasks[task_id]["checkpoint"] = True
                logger.debug("Task {} checkpointed".format(task_id))

    self.checkpointed_tasks += count

    if count == 0:
        if self.checkpointed_tasks == 0:
            logger.warn("No tasks checkpointed, please ensure caching is enabled")
        else:
            logger.debug("No tasks checkpointed")
    else:
        logger.info("Done checkpointing {} tasks".format(count))

    return checkpoint_dir