A modest string writer

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. # -*- coding: utf-8 -*-
  2. import imp
  3. import os
  4. import sys
  5. import yaml
  6. class PluginError(Exception):
  7. pass
  8. class PluginUsageError(PluginError):
  9. pass
  10. class ConfigError(Exception):
  11. def __init__(self, path, key):
  12. self.path = path
  13. self.key = key
  14. def __str__(self):
  15. return "invalid key: %r in %s" % (self.key, self.path)
  16. class Config(object):
  17. def __init__(self, paths, valid_keys):
  18. self._valid_keys = valid_keys
  19. for path in paths:
  20. self.load(path)
  21. def __str__(self):
  22. ss = [" %s: %s" % (vk, getattr(self, vk))
  23. for vk in self._valid_keys]
  24. return "\n".join(ss)
  25. def load(self, path):
  26. try:
  27. with open(path) as fh:
  28. conf = yaml.load(fh)
  29. except IOError:
  30. pass
  31. else:
  32. for key in conf.keys():
  33. if key in self._valid_keys:
  34. setattr(self, key, conf[key])
  35. else:
  36. raise ConfigError(path, key)
  37. class XGPRenderer(object):
  38. def __init__(self, txt=None):
  39. self.txt = txt
  40. self.img = None
  41. self.tool = txt
  42. self.bar = None
  43. self.click = None
  44. self._fields = ['txt', 'img', 'tool', 'bar', 'click']
  45. def __str__(self):
  46. out = ""
  47. for field in self._fields:
  48. value = getattr(self, field, None)
  49. if value is not None:
  50. out += "<%(f)s>%(v)s</%(f)s>" % {'f': field, 'v': value}
  51. return out
  52. class BasePlugin(object):
  53. def __init__(self, data):
  54. self.data = data
  55. def render(self):
  56. method_n = "render_" + self.data.fmt
  57. def ex(__):
  58. raise PluginError("plugin method not defined: " + method_n)
  59. render_fn = getattr(self, method_n, ex)
  60. return render_fn()
  61. def render_xgp(self):
  62. return XGPRenderer(self.render_plain())
  63. class PluginData(object):
  64. def __init__(self, cmdargs):
  65. self.args = []
  66. self.fmt = "plain"
  67. self.maxlen = 40
  68. self.name = ""
  69. self._load(cmdargs)
  70. def _load(self, cmdargs):
  71. if cmdargs['--plain']:
  72. self.fmt = "plain"
  73. elif cmdargs['--xgp']:
  74. self.fmt = "xgp"
  75. self.name = cmdargs['COMMAND']
  76. self.args = cmdargs['ARGS']
  77. if cmdargs['--chars']:
  78. self.maxlen = int(cmdargs['--chars'])
  79. class Subcommand(object):
  80. def __init__(self, cmdargs, pluginpath):
  81. self.plugindata = PluginData(cmdargs)
  82. self.name = cmdargs['COMMAND']
  83. self._load_plugin(pluginpath)
  84. def _load_plugin(self, pluginpath):
  85. modpath = "%s/%s.py" % (pluginpath, self.name)
  86. try:
  87. with open(modpath) as fh:
  88. module = imp.load_module(self.name, fh, modpath,
  89. (".py", "r", imp.PY_SOURCE))
  90. except IOError:
  91. raise PluginError("no such plugin: %s" % self.name)
  92. self.plugin = module.Plugin(self.plugindata)
  93. def _format_output(self, raw):
  94. suffix = u'…'
  95. cooked = raw.decode("utf-8")
  96. if self.plugindata.fmt == "plain":
  97. if self.plugindata.maxlen and len(raw) > self.plugindata.maxlen:
  98. newlen = self.plugindata.maxlen - len(suffix)
  99. cooked = cooked[:newlen] + suffix
  100. return cooked.encode("utf-8")
  101. def get_output(self):
  102. return self._format_output(self.plugin.render())
  103. class App:
  104. name = "sardine"
  105. cfg_paths = [
  106. '/etc/sardine/config',
  107. '%(HOME)s/.config/sardine/config' % os.environ,
  108. '.sardine.config'
  109. ]
  110. cfg_valid_keys = [
  111. ]
  112. def __init__(self, cmdargs):
  113. self.cmdargs = cmdargs
  114. self._set_pluginpath()
  115. self.config = Config(self.cfg_paths, self.cfg_valid_keys)
  116. def _set_pluginpath(self):
  117. mypath = os.path.dirname(os.path.realpath(sys.argv[0]))
  118. path = mypath + "/plugins"
  119. path = os.environ.get("SARDINE_PLUGINS", path)
  120. self.pluginpath = path
  121. def die(self, msg, rv=9):
  122. sys.stderr.write("%s\n" % msg)
  123. self.exit(rv)
  124. def exit(self, rv=0):
  125. sys.exit(rv)
  126. def run(self):
  127. try:
  128. self.subcommand = Subcommand(self.cmdargs, self.pluginpath)
  129. print self.subcommand.get_output()
  130. except PluginUsageError as e:
  131. msg = ("plugin usage: %s %s %s"
  132. % (self.name, self.subcommand.name, e))
  133. self.die(msg, 11)
  134. except PluginError as e:
  135. self.die(e, 10)