| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  | """Subinterpreters High Level Module.""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  | import time | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  | import _xxsubinterpreters as _interpreters | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # aliases: | 
					
						
							|  |  |  | from _xxsubinterpreters import ( | 
					
						
							|  |  |  |     ChannelError, ChannelNotFoundError, ChannelEmptyError, | 
					
						
							|  |  |  |     is_shareable, | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __all__ = [ | 
					
						
							|  |  |  |     'Interpreter', 'get_current', 'get_main', 'create', 'list_all', | 
					
						
							|  |  |  |     'SendChannel', 'RecvChannel', | 
					
						
							|  |  |  |     'create_channel', 'list_all_channels', 'is_shareable', | 
					
						
							|  |  |  |     'ChannelError', 'ChannelNotFoundError', | 
					
						
							|  |  |  |     'ChannelEmptyError', | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def create(*, isolated=True): | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |     """Return a new (idle) Python interpreter.""" | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |     id = _interpreters.create(isolated=isolated) | 
					
						
							|  |  |  |     return Interpreter(id, isolated=isolated) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def list_all(): | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |     """Return all existing interpreters.""" | 
					
						
							|  |  |  |     return [Interpreter(id) for id in _interpreters.list_all()] | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_current(): | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |     """Return the currently running interpreter.""" | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |     id = _interpreters.get_current() | 
					
						
							|  |  |  |     return Interpreter(id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_main(): | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |     """Return the main interpreter.""" | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |     id = _interpreters.get_main() | 
					
						
							|  |  |  |     return Interpreter(id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Interpreter: | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |     """A single Python interpreter.""" | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, id, *, isolated=None): | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |         if not isinstance(id, (int, _interpreters.InterpreterID)): | 
					
						
							|  |  |  |             raise TypeError(f'id must be an int, got {id!r}') | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |         self._id = id | 
					
						
							|  |  |  |         self._isolated = isolated | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         data = dict(id=int(self._id), isolated=self._isolated) | 
					
						
							|  |  |  |         kwargs = (f'{k}={v!r}' for k, v in data.items()) | 
					
						
							|  |  |  |         return f'{type(self).__name__}({", ".join(kwargs)})' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __hash__(self): | 
					
						
							|  |  |  |         return hash(self._id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __eq__(self, other): | 
					
						
							|  |  |  |         if not isinstance(other, Interpreter): | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return other._id == self._id | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def id(self): | 
					
						
							|  |  |  |         return self._id | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def isolated(self): | 
					
						
							|  |  |  |         if self._isolated is None: | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |             # XXX The low-level function has not been added yet. | 
					
						
							|  |  |  |             # See bpo-.... | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |             self._isolated = _interpreters.is_isolated(self._id) | 
					
						
							|  |  |  |         return self._isolated | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def is_running(self): | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |         """Return whether or not the identified interpreter is running.""" | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |         return _interpreters.is_running(self._id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def close(self): | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |         """Finalize and destroy the interpreter.
 | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |         Attempting to destroy the current interpreter results | 
					
						
							|  |  |  |         in a RuntimeError. | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         return _interpreters.destroy(self._id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def run(self, src_str, /, *, channels=None): | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |         """Run the given source code in the interpreter.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |         This blocks the current Python thread until done. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |         _interpreters.run_string(self._id, src_str, channels) | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def create_channel(): | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |     """Return (recv, send) for a new cross-interpreter channel.
 | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |     The channel may be used to pass data safely between interpreters. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |     cid = _interpreters.channel_create() | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |     recv, send = RecvChannel(cid), SendChannel(cid) | 
					
						
							|  |  |  |     return recv, send | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def list_all_channels(): | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |     """Return a list of (recv, send) for all open channels.""" | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |     return [(RecvChannel(cid), SendChannel(cid)) | 
					
						
							|  |  |  |             for cid in _interpreters.channel_list_all()] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  | class _ChannelEnd: | 
					
						
							|  |  |  |     """The base class for RecvChannel and SendChannel.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, id): | 
					
						
							|  |  |  |         if not isinstance(id, (int, _interpreters.ChannelID)): | 
					
						
							|  |  |  |             raise TypeError(f'id must be an int, got {id!r}') | 
					
						
							|  |  |  |         self._id = id | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         return f'{type(self).__name__}(id={int(self._id)})' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __hash__(self): | 
					
						
							|  |  |  |         return hash(self._id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __eq__(self, other): | 
					
						
							|  |  |  |         if isinstance(self, RecvChannel): | 
					
						
							|  |  |  |             if not isinstance(other, RecvChannel): | 
					
						
							|  |  |  |                 return NotImplemented | 
					
						
							|  |  |  |         elif not isinstance(other, SendChannel): | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							|  |  |  |         return other._id == self._id | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def id(self): | 
					
						
							|  |  |  |         return self._id | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  | _NOT_SET = object() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  | class RecvChannel(_ChannelEnd): | 
					
						
							|  |  |  |     """The receiving end of a cross-interpreter channel.""" | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |     def recv(self, *, _sentinel=object(), _delay=10 / 1000):  # 10 milliseconds | 
					
						
							|  |  |  |         """Return the next object from the channel.
 | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |         This blocks until an object has been sent, if none have been | 
					
						
							|  |  |  |         sent already. | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |         obj = _interpreters.channel_recv(self._id, _sentinel) | 
					
						
							|  |  |  |         while obj is _sentinel: | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |             time.sleep(_delay) | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |             obj = _interpreters.channel_recv(self._id, _sentinel) | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |         return obj | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def recv_nowait(self, default=_NOT_SET): | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |         """Return the next object from the channel.
 | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |         If none have been sent then return the default if one | 
					
						
							|  |  |  |         is provided or fail with ChannelEmptyError.  Otherwise this | 
					
						
							|  |  |  |         is the same as recv(). | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         if default is _NOT_SET: | 
					
						
							|  |  |  |             return _interpreters.channel_recv(self._id) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return _interpreters.channel_recv(self._id, default) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  | class SendChannel(_ChannelEnd): | 
					
						
							|  |  |  |     """The sending end of a cross-interpreter channel.""" | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def send(self, obj): | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |         """Send the object (i.e. its data) to the channel's receiving end.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         This blocks until the object is received. | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         _interpreters.channel_send(self._id, obj) | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |         # XXX We are missing a low-level channel_send_wait(). | 
					
						
							|  |  |  |         # See bpo-32604 and gh-19829. | 
					
						
							|  |  |  |         # Until that shows up we fake it: | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |         time.sleep(2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def send_nowait(self, obj): | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |         """Send the object to the channel's receiving end.
 | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |         If the object is immediately received then return True | 
					
						
							|  |  |  |         (else False).  Otherwise this is the same as send(). | 
					
						
							| 
									
										
										
										
											2020-06-10 00:53:23 -03:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-06-16 18:24:40 -06:00
										 |  |  |         # XXX Note that at the moment channel_send() only ever returns | 
					
						
							|  |  |  |         # None.  This should be fixed when channel_send_wait() is added. | 
					
						
							|  |  |  |         # See bpo-32604 and gh-19829. | 
					
						
							|  |  |  |         return _interpreters.channel_send(self._id, obj) |