Skip to content

Commit eeb177f

Browse files
authored
Fix performance of streaming long text (ipython#14941)
Fixes ipython#14937 - [x] adds failing test - [x] fixes the issue by using list of lines to accumulate stream instead or re-allocating a long string each time
2 parents 4d0c438 + 9482534 commit eeb177f

File tree

4 files changed

+17
-5
lines changed

4 files changed

+17
-5
lines changed

IPython/core/history.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ class HistoryOutput:
590590
output_type: typing.Literal[
591591
"out_stream", "err_stream", "display_data", "execute_result"
592592
]
593-
bundle: typing.Dict[str, str]
593+
bundle: typing.Dict[str, str | list[str]]
594594

595595

596596
class HistoryManager(HistoryAccessor):

IPython/core/interactiveshell.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3063,11 +3063,11 @@ def write(data, *args, **kwargs):
30633063
output_stream = outputs[-1]
30643064
if output_stream is None:
30653065
output_stream = HistoryOutput(
3066-
output_type=output_type, bundle={"stream": ""}
3066+
output_type=output_type, bundle={"stream": []}
30673067
)
30683068
outputs_by_counter[execution_count].append(output_stream)
30693069

3070-
output_stream.bundle["stream"] += data # Append to existing stream
3070+
output_stream.bundle["stream"].append(data) # Append to existing stream
30713071
return result
30723072

30733073
stream.write = write

IPython/core/magics/basic.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -571,9 +571,11 @@ def notebook(self, s):
571571
for output in outputs[execution_count]:
572572
for mime_type, data in output.bundle.items():
573573
if output.output_type == "out_stream":
574-
cell.outputs.append(v4.new_output("stream", text=[data]))
574+
text = data if isinstance(data, list) else [data]
575+
cell.outputs.append(v4.new_output("stream", text=text))
575576
elif output.output_type == "err_stream":
576-
err_output = v4.new_output("stream", text=[data])
577+
text = data if isinstance(data, list) else [data]
578+
err_output = v4.new_output("stream", text=text)
577579
err_output.name = "stderr"
578580
cell.outputs.append(err_output)
579581
elif output.output_type == "execute_result":

tests/test_interactiveshell.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import shutil
1717
import sys
1818
import tempfile
19+
import time
1920
import unittest
2021
import pytest
2122
from unittest import mock
@@ -84,6 +85,15 @@ def test_run_cell_multiline(self):
8485
self.assertEqual(res.success, True)
8586
self.assertEqual(res.result, None)
8687

88+
def test_stream_performance(self):
89+
"""It should be fast to execute."""
90+
src = "for i in range(250_000): print(i)"
91+
start = time.perf_counter()
92+
ip.run_cell(src)
93+
end = time.perf_counter()
94+
duration = end - start
95+
assert duration < 10
96+
8797
def test_multiline_string_cells(self):
8898
"Code sprinkled with multiline strings should execute (GH-306)"
8999
ip.run_cell("tmp=0")

0 commit comments

Comments
 (0)