config management and some more sensible paths

workspace
Brendan Howell 9 years ago
parent 8207a45654
commit 8a4f0774a3

@ -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

@ -1,4 +1,5 @@
# bureau # bureau
import configparser
import functools import functools
import json import json
import os.path import os.path
@ -12,6 +13,7 @@ from mako.template import Template
def update_commands(cls): def update_commands(cls):
""" this is some internal magic to keep track of our commands """
for name, method in cls.__dict__.items(): for name, method in cls.__dict__.items():
print("name %s method %s" % (name, method)) print("name %s method %s" % (name, method))
if hasattr(method, "command"): if hasattr(method, "command"):
@ -21,9 +23,12 @@ def update_commands(cls):
def add_command(comstr, name=""): def add_command(comstr, name=""):
""" decorator for making a method into a command """
def decorator(func): def decorator(func):
""" the decorator itself """
@functools.wraps(func) @functools.wraps(func)
def func_wrap(*args, **kwargs): def func_wrap(*args, **kwargs):
""" this is to avoid roaching the namespace """
return func(*args, **kwargs) return func(*args, **kwargs)
func_wrap.command = comstr func_wrap.command = comstr
func_wrap.name = name func_wrap.name = name
@ -32,9 +37,12 @@ def add_command(comstr, name=""):
def add_api(apistr, name=""): def add_api(apistr, name=""):
""" decorator for making a method into a public bureau api method"""
def decorator(func): def decorator(func):
""" the decorator itself """
@functools.wraps(func) @functools.wraps(func)
def func_wrap(*args, **kwargs): def func_wrap(*args, **kwargs):
""" this is to avoid roaching the namespace """
return func(*args, **kwargs) return func(*args, **kwargs)
func_wrap.api = apistr func_wrap.api = apistr
func_wrap.name = name func_wrap.name = name
@ -42,45 +50,79 @@ def add_api(apistr, name=""):
return decorator 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" name = "TEST"
prefix = "00" prefix = "00"
version = 0 version = 0
config = {} default_config = {"smallprinter": "/dev/usb/lp0"}
def __init__(self): def __init__(self):
""" set up ZeroMQ connections and register commands""" """ set up ZeroMQ connections and register commands"""
self.commands = {} self.commands = {}
self.api = {} self.api = {}
if not os.path.exists(".screenless"): basepath = os.path.expanduser("~/.screenless")
os.mkdir(".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.context = zmq.Context()
self._recv = self.context.socket(zmq.REP) 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(("bureau " + self.name + " waiting for messages"))
print("commands: ") print("commands: ")
print(self.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): 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 += "." message += "."
if data: if data:
message += json.dumps(data) message += json.dumps(data)
sender = self.context.socket(zmq.REQ) sender = self.context.socket(zmq.REQ)
sender.connect("ipc://.screenless/" + recipient + ".ipc") sender.connect("ipc://" + recipient + ".ipc")
sender.send_string(message) sender.send_string(message)
# TODO: retry this a few times with a proper sleep/timeout # TODO: retry this a few times with a proper sleep/timeout
time.sleep(0.5) time.sleep(0.5)
try: try:
ret = json.loads(sender.recv(flags=zmq.NOBLOCK)) ret = json.loads(sender.recv(flags=zmq.NOBLOCK))
# TODO: deal with non-json replies
except zmq.ZMQError: except zmq.ZMQError:
print("message sent but got no reply...") print("message sent but got no reply...")
ret = None ret = None
return ret return ret
def _publish_methods(self): 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 # register bureau with Inhuman Resources
bureau_detail = {"name": self.name, "prefix": self.prefix, bureau_detail = {"name": self.name, "prefix": self.prefix,
"desc": self.__doc__} "desc": self.__doc__}
@ -132,6 +174,9 @@ class Bureau:
subprocess.call("lpr -P " + lpname + " " + pdffile) subprocess.call("lpr -P " + lpname + " " + pdffile)
def print_small(self, text, printer="/dev/usb/lp1"): def print_small(self, text, printer="/dev/usb/lp1"):
"""
print on Thermal Line printer.
"""
lp = open(printer, "w") lp = open(printer, "w")
text += "\r\n" * 10 text += "\r\n" * 10
text += ".d0" text += ".d0"
@ -140,6 +185,9 @@ class Bureau:
@add_command("test") @add_command("test")
def test(self, data): def test(self, data):
"""
Standard test command.
"""
# stupid test to see if modules work # stupid test to see if modules work
print(("hi! testing. " + self.name + " bureau seems to work!")) print(("hi! testing. " + self.name + " bureau seems to work!"))
return b"seems to work." return b"seems to work."
@ -158,14 +206,15 @@ class Bureau:
pass pass
def run(self): def run(self):
"""main loop for processing messages """
main loop for processing messages
This runs all relelvant event processing loops. This runs all relelvant event processing loops.
""" """
# start the hardware input handler # start the hardware input handler
io = threading.Thread(target=self.run_io) io_handler = threading.Thread(target=self.run_io)
io.start() io_handler.start()
# register commands and api methods # register commands and api methods
self._publish_methods() self._publish_methods()
@ -184,8 +233,8 @@ class Bureau:
else: else:
data = None data = None
print("data: " + str(data)) print("data: " + str(data))
except IndexError as e: except IndexError as err:
print("invalid message: ", e) print("invalid message: ", err)
continue continue
print(("got method: " + ref)) print(("got method: " + ref))
@ -201,12 +250,12 @@ class Bureau:
ret = "" ret = ""
ret = b"0" + ret ret = b"0" + ret
self._recv.send(ret) self._recv.send(ret)
except TypeError as e: except TypeError as err:
print(e) print(err)
print("invalid data for command '{}': {}".format(ref, data)) print("invalid data for command '{}': {}".format(ref, data))
self._recv.send_unicode("Error. Invalid or missing data.") self._recv.send_unicode("Error. Invalid or missing data.")
except KeyError as e: except KeyError as err:
print(e) print(err)
print("You are calling a command as an API or vice-versa.") print("You are calling a command as an API or vice-versa.")
self._recv.send_unicode( self._recv.send_unicode(
"Error. Command called as API or API as command.") "Error. Command called as API or API as command.")
@ -216,5 +265,5 @@ class Bureau:
if __name__ == "__main__": if __name__ == "__main__":
test = Bureau() buro = Bureau()
test.run() buro.run()

Loading…
Cancel
Save