Skip to content

Commit

Permalink
remove py.execnet, substitute py.execnet usages with "execnet" ones.
Browse files Browse the repository at this point in the history
--HG--
branch : trunk
  • Loading branch information
hpk42 committed Oct 2, 2009
1 parent 496e3b1 commit ab9f6a7
Show file tree
Hide file tree
Showing 54 changed files with 66 additions and 3,851 deletions.
2 changes: 1 addition & 1 deletion bin-for-dist/gensetup.py
Expand Up @@ -3,7 +3,7 @@
sys.path.insert(0, sys.argv[1])
import py

toolpath = py.magic.autopath()
toolpath = py.path.local(__file__)
binpath = py.path.local(py.__file__).dirpath('bin')

def error(msg):
Expand Down
2 changes: 1 addition & 1 deletion bin-for-dist/test_install.py
Expand Up @@ -78,7 +78,7 @@ def create(self, sitepackages=False):

def makegateway(self):
python = self._cmd('python')
return py.execnet.makegateway("popen//python=%s" %(python,))
return execnet.makegateway("popen//python=%s" %(python,))

def pcall(self, cmd, *args, **kw):
self.ensure()
Expand Down
9 changes: 6 additions & 3 deletions conftest.py
Expand Up @@ -17,15 +17,18 @@ def pytest_addoption(parser):

def pytest_funcarg__specssh(request):
return getspecssh(request.config)
def pytest_funcarg__specsocket(request):
return getsocketspec(request.config)
def getgspecs(config=None):
if config is None:
config = py.test.config
return [execnet.XSpec(spec)
for spec in config.getvalueorskip("gspecs")]


# configuration information for tests
def getgspecs(config=None):
if config is None:
config = py.test.config
return [py.execnet.XSpec(spec)
return [execnet.XSpec(spec)
for spec in config.getvalueorskip("gspecs")]

def getspecssh(config=None):
Expand Down
4 changes: 2 additions & 2 deletions contrib/svn-sync-repo.py
Expand Up @@ -3,7 +3,7 @@
"""
small utility for hot-syncing a svn repository through ssh.
uses py.execnet.
uses execnet.
"""

Expand Down Expand Up @@ -105,7 +105,7 @@ def get_svn_youngest(repo):
return int(rev)

def getgateway(host, keyfile=None):
return py.execnet.SshGateway(host, identity=keyfile)
return execnet.SshGateway(host, identity=keyfile)

if __name__ == '__main__':
if len(sys.argv) < 3:
Expand Down
2 changes: 1 addition & 1 deletion contrib/sysinfo.py
Expand Up @@ -95,7 +95,7 @@ def error(*args):
def getinfo(sshname, ssh_config=None, loginfo=sys.stdout):
debug("connecting to", sshname)
try:
gw = py.execnet.SshGateway(sshname, ssh_config=ssh_config)
gw = execnet.SshGateway(sshname, ssh_config=ssh_config)
except IOError:
error("could not get sshagteway", sshname)
else:
Expand Down
2 changes: 2 additions & 0 deletions doc/changelog.txt
@@ -1,6 +1,8 @@
Changes between 1.0.x and 'trunk'
=====================================

* remove py.execnet code and substitute all usages with 'execnet' proper

* fix issue50 - cached_setup now caches more to expectations
for test functions with multiple arguments.

Expand Down
264 changes: 6 additions & 258 deletions doc/execnet.txt
Expand Up @@ -2,263 +2,11 @@
py.execnet: *elastic* distributed programming
==============================================================================

``execnet`` helps you to:
Since pylib 1.1 "py.execnet" is separated out of hte lib and now
available through the standalone `execnet standalone package`_.

* ad-hoc instantiate local or remote Python Processes
* send code for execution in one or many processes
* send and receive data between processes through channels

One of it's unique features is that it uses a **zero-install**
technique: no manual installation steps are required on
remote places, only a basic working Python interpreter
and some input/output connection to it.

There is a `EuroPython2009 talk`_ from July 2009 with
examples and some pictures.

.. contents::
:local:
:depth: 2

.. _`EuroPython2009 talk`: http://codespeak.net/download/py/ep2009-execnet.pdf

Gateways: immediately spawn local or remote process
===================================================

In order to send code to a remote place or a subprocess
you need to instantiate a so-called Gateway object.
There are currently three Gateway classes:

* :api:`py.execnet.PopenGateway` to open a subprocess
on the local machine. Useful for making use
of multiple processors to to contain code execution
in a separated environment.

* :api:`py.execnet.SshGateway` to connect to
a remote ssh server and distribute execution to it.

* :api:`py.execnet.SocketGateway` a way to connect to
a remote Socket based server. *Note* that this method
requires a manually started
:source:py/execnet/script/socketserver.py
script. You can run this "server script" without
having the py lib installed on the remote system
and you can setup it up as permanent service.


remote_exec: execute source code remotely
===================================================

All gateways offer remote code execution via this high level function::

def remote_exec(source):
"""return channel object for communicating with the asynchronously
executing 'source' code which will have a corresponding 'channel'
object in its executing namespace."""

With `remote_exec` you send source code to the other
side and get both a local and a remote Channel_ object,
which you can use to have the local and remote site
communicate data in a structured way. Here is
an example for reading the PID::

>>> import py
>>> gw = py.execnet.PopenGateway()
>>> channel = gw.remote_exec("""
... import os
... channel.send(os.getpid())
... """)
>>> remote_pid = channel.receive()
>>> remote_pid != py.std.os.getpid()
True

.. _`Channel`:
.. _`channel-api`:
.. _`exchange data`:

Channels: bidirectionally exchange data between hosts
=======================================================

A channel object allows to send and receive data between
two asynchronously running programs. When calling
`remote_exec` you will get a channel object back and
the code fragment running on the other side will
see a channel object in its global namespace.

Here is the interface of channel objects::

#
# API for sending and receiving anonymous values
#
channel.send(item):
sends the given item to the other side of the channel,
possibly blocking if the sender queue is full.
Note that items need to be marshallable (all basic
python types are).

channel.receive():
receives an item that was sent from the other side,
possibly blocking if there is none.
Note that exceptions from the other side will be
reraised as gateway.RemoteError exceptions containing
a textual representation of the remote traceback.

channel.waitclose(timeout=None):
wait until this channel is closed. Note that a closed
channel may still hold items that will be received or
send. Note that exceptions from the other side will be
reraised as gateway.RemoteError exceptions containing
a textual representation of the remote traceback.

channel.close():
close this channel on both the local and the remote side.
A remote side blocking on receive() on this channel
will get woken up and see an EOFError exception.


.. _xspec:


XSpec: string specification for gateway type and configuration
===============================================================

``py.execnet`` supports a simple extensible format for
specifying and configuring Gateways for remote execution.
You can use a string specification to instantiate a new gateway,
for example a new SshGateway::

gateway = py.execnet.makegateway("ssh=myhost")

Let's look at some examples for valid specifications.
Specification for an ssh connection to `wyvern`, running on python2.4 in the (newly created) 'mycache' subdirectory::

ssh=wyvern//python=python2.4//chdir=mycache

Specification of a python2.5 subprocess; with a low CPU priority ("nice" level). Current dir will be the current dir of the instantiator (that's true for all 'popen' specifications unless they specify 'chdir')::

popen//python=2.5//nice=20

Specification of a Python Socket server process that listens on 192.168.1.4:8888; current dir will be the 'pyexecnet-cache' sub directory which is used a default for all remote processes::

socket=192.168.1.4:8888

More generally, a specification string has this general format::

key1=value1//key2=value2//key3=value3

If you omit a value, a boolean true value is assumed. Currently
the following key/values are supported:

* ``popen`` for a PopenGateway
* ``ssh=host`` for a SshGateway
* ``socket=address:port`` for a SocketGateway
* ``python=executable`` for specifying Python executables
* ``chdir=path`` change remote working dir to given relative or absolute path
* ``nice=value`` decrease remote nice level if platforms supports it


Examples of py.execnet usage
===============================================================

Compare cwd() of Popen Gateways
----------------------------------------

A PopenGateway has the same working directory as the instantiatior::

>>> import py, os
>>> gw = py.execnet.PopenGateway()
>>> ch = gw.remote_exec("import os; channel.send(os.getcwd())")
>>> res = ch.receive()
>>> assert res == os.getcwd()
>>> gw.exit()

Synchronously receive results from two sub processes
-----------------------------------------------------

Use MultiChannels for receiving multiple results from remote code::

>>> import py
>>> ch1 = py.execnet.PopenGateway().remote_exec("channel.send(1)")
>>> ch2 = py.execnet.PopenGateway().remote_exec("channel.send(2)")
>>> mch = py.execnet.MultiChannel([ch1, ch2])
>>> l = mch.receive_each()
>>> assert len(l) == 2
>>> assert 1 in l
>>> assert 2 in l

Asynchronously receive results from two sub processes
-----------------------------------------------------

Use ``MultiChannel.make_receive_queue()`` for asynchronously receiving
multiple results from remote code. This standard Queue provides
``(channel, result)`` tuples which allows to determine where
a result comes from::

>>> import py
>>> ch1 = py.execnet.PopenGateway().remote_exec("channel.send(1)")
>>> ch2 = py.execnet.PopenGateway().remote_exec("channel.send(2)")
>>> mch = py.execnet.MultiChannel([ch1, ch2])
>>> queue = mch.make_receive_queue()
>>> chan1, res1 = queue.get() # you may also specify a timeout
>>> chan2, res2 = queue.get()
>>> res1 + res2
3
>>> assert chan1 in (ch1, ch2)
>>> assert chan2 in (ch1, ch2)
>>> assert chan1 != chan2

Receive file contents from remote SSH account
-----------------------------------------------------

Here is a small program that you can use to retrieve
contents of remote files::

import py
# open a gateway to a fresh child process
gw = py.execnet.SshGateway('codespeak.net')
channel = gw.remote_exec("""
for fn in channel:
f = open(fn, 'rb')
channel.send(f.read())
f.close()
""")

for fn in somefilelist:
channel.send(fn)
content = channel.receive()
# process content

# later you can exit / close down the gateway
gw.exit()


Instantiate a socket server in a new subprocess
-----------------------------------------------------

The following example opens a PopenGateway, i.e. a python
child process, and starts a socket server within that process
and then opens a second gateway to the freshly started
socketserver::

import py

popengw = py.execnet.PopenGateway()
socketgw = py.execnet.SocketGateway.new_remote(popengw, ("127.0.0.1", 0))

print socketgw._rinfo() # print some info about the remote environment


Sending a module / checking if run through remote_exec
--------------------------------------------------------------

You can pass a module object to ``remote_exec`` in which case
its source code will be sent. No dependencies will be transferred
so the module must be self-contained or only use modules that are
installed on the "other" side. Module code can detect if it is
running in a remote_exec situation by checking for the special
``__name__`` attribute like this::

if __name__ == '__channelexec__':
# ... call module functions ...

If you have usages of the "py.execnet.*" 1.0 API you can likely
rename all occurences of the string ``py.execnet.`` with the
string ``execnet.``.

.. _`execnet standalone package`: http://codespeak.net/execnet
2 changes: 1 addition & 1 deletion doc/test/customize.txt
Expand Up @@ -364,7 +364,7 @@ remote environment. For this you can implement the newgateway hook:
def pytest_gwmanage_newgateway(gateway, platinfo):
""" called after a gateway is instantiated. """

The ``gateway`` object here has a ``spec`` attribute which is an ``py.execnet.XSpec``
The ``gateway`` object here has a ``spec`` attribute which is an ``execnet.XSpec``
object, which has attributes that map key/values as specified from a ``--txspec``
option. The platinfo object is a dictionary with information about the remote process:

Expand Down
2 changes: 1 addition & 1 deletion doc/test/funcargs.txt
Expand Up @@ -165,7 +165,7 @@ and to offer a new mysetup method:
host = self.config.option.ssh
if host is None:
py.test.skip("specify ssh host with --ssh")
return py.execnet.SshGateway(host)
return execnet.SshGateway(host)


Now any test function can use the ``mysetup.getsshconnection()`` method like this:
Expand Down
4 changes: 2 additions & 2 deletions example/execnet/popen_read_multiple.py
Expand Up @@ -9,7 +9,7 @@

channels = []
for i in range(NUM_PROCESSES):
gw = py.execnet.PopenGateway() # or use SSH or socket gateways
gw = execnet.PopenGateway() # or use SSH or socket gateways
channel = gw.remote_exec("""
import time
secs = channel.receive()
Expand All @@ -19,7 +19,7 @@
channels.append(channel)
print "*** instantiated subprocess", gw

mc = py.execnet.MultiChannel(channels)
mc = execnet.MultiChannel(channels)
queue = mc.make_receive_queue()

print "***", "verifying that timeout on receiving results from blocked subprocesses works"
Expand Down
2 changes: 1 addition & 1 deletion example/execnet/redirect_remote_output.py
Expand Up @@ -10,7 +10,7 @@

import py

gw = py.execnet.PopenGateway()
gw = execnet.PopenGateway()

outchan = gw.remote_exec("""
import sys
Expand Down
2 changes: 1 addition & 1 deletion example/execnet/svn-sync-repo.py
Expand Up @@ -82,7 +82,7 @@ def get_svn_youngest(repo):
return int(rev)

def getgateway(host, keyfile=None):
return py.execnet.SshGateway(host, identity=keyfile)
return execnet.SshGateway(host, identity=keyfile)

if __name__ == '__main__':
if len(sys.argv) < 3:
Expand Down

0 comments on commit ab9f6a7

Please sign in to comment.