wmii

git clone git://oldgit.suckless.org/wmii/
Log | Files | Refs | README | LICENSE

commit 71891f6f00d9e8134f1197f010a5f2ad7437f6ae
parent f4d2ca326122d3c9dfd677306e4bbfd9cff64afe
Author: Kris Maglione <kris@suckless.org>
Date:   Fri,  9 Jul 2010 17:48:46 -0400

[python] Rephrase async client request chains as coroutines.

Diffstat:
alternative_wmiircs/python/pyxp/asyncclient.py | 286+++++++++++++++++++++++++++++++++++++++----------------------------------------
alternative_wmiircs/python/pyxp/client.py | 4+++-
alternative_wmiircs/python/pyxp/fields.py | 16+++++++++-------
alternative_wmiircs/python/pyxp/messages.py | 3+--
4 files changed, 155 insertions(+), 154 deletions(-)

diff --git a/alternative_wmiircs/python/pyxp/asyncclient.py b/alternative_wmiircs/python/pyxp/asyncclient.py @@ -2,196 +2,194 @@ from pyxp import client, fcall 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) + + path = self._splitpath(path) + fid = self._getfid() + ofid = ROOT_FID + + def fail_(resp, exc, tb): + if ofid != ROOT_FID: + self._aclunk(fid) + self.respond(fail or callback, resp, exc, tb) + yield callback, fail_ - def next(resp=None, exc=None, tb=None): - if exc and ctxt['ofid'] != ROOT_FID: - self._aclunk(ctxt['fid']) - ctxt['fid'] = None + while path: + wname = path[:fcall.MAX_WELEM] + path = path[fcall.MAX_WELEM:] - if not ctxt['path'] and resp or exc: - return self.respond(fail if exc and fail else callback, - ctxt['fid'], exc, tb) + resp = yield fcall.Twalk(fid=ofid, newfid=fid, wname=wname) + ofid = fid - wname = ctxt['path'][:fcall.MAX_WELEM] - ofid = ctxt['ofid'] - ctxt['path'] = ctxt['path'][fcall.MAX_WELEM:] - if resp: - ctxt['ofid'] = ctxt['fid'] + resp = fid + while resp is not None: + resp = yield send(gen, resp) - self._dorpc(fcall.Twalk(fid=ofid, newfid=ctxt['fid'], wname=wname), - next) - next() + 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() - - return self._aopen(path, mode, fcall.Tcreate(mode=mode, name=name, perm=perm), - callback, fail, origpath='/'.join(path + [name])) + 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])) + @_awalk 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) + yield fcall.Tremove(fid=(yield path, callback, fail)) + @_awalk 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, (file, exc, tb), callback, *args, **kwargs): - if exc: - self.respond(callback, file, exc, tb) - else: - file.aread(wrap_callback(callback, file), *args, **kwargs) - - @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) - -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) + resp = yield fcall.Tstat(fid=(yield path, callback, fail)) + yield resp.stat + @awithfile def aread(self, callback, fail=None, count=None, offset=None, buf=''): - ctxt = dict(res=[], count=self.iounit, offset=self.offset) + file = yield callback, fail, OREAD + file.aread(callback, fail, count, offset, buf) - if count is not None: - ctxt['count'] = count - if offset is not None: - ctxt['offset'] = offset + @awithfile + def awrite(self, data, callback=True, fail=None, offset=None): + file = yield callback, fail, OWRITE + file.awrite(data, callback, fail, offset) - def next(resp=None, exc=None, tb=None): - if resp and resp.data: - ctxt['res'].append(resp.data) - ctxt['offset'] += len(resp.data) + @awithfile + def areadlines(self, callback): + file = yield callback, fail, OREAD + file.areadlines(callback) - if ctxt['count'] == 0: - if offset is None: - self.offset = ctxt['offset'] - return Client.respond(callback, ''.join(ctxt['res']), exc, tb) +class File(client.File): - n = min(ctxt['count'], self.iounit) - ctxt['count'] -= n + @requestchain + def stat(self, callback, fail=None): + yield callback, fail + resp = yield fcall.Tstat() + yield resp.stat - self._dorpc(fcall.Tread(offset=ctxt['offset'], count=n), - next, fail or callback) - next() + @requestchain + def aread(self, callback, fail=None, count=None, offset=None, buf=''): + yield callback, fail + + setoffset = offset is None + if count is None: + count = self.iounit + if offset is None: + offset = self.offset + + 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 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 - - 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) - - 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() - + yield callback, fail + setoffset = offset is None + if offset is None: + offset = self.offset + + 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 + + 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 --git a/alternative_wmiircs/python/pyxp/client.py b/alternative_wmiircs/python/pyxp/client.py @@ -45,8 +45,10 @@ class Client(object): @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 --git a/alternative_wmiircs/python/pyxp/fields.py b/alternative_wmiircs/python/pyxp/fields.py @@ -23,18 +23,20 @@ class Int(Field): 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 --git a/alternative_wmiircs/python/pyxp/messages.py b/alternative_wmiircs/python/pyxp/messages.py @@ -51,9 +51,8 @@ class Message(object): 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 = []