Options unification: string choices

This commit is contained in:
Aldo Cortesi 2017-03-06 13:17:53 +13:00
parent e70b46672c
commit 71a830c836
4 changed files with 66 additions and 46 deletions

View File

@ -1,7 +1,27 @@
from typing import Tuple, Optional, Sequence, Union from typing import Tuple, Optional, Sequence, Union
from mitmproxy.net import tcp
from mitmproxy import optmanager from mitmproxy import optmanager
# We redefine these here for now to avoid importing Urwid-related guff on
# platforms that don't support it, and circular imports. We can do better using
# a lazy checker down the track.
console_palettes = [
"lowlight",
"lowdark",
"light",
"dark",
"solarized_light",
"solarized_dark"
]
view_orders = [
"time",
"method",
"url",
"size",
]
APP_HOST = "mitm.it" APP_HOST = "mitm.it"
APP_PORT = 80 APP_PORT = 80
CA_DIR = "~/.mitmproxy" CA_DIR = "~/.mitmproxy"
@ -260,8 +280,20 @@ class Options(optmanager.OptManager):
requests. Format: username:password requests. Format: username:password
""" """
) )
self.add_option("ssl_version_client", "secure", str) self.add_option(
self.add_option("ssl_version_server", "secure", str) "ssl_version_client", "secure", str,
"Set supported SSL/TLS versions for client connections. "
"SSLv2, SSLv3 and 'all' are INSECURE. Defaults to secure, which "
"is TLS1.0+.",
choices=tcp.sslversion_choices.keys(),
)
self.add_option(
"ssl_version_server", "secure", str,
"Set supported SSL/TLS versions for server connections. "
"SSLv2, SSLv3 and 'all' are INSECURE. Defaults to secure, "
"which is TLS1.0+.",
choices=tcp.sslversion_choices.keys(),
)
self.add_option( self.add_option(
"ssl_insecure", False, bool, "ssl_insecure", False, bool,
"Do not verify upstream server SSL/TLS certificates." "Do not verify upstream server SSL/TLS certificates."
@ -298,7 +330,11 @@ class Options(optmanager.OptManager):
"console_focus_follow", False, bool, "console_focus_follow", False, bool,
"Focus follows new flows." "Focus follows new flows."
) )
self.add_option("console_palette", "dark", Optional[str]) self.add_option(
"console_palette", "dark", Optional[str],
help="Select color palette: " + ", ".join(console_palettes),
choices=sorted(console_palettes),
)
self.add_option( self.add_option(
"console_palette_transparent", False, bool, "console_palette_transparent", False, bool,
"Set transparent background for palette." "Set transparent background for palette."
@ -307,10 +343,12 @@ class Options(optmanager.OptManager):
"console_mouse", True, bool, "console_mouse", True, bool,
"Console mouse interaction." "Console mouse interaction."
) )
self.add_option("console_order", None, Optional[str])
self.add_option( self.add_option(
"console_order_reversed", False, bool, "console_order", None, Optional[str],
"Flow sort order.",
choices=view_orders,
) )
self.add_option("console_order_reversed", False, bool)
self.add_option( self.add_option(
"filter", None, Optional[str], "filter", None, Optional[str],

View File

@ -20,14 +20,15 @@ unset = object()
class _Option: class _Option:
__slots__ = ("name", "typespec", "value", "_default", "help") __slots__ = ("name", "typespec", "value", "_default", "choices", "help")
def __init__( def __init__(
self, self,
name: str, name: str,
default: typing.Any, default: typing.Any,
typespec: typing.Type, typespec: typing.Type,
help: typing.Optional[str] help: typing.Optional[str],
choices: typing.Optional[typing.Sequence[str]]
) -> None: ) -> None:
typecheck.check_type(name, default, typespec) typecheck.check_type(name, default, typespec)
self.name = name self.name = name
@ -35,6 +36,7 @@ class _Option:
self.typespec = typespec self.typespec = typespec
self.value = unset self.value = unset
self.help = help self.help = help
self.choices = choices
def __repr__(self): def __repr__(self):
return "{value} [{type}]".format(value=self.current(), type=self.typespec) return "{value} [{type}]".format(value=self.current(), type=self.typespec)
@ -67,7 +69,9 @@ class _Option:
return True return True
def __deepcopy__(self, _): def __deepcopy__(self, _):
o = _Option(self.name, self.default, self.typespec, self.help) o = _Option(
self.name, self.default, self.typespec, self.help, self.choices
)
if self.has_changed(): if self.has_changed():
o.value = self.current() o.value = self.current()
return o return o
@ -98,11 +102,12 @@ class OptManager:
name: str, name: str,
default: typing.Any, default: typing.Any,
typespec: typing.Type, typespec: typing.Type,
help: str = None help: typing.Optional[str] = None,
choices: typing.Optional[typing.Sequence[str]] = None
) -> None: ) -> None:
if name in self._options: if name in self._options:
raise ValueError("Option %s already exists" % name) raise ValueError("Option %s already exists" % name)
self._options[name] = _Option(name, default, typespec, help) self._options[name] = _Option(name, default, typespec, help, choices)
@contextlib.contextmanager @contextlib.contextmanager
def rollback(self, updated): def rollback(self, updated):
@ -337,7 +342,7 @@ class OptManager:
type=int, type=int,
dest=option, dest=option,
help=o.help, help=o.help,
metavar=metavar metavar=metavar,
) )
elif o.typespec in (str, typing.Optional[str]): elif o.typespec in (str, typing.Optional[str]):
parser.add_argument( parser.add_argument(
@ -346,7 +351,8 @@ class OptManager:
type=str, type=str,
dest=option, dest=option,
help=o.help, help=o.help,
metavar=metavar metavar=metavar,
choices=o.choices
) )
elif o.typespec == typing.Sequence[str]: elif o.typespec == typing.Sequence[str]:
parser.add_argument( parser.add_argument(
@ -355,7 +361,8 @@ class OptManager:
type=str, type=str,
dest=option, dest=option,
help=o.help, help=o.help,
metavar=metavar metavar=metavar,
choices=o.choices,
) )
else: else:
raise ValueError("Unsupported option type: %s", o.typespec) raise ValueError("Unsupported option type: %s", o.typespec)

View File

@ -4,9 +4,7 @@ import os
from mitmproxy import exceptions from mitmproxy import exceptions
from mitmproxy import options from mitmproxy import options
from mitmproxy import platform from mitmproxy import platform
from mitmproxy.net import tcp
from mitmproxy import version from mitmproxy import version
from mitmproxy.addons import view
CONFIG_PATH = os.path.join(options.CA_DIR, "config.yaml") CONFIG_PATH = os.path.join(options.CA_DIR, "config.yaml")
@ -264,20 +262,8 @@ def proxy_ssl_options(parser, opts):
opts.make_parser(group, "ssl_insecure") opts.make_parser(group, "ssl_insecure")
opts.make_parser(group, "ssl_verify_upstream_trusted_cadir", metavar="PATH") opts.make_parser(group, "ssl_verify_upstream_trusted_cadir", metavar="PATH")
opts.make_parser(group, "ssl_verify_upstream_trusted_ca", metavar="PATH") opts.make_parser(group, "ssl_verify_upstream_trusted_ca", metavar="PATH")
group.add_argument( opts.make_parser(group, "ssl_version_client", metavar="VERSION")
"--ssl-version-client", dest="ssl_version_client", opts.make_parser(group, "ssl_version_server", metavar="VERSION")
action="store",
choices=tcp.sslversion_choices.keys(),
help="Set supported SSL/TLS versions for client connections. "
"SSLv2, SSLv3 and 'all' are INSECURE. Defaults to secure, which is TLS1.0+."
)
group.add_argument(
"--ssl-version-server", dest="ssl_version_server",
action="store",
choices=tcp.sslversion_choices.keys(),
help="Set supported SSL/TLS versions for server connections. "
"SSLv2, SSLv3 and 'all' are INSECURE. Defaults to secure, which is TLS1.0+."
)
def onboarding_app(parser, opts): def onboarding_app(parser, opts):
@ -387,25 +373,14 @@ def common_options(parser, opts):
def mitmproxy(opts): def mitmproxy(opts):
# Don't import mitmproxy.tools.console for mitmdump, urwid is not available # Don't import mitmproxy.tools.console for mitmdump, urwid is not available
# on all platforms. # on all platforms.
from .console import palettes
parser = argparse.ArgumentParser(usage="%(prog)s [options]") parser = argparse.ArgumentParser(usage="%(prog)s [options]")
common_options(parser, opts) common_options(parser, opts)
parser.add_argument(
"--palette", type=str, opts.make_parser(parser, "console_palette")
action="store", dest="console_palette",
choices=sorted(palettes.palettes.keys()),
help="Select color palette: " + ", ".join(palettes.palettes.keys())
)
opts.make_parser(parser, "console_palette_transparent") opts.make_parser(parser, "console_palette_transparent")
opts.make_parser(parser, "console_eventlog") opts.make_parser(parser, "console_eventlog")
opts.make_parser(parser, "console_focus_follow") opts.make_parser(parser, "console_focus_follow")
parser.add_argument( opts.make_parser(parser, "console_order")
"--order",
type=str, dest="console_order",
choices=[o[1] for o in view.orders],
help="Flow sort order."
)
opts.make_parser(parser, "console_mouse") opts.make_parser(parser, "console_mouse")
group = parser.add_argument_group( group = parser.add_argument_group(
"Filters", "Filters",

View File

@ -252,14 +252,14 @@ def test_merge():
def test_option(): def test_option():
o = optmanager._Option("test", 1, int, None) o = optmanager._Option("test", 1, int, None, None)
assert o.current() == 1 assert o.current() == 1
with pytest.raises(TypeError): with pytest.raises(TypeError):
o.set("foo") o.set("foo")
with pytest.raises(TypeError): with pytest.raises(TypeError):
optmanager._Option("test", 1, str, None) optmanager._Option("test", 1, str, None, None)
o2 = optmanager._Option("test", 1, int, None) o2 = optmanager._Option("test", 1, int, None, None)
assert o2 == o assert o2 == o
o2.set(5) o2.set(5)
assert o2 != o assert o2 != o