ipkernel.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. """The IPython kernel implementation"""
  2. from __future__ import annotations
  3. import asyncio
  4. import builtins
  5. import getpass
  6. import os
  7. import signal
  8. import sys
  9. import threading
  10. import typing as t
  11. from contextlib import contextmanager
  12. from functools import partial
  13. import comm
  14. from IPython.core import release
  15. from IPython.utils.tokenutil import line_at_cursor, token_at_cursor
  16. from traitlets import Any, Bool, HasTraits, Instance, List, Type, default, observe, observe_compat
  17. from zmq.eventloop.zmqstream import ZMQStream
  18. from .comm.comm import BaseComm
  19. from .comm.manager import CommManager
  20. from .compiler import XCachingCompiler
  21. from .eventloops import _use_appnope
  22. from .kernelbase import Kernel as KernelBase
  23. from .kernelbase import _accepts_parameters
  24. from .zmqshell import ZMQInteractiveShell
  25. try:
  26. from IPython.core.interactiveshell import _asyncio_runner # type:ignore[attr-defined]
  27. except ImportError:
  28. _asyncio_runner = None # type:ignore[assignment]
  29. try:
  30. from IPython.core.completer import provisionalcompleter as _provisionalcompleter
  31. from IPython.core.completer import rectify_completions as _rectify_completions
  32. _use_experimental_60_completion = True
  33. except ImportError:
  34. _use_experimental_60_completion = False
  35. _EXPERIMENTAL_KEY_NAME = "_jupyter_types_experimental"
  36. def _create_comm(*args, **kwargs):
  37. """Create a new Comm."""
  38. return BaseComm(*args, **kwargs)
  39. # there can only be one comm manager in a ipykernel process
  40. _comm_lock = threading.Lock()
  41. _comm_manager: CommManager | None = None
  42. def _get_comm_manager(*args, **kwargs):
  43. """Create a new CommManager."""
  44. global _comm_manager # noqa: PLW0603
  45. if _comm_manager is None:
  46. with _comm_lock:
  47. if _comm_manager is None:
  48. _comm_manager = CommManager(*args, **kwargs)
  49. return _comm_manager
  50. comm.create_comm = _create_comm
  51. comm.get_comm_manager = _get_comm_manager
  52. class IPythonKernel(KernelBase):
  53. """The IPython Kernel class."""
  54. shell = Instance("IPython.core.interactiveshell.InteractiveShellABC", allow_none=True)
  55. shell_class = Type(ZMQInteractiveShell)
  56. # use fully-qualified name to ensure lazy import and prevent the issue from
  57. # https://github.com/ipython/ipykernel/issues/1198
  58. debugger_class = Type("ipykernel.debugger.Debugger")
  59. compiler_class = Type(XCachingCompiler)
  60. use_experimental_completions = Bool(
  61. True,
  62. help="Set this flag to False to deactivate the use of experimental IPython completion APIs.",
  63. ).tag(config=True)
  64. debugpy_stream = Instance(ZMQStream, allow_none=True)
  65. user_module = Any()
  66. @observe("user_module")
  67. @observe_compat
  68. def _user_module_changed(self, change):
  69. if self.shell is not None:
  70. self.shell.user_module = change["new"]
  71. user_ns = Instance("collections.abc.Mapping", allow_none=True)
  72. @default("user_ns")
  73. def _default_user_ns(self):
  74. return dict()
  75. @observe("user_ns")
  76. @observe_compat
  77. def _user_ns_changed(self, change):
  78. if self.shell is not None:
  79. self.shell.user_ns = change["new"]
  80. self.shell.init_user_ns()
  81. # A reference to the Python builtin 'raw_input' function.
  82. # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
  83. _sys_raw_input = Any()
  84. _sys_eval_input = Any()
  85. def __init__(self, **kwargs):
  86. """Initialize the kernel."""
  87. super().__init__(**kwargs)
  88. from .debugger import _is_debugpy_available
  89. self._kernel_modules = [
  90. m.__file__ for m in sys.modules.values() if hasattr(m, "__file__") and m.__file__
  91. ]
  92. # Initialize the Debugger
  93. if _is_debugpy_available:
  94. self.debugger = self.debugger_class(
  95. self.log,
  96. self.debugpy_stream,
  97. self._publish_debug_event,
  98. self.debug_shell_socket,
  99. self.session,
  100. self._kernel_modules,
  101. self.debug_just_my_code,
  102. self.filter_internal_frames,
  103. )
  104. # Initialize the InteractiveShell subclass
  105. self.shell = self.shell_class.instance(
  106. parent=self,
  107. profile_dir=self.profile_dir,
  108. user_module=self.user_module,
  109. user_ns=self.user_ns,
  110. kernel=self,
  111. compiler_class=self.compiler_class,
  112. )
  113. self.shell.displayhook.session = self.session # type:ignore[attr-defined]
  114. jupyter_session_name = os.environ.get("JPY_SESSION_NAME")
  115. if jupyter_session_name:
  116. self.shell.user_ns["__session__"] = jupyter_session_name
  117. self.shell.displayhook.pub_socket = self.iopub_socket # type:ignore[attr-defined]
  118. self.shell.displayhook.topic = self._topic("execute_result") # type:ignore[attr-defined]
  119. self.shell.display_pub.session = self.session # type:ignore[attr-defined]
  120. self.shell.display_pub.pub_socket = self.iopub_socket # type:ignore[attr-defined]
  121. self.comm_manager = comm.get_comm_manager()
  122. assert isinstance(self.comm_manager, HasTraits)
  123. self.shell.configurables.append(self.comm_manager) # type:ignore[arg-type]
  124. comm_msg_types = ["comm_open", "comm_msg", "comm_close"]
  125. for msg_type in comm_msg_types:
  126. self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type)
  127. if _use_appnope() and self._darwin_app_nap:
  128. # Disable app-nap as the kernel is not a gui but can have guis
  129. import appnope # type:ignore[import-untyped]
  130. appnope.nope()
  131. help_links = List(
  132. [
  133. {
  134. "text": "Python Reference",
  135. "url": "https://docs.python.org/%i.%i" % sys.version_info[:2],
  136. },
  137. {
  138. "text": "IPython Reference",
  139. "url": "https://ipython.org/documentation.html",
  140. },
  141. {
  142. "text": "NumPy Reference",
  143. "url": "https://docs.scipy.org/doc/numpy/reference/",
  144. },
  145. {
  146. "text": "SciPy Reference",
  147. "url": "https://docs.scipy.org/doc/scipy/reference/",
  148. },
  149. {
  150. "text": "Matplotlib Reference",
  151. "url": "https://matplotlib.org/contents.html",
  152. },
  153. {
  154. "text": "SymPy Reference",
  155. "url": "http://docs.sympy.org/latest/index.html",
  156. },
  157. {
  158. "text": "pandas Reference",
  159. "url": "https://pandas.pydata.org/pandas-docs/stable/",
  160. },
  161. ]
  162. ).tag(config=True)
  163. # Kernel info fields
  164. implementation = "ipython"
  165. implementation_version = release.version
  166. language_info = {
  167. "name": "python",
  168. "version": sys.version.split()[0],
  169. "mimetype": "text/x-python",
  170. "codemirror_mode": {"name": "ipython", "version": sys.version_info[0]},
  171. "pygments_lexer": "ipython%d" % 3,
  172. "nbconvert_exporter": "python",
  173. "file_extension": ".py",
  174. }
  175. def dispatch_debugpy(self, msg):
  176. from .debugger import _is_debugpy_available
  177. if _is_debugpy_available:
  178. # The first frame is the socket id, we can drop it
  179. frame = msg[1].bytes.decode("utf-8")
  180. self.log.debug("Debugpy received: %s", frame)
  181. self.debugger.tcp_client.receive_dap_frame(frame)
  182. @property
  183. def banner(self): # type:ignore[override]
  184. if self.shell:
  185. return self.shell.banner
  186. return None
  187. async def poll_stopped_queue(self):
  188. """Poll the stopped queue."""
  189. while True:
  190. await self.debugger.handle_stopped_event()
  191. def start(self):
  192. """Start the kernel."""
  193. if self.shell:
  194. self.shell.exit_now = False
  195. if self.debugpy_stream is None:
  196. self.log.warning("debugpy_stream undefined, debugging will not be enabled")
  197. else:
  198. self.debugpy_stream.on_recv(self.dispatch_debugpy, copy=False)
  199. super().start()
  200. if self.debugpy_stream:
  201. asyncio.run_coroutine_threadsafe(
  202. self.poll_stopped_queue(), self.control_thread.io_loop.asyncio_loop
  203. )
  204. def set_parent(self, ident, parent, channel="shell"):
  205. """Overridden from parent to tell the display hook and output streams
  206. about the parent message.
  207. """
  208. super().set_parent(ident, parent, channel)
  209. if channel == "shell" and self.shell:
  210. self.shell.set_parent(parent)
  211. def init_metadata(self, parent):
  212. """Initialize metadata.
  213. Run at the beginning of each execution request.
  214. """
  215. md = super().init_metadata(parent)
  216. # FIXME: remove deprecated ipyparallel-specific code
  217. # This is required for ipyparallel < 5.0
  218. md.update(
  219. {
  220. "dependencies_met": True,
  221. "engine": self.ident,
  222. }
  223. )
  224. return md
  225. def finish_metadata(self, parent, metadata, reply_content):
  226. """Finish populating metadata.
  227. Run after completing an execution request.
  228. """
  229. # FIXME: remove deprecated ipyparallel-specific code
  230. # This is required by ipyparallel < 5.0
  231. metadata["status"] = reply_content["status"]
  232. if reply_content["status"] == "error" and reply_content["ename"] == "UnmetDependency":
  233. metadata["dependencies_met"] = False
  234. return metadata
  235. def _forward_input(self, allow_stdin=False):
  236. """Forward raw_input and getpass to the current frontend.
  237. via input_request
  238. """
  239. self._allow_stdin = allow_stdin
  240. self._sys_raw_input = builtins.input
  241. builtins.input = self.raw_input
  242. self._save_getpass = getpass.getpass
  243. getpass.getpass = self.getpass
  244. def _restore_input(self):
  245. """Restore raw_input, getpass"""
  246. builtins.input = self._sys_raw_input
  247. getpass.getpass = self._save_getpass
  248. @property
  249. def execution_count(self):
  250. if self.shell:
  251. return self.shell.execution_count
  252. return None
  253. @execution_count.setter
  254. def execution_count(self, value):
  255. # Ignore the incrementing done by KernelBase, in favour of our shell's
  256. # execution counter.
  257. pass
  258. @contextmanager
  259. def _cancel_on_sigint(self, future):
  260. """ContextManager for capturing SIGINT and cancelling a future
  261. SIGINT raises in the event loop when running async code,
  262. but we want it to halt a coroutine.
  263. Ideally, it would raise KeyboardInterrupt,
  264. but this turns it into a CancelledError.
  265. At least it gets a decent traceback to the user.
  266. """
  267. sigint_future: asyncio.Future[int] = asyncio.Future()
  268. # whichever future finishes first,
  269. # cancel the other one
  270. def cancel_unless_done(f, _ignored):
  271. if f.cancelled() or f.done():
  272. return
  273. f.cancel()
  274. # when sigint finishes,
  275. # abort the coroutine with CancelledError
  276. sigint_future.add_done_callback(partial(cancel_unless_done, future))
  277. # when the main future finishes,
  278. # stop watching for SIGINT events
  279. future.add_done_callback(partial(cancel_unless_done, sigint_future))
  280. def handle_sigint(*args):
  281. def set_sigint_result():
  282. if sigint_future.cancelled() or sigint_future.done():
  283. return
  284. sigint_future.set_result(1)
  285. # use add_callback for thread safety
  286. self.io_loop.add_callback(set_sigint_result)
  287. # set the custom sigint handler during this context
  288. save_sigint = signal.signal(signal.SIGINT, handle_sigint)
  289. try:
  290. yield
  291. finally:
  292. # restore the previous sigint handler
  293. signal.signal(signal.SIGINT, save_sigint)
  294. @contextmanager
  295. def _dummy_context_manager(self, *args):
  296. # Signals only work in main thread, so cannot use _cancel_on_sigint in subshells.
  297. yield
  298. async def execute_request(self, stream, ident, parent):
  299. """Override for cell output - cell reconciliation."""
  300. await super().execute_request(stream, ident, parent)
  301. async def do_execute(
  302. self,
  303. code,
  304. silent,
  305. store_history=True,
  306. user_expressions=None,
  307. allow_stdin=False,
  308. *,
  309. cell_meta=None,
  310. cell_id=None,
  311. ):
  312. """Handle code execution."""
  313. shell = self.shell # we'll need this a lot here
  314. assert shell is not None
  315. self._forward_input(allow_stdin)
  316. reply_content: dict[str, t.Any] = {}
  317. if hasattr(shell, "run_cell_async") and hasattr(shell, "should_run_async"):
  318. run_cell = shell.run_cell_async
  319. should_run_async = shell.should_run_async
  320. accepts_params = _accepts_parameters(run_cell, ["cell_id"])
  321. else:
  322. should_run_async = lambda cell: False # noqa: ARG005, E731
  323. # older IPython,
  324. # use blocking run_cell and wrap it in coroutine
  325. async def run_cell(*args, **kwargs):
  326. return shell.run_cell(*args, **kwargs)
  327. accepts_params = _accepts_parameters(shell.run_cell, ["cell_id"])
  328. try:
  329. # default case: runner is asyncio and asyncio is already running
  330. # TODO: this should check every case for "are we inside the runner",
  331. # not just asyncio
  332. preprocessing_exc_tuple = None
  333. try:
  334. transformed_cell = shell.transform_cell(code)
  335. except Exception:
  336. transformed_cell = code
  337. preprocessing_exc_tuple = sys.exc_info()
  338. if (
  339. _asyncio_runner # type:ignore[truthy-bool]
  340. and shell.loop_runner is _asyncio_runner
  341. and asyncio.get_event_loop().is_running()
  342. and should_run_async(
  343. code,
  344. transformed_cell=transformed_cell,
  345. preprocessing_exc_tuple=preprocessing_exc_tuple,
  346. )
  347. ):
  348. if accepts_params["cell_id"]:
  349. coro = run_cell(
  350. code,
  351. store_history=store_history,
  352. silent=silent,
  353. transformed_cell=transformed_cell,
  354. preprocessing_exc_tuple=preprocessing_exc_tuple,
  355. cell_id=cell_id,
  356. )
  357. else:
  358. coro = run_cell(
  359. code,
  360. store_history=store_history,
  361. silent=silent,
  362. transformed_cell=transformed_cell,
  363. preprocessing_exc_tuple=preprocessing_exc_tuple,
  364. )
  365. coro_future = asyncio.ensure_future(coro)
  366. cm = (
  367. self._cancel_on_sigint
  368. if threading.current_thread() == self.shell_channel_thread.parent_thread
  369. else self._dummy_context_manager
  370. )
  371. with cm(coro_future):
  372. res = None
  373. try:
  374. res = await coro_future
  375. finally:
  376. shell.events.trigger("post_execute")
  377. if not silent:
  378. shell.events.trigger("post_run_cell", res)
  379. else:
  380. # runner isn't already running,
  381. # make synchronous call,
  382. # letting shell dispatch to loop runners
  383. if accepts_params["cell_id"]:
  384. res = shell.run_cell(
  385. code,
  386. store_history=store_history,
  387. silent=silent,
  388. cell_id=cell_id,
  389. )
  390. else:
  391. res = shell.run_cell(code, store_history=store_history, silent=silent)
  392. finally:
  393. self._restore_input()
  394. err = res.error_before_exec if res.error_before_exec is not None else res.error_in_exec
  395. if res.success:
  396. reply_content["status"] = "ok"
  397. else:
  398. reply_content["status"] = "error"
  399. reply_content.update(
  400. {
  401. "traceback": shell._last_traceback or [],
  402. "ename": str(type(err).__name__),
  403. "evalue": str(err),
  404. }
  405. )
  406. # FIXME: deprecated piece for ipyparallel (remove in 5.0):
  407. e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method="execute")
  408. reply_content["engine_info"] = e_info
  409. # Return the execution counter so clients can display prompts
  410. reply_content["execution_count"] = shell.execution_count - 1
  411. if "traceback" in reply_content:
  412. self.log.info(
  413. "Exception in execute request:\n%s",
  414. "\n".join(reply_content["traceback"]),
  415. )
  416. # At this point, we can tell whether the main code execution succeeded
  417. # or not. If it did, we proceed to evaluate user_expressions
  418. if reply_content["status"] == "ok":
  419. reply_content["user_expressions"] = shell.user_expressions(user_expressions or {})
  420. else:
  421. # If there was an error, don't even try to compute expressions
  422. reply_content["user_expressions"] = {}
  423. # Payloads should be retrieved regardless of outcome, so we can both
  424. # recover partial output (that could have been generated early in a
  425. # block, before an error) and always clear the payload system.
  426. reply_content["payload"] = shell.payload_manager.read_payload()
  427. # Be aggressive about clearing the payload because we don't want
  428. # it to sit in memory until the next execute_request comes in.
  429. shell.payload_manager.clear_payload()
  430. return reply_content
  431. def do_complete(self, code, cursor_pos):
  432. """Handle code completion."""
  433. if _use_experimental_60_completion and self.use_experimental_completions:
  434. return self._experimental_do_complete(code, cursor_pos)
  435. # FIXME: IPython completers currently assume single line,
  436. # but completion messages give multi-line context
  437. # For now, extract line from cell, based on cursor_pos:
  438. if cursor_pos is None:
  439. cursor_pos = len(code)
  440. line, offset = line_at_cursor(code, cursor_pos)
  441. line_cursor = cursor_pos - offset
  442. assert self.shell is not None
  443. txt, matches = self.shell.complete("", line, line_cursor)
  444. return {
  445. "matches": matches,
  446. "cursor_end": cursor_pos,
  447. "cursor_start": cursor_pos - len(txt),
  448. "metadata": {},
  449. "status": "ok",
  450. }
  451. async def do_debug_request(self, msg):
  452. """Handle a debug request."""
  453. from .debugger import _is_debugpy_available
  454. if _is_debugpy_available:
  455. return await self.debugger.process_request(msg)
  456. return None
  457. def _experimental_do_complete(self, code, cursor_pos):
  458. """
  459. Experimental completions from IPython, using Jedi.
  460. """
  461. if cursor_pos is None:
  462. cursor_pos = len(code)
  463. with _provisionalcompleter():
  464. assert self.shell is not None
  465. raw_completions = self.shell.Completer.completions(code, cursor_pos)
  466. completions = list(_rectify_completions(code, raw_completions))
  467. comps = []
  468. for comp in completions:
  469. comps.append(
  470. dict(
  471. start=comp.start,
  472. end=comp.end,
  473. text=comp.text,
  474. type=comp.type,
  475. signature=comp.signature,
  476. )
  477. )
  478. if completions:
  479. s = completions[0].start
  480. e = completions[0].end
  481. matches = [c.text for c in completions]
  482. else:
  483. s = cursor_pos
  484. e = cursor_pos
  485. matches = []
  486. return {
  487. "matches": matches,
  488. "cursor_end": e,
  489. "cursor_start": s,
  490. "metadata": {_EXPERIMENTAL_KEY_NAME: comps},
  491. "status": "ok",
  492. }
  493. def do_inspect(self, code, cursor_pos, detail_level=0, omit_sections=()):
  494. """Handle code inspection."""
  495. name = token_at_cursor(code, cursor_pos)
  496. reply_content: dict[str, t.Any] = {"status": "ok"}
  497. reply_content["data"] = {}
  498. reply_content["metadata"] = {}
  499. assert self.shell is not None
  500. try:
  501. if release.version_info >= (8,):
  502. # `omit_sections` keyword will be available in IPython 8, see
  503. # https://github.com/ipython/ipython/pull/13343
  504. bundle = self.shell.object_inspect_mime(
  505. name,
  506. detail_level=detail_level,
  507. omit_sections=omit_sections,
  508. )
  509. else:
  510. bundle = self.shell.object_inspect_mime(name, detail_level=detail_level)
  511. reply_content["data"].update(bundle)
  512. if not self.shell.enable_html_pager:
  513. reply_content["data"].pop("text/html")
  514. reply_content["found"] = True
  515. except KeyError:
  516. reply_content["found"] = False
  517. return reply_content
  518. def do_history(
  519. self,
  520. hist_access_type,
  521. output,
  522. raw,
  523. session=0,
  524. start=0,
  525. stop=None,
  526. n=None,
  527. pattern=None,
  528. unique=False,
  529. ):
  530. """Handle code history."""
  531. assert self.shell is not None
  532. if hist_access_type == "tail":
  533. hist = self.shell.history_manager.get_tail(
  534. n, raw=raw, output=output, include_latest=True
  535. )
  536. elif hist_access_type == "range":
  537. hist = self.shell.history_manager.get_range(
  538. session, start, stop, raw=raw, output=output
  539. )
  540. elif hist_access_type == "search":
  541. hist = self.shell.history_manager.search(
  542. pattern, raw=raw, output=output, n=n, unique=unique
  543. )
  544. else:
  545. hist = []
  546. return {
  547. "status": "ok",
  548. "history": list(hist),
  549. }
  550. def do_shutdown(self, restart):
  551. """Handle kernel shutdown."""
  552. if self.shell:
  553. self.shell.exit_now = True
  554. return dict(status="ok", restart=restart)
  555. def do_is_complete(self, code):
  556. """Handle an is_complete request."""
  557. transformer_manager = getattr(self.shell, "input_transformer_manager", None)
  558. if transformer_manager is None:
  559. # input_splitter attribute is deprecated
  560. assert self.shell is not None
  561. transformer_manager = self.shell.input_splitter
  562. status, indent_spaces = transformer_manager.check_complete(code)
  563. r = {"status": status}
  564. if status == "incomplete":
  565. r["indent"] = " " * indent_spaces
  566. return r
  567. def do_apply(self, content, bufs, msg_id, reply_metadata):
  568. """Handle an apply request."""
  569. try:
  570. from ipyparallel.serialize import serialize_object, unpack_apply_message
  571. except ImportError:
  572. from .serialize import serialize_object, unpack_apply_message
  573. shell = self.shell
  574. assert shell is not None
  575. try:
  576. working = shell.user_ns
  577. prefix = "_" + str(msg_id).replace("-", "") + "_"
  578. f, args, kwargs = unpack_apply_message(bufs, working, copy=False)
  579. fname = getattr(f, "__name__", "f")
  580. fname = prefix + "f"
  581. argname = prefix + "args"
  582. kwargname = prefix + "kwargs"
  583. resultname = prefix + "result"
  584. ns = {fname: f, argname: args, kwargname: kwargs, resultname: None}
  585. # print ns
  586. working.update(ns)
  587. code = f"{resultname} = {fname}(*{argname},**{kwargname})"
  588. try:
  589. exec(code, shell.user_global_ns, shell.user_ns)
  590. result = working.get(resultname)
  591. finally:
  592. for key in ns:
  593. working.pop(key)
  594. assert self.session is not None
  595. result_buf = serialize_object(
  596. result,
  597. buffer_threshold=self.session.buffer_threshold,
  598. item_threshold=self.session.item_threshold,
  599. )
  600. except BaseException as e:
  601. # invoke IPython traceback formatting
  602. shell.showtraceback()
  603. reply_content = {
  604. "traceback": shell._last_traceback or [],
  605. "ename": str(type(e).__name__),
  606. "evalue": str(e),
  607. }
  608. # FIXME: deprecated piece for ipyparallel (remove in 5.0):
  609. e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method="apply")
  610. reply_content["engine_info"] = e_info
  611. self.send_response(
  612. self.iopub_socket,
  613. "error",
  614. reply_content,
  615. ident=self._topic("error"),
  616. )
  617. self.log.info("Exception in apply request:\n%s", "\n".join(reply_content["traceback"]))
  618. result_buf = []
  619. reply_content["status"] = "error"
  620. else:
  621. reply_content = {"status": "ok"}
  622. return reply_content, result_buf
  623. def do_clear(self):
  624. """Clear the kernel."""
  625. if self.shell:
  626. self.shell.reset(False)
  627. return dict(status="ok")
  628. # This exists only for backwards compatibility - use IPythonKernel instead
  629. class Kernel(IPythonKernel):
  630. """DEPRECATED. An alias for the IPython kernel class."""
  631. def __init__(self, *args, **kwargs): # pragma: no cover
  632. """DEPRECATED."""
  633. import warnings
  634. warnings.warn(
  635. "Kernel is a deprecated alias of ipykernel.ipkernel.IPythonKernel",
  636. DeprecationWarning,
  637. stacklevel=2,
  638. )
  639. super().__init__(*args, **kwargs)