[hackers] [wmii] [python] Rephrase async client request chains as coroutines. || Kris Maglione

From: <hg_AT_suckless.org>
Date: Sat, 10 Jul 2010 00:00:13 +0000 (UTC)

changeset: 2761:8661a94bfc27
user: Kris Maglione <kris_AT_suckless.org>
date: Fri Jul 09 17:48:46 2010 -0400
files: alternative_wmiircs/python/pyxp/asyncclient.py alternative_wmiircs/python/pyxp/client.py alternative_wmiircs/python/pyxp/fields.py alternative_wmiircs/python/pyxp/messages.py
description:
[python] Rephrase async client request chains as coroutines.

diff -r af47966054cb -r 8661a94bfc27 alternative_wmiircs/python/pyxp/asyncclient.py
--- a/alternative_wmiircs/python/pyxp/asyncclient.py Fri Jul 09 17:17:39 2010 -0400
+++ b/alternative_wmiircs/python/pyxp/asyncclient.py Fri Jul 09 17:48:46 2010 -0400
@@ -2,196 +2,194 @@
 from pyxp.client import *
 from functools import wraps
 
-def awithfile(*oargs, **okwargs):
- def wrapper(fn):
- @wraps(fn)
- def next(self, path, *args, **kwargs):
- def next(file, exc, tb):
- fn(self, (file, exc, tb), *args, **kwargs)
- self.aopen(path, next, *oargs, **okwargs)
- return next
+def send(iter, val, default=None):
+ try:
+ return iter.send(val)
+ except StopIteration:
+ return default
+
+def awithfile(fn):
+ @wraps(fn)
+ def wrapper(self, path, *args, **kwargs):
+ gen = fn(self, *args, **kwargs)
+ callback, fail, mode = next(gen)
+ def cont(file):
+ send(gen, file)
+ self.aopen(path, cont, fail=fail or callback, mode=mode)
     return wrapper
 
-def wrap_callback(fn, file):
- def callback(data, exc, tb):
- file.close()
- Client.respond(fn, data, exc, tb)
- return callback
+def requestchain(fn):
+ @wraps(fn)
+ def wrapper(self, *args, **kwargs):
+ gen = fn(self, *args, **kwargs)
+ callback, fail = next(gen)
+
+ def cont(val):
+ data = gen.send(val)
+ if isinstance(data, fcall.Fcall):
+ self._dorpc(data, cont, fail or callback)
+ else:
+ Client.respond(callback, data)
+ cont(None)
+ return wrapper
 
 class Client(client.Client):
     ROOT_FID = 0
 
- def _awalk(self, path, callback, fail=None):
- path = self._splitpath(path)
- ctxt = dict(path=path, fid=self._getfid(), ofid=ROOT_FID)
+ def _awalk(fn):
+ @wraps(fn)
+ @requestchain
+ def wrapper(self, *args, **kwargs):
+ gen = fn(self, *args, **kwargs)
+ path, callback, fail = next(gen)
 
- def next(resp=None, exc=None, tb=None):
- if exc and ctxt['ofid'] != ROOT_FID:
- self._aclunk(ctxt['fid'])
- ctxt['fid'] = None
+ path = self._splitpath(path)
+ fid = self._getfid()
+ ofid = ROOT_FID
 
- if not ctxt['path'] and resp or exc:
- return self.respond(fail if exc and fail else callback,
- ctxt['fid'], exc, tb)
+ def fail_(resp, exc, tb):
+ if ofid != ROOT_FID:
+ self._aclunk(fid)
+ self.respond(fail or callback, resp, exc, tb)
+ yield callback, fail_
 
- wname = ctxt['path'][:fcall.MAX_WELEM]
- ofid = ctxt['ofid']
- ctxt['path'] = ctxt['path'][fcall.MAX_WELEM:]
- if resp:
- ctxt['ofid'] = ctxt['fid']
+ while path:
+ wname = path[:fcall.MAX_WELEM]
+ path = path[fcall.MAX_WELEM:]
 
- self._dorpc(fcall.Twalk(fid=ofid, newfid=ctxt['fid'], wname=wname),
- next)
- next()
+ resp = yield fcall.Twalk(fid=ofid, newfid=fid, wname=wname)
+ ofid = fid
+
+ resp = fid
+ while resp is not None:
+ resp = yield send(gen, resp)
+
+ return wrapper
 
     _file = property(lambda self: File)
+
+ @_awalk
     def _aopen(self, path, mode, fcall, callback, fail=None, origpath=None):
         path = self._splitpath(path)
 
- def next(fid, exc, tb):
- def next(resp, exc, tb):
- file = self._file(self, origpath or '/'.join(path), resp, fid, mode,
- cleanup=lambda: self._clunk(fid))
- self.respond(callback, file)
- fcall.fid = fid
- self._dorpc(fcall, next, fail or callback)
- self._awalk(path, next, fail or callback)
+ fcall.fid = yield path, callback, fail
+ resp = yield fcall
+ yield self._file(self, origpath or '/'.join(path), resp, fcall.fid, mode,
+ cleanup=lambda: self._clunk(fcall.fid))
 
     def aopen(self, path, callback=True, fail=None, mode=OREAD):
         assert callable(callback)
- return self._aopen(path, mode, fcall.Topen(mode=mode),
- callback, fail)
+ self._aopen(path, mode, fcall.Topen(mode=mode), callback, fail)
 
     def acreate(self, path, callback=True, fail=None, mode=OREAD, perm=0):
         path = self._splitpath(path)
         name = path.pop()
 
- if not callable(callback):
- callback = lambda resp: resp and resp.close()
+ self._aopen(path, mode,
+ fcall.Tcreate(mode=mode, name=name, perm=perm),
+ callback if callable(callback) else lambda resp: resp and resp.close(),
+ fail, origpath='/'.join(path + [name]))
 
- return self._aopen(path, mode, fcall.Tcreate(mode=mode, name=name, perm=perm),
- callback, fail, origpath='/'.join(path + [name]))
+ @_awalk
+ def aremove(self, path, callback=True, fail=None):
+ yield fcall.Tremove(fid=(yield path, callback, fail))
 
- def aremove(self, path, callback=True, fail=None):
- def next(fid):
- self._dorpc(fcall.Tremove(fid=fid), callback, fail)
- self._awalk(path, next, fail or callback)
+ @_awalk
+ def astat(self, path, callback, fail=None):
+ resp = yield fcall.Tstat(fid=(yield path, callback, fail))
+ yield resp.stat
 
- def astat(self, path, callback, fail=None):
- def next(fid):
- def next(resp):
- self.respond(callback, resp.stat)
- self._dorpc(fcall.Tstat(fid=fid), next, fail or callback)
- self._awalk(self, next, fail or callback)
+ @awithfile
+ def aread(self, callback, fail=None, count=None, offset=None, buf=''):
+ file = yield callback, fail, OREAD
+ file.aread(callback, fail, count, offset, buf)
 
- @awithfile()
- def aread(self, (file, exc, tb), callback, *args, **kwargs):
- if exc:
- self.respond(callback, file, exc, tb)
- else:
- file.aread(wrap_callback(callback, file), *args, **kwargs)
+ @awithfile
+ def awrite(self, data, callback=True, fail=None, offset=None):
+ file = yield callback, fail, OWRITE
+ file.awrite(data, callback, fail, offset)
 
- @awithfile(mode=OWRITE)
- def awrite(self, (file, exc, tb), data, callback=True, *args, **kwargs):
- if exc:
- self.respond(callback, file, exc, tb)
- else:
- file.awrite(data, wrap_callback(callback, file), *args, **kwargs)
-
- @awithfile()
- def areadlines(self, (file, exc, tb), fn):
- def callback(resp):
- if resp is None:
- file.close()
- if fn(resp) is False:
- file.close()
- return False
- if exc:
- callback(None)
- else:
- file.sreadlines(callback)
+ @awithfile
+ def areadlines(self, callback):
+ file = yield callback, fail, OREAD
+ file.areadlines(callback)
 
 class File(client.File):
 
- def stat(self, callback):
- def next(resp, exc, tb):
- Client.respond(callback, resp.stat, exc, tb)
- resp = self._dorpc(fcall.Tstat(), next, callback)
+ @requestchain
+ def stat(self, callback, fail=None):
+ yield callback, fail
+ resp = yield fcall.Tstat()
+ yield resp.stat
 
+ @requestchain
     def aread(self, callback, fail=None, count=None, offset=None, buf=''):
- ctxt = dict(res=[], count=self.iounit, offset=self.offset)
+ yield callback, fail
 
- if count is not None:
- ctxt['count'] = count
- if offset is not None:
- ctxt['offset'] = offset
+ setoffset = offset is None
+ if count is None:
+ count = self.iounit
+ if offset is None:
+ offset = self.offset
 
- def next(resp=None, exc=None, tb=None):
- if resp and resp.data:
- ctxt['res'].append(resp.data)
- ctxt['offset'] += len(resp.data)
+ res = []
+ while count > 0:
+ n = min(count, self.iounit)
+ count -= n
+ resp = yield fcall.Tread(offset=offset, count=n)
+ res.append(resp.data)
+ offset += len(resp.data)
+ if len(resp.data) == 0:
+ break
 
- if ctxt['count'] == 0:
- if offset is None:
- self.offset = ctxt['offset']
- return Client.respond(callback, ''.join(ctxt['res']), exc, tb)
-
- n = min(ctxt['count'], self.iounit)
- ctxt['count'] -= n
-
- self._dorpc(fcall.Tread(offset=ctxt['offset'], count=n),
- next, fail or callback)
- next()
+ if setoffset:
+ self.offset = offset
+ yield ''.join(res)
 
     def areadlines(self, callback):
- ctxt = dict(last=None)
- def next(data, exc, tb):
+ class ctxt:
+ last = None
+ def cont(data, exc, tb):
             res = True
             if data:
                 lines = data.split('\n')
- if ctxt['last']:
- lines[0] = ctxt['last'] + lines[0]
+ if ctxt.last:
+ lines[0] = ctxt.last + lines[0]
                 for i in range(0, len(lines) - 1):
                     res = callback(lines[i])
                     if res is False:
- break
- ctxt['last'] = lines[-1]
- if res is not False:
- self.aread(next)
+ return
+ ctxt.last = lines[-1]
+ self.aread(cont)
             else:
- if ctxt['last']:
- callback(ctxt['last'])
+ if ctxt.last:
+ callback(ctxt.last)
                 callback(None)
- self.aread(next)
+ self.aread(cont)
 
+ @requestchain
     def awrite(self, data, callback=True, fail=None, offset=None):
- ctxt = dict(offset=self.offset, off=0)
- if offset is not None:
- ctxt['offset'] = offset
+ yield callback, fail
+ setoffset = offset is None
+ if offset is None:
+ offset = self.offset
 
- def next(resp=None, exc=None, tb=None):
- if resp:
- ctxt['off'] += resp.count
- ctxt['offset'] += resp.count
- if ctxt['off'] < len(data) or not (exc or resp):
- n = min(len(data), self.iounit)
+ off = 0
+ while off < len(data):
+ n = min(len(data), self.iounit)
+ resp = yield fcall.Twrite(offset=offset, data=data[off:off+n])
+ off += resp.count
+ offset += resp.count
 
- self._dorpc(fcall.Twrite(offset=ctxt['offset'],
- data=data[ctxt['off']:ctxt['off']+n]),
- next, fail or callback)
- else:
- if offset is None:
- self.offset = ctxt['offset']
- Client.respond(callback, ctxt['off'], exc, tb)
- next()
+ if setoffset:
+ self.offset = offset
+ yield off
 
+ @requestchain
     def aremove(self, callback=True, fail=None):
- def next(resp, exc, tb):
- self.close()
- if exc and fail:
- Client.respond(fail, resp and True, exc, tb)
- else:
- Client.respond(callback, resp and True, exc, tb)
- self._dorpc(fcall.Tremove(), next)
+ yield callback, fail
+ yield fcall.Tremove()
+ self.close()
+ yield True
 
 # vim:se sts=4 sw=4 et:
diff -r af47966054cb -r 8661a94bfc27 alternative_wmiircs/python/pyxp/client.py
--- a/alternative_wmiircs/python/pyxp/client.py Fri Jul 09 17:17:39 2010 -0400
+++ b/alternative_wmiircs/python/pyxp/client.py Fri Jul 09 17:48:46 2010 -0400
@@ -45,8 +45,10 @@
 
     @staticmethod
     def respond(callback, data, exc=None, tb=None):
- if callable(callback):
+ if hasattr(callback, 'func_code'):
             callback(*(data, exc, tb)[0:callback.func_code.co_argcount])
+ elif callable(callback):
+ callback(data)
 
     def __enter__(self):
         return self
diff -r af47966054cb -r 8661a94bfc27 alternative_wmiircs/python/pyxp/fields.py
--- a/alternative_wmiircs/python/pyxp/fields.py Fri Jul 09 17:17:39 2010 -0400
+++ b/alternative_wmiircs/python/pyxp/fields.py Fri Jul 09 17:48:46 2010 -0400
@@ -23,18 +23,20 @@
     def encoder(cls, n):
         if n not in cls.encoders:
             exec ('def enc(n):\n' +
- ' assert n == n & 0x%s, "Arithmetic overflow"\n' % ('ff' * n) +
- ' return "".join((' + ','.join(
- 'chr((n >> %d) & 0xff)' % (i * 8)
- for i in range(0, n)) + ',))\n')
+ ' assert n == n & 0x%s, "Arithmetic overflow"\n' +
+ ' return ''.join((%s,))'
+ ) % ('ff' * n,
+ ','.join('chr((n >> %d) & 0xff)' % (i * 8)
+ for i in range(0, n)))
+
             cls.encoders[n] = enc
         return cls.encoders[n]
     @classmethod
     def decoder(cls, n):
         if n not in cls.decoders:
- cls.decoders[n] = eval('lambda data, offset: ' + '|'.join(
- 'ord(data[offset + %d]) << %d' % (i, i * 8)
- for i in range(0, n)))
+ cls.decoders[n] = eval('lambda data, offset: ' +
+ '|'.join('ord(data[offset + %d]) << %d' % (i, i * 8)
+ for i in range(0, n)))
         return cls.decoders[n]
 
     def __init__(self, size):
diff -r af47966054cb -r 8661a94bfc27 alternative_wmiircs/python/pyxp/messages.py
--- a/alternative_wmiircs/python/pyxp/messages.py Fri Jul 09 17:17:39 2010 -0400
+++ b/alternative_wmiircs/python/pyxp/messages.py Fri Jul 09 17:48:46 2010 -0400
@@ -51,9 +51,8 @@
         vals = {}
         start = offset
         for field in cls.fields:
- size, val = field.unmarshall(data, offset)
+ size, vals[field.name] = field.unmarshall(data, offset)
             offset += size
- vals[field.name] = val
         return offset - start, cls(**vals)
     def marshall(self):
         res = []
Received on Sat Jul 10 2010 - 02:00:13 CEST

This archive was generated by hypermail 2.2.0 : Sat Jul 10 2010 - 02:12:04 CEST