wmii

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

commit f4d2ca326122d3c9dfd677306e4bbfd9cff64afe
parent 1ede2c989512090b798b9f73da82288043db683c
Author: Kris Maglione <kris@suckless.org>
Date:   Fri,  9 Jul 2010 17:17:39 -0400

[python] Minor cleanups.

Diffstat:
alternative_wmiircs/python/pygmi/fs.py | 13++++++++-----
alternative_wmiircs/python/pygmi/menu.py | 7++++---
alternative_wmiircs/python/pygmi/monitor.py | 3+++
alternative_wmiircs/python/pygmi/util.py | 7++++---
alternative_wmiircs/python/pyxp/asyncclient.py | 96+++++++++++++++++++++++++++++++++++++++++--------------------------------------
alternative_wmiircs/python/pyxp/client.py | 34++++++++++++++++------------------
alternative_wmiircs/python/pyxp/messages.py | 2+-
alternative_wmiircs/python/pyxp/mux.py | 112++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
alternative_wmiircs/python/wmiirc | 5+++++
9 files changed, 153 insertions(+), 126 deletions(-)

diff --git a/alternative_wmiircs/python/pygmi/fs.py b/alternative_wmiircs/python/pygmi/fs.py @@ -13,6 +13,11 @@ __all__ = ('wmii', 'Tags', 'Tag', 'Area', 'Frame', 'Client', spacere = re.compile(r'\s') sentinel = {} +def tounicode(obj): + if isinstance(obj, str): + return obj.decode('UTF-8') + return unicode(obj) + class utf8(object): def __str__(self): return unicode(self).encode('utf-8') @@ -85,12 +90,10 @@ class Ctl(object): """ Arguments are joined by ascii spaces and written to the ctl file. """ - def next(file, exc=None, tb=None): - if exc: - print exc + def next(file): if file: self.ctl_file = file - file.awrite(u' '.join(map(unicode, args))) + file.awrite(u' '.join(map(tounicode, args))) if self.ctl_file: return next(self.ctl_file) getattr(client, self.ctl_open)(self.ctl_path, callback=next, mode=OWRITE) @@ -687,7 +690,7 @@ class Rule(collections.MutableMapping, utf8): if val is False: return "off" if val in (Toggle, Always, Never): return unicode(val).lower() - return unicode(val) + return tounicode(val) def __get__(self, obj, cls): return self diff --git a/alternative_wmiircs/python/pygmi/menu.py b/alternative_wmiircs/python/pygmi/menu.py @@ -3,11 +3,12 @@ from pygmi.util import call __all__ = 'Menu', 'ClickMenu' -def inthread(args, action, **kwargs): +def inthread(name, args, action, **kwargs): fn = lambda: call(*args, **kwargs) if not action: return fn() t = Thread(target=lambda: action(fn())) + t.name += '-%s' % name t.daemon = True t.start() @@ -29,7 +30,7 @@ class Menu(object): args += ['-h', self.histfile] if self.nhist: args += ['-n', self.nhist] - return inthread(map(str, args), self.action, input='\n'.join(choices)) + return inthread('Menu', map(str, args), self.action, input='\n'.join(choices)) call = __call__ class ClickMenu(object): @@ -48,7 +49,7 @@ class ClickMenu(object): if self.prev: args += ['-i', self.prev] args += ['--'] + list(choices) - return inthread(map(str, args), self.action) + return inthread('ClickMenu', map(str, args), self.action) call = __call__ # vim:se sts=4 sw=4 et: diff --git a/alternative_wmiircs/python/pygmi/monitor.py b/alternative_wmiircs/python/pygmi/monitor.py @@ -51,6 +51,8 @@ class Monitor(object): side = 'right' interval = 1.0 + define = classmethod(defmonitor) + def __init__(self, name=None, interval=None, side=None, action=None, colors=None, label=None): """ @@ -93,6 +95,7 @@ class Monitor(object): self.button.create(*label) self.timer = Timer(self.interval, self.tick) + self.timer.name = 'Monitor-Timer-%s' % self.name self.timer.daemon = True self.timer.start() diff --git a/alternative_wmiircs/python/pygmi/util.py b/alternative_wmiircs/python/pygmi/util.py @@ -1,3 +1,4 @@ +from functools import partial, update_wrapper, wraps import os import signal import subprocess @@ -43,6 +44,7 @@ def program_list(path): def curry(func, *args, **kwargs): if _ in args: blank = [i for i in range(0, len(args)) if args[i] is _] + @wraps(func) def curried(*newargs, **newkwargs): ary = list(args) for k, v in zip(blank, newargs): @@ -50,9 +52,8 @@ def curry(func, *args, **kwargs): ary = tuple(ary) + newargs[len(blank):] return func(*ary, **dict(kwargs, **newkwargs)) else: - def curried(*newargs, **newkwargs): - return func(*(args + newargs), **dict(kwargs, **newkwargs)) - curried.__name__ = func.__name__ + '__curried__' + curried = update_wrapper(partial(func, *args, **kwargs), func) + curried.__name__ += '__curried__' return curried def find_script(name): diff --git a/alternative_wmiircs/python/pyxp/asyncclient.py b/alternative_wmiircs/python/pyxp/asyncclient.py @@ -1,14 +1,17 @@ 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 return wrapper + def wrap_callback(fn, file): def callback(data, exc, tb): file.close() @@ -19,81 +22,82 @@ 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 next(resp=None, exc=None, tb=None): if exc and ctxt['ofid'] != ROOT_FID: self._aclunk(ctxt['fid']) + ctxt['fid'] = None + if not ctxt['path'] and resp or exc: - if exc and fail: - return self.respond(fail, None, exc, tb) - return self.respond(callback, ctxt['fid'], exc, tb) + return self.respond(fail if exc and fail else callback, + ctxt['fid'], exc, tb) + wname = ctxt['path'][:fcall.MAX_WELEM] ofid = ctxt['ofid'] ctxt['path'] = ctxt['path'][fcall.MAX_WELEM:] if resp: ctxt['ofid'] = ctxt['fid'] - self._dorpc(fcall.Twalk(fid=ofid, - newfid=ctxt['fid'], - wname=wname), - next) + + self._dorpc(fcall.Twalk(fid=ofid, newfid=ctxt['fid'], wname=wname), + next) next() _file = property(lambda self: File) - def _aopen(self, path, mode, open, callback, fail=None, origpath=None): - resp = None + 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): - def cleanup(): - self._clunk(fid) - file = self._file(self, origpath or '/'.join(path), resp, fid, mode, cleanup) + file = self._file(self, origpath or '/'.join(path), resp, fid, mode, + cleanup=lambda: self._clunk(fid)) self.respond(callback, file) - self._dorpc(open(fid), next, fail or callback) + fcall.fid = fid + self._dorpc(fcall, next, fail or callback) self._awalk(path, next, fail or callback) def aopen(self, path, callback=True, fail=None, mode=OREAD): assert callable(callback) - path = self._splitpath(path) - def open(fid): - return fcall.Topen(fid=fid, mode=mode) - return self._aopen(path, mode, open, fail or callback) + return 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() - def open(fid): - return fcall.Tcreate(fid=fid, mode=mode, name=name, perm=perm) + if not callable(callback): - def callback(resp, exc, tb): - if resp: - resp.close() - return self._aopen(path, mode, open, callback, fail, - origpath='/'.join(path + [name])) + 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])) def aremove(self, path, callback=True, fail=None): - path = self._splitpath(path) - def next(fid, exc, tb): + def next(fid): self._dorpc(fcall.Tremove(fid=fid), callback, fail) - self._awalk(path, next, callback, fail) + self._awalk(path, next, fail or callback) - def astat(self, path, callback, fail = None): - path = self._splitpath(path) - def next(fid, exc, tb): - def next(resp, exc, tb): - callback(resp.stat, exc, tb) - self._dorpc(fcall.Tstat(fid=fid), next, callback) + 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: - callback(file, exc, tb) + 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): @@ -108,36 +112,35 @@ class Client(client.Client): file.sreadlines(callback) class File(client.File): - @staticmethod - def respond(callback, data, exc=None, tb=None): - if callable(callback): - callback(data, exc, tb) def stat(self, callback): def next(resp, exc, tb): - callback(resp.stat, exc, tb) + Client.respond(callback, resp.stat, exc, tb) resp = self._dorpc(fcall.Tstat(), next, callback) def aread(self, callback, fail=None, count=None, offset=None, buf=''): ctxt = dict(res=[], count=self.iounit, offset=self.offset) + if count is not None: ctxt['count'] = count if offset is not None: ctxt['offset'] = offset + def next(resp=None, exc=None, tb=None): if resp and resp.data: ctxt['res'].append(resp.data) ctxt['offset'] += len(resp.data) + if ctxt['count'] == 0: if offset is None: self.offset = ctxt['offset'] - return callback(''.join(ctxt['res']), exc, tb) + 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, fail or callback) next() def areadlines(self, callback): @@ -165,6 +168,7 @@ class File(client.File): 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 @@ -173,21 +177,21 @@ class File(client.File): n = min(len(data), self.iounit) self._dorpc(fcall.Twrite(offset=ctxt['offset'], - data=data[ctxt['off']:ctxt['off']+n]), - next, fail or callback) + data=data[ctxt['off']:ctxt['off']+n]), + next, fail or callback) else: if offset is None: self.offset = ctxt['offset'] - self.respond(callback, ctxt['off'], exc, tb) + Client.respond(callback, ctxt['off'], exc, tb) next() def aremove(self, callback=True, fail=None): def next(resp, exc, tb): self.close() if exc and fail: - self.respond(fail, resp and True, exc, tb) + Client.respond(fail, resp and True, exc, tb) else: - self.respond(callback, resp and True, exc, tb) + Client.respond(callback, resp and True, exc, tb) self._dorpc(fcall.Tremove(), next) # vim:se sts=4 sw=4 et: diff --git a/alternative_wmiircs/python/pyxp/client.py b/alternative_wmiircs/python/pyxp/client.py @@ -46,8 +46,7 @@ class Client(object): @staticmethod def respond(callback, data, exc=None, tb=None): if callable(callback): - callback(data, exc, tb) - + callback(*(data, exc, tb)[0:callback.func_code.co_argcount]) def __enter__(self): return self @@ -77,13 +76,13 @@ class Client(object): if root: path = self._splitpath(root) resp = self._dorpc(fcall.Twalk(fid=ROOT_FID, - newfid=ROOT_FID, - wname=path)) - except Exception, e: + newfid=ROOT_FID, + wname=path)) + except Exception: traceback.print_exc(sys.stdout) if getattr(self, 'mux', None): self.mux.fd.close() - raise e + raise def _cleanup(self): try: @@ -102,21 +101,22 @@ class Client(object): raise ProtocolException, "Missmatched RPC message types: %s => %s" % ( req.__class__.__name__, resp.__class__.__name__) return resp + def next(mux, resp): try: res = doresp(resp) except Exception, e: - if error: - self.respond(error, None, e, None) - else: - self.respond(callback, None, e, None) + self.respond(error or callback, None, e, None) else: self.respond(callback, res) + if not callback: return doresp(self.mux.rpc(req)) self.mux.rpc(req, next) def _splitpath(self, path): + if isinstance(path, list): + return path return [v for v in path.split('/') if v != ''] def _getfid(self): @@ -163,12 +163,13 @@ class Client(object): return Res _file = property(lambda self: File) - def _open(self, path, mode, open, origpath=None): + def _open(self, path, mode, fcall, origpath=None): resp = None with self._walk(path) as nfid: fid = nfid - resp = self._dorpc(open(fid)) + fcall.fid = fid + resp = self._dorpc(fcall) def cleanup(): self._aclunk(fid) @@ -178,17 +179,14 @@ class Client(object): def open(self, path, mode=OREAD): path = self._splitpath(path) - def open(fid): - return fcall.Topen(fid=fid, mode=mode) - return self._open(path, mode, open) + return self._open(path, mode, fcall.Topen(mode=mode)) def create(self, path, mode=OREAD, perm=0): path = self._splitpath(path) name = path.pop() - def open(fid): - return fcall.Tcreate(fid=fid, mode=mode, name=name, perm=perm) - return self._open(path, mode, open, origpath='/'.join(path + [name])) + return self._open(path, mode, fcall.Tcreate(mode=mode, name=name, perm=perm), + origpath='/'.join(path + [name])) def remove(self, path): path = self._splitpath(path) diff --git a/alternative_wmiircs/python/pyxp/messages.py b/alternative_wmiircs/python/pyxp/messages.py @@ -28,7 +28,7 @@ class Message(object): __metaclass__ = MessageBase def __init__(self, *args, **kwargs): if args: - args = dict(zip([f.name for f in self.fields], args)) + args = dict(zip((f.name for f in self.fields), args)) args.update(kwargs) kwargs = args; for k, v in kwargs.iteritems(): diff --git a/alternative_wmiircs/python/pyxp/mux.py b/alternative_wmiircs/python/pyxp/mux.py @@ -14,6 +14,7 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +import os import sys import traceback @@ -26,9 +27,8 @@ __all__ = 'Mux', class Mux(object): def __init__(self, con, process, flush=None, mintag=0, maxtag=1<<16 - 1): - self.queue = set() self.lock = RLock() - self.rendez = Condition(self.lock) + self.tagcond = Condition(self.lock) self.outlock = RLock() self.inlock = RLock() self.process = process @@ -50,35 +50,32 @@ class Mux(object): raise Exception("No connection") def mux(self, rpc): - try: - self.lock.acquire() - rpc.waiting = True - while self.muxer and self.muxer != rpc and rpc.data is None: - rpc.wait() - - if rpc.data is None: - assert not self.muxer or self.muxer is rpc - self.muxer = rpc - self.lock.release() - try: - while rpc.data is None: - data = self.recv() - if data is None: - self.lock.acquire() - self.queue.remove(rpc) - raise Exception("unexpected eof") - self.dispatch(data) - finally: - self.lock.acquire() - self.electmuxer() - except Exception, e: - traceback.print_exc(sys.stderr) - if self.flush: - self.flush(self, rpc.data) - raise e - finally: - if self.lock._is_owned(): - self.lock.release() + with self.lock: + try: + rpc.waiting = True + while self.muxer and self.muxer != rpc and rpc.data is None: + rpc.wait() + + if rpc.data is None: + assert self.muxer in (rpc, None) + self.muxer = rpc + try: + self.lock.release() + while rpc.data is None: + data = self.recv() + if data is None: + raise Exception("unexpected eof") + self.dispatch(data) + finally: + self.lock.acquire() + self.electmuxer() + except Exception: + traceback.print_exc(sys.stderr) + if rpc.tag in self.wait: + self.wait.pop(rpc.tag) + if self.flush: + self.flush(self, rpc.data) + raise return rpc.data @@ -90,11 +87,10 @@ class Mux(object): return self.mux(rpc) def async_dispatch(self, rpc): - self.async_mux.pop(rpc) rpc.async(self, rpc.data) def electmuxer(self): - for rpc in self.queue: + for rpc in self.wait.itervalues(): if self.muxer != rpc and rpc.waiting: self.muxer = rpc rpc.notify() @@ -102,22 +98,19 @@ class Mux(object): self.muxer = None def dispatch(self, dat): - tag = dat.tag - rpc = None with self.lock: - rpc = self.wait.get(tag, None) - if rpc is None or rpc not in self.queue: - #print "bad rpc tag: %u (no one waiting on it)" % dat.tag - return - self.puttag(rpc) - self.queue.remove(rpc) - rpc.dispatch(dat) + rpc = self.wait.get(dat.tag, None) + if rpc: + self.puttag(rpc) + rpc.dispatch(dat) + elif False: + print "bad rpc tag: %u (no one waiting on it)" % dat.tag def gettag(self, r): tag = 0 while not self.free: - self.rendez.wait() + self.tagcond.wait() tag = self.free.pop() @@ -134,21 +127,36 @@ class Mux(object): if rpc.tag in self.wait: del self.wait[rpc.tag] self.free.add(rpc.tag) - self.rendez.notify() + self.tagcond.notify() def send(self, dat): data = ''.join(dat.marshall()) n = self.fd.send(data) return n == len(data) def recv(self): + def readn(fd, n): + data = '' + while len(data) < n: + try: + s = fd.recv(n - len(data)) + if len(s) == 0: + raise Exception('unexpected end of file') + data += s + except os.error, e: + if e.errno != os.errno.EINTR: + raise e + return data + try: with self.inlock: - data = self.fd.recv(4) + data = readn(self.fd, 4) if data: - len = fields.Int.decoders[4](data, 0) - data += self.fd.recv(len - 4) + nmsg = fields.Int.decoders[4](data, 0) + data += readn(self.fd, nmsg - 4) return self.process(data) except Exception, e: + print e.__class__.__name__ + print repr(e) traceback.print_exc(sys.stderr) return None @@ -158,13 +166,11 @@ class Mux(object): with self.lock: self.gettag(rpc) - self.queue.add(rpc) if rpc.tag >= 0 and self.send(dat): return rpc with self.lock: - self.queue.remove(rpc) self.puttag(rpc) class Rpc(Condition): @@ -176,6 +182,9 @@ class Rpc(Condition): self.async = async self.waiting = False + def __repr__(self): + return '<Rpc tag=%s orig=%s data=%s async=%s waiting=%s>' % tuple(map(repr, (self.tag, self.orig, self.data, self.async, self.waiting))) + def dispatch(self, data=None): self.data = data self.notify() @@ -183,8 +192,11 @@ class Rpc(Condition): self.mux.async_dispatch(self) class Queue(Thread): + _id = 1 + def __init__(self, op): - super(Queue, self).__init__() + super(Queue, self).__init__(name='Queue-%d-%s' % (Queue._id, repr(op))) + Queue._id += 1 self.cond = Condition() self.op = op self.queue = [] diff --git a/alternative_wmiircs/python/wmiirc b/alternative_wmiircs/python/wmiirc @@ -1,4 +1,9 @@ #!/usr/bin/env python +import os, sys +path = [] +for p in os.environ.get("WMII_CONFPATH", "").split(':'): + path += [p, p + '/python'] +sys.path = path + sys.path from pygmi import events import wmiirc