# Buggy code
def submit(self, func, *args, parsl_sites="all", fn_hash=None, cache=False, **kwargs):
    """Add task to the dataflow system.

    >>> IF all deps are met :
    >>>   send to the runnable queue and launch the task
    >>> ELSE:
    >>>   post the task in the pending queue

    Args:
        - func : A function object
        - *args : Args to the function

    KWargs :
        - parsl_sites (List|String) : List of sites this call could go to.
                Default='all'
        - fn_hash (Str) : Hash of the function and inputs
                Default=None
        - cache (Bool) : To enable memoization or not
        - kwargs (dict) : Rest of the kwargs to the fn passed as dict.

    Returns:
           (AppFuture) [DataFutures,]

    """
    task_id = self.task_count
    self.task_count += 1

    # Get the dep count and a list of dependencies for the task
    dep_cnt, depends = self._count_all_deps(task_id, args, kwargs)

    task_def = {
        "depends": depends,
        "sites": parsl_sites,
        "func": func,
        "func_name": func.__name__,
        "args": args,
        "kwargs": kwargs,
        "fn_hash": fn_hash,
        "memoize": cache,
        "callback": None,
        "dep_cnt": dep_cnt,
        "exec_fu": None,
        "checkpoint": None,
        "fail_count": 0,
        "fail_history": [],
        "env": None,
        "status": States.unsched,
        "app_fu": None,
    }

    if task_id in self.tasks:
        raise DuplicateTaskError("Task {0} in pending list".format(task_id))
    else:
        self.tasks[task_id] = task_def

    # Extract stdout and stderr to pass to AppFuture:
    task_stdout = kwargs.get("stdout", None)
    task_stderr = kwargs.get("stderr", None)

    logger.info(
        "Task {} submitted for App {}, waiting on tasks {}".format(
            task_id, task_def["func_name"], [fu.tid for fu in depends]
        )
    )

    # Handle three cases here:
    # No pending deps
    #     - But has failures -> dep_fail
    #     - No failures -> running
    # Has pending deps -> pending
    if dep_cnt == 0:
        new_args, kwargs, exceptions = self.sanitize_and_wrap(task_id, args, kwargs)
        self.tasks[task_id]["args"] = new_args
        self.tasks[task_id]["kwargs"] = kwargs

        if not exceptions:
            self.tasks[task_id]["exec_fu"] = self.launch_task(
                task_id, func, *new_args, **kwargs
            )
            self.tasks[task_id]["app_fu"] = AppFuture(
                self.tasks[task_id]["exec_fu"],
                tid=task_id,
                stdout=task_stdout,
                stderr=task_stderr,
            )
            self.tasks[task_id]["status"] = States.running
            logger.debug(
                "Task {} launched with AppFut:{}".format(task_id, task_def["app_fu"])
            )

        else:
            self.tasks[task_id]["exec_fu"] = None
            app_fu = AppFuture(
                self.tasks[task_id]["exec_fu"],
                tid=task_id,
                stdout=task_stdout,
                stderr=task_stderr,
            )
            app_fu.set_exception(
                DependencyError(exceptions, "Failures in input dependencies", None)
            )
            self.tasks[task_id]["app_fu"] = app_fu
            self.tasks[task_id]["status"] = States.dep_fail
            logger.debug(
                "Task {} failed due to failure in parent task(s):{}".format(
                    task_id, task_def["app_fu"]
                )
            )

    else:
        # Send to pending, create the AppFuture with no parent and have it set
        # when an executor future is available.
        self.tasks[task_id]["app_fu"] = AppFuture(
            None, tid=task_id, stdout=task_stdout, stderr=task_stderr
        )
        self.tasks[task_id]["status"] = States.pending
        logger.debug(
            "Task {} launched with AppFut:{}".format(task_id, task_def["app_fu"])
        )

    return task_def["app_fu"]


# 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 submit(self, func, *args, parsl_sites="all", fn_hash=None, cache=False, **kwargs):
    """Add task to the dataflow system.

    >>> IF all deps are met :
    >>>   send to the runnable queue and launch the task
    >>> ELSE:
    >>>   post the task in the pending queue

    Args:
        - func : A function object
        - *args : Args to the function

    KWargs :
        - parsl_sites (List|String) : List of sites this call could go to.
                Default='all'
        - fn_hash (Str) : Hash of the function and inputs
                Default=None
        - cache (Bool) : To enable memoization or not
        - kwargs (dict) : Rest of the kwargs to the fn passed as dict.

    Returns:
           (AppFuture) [DataFutures,]

    """
    task_id = self.task_count
    self.task_count += 1

    # Get the dep count and a list of dependencies for the task
    dep_cnt, depends = self._count_all_deps(task_id, args, kwargs)

    task_def = {
        "depends": depends,
        "sites": parsl_sites,
        "func": func,
        "func_name": func.__name__,
        "args": args,
        "kwargs": kwargs,
        "fn_hash": fn_hash,
        "memoize": cache,
        "callback": None,
        "dep_cnt": dep_cnt,
        "exec_fu": None,
        "checkpoint": None,
        "fail_count": 0,
        "fail_history": [],
        "env": None,
        "status": States.unsched,
        "app_fu": None,
    }

    if task_id in self.tasks:
        raise DuplicateTaskError("Task {0} in pending list".format(task_id))
    else:
        self.tasks[task_id] = task_def

    # Extract stdout and stderr to pass to AppFuture:
    task_stdout = kwargs.get("stdout", None)
    task_stderr = kwargs.get("stderr", None)

    logger.info(
        "Task {} submitted for App {}, waiting on tasks {}".format(
            task_id, task_def["func_name"], [fu.tid for fu in depends]
        )
    )

    # Handle three cases here:
    # No pending deps
    #     - But has failures -> dep_fail
    #     - No failures -> running
    # Has pending deps -> pending
    if dep_cnt == 0:
        new_args, kwargs, exceptions = self.sanitize_and_wrap(task_id, args, kwargs)
        self.tasks[task_id]["args"] = new_args
        self.tasks[task_id]["kwargs"] = kwargs

        if not exceptions:
            self.tasks[task_id]["exec_fu"] = self.launch_task(
                task_id, func, *new_args, **kwargs
            )
            self.tasks[task_id]["app_fu"] = AppFuture(
                self.tasks[task_id]["exec_fu"],
                tid=task_id,
                stdout=task_stdout,
                stderr=task_stderr,
            )
            logger.debug(
                "Task {} launched with AppFut:{}".format(task_id, task_def["app_fu"])
            )

        else:
            self.tasks[task_id]["exec_fu"] = None
            app_fu = AppFuture(
                self.tasks[task_id]["exec_fu"],
                tid=task_id,
                stdout=task_stdout,
                stderr=task_stderr,
            )
            app_fu.set_exception(
                DependencyError(exceptions, "Failures in input dependencies", None)
            )
            self.tasks[task_id]["app_fu"] = app_fu
            self.tasks[task_id]["status"] = States.dep_fail
            logger.debug(
                "Task {} failed due to failure in parent task(s):{}".format(
                    task_id, task_def["app_fu"]
                )
            )

    else:
        # Send to pending, create the AppFuture with no parent and have it set
        # when an executor future is available.
        self.tasks[task_id]["app_fu"] = AppFuture(
            None, tid=task_id, stdout=task_stdout, stderr=task_stderr
        )
        self.tasks[task_id]["status"] = States.pending
        logger.debug(
            "Task {} launched with AppFut:{}".format(task_id, task_def["app_fu"])
        )

    return task_def["app_fu"]