From 2206502ccaaf51b2a009c385004cfd7f11f3f739 Mon Sep 17 00:00:00 2001 From: jesopo Date: Sat, 1 Sep 2018 18:27:10 +0100 Subject: [PATCH 1/5] Don't purge children on purge_context() in EventHook, make event_context str(uuid) instead of just uuid in ModuleManager --- EventManager.py | 26 ++++++++++---------------- ModuleManager.py | 3 +-- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/EventManager.py b/EventManager.py index 326192ad..67cd1f76 100644 --- a/EventManager.py +++ b/EventManager.py @@ -86,14 +86,13 @@ class EventHook(object): self._hooks = [] self._stored_events = [] self._context_hooks = {} - self._current_context = None def _make_event(self, kwargs): return Event(self.bot, self.name, **kwargs) def _get_path(self): - path = [self.name] - parent = self.parent + path = [] + parent = self while not parent == None and not parent.name == None: path.append(parent.name) parent = parent.parent @@ -134,7 +133,7 @@ class EventHook(object): for event_name in event_chain: event_obj = event_obj.get_child(event_name) if not context == None: - event_obj = event_obj.new_context(context) + return event_obj.new_context(context) return event_obj if extra_subevents: @@ -170,10 +169,9 @@ class EventHook(object): start = time.monotonic() event = self._make_event(kwargs) - called = 0 returns = [] - for hook in self.get_hooks(): - if (maximum and called == maximum) or event.eaten: + for hook in self.get_hooks()[:maximum]: + if event.eaten: break try: returns.append(hook.call(event)) @@ -181,10 +179,8 @@ class EventHook(object): traceback.print_exc() self.bot.log.error("failed to call event \"%s\"", [ event_path], exc_info=True) - called += 1 - end = time.monotonic() - total_milliseconds = (end - start) * 1000 + total_milliseconds = (time.monotonic() - start) * 1000 self.bot.log.debug("event \"%s\" called in %fms", [ event_path, total_milliseconds]) @@ -204,8 +200,7 @@ class EventHook(object): del self._children[child_name_lower] def check_purge(self): - if len(self.get_hooks()) == 0 and len(self.get_children() - ) == 0 and not self.parent == None: + if self.is_empty() and not self.parent == None: self.parent.remove_child(self.name) self.parent.check_purge() @@ -216,15 +211,14 @@ class EventHook(object): def purge_context(self, context): if self.has_context(context): self.remove_context(context) + for child_name in self.get_children()[:]: child = self.get_child(child_name) child.purge_context(context) - if child.is_empty(): - self.remove_child(child_name) def get_hooks(self): - return sorted(self._hooks + list(itertools.chain.from_iterable( - self._context_hooks.values())), key=lambda e: e.priority) + return sorted(self._hooks + sum(self._context_hooks.values(), []), + key=lambda e: e.priority) def get_children(self): return list(self._children.keys()) def is_empty(self): diff --git a/ModuleManager.py b/ModuleManager.py index 404502e8..d2457ae5 100644 --- a/ModuleManager.py +++ b/ModuleManager.py @@ -49,13 +49,12 @@ class ModuleManager(object): if not inspect.isclass(module.Module): raise ImportError("module '%s' has a Module attribute but it is not a class.") - event_context = uuid.uuid4() + event_context = str(uuid.uuid4()) module_object = module.Module(self.bot, self.events.new_context( event_context)) if not hasattr(module_object, "_name"): module_object._name = name.title() module_object._event_context = event_context - module_object._is_unloaded = False module_object._import_name = name assert not module_object._name in self.modules, ( From 164f0b0bbf0555436ab161f9bcb47ccc28c280cf Mon Sep 17 00:00:00 2001 From: jesopo Date: Sat, 1 Sep 2018 18:49:50 +0100 Subject: [PATCH 2/5] Log when a module is unloaded, including how many references are left to the module object --- ModuleManager.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ModuleManager.py b/ModuleManager.py index d2457ae5..e0b5a215 100644 --- a/ModuleManager.py +++ b/ModuleManager.py @@ -91,4 +91,10 @@ class ModuleManager(object): self.events.purge_context(event_context) del sys.modules[name] + references = sys.getrefcount(module) del module + references -= 1 # 'del module' removes one reference + references -= 1 # one of the refs is from getrefcount + + self.bot.log.info("Module '%s' unloaded (%d reference%s)", + [name, references, "" if references == 1 else "s"]) From d5bd32a4c7ce34b8dc70ca2cbda6538395f41a09 Mon Sep 17 00:00:00 2001 From: jesopo Date: Sat, 1 Sep 2018 18:58:20 +0100 Subject: [PATCH 3/5] Remove unneeded "import gc" in ModuleManager.py --- ModuleManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ModuleManager.py b/ModuleManager.py index e0b5a215..a05918cb 100644 --- a/ModuleManager.py +++ b/ModuleManager.py @@ -1,4 +1,4 @@ -import gc, glob, imp, inspect, os, sys, uuid +import glob, imp, inspect, os, sys, uuid class ModuleManager(object): def __init__(self, bot, events, directory="modules"): From 23f859beb25af33d9ade8efdfb53337ae2227b5f Mon Sep 17 00:00:00 2001 From: jesopo Date: Sat, 1 Sep 2018 19:07:50 +0100 Subject: [PATCH 4/5] Don't persist timer, use events.on("send.stdout") --- modules/ducks.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/ducks.py b/modules/ducks.py index 9306f518..e7fd00c3 100644 --- a/modules/ducks.py +++ b/modules/ducks.py @@ -47,7 +47,7 @@ class Module(object): wait = self.get_random_duck_time() self.bot.log.info("Sending out a wave of ducks in %s seconds", [wait]) - self.bot.add_timer("show-duck", wait) + self.bot.add_timer("show-duck", wait, persist=False) def bootstrap(self, event): for server in self.bot.servers.values(): @@ -210,12 +210,11 @@ class Module(object): channel.set_setting("active-duck", False) def duck_decoy(self, event): - event["stdout"].write(random.choice(DUCK_LIST)) + self.events.on("send").on("stdout").call(target=event["channel"], + module_name="Ducks", server=event["server"], + message=random.choice(DUCK_LIST)) def set_decoy(self, event): - channel = event["target"] - next_decoy_time = self.get_random_duck_time() - - self.bot.add_timer("duck-decoy", next_decoy_time, None, None, False, - channel=channel) + self.bot.add_timer("duck-decoy", next_decoy_time, persist=False, + server=event["server"], channel=event["target"]) From e9b5519e86f48102e8c696d1ae6acaaaeb2c5180 Mon Sep 17 00:00:00 2001 From: jesopo Date: Sat, 1 Sep 2018 19:11:25 +0100 Subject: [PATCH 5/5] re-add self.events in ducks.py --- modules/ducks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ducks.py b/modules/ducks.py index e7fd00c3..3056d7bb 100644 --- a/modules/ducks.py +++ b/modules/ducks.py @@ -16,6 +16,7 @@ DUCK_LIST = [ class Module(object): def __init__(self, bot, events): self.bot = bot + self.events = events events.on("received.command.bef").hook(self.duck_bef, help="Befriend a duck!")