-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinterpretor.py
128 lines (110 loc) · 4.13 KB
/
interpretor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import imp
import os
from os.path import basename, isdir, join, splitext
import queue
import sys
import threading
import time
from common import read_json, read_file_or_die
from flatactors import Actor
class InterpretorActor(Actor):
def constructor(self):
self.daemon = False
def initialize(self):
self.wait_for_message = False
self.active_threads = []
self.settings = read_json(read_file_or_die('config/general.json'))
def main_loop(self, message):
if message:
target, source, subject, payload = message
if subject == 'quit' and source == 'master':
self.stop()
return
elif subject == 'interpret':
self.spawn_worker_swarm(source, *payload)
for thread in list(self.active_threads):
if not thread.is_alive():
self.active_threads.remove(thread)
def spawn_worker_swarm(self, source, destination, author, content):
for plugin_data in list_plugins(self.settings['plugin_blacklist']):
worker = PluginExecutor(
plugin_data,
source,
destination,
author,
content,
self.settings['command_prefix'],
self.send
)
self.active_threads.append(worker)
worker.start()
class PluginExecutor(threading.Thread):
def __init__(self, plugin_data, source, destination, author, content,
command_prefix, send_to_master):
super().__init__()
self.plugin_data = plugin_data
self.source = source
self.destination = destination
self.author = author
self.content = content
self.command_prefix = command_prefix
self.send_to_master = send_to_master
def run(self):
try:
loaded_plugin = load_plugin(self.plugin_data)
# TODO: Make these errors much more verbose and informative!
except ImportError:
self.send_error('Error when trying to import')
return
except SyntaxError:
self.send_error('Syntax error')
return
try:
response = loaded_plugin.run(self.author, self.content,
self.command_prefix)
except Exception as e:
self.send_error('Exception "{}"'.format(e))
return
else:
if response:
if isinstance(response, list) or isinstance(response, tuple):
for item in response:
self.send_result(item)
else:
self.send_result(response)
def send_error(self, error):
self.respond('logger:errors', 'error', error)
def send_result(self, result):
if isinstance(result, str):
self.respond(self.source, 'response', (self.destination, result))
else:
self.send_error('Strange result format "{}" in data: {}'
''.format(type(result), str(result)))
def respond(self, target, subject, message):
sender = 'plugin:{}/{}'.format(basename(self.plugin_data[1]),
self.plugin_data[0])
self.send_to_master(target, subject, message, sender=sender)
def list_plugins(blacklist):
root_path = join(sys.path[0], 'plugins')
plugin_dirs = [join(root_path,d)
for d in os.listdir(root_path)
if isdir(join(root_path,d))]
plugins = [(splitext(f)[0], subdir)
for subdir in plugin_dirs for f in os.listdir(subdir)
if splitext(f)[1] == '.py'
and basename(subdir)+'/'+splitext(f)[0] not in blacklist]
return plugins
def load_plugin(plugin_data):
plugin_name, plugin_path = plugin_data
try:
fp, pathname, description = imp.find_module(plugin_name, [plugin_path])
plugin = imp.load_module(plugin_name, fp, pathname, description)
except Exception as e:
raise e
else:
return plugin
finally:
try:
fp.close()
except UnboundLocalError:
pass