Browse Source

Avoid duplicating code thanks to _BaseManager

tags/v0.1^2
Ben Kurtovic 12 years ago
parent
commit
03fe3305d9
2 changed files with 55 additions and 122 deletions
  1. +54
    -121
      earwigbot/managers.py
  2. +1
    -1
      earwigbot/wiki/sitesdb.py

+ 54
- 121
earwigbot/managers.py View File

@@ -33,22 +33,22 @@ from earwigbot.tasks import BaseTask
__all__ = ["CommandManager", "TaskManager"] __all__ = ["CommandManager", "TaskManager"]


class _BaseManager(object): class _BaseManager(object):
pass


class CommandManager(_BaseManager):
def __init__(self, bot):
def __init__(self, bot, name, attribute, base):
self.bot = bot self.bot = bot
self.logger = bot.logger.getChild("commands")
self._commands = {}
self._command_access_lock = Lock()
self.logger = bot.logger.getChild(name)

self._resources = {}
self._resource_name = name # e.g. "commands" or "tasks"
self._resource_attribute = attribute # e.g. "Command" or "Task"
self._resource_base = base # e.g. BaseCommand or BaseTask
self._resource_access_lock = Lock()


def __iter__(self): def __iter__(self):
for name in self._commands:
for name in self._resources:
yield name yield name


def _load_command(self, name, path):
"""Load a specific command from a module, identified by name and path.
def _load_resource(self, name, path):
"""Load a specific resource from a module, identified by name and path.


We'll first try to import it using imp magic, and if that works, make We'll first try to import it using imp magic, and if that works, make
an instance of the 'Command' class inside (assuming it is an instance an instance of the 'Command' class inside (assuming it is an instance
@@ -59,30 +59,30 @@ class CommandManager(_BaseManager):
try: try:
module = imp.load_module(name, f, path, desc) module = imp.load_module(name, f, path, desc)
except Exception: except Exception:
e = "Couldn't load module {0} from {1}"
e = "Couldn't load module {0} (from {1})"
self.logger.exception(e.format(name, path)) self.logger.exception(e.format(name, path))
return return
finally: finally:
f.close() f.close()


attr = self._resource_attribute
if not hasattr(module, attr):
return # No resources in this module
resource_class = getattr(module, attr)
try: try:
command_class = module.Command
except AttributeError:
return # No command in this module
try:
command = command_class(self.bot)
resource = resource_class(self.bot) # Create instance of resource
except Exception: except Exception:
e = "Error initializing Command() class in {0} (from {1})"
self.logger.exception(e.format(name, path))
e = "Error instantiating {0} class in {1} (from {2})"
self.logger.exception(e.format(attr, name, path))
return return
if not isinstance(command, BaseCommand):
if not isinstance(resource, self._resource_base):
return return


self._commands[command.name] = command
self.logger.debug("Loaded command {0}".format(command.name))
self._resources[resource.name] = resource
self.logger.debug("Loaded {0} {1}".format(attr.lower(), resource.name))


def _load_directory(self, dir): def _load_directory(self, dir):
"""Load all valid commands in a given directory."""
"""Load all valid resources in a given directory."""
processed = [] processed = []
for name in listdir(dir): for name in listdir(dir):
if not name.endswith(".py") and not name.endswith(".pyc"): if not name.endswith(".py") and not name.endswith(".pyc"):
@@ -91,26 +91,40 @@ class CommandManager(_BaseManager):
continue continue
modname = sub("\.pyc?$", "", name) # Remove extension modname = sub("\.pyc?$", "", name) # Remove extension
if modname not in processed: if modname not in processed:
self._load_command(modname, dir)
self._load_resource(modname, dir)
processed.append(modname) processed.append(modname)


def load(self): def load(self):
"""Load (or reload) all valid commands into self._commands."""
with self._command_access_lock:
self._commands.clear()
builtin_dir = path.join(path.dirname(__file__), "commands")
plugins_dir = path.join(self.bot.config.root_dir, "commands")
self._load_directory(builtin_dir) # Built-in commands
self._load_directory(plugins_dir) # Custom commands, aka plugins

msg = "Loaded {0} commands: {1}"
commands = ", ".join(self._commands.keys())
self.logger.info(msg.format(len(self._commands), commands))
"""Load (or reload) all valid resources into self._resources."""
name = self._resource_name # e.g. "commands" or "tasks"
with self._resource_access_lock:
self._resources.clear()
builtin_dir = path.join(path.dirname(__file__), name)
plugins_dir = path.join(self.bot.config.root_dir, name)
self._load_directory(builtin_dir) # Built-in resources
self._load_directory(plugins_dir) # Custom resources, aka plugins

msg = "Loaded {0} {1}: {2}"
resources = ", ".join(self._resources.keys())
self.logger.info(msg.format(len(self._resources), name, resources))

def get(self, key):
"""Return the class instance associated with a certain resource.

Will raise KeyError if the resource (command or task) is not found.
"""
return self._resources[key]


class CommandManager(_BaseManager):
def __init__(self, bot):
super(CommandManager, self).__init__(bot, "commands", "Command",
BaseCommand)


def check(self, hook, data): def check(self, hook, data):
"""Given an IRC event, check if there's anything we can respond to.""" """Given an IRC event, check if there's anything we can respond to."""
with self._command_access_lock:
for command in self._commands.values():
with self._resource_access_lock:
for command in self._resources.values():
if hook in command.hooks: if hook in command.hooks:
if command.check(data): if command.check(data):
try: try:
@@ -120,24 +134,10 @@ class CommandManager(_BaseManager):
self.logger.exception(e.format(data.command)) self.logger.exception(e.format(data.command))
break break


def get(self, command_name):
"""Return the class instance associated with a certain command name.

Will raise KeyError if the command is not found.
"""
return self._command[command_name]



class TaskManager(_BaseManager): class TaskManager(_BaseManager):
def __init__(self, bot): def __init__(self, bot):
self.bot = bot
self.logger = bot.logger.getChild("tasks")
self._tasks = {}
self._task_access_lock = Lock()

def __iter__(self):
for name in self._tasks:
yield name
super(TaskManager, self).__init__(bot, "tasks", "Task", BaseTask)


def _wrapper(self, task, **kwargs): def _wrapper(self, task, **kwargs):
"""Wrapper for task classes: run the task and catch any errors.""" """Wrapper for task classes: run the task and catch any errors."""
@@ -150,74 +150,14 @@ class TaskManager(_BaseManager):
msg = "Task '{0}' finished without error" msg = "Task '{0}' finished without error"
self.logger.info(msg.format(task.name)) self.logger.info(msg.format(task.name))


def _load_task(self, name, path):
"""Load a specific task from a module, identified by name and path.

We'll first try to import it using imp magic, and if that works, make
an instance of the 'Task' class inside (assuming it is an instance of
BaseTask), add it to self._tasks, and log the addition. Any problems
along the way will either be ignored or logged.
"""
f, path, desc = imp.find_module(name, [path])
try:
module = imp.load_module(name, f, path, desc)
except Exception:
e = "Couldn't load module {0} from {1}"
self.logger.exception(e.format(name, path))
return
finally:
f.close()

try:
task_class = module.Task
except AttributeError:
return # No task in this module
try:
task = task_class(self.bot)
except Exception:
e = "Error initializing Task() class in {0} (from {1})"
self.logger.exception(e.format(name, path))
return
if not isinstance(task, BaseTask):
return

self._tasks[task.name] = task
self.logger.debug("Loaded task {0}".format(task.name))

def _load_directory(self, dir):
"""Load all valid tasks in a given directory."""
processed = []
for name in listdir(dir):
if not name.endswith(".py") and not name.endswith(".pyc"):
continue
if name.startswith("_") or name.startswith("."):
continue
modname = sub("\.pyc?$", "", name) # Remove extension
if modname not in processed:
self._load_task(modname, dir)
processed.append(modname)

def load(self):
"""Load (or reload) all valid tasks into self._tasks."""
with self._task_access_lock:
self._tasks.clear()
builtin_dir = path.join(path.dirname(__file__), "tasks")
plugins_dir = path.join(self.bot.config.root_dir, "tasks")
self._load_directory(builtin_dir) # Built-in tasks
self._load_directory(plugins_dir) # Custom tasks, aka plugins

msg = "Loaded {0} tasks: {1}"
tasks = ', '.join(self._tasks.keys())
self.logger.info(msg.format(len(self._tasks), tasks))

def start(self, task_name, **kwargs): def start(self, task_name, **kwargs):
"""Start a given task in a new thread. kwargs are passed to task.run""" """Start a given task in a new thread. kwargs are passed to task.run"""
msg = "Starting task '{0}' in a new thread" msg = "Starting task '{0}' in a new thread"
self.logger.info(msg.format(task_name)) self.logger.info(msg.format(task_name))


with self._task_access_lock:
with self._resource_access_lock:
try: try:
task = self._tasks[task_name]
task = self._resources[task_name]
except KeyError: except KeyError:
e = "Couldn't find task '{0}':" e = "Couldn't find task '{0}':"
self.logger.error(e.format(task_name)) self.logger.error(e.format(task_name))
@@ -241,10 +181,3 @@ class TaskManager(_BaseManager):
self.start(task[0], **task[1]) # so pass those to start self.start(task[0], **task[1]) # so pass those to start
else: # Otherwise, just pass task_name else: # Otherwise, just pass task_name
self.start(task) self.start(task)

def get(self, task_name):
"""Return the class instance associated with a certain task name.

Will raise KeyError if the task is not found.
"""
return self._tasks[task_name]

+ 1
- 1
earwigbot/wiki/sitesdb.py View File

@@ -55,7 +55,7 @@ class SitesDB(object):
def __init__(self, config): def __init__(self, config):
"""Set up the manager with an attribute for the BotConfig object.""" """Set up the manager with an attribute for the BotConfig object."""
self.config = config self.config = config
self._sitesdb = path.join(config.root_dir, "sitesdb")
self._sitesdb = path.join(config.root_dir, "sites.db")
self._cookie_file = path.join(config.root_dir, ".cookies") self._cookie_file = path.join(config.root_dir, ".cookies")
self._cookiejar = None self._cookiejar = None




Loading…
Cancel
Save