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