Skip to content

Commit

Permalink
requested changes
Browse files Browse the repository at this point in the history
  • Loading branch information
calebj committed Jun 11, 2018
1 parent 4d068cb commit 72dd79e
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 69 deletions.
86 changes: 47 additions & 39 deletions sopel/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from sopel import tools
from sopel import irc
from sopel.db import SopelDB
from sopel.tools import stderr, Identifier, iteritems
from sopel.tools import stderr, Identifier
import sopel.tools.jobs
from sopel.trigger import Trigger
from sopel.module import NOLIMIT
Expand Down Expand Up @@ -92,9 +92,9 @@ def __init__(self, config, daemon=False):

self._modules = dict()
"""A dictionary of modules currently registered by the bot. sys.modules
doesn't work because although del <module> removes it from the namespace,
it remains in sys.modules. Thus, we need another way to keep track of
what has been (un)loaded."""
doesn't work because although del <module> removes it from the
namespace, it remains in sys.modules. Thus, we need another way to keep
track of what has been (un)loaded."""

self.privileges = dict()
"""A dictionary of channels to their users and privilege levels
Expand Down Expand Up @@ -199,40 +199,6 @@ def setup(self):
else:
stderr("Warning: Couldn't load any modules")

def unregister_module(self, module):
for obj_name, obj in iteritems(vars(module)):
self.unregister(obj)
callables, _, _ = sopel.loader.clean_module(module, self.config)
if hasattr(module, "setup"):
delattr(module, "setup")
del self._modules[module.__name__]

def unregister(self, obj):
if not callable(obj):
return
if hasattr(obj, 'commands'):
module_name = obj.__module__.rsplit('.', 1)[-1]
category = getattr(obj, 'category', module_name)
if obj.commands in self._command_groups[category]:
self._command_groups[category].remove(obj.commands)
if len(self._command_groups[category]) is 0:
del self._command_groups[category]
for command, docs in obj._docs.items():
del self.doc[command]

if hasattr(obj, 'rule'): # commands and intents have it added
for rule in obj.rule:
callb_list = self._callables[obj.priority][rule]
if obj in callb_list:
callb_list.remove(obj)
if hasattr(obj, 'interval'):
for interval in obj.interval:
job = sopel.tools.jobs.Job(interval, obj)
self.scheduler.del_job(job)
if (getattr(obj, '__name__', None) == 'shutdown'
and obj in self.shutdown_methods):
self.shutdown_methods.remove(obj)

def register(self, callables, jobs, shutdowns, urls):
# Append module's shutdown function to the bot's list of functions to
# call on shutdown
Expand All @@ -248,7 +214,7 @@ def register(self, callables, jobs, shutdowns, urls):
# TODO doc and make decorator for this. Not sure if this is how
# it should work yet, so not making it public for 6.0.
category = getattr(callbl, 'category', module_name)
self._command_groups[category].append(callbl.commands)
self._command_groups[category].append(callbl.commands[0])
for command, docs in callbl._docs.items():
self.doc[command] = docs
for func in jobs:
Expand All @@ -272,6 +238,48 @@ def register_module(self, module):
self.register(*relevant_parts)
self._modules[module.__name__] = module

def unregister(self, callables, jobs, shutdowns, urls):
for shutdown in shutdowns:
if shutdown in self.shutdown_methods:
self.shutdown_methods.remove(shutdowns)

for callbl in callables:
if hasattr(callbl, 'rule'):
for rule in callbl.rule:
if callbl in self._callables[callbl.priority][rule]:
self._callables[callbl.priority][rule].remove(callbl)
else:
pattern = re.compile('.*')
if callbl in self._callables[callbl.priority][pattern]:
self._callables[callbl.priority][pattern].remove(callbl)

if hasattr(callbl, 'commands'):
module_name = callbl.__module__.rsplit('.', 1)[-1]
# TODO doc and make decorator for this. Not sure if this is how
# it should work yet, so not making it public for 6.0.
category = getattr(callbl, 'category', module_name)
if callbl.commands[0] in self._command_groups[category]:
self._command_groups[category].remove(callbl.commands[0])

for command, docs in callbl._docs.items():
self.doc.pop(command, None)

for func in jobs:
for interval in func.interval:
self.scheduler.del_job_by_params(interval, func)

for func in urls:
self.memory['url_callbacks'].pop(func.url_regex, None)

def unregister_module(self, module):
relevant_parts = sopel.loader.clean_module(module, self.config)
self.unregister(*relevant_parts)

if hasattr(module, "teardown"):
module.teardown(self)

del self._modules[module.__name__]

def part(self, channel, msg=None):
"""Part a channel."""
self.write(['PART', channel], msg)
Expand Down
1 change: 0 additions & 1 deletion sopel/modules/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ def help(bot, trigger):
for category, cmds in collections.OrderedDict(sorted(bot.command_groups.items())).items():
category = category.upper().ljust(name_length)
cmds = set(cmds) # remove duplicates
cmds = ['|'.join(cg) for cg in cmds]
cmds = ' '.join(cmds)
msg = category + ' ' + cmds
indent = ' ' * (name_length + 2)
Expand Down
102 changes: 74 additions & 28 deletions sopel/modules/reload.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,15 @@
"""
from __future__ import unicode_literals, absolute_import, print_function, division

import collections
import sys
import time
from sopel.tools import iteritems, get_raising_file_and_line
from sopel.tools import get_raising_file_and_line
import sopel.loader
import sopel.module
import subprocess
import os


@sopel.module.commands("reload")
@sopel.module.nickname_commands("reload")
@sopel.module.priority("low")
@sopel.module.thread(False)
@sopel.module.require_admin()
Expand All @@ -27,28 +25,41 @@ def f_reload(bot, trigger):
name = trigger.group(2)

if not name or name == '*' or name.upper() == 'ALL THE THINGS':
bot._callables = {
'high': collections.defaultdict(list),
'medium': collections.defaultdict(list),
'low': collections.defaultdict(list)
}
bot.shutdown_methods.clear()
bot.scheduler.clear_jobs()
bot._command_groups = collections.defaultdict(list)
bot.setup()
return bot.reply('done')

if name not in sys.modules:
return bot.reply('"%s" not loaded, try the `load` command' % name)
modules = sopel.loader.enumerate_modules(bot.config)
names = []
ok_names = []
failed_names = []

for name, module in bot._modules.items():
names.append(name)
try:
bot.unregister_module(module)
except Exception:
failed_names.append(name)
continue

if name not in modules:
failed_names.append(name)
continue

path, type_ = modules[name]
load_module(bot, name, path, type_)
ok_names.append(name)

return bot.say('Reloaded: %s\nFailed to reload: %s'
% (','.join(l) for l in (ok_names, failed_names)))
else:
if name not in bot._modules:
return bot.reply('"%s" not loaded, try the `load` command' % name)

old_module = bot._modules[name]
bot.unregister_module(old_module)
old_module = bot._modules[name]
bot.unregister_module(old_module)

modules = sopel.loader.enumerate_modules(bot.config)
if name not in modules:
return bot.reply('"%s" not loaded, try the `load` command' % name)
path, type_ = modules[name]
load_module(bot, name, path, type_)
modules = sopel.loader.enumerate_modules(bot.config)
if name not in modules:
return bot.reply('Module %s not found, was it deleted?' % name)
path, type_ = modules[name]
load_module(bot, name, path, type_)


def load_module(bot, name, path, type_):
Expand All @@ -65,7 +76,7 @@ def load_module(bot, name, path, type_):
bot.reply('%r (version: %s)' % (module, modified))


@sopel.module.commands('update')
@sopel.module.nickname_commands('update')
@sopel.module.require_admin()
def f_update(bot, trigger):
"""Pulls the latest versions of all modules from Git"""
Expand All @@ -77,7 +88,7 @@ def f_update(bot, trigger):
f_reload(bot, trigger)


@sopel.module.commands("load")
@sopel.module.nickname_commands("load")
@sopel.module.priority("low")
@sopel.module.thread(False)
@sopel.module.require_admin()
Expand All @@ -97,14 +108,14 @@ def f_load(bot, trigger):
path, type_ = mods[name]
load_module(bot, name, path, type_)

@sopel.module.commands("unload")

@sopel.module.nickname_commands("unload")
@sopel.module.priority("low")
@sopel.module.thread(False)
@sopel.module.require_admin()
def f_unload(bot, trigger):
""""Unloads" a module, for use by admins only."""
name = trigger.group(2)
path = ''
if name == bot.config.core.owner:
return bot.reply('What?')

Expand All @@ -114,3 +125,38 @@ def f_unload(bot, trigger):
old_module = bot._modules[name]
bot.unregister_module(old_module)
bot.reply('done.')


# Catch PM based messages
@sopel.module.commands("reload")
@sopel.module.priority("low")
@sopel.module.thread(False)
def pm_f_reload(bot, trigger):
"""Wrapper for allowing delivery of .reload command via PM"""
if trigger.is_privmsg:
f_reload(bot, trigger)


@sopel.module.commands('update')
def pm_f_update(bot, trigger):
"""Wrapper for allowing delivery of .update command via PM"""
if trigger.is_privmsg:
f_update(bot, trigger)


@sopel.module.commands("load")
@sopel.module.priority("low")
@sopel.module.thread(False)
def pm_f_load(bot, trigger):
"""Wrapper for allowing delivery of .load command via PM"""
if trigger.is_privmsg:
f_load(bot, trigger)


@sopel.module.commands("unload")
@sopel.module.priority("low")
@sopel.module.thread(False)
def pm_f_unload(bot, trigger):
"""Wrapper for allowing delivery of .unload command via PM"""
if trigger.is_privmsg:
f_unload(bot, trigger)
9 changes: 8 additions & 1 deletion sopel/tools/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,16 @@ def add_job(self, job):
self._jobs.put(job)

def del_job(self, del_job):
"""Iterates through the job queue and re-adds all but the given job"""
for i in range(self._jobs.qsize()):
job = self._jobs.get()
if job and job is not del_job:
if job is not del_job:
self._jobs.put(job)

def del_job_by_params(self, interval, func):
for i in range(self._jobs.qsize()):
job = self._jobs.get()
if job.func is not func or job.interval != interval:
self._jobs.put(job)

def clear_jobs(self):
Expand Down

0 comments on commit 72dd79e

Please sign in to comment.