shellchannel.py 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. """A thread for a shell channel."""
  2. from __future__ import annotations
  3. import asyncio
  4. from threading import current_thread
  5. from typing import Any
  6. import zmq
  7. from .subshell_manager import SubshellManager
  8. from .thread import SHELL_CHANNEL_THREAD_NAME, BaseThread
  9. class ShellChannelThread(BaseThread):
  10. """A thread for a shell channel.
  11. Communicates with shell/subshell threads via pairs of ZMQ inproc sockets.
  12. """
  13. def __init__(
  14. self,
  15. context: zmq.Context[Any],
  16. shell_socket: zmq.Socket[Any],
  17. **kwargs,
  18. ):
  19. """Initialize the thread."""
  20. super().__init__(name=SHELL_CHANNEL_THREAD_NAME, **kwargs)
  21. self._manager: SubshellManager | None = None
  22. self._zmq_context = context # Avoid use of self._context
  23. self._shell_socket = shell_socket
  24. # Record the parent thread - the thread that started the app (usually the main thread)
  25. self.parent_thread = current_thread()
  26. self.asyncio_lock = asyncio.Lock()
  27. @property
  28. def manager(self) -> SubshellManager:
  29. # Lazy initialisation.
  30. if self._manager is None:
  31. assert current_thread() == self.parent_thread
  32. self._manager = SubshellManager(
  33. self._zmq_context,
  34. self.io_loop,
  35. self._shell_socket,
  36. )
  37. return self._manager
  38. def run(self) -> None:
  39. """Run the thread."""
  40. try:
  41. super().run()
  42. finally:
  43. if self._manager:
  44. self._manager.close()