mirror of
https://github.com/vee1e/mitmproxy.git
synced 2026-09-01 18:27:18 +00:00
Make it possible to set sequence options (#4210)
* Make it possible to set sequence options Attempts to fix #3015 through looking at whether or not the option is of the type Sequence[str]. Treat all deferred options as potentially Sequence options, by making the deferred dict values a list. * Add full test coverage to optmanager again * Document how to set sequence options * minor improvements * update changelog Co-authored-by: Maximilian Hils <git@maximilianhils.com>
This commit is contained in:
parent
7603987ee0
commit
64961232e6
5 changed files with 32 additions and 10 deletions
|
|
@ -67,6 +67,7 @@ Mitmproxy has a completely new proxy core, fixing many longstanding issues:
|
|||
(@mhils)
|
||||
* `json()` method for HTTP Request and Response instances will return decoded JSON body. (@rbdixon)
|
||||
* Support for HTTP/2 Push Promises has been dropped. (@mhils)
|
||||
* Make it possible to set sequence options from the command line (@Yopi)
|
||||
* --- TODO: add new PRs above this line ---
|
||||
* ... and various other fixes, documentation improvements, dependency version bumps, etc.
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ class Core:
|
|||
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.
|
||||
Sequences are set using multiple invocations to set for
|
||||
the same option.
|
||||
"""
|
||||
strspec = f"{option}={value}"
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import collections
|
||||
import contextlib
|
||||
import blinker
|
||||
import blinker._saferef
|
||||
|
|
@ -91,7 +92,7 @@ class OptManager:
|
|||
mutation doesn't change the option state inadvertently.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.deferred: typing.Dict[str, str] = {}
|
||||
self.deferred: typing.Dict[str, typing.List[str]] = {}
|
||||
self.changed = blinker.Signal()
|
||||
self.errored = blinker.Signal()
|
||||
# Options must be the last attribute here - after that, we raise an
|
||||
|
|
@ -295,7 +296,7 @@ class OptManager:
|
|||
are added.
|
||||
"""
|
||||
vals = {}
|
||||
unknown = {}
|
||||
unknown: typing.Dict[str, typing.List[str]] = collections.defaultdict(list)
|
||||
for i in spec:
|
||||
parts = i.split("=", maxsplit=1)
|
||||
if len(parts) == 1:
|
||||
|
|
@ -303,9 +304,9 @@ class OptManager:
|
|||
else:
|
||||
optname, optval = parts[0], parts[1]
|
||||
if optname in self._options:
|
||||
vals[optname] = self.parse_setval(self._options[optname], optval)
|
||||
vals[optname] = self.parse_setval(self._options[optname], optval, vals.get(optname))
|
||||
else:
|
||||
unknown[optname] = optval
|
||||
unknown[optname].append(optval)
|
||||
if defer:
|
||||
self.deferred.update(unknown)
|
||||
elif unknown:
|
||||
|
|
@ -318,15 +319,16 @@ class OptManager:
|
|||
have since been added.
|
||||
"""
|
||||
update = {}
|
||||
for optname, optval in self.deferred.items():
|
||||
for optname, optvals in self.deferred.items():
|
||||
if optname in self._options:
|
||||
optval = self.parse_setval(self._options[optname], optval)
|
||||
update[optname] = optval
|
||||
for optval in optvals:
|
||||
optval = self.parse_setval(self._options[optname], optval, update.get(optname))
|
||||
update[optname] = optval
|
||||
self.update(**update)
|
||||
for k in update.keys():
|
||||
del self.deferred[k]
|
||||
|
||||
def parse_setval(self, o: _Option, optstr: typing.Optional[str]) -> typing.Any:
|
||||
def parse_setval(self, o: _Option, optstr: typing.Optional[str], currentvalue: typing.Any) -> typing.Any:
|
||||
"""
|
||||
Convert a string to a value appropriate for the option type.
|
||||
"""
|
||||
|
|
@ -357,7 +359,10 @@ class OptManager:
|
|||
if not optstr:
|
||||
return []
|
||||
else:
|
||||
return getattr(self, o.name) + [optstr]
|
||||
if currentvalue:
|
||||
return getattr(self, o.name) + currentvalue + [optstr]
|
||||
else:
|
||||
return getattr(self, o.name) + [optstr]
|
||||
raise NotImplementedError("Unsupported option type: %s", o.typespec)
|
||||
|
||||
def make_parser(self, parser, optname, metavar=None, short=None):
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ def common_options(parser, opts):
|
|||
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.
|
||||
Sequences are set using multiple invocations to set for
|
||||
the same option.
|
||||
"""
|
||||
)
|
||||
parser.add_argument(
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ def test_defaults():
|
|||
def test_required_int():
|
||||
o = TO()
|
||||
with pytest.raises(exceptions.OptionsError):
|
||||
o.parse_setval(o._options["required_int"], None)
|
||||
o.parse_setval(o._options["required_int"], None, None)
|
||||
|
||||
|
||||
def test_deepcopy():
|
||||
|
|
@ -439,6 +439,9 @@ def test_set():
|
|||
opts.set("seqstr")
|
||||
assert opts.seqstr == []
|
||||
|
||||
opts.set(*('seqstr=foo', 'seqstr=bar'))
|
||||
assert opts.seqstr == ["foo", "bar"]
|
||||
|
||||
with pytest.raises(exceptions.OptionsError):
|
||||
opts.set("deferredoption=wobble")
|
||||
|
||||
|
|
@ -450,3 +453,12 @@ def test_set():
|
|||
opts.process_deferred()
|
||||
assert "deferredoption" not in opts.deferred
|
||||
assert opts.deferredoption == "wobble"
|
||||
|
||||
opts.set(*('deferredsequenceoption=a', 'deferredsequenceoption=b'), defer=True)
|
||||
assert "deferredsequenceoption" in opts.deferred
|
||||
opts.process_deferred()
|
||||
assert "deferredsequenceoption" in opts.deferred
|
||||
opts.add_option("deferredsequenceoption", typing.Sequence[str], [], "help")
|
||||
opts.process_deferred()
|
||||
assert "deferredsequenceoption" not in opts.deferred
|
||||
assert opts.deferredsequenceoption == ["a", "b"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue