Skip to content

Commit

Permalink
reload: Recurse into packaged modules
Browse files Browse the repository at this point in the history
Force-reload all sub-modules of packages recursively.

See sopel-irc#1056.
  • Loading branch information
dgw authored and unknown committed Oct 27, 2018
1 parent 0a69d29 commit 0af5ba7
Showing 1 changed file with 35 additions and 5 deletions.
40 changes: 35 additions & 5 deletions sopel/modules/reload.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
import sopel.module
import subprocess

try:
from importlib import reload
except ImportError:
try:
from imp import reload
except ImportError:
pass # fallback to builtin if neither module is available


@sopel.module.nickname_commands("reload")
@sopel.module.priority("low")
Expand All @@ -37,14 +45,38 @@ def f_reload(bot, trigger):
bot.setup()
return bot.reply('done')

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

reload_module_tree(bot, name)


def reload_module_tree(bot, name, seen=None):
from types import ModuleType

old_module = sys.modules[name]

if seen is None:
seen = {}
if name not in seen:
seen[name] = []

old_callables = {}
for obj_name, obj in iteritems(vars(old_module)):
bot.unregister(obj)
if callable(obj):
bot.unregister(obj)
elif (type(obj) is ModuleType and
obj.__name__.startswith(name + '.') and
obj.__name__ not in sys.builtin_module_names):
# recurse into submodules, see issue 1056
if obj not in seen[name]:
seen[name].append(obj)
reload(obj)
reload_module_tree(bot, obj.__name__, seen)

modules = sopel.loader.enumerate_modules(bot.config)
if name not in modules:
return # Only reload the top-level module, once recursion is finished

# Also remove all references to sopel callables from top level of the
# module, so that they will not get loaded again if reloading the
Expand All @@ -53,12 +85,10 @@ def f_reload(bot, trigger):
delattr(old_module, obj_name)

# Also delete the setup function
# Sub-modules shouldn't have setup functions, so do after the recursion check
if hasattr(old_module, "setup"):
delattr(old_module, "setup")

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_)

Expand Down

0 comments on commit 0af5ba7

Please sign in to comment.