mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			258 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			258 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""Subinterpreters High Level Module."""
 | 
						|
 | 
						|
import threading
 | 
						|
import weakref
 | 
						|
import _interpreters
 | 
						|
 | 
						|
# aliases:
 | 
						|
from _interpreters import (
 | 
						|
    InterpreterError, InterpreterNotFoundError, NotShareableError,
 | 
						|
    is_shareable,
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
__all__ = [
 | 
						|
    'get_current', 'get_main', 'create', 'list_all', 'is_shareable',
 | 
						|
    'Interpreter',
 | 
						|
    'InterpreterError', 'InterpreterNotFoundError', 'ExecutionFailed',
 | 
						|
    'NotShareableError',
 | 
						|
    'create_queue', 'Queue', 'QueueEmpty', 'QueueFull',
 | 
						|
]
 | 
						|
 | 
						|
 | 
						|
_queuemod = None
 | 
						|
 | 
						|
def __getattr__(name):
 | 
						|
    if name in ('Queue', 'QueueEmpty', 'QueueFull', 'create_queue'):
 | 
						|
        global create_queue, Queue, QueueEmpty, QueueFull
 | 
						|
        ns = globals()
 | 
						|
        from .queues import (
 | 
						|
            create as create_queue,
 | 
						|
            Queue, QueueEmpty, QueueFull,
 | 
						|
        )
 | 
						|
        return ns[name]
 | 
						|
    else:
 | 
						|
        raise AttributeError(name)
 | 
						|
 | 
						|
 | 
						|
_EXEC_FAILURE_STR = """
 | 
						|
{superstr}
 | 
						|
 | 
						|
Uncaught in the interpreter:
 | 
						|
 | 
						|
{formatted}
 | 
						|
""".strip()
 | 
						|
 | 
						|
class ExecutionFailed(InterpreterError):
 | 
						|
    """An unhandled exception happened during execution.
 | 
						|
 | 
						|
    This is raised from Interpreter.exec() and Interpreter.call().
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, excinfo):
 | 
						|
        msg = excinfo.formatted
 | 
						|
        if not msg:
 | 
						|
            if excinfo.type and excinfo.msg:
 | 
						|
                msg = f'{excinfo.type.__name__}: {excinfo.msg}'
 | 
						|
            else:
 | 
						|
                msg = excinfo.type.__name__ or excinfo.msg
 | 
						|
        super().__init__(msg)
 | 
						|
        self.excinfo = excinfo
 | 
						|
 | 
						|
    def __str__(self):
 | 
						|
        try:
 | 
						|
            formatted = self.excinfo.errdisplay
 | 
						|
        except Exception:
 | 
						|
            return super().__str__()
 | 
						|
        else:
 | 
						|
            return _EXEC_FAILURE_STR.format(
 | 
						|
                superstr=super().__str__(),
 | 
						|
                formatted=formatted,
 | 
						|
            )
 | 
						|
 | 
						|
 | 
						|
def create():
 | 
						|
    """Return a new (idle) Python interpreter."""
 | 
						|
    id = _interpreters.create(reqrefs=True)
 | 
						|
    return Interpreter(id, _ownsref=True)
 | 
						|
 | 
						|
 | 
						|
def list_all():
 | 
						|
    """Return all existing interpreters."""
 | 
						|
    return [Interpreter(id, _whence=whence)
 | 
						|
            for id, whence in _interpreters.list_all(require_ready=True)]
 | 
						|
 | 
						|
 | 
						|
def get_current():
 | 
						|
    """Return the currently running interpreter."""
 | 
						|
    id, whence = _interpreters.get_current()
 | 
						|
    return Interpreter(id, _whence=whence)
 | 
						|
 | 
						|
 | 
						|
def get_main():
 | 
						|
    """Return the main interpreter."""
 | 
						|
    id, whence = _interpreters.get_main()
 | 
						|
    assert whence == _interpreters.WHENCE_RUNTIME, repr(whence)
 | 
						|
    return Interpreter(id, _whence=whence)
 | 
						|
 | 
						|
 | 
						|
_known = weakref.WeakValueDictionary()
 | 
						|
 | 
						|
class Interpreter:
 | 
						|
    """A single Python interpreter.
 | 
						|
 | 
						|
    Attributes:
 | 
						|
 | 
						|
    "id" - the unique process-global ID number for the interpreter
 | 
						|
    "whence" - indicates where the interpreter was created
 | 
						|
 | 
						|
    If the interpreter wasn't created by this module
 | 
						|
    then any method that modifies the interpreter will fail,
 | 
						|
    i.e. .close(), .prepare_main(), .exec(), and .call()
 | 
						|
    """
 | 
						|
 | 
						|
    _WHENCE_TO_STR = {
 | 
						|
       _interpreters.WHENCE_UNKNOWN: 'unknown',
 | 
						|
       _interpreters.WHENCE_RUNTIME: 'runtime init',
 | 
						|
       _interpreters.WHENCE_LEGACY_CAPI: 'legacy C-API',
 | 
						|
       _interpreters.WHENCE_CAPI: 'C-API',
 | 
						|
       _interpreters.WHENCE_XI: 'cross-interpreter C-API',
 | 
						|
       _interpreters.WHENCE_STDLIB: '_interpreters module',
 | 
						|
    }
 | 
						|
 | 
						|
    def __new__(cls, id, /, _whence=None, _ownsref=None):
 | 
						|
        # There is only one instance for any given ID.
 | 
						|
        if not isinstance(id, int):
 | 
						|
            raise TypeError(f'id must be an int, got {id!r}')
 | 
						|
        id = int(id)
 | 
						|
        if _whence is None:
 | 
						|
            if _ownsref:
 | 
						|
                _whence = _interpreters.WHENCE_STDLIB
 | 
						|
            else:
 | 
						|
                _whence = _interpreters.whence(id)
 | 
						|
        assert _whence in cls._WHENCE_TO_STR, repr(_whence)
 | 
						|
        if _ownsref is None:
 | 
						|
            _ownsref = (_whence == _interpreters.WHENCE_STDLIB)
 | 
						|
        try:
 | 
						|
            self = _known[id]
 | 
						|
            assert hasattr(self, '_ownsref')
 | 
						|
        except KeyError:
 | 
						|
            self = super().__new__(cls)
 | 
						|
            _known[id] = self
 | 
						|
            self._id = id
 | 
						|
            self._whence = _whence
 | 
						|
            self._ownsref = _ownsref
 | 
						|
            if _ownsref:
 | 
						|
                # This may raise InterpreterNotFoundError:
 | 
						|
                _interpreters.incref(id)
 | 
						|
        return self
 | 
						|
 | 
						|
    def __repr__(self):
 | 
						|
        return f'{type(self).__name__}({self.id})'
 | 
						|
 | 
						|
    def __hash__(self):
 | 
						|
        return hash(self._id)
 | 
						|
 | 
						|
    def __del__(self):
 | 
						|
        self._decref()
 | 
						|
 | 
						|
    # for pickling:
 | 
						|
    def __getnewargs__(self):
 | 
						|
        return (self._id,)
 | 
						|
 | 
						|
    # for pickling:
 | 
						|
    def __getstate__(self):
 | 
						|
        return None
 | 
						|
 | 
						|
    def _decref(self):
 | 
						|
        if not self._ownsref:
 | 
						|
            return
 | 
						|
        self._ownsref = False
 | 
						|
        try:
 | 
						|
            _interpreters.decref(self._id)
 | 
						|
        except InterpreterNotFoundError:
 | 
						|
            pass
 | 
						|
 | 
						|
    @property
 | 
						|
    def id(self):
 | 
						|
        return self._id
 | 
						|
 | 
						|
    @property
 | 
						|
    def whence(self):
 | 
						|
        return self._WHENCE_TO_STR[self._whence]
 | 
						|
 | 
						|
    def is_running(self):
 | 
						|
        """Return whether or not the identified interpreter is running."""
 | 
						|
        return _interpreters.is_running(self._id)
 | 
						|
 | 
						|
    # Everything past here is available only to interpreters created by
 | 
						|
    # interpreters.create().
 | 
						|
 | 
						|
    def close(self):
 | 
						|
        """Finalize and destroy the interpreter.
 | 
						|
 | 
						|
        Attempting to destroy the current interpreter results
 | 
						|
        in an InterpreterError.
 | 
						|
        """
 | 
						|
        return _interpreters.destroy(self._id, restrict=True)
 | 
						|
 | 
						|
    def prepare_main(self, ns=None, /, **kwargs):
 | 
						|
        """Bind the given values into the interpreter's __main__.
 | 
						|
 | 
						|
        The values must be shareable.
 | 
						|
        """
 | 
						|
        ns = dict(ns, **kwargs) if ns is not None else kwargs
 | 
						|
        _interpreters.set___main___attrs(self._id, ns, restrict=True)
 | 
						|
 | 
						|
    def exec(self, code, /):
 | 
						|
        """Run the given source code in the interpreter.
 | 
						|
 | 
						|
        This is essentially the same as calling the builtin "exec"
 | 
						|
        with this interpreter, using the __dict__ of its __main__
 | 
						|
        module as both globals and locals.
 | 
						|
 | 
						|
        There is no return value.
 | 
						|
 | 
						|
        If the code raises an unhandled exception then an ExecutionFailed
 | 
						|
        exception is raised, which summarizes the unhandled exception.
 | 
						|
        The actual exception is discarded because objects cannot be
 | 
						|
        shared between interpreters.
 | 
						|
 | 
						|
        This blocks the current Python thread until done.  During
 | 
						|
        that time, the previous interpreter is allowed to run
 | 
						|
        in other threads.
 | 
						|
        """
 | 
						|
        excinfo = _interpreters.exec(self._id, code, restrict=True)
 | 
						|
        if excinfo is not None:
 | 
						|
            raise ExecutionFailed(excinfo)
 | 
						|
 | 
						|
    def call(self, callable, /):
 | 
						|
        """Call the object in the interpreter with given args/kwargs.
 | 
						|
 | 
						|
        Only functions that take no arguments and have no closure
 | 
						|
        are supported.
 | 
						|
 | 
						|
        The return value is discarded.
 | 
						|
 | 
						|
        If the callable raises an exception then the error display
 | 
						|
        (including full traceback) is send back between the interpreters
 | 
						|
        and an ExecutionFailed exception is raised, much like what
 | 
						|
        happens with Interpreter.exec().
 | 
						|
        """
 | 
						|
        # XXX Support args and kwargs.
 | 
						|
        # XXX Support arbitrary callables.
 | 
						|
        # XXX Support returning the return value (e.g. via pickle).
 | 
						|
        excinfo = _interpreters.call(self._id, callable, restrict=True)
 | 
						|
        if excinfo is not None:
 | 
						|
            raise ExecutionFailed(excinfo)
 | 
						|
 | 
						|
    def call_in_thread(self, callable, /):
 | 
						|
        """Return a new thread that calls the object in the interpreter.
 | 
						|
 | 
						|
        The return value and any raised exception are discarded.
 | 
						|
        """
 | 
						|
        def task():
 | 
						|
            self.call(callable)
 | 
						|
        t = threading.Thread(target=task)
 | 
						|
        t.start()
 | 
						|
        return t
 |