conf: use shutil.move instead of os.rename for saving
[urk.git] / events.py
blobf048f687450d0c3f66b11f570449263e380e711f
1 import sys
2 import os
3 import traceback
4 import imp
6 pyending = os.extsep + 'py'
8 class error(Exception):
9 pass
11 class EventStopError(error):
12 pass
14 class CommandError(error):
15 pass
17 class data(object):
18 done = False
19 quiet = False
21 def __init__(self, **kwargs):
22 for attr in kwargs.items():
23 setattr(self, *attr)
25 trigger_sequence = ("pre", "setup", "on", "setdown", "post")
27 events = {}
28 loaded = {}
30 # An event has occurred, the e_name event!
31 def trigger(e_name, e_data=None, **kwargs):
32 if e_data is None:
33 e_data = data(**kwargs)
35 #print 'Event:', e_name, e_data
37 failure = True
38 error = None
39 if e_name in events:
40 for e_stage in trigger_sequence:
41 if e_stage in events[e_name]:
42 for f_ref, s_name in events[e_name][e_stage]:
43 try:
44 f_ref(e_data)
45 except EventStopError:
46 return
47 except CommandError, e:
48 error = e.args
49 continue
50 except:
51 traceback.print_exc()
52 failure = False
53 if failure:
54 return error
56 # Stop all processing of the current event now!
57 def halt():
58 raise EventStopError
60 # Registers a specific function with an event at the given sequence stage.
61 def register(e_name, e_stage, f_ref, s_name=""):
62 if e_name not in events:
63 events[e_name] = {}
65 if e_stage not in events[e_name]:
66 events[e_name][e_stage] = []
68 events[e_name][e_stage] += [(f_ref, s_name)]
70 # turn a filename (or module name) and trim it to the name of the module
71 def get_scriptname(name):
72 s_name = os.path.basename(name)
73 if s_name.endswith(pyending):
74 s_name = s_name[:-len(pyending)]
75 return s_name
77 #take a given script name and turn it into a filename
78 def get_filename(name):
79 # split the directory and filename
80 dirname = os.path.dirname(name)
81 s_name = get_scriptname(name)
83 for path in dirname and (dirname,) or sys.path:
84 filename = os.path.join(path, s_name + pyending)
85 if os.access(filename, os.R_OK):
86 return filename
88 raise ImportError("No urk script %s found" % name)
90 # register the events defined by obj
91 def register_all(name, obj):
92 # we look through everything defined in the file
93 for f in dir(obj):
94 # for each bit of the event sequence
95 for e_stage in trigger_sequence:
97 # if the function is for this bit
98 if f.startswith(e_stage):
100 # get a reference to a function
101 f_ref = getattr(obj, f)
103 # normalise to the event name
104 e_name = f.replace(e_stage, "", 1)
106 # add our function to the right event section
107 register(e_name, e_stage, f_ref, name)
109 break
111 #load a .py file into a new module object without affecting sys.modules
112 def load_pyfile(filename):
113 s_name = get_scriptname(filename)
115 module = imp.new_module(s_name)
116 module.__file__ = filename
118 # When a module gets collected, everything in its __dict__ gets set to None
119 # We can't let that happen until all the objects that need it are gone
120 # This should protect the module from being collected without __dict__
121 module.__module__ = module
123 f = file(filename,"U")
124 source = f.read()
125 f.close()
127 exec compile(source, filename, "exec") in module.__dict__
128 return module
130 # Load a python script and register
131 # the functions defined in it for events.
132 # Return True if we loaded the script, False if it was already loaded
133 def load(name):
134 s_name = get_scriptname(name)
135 filename = get_filename(name)
137 if s_name in loaded:
138 return False
140 loaded[s_name] = None
142 try:
143 loaded[s_name] = load_pyfile(filename)
144 except:
145 del loaded[s_name]
146 raise
148 register_all(s_name, loaded[s_name])
150 return True
152 # Is the script with the given name loaded?
153 def is_loaded(name):
154 return get_scriptname(name) in loaded
156 # Remove any function which was defined in the given script
157 def unload(name):
158 s_name = get_scriptname(name)
160 del loaded[s_name]
162 for e_name in list(events):
163 for e_stage in list(events[e_name]):
164 to_check = events[e_name][e_stage]
166 events[e_name][e_stage] = [(f, m) for f, m in to_check if m != s_name]
168 if not events[e_name][e_stage]:
169 del events[e_name][e_stage]
171 if not events[e_name]:
172 del events[e_name]
174 def reload(name):
175 s_name = get_scriptname(name)
177 if s_name not in loaded:
178 return False
180 temp = loaded[s_name]
182 unload(s_name)
184 try:
185 load(name)
186 return True
187 except:
188 loaded[s_name] = temp
189 register_all(s_name, temp)
190 raise
192 def run(text, window, network):
193 split = text.split(' ')
195 c_data = data(name=split.pop(0), text=text, window=window, network=network)
197 if split and split[0].startswith('-'):
198 c_data.switches = set(split.pop(0)[1:])
199 else:
200 c_data.switches = set()
202 c_data.args = split
204 event_name = "Command" + c_data.name.capitalize()
205 if event_name in events:
206 result = trigger(event_name, c_data)
208 if result:
209 c_data.window.write("* /%s: %s" % (c_data.name, result[0]))
210 else:
211 trigger("Command", c_data)
213 if not c_data.done:
214 c_data.window.write("* /%s: No such command exists" % (c_data.name))
216 # Script stuff starts here
218 def onCommandPyeval(e):
219 loc = sys.modules.copy()
220 loc.update(e.__dict__)
221 import pydoc #fix nonresponsive help() command
222 old_pager, pydoc.pager = pydoc.pager, pydoc.plainpager
223 try:
224 result = repr(eval(' '.join(e.args), loc))
225 if 's' in e.switches:
226 run(
227 'say - %s => %s' % (' '.join(e.args),result),
228 e.window,
229 e.network
231 else:
232 e.window.write(result)
233 except:
234 for line in traceback.format_exc().split('\n'):
235 e.window.write(line)
236 pydoc.pager = old_pager
238 def onCommandPyexec(e):
239 loc = sys.modules.copy()
240 loc.update(e.__dict__)
241 import pydoc #fix nonresponsive help() command
242 old_pager, pydoc.pager = pydoc.pager, pydoc.plainpager
243 try:
244 exec ' '.join(e.args) in loc
245 except:
246 for line in traceback.format_exc().split('\n'):
247 e.window.write(line)
248 pydoc.pager = old_pager
250 def onCommandLoad(e):
251 if e.args:
252 name = e.args[0]
253 else:
254 e.window.write('Usage: /load scriptname')
256 try:
257 if load(name):
258 e.window.write("* The script '%s' has been loaded." % name)
259 else:
260 raise CommandError("The script is already loaded; use /reload instead")
261 except:
262 e.window.write(traceback.format_exc(), line_ending='')
263 raise CommandError("Error loading the script")
265 def onCommandUnload(e):
266 if e.args:
267 name = e.args[0]
268 else:
269 e.window.write('Usage: /unload scriptname')
271 if is_loaded(name):
272 unload(name)
273 e.window.write("* The script '%s' has been unloaded." % name)
274 else:
275 raise CommandError("No such script is loaded")
277 def onCommandReload(e):
278 if e.args:
279 name = e.args[0]
280 else:
281 e.window.write('Usage: /reload scriptname')
283 try:
284 if reload(name):
285 e.window.write("* The script '%s' has been reloaded." % name)
286 else:
287 raise CommandError("The script isn't loaded yet; use /load instead")
288 except:
289 e.window.write(traceback.format_exc(), line_ending='')
291 def onCommandScripts(e):
292 e.window.write("Loaded scripts:")
293 for name in loaded:
294 e.window.write("* %s" % name)
296 def onCommandEcho(e):
297 e.window.write(' '.join(e.args))
299 name = ''
300 for name in globals():
301 if name.startswith('onCommand'):
302 register(name[2:], "on", globals()[name], '_events')
303 del name