interpreters.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. """Subinterpreters High Level Module."""
  2. import time
  3. import _xxsubinterpreters as _interpreters
  4. # aliases:
  5. from _xxsubinterpreters import (
  6. ChannelError, ChannelNotFoundError, ChannelEmptyError,
  7. is_shareable,
  8. )
  9. __all__ = [
  10. 'Interpreter', 'get_current', 'get_main', 'create', 'list_all',
  11. 'SendChannel', 'RecvChannel',
  12. 'create_channel', 'list_all_channels', 'is_shareable',
  13. 'ChannelError', 'ChannelNotFoundError',
  14. 'ChannelEmptyError',
  15. ]
  16. def create(*, isolated=True):
  17. """Return a new (idle) Python interpreter."""
  18. id = _interpreters.create(isolated=isolated)
  19. return Interpreter(id, isolated=isolated)
  20. def list_all():
  21. """Return all existing interpreters."""
  22. return [Interpreter(id) for id in _interpreters.list_all()]
  23. def get_current():
  24. """Return the currently running interpreter."""
  25. id = _interpreters.get_current()
  26. return Interpreter(id)
  27. def get_main():
  28. """Return the main interpreter."""
  29. id = _interpreters.get_main()
  30. return Interpreter(id)
  31. class Interpreter:
  32. """A single Python interpreter."""
  33. def __init__(self, id, *, isolated=None):
  34. if not isinstance(id, (int, _interpreters.InterpreterID)):
  35. raise TypeError(f'id must be an int, got {id!r}')
  36. self._id = id
  37. self._isolated = isolated
  38. def __repr__(self):
  39. data = dict(id=int(self._id), isolated=self._isolated)
  40. kwargs = (f'{k}={v!r}' for k, v in data.items())
  41. return f'{type(self).__name__}({", ".join(kwargs)})'
  42. def __hash__(self):
  43. return hash(self._id)
  44. def __eq__(self, other):
  45. if not isinstance(other, Interpreter):
  46. return NotImplemented
  47. else:
  48. return other._id == self._id
  49. @property
  50. def id(self):
  51. return self._id
  52. @property
  53. def isolated(self):
  54. if self._isolated is None:
  55. # XXX The low-level function has not been added yet.
  56. # See bpo-....
  57. self._isolated = _interpreters.is_isolated(self._id)
  58. return self._isolated
  59. def is_running(self):
  60. """Return whether or not the identified interpreter is running."""
  61. return _interpreters.is_running(self._id)
  62. def close(self):
  63. """Finalize and destroy the interpreter.
  64. Attempting to destroy the current interpreter results
  65. in a RuntimeError.
  66. """
  67. return _interpreters.destroy(self._id)
  68. def run(self, src_str, /, *, channels=None):
  69. """Run the given source code in the interpreter.
  70. This blocks the current Python thread until done.
  71. """
  72. _interpreters.run_string(self._id, src_str, channels)
  73. def create_channel():
  74. """Return (recv, send) for a new cross-interpreter channel.
  75. The channel may be used to pass data safely between interpreters.
  76. """
  77. cid = _interpreters.channel_create()
  78. recv, send = RecvChannel(cid), SendChannel(cid)
  79. return recv, send
  80. def list_all_channels():
  81. """Return a list of (recv, send) for all open channels."""
  82. return [(RecvChannel(cid), SendChannel(cid))
  83. for cid in _interpreters.channel_list_all()]
  84. class _ChannelEnd:
  85. """The base class for RecvChannel and SendChannel."""
  86. def __init__(self, id):
  87. if not isinstance(id, (int, _interpreters.ChannelID)):
  88. raise TypeError(f'id must be an int, got {id!r}')
  89. self._id = id
  90. def __repr__(self):
  91. return f'{type(self).__name__}(id={int(self._id)})'
  92. def __hash__(self):
  93. return hash(self._id)
  94. def __eq__(self, other):
  95. if isinstance(self, RecvChannel):
  96. if not isinstance(other, RecvChannel):
  97. return NotImplemented
  98. elif not isinstance(other, SendChannel):
  99. return NotImplemented
  100. return other._id == self._id
  101. @property
  102. def id(self):
  103. return self._id
  104. _NOT_SET = object()
  105. class RecvChannel(_ChannelEnd):
  106. """The receiving end of a cross-interpreter channel."""
  107. def recv(self, *, _sentinel=object(), _delay=10 / 1000): # 10 milliseconds
  108. """Return the next object from the channel.
  109. This blocks until an object has been sent, if none have been
  110. sent already.
  111. """
  112. obj = _interpreters.channel_recv(self._id, _sentinel)
  113. while obj is _sentinel:
  114. time.sleep(_delay)
  115. obj = _interpreters.channel_recv(self._id, _sentinel)
  116. return obj
  117. def recv_nowait(self, default=_NOT_SET):
  118. """Return the next object from the channel.
  119. If none have been sent then return the default if one
  120. is provided or fail with ChannelEmptyError. Otherwise this
  121. is the same as recv().
  122. """
  123. if default is _NOT_SET:
  124. return _interpreters.channel_recv(self._id)
  125. else:
  126. return _interpreters.channel_recv(self._id, default)
  127. class SendChannel(_ChannelEnd):
  128. """The sending end of a cross-interpreter channel."""
  129. def send(self, obj):
  130. """Send the object (i.e. its data) to the channel's receiving end.
  131. This blocks until the object is received.
  132. """
  133. _interpreters.channel_send(self._id, obj)
  134. # XXX We are missing a low-level channel_send_wait().
  135. # See bpo-32604 and gh-19829.
  136. # Until that shows up we fake it:
  137. time.sleep(2)
  138. def send_nowait(self, obj):
  139. """Send the object to the channel's receiving end.
  140. If the object is immediately received then return True
  141. (else False). Otherwise this is the same as send().
  142. """
  143. # XXX Note that at the moment channel_send() only ever returns
  144. # None. This should be fixed when channel_send_wait() is added.
  145. # See bpo-32604 and gh-19829.
  146. return _interpreters.channel_send(self._id, obj)