[py-dev] outlining what i intend to do with execnet + what additional features could be helpfull

holger krekel holger at merlinux.eu
Mon Aug 24 16:16:26 CEST 2009


On Mon, Aug 24, 2009 at 15:42 +0200, Ronny Pfannschmidt wrote:
> On Mon, 2009-08-24 at 14:34 +0200, holger krekel wrote:
> > Hi Ronny, 
> > 
> > from IRC i also know that you are wondering how/if to best apply 
> > py.execnet for the tasks.  Can you post an example of
> > a frontend command/API you want to implement and some 
> > code of how that translates to one particular API? 
> > I'd then consider and suggest a way/method to apply execnet.
> 
> i think a good initial example for anyvc would be one of the unittests
> 
> def test_build_first_commit(mgr):
>     repo = mgr.make_repo('repo')
>     with repo.transaction(message='initial', author='test') as root:
>         with root.join('test.txt').open('w') as f:
>             f.write("text")
> 
>     with repo.get_default_head() as root:
>         with root.join("test.txt").open() as f:
>             content = f.read()
>             assert content == 'text'
> 
> this test creates a repo, an initial commit, then asserts the content
> mgr is a utility to manage the directories where tests dump their data
> make_repo creates the repository object that should "be" in another
> object when using py.execnet

The execnet spirit is to design your own local protocol.  Let
me give and give some context by means of an example: py/path/gateway 
contains an experimental remote-path implementation, using execnet: 

* remotepath.py: a "RemotePath" class (similar to your frontend "Repo" one)
* channeltest.py: a PathServer backend implementing the fs access code 

Basically the frontend-class is connected through (sub) channels with 
a backend instance.  If you say on the frontend: 

    x = remotepath.join("hello")

this will enter this code on the caller/client/frontend side: 

    def join(self, *args):
        id = ~COUNTER.next()
        self._channel.send(('JOIN', self._id, id) + args)
        return RemotePath(self._channel, id)

which creates a prospective 'id' for the result, sends a command 
to the backend and immediately returns.  This has *zero* latency. 

If you later perform an operation on the returned instance like list()

    def listdir(self, *args):
        self._channel.send(('LIST', self._id) + args)
        return [RemotePath(self._channel, id, basename)
                for (id, basename) in self._channel.receive()]

this will send a LIST command to the backend PathServer which 
itself implements it like this: 

    def command_LIST(self, id, *args):
        path = self.C2P[id]
        answer = [(self.p2c(p), p.basename) for p in path.listdir(*args)]
        self.channel.send(answer)
  
so here we create a local path using the client-provided ID
and instantiate an answer.  C2P and p2c() help to manage the
ID/path relations.   

For implementing our own small custom "command" pattern 
protocol the backend PAthServer implements a simple dispatch
loop based on the frontend/backend connecting channel: 

    def serve(self):
        try:
            while 1:
                msg = self.channel.receive()
                meth = getattr(self, 'command_' + msg[0])
                meth(*msg[1:])
        except EOFError:
            pass

hope this makes sense to you and serves as inspiration.  To summarize:
Design your own protocol, optimized for your special purposes.
You can easily evolve your protocol on a per-need basis -
no need to have global RPC or IDL/RMI interface definitions.
There also is no need for "matching" server/client software etc.
It's a zero-install "local" protocol, also easy to test. 

best,

holger



More information about the Pytest-dev mailing list