diff --git a/mitmproxy/command.py b/mitmproxy/command.py index 451415765..114e882d8 100644 --- a/mitmproxy/command.py +++ b/mitmproxy/command.py @@ -1,5 +1,5 @@ """ - This module manges and invokes typed commands. + This module manages and invokes typed commands. """ import inspect import types @@ -131,8 +131,13 @@ class CommandManager(mitmproxy.types._CommandBase): for i in dir(addon): if not i.startswith("__"): o = getattr(addon, i) - if hasattr(o, "command_path"): - self.add(o.command_path, o) + try: + is_command = hasattr(o, "command_path") + except Exception: + pass # hasattr may raise if o implements __getattr__. + else: + if is_command: + self.add(o.command_path, o) def add(self, path: str, func: typing.Callable): self.commands[path] = Command(self, path, func) diff --git a/test/mitmproxy/test_command.py b/test/mitmproxy/test_command.py index e2b807532..3d0a43f88 100644 --- a/test/mitmproxy/test_command.py +++ b/test/mitmproxy/test_command.py @@ -309,6 +309,31 @@ class TDec: pass +class TAttr: + def __getattr__(self, item): + raise IOError + + +class TCmds(TAttr): + def __init__(self): + self.TAttr = TAttr() + + @command.command("empty") + def empty(self) -> None: + pass + + +def test_collect_commands(): + """ + This tests for the error thrown by hasattr() + """ + with taddons.context() as tctx: + c = command.CommandManager(tctx.master) + a = TCmds() + c.collect_commands(a) + assert "empty" in c.commands + + def test_decorator(): with taddons.context() as tctx: c = command.CommandManager(tctx.master)