diff --git a/MantidPlot/ipython_widget/mantid_ipython_widget.py b/MantidPlot/ipython_widget/mantid_ipython_widget.py
index bd3b4d3073bf669e541d7213a7d77174ef25127f..74e588cf42401477fe76c9146c7a8fc28bbcbe5c 100644
--- a/MantidPlot/ipython_widget/mantid_ipython_widget.py
+++ b/MantidPlot/ipython_widget/mantid_ipython_widget.py
@@ -7,11 +7,10 @@
 from __future__ import (absolute_import, division,
                         print_function)
 
-import inspect
 import threading
 import types
 import warnings
-
+from mantid.py3compat import getfullargspec
 from PyQt4 import QtGui
 
 # IPython monkey patches the  pygments.lexer.RegexLexer.get_tokens_unprocessed method
@@ -36,7 +35,8 @@ except ImportError:
     from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
     from IPython.qt.inprocess import QtInProcessKernelManager
 
-def our_run_code(self, code_obj, result=None):
+
+def our_run_code(self, code_obj, result=None, async_=False):
     """ Method with which we replace the run_code method of IPython's InteractiveShell class.
         It calls the original method (renamed to ipython_run_code) on a separate thread
         so that we can avoid locking up the whole of MantidPlot while a command runs.
@@ -47,18 +47,20 @@ def our_run_code(self, code_obj, result=None):
           A compiled code object, to be executed
         result : ExecutionResult, optional
           An object to store exceptions that occur during execution.
+        async_ : Bool (Experimental))
+           Attempt to run top-level asynchronous code in a default loop.
         Returns
         -------
         False : Always, as it doesn't seem to matter.
     """
-
-    t = threading.Thread()
-    #ipython 3.0 introduces a third argument named result
-    nargs = len(inspect.getargspec(self.ipython_run_code).args)
-    if (nargs == 3):
-        t = threading.Thread(target=self.ipython_run_code, args=[code_obj,result])
+    # Different target arguments depending on IPython's version
+    if 'result' in getfullargspec(self.ipython_run_code).args:
+        if 'async_' in getfullargspec(self.ipython_run_code).kwonlyargs:
+            return self.ipython_run_code(code_obj, result, async_=async_)  # return coroutine to be awaited
+        else:
+            t = threading.Thread(target=self.ipython_run_code, args=(code_obj, result))
     else:
-        t = threading.Thread(target=self.ipython_run_code, args=[code_obj])
+        t = threading.Thread(target=self.ipython_run_code, args=(code_obj,))
     t.start()
     while t.is_alive():
         QtGui.QApplication.processEvents()
@@ -84,10 +86,10 @@ class MantidIPythonWidget(RichIPythonWidget):
 
         # Figure out the full path to the mantidplotrc.py file and then %run it
         from os import path
-        mantidplotpath = path.split(path.dirname(__file__))[0] # It's the directory above this one
+        mantidplotpath = path.split(path.dirname(__file__))[0]  # It's the directory above this one
         mantidplotrc = path.join(mantidplotpath, 'mantidplotrc.py')
         shell = kernel.shell
-        shell.run_line_magic('run',mantidplotrc)
+        shell.run_line_magic('run', mantidplotrc)
 
         # These 3 lines replace the run_code method of IPython's InteractiveShell class (of which the
         # shell variable is a derived instance) with our method defined above. The original method
diff --git a/qt/python/mantidqt/widgets/jupyterconsole.py b/qt/python/mantidqt/widgets/jupyterconsole.py
index 8368014924bfcc8b030d49c712fb40357d7e21c1..5aaeeac67aafbc16ebb2f56841ec37c84ed430e6 100644
--- a/qt/python/mantidqt/widgets/jupyterconsole.py
+++ b/qt/python/mantidqt/widgets/jupyterconsole.py
@@ -71,17 +71,30 @@ def async_wrapper(orig_run_code, shell_instance):
     :param shell_instance: The shell instance associated with the orig_run_code
     :return: A new method that can be attached to shell_instance
     """
-    def async_run_code(self, code_obj, result=None):
+    def async_run_code(self, code_obj, result=None, async_=False):
         """A monkey-patched replacement for the InteractiveShell.run_code method.
         It runs the in a separate thread and calls QApplication.processEvents
-        periodically until the method finishes
+        periodically until the method finishes.
+
+        :param code_obj : code object
+          A compiled code object, to be executed
+        :param result : ExecutionResult, optional
+          An object to store exceptions that occur during execution.
+        :param async_ : Bool (Experimental))
+           Attempt to run top-level asynchronous code in a default loop.
+
+        :returns: An AsyncTaskResult object
         """
-        # ipython 3.0 introduces a third argument named result
-        if len(getfullargspec(orig_run_code).args) == 3:
-            args = (code_obj, result)
+        # Different target arguments depending on IPython's version
+        if 'result' in getfullargspec(orig_run_code).args:
+            if 'async_' in getfullargspec(orig_run_code).kwonlyargs:
+                return orig_run_code(code_obj, result, async_=async_)  # return coroutine to be awaited
+            else:
+                task = BlockingAsyncTaskWithCallback(target=orig_run_code, args=(code_obj, result),
+                                                     blocking_cb=QApplication.processEvents)
         else:
-            args = (code_obj,)
-        task = BlockingAsyncTaskWithCallback(target=orig_run_code, args=args, blocking_cb=QApplication.processEvents)
+            task = BlockingAsyncTaskWithCallback(target=orig_run_code, args=(code_obj,),
+                                                 blocking_cb=QApplication.processEvents)
         return task.start()
 
     return types.MethodType(async_run_code, shell_instance)