# -*- coding: utf-8 -*- import imp import os import sys import yaml class PluginError(Exception): pass class PluginUsageError(PluginError): pass class ConfigError(Exception): def __init__(self, path, key): self.path = path self.key = key def __str__(self): return "invalid key: %r in %s" % (self.key, self.path) class Config(object): def __init__(self, paths, valid_keys): self._valid_keys = valid_keys for path in paths: self.load(path) def __str__(self): ss = [" %s: %s" % (vk, getattr(self, vk)) for vk in self._valid_keys] return "\n".join(ss) def load(self, path): try: with open(path) as fh: conf = yaml.load(fh) except IOError: pass else: for key in conf.keys(): if key in self._valid_keys: setattr(self, key, conf[key]) else: raise ConfigError(path, key) class XGPRenderer(object): def __init__(self, txt=None): self.txt = txt self.img = None self.tool = txt self.bar = None self.click = None self._fields = ['txt', 'img', 'tool', 'bar', 'click'] def __str__(self): out = "" for field in self._fields: value = getattr(self, field, None) if value is not None: out += "<%(f)s>%(v)s" % {'f': field, 'v': value} return out class BasePlugin(object): def __init__(self, data): self.data = data def render(self): method_n = "render_" + self.data.fmt def ex(__): raise PluginError("plugin method not defined: " + method_n) render_fn = getattr(self, method_n, ex) return render_fn() def render_xgp(self): return XGPRenderer(self.render_plain()) class PluginData(object): def __init__(self, cmdargs): self.args = [] self.fmt = "plain" self.maxlen = 40 self.name = "" self._load(cmdargs) def _load(self, cmdargs): if cmdargs['--plain']: self.fmt = "plain" elif cmdargs['--xgp']: self.fmt = "xgp" self.name = cmdargs['COMMAND'] self.args = cmdargs['ARGS'] if cmdargs['--chars']: self.maxlen = int(cmdargs['--chars']) class Subcommand(object): def __init__(self, cmdargs, pluginpath): self.plugindata = PluginData(cmdargs) self.name = cmdargs['COMMAND'] self._load_plugin(pluginpath) def _load_plugin(self, pluginpath): modpath = "%s/%s.py" % (pluginpath, self.name) try: with open(modpath) as fh: module = imp.load_module(self.name, fh, modpath, (".py", "r", imp.PY_SOURCE)) except IOError: raise PluginError("no such plugin: %s" % self.name) self.plugin = module.Plugin(self.plugindata) def _format_output(self, raw): suffix = u'…' cooked = raw.decode("utf-8") if self.plugindata.fmt == "plain": if self.plugindata.maxlen and len(raw) > self.plugindata.maxlen: newlen = self.plugindata.maxlen - len(suffix) cooked = cooked[:newlen] + suffix return cooked.encode("utf-8") def get_output(self): return self._format_output(self.plugin.render()) class App: name = "sardine" cfg_paths = [ '/etc/sardine/config', '%(HOME)s/.config/sardine/config' % os.environ, '.sardine.config' ] cfg_valid_keys = [ ] def __init__(self, cmdargs): self.cmdargs = cmdargs self._set_pluginpath() self.config = Config(self.cfg_paths, self.cfg_valid_keys) def _set_pluginpath(self): mypath = os.path.dirname(os.path.realpath(sys.argv[0])) path = mypath + "/plugins" path = os.environ.get("SARDINE_PLUGINS", path) self.pluginpath = path def die(self, msg, rv=9): sys.stderr.write("%s\n" % msg) self.exit(rv) def exit(self, rv=0): sys.exit(rv) def run(self): try: self.subcommand = Subcommand(self.cmdargs, self.pluginpath) print self.subcommand.get_output() except PluginUsageError as e: msg = ("plugin usage: %s %s %s" % (self.name, self.subcommand.name, e)) self.die(msg, 11) except PluginError as e: self.die(e, 10)