From 03fe3305d9592b54813bd2c3d46b5cffe9f9d808 Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Sat, 7 Apr 2012 18:21:56 -0400 Subject: [PATCH] Avoid duplicating code thanks to _BaseManager --- earwigbot/managers.py | 175 ++++++++++++++-------------------------------- earwigbot/wiki/sitesdb.py | 2 +- 2 files changed, 55 insertions(+), 122 deletions(-) diff --git a/earwigbot/managers.py b/earwigbot/managers.py index 5df4e73..d3fbfca 100644 --- a/earwigbot/managers.py +++ b/earwigbot/managers.py @@ -33,22 +33,22 @@ from earwigbot.tasks import BaseTask __all__ = ["CommandManager", "TaskManager"] class _BaseManager(object): - pass - - -class CommandManager(_BaseManager): - def __init__(self, bot): + def __init__(self, bot, name, attribute, base): 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): - for name in self._commands: + for name in self._resources: 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 an instance of the 'Command' class inside (assuming it is an instance @@ -59,30 +59,30 @@ class CommandManager(_BaseManager): try: module = imp.load_module(name, f, path, desc) 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)) return finally: f.close() + attr = self._resource_attribute + if not hasattr(module, attr): + return # No resources in this module + resource_class = getattr(module, attr) 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: - 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 - if not isinstance(command, BaseCommand): + if not isinstance(resource, self._resource_base): 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): - """Load all valid commands in a given directory.""" + """Load all valid resources in a given directory.""" processed = [] for name in listdir(dir): if not name.endswith(".py") and not name.endswith(".pyc"): @@ -91,26 +91,40 @@ class CommandManager(_BaseManager): continue modname = sub("\.pyc?$", "", name) # Remove extension if modname not in processed: - self._load_command(modname, dir) + self._load_resource(modname, dir) processed.append(modname) 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): """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 command.check(data): try: @@ -120,24 +134,10 @@ class CommandManager(_BaseManager): self.logger.exception(e.format(data.command)) 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): 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): """Wrapper for task classes: run the task and catch any errors.""" @@ -150,74 +150,14 @@ class TaskManager(_BaseManager): msg = "Task '{0}' finished without error" 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): """Start a given task in a new thread. kwargs are passed to task.run""" msg = "Starting task '{0}' in a new thread" self.logger.info(msg.format(task_name)) - with self._task_access_lock: + with self._resource_access_lock: try: - task = self._tasks[task_name] + task = self._resources[task_name] except KeyError: e = "Couldn't find task '{0}':" 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 else: # Otherwise, just pass task_name 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] diff --git a/earwigbot/wiki/sitesdb.py b/earwigbot/wiki/sitesdb.py index 7ad8bf8..6178564 100644 --- a/earwigbot/wiki/sitesdb.py +++ b/earwigbot/wiki/sitesdb.py @@ -55,7 +55,7 @@ class SitesDB(object): def __init__(self, config): """Set up the manager with an attribute for the BotConfig object.""" 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._cookiejar = None