summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--robots/client.py172
-rw-r--r--robots/server.py32
-rw-r--r--simple.py6
3 files changed, 173 insertions, 37 deletions
diff --git a/robots/client.py b/robots/client.py
index 7d60ba2..c25d641 100644
--- a/robots/client.py
+++ b/robots/client.py
@@ -1,29 +1,79 @@
import os
-from threading import Thread
-import time
-import uuid
import zmq
import logbook
+import urwid
-import network
+from network.client import Discoverer
-SERVER_KIND = 'robots'
+log = logbook.Logger(__name__)
+
+def button_width(btn):
+ return len(btn.label) + 4
+
+def focusable(widget):
+ return urwid.AttrMap(widget, None, focus_map='reversed')
+
+class Browser(Discoverer):
+ SERVER_KIND = 'robots'
+
+ def __init__(self, avail_walker, added_walker, ctx=None):
+ super(Browser, self).__init__(self.SERVER_KIND)
-class Browser(object):
- def __init__(self, client, ctx=None):
if ctx is None:
ctx = zmq.Context.instance()
- self.client = client
- self.running = False
- self.targets = {}
self.ctx = ctx
- def update(self, services):
+ self.targets = {}
+ self.added = []
+
+ self.avail_walker = avail_walker
+ self.added_walker = added_walker
+
+ def make_server_widget(self, target, info):
+ return urwid.AttrMap(
+ urwid.Text(info['name']),
+ 'bright',
+ )
+
+ def make_bot_widget(self, key, server_info, name):
+ button = urwid.Button('Add')
+
+ def on_button_pressed(_):
+ self.added.append((key, server_info, name))
+ self.update_added()
+
+ urwid.connect_signal(button, 'click', on_button_pressed)
+
+ return focusable(urwid.Columns([
+ urwid.Text(' ' * 4 + name),
+ (button_width(button), button),
+ ]))
+
+ def make_added_widget(self, key, server_info, name):
+ button = urwid.Button('Remove')
+
+ widget = focusable(urwid.Columns([
+ urwid.Text(server_info['name'] + ' / ' + name),
+ (button_width(button), button),
+ ]))
+
+ def on_button_pressed(_):
+ self.added.remove((key, server_info, name))
+ self.update_added()
+
+ urwid.connect_signal(button, 'click', on_button_pressed)
+
+ return widget
+
+ def on_services_changed(self):
current_targets = set()
- for service in services:
+ for service in self.services.values():
+ if service is None:
+ continue
+
target = service['target']
current_targets.add(target)
@@ -45,18 +95,86 @@ class Browser(object):
if target not in current_targets:
del self.targets[target]
- network.browser.clear_terminal()
- for info in self.targets.values():
- print(info['name'] + ':')
- for name in info['bots']:
- print(' * ' + name)
- print()
-
- def run(self, client_run):
- client_run()
-
-client = network.Client(SERVER_KIND)
-client.find_server(
- browser_cls=Browser,
- connect=False
-)
+ self.update_avail()
+
+ def update_avail(self):
+ things = []
+ for target, info in sorted(
+ self.targets.items(),
+ key=lambda item: item[1]['name'],
+ ):
+ things.append(self.make_server_widget(target, info))
+ for name in sorted(info['bots']):
+ key = (target, name)
+ things.append(self.make_bot_widget(key, info, name))
+ things.append(urwid.Text(''))
+
+ self.avail_walker[:] = things
+
+ def update_added(self):
+ things = []
+ for key, server_info, name in self.added:
+ things.append(self.make_added_widget(key, server_info, name))
+ self.added_walker[:] = things
+
+def make_ui(avail_walker, added_walker):
+ start_game = urwid.Button('Start Game')
+
+ def on_start_game(_):
+ raise urwid.ExitMainLoop()
+ urwid.connect_signal(start_game, 'click', on_start_game)
+
+ main = urwid.Columns([
+ urwid.Frame(
+ urwid.LineBox(urwid.ListBox(avail_walker)),
+ header=urwid.Text('Available bots:'),
+ ),
+ urwid.Frame(
+ urwid.Pile([
+ urwid.LineBox(urwid.ListBox(added_walker)),
+ ('pack', urwid.Padding(
+ focusable(start_game),
+ align='right',
+ width=button_width(start_game),
+ )),
+ ]),
+ header=urwid.Text('Bots in game:'),
+ ),
+ ])
+
+ stdout = urwid.Text('')
+
+ def update_stdout(data):
+ content = stdout.text + data.decode('utf-8')
+ lines = content.split('\n')
+ stdout.set_text('\n'.join(lines[-5:]))
+
+ frame = urwid.Frame(main, footer=urwid.LineBox(stdout))
+ return frame, update_stdout
+
+def choose_bots():
+ avail_walker = urwid.SimpleFocusListWalker([])
+ added_walker = urwid.SimpleFocusListWalker([])
+
+ frame, update_stdout = make_ui(avail_walker, added_walker)
+ mainloop = urwid.MainLoop(
+ frame,
+ palette=[('reversed', 'standout', '')],
+ event_loop=urwid.GLibEventLoop(),
+ )
+
+ pipe = mainloop.watch_pipe(update_stdout)
+ handler = logbook.StreamHandler(os.fdopen(pipe, 'w'))
+
+ with handler.applicationbound():
+ browser = Browser(avail_walker, added_walker)
+ mainloop.run()
+
+ return [key for key, _, _ in browser.added]
+
+def main():
+ bots = choose_bots()
+ print(bots)
+
+if __name__ == '__main__':
+ main()
diff --git a/robots/server.py b/robots/server.py
index 759766c..421afd8 100644
--- a/robots/server.py
+++ b/robots/server.py
@@ -1,4 +1,5 @@
from functools import wraps
+import inspect
import json
import os
import uuid
@@ -9,6 +10,9 @@ import network
log = logbook.Logger(__name__)
+def n_required_args(argspec):
+ return len(argspec.args) - len(argspec.defaults or ())
+
def bad_request(msg):
return {'status': 400, 'message': msg}
@@ -42,14 +46,6 @@ def json_handler(handle):
return handler
-def get_options(fn):
- arguments = fn.__code__.co_varnames
- defaults = fn.__defaults__
-
- required = arguments[:-len(defaults)]
- optional = list(zip(defaults, arguments[-len(defaults):]))
- return (required, optional)
-
class Server(object):
SERVER_NAME = os.getenv('NAME')
SERVER_KIND = 'robots'
@@ -63,8 +59,26 @@ class Server(object):
self.instances = {}
def add_bot(self, fn, name=None):
+ """
+ >>> s = Server()
+ >>> s.add_bot(lambda: 5, name='Alice')
+ >>> list(s.bots)
+ ['Alice']
+ >>> s.add_bot(lambda zebra, orange: 5, name='Bob')
+ Traceback (most recent call last):
+ ...
+ ValueError: Bot function should have no required arguments (found: zebra, orange)
+ """
if name is None:
name = fn.__name__
+
+ argspec = inspect.getargspec(fn)
+ if n_required_args(argspec) != 0:
+ raise ValueError(
+ 'Bot function should have no required arguments (found: %s)' %
+ ', '.join(argspec.args)
+ )
+
self.bots[name] = fn
def add_simple_bot(self, fn, name=None):
@@ -74,7 +88,7 @@ class Server(object):
def list_bots(self):
bots = {
- name: None and get_options(fn)
+ name: {}
for name, fn in self.bots.items()
}
return success(bots=bots)
diff --git a/simple.py b/simple.py
index f8a7edd..1ff37f2 100644
--- a/simple.py
+++ b/simple.py
@@ -1,4 +1,5 @@
import random
+import sys
import robots
@@ -10,6 +11,9 @@ def random_walk(whoami, state):
)
if __name__ == '__main__':
+ name, = sys.argv[1:]
server = robots.Server()
- server.add_simple_bot(random_walk, 'Alice')
+ server.SERVER_NAME = name
+ server.add_simple_bot(random_walk, 'Bot1')
+ server.add_simple_bot(random_walk, 'Bot2')
server.run()