commit f4d2ca326122d3c9dfd677306e4bbfd9cff64afe
parent 1ede2c989512090b798b9f73da82288043db683c
Author: Kris Maglione <kris@suckless.org>
Date: Fri, 9 Jul 2010 17:17:39 -0400
[python] Minor cleanups.
Diffstat:
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