|
2 | 2 | py.execnet: *elastic* distributed programming |
3 | 3 | ============================================================================== |
4 | 4 |
|
5 | | -``execnet`` helps you to: |
| 5 | +Since pylib 1.1 "py.execnet" is separated out of hte lib and now |
| 6 | +available through the standalone `execnet standalone package`_. |
6 | 7 |
|
7 | | -* ad-hoc instantiate local or remote Python Processes |
8 | | -* send code for execution in one or many processes |
9 | | -* send and receive data between processes through channels |
10 | | - |
11 | | -One of it's unique features is that it uses a **zero-install** |
12 | | -technique: no manual installation steps are required on |
13 | | -remote places, only a basic working Python interpreter |
14 | | -and some input/output connection to it. |
15 | | - |
16 | | -There is a `EuroPython2009 talk`_ from July 2009 with |
17 | | -examples and some pictures. |
18 | | - |
19 | | -.. contents:: |
20 | | - :local: |
21 | | - :depth: 2 |
22 | | - |
23 | | -.. _`EuroPython2009 talk`: http://codespeak.net/download/py/ep2009-execnet.pdf |
24 | | - |
25 | | -Gateways: immediately spawn local or remote process |
26 | | -=================================================== |
27 | | - |
28 | | -In order to send code to a remote place or a subprocess |
29 | | -you need to instantiate a so-called Gateway object. |
30 | | -There are currently three Gateway classes: |
31 | | - |
32 | | -* :api:`py.execnet.PopenGateway` to open a subprocess |
33 | | - on the local machine. Useful for making use |
34 | | - of multiple processors to to contain code execution |
35 | | - in a separated environment. |
36 | | - |
37 | | -* :api:`py.execnet.SshGateway` to connect to |
38 | | - a remote ssh server and distribute execution to it. |
39 | | - |
40 | | -* :api:`py.execnet.SocketGateway` a way to connect to |
41 | | - a remote Socket based server. *Note* that this method |
42 | | - requires a manually started |
43 | | - :source:py/execnet/script/socketserver.py |
44 | | - script. You can run this "server script" without |
45 | | - having the py lib installed on the remote system |
46 | | - and you can setup it up as permanent service. |
47 | | - |
48 | | - |
49 | | -remote_exec: execute source code remotely |
50 | | -=================================================== |
51 | | - |
52 | | -All gateways offer remote code execution via this high level function:: |
53 | | - |
54 | | - def remote_exec(source): |
55 | | - """return channel object for communicating with the asynchronously |
56 | | - executing 'source' code which will have a corresponding 'channel' |
57 | | - object in its executing namespace.""" |
58 | | - |
59 | | -With `remote_exec` you send source code to the other |
60 | | -side and get both a local and a remote Channel_ object, |
61 | | -which you can use to have the local and remote site |
62 | | -communicate data in a structured way. Here is |
63 | | -an example for reading the PID:: |
64 | | - |
65 | | - >>> import py |
66 | | - >>> gw = py.execnet.PopenGateway() |
67 | | - >>> channel = gw.remote_exec(""" |
68 | | - ... import os |
69 | | - ... channel.send(os.getpid()) |
70 | | - ... """) |
71 | | - >>> remote_pid = channel.receive() |
72 | | - >>> remote_pid != py.std.os.getpid() |
73 | | - True |
74 | | - |
75 | | -.. _`Channel`: |
76 | | -.. _`channel-api`: |
77 | | -.. _`exchange data`: |
78 | | - |
79 | | -Channels: bidirectionally exchange data between hosts |
80 | | -======================================================= |
81 | | - |
82 | | -A channel object allows to send and receive data between |
83 | | -two asynchronously running programs. When calling |
84 | | -`remote_exec` you will get a channel object back and |
85 | | -the code fragment running on the other side will |
86 | | -see a channel object in its global namespace. |
87 | | - |
88 | | -Here is the interface of channel objects:: |
89 | | - |
90 | | - # |
91 | | - # API for sending and receiving anonymous values |
92 | | - # |
93 | | - channel.send(item): |
94 | | - sends the given item to the other side of the channel, |
95 | | - possibly blocking if the sender queue is full. |
96 | | - Note that items need to be marshallable (all basic |
97 | | - python types are). |
98 | | - |
99 | | - channel.receive(): |
100 | | - receives an item that was sent from the other side, |
101 | | - possibly blocking if there is none. |
102 | | - Note that exceptions from the other side will be |
103 | | - reraised as gateway.RemoteError exceptions containing |
104 | | - a textual representation of the remote traceback. |
105 | | - |
106 | | - channel.waitclose(timeout=None): |
107 | | - wait until this channel is closed. Note that a closed |
108 | | - channel may still hold items that will be received or |
109 | | - send. Note that exceptions from the other side will be |
110 | | - reraised as gateway.RemoteError exceptions containing |
111 | | - a textual representation of the remote traceback. |
112 | | - |
113 | | - channel.close(): |
114 | | - close this channel on both the local and the remote side. |
115 | | - A remote side blocking on receive() on this channel |
116 | | - will get woken up and see an EOFError exception. |
117 | | - |
118 | | - |
119 | | -.. _xspec: |
120 | | - |
121 | | - |
122 | | -XSpec: string specification for gateway type and configuration |
123 | | -=============================================================== |
124 | | - |
125 | | -``py.execnet`` supports a simple extensible format for |
126 | | -specifying and configuring Gateways for remote execution. |
127 | | -You can use a string specification to instantiate a new gateway, |
128 | | -for example a new SshGateway:: |
129 | | - |
130 | | - gateway = py.execnet.makegateway("ssh=myhost") |
131 | | - |
132 | | -Let's look at some examples for valid specifications. |
133 | | -Specification for an ssh connection to `wyvern`, running on python2.4 in the (newly created) 'mycache' subdirectory:: |
134 | | - |
135 | | - ssh=wyvern//python=python2.4//chdir=mycache |
136 | | - |
137 | | -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'):: |
138 | | - |
139 | | - popen//python=2.5//nice=20 |
140 | | - |
141 | | -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:: |
142 | | - |
143 | | - socket=192.168.1.4:8888 |
144 | | - |
145 | | -More generally, a specification string has this general format:: |
146 | | - |
147 | | - key1=value1//key2=value2//key3=value3 |
148 | | - |
149 | | -If you omit a value, a boolean true value is assumed. Currently |
150 | | -the following key/values are supported: |
151 | | - |
152 | | -* ``popen`` for a PopenGateway |
153 | | -* ``ssh=host`` for a SshGateway |
154 | | -* ``socket=address:port`` for a SocketGateway |
155 | | -* ``python=executable`` for specifying Python executables |
156 | | -* ``chdir=path`` change remote working dir to given relative or absolute path |
157 | | -* ``nice=value`` decrease remote nice level if platforms supports it |
158 | | - |
159 | | - |
160 | | -Examples of py.execnet usage |
161 | | -=============================================================== |
162 | | - |
163 | | -Compare cwd() of Popen Gateways |
164 | | ----------------------------------------- |
165 | | - |
166 | | -A PopenGateway has the same working directory as the instantiatior:: |
167 | | - |
168 | | - >>> import py, os |
169 | | - >>> gw = py.execnet.PopenGateway() |
170 | | - >>> ch = gw.remote_exec("import os; channel.send(os.getcwd())") |
171 | | - >>> res = ch.receive() |
172 | | - >>> assert res == os.getcwd() |
173 | | - >>> gw.exit() |
174 | | - |
175 | | -Synchronously receive results from two sub processes |
176 | | ------------------------------------------------------ |
177 | | - |
178 | | -Use MultiChannels for receiving multiple results from remote code:: |
179 | | - |
180 | | - >>> import py |
181 | | - >>> ch1 = py.execnet.PopenGateway().remote_exec("channel.send(1)") |
182 | | - >>> ch2 = py.execnet.PopenGateway().remote_exec("channel.send(2)") |
183 | | - >>> mch = py.execnet.MultiChannel([ch1, ch2]) |
184 | | - >>> l = mch.receive_each() |
185 | | - >>> assert len(l) == 2 |
186 | | - >>> assert 1 in l |
187 | | - >>> assert 2 in l |
188 | | - |
189 | | -Asynchronously receive results from two sub processes |
190 | | ------------------------------------------------------ |
191 | | - |
192 | | -Use ``MultiChannel.make_receive_queue()`` for asynchronously receiving |
193 | | -multiple results from remote code. This standard Queue provides |
194 | | -``(channel, result)`` tuples which allows to determine where |
195 | | -a result comes from:: |
196 | | - |
197 | | - >>> import py |
198 | | - >>> ch1 = py.execnet.PopenGateway().remote_exec("channel.send(1)") |
199 | | - >>> ch2 = py.execnet.PopenGateway().remote_exec("channel.send(2)") |
200 | | - >>> mch = py.execnet.MultiChannel([ch1, ch2]) |
201 | | - >>> queue = mch.make_receive_queue() |
202 | | - >>> chan1, res1 = queue.get() # you may also specify a timeout |
203 | | - >>> chan2, res2 = queue.get() |
204 | | - >>> res1 + res2 |
205 | | - 3 |
206 | | - >>> assert chan1 in (ch1, ch2) |
207 | | - >>> assert chan2 in (ch1, ch2) |
208 | | - >>> assert chan1 != chan2 |
209 | | - |
210 | | -Receive file contents from remote SSH account |
211 | | ------------------------------------------------------ |
212 | | - |
213 | | -Here is a small program that you can use to retrieve |
214 | | -contents of remote files:: |
215 | | - |
216 | | - import py |
217 | | - # open a gateway to a fresh child process |
218 | | - gw = py.execnet.SshGateway('codespeak.net') |
219 | | - channel = gw.remote_exec(""" |
220 | | - for fn in channel: |
221 | | - f = open(fn, 'rb') |
222 | | - channel.send(f.read()) |
223 | | - f.close() |
224 | | - """) |
225 | | - |
226 | | - for fn in somefilelist: |
227 | | - channel.send(fn) |
228 | | - content = channel.receive() |
229 | | - # process content |
230 | | - |
231 | | - # later you can exit / close down the gateway |
232 | | - gw.exit() |
233 | | - |
234 | | - |
235 | | -Instantiate a socket server in a new subprocess |
236 | | ------------------------------------------------------ |
237 | | - |
238 | | -The following example opens a PopenGateway, i.e. a python |
239 | | -child process, and starts a socket server within that process |
240 | | -and then opens a second gateway to the freshly started |
241 | | -socketserver:: |
242 | | - |
243 | | - import py |
244 | | - |
245 | | - popengw = py.execnet.PopenGateway() |
246 | | - socketgw = py.execnet.SocketGateway.new_remote(popengw, ("127.0.0.1", 0)) |
247 | | - |
248 | | - print socketgw._rinfo() # print some info about the remote environment |
249 | | - |
250 | | - |
251 | | -Sending a module / checking if run through remote_exec |
252 | | --------------------------------------------------------------- |
253 | | - |
254 | | -You can pass a module object to ``remote_exec`` in which case |
255 | | -its source code will be sent. No dependencies will be transferred |
256 | | -so the module must be self-contained or only use modules that are |
257 | | -installed on the "other" side. Module code can detect if it is |
258 | | -running in a remote_exec situation by checking for the special |
259 | | -``__name__`` attribute like this:: |
260 | | - |
261 | | - if __name__ == '__channelexec__': |
262 | | - # ... call module functions ... |
263 | | - |
| 8 | +If you have usages of the "py.execnet.*" 1.0 API you can likely |
| 9 | +rename all occurences of the string ``py.execnet.`` with the |
| 10 | +string ``execnet.``. |
264 | 11 |
|
| 12 | +.. _`execnet standalone package`: http://codespeak.net/execnet |
0 commit comments