def __init__(self, manager: "CommandManager", name: str, func: typing.Callable) -> None: self.name = name self.manager = manager self.func = func self.signature = inspect.signature(self.func) if func.__doc__: txt = func.__doc__.strip() self.help = "\n".join(textwrap.wrap(txt)) else: self.help = None # This fails with a CommandException if types are invalid for name, parameter in self.signature.parameters.items(): t = parameter.annotation if not seleniumwire.thirdparty.mitmproxy.types.CommandTypes.get( parameter.annotation, None): raise exceptions.CommandError( f"Argument {name} has an unknown type ({_empty_as_none(t)}) in {func}." ) if self.return_type and not seleniumwire.thirdparty.mitmproxy.types.CommandTypes.get( self.return_type, None): raise exceptions.CommandError( f"Return type has an unknown type ({self.return_type}) in {func}." )
def typename(t: type) -> str: """ Translates a type to an explanatory string. """ if t == inspect._empty: # type: ignore raise exceptions.CommandError("missing type annotation") to = seleniumwire.thirdparty.mitmproxy.types.CommandTypes.get(t, None) if not to: raise exceptions.CommandError("unsupported type: %s" % getattr(t, "__name__", t)) return to.display
def parsearg(manager: CommandManager, spec: str, argtype: type) -> typing.Any: """ Convert a string to a argument to the appropriate type. """ t = seleniumwire.thirdparty.mitmproxy.types.CommandTypes.get(argtype, None) if not t: raise exceptions.CommandError(f"Unsupported argument type: {argtype}") try: return t.parse(manager, argtype, spec) except exceptions.TypeError as e: raise exceptions.CommandError(str(e)) from e
def flow_set(self, flows: typing.Sequence[flow.Flow], attr: str, value: str) -> None: """ Quickly set a number of common values on flows. """ val: typing.Union[int, str] = value if attr == "status_code": try: val = int(val) # type: ignore except ValueError as v: raise exceptions.CommandError( "Status code is not an integer: %s" % val) from v updated = [] for f in flows: req = getattr(f, "request", None) rupdate = True if req: if attr == "method": req.method = val elif attr == "host": req.host = val elif attr == "path": req.path = val elif attr == "url": try: req.url = val except ValueError as e: raise exceptions.CommandError("URL %s is invalid: %s" % (repr(val), e)) from e else: self.rupdate = False resp = getattr(f, "response", None) supdate = True if resp: if attr == "status_code": resp.status_code = val if val in status_codes.RESPONSES: resp.reason = status_codes.RESPONSES[ val] # type: ignore elif attr == "reason": resp.reason = val else: supdate = False if rupdate or supdate: updated.append(f) ctx.master.addons.trigger("update", updated) ctx.log.alert("Set %s on %s flows." % (attr, len(updated)))
def call(self, command_name: str, *args: typing.Sequence[typing.Any]) -> typing.Any: """ Call a command with native arguments. May raise CommandError. """ if command_name not in self.commands: raise exceptions.CommandError("Unknown command: %s" % command_name) return self.commands[command_name].func(*args)
def verify_arg_signature(f: typing.Callable, args: typing.Iterable[typing.Any], kwargs: dict) -> None: sig = inspect.signature(f) try: sig.bind(*args, **kwargs) except TypeError as v: raise exceptions.CommandError("command argument mismatch: %s" % v.args[0])
def call_strings(self, command_name: str, args: typing.Sequence[str]) -> typing.Any: """ Call a command using a list of string arguments. May raise CommandError. """ if command_name not in self.commands: raise exceptions.CommandError("Unknown command: %s" % command_name) return self.commands[command_name].call(args)
def options_save( self, path: seleniumwire.thirdparty.mitmproxy.types.Path) -> None: """ Save options to a file. """ try: optmanager.save(ctx.options, path) except OSError as e: raise exceptions.CommandError("Could not save options - %s" % e) from e
def options_load( self, path: seleniumwire.thirdparty.mitmproxy.types.Path) -> None: """ Load options from a file. """ try: optmanager.load_paths(ctx.options, path) except (OSError, exceptions.OptionsError) as e: raise exceptions.CommandError("Could not load options - %s" % e) from e
def options_reset_one(self, name: str) -> None: """ Reset one option to its default value. """ if name not in ctx.options: raise exceptions.CommandError("No such option: %s" % name) setattr( ctx.options, name, ctx.options.default(name), )
def execute(self, cmdstr: str) -> typing.Any: """ Execute a command string. May raise CommandError. """ parts, _ = self.parse_partial(cmdstr) if not parts: raise exceptions.CommandError(f"Invalid command: {cmdstr!r}") command_name, *args = [ unquote(part.value) for part in parts if part.type != seleniumwire.thirdparty.mitmproxy.types.Space ] return self.call_strings(command_name, args)
def set(self, option: str, value: str = "") -> None: """ Set an option. When the value is omitted, booleans are set to true, strings and integers are set to None (if permitted), and sequences are emptied. Boolean values can be true, false or toggle. Multiple values are concatenated with a single space. """ strspec = f"{option}={value}" try: ctx.options.set(strspec) except exceptions.OptionsError as e: raise exceptions.CommandError(e) from e
def call(self, args: typing.Sequence[str]) -> typing.Any: """ Call the command with a list of arguments. At this point, all arguments are strings. """ bound_args = self.prepare_args(args) ret = self.func(*bound_args.args, **bound_args.kwargs) if ret is None and self.return_type is None: return typ = seleniumwire.thirdparty.mitmproxy.types.CommandTypes.get( self.return_type) assert typ if not typ.is_valid(self.manager, typ, ret): raise exceptions.CommandError( f"{self.name} returned unexpected data - expected {typ.display}" ) return ret
def prepare_args(self, args: typing.Sequence[str]) -> inspect.BoundArguments: try: bound_arguments = self.signature.bind(*args) except TypeError: expected = f'Expected: {str(self.signature.parameters)}' received = f'Received: {str(args)}' raise exceptions.CommandError( f"Command argument mismatch: \n {expected}\n {received}") for name, value in bound_arguments.arguments.items(): convert_to = self.signature.parameters[name].annotation bound_arguments.arguments[name] = parsearg(self.manager, value, convert_to) bound_arguments.apply_defaults() return bound_arguments