Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions IPython/core/interactiveshell.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ class ExecutionInfo:
silent = False
shell_futures = True
cell_id = None
cell_meta = None

def __init__(
self,
Expand All @@ -266,6 +267,7 @@ def __init__(
silent,
shell_futures,
cell_id,
cell_meta=None,
transformed_cell=None,
):
self.raw_cell = raw_cell
Expand All @@ -274,6 +276,7 @@ def __init__(
self.silent = silent
self.shell_futures = shell_futures
self.cell_id = cell_id
self.cell_meta = cell_meta

def __repr__(self):
name = self.__class__.__qualname__
Expand All @@ -286,7 +289,7 @@ def __repr__(self):
else self.transformed_cell
)
return (
'<%s object at %x, raw_cell="%s" transformed_cell="%s" store_history=%s silent=%s shell_futures=%s cell_id=%s>'
'<%s object at %x, raw_cell="%s" transformed_cell="%s" store_history=%s silent=%s shell_futures=%s cell_id=%s cell_meta=%s>'
% (
name,
id(self),
Expand All @@ -296,6 +299,7 @@ def __repr__(self):
self.silent,
self.shell_futures,
self.cell_id,
self.cell_meta,
)
)

Expand Down Expand Up @@ -3088,6 +3092,7 @@ def run_cell(
silent=False,
shell_futures=True,
cell_id=None,
cell_meta=None,
):
"""Run a complete IPython cell.

Expand All @@ -3113,6 +3118,12 @@ def run_cell(
history across kernel restarts. In notebook contexts, this is typically
a UUID generated by the frontend. If None, the kernel may generate an
internal identifier or proceed without cell tracking capabilities.
cell_meta : dict, optional
Metadata associated with the request. This will be passed to any event
listeners as part of ExecutionInfo, enabling extension authors to append
data to execution requests from a client which can then be read by an IPython
extension. Extension authors are encouraged to place data associated with their extension
under a single string key in the dictionary.
Returns
-------
result : :class:`ExecutionResult`
Expand All @@ -3121,7 +3132,7 @@ def run_cell(
with self._tee(channel="stdout"), self._tee(channel="stderr"):
try:
result = self._run_cell(
raw_cell, store_history, silent, shell_futures, cell_id
raw_cell, store_history, silent, shell_futures, cell_id, cell_meta
)
finally:
self.events.trigger("post_execute")
Expand All @@ -3136,6 +3147,7 @@ def _run_cell(
silent: bool,
shell_futures: bool,
cell_id: str,
cell_meta: Optional[dict],
) -> ExecutionResult:
"""Internal method to run a complete IPython cell."""

Expand All @@ -3157,6 +3169,7 @@ def _run_cell(
transformed_cell=transformed_cell,
preprocessing_exc_tuple=preprocessing_exc_tuple,
cell_id=cell_id,
cell_meta=cell_meta,
)

# run_cell_async is async, but may not actually need an eventloop.
Expand Down Expand Up @@ -3184,6 +3197,7 @@ def _run_cell(
silent,
shell_futures,
cell_id,
cell_meta,
transformed_cell=transformed_cell,
)
result = ExecutionResult(info)
Expand Down Expand Up @@ -3245,6 +3259,7 @@ async def run_cell_async(
transformed_cell: Optional[str] = None,
preprocessing_exc_tuple: Optional[AnyType] = None,
cell_id=None,
cell_meta=None,
) -> ExecutionResult:
"""Run a complete IPython cell asynchronously.

Expand Down Expand Up @@ -3281,6 +3296,7 @@ async def run_cell_async(
silent,
shell_futures,
cell_id,
cell_meta,
transformed_cell=transformed_cell,
)
result = ExecutionResult(info)
Expand Down
40 changes: 40 additions & 0 deletions tests/test_interactiveshell.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from IPython.testing import tools as tt
from IPython.utils.process import find_cmd

from IPython.core.interactiveshell import InteractiveShell
# -----------------------------------------------------------------------------
# Globals
# -----------------------------------------------------------------------------
Expand Down Expand Up @@ -1262,3 +1263,42 @@ def test_enable_gui_osx():
ip.enable_gui()

ip.simple_prompt = simple_prompt

class TestCellMeta(unittest.TestCase):

def test_cell_meta(self):
reply = ip.run_cell(("a=1\n"), cell_meta={"test": [1, 2, 3]})
assert reply.info.cell_meta == {"test": [1, 2, 3]}

def test_cell_meta_default_arg(self):
reply = ip.run_cell("a=1")
assert reply.info.cell_meta is None

def test_cell_meta_nested_dict(self):
meta = {"nested": {"a": [1, 2, 3]}, "flag": True}
reply = ip.run_cell("a=1", cell_meta=meta)
assert reply.info.cell_meta == meta

def test_cell_meta_error(self):
reply = ip.run_cell("raise ValueError()", cell_meta={"test": [1, 2, 3]})
assert reply.info.cell_meta == {"test": [1, 2, 3]}

def test_cell_meta_listener(self):
pre_explicit = mock.Mock()
post_explicit = mock.Mock()

ip.events.register("pre_run_cell", pre_explicit)
ip.events.register("post_run_cell", post_explicit)

test_meta = {"test": [1, 2, 3]}
try:
ip.run_cell("1", cell_meta=test_meta)
(info,) = pre_explicit.call_args[0]
(result,) = post_explicit.call_args[0]
self.assertEqual(info.cell_meta, test_meta)
self.assertEqual(result.info.cell_meta, test_meta)
self.assertEqual(info, result.info)
finally:
# remove post-exec
ip.events.unregister("pre_run_cell", pre_explicit)
ip.events.unregister("post_run_cell", post_explicit)
1 change: 1 addition & 0 deletions tests/test_zzz_autoreload.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ def run_code(self, code):
silent=False,
shell_futures=False,
cell_id=None,
cell_meta=None
),
)
exec(code, self.user_ns)
Expand Down
Loading