diff --git a/docs/api.rst b/docs/api.rst index 5d98b8ce..e0ff7ac5 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -41,6 +41,43 @@ Channel Class :members: +ExternalContext Class +--------------------- + +.. class:: econtext.core.ExternalContext + + External context implementation. + + .. attribute:: broker + + The :py:class:`econtext.core.Broker` instance. + + .. attribute:: context + + The :py:class:`econtext.core.Context` instance. + + .. attribute:: channel + + The :py:class:`econtext.core.Channel` over which + :py:data:`CALL_FUNCTION` requests are received. + + .. attribute:: stdout_log + + The :py:class:`econtext.core.IoLogger` connected to ``stdout``. + + .. attribute:: importer + + The :py:class:`econtext.core.Importer` instance. + + .. attribute:: stdout_log + + The :py:class:`IoLogger` connected to ``stdout``. + + .. attribute:: stderr_log + + The :py:class:`IoLogger` connected to ``stderr``. + + econtext.master =============== @@ -55,6 +92,13 @@ Helper Functions .. autofunction:: econtext.master.minimize_source +Context Class +------------- + +.. autoclass:: econtext.master.Context + :members: + + Broker Class ------------ diff --git a/docs/howitworks.rst b/docs/howitworks.rst index de1d6646..52cd6c5c 100644 --- a/docs/howitworks.rst +++ b/docs/howitworks.rst @@ -120,7 +120,7 @@ Since the bootstrap consists of the :py:mod:`econtext.core` source code, and this code is loaded by Python by way of its main script (``__main__`` module), initially the module layout in the slave will be incorrect. -The first step taken after bootstrap is to rearrange ``sys.modules`` slightly +The first step taken after bootstrap is to rearrange :py:data:`sys.modules` slightly so that :py:mod:`econtext.core` appears in the correct location, and all classes defined in that module have their ``__module__`` attribute fixed up such that :py:mod:`cPickle` correctly serializes instance module names. diff --git a/docs/index.rst b/docs/index.rst index 7080f005..7f9a44f0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -56,8 +56,8 @@ connection. $ python preamble_size.py SSH command size: 411 - Preamble size: 5085 (4.96KiB) - econtext.master size: 2403 (2.35KiB) + Preamble size: 4892 (4.78KiB) + econtext.master size: 2627 (2.57KiB) Once bootstrapped, the remote process is configured with a customizable **argv[0]**, readily visible to system administrators of the remote machine diff --git a/econtext/core.py b/econtext/core.py index 963db99e..ac8de96f 100644 --- a/econtext/core.py +++ b/econtext/core.py @@ -22,7 +22,6 @@ import sys import threading import time import traceback -import types import zlib @@ -471,25 +470,6 @@ class Context(object): IOLOG.debug('%r._enqueue_await_reply(): got reply: %r', self, data) return data - def call_with_deadline(self, deadline, with_context, fn, *args, **kwargs): - LOG.debug('%r.call_with_deadline(%r, %r, %r, *%r, **%r)', - self, deadline, with_context, fn, args, kwargs) - - if isinstance(fn, types.MethodType) and \ - isinstance(fn.im_self, (type, types.ClassType)): - klass = fn.im_self.__name__ - else: - klass = None - - call = (with_context, fn.__module__, klass, fn.__name__, args, kwargs) - result = self.enqueue_await_reply(CALL_FUNCTION, deadline, call) - if isinstance(result, CallError): - raise result - return result - - def call(self, fn, *args, **kwargs): - return self.call_with_deadline(None, False, fn, *args, **kwargs) - def __repr__(self): bits = filter(None, (self.name, self.hostname, self.username)) return 'Context(%s)' % ', '.join(map(repr, bits)) diff --git a/econtext/master.py b/econtext/master.py index 5f22a31a..6a5046a4 100644 --- a/econtext/master.py +++ b/econtext/master.py @@ -14,6 +14,7 @@ import re import socket import sys import textwrap +import types import zlib import econtext.core @@ -271,3 +272,32 @@ class Context(econtext.core.Context): def on_disconnect(self): self.stream = None + + def call_with_deadline(self, deadline, with_context, fn, *args, **kwargs): + """Invoke `fn([context,] *args, **kwargs)` in the external context. + + If `with_context` is True, pass its + :py:class:`econtext.core.ExternalContext` instance as first parameter. + + If `deadline` is not ``None``, expire the call after `deadline` + seconds. If `deadline` is ``None``, the invocation may block + indefinitely.""" + LOG.debug('%r.call_with_deadline(%r, %r, %r, *%r, **%r)', + self, deadline, with_context, fn, args, kwargs) + + if isinstance(fn, types.MethodType) and \ + isinstance(fn.im_self, (type, types.ClassType)): + klass = fn.im_self.__name__ + else: + klass = None + + call = (with_context, fn.__module__, klass, fn.__name__, args, kwargs) + result = self.enqueue_await_reply(econtext.core.CALL_FUNCTION, + deadline, call) + if isinstance(result, econtext.core.CallError): + raise result + return result + + def call(self, fn, *args, **kwargs): + """Invoke `fn(*args, **kwargs)` in the external context.""" + return self.call_with_deadline(None, False, fn, *args, **kwargs)