# Buggy code
def __repr__(self):
    parent = self.parent.parent
    if parent:
        with parent._condition:
            if parent._state == FINISHED:
                if parent._exception:
                    return "<%s at %#x state=%s raised %s>" % (
                        self.__class__.__name__,
                        id(self),
                        _STATE_TO_DESCRIPTION_MAP[parent._state],
                        parent._exception.__class__.__name__,
                    )
                else:
                    return "<%s at %#x state=%s returned %s>" % (
                        self.__class__.__name__,
                        id(self),
                        _STATE_TO_DESCRIPTION_MAP[parent._state],
                        self.filepath + "_file",
                    )
            return "<%s at %#x state=%s>" % (
                self.__class__.__name__,
                id(self),
                _STATE_TO_DESCRIPTION_MAP[parent._state],
            )

    else:
        return "<%s at %#x state=%s>" % (
            self.__class__.__name__,
            id(self),
            _STATE_TO_DESCRIPTION_MAP[self._state],
        )


# Traceback (AttributeError)
In [9]: from parsl import *

In [10]: dfk = DataFlowKernel(config=config)
In [11]: unsorted_file = File("globus://037f054a-15cf-11e8-b611-0ac6873fc732/unsorted.txt")
In [12]: unsorted_file.stage_in()
DEBUG:parsl.app.futures:Creating DataFuture with parent : None
DEBUG:parsl.app.futures:Filepath : /Users/awoodard/unsorted.txt
Out[12]: ---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
~/software/anaconda3/envs/parsl_py36/lib/python3.6/site-packages/IPython/core/formatters.py in __call__(self, obj)
700                 type_pprinters=self.type_printers,
701                 deferred_pprinters=self.deferred_printers)
--> 702             printer.pretty(obj)
703             printer.flush()
704             return stream.getvalue()

~/software/anaconda3/envs/parsl_py36/lib/python3.6/site-packages/IPython/lib/pretty.py in pretty(self, obj)
393                             if callable(meth):
394                                 return meth(obj, self, cycle)
--> 395             return _default_pprint(obj, self, cycle)
396         finally:
397             self.end_group()

~/software/anaconda3/envs/parsl_py36/lib/python3.6/site-packages/IPython/lib/pretty.py in _default_pprint(obj, p, cycle)
508     if _safe_getattr(klass, '__repr__', None) is not object.__repr__:
509         # A user-provided repr. Find newlines and replace them with p.break_()
--> 510         _repr_pprint(obj, p, cycle)
511         return
512     p.begin_group(1, '<')

~/software/anaconda3/envs/parsl_py36/lib/python3.6/site-packages/IPython/lib/pretty.py in _repr_pprint(obj, p, cycle)
699     """A pprint that just redirects to the normal repr function."""
700     # Find newlines and replace them with p.break_()
--> 701     output = repr(obj)
702     for idx,output_line in enumerate(output.splitlines()):
703         if idx:

~/ci/parsl/parsl/app/futures.py in __repr__(self)
182     def __repr__(self):
183
--> 184         parent = self.parent.parent
185         if parent:
186             with parent._condition:

AttributeError: 'Future' object has no attribute 'parent'

# Fixed code
def __repr__(self):
    # The DataFuture could be wrapping an AppFuture whose parent is a Future
    # check to find the top level parent
    if isinstance(self.parent, AppFuture):
        parent = self.parent.parent
    else:
        parent = self.parent

    if parent:
        with parent._condition:
            if parent._state == FINISHED:
                if parent._exception:
                    return "<%s at %#x state=%s raised %s>" % (
                        self.__class__.__name__,
                        id(self),
                        _STATE_TO_DESCRIPTION_MAP[parent._state],
                        parent._exception.__class__.__name__,
                    )
                else:
                    return "<%s at %#x state=%s returned %s>" % (
                        self.__class__.__name__,
                        id(self),
                        _STATE_TO_DESCRIPTION_MAP[parent._state],
                        self.filepath + "_file",
                    )
            return "<%s at %#x state=%s>" % (
                self.__class__.__name__,
                id(self),
                _STATE_TO_DESCRIPTION_MAP[parent._state],
            )

    else:
        return "<%s at %#x state=%s>" % (
            self.__class__.__name__,
            id(self),
            _STATE_TO_DESCRIPTION_MAP[self._state],
        )