From 8a4f0774a3ae894c583f2560696b82f6b59a77cc Mon Sep 17 00:00:00 2001 From: Brendan Howell Date: Thu, 18 Feb 2016 00:35:36 +0100 Subject: [PATCH] config management and some more sensible paths --- reqirements.txt | 28 ++++++++++++ screenless/bureau/bureau.py | 83 ++++++++++++++++++++++++++++-------- screenless/bureau/evdevkb.py | 0 3 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 reqirements.txt delete mode 100644 screenless/bureau/evdevkb.py diff --git a/reqirements.txt b/reqirements.txt new file mode 100644 index 0000000..379d433 --- /dev/null +++ b/reqirements.txt @@ -0,0 +1,28 @@ +Babel==1.3 +CairoSVG==1.0.13 +Jinja2==2.7.3 +Mako==1.0.1 +MarkupSafe==0.23 +Pygments==2.0.2 +Pyphen==0.9.1 +Sphinx==1.3.1 +TheScreenlessOffice==0.1 +WeasyPrint==0.23 +alabaster==0.7.3 +cairocffi==0.6 +cffi==0.9.2 +cssselect==0.9.1 +docutils==0.12 +evdev==0.4.7 +html2text==2015.4.14 +html5lib==0.99999 +lxml==3.4.4 +pycparser==2.13 +pytz==2015.2 +pyzmq==14.5.0 +screenless==0.1 +six==1.9.0 +snowballstemmer==1.2.0 +sphinx-rtd-theme==0.1.7 +tinycss==0.3 +twitter==1.17.0 diff --git a/screenless/bureau/bureau.py b/screenless/bureau/bureau.py index 8ebccd4..fed1536 100644 --- a/screenless/bureau/bureau.py +++ b/screenless/bureau/bureau.py @@ -1,4 +1,5 @@ # bureau +import configparser import functools import json import os.path @@ -12,6 +13,7 @@ from mako.template import Template def update_commands(cls): + """ this is some internal magic to keep track of our commands """ for name, method in cls.__dict__.items(): print("name %s method %s" % (name, method)) if hasattr(method, "command"): @@ -21,9 +23,12 @@ def update_commands(cls): def add_command(comstr, name=""): + """ decorator for making a method into a command """ def decorator(func): + """ the decorator itself """ @functools.wraps(func) def func_wrap(*args, **kwargs): + """ this is to avoid roaching the namespace """ return func(*args, **kwargs) func_wrap.command = comstr func_wrap.name = name @@ -32,9 +37,12 @@ def add_command(comstr, name=""): def add_api(apistr, name=""): + """ decorator for making a method into a public bureau api method""" def decorator(func): + """ the decorator itself """ @functools.wraps(func) def func_wrap(*args, **kwargs): + """ this is to avoid roaching the namespace """ return func(*args, **kwargs) func_wrap.api = apistr func_wrap.name = name @@ -42,45 +50,79 @@ def add_api(apistr, name=""): return decorator -class Bureau: +class Bureau(object): + """ Bureau is a base class that implements standard methods for + inter-bureau communication, IO, registration and some convenient stuff + for printing. """ name = "TEST" prefix = "00" version = 0 - config = {} + default_config = {"smallprinter": "/dev/usb/lp0"} def __init__(self): """ set up ZeroMQ connections and register commands""" self.commands = {} self.api = {} - if not os.path.exists(".screenless"): - os.mkdir(".screenless") + basepath = os.path.expanduser("~/.screenless") + if not os.path.exists(basepath): + os.mkdir(basepath) + os.chdir(basepath) + + self.config = configparser.ConfigParser() + self.load_config() self.context = zmq.Context() self._recv = self.context.socket(zmq.REP) - self._recv.bind("ipc://.screenless/" + self.prefix + ".ipc") + self._recv.bind("ipc://" + self.prefix + ".ipc") print(("bureau " + self.name + " waiting for messages")) print("commands: ") print(self.commands) + def load_config(self): + """ + load (or reload) config data from file + """ + cfgfile = self.prefix + ".ini" + if os.path.exists(cfgfile): + self.config.read(cfgfile) + else: + self.config["DEFAULT"] = self.default_config + self.config["bureau"] = self.default_config + with open(cfgfile, "w") as configfile: + self.config.write(configfile) + def send(self, recipient, message, data=None): + """ + send commands or API calls to another bureau. + recipient: the 2-character bureau ID + message: a text based message as used in many commands + data: an optional dict, used in API calls + + returns either empty string, text or a json object + """ message += "." if data: message += json.dumps(data) sender = self.context.socket(zmq.REQ) - sender.connect("ipc://.screenless/" + recipient + ".ipc") + sender.connect("ipc://" + recipient + ".ipc") sender.send_string(message) # TODO: retry this a few times with a proper sleep/timeout time.sleep(0.5) try: ret = json.loads(sender.recv(flags=zmq.NOBLOCK)) + # TODO: deal with non-json replies except zmq.ZMQError: print("message sent but got no reply...") ret = None return ret def _publish_methods(self): + """ + this internal method registers all public commands and bureau API + methods. Inhuman Resources module can then display menus and docs. + """ # register bureau with Inhuman Resources bureau_detail = {"name": self.name, "prefix": self.prefix, "desc": self.__doc__} @@ -132,6 +174,9 @@ class Bureau: subprocess.call("lpr -P " + lpname + " " + pdffile) def print_small(self, text, printer="/dev/usb/lp1"): + """ + print on Thermal Line printer. + """ lp = open(printer, "w") text += "\r\n" * 10 text += ".d0" @@ -140,6 +185,9 @@ class Bureau: @add_command("test") def test(self, data): + """ + Standard test command. + """ # stupid test to see if modules work print(("hi! testing. " + self.name + " bureau seems to work!")) return b"seems to work." @@ -158,14 +206,15 @@ class Bureau: pass def run(self): - """main loop for processing messages + """ + main loop for processing messages This runs all relelvant event processing loops. """ # start the hardware input handler - io = threading.Thread(target=self.run_io) - io.start() + io_handler = threading.Thread(target=self.run_io) + io_handler.start() # register commands and api methods self._publish_methods() @@ -184,8 +233,8 @@ class Bureau: else: data = None print("data: " + str(data)) - except IndexError as e: - print("invalid message: ", e) + except IndexError as err: + print("invalid message: ", err) continue print(("got method: " + ref)) @@ -201,12 +250,12 @@ class Bureau: ret = "" ret = b"0" + ret self._recv.send(ret) - except TypeError as e: - print(e) + except TypeError as err: + print(err) print("invalid data for command '{}': {}".format(ref, data)) self._recv.send_unicode("Error. Invalid or missing data.") - except KeyError as e: - print(e) + except KeyError as err: + print(err) print("You are calling a command as an API or vice-versa.") self._recv.send_unicode( "Error. Command called as API or API as command.") @@ -216,5 +265,5 @@ class Bureau: if __name__ == "__main__": - test = Bureau() - test.run() + buro = Bureau() + buro.run() diff --git a/screenless/bureau/evdevkb.py b/screenless/bureau/evdevkb.py deleted file mode 100644 index e69de29..0000000