A Python robot that edits Wikipedia and interacts with people over IRC https://en.wikipedia.org/wiki/User:EarwigBot
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

task_manager.py 3.1 KiB

13 years ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. # -*- coding: utf-8 -*-
  2. """
  3. EarwigBot's Wiki Bot Task Manager
  4. This module provides some functions to run and load bot tasks from wiki/tasks/.
  5. """
  6. import time
  7. import traceback
  8. import threading
  9. import os
  10. from core import config
  11. # store loaded tasks as a dict where the key is the task name and the value is
  12. # an instance of the task class (wiki.tasks.task_file.Task())
  13. task_list = dict()
  14. def load_tasks():
  15. """Load all valid task classes from wiki/tasks/, and add them to the
  16. task_list variable."""
  17. files = os.listdir(os.path.join("wiki", "tasks"))
  18. files.sort() # alphabetically sort all files in wiki/tasks/
  19. for f in files:
  20. if not os.path.isfile(os.path.join("wiki", "tasks", f)):
  21. continue # ignore non-files
  22. if f.startswith("_") or not f.endswith(".py"):
  23. continue # ignore non-python files or files beginning with an _
  24. load_class_from_file(f)
  25. print "Found %s tasks: %s." % (len(task_list), ', '.join(task_list.keys()))
  26. def load_class_from_file(f):
  27. """Look in a given file for the task class."""
  28. global task_list
  29. module = f[:-3] # strip .py from end
  30. try:
  31. exec "from wiki.tasks import %s as m" % module
  32. except: # importing the file failed for some reason...
  33. print "Couldn't load task file %s:" % f
  34. traceback.print_exc()
  35. return
  36. try:
  37. task_class = m.Task
  38. except:
  39. print "Couldn't find or get task class in file %s:" % f
  40. traceback.print_exc()
  41. return
  42. task_name = task_class.task_name
  43. task_list[task_name] = task_class()
  44. print "Added task %s from wiki/tasks/%s..." % (task_name, f)
  45. def start_tasks(now=time.gmtime()):
  46. """Start all tasks that are supposed to be run at a given time."""
  47. tasks = config.schedule(now.tm_min, now.tm_hour, now.tm_mday, now.tm_mon,
  48. now.tm_wday) # get list of tasks to run this turn
  49. for task in tasks:
  50. if isinstance(task, list): # they've specified kwargs
  51. start_task(task[0], **task[1]) # so pass those to start_task
  52. else: # otherwise, just pass task_name
  53. start_task(task)
  54. def start_task(task_name, **kwargs):
  55. """Start a given task in a new thread. Pass args to the task's run()
  56. function."""
  57. print "Starting task '{0}' in a new thread...".format(task_name)
  58. try:
  59. task = task_list[task_name]
  60. except KeyError:
  61. print ("Couldn't find task '{0}': wiki/tasks/{0}.py does not exist.").format(task_name)
  62. return
  63. task_thread = threading.Thread(target=lambda: task_wrapper(task, **kwargs))
  64. task_thread.name = "{0} ({1})".format(task_name, time.strftime("%b %d %H:%M:%S"))
  65. # stop bot task threads automagically if the main bot stops
  66. task_thread.daemon = True
  67. task_thread.start()
  68. def task_wrapper(task, **kwargs):
  69. """Wrapper for task classes: run the task and catch any errors."""
  70. try:
  71. task.run(**kwargs)
  72. except:
  73. print "Task '{0}' raised an exception and had to stop:".format(task.task_name)
  74. traceback.print_exc()
  75. else:
  76. print "Task '{0}' finished without error.".format(task.task_name)