def register(self, addon): """ Register an addon, call its load event, and then register all its sub-addons. This should be used by addons that dynamically manage addons. If the calling addon is already running, it should follow with running and configure events. Must be called within a current context. """ for a in traverse([addon]): name = _get_name(a) if name in self.lookup: raise exceptions.AddonManagerError( "An addon called '%s' already exists." % name ) l = Loader(self.master) self.invoke_addon(addon, "load", l) for a in traverse([addon]): name = _get_name(a) self.lookup[name] = a for a in traverse([addon]): self.master.commands.collect_commands(a) self.master.options.process_deferred() return addon
def invoke_addon_sync(self, addon, event: hooks.Hook): """ Invoke an event on an addon and all its children. """ for addon, func in self._iter_hooks(addon, event): if inspect.iscoroutinefunction(func): raise exceptions.AddonManagerError( f"Async handler {event.name} ({addon}) cannot be called from sync context" ) func(*event.args())
def invoke_addon(self, addon, name, *args, **kwargs): """ Invoke an event on an addon and all its children. """ if name not in eventsequence.Events: raise exceptions.AddonManagerError("Unknown event: %s" % name) for a in traverse([addon]): func = getattr(a, name, None) if func: if callable(func): func(*args, **kwargs) elif isinstance(func, types.ModuleType): # we gracefully exclude module imports with the same name as hooks. # For example, a user may have "from mitmproxy import log" in an addon, # which has the same name as the "log" hook. In this particular case, # we end up in an error loop because we "log" this error. pass else: raise exceptions.AddonManagerError( "Addon handler {} ({}) not callable".format(name, a))
def invoke_addon(self, addon, name, *args, **kwargs): """ Invoke an event on an addon and all its children. This method must run within an established handler context. """ if name not in eventsequence.Events: name = "event_" + name for a in traverse([addon]): func = getattr(a, name, None) if func: if not callable(func): raise exceptions.AddonManagerError( "Addon handler %s not callable" % name) func(*args, **kwargs)
def remove(self, addon): """ Remove an addon and all its sub-addons. If the addon is not in the chain - that is, if it's managed by a parent addon - it's the parent's responsibility to remove it from its own addons attribute. """ for a in traverse([addon]): n = _get_name(a) if n not in self.lookup: raise exceptions.AddonManagerError("No such addon: %s" % n) self.chain = [i for i in self.chain if i is not a] del self.lookup[_get_name(a)] self.invoke_addon(addon, "done")
def invoke_addon(self, addon, event: hooks.Hook): """ Invoke an event on an addon and all its children. """ assert isinstance(event, hooks.Hook) for a in traverse([addon]): func = getattr(a, event.name, None) if func: if callable(func): func(*event.args()) elif isinstance(func, types.ModuleType): # we gracefully exclude module imports with the same name as hooks. # For example, a user may have "from mitmproxy import log" in an addon, # which has the same name as the "log" hook. In this particular case, # we end up in an error loop because we "log" this error. pass else: raise exceptions.AddonManagerError( f"Addon handler {event.name} ({a}) not callable")
def _iter_hooks(self, addon, event: hooks.Hook): """ Enumerate all hook callables belonging to the given addon """ assert isinstance(event, hooks.Hook) for a in traverse([addon]): func = getattr(a, event.name, None) if func: if callable(func): yield a, func elif isinstance(func, types.ModuleType): # we gracefully exclude module imports with the same name as hooks. # For example, a user may have "from mitmproxy import log" in an addon, # which has the same name as the "log" hook. In this particular case, # we end up in an error loop because we "log" this error. pass else: raise exceptions.AddonManagerError( f"Addon handler {event.name} ({a}) not callable")
def register(self, addon): """ Register an addon, call its load event, and then register all its sub-addons. This should be used by addons that dynamically manage addons. If the calling addon is already running, it should follow with running and configure events. Must be called within a current context. """ api_changes = { # mitmproxy 6 -> mitmproxy 7 "clientconnect": "client_connected", "clientdisconnect": "client_disconnected", "serverconnect": "server_connect and server_connected", "serverdisconnect": "server_disconnected", } for a in traverse([addon]): for old, new in api_changes.items(): if hasattr(a, old): ctx.log.warn( f"The {old} event has been removed, use {new} instead. " f"For more details, see https://docs.mitmproxy.org/stable/addons-events/." ) name = _get_name(a) if name in self.lookup: raise exceptions.AddonManagerError( "An addon called '%s' already exists." % name) l = Loader(self.master) self.invoke_addon(addon, LoadHook(l)) for a in traverse([addon]): name = _get_name(a) self.lookup[name] = a for a in traverse([addon]): self.master.commands.collect_commands(a) self.master.options.process_deferred() return addon