More doc updates.

pull/35/head
David Wilson 9 years ago
parent 1f27872eda
commit 1bc8681cb4

@ -9,27 +9,27 @@ History
The first version of econtext was written in late 2006 for use in an The first version of econtext was written in late 2006 for use in an
infrastructure management program, however at the time I lacked the pragmatism infrastructure management program, however at the time I lacked the pragmatism
necessary for pushing my little design from concept to a working necessary for pushing my little design from concept to a working
implementation. I tired of it when I couldn't find a way to combine every implementation. I tired of it when no way could be found to combine every
communication style (*blocking execute function, asynchronous execute function, communication style (*blocking execute function, asynchronous execute function,
proxy slave-of-slave context*) into one neat abstraction. That unification proxy slave-of-slave context*) into one neat abstraction. That unification
still has not happened, but I'm no longer as burdened by the problem. still has not happened, but I'm no longer as burdened by it.
Every few years I would pick through the source code, especially after periods Every few years I would pick through the source code, especially after periods
of working commercially with some contemporary management systems, none of of working commercially with some contemporary infrastructure management
which in the meantime had anything close to as neat an approach to running systems, none of which had anything close to as neat an approach to running
Python code on remote machines, and suffered from shockingly beginner-level Python code on remote machines, and suffered from shockingly beginner-level
bugs such as failing to report SSH diagnostic messages. bugs such as failing to report SSH diagnostic messages.
And every few years I'd put that code down again, especially since moving to an And every few years I'd put that code down again, especially since moving to an
OS X laptop where :py:func:`select.poll` was not available, the struggle to get OS X laptop where :py:func:`select.poll` was not available, the struggle to get
back on top of the project seemed more hassle than it was worth. back on top seemed more hassle than it was worth.
That changed in 2016 during a quiet evening at home with a clear head and That changed in 2016 during a quiet evening at home with a clear head and
nothing better to do, after a full day of exposure to Ansible's intensely nothing better to do, after a full day of exposure to Ansible's intensely
unbearable tendency to make running a 50 line Python script across a 1Gbit/sec unbearable tendency to make running a 50 line Python script across a 1Gbit/sec
LAN feel like I were configuring a host on Mars. Poking through what Ansible LAN feel like I were configuring a host on Mars. Poking through Ansible, I was
was doing, I was shocked to discover it writing temporary files everywhere, and shocked to discover it writing temporary files everywhere, and uploading a
uploading a 56KiB zip file apparently for every playbook step. 56KiB zip file apparently for every playbook step.
.. figure:: _static/wtf.gif .. figure:: _static/wtf.gif
@ -42,8 +42,8 @@ write the IO and log forwarders, rewrite the module importer, move from
:py:func:`select.poll` to :py:func:`select.select`, and even refactor the :py:func:`select.poll` to :py:func:`select.select`, and even refactor the
special cases out of the main loop. special cases out of the main loop.
So there you have it. As of writing :py:mod:`econtext.core` consists of 550 So there you have it. As of writing :py:mod:`econtext.core` consists of 528
source lines, and those 550 lines have taken me almost a decade to write. I source lines, and those 528 lines have taken me almost a decade to write. I
have long had a preference for avoiding infrastructure work commercially, not have long had a preference for avoiding infrastructure work commercially, not
least for the inescapable depression induced by considering the wasted effort least for the inescapable depression induced by considering the wasted effort
across the world caused by universally horrific tooling. This is my small across the world caused by universally horrific tooling. This is my small

@ -195,6 +195,26 @@ written to by
and :py:meth:`call() <econtext.master.Context.call>`. and :py:meth:`call() <econtext.master.Context.call>`.
Shutdown
########
When the master signals the :py:data:`CALL_FUNCTION
<econtext.core.CALL_FUNCTION>` :py:class:`Channel <econtext.core.Channel>` is
closed, the slave calls :py:meth:`shutdown() <econtext.core.Broker.shutdown>`
followed by :py:meth:`wait() <econtext.core.Broker.wait>` on its own broker,
triggering graceful shutdown.
During shutdown, the master will wait a few seconds for slaves to disconnect
gracefully before force disconnecting them, while the slaves will use that time
to call :py:meth:`socket.shutdown(SHUT_WR) <socket.socket.shutdown>` on their
:py:class:`IoLogger <econtext.core.IoLogger>` socket's write ends before
draining any remaining data buffered on the read ends.
If the main thread (responsible for function call dispatch) fails to trigger
shutdown (because some user function is hanging), then the eventual force
disconnection by the master will cause the IO multiplexer thread to enter
shutdown by itself.
Stream Protocol Stream Protocol
--------------- ---------------
@ -239,17 +259,22 @@ Slaves listen on the following handles:
imports ``mod_name``, then attempts to execute imports ``mod_name``, then attempts to execute
`class_name.func_name(\*args, \**kwargs)`. `class_name.func_name(\*args, \**kwargs)`.
.. data:: econtext.core.SHUTDOWN
Triggers :py:meth:`econtext.core.Broker.shutdown` remotely, causing the
slave to drain its :py:class:`IoLoggers <econtext.core.IoLogger>` and
output stream buffer before disconnecting from the master and terminating
the process.
Additional handles are created to receive the result of every function call Additional handles are created to receive the result of every function call
triggered by :py:meth:`call_with_deadline() <econtext.master.Context.call_with_deadline>`. triggered by :py:meth:`call_with_deadline() <econtext.master.Context.call_with_deadline>`.
Sentinel Value
##############
.. autodata:: econtext.core._DEAD
The special value :py:data:`econtext.core._DEAD` is used to signal
disconnection or closure of the remote end. It is used internally by
:py:class:`Channel <econtext.core.Channel>` and also passed to any function
still registered with :py:meth:`add_handle_cb()
<econtext.core.Context.add_handle_cb>` during Broker shutdown.
Use of Pickle Use of Pickle
############# #############
@ -261,13 +286,14 @@ serialization code in the bootstrap.
The pickler active in slave contexts will instantiate any class, however in the The pickler active in slave contexts will instantiate any class, however in the
master it is initially restricted to only permitting master it is initially restricted to only permitting
:py:class:`econtext.core.CallError`. While not recommended, it is possible to :py:class:`CallError <econtext.core.CallError>` and :py:data:`_DEAD
register more using :py:meth:`econtext.master.LocalStream.allow_class`. <econtext.core._DEAD>`. While not recommended, it is possible to register more
using :py:meth:`econtext.master.LocalStream.allow_class`.
The choice of pickle is one area to be revisited later. All accounts suggest it The choice of Pickle is one area to be revisited later. All accounts suggest it
cannot be used securely, however few of those accounts appear to be expert cannot be used securely, however few of those accounts appear to be expert, and
opinions, and none mention any additional attacks that would not be prevented none mention any additional attacks that would not be prevented by using a
by using a restrictive class whitelist. restrictive class whitelist.
Use of HMAC Use of HMAC
@ -348,15 +374,15 @@ package.
Child Module Enumeration Child Module Enumeration
######################## ########################
Package children are enumerated using the :py:mod:`pkgutil` module. Package children are enumerated using :py:func:`pkgutil.iter_modules`.
Use Of Threads Use Of Threads
-------------- --------------
The package mandatorily runs the IO multiplexer in a thread. This is so the The package always runs the IO multiplexer in a thread. This is so the
multiplexer always retains control flow in order to shut down gracefully, say, multiplexer retains control flow in order to shut down gracefully, say, if the
if the user's code has hung and the master context has disconnected. user's code has hung and the master context has disconnected.
While it is possible for the IO multiplexer to recover control of a hung While it is possible for the IO multiplexer to recover control of a hung
function call on UNIX using for example :py:mod:`signal.SIGALRM <signal>`, this function call on UNIX using for example :py:mod:`signal.SIGALRM <signal>`, this

@ -18,8 +18,8 @@ Introduction
------------ ------------
The Python ``econtext`` package implements external *execution contexts*: an The Python ``econtext`` package implements external *execution contexts*: an
execution context is somewhere you can run Python code external to your main execution context is somewhere you can run Python functions external to your
process, even on a remote machine. main process, even on a remote machine.
There is **no requirement for installing packages, copying files around, There is **no requirement for installing packages, copying files around,
writing shell scripts, upfront configuration, or providing any secondary link writing shell scripts, upfront configuration, or providing any secondary link

@ -1,9 +1,6 @@
""" """
On the econtext master, this is imported from ``econtext/__init__.py`` as would On the econtext master, this is imported from ``econtext/__init__.py`` as would
be expected. On the slave, it is built dynamically during startup. be expected. On the slave, it is built dynamically during startup.
As a convenience, the econtext package exports all of the functions and
variables from :py:mod:`econtext.core`.
""" """
#: This is ``True`` in slave contexts. It is used in single-file Python #: This is ``True`` in slave contexts. It is used in single-file Python
@ -24,5 +21,3 @@ variables from :py:mod:`econtext.core`.
#: econtext.utils.run_with_broker(main) #: econtext.utils.run_with_broker(main)
#: #:
slave = False slave = False
from econtext.core import * # NOQA

@ -71,6 +71,7 @@ class Dead(object):
return '<Dead>' return '<Dead>'
#: Sentinel value used to represent Channel disconnection.
_DEAD = Dead() _DEAD = Dead()
@ -656,8 +657,6 @@ class ExternalContext(object):
sys.modules['econtext'] = econtext sys.modules['econtext'] = econtext
sys.modules['econtext.core'] = econtext.core sys.modules['econtext.core'] = econtext.core
exec 'from econtext.core import *' in vars(econtext)
for klass in vars(econtext.core).itervalues(): for klass in vars(econtext.core).itervalues():
if hasattr(klass, '__module__'): if hasattr(klass, '__module__'):
klass.__module__ = 'econtext.core' klass.__module__ = 'econtext.core'

Loading…
Cancel
Save