diff --git a/i3pystatus/core/modules.py b/i3pystatus/core/modules.py index d06cd8b..fe45190 100644 --- a/i3pystatus/core/modules.py +++ b/i3pystatus/core/modules.py @@ -10,8 +10,12 @@ from i3pystatus.core.command import run_through_shell def is_method_of(method, object): """Decide whether ``method`` is contained within the MRO of ``object``.""" + if not callable(method) or not hasattr(method, "__name__"): + return False + if inspect.ismethod(method): + return method.__self__ is object for cls in inspect.getmro(object.__class__): - if method in cls.__dict__.values(): + if cls.__dict__.get(method.__name__, None) is method: return True return False diff --git a/tests/test_core_modules.py b/tests/test_core_modules.py index 4806dc8..0ca7897 100644 --- a/tests/test_core_modules.py +++ b/tests/test_core_modules.py @@ -4,6 +4,7 @@ from unittest.mock import MagicMock import pytest from i3pystatus import IntervalModule +from i3pystatus.core.modules import is_method_of left_click = 1 right_click = 3 @@ -120,3 +121,27 @@ def test_callback_handler_function(): dut = TestClicks() dut.on_click(scroll_up) callback_mock.callback.assert_called_once_with("upscroll") + + +def test_is_method_of(): + class TestClass: + def method(self): + pass + + # member assigned functions cannot be distinguished in unbound state + # by principle from methods, since both are functions. However, in + # most cases it can still be shown correctly that a member assigned + # function is not a method, since the member name and function name + # are different (in this example the assigned member is 'assigned_function', + # while the name of the function is 'len', hence is_method_of can say for + # sure that assigned_function isn't a method + assigned_function = len + member = 1234 + string_member = "abcd" + + object = TestClass() + for source_object in [object, TestClass]: + assert is_method_of(source_object.method, object) + assert not is_method_of(source_object.assigned_function, object) + assert not is_method_of(source_object.member, object) + assert not is_method_of(source_object.string_member, object)