-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathipython-setup.py
More file actions
330 lines (255 loc) · 10.1 KB
/
ipython-setup.py
File metadata and controls
330 lines (255 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
"""
IPython Bootstrap Setup for Enhanced Pyodide Runtime
This module sets up a complete IPython environment with rich display support,
matplotlib integration, enhanced error formatting, and proper output handling.
Note: For HTTPS requests, use the `requests` library instead of urllib:
import requests
import pandas as pd
from io import StringIO
response = requests.get("https://example.com/data.csv")
df = pd.read_csv(StringIO(response.text))
"""
import os
import sys
import io
import json
import traceback
from IPython.core.interactiveshell import InteractiveShell
from IPython.core.displayhook import DisplayHook
from IPython.core.displaypub import DisplayPublisher
from IPython.core.history import HistoryManager
import matplotlib
import matplotlib.pyplot as plt
# Configure matplotlib for rich SVG output
matplotlib.use("svg")
plt.rcParams["figure.dpi"] = 100
plt.rcParams["savefig.dpi"] = 100
plt.rcParams["figure.facecolor"] = "white"
plt.rcParams["savefig.facecolor"] = "white"
plt.rcParams["figure.figsize"] = (8, 6)
os.environ.update(
{
"TERM": "xterm-256color",
"FORCE_COLOR": "1",
"COLORTERM": "truecolor",
"CLICOLOR": "1",
"CLICOLOR_FORCE": "1",
}
)
# Mock terminal support for rich colors
class ColorfulStream:
"""Stream wrapper that reports as a TTY for color support"""
def __init__(self, original):
self._original = original
def __getattr__(self, name):
return getattr(self._original, name)
def isatty(self):
return True
def write(self, text):
return self._original.write(text)
def flush(self):
return self._original.flush()
# Replace stdout and stderr with colorful versions
sys.stdout = ColorfulStream(sys.stdout)
sys.stderr = ColorfulStream(sys.stderr)
class LiteHistoryManager(HistoryManager):
"""Lightweight history manager for web environment"""
def __init__(self, shell=None, config=None, **traits):
self.enabled = False
super().__init__(shell=shell, config=config, **traits)
class RichDisplayPublisher(DisplayPublisher):
"""Enhanced display publisher for rich output handling"""
def __init__(self, shell=None, *args, **kwargs):
super().__init__(shell, *args, **kwargs)
self.js_callback = None
def publish(
self,
data,
metadata=None,
source=None,
*,
transient=None,
update=False,
**kwargs,
):
"""Publish display data with proper serialization"""
if self.js_callback and data:
# Convert data to serializable format
serializable_data = self._make_serializable(data)
serializable_metadata = self._make_serializable(metadata or {})
serializable_transient = self._make_serializable(transient or {})
self.js_callback(
serializable_data, serializable_metadata, serializable_transient, update
)
def clear_output(self, wait=False):
"""Clear output signal"""
# TODO: Implement clear_output with our schema and protocol
print(f"[CLEAR_OUTPUT:{wait}]", flush=True)
def _make_serializable(self, obj):
"""Convert objects to JSON-serializable format"""
if obj is None:
return {}
if hasattr(obj, "to_dict"):
return obj.to_dict()
if isinstance(obj, dict):
result = {}
for key, value in obj.items():
try:
# Test if value is JSON serializable
json.dumps(value)
result[str(key)] = value
except (TypeError, ValueError) as e:
# Log serialization issues to structured logs
print(
f"[SERIALIZATION_WARNING] Non-serializable value for key '{key}': {e}",
flush=True,
)
# Convert non-serializable values to strings
result[str(key)] = str(value)
return result
try:
# Test if object is JSON serializable
json.dumps(obj)
return obj
except (TypeError, ValueError):
return str(obj)
class RichDisplayHook(DisplayHook):
"""Enhanced display hook for execution results with rich formatting"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.js_callback = None
self.execution_count = 0
def __call__(self, result):
"""Handle execution results with proper serialization"""
if result is not None:
self.execution_count += 1
# Format the result using IPython's rich formatting
try:
format_dict, md_dict = self.compute_format_data(result)
# Make data serializable
if self.js_callback and format_dict:
serializable_data = self._make_serializable(format_dict)
serializable_metadata = self._make_serializable(md_dict or {})
self.js_callback(
self.execution_count, serializable_data, serializable_metadata
)
except Exception as e:
# Log formatting errors to structured logs instead of stderr
print(
f"[DISPLAY_HOOK_ERROR] ErrorWarning: Error formatting result: {e}",
file=sys.stderr,
)
# Fallback to simple string representation
if self.js_callback:
fallback_data = {"text/plain": str(result)}
self.js_callback(self.execution_count, fallback_data, {})
return result
def _make_serializable(self, obj):
"""Convert objects to JSON-serializable format"""
if obj is None:
return {}
if isinstance(obj, dict):
result = {}
for key, value in obj.items():
try:
# Test if value is JSON serializable
json.dumps(value)
result[str(key)] = value
except (TypeError, ValueError) as e:
# Log serialization issues to structured logs
print(
f"[SERIALIZATION_WARNING] Non-serializable value for key '{key}': {e}",
flush=True,
)
# Convert non-serializable values to strings
result[str(key)] = str(value)
return result
try:
# Test if object is JSON serializable
json.dumps(obj)
return obj
except (TypeError, ValueError):
return str(obj)
shell = InteractiveShell.instance(
displayhook_class=RichDisplayHook,
display_pub_class=RichDisplayPublisher,
)
# Override history manager
shell.history_manager = LiteHistoryManager(shell=shell, parent=shell)
# Enhanced matplotlib show function with SVG capture
_original_show = plt.show
def _capture_matplotlib_show(block=None):
"""Capture matplotlib plots as high-quality SVG and send via display system"""
if plt.get_fignums():
fig = plt.gcf()
svg_buffer = io.StringIO()
try:
# Save as SVG with high quality settings
fig.savefig(
svg_buffer,
format="svg",
bbox_inches="tight",
facecolor="white",
edgecolor="none",
dpi=100,
transparent=False,
)
svg_content = svg_buffer.getvalue()
svg_buffer.close()
# Use IPython's display system to show SVG
from IPython.display import display, SVG
display(SVG(svg_content))
# Clear the figure
plt.clf()
except Exception as e:
print(f"Error capturing matplotlib plot: {e}", file=sys.stderr)
return _original_show(block=block) if block is not None else _original_show()
# Replace matplotlib show with our enhanced version
plt.show = _capture_matplotlib_show
def setup_rich_formatters():
"""Set up rich formatters for pandas, numpy, and other data types"""
try:
import pandas as pd
# Enhanced pandas display options
pd.set_option("display.max_rows", 100)
pd.set_option("display.max_columns", 20)
pd.set_option("display.width", None)
pd.set_option("display.max_colwidth", 50)
except ImportError:
print("INFO: Pandas not available for rich formatting")
try:
import numpy as np
# Enhanced numpy display
np.set_printoptions(precision=4, suppress=True, linewidth=120)
except ImportError:
print("INFO: NumPy not available for rich formatting")
# Apply rich formatters
setup_rich_formatters()
def format_exception(exc_type, exc_value, exc_traceback):
"""Format exceptions with standard Python traceback formatting"""
try:
# Use standard traceback formatting to preserve exception type information
return "".join(traceback.format_exception(exc_type, exc_value, exc_traceback))
except Exception as format_error:
# Log formatting errors to structured logs instead of stderr
print(
f"[FORMATTER_ERROR] Failed to format exception: {format_error}", flush=True
)
# Fallback to basic formatting
return f"{exc_type.__name__}: {exc_value}"
# Override exception formatting
sys.excepthook = lambda exc_type, exc_value, exc_traceback: print(
format_exception(exc_type, exc_value, exc_traceback), file=sys.stderr
)
# Set up global callbacks (will be overridden by worker)
def default_display_callback(data, metadata, transient, update=False):
"""Default display callback - does nothing"""
pass
def default_execution_callback(execution_count, data, metadata):
"""Default execution callback - does nothing"""
pass
# Make callbacks available globally
js_display_callback = default_display_callback
js_execution_callback = default_execution_callback
# Export the configured shell for use by the worker
__all__ = ["shell", "js_display_callback", "js_execution_callback"]