From aed169de4d43bd0a8223296328aa98d6bab1c213 Mon Sep 17 00:00:00 2001 From: enkore Date: Wed, 27 Jan 2016 19:07:59 +0100 Subject: [PATCH] Implement decided resolution of #304 - Remove self for normal callables - Retain self for methods (of course) - Add decorator to retrieve self for special callbacks that need it (Yes, the example is kinda stupid and would be unnecessary with #300) --- i3pystatus/core/modules.py | 15 ++++++++++++++- i3pystatus/core/util.py | 31 ++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/i3pystatus/core/modules.py b/i3pystatus/core/modules.py index f38cc6a..ab61442 100644 --- a/i3pystatus/core/modules.py +++ b/i3pystatus/core/modules.py @@ -1,3 +1,5 @@ +import inspect + from i3pystatus.core.settings import SettingsBase from i3pystatus.core.threading import Manager from i3pystatus.core.util import (convert_position, @@ -6,6 +8,14 @@ from i3pystatus.core.command import execute from i3pystatus.core.command import run_through_shell +def is_method_of(method, object): + """Decide whether ``method`` is contained within the MRO of ``object``.""" + for cls in inspect.getmro(object.__class__): + if method in cls.__dict__.values(): + return True + return False + + class Module(SettingsBase): output = None position = 0 @@ -80,8 +90,11 @@ class Module(SettingsBase): else: args = [] - if callable(cb): + our_method = is_method_of(cb, self) + if callable(cb) and not our_method: self.__log_button_event(button, cb, args, "Python callback") + cb(*args) + elif our_method: cb(self, *args) elif hasattr(self, cb): if cb is not "run": diff --git a/i3pystatus/core/util.py b/i3pystatus/core/util.py index b1d0e2d..c5169f4 100644 --- a/i3pystatus/core/util.py +++ b/i3pystatus/core/util.py @@ -3,7 +3,7 @@ import functools import re import socket import string - +import inspect from threading import Timer, RLock @@ -558,3 +558,32 @@ class MultiClickHandler(object): self.clear_timer() return ret + + +def get_module(function): + """Function decorator for retrieving the ``self`` argument from the stack. + + Intended for use with callbacks that need access to a modules variables, for example: + + .. code:: python + + from i3pystatus import Status + from i3pystatus.core.util import get_module + from i3pystatus.core.command import execute + status = Status(...) + # other modules etc. + @get_module + def display_ip_verbose(module): + execute('sh -c "ip addr show dev {dev} | xmessage -file -"'.format(dev=module.interface)) + status.register("network", interface="wlan1", on_leftclick=display_ip_verbose) + """ + @functools.wraps(function) + def call_wrapper(*args, **kwargs): + stack = inspect.stack() + caller_frame_info = stack[1] + self = caller_frame_info[0].f_locals["self"] + # not completly sure whether this is necessary + # see note in Python docs about stack frames + del stack + function(self, *args, **kwargs) + return call_wrapper