Skip to content
Merged
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
4 changes: 4 additions & 0 deletions IPython/core/display_trap.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ def __exit__(self, type, value, traceback):
# Returning False will cause exceptions to propagate
return False

@property
def is_active(self) -> bool:
return self._nested_level != 0

def set(self):
"""Set the hook."""
if sys.displayhook is not self.hook:
Expand Down
24 changes: 17 additions & 7 deletions IPython/core/displayhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
from traitlets import Instance, Float
from warnings import warn

from .history import HistoryOutput

# TODO: Move the various attributes (cache_size, [others now moved]). Some
# of these are also attributes of InteractiveShell. They should be on ONE object
# only and the other objects should ask that one object for their values.
Expand All @@ -35,6 +37,7 @@ class DisplayHook(Configurable):

def __init__(self, shell=None, cache_size=1000, **kwargs):
super(DisplayHook, self).__init__(shell=shell, **kwargs)
self._is_active = False
cache_size_min = 3
if cache_size <= 0:
self.do_full_cache = 0
Expand All @@ -51,7 +54,7 @@ def __init__(self, shell=None, cache_size=1000, **kwargs):

# we need a reference to the user-level namespace
self.shell = shell

self._,self.__,self.___ = '','',''

# these are deliberately global:
Expand Down Expand Up @@ -84,13 +87,13 @@ def check_for_underscore(self):
def quiet(self):
"""Should we silence the display hook because of ';'?"""
# do not print output if input ends in ';'

try:
cell = self.shell.history_manager.input_hist_parsed[-1]
except IndexError:
# some uses of ipshellembed may fail here
return False

return self.semicolon_at_end_of_expression(cell)

@staticmethod
Expand All @@ -110,7 +113,11 @@ def semicolon_at_end_of_expression(expression):

def start_displayhook(self):
"""Start the displayhook, initializing resources."""
pass
self._is_active = True

@property
def is_active(self):
return self._is_active

def write_output_prompt(self):
"""Write the output prompt.
Expand Down Expand Up @@ -242,7 +249,10 @@ def fill_exec_result(self, result):

def log_output(self, format_dict):
"""Log the output."""
if 'text/plain' not in format_dict:
self.shell.history_manager.outputs[self.prompt_count].append(
HistoryOutput(output_type="execute_result", bundle=format_dict)
)
if "text/plain" not in format_dict:
# nothing to do
return
if self.shell.logger.log_output:
Expand All @@ -254,6 +264,7 @@ def finish_displayhook(self):
"""Finish up all displayhook activities."""
sys.stdout.write(self.shell.separate_out2)
sys.stdout.flush()
self._is_active = False

def __call__(self, result=None):
"""Printing with history cache management.
Expand All @@ -280,13 +291,12 @@ def cull_cache(self):
cull_count = max(int(sz * self.cull_fraction), 2)
warn('Output cache limit (currently {sz} entries) hit.\n'
'Flushing oldest {cull_count} entries.'.format(sz=sz, cull_count=cull_count))

for i, n in enumerate(sorted(oh)):
if i >= cull_count:
break
self.shell.user_ns.pop('_%i' % n, None)
oh.pop(n, None)


def flush(self):
if not self.do_full_cache:
Expand Down
14 changes: 14 additions & 0 deletions IPython/core/displaypub.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

# This used to be defined here - it is imported for backwards compatibility
from .display_functions import publish_display_data
from .history import HistoryOutput

import typing as t

Expand All @@ -41,6 +42,7 @@ class DisplayPublisher(Configurable):

def __init__(self, shell=None, *args, **kwargs):
self.shell = shell
self._is_publishing = False
super().__init__(*args, **kwargs)

def _validate_data(self, data, metadata=None):
Expand Down Expand Up @@ -129,13 +131,25 @@ def publish(
if self.shell is not None:
handlers = getattr(self.shell, "mime_renderers", {})

outputs = self.shell.history_manager.outputs

outputs[self.shell.execution_count].append(
HistoryOutput(output_type="display_data", bundle=data)
)

for mime, handler in handlers.items():
if mime in data:
handler(data[mime], metadata.get(mime, None))
return

self._is_publishing = True
if "text/plain" in data:
print(data["text/plain"])
self._is_publishing = False

@property
def is_publishing(self):
return self._is_publishing

def clear_output(self, wait=False):
"""Clear the output of the cell receiving output."""
Expand Down
19 changes: 18 additions & 1 deletion IPython/core/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
import threading
from pathlib import Path

from collections import defaultdict
from contextlib import contextmanager
from dataclasses import dataclass
from decorator import decorator
from traitlets import (
Any,
Expand Down Expand Up @@ -583,6 +585,14 @@ def get_range_by_str(
yield from self.get_range(sess, s, e, raw=raw, output=output)


@dataclass
class HistoryOutput:
output_type: typing.Literal[
"out_stream", "err_stream", "display_data", "execute_result"
]
bundle: typing.Dict[str, str]


class HistoryManager(HistoryAccessor):
"""A class to organize all history-related functionality in one place."""

Expand Down Expand Up @@ -610,7 +620,11 @@ def _dir_hist_default(self) -> list[Path]:
# execution count.
output_hist = Dict()
# The text/plain repr of outputs.
output_hist_reprs: dict[int, str] = Dict() # type: ignore [assignment]
output_hist_reprs: typing.Dict[int, str] = Dict() # type: ignore [assignment]
# Maps execution_count to MIME bundles
outputs: typing.Dict[int, typing.List[HistoryOutput]] = defaultdict(list)
# Maps execution_count to exception tracebacks
exceptions: typing.Dict[int, typing.Dict[str, Any]] = Dict() # type: ignore [assignment]

# The number of the current session in the history database
session_number: int = Integer() # type: ignore [assignment]
Expand Down Expand Up @@ -749,6 +763,9 @@ def reset(self, new_session: bool = True) -> None:
"""Clear the session history, releasing all object references, and
optionally open a new session."""
self.output_hist.clear()
self.outputs.clear()
self.exceptions.clear()

# The directory history can't be completely empty
self.dir_hist[:] = [Path.cwd()]

Expand Down
6 changes: 3 additions & 3 deletions IPython/core/historyapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ def start(self):
print("There are already at most %d entries in the history database." % self.keep)
print("Not doing anything. Use --keep= argument to keep fewer entries")
return

print("Trimming history to the most recent %d entries." % self.keep)

inputs.pop() # Remove the extra element we got to check the length.
inputs.reverse()
if inputs:
Expand All @@ -71,7 +71,7 @@ def start(self):
sessions = list(con.execute('SELECT session, start, end, num_cmds, remark FROM '
'sessions WHERE session >= ?', (first_session,)))
con.close()

# Create the new history database.
new_hist_file = profile_dir / "history.sqlite.new"
i = 0
Expand Down
Loading