17db96d56Sopenharmony_ci"""Subinterpreters High Level Module.""" 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciimport time 47db96d56Sopenharmony_ciimport _xxsubinterpreters as _interpreters 57db96d56Sopenharmony_ci 67db96d56Sopenharmony_ci# aliases: 77db96d56Sopenharmony_cifrom _xxsubinterpreters import ( 87db96d56Sopenharmony_ci ChannelError, ChannelNotFoundError, ChannelEmptyError, 97db96d56Sopenharmony_ci is_shareable, 107db96d56Sopenharmony_ci) 117db96d56Sopenharmony_ci 127db96d56Sopenharmony_ci 137db96d56Sopenharmony_ci__all__ = [ 147db96d56Sopenharmony_ci 'Interpreter', 'get_current', 'get_main', 'create', 'list_all', 157db96d56Sopenharmony_ci 'SendChannel', 'RecvChannel', 167db96d56Sopenharmony_ci 'create_channel', 'list_all_channels', 'is_shareable', 177db96d56Sopenharmony_ci 'ChannelError', 'ChannelNotFoundError', 187db96d56Sopenharmony_ci 'ChannelEmptyError', 197db96d56Sopenharmony_ci ] 207db96d56Sopenharmony_ci 217db96d56Sopenharmony_ci 227db96d56Sopenharmony_cidef create(*, isolated=True): 237db96d56Sopenharmony_ci """Return a new (idle) Python interpreter.""" 247db96d56Sopenharmony_ci id = _interpreters.create(isolated=isolated) 257db96d56Sopenharmony_ci return Interpreter(id, isolated=isolated) 267db96d56Sopenharmony_ci 277db96d56Sopenharmony_ci 287db96d56Sopenharmony_cidef list_all(): 297db96d56Sopenharmony_ci """Return all existing interpreters.""" 307db96d56Sopenharmony_ci return [Interpreter(id) for id in _interpreters.list_all()] 317db96d56Sopenharmony_ci 327db96d56Sopenharmony_ci 337db96d56Sopenharmony_cidef get_current(): 347db96d56Sopenharmony_ci """Return the currently running interpreter.""" 357db96d56Sopenharmony_ci id = _interpreters.get_current() 367db96d56Sopenharmony_ci return Interpreter(id) 377db96d56Sopenharmony_ci 387db96d56Sopenharmony_ci 397db96d56Sopenharmony_cidef get_main(): 407db96d56Sopenharmony_ci """Return the main interpreter.""" 417db96d56Sopenharmony_ci id = _interpreters.get_main() 427db96d56Sopenharmony_ci return Interpreter(id) 437db96d56Sopenharmony_ci 447db96d56Sopenharmony_ci 457db96d56Sopenharmony_ciclass Interpreter: 467db96d56Sopenharmony_ci """A single Python interpreter.""" 477db96d56Sopenharmony_ci 487db96d56Sopenharmony_ci def __init__(self, id, *, isolated=None): 497db96d56Sopenharmony_ci if not isinstance(id, (int, _interpreters.InterpreterID)): 507db96d56Sopenharmony_ci raise TypeError(f'id must be an int, got {id!r}') 517db96d56Sopenharmony_ci self._id = id 527db96d56Sopenharmony_ci self._isolated = isolated 537db96d56Sopenharmony_ci 547db96d56Sopenharmony_ci def __repr__(self): 557db96d56Sopenharmony_ci data = dict(id=int(self._id), isolated=self._isolated) 567db96d56Sopenharmony_ci kwargs = (f'{k}={v!r}' for k, v in data.items()) 577db96d56Sopenharmony_ci return f'{type(self).__name__}({", ".join(kwargs)})' 587db96d56Sopenharmony_ci 597db96d56Sopenharmony_ci def __hash__(self): 607db96d56Sopenharmony_ci return hash(self._id) 617db96d56Sopenharmony_ci 627db96d56Sopenharmony_ci def __eq__(self, other): 637db96d56Sopenharmony_ci if not isinstance(other, Interpreter): 647db96d56Sopenharmony_ci return NotImplemented 657db96d56Sopenharmony_ci else: 667db96d56Sopenharmony_ci return other._id == self._id 677db96d56Sopenharmony_ci 687db96d56Sopenharmony_ci @property 697db96d56Sopenharmony_ci def id(self): 707db96d56Sopenharmony_ci return self._id 717db96d56Sopenharmony_ci 727db96d56Sopenharmony_ci @property 737db96d56Sopenharmony_ci def isolated(self): 747db96d56Sopenharmony_ci if self._isolated is None: 757db96d56Sopenharmony_ci # XXX The low-level function has not been added yet. 767db96d56Sopenharmony_ci # See bpo-.... 777db96d56Sopenharmony_ci self._isolated = _interpreters.is_isolated(self._id) 787db96d56Sopenharmony_ci return self._isolated 797db96d56Sopenharmony_ci 807db96d56Sopenharmony_ci def is_running(self): 817db96d56Sopenharmony_ci """Return whether or not the identified interpreter is running.""" 827db96d56Sopenharmony_ci return _interpreters.is_running(self._id) 837db96d56Sopenharmony_ci 847db96d56Sopenharmony_ci def close(self): 857db96d56Sopenharmony_ci """Finalize and destroy the interpreter. 867db96d56Sopenharmony_ci 877db96d56Sopenharmony_ci Attempting to destroy the current interpreter results 887db96d56Sopenharmony_ci in a RuntimeError. 897db96d56Sopenharmony_ci """ 907db96d56Sopenharmony_ci return _interpreters.destroy(self._id) 917db96d56Sopenharmony_ci 927db96d56Sopenharmony_ci def run(self, src_str, /, *, channels=None): 937db96d56Sopenharmony_ci """Run the given source code in the interpreter. 947db96d56Sopenharmony_ci 957db96d56Sopenharmony_ci This blocks the current Python thread until done. 967db96d56Sopenharmony_ci """ 977db96d56Sopenharmony_ci _interpreters.run_string(self._id, src_str, channels) 987db96d56Sopenharmony_ci 997db96d56Sopenharmony_ci 1007db96d56Sopenharmony_cidef create_channel(): 1017db96d56Sopenharmony_ci """Return (recv, send) for a new cross-interpreter channel. 1027db96d56Sopenharmony_ci 1037db96d56Sopenharmony_ci The channel may be used to pass data safely between interpreters. 1047db96d56Sopenharmony_ci """ 1057db96d56Sopenharmony_ci cid = _interpreters.channel_create() 1067db96d56Sopenharmony_ci recv, send = RecvChannel(cid), SendChannel(cid) 1077db96d56Sopenharmony_ci return recv, send 1087db96d56Sopenharmony_ci 1097db96d56Sopenharmony_ci 1107db96d56Sopenharmony_cidef list_all_channels(): 1117db96d56Sopenharmony_ci """Return a list of (recv, send) for all open channels.""" 1127db96d56Sopenharmony_ci return [(RecvChannel(cid), SendChannel(cid)) 1137db96d56Sopenharmony_ci for cid in _interpreters.channel_list_all()] 1147db96d56Sopenharmony_ci 1157db96d56Sopenharmony_ci 1167db96d56Sopenharmony_ciclass _ChannelEnd: 1177db96d56Sopenharmony_ci """The base class for RecvChannel and SendChannel.""" 1187db96d56Sopenharmony_ci 1197db96d56Sopenharmony_ci def __init__(self, id): 1207db96d56Sopenharmony_ci if not isinstance(id, (int, _interpreters.ChannelID)): 1217db96d56Sopenharmony_ci raise TypeError(f'id must be an int, got {id!r}') 1227db96d56Sopenharmony_ci self._id = id 1237db96d56Sopenharmony_ci 1247db96d56Sopenharmony_ci def __repr__(self): 1257db96d56Sopenharmony_ci return f'{type(self).__name__}(id={int(self._id)})' 1267db96d56Sopenharmony_ci 1277db96d56Sopenharmony_ci def __hash__(self): 1287db96d56Sopenharmony_ci return hash(self._id) 1297db96d56Sopenharmony_ci 1307db96d56Sopenharmony_ci def __eq__(self, other): 1317db96d56Sopenharmony_ci if isinstance(self, RecvChannel): 1327db96d56Sopenharmony_ci if not isinstance(other, RecvChannel): 1337db96d56Sopenharmony_ci return NotImplemented 1347db96d56Sopenharmony_ci elif not isinstance(other, SendChannel): 1357db96d56Sopenharmony_ci return NotImplemented 1367db96d56Sopenharmony_ci return other._id == self._id 1377db96d56Sopenharmony_ci 1387db96d56Sopenharmony_ci @property 1397db96d56Sopenharmony_ci def id(self): 1407db96d56Sopenharmony_ci return self._id 1417db96d56Sopenharmony_ci 1427db96d56Sopenharmony_ci 1437db96d56Sopenharmony_ci_NOT_SET = object() 1447db96d56Sopenharmony_ci 1457db96d56Sopenharmony_ci 1467db96d56Sopenharmony_ciclass RecvChannel(_ChannelEnd): 1477db96d56Sopenharmony_ci """The receiving end of a cross-interpreter channel.""" 1487db96d56Sopenharmony_ci 1497db96d56Sopenharmony_ci def recv(self, *, _sentinel=object(), _delay=10 / 1000): # 10 milliseconds 1507db96d56Sopenharmony_ci """Return the next object from the channel. 1517db96d56Sopenharmony_ci 1527db96d56Sopenharmony_ci This blocks until an object has been sent, if none have been 1537db96d56Sopenharmony_ci sent already. 1547db96d56Sopenharmony_ci """ 1557db96d56Sopenharmony_ci obj = _interpreters.channel_recv(self._id, _sentinel) 1567db96d56Sopenharmony_ci while obj is _sentinel: 1577db96d56Sopenharmony_ci time.sleep(_delay) 1587db96d56Sopenharmony_ci obj = _interpreters.channel_recv(self._id, _sentinel) 1597db96d56Sopenharmony_ci return obj 1607db96d56Sopenharmony_ci 1617db96d56Sopenharmony_ci def recv_nowait(self, default=_NOT_SET): 1627db96d56Sopenharmony_ci """Return the next object from the channel. 1637db96d56Sopenharmony_ci 1647db96d56Sopenharmony_ci If none have been sent then return the default if one 1657db96d56Sopenharmony_ci is provided or fail with ChannelEmptyError. Otherwise this 1667db96d56Sopenharmony_ci is the same as recv(). 1677db96d56Sopenharmony_ci """ 1687db96d56Sopenharmony_ci if default is _NOT_SET: 1697db96d56Sopenharmony_ci return _interpreters.channel_recv(self._id) 1707db96d56Sopenharmony_ci else: 1717db96d56Sopenharmony_ci return _interpreters.channel_recv(self._id, default) 1727db96d56Sopenharmony_ci 1737db96d56Sopenharmony_ci 1747db96d56Sopenharmony_ciclass SendChannel(_ChannelEnd): 1757db96d56Sopenharmony_ci """The sending end of a cross-interpreter channel.""" 1767db96d56Sopenharmony_ci 1777db96d56Sopenharmony_ci def send(self, obj): 1787db96d56Sopenharmony_ci """Send the object (i.e. its data) to the channel's receiving end. 1797db96d56Sopenharmony_ci 1807db96d56Sopenharmony_ci This blocks until the object is received. 1817db96d56Sopenharmony_ci """ 1827db96d56Sopenharmony_ci _interpreters.channel_send(self._id, obj) 1837db96d56Sopenharmony_ci # XXX We are missing a low-level channel_send_wait(). 1847db96d56Sopenharmony_ci # See bpo-32604 and gh-19829. 1857db96d56Sopenharmony_ci # Until that shows up we fake it: 1867db96d56Sopenharmony_ci time.sleep(2) 1877db96d56Sopenharmony_ci 1887db96d56Sopenharmony_ci def send_nowait(self, obj): 1897db96d56Sopenharmony_ci """Send the object to the channel's receiving end. 1907db96d56Sopenharmony_ci 1917db96d56Sopenharmony_ci If the object is immediately received then return True 1927db96d56Sopenharmony_ci (else False). Otherwise this is the same as send(). 1937db96d56Sopenharmony_ci """ 1947db96d56Sopenharmony_ci # XXX Note that at the moment channel_send() only ever returns 1957db96d56Sopenharmony_ci # None. This should be fixed when channel_send_wait() is added. 1967db96d56Sopenharmony_ci # See bpo-32604 and gh-19829. 1977db96d56Sopenharmony_ci return _interpreters.channel_send(self._id, obj) 198