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