Skip to content
Next Next commit
PEP 554 for use in test suite
  • Loading branch information
nanjekyejoannah committed May 7, 2020
commit 02086f767dd1f5cafb9d9de12b5f17023ea8e0e9
147 changes: 147 additions & 0 deletions Doc/library/_interpreters.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
:mod:`_interpreters` --- High-level Subinterpreters Module
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated
==========================================================

.. module:: _interpreters
:synopsis: High-level Subinterpreters Module.

**Source code:** :source:`Lib/test/support/_interpreters.py`

--------------

This module provides high-level tools for working with sub-interpreters,
such as creating them, running code in them, or sending data between them.
It is a wrapper around the low-level `_xxsubinterpreters` module.

.. versionchanged:: added in 3.9

Interpreter Objects
-------------------

The Interpreter object represents a single interpreter.
.. class:: Interpreter(id)

The class implementing a subinterpreter object.

.. method:: is_running()

Return `True` if the identified interpreter is running.

.. method:: close()

Destroy the interpreter. Attempting to destroy the current
interpreter results in a `RuntimeError`.

.. method:: run(self, src_str, /, *, channels=None):

Run the given source code in the interpreter. This blocks
the current thread until done. `channels` should be in
the form : `(RecvChannel, SendChannel)`.

RecvChannel Objects
-------------------

The RecvChannel object represents a recieving channel.

.. class:: RecvChannel(id)

This class represents the receiving end of a channel.

.. method:: recv()

Get the next object from the channel, and wait if
none have been sent. Associate the interpreter
with the channel.

.. method:: recv_nowait(default=None)

Like ``recv()``, but return the default result
instead of waiting.


SendChannel Objects
--------------------

The ``SendChannel`` object represents a sending channel.

.. class:: SendChannel(id)

This class represents the sending end of a channel.

.. method:: send(obj)

Send the object ``obj`` to the receiving end of the channel
and wait. Associate the interpreter with the channel.

.. method:: send_nowait(obj)

Similar to ``send()``, but returns ``False`` if
*obj* is not immediately received instead of blocking.


This module defines the following global functions:


.. function:: is_shareable(obj)

Return ``True`` if the object's data can be shared between
interpreters.

.. function:: create_channel()

Create a new channel for passing data between interpreters.

.. function:: list_all_channels()

Return all open channels.

.. function:: create(*, isolated=True)

Initialize a new (idle) Python interpreter. Get the currently
running interpreter. This method returns an ``Interpreter`` object.

.. function:: get_current()

Get the currently running interpreter. This method returns
an ``Interpreter`` object.

.. function:: get_main()

Get the main interpreter. This method returns
an ``Interpreter`` object.

.. function:: list_all()

Get all existing interpreters. Returns a list
of ``Interpreter`` objects.

This module also defines the following exceptions.

.. exception:: RunFailedError

This exception, a subclass of :exc:`RuntimeError`, is raised when the
``Interpreter.run()`` results in an uncaught exception.

.. exception:: ChannelError

This exception is a subclass of :exc:`Exception`, and is the base
class for all channel-related exceptions.

.. exception:: ChannelNotFoundError

This exception is a subclass of :exc:`ChannelError`, and is raised
when the the identified channel is not found.

.. exception:: ChannelEmptyError

This exception is a subclass of :exc:`ChannelError`, and is raised when
the channel is unexpectedly empty.

.. exception:: ChannelNotEmptyError

This exception is a subclass of :exc:`ChannelError`, and is raised when
the channel is unexpectedly not empty.

.. exception:: NotReceivedError

This exception is a subclass of :exc:`ChannelError`, and is raised when
nothing was waiting to receive a sent object.
235 changes: 235 additions & 0 deletions Lib/test/support/_interpreters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
"""Subinterpreters High Level Module."""
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated

import logging
import _xxsubinterpreters as _interpreters

__all__ = ['Interpreter', 'SendChannel', 'RecvChannel',
'is_shareable', 'create_channel',
'list_all_channels', 'get_current',
'get_current', 'create']


def create(*, isolated=True):
""" create() -> Interpreter

Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated
Initialize a new (idle) Python interpreter.
"""
id = _interpreters.create()
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated
return Interpreter(id)
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated

def list_all():
""" list_all() -> [Interpreter]

Get all existing interpreters.
"""
return [Interpreter(id) for id in
_interpreters.list_all()]

def get_current():
""" get_current() -> Interpreter

Get the currently running interpreter.
"""
id = _interpreters.get_current()
return Interpreter(id)

def get_main():
""" get_main() -> Interpreter

Get the main interpreter.
"""
id = _interpreters.get_main()
return Interpreter(id)


class Interpreter:
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated

def __init__(self, id):
self._id = id
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def __init__(self, id):
self._id = id
def __init__(self, id, *, isolated=None):
self._id = id
self._isolated = isolated


@property
def id(self):
return self._id

Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated
def is_running(self):
"""is_running() -> bool

Return whether or not the identified
interpreter is running.
"""
return _interpreters.is_running(self._id)

def close(self):
"""close()

Finalize and destroy the interpreter.

Attempting to destroy the current
interpreter results in a RuntimeError.
"""
return _interpreters.destroy(self._id)

def run(self, src_str, /, *, channels=None):
"""run(src_str, /, *, channels=None)

Run the given source code in the interpreter.
This blocks the current thread until done.
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated
"""
try:
_interpreters.run_string(self._id, src_str)
except RunFailedError as err:
logger.error(err)
raise
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated


def is_shareable(obj):
""" is_shareable(obj) -> Bool

Return `True` if the object's data can be
shared between interpreters and `False` otherwise.
"""
return _interpreters.is_shareable(obj)

def create_channel():
""" create_channel() -> (RecvChannel, SendChannel)

Create a new channel for passing data between
interpreters.
"""

cid = _interpreters.channel_create()
return (RecvChannel(cid), SendChannel(cid))

def list_all_channels():
""" list_all_channels() -> [(RecvChannel, SendChannel)]

Get all open channels.
"""
return [(RecvChannel(cid), SendChannel(cid))
for cid in _interpreters.channel_list_all()]

def wait(timeout):
#The implementation for wait
# will be non trivial to be useful
import time
time.sleep(timeout)

Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated
class RecvChannel:

def __init__(self, id):
self.id = id

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ID should be read-only.

Suggested change
def __init__(self, id):
self.id = id
def __init__(self, id):
self._id = id
@property
def id(self):
return self._id

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making this readonly makes it hard to return RecvCnannel in methods like create_channel(). what do you think?

def recv(self):
""" channel_recv() -> obj

Get the next object from the channel,
and wait if none have been sent.
Associate the interpreter with the channel.
"""
try:
obj = _interpreters.channel_recv(self.id)
if obj == None:
wait(2)
obj = _interpreters.channel_recv(self.id)
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated
except _interpreters.ChannelEmptyError:
raise ChannelEmptyError
except _interpreters.ChannelNotFoundError:
raise ChannelNotFoundError
except _interpreters.ChannelClosedError:
raise ChannelClosedError
except _interpreters.RunFailedError:
raise RunFailedError
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated
return obj

def recv_nowait(self, default=None):
"""recv_nowait(default=None) -> object

Like recv(), but return the default
instead of waiting.
"""
try:
obj = _interpreters.channel_recv(self.id)
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated
except _interpreters.ChannelEmptyError:
raise ChannelEmptyError
except _interpreters.ChannelNotFoundError:
raise ChannelNotFoundError
except _interpreters.ChannelClosedError:
raise ChannelClosedError
except _interpreters.RunFailedError:
raise RunFailedError
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated
return obj


class SendChannel:
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated

def __init__(self, id):
self.id = id
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ID should be read-only.

Suggested change
def __init__(self, id):
self.id = id
def __init__(self, id):
self._id = id
@property
def id(self):
return self._id

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above. Making this readonly makes it hard to return SendChannel in methods like create_channel() . what do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're only read-only to users. Regardless, I'm not clear on why we would change the value ever, even in create_channel().


def send(self, obj):
""" send(obj)

Send the object (i.e. its data) to the receiving
end of the channel and wait. Associate the interpreter
with the channel.
"""
try:
_interpreters.channel_send(self.id, obj)
wait(2)
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated
except _interpreters.ChannelNotFoundError:
raise ChannelNotFoundError
except _interpreters.ChannelClosedError:
raise ChannelClosedError
except _interpreters.RunFailedError:
raise RunFailedError
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated

def send_nowait(self, obj):
""" send_nowait(obj)

Like send(), but return False if not received.
"""
try:
_interpreters.channel_send(self.id, obj)
except _interpreters.ChannelNotFoundError:
raise ChannelNotFoundError
except _interpreters.ChannelClosedError:
raise ChannelClosedError
except _interpreters.RunFailedError:
raise RunFailedError
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated

recv_obj = _interpreters.channel_recv(self.id)
if recv_obj:
return obj
else:
return False
Comment thread
nanjekyejoannah marked this conversation as resolved.
Outdated


class ChannelError(Exception):
pass


class ChannelNotFoundError(ChannelError):
pass


class ChannelEmptyError(ChannelError):
pass


class ChannelNotEmptyError(ChannelError):
pass


class NotReceivedError(ChannelError):
pass


class ChannelClosedError(ChannelError):
pass


class ChannelReleasedError(ChannelClosedError):
pass


class RunFailedError(RuntimeError):
pass
Loading