aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAldo Cortesi <aldo@corte.si>2017-03-14 09:22:44 +1300
committerAldo Cortesi <aldo@corte.si>2017-03-14 09:40:33 +1300
commitb745428b5c4e89753a83fe228cb5478327eeb540 (patch)
tree7681c0f345d13243e1c28deb8c8144c2061a26e4
parent2832e790fd27d45f8bfbed1b4ce7f1e15910018d (diff)
downloadmitmproxy-b745428b5c4e89753a83fe228cb5478327eeb540.tar.gz
mitmproxy-b745428b5c4e89753a83fe228cb5478327eeb540.tar.bz2
mitmproxy-b745428b5c4e89753a83fe228cb5478327eeb540.zip
Enable custom options in config files
We also now ignore unknown options in config files by default, and print a warning if verbosity is incremented.
-rw-r--r--examples/simple/custom_option.py3
-rw-r--r--mitmproxy/optmanager.py45
-rw-r--r--mitmproxy/tools/main.py5
-rw-r--r--test/mitmproxy/test_optmanager.py15
4 files changed, 45 insertions, 23 deletions
diff --git a/examples/simple/custom_option.py b/examples/simple/custom_option.py
index a8b4e778..324d27e7 100644
--- a/examples/simple/custom_option.py
+++ b/examples/simple/custom_option.py
@@ -7,4 +7,5 @@ def start(options):
def configure(options, updated):
- ctx.log.info("custom option value: %s" % options.custom)
+ if "custom" in updated:
+ ctx.log.info("custom option value: %s" % options.custom)
diff --git a/mitmproxy/optmanager.py b/mitmproxy/optmanager.py
index 225a2f73..495354f4 100644
--- a/mitmproxy/optmanager.py
+++ b/mitmproxy/optmanager.py
@@ -81,8 +81,6 @@ class _Option:
class OptManager:
"""
OptManager is the base class from which Options objects are derived.
- Note that the __init__ method of all child classes must force all
- arguments to be positional only, by including a "*" argument.
.changed is a blinker Signal that triggers whenever options are
updated. If any handler in the chain raises an exceptions.OptionsError
@@ -176,15 +174,29 @@ class OptManager:
o.reset()
self.changed.send(self._options.keys())
+ def update_known(self, **kwargs):
+ """
+ Update and set all known options from kwargs. Returns a dictionary
+ of unknown options.
+ """
+ known, unknown = {}, {}
+ for k, v in kwargs.items():
+ if k in self._options:
+ known[k] = v
+ else:
+ unknown[k] = v
+ updated = set(known.keys())
+ if updated:
+ with self.rollback(updated):
+ for k, v in known.items():
+ self._options[k].set(v)
+ self.changed.send(self, updated=updated)
+ return unknown
+
def update(self, **kwargs):
- updated = set(kwargs.keys())
- with self.rollback(updated):
- for k, v in kwargs.items():
- if k not in self._options:
- raise KeyError("No such option: %s" % k)
- self._options[k].set(v)
- self.changed.send(self, updated=updated)
- return self
+ u = self.update_known(**kwargs)
+ if u:
+ raise KeyError("Unknown options: %s" % ", ".join(u.keys()))
def setter(self, attr):
"""
@@ -413,12 +425,11 @@ def load(opts, text):
"""
Load configuration from text, over-writing options already set in
this object. May raise OptionsError if the config file is invalid.
+
+ Returns a dictionary of all unknown options.
"""
data = parse(text)
- try:
- opts.update(**data)
- except KeyError as v:
- raise exceptions.OptionsError(v)
+ return opts.update_known(**data)
def load_paths(opts, *paths):
@@ -426,18 +437,22 @@ def load_paths(opts, *paths):
Load paths in order. Each path takes precedence over the previous
path. Paths that don't exist are ignored, errors raise an
OptionsError.
+
+ Returns a dictionary of unknown options.
"""
+ ret = {}
for p in paths:
p = os.path.expanduser(p)
if os.path.exists(p) and os.path.isfile(p):
with open(p, "r") as f:
txt = f.read()
try:
- load(opts, txt)
+ ret.update(load(opts, txt))
except exceptions.OptionsError as e:
raise exceptions.OptionsError(
"Error reading %s: %s" % (p, e)
)
+ return ret
def serialize(opts, text, defaults=False):
diff --git a/mitmproxy/tools/main.py b/mitmproxy/tools/main.py
index 8210580c..35567b62 100644
--- a/mitmproxy/tools/main.py
+++ b/mitmproxy/tools/main.py
@@ -69,10 +69,13 @@ def run(MasterKlass, args): # pragma: no cover
args = parser.parse_args(args)
master = None
try:
- optmanager.load_paths(opts, args.conf)
+ unknown = optmanager.load_paths(opts, args.conf)
server = process_options(parser, opts, args)
master = MasterKlass(opts, server)
master.addons.configure_all(opts, opts.keys())
+ remaining = opts.update_known(**unknown)
+ if remaining and opts.verbosity > 1:
+ print("Ignored options: %s" % remaining)
if args.options:
print(optmanager.dump_defaults(opts))
sys.exit(0)
diff --git a/test/mitmproxy/test_optmanager.py b/test/mitmproxy/test_optmanager.py
index 012c463c..df392829 100644
--- a/test/mitmproxy/test_optmanager.py
+++ b/test/mitmproxy/test_optmanager.py
@@ -83,10 +83,11 @@ def test_options():
with pytest.raises(TypeError):
TO(nonexistent = "value")
- with pytest.raises(Exception, match="No such option"):
+ with pytest.raises(Exception, match="Unknown options"):
o.nonexistent = "value"
- with pytest.raises(Exception, match="No such option"):
+ with pytest.raises(Exception, match="Unknown options"):
o.update(nonexistent = "value")
+ assert o.update_known(nonexistent = "value") == {"nonexistent": "value"}
rec = []
@@ -226,9 +227,7 @@ def test_serialize():
t = ""
optmanager.load(o2, t)
-
- with pytest.raises(exceptions.OptionsError, matches='No such option: foobar'):
- optmanager.load(o2, "foobar: '123'")
+ assert optmanager.load(o2, "foobar: '123'") == {"foobar": "123"}
def test_serialize_defaults():
@@ -252,7 +251,11 @@ def test_saving(tmpdir):
with open(dst, 'a') as f:
f.write("foobar: '123'")
- with pytest.raises(exceptions.OptionsError, matches=''):
+ assert optmanager.load_paths(o, dst) == {"foobar": "123"}
+
+ with open(dst, 'a') as f:
+ f.write("'''")
+ with pytest.raises(exceptions.OptionsError):
optmanager.load_paths(o, dst)