From 1413958a674e218fd5a9470ea7cf46194d4371cf Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Tue, 5 Jun 2018 18:47:42 +0200 Subject: [PATCH] fix syntax-error on older python interpreters --- .travis.yml | 2 + mitmproxy/tools/_main.py | 175 ++++++++++++++++++++++++++++++++++++ mitmproxy/tools/main.py | 188 ++++----------------------------------- tox.ini | 8 ++ 4 files changed, 200 insertions(+), 173 deletions(-) create mode 100644 mitmproxy/tools/_main.py diff --git a/.travis.yml b/.travis.yml index da916e3f7..1b60d2bce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,8 @@ git: matrix: fast_finish: true include: + - python: 3.5 + env: TOXENV=py35 # this just makes sure that our version detection shows an appropriate error message - python: 3.6 env: TOXENV=lint - os: osx diff --git a/mitmproxy/tools/_main.py b/mitmproxy/tools/_main.py new file mode 100644 index 000000000..8baaa1c45 --- /dev/null +++ b/mitmproxy/tools/_main.py @@ -0,0 +1,175 @@ +""" +This file contains python3.6+ syntax! +Feel free to import and use whatever new package you deem necessary. +""" + +import os +import sys +import asyncio +import argparse # noqa +import signal # noqa +import typing # noqa + +from mitmproxy.tools import cmdline # noqa +from mitmproxy import exceptions, master # noqa +from mitmproxy import options # noqa +from mitmproxy import optmanager # noqa +from mitmproxy import proxy # noqa +from mitmproxy import log # noqa +from mitmproxy.utils import debug, arg_check # noqa + +OPTIONS_FILE_NAME = "config.yaml" + + +def assert_utf8_env(): + spec = "" + for i in ["LANG", "LC_CTYPE", "LC_ALL"]: + spec += os.environ.get(i, "").lower() + if "utf" not in spec: + print( + "Error: mitmproxy requires a UTF console environment.", + file=sys.stderr + ) + print( + "Set your LANG environment variable to something like en_US.UTF-8", + file=sys.stderr + ) + sys.exit(1) + + +def process_options(parser, opts, args): + if args.version: + print(debug.dump_system_info()) + sys.exit(0) + if args.quiet or args.options or args.commands: + # also reduce log verbosity if --options or --commands is passed, + # we don't want log messages from regular startup then. + args.termlog_verbosity = 'error' + args.flow_detail = 0 + if args.verbose: + args.termlog_verbosity = 'debug' + args.flow_detail = 2 + + adict = {} + for n in dir(args): + if n in opts: + adict[n] = getattr(args, n) + opts.merge(adict) + + return proxy.config.ProxyConfig(opts) + + +def run( + master_cls: typing.Type[master.Master], + make_parser: typing.Callable[[options.Options], argparse.ArgumentParser], + arguments: typing.Sequence[str], + extra: typing.Callable[[typing.Any], dict] = None +) -> master.Master: # pragma: no cover + """ + extra: Extra argument processing callable which returns a dict of + options. + """ + debug.register_info_dumpers() + + opts = options.Options() + master = master_cls(opts) + + parser = make_parser(opts) + + # To make migration from 2.x to 3.0 bearable. + if "-R" in sys.argv and sys.argv[sys.argv.index("-R") + 1].startswith("http"): + print("-R is used for specifying replacements.\n" + "To use mitmproxy in reverse mode please use --mode reverse:SPEC instead") + + try: + args = parser.parse_args(arguments) + except SystemExit: + arg_check.check() + sys.exit(1) + try: + opts.confdir = args.confdir + optmanager.load_paths( + opts, + os.path.join(opts.confdir, OPTIONS_FILE_NAME), + ) + pconf = process_options(parser, opts, args) + server: typing.Any = None + if pconf.options.server: + try: + server = proxy.server.ProxyServer(pconf) + except exceptions.ServerException as v: + print(str(v), file=sys.stderr) + sys.exit(1) + else: + server = proxy.server.DummyServer(pconf) + + master.server = server + if args.options: + print(optmanager.dump_defaults(opts)) + sys.exit(0) + if args.commands: + master.commands.dump() + sys.exit(0) + opts.set(*args.setoptions, defer=True) + if extra: + opts.update(**extra(args)) + + loop = asyncio.get_event_loop() + for signame in ('SIGINT', 'SIGTERM'): + try: + loop.add_signal_handler(getattr(signal, signame), master.shutdown) + except NotImplementedError: + # Not supported on Windows + pass + + # Make sure that we catch KeyboardInterrupts on Windows. + # https://stackoverflow.com/a/36925722/934719 + if os.name == "nt": + async def wakeup(): + while True: + await asyncio.sleep(0.2) + asyncio.ensure_future(wakeup()) + + master.run() + except exceptions.OptionsError as e: + print("%s: %s" % (sys.argv[0], e), file=sys.stderr) + sys.exit(1) + except (KeyboardInterrupt, RuntimeError) as e: + pass + return master + + +def mitmproxy(args=None) -> typing.Optional[int]: # pragma: no cover + if os.name == "nt": + print("Error: mitmproxy's console interface is not supported on Windows. " + "You can run mitmdump or mitmweb instead.", file=sys.stderr) + return 1 + assert_utf8_env() + from mitmproxy.tools import console + run(console.master.ConsoleMaster, cmdline.mitmproxy, args) + return None + + +def mitmdump(args=None) -> typing.Optional[int]: # pragma: no cover + from mitmproxy.tools import dump + + def extra(args): + if args.filter_args: + v = " ".join(args.filter_args) + return dict( + save_stream_filter=v, + readfile_filter=v, + dumper_filter=v, + ) + return {} + + m = run(dump.DumpMaster, cmdline.mitmdump, args, extra) + if m and m.errorcheck.has_errored: # type: ignore + return 1 + return None + + +def mitmweb(args=None) -> typing.Optional[int]: # pragma: no cover + from mitmproxy.tools import web + run(web.master.WebMaster, cmdline.mitmweb, args) + return None diff --git a/mitmproxy/tools/main.py b/mitmproxy/tools/main.py index acae9ee33..cd99e6d5f 100644 --- a/mitmproxy/tools/main.py +++ b/mitmproxy/tools/main.py @@ -1,180 +1,22 @@ +""" +This file must be kept in a python2.7 and python3.5 compatible syntax! +DO NOT use type annotations or other python3.6-only features that makes this file unparsable by older interpreters! +""" + from __future__ import print_function # this is here for the version check to work on Python 2. -import asyncio import sys if sys.version_info < (3, 6): # This must be before any mitmproxy imports, as they already break! # Keep all other imports below with the 'noqa' magic comment. - print("#" * 49, file=sys.stderr) - print("# mitmproxy requires Python 3.6 or higher! #", file=sys.stderr) - print("#" * 49, file=sys.stderr) - -import argparse # noqa -import os # noqa -import signal # noqa -import typing # noqa - -from mitmproxy.tools import cmdline # noqa -from mitmproxy import exceptions, master # noqa -from mitmproxy import options # noqa -from mitmproxy import optmanager # noqa -from mitmproxy import proxy # noqa -from mitmproxy import log # noqa -from mitmproxy.utils import debug, arg_check # noqa - -OPTIONS_FILE_NAME = "config.yaml" - - -def assert_utf8_env(): - spec = "" - for i in ["LANG", "LC_CTYPE", "LC_ALL"]: - spec += os.environ.get(i, "").lower() - if "utf" not in spec: - print( - "Error: mitmproxy requires a UTF console environment.", - file=sys.stderr - ) - print( - "Set your LANG environment variable to something like en_US.UTF-8", - file=sys.stderr - ) - sys.exit(1) - - -def process_options(parser, opts, args): - if args.version: - print(debug.dump_system_info()) - sys.exit(0) - if args.quiet or args.options or args.commands: - # also reduce log verbosity if --options or --commands is passed, - # we don't want log messages from regular startup then. - args.termlog_verbosity = 'error' - args.flow_detail = 0 - if args.verbose: - args.termlog_verbosity = 'debug' - args.flow_detail = 2 - - adict = {} - for n in dir(args): - if n in opts: - adict[n] = getattr(args, n) - opts.merge(adict) - - return proxy.config.ProxyConfig(opts) - - -def run( - master_cls: typing.Type[master.Master], - make_parser: typing.Callable[[options.Options], argparse.ArgumentParser], - arguments: typing.Sequence[str], - extra: typing.Callable[[typing.Any], dict] = None -) -> master.Master: # pragma: no cover - """ - extra: Extra argument processing callable which returns a dict of - options. - """ - debug.register_info_dumpers() - - opts = options.Options() - master = master_cls(opts) - - parser = make_parser(opts) - - # To make migration from 2.x to 3.0 bearable. - if "-R" in sys.argv and sys.argv[sys.argv.index("-R") + 1].startswith("http"): - print("-R is used for specifying replacements.\n" - "To use mitmproxy in reverse mode please use --mode reverse:SPEC instead") - - try: - args = parser.parse_args(arguments) - except SystemExit: - arg_check.check() - sys.exit(1) - try: - opts.confdir = args.confdir - optmanager.load_paths( - opts, - os.path.join(opts.confdir, OPTIONS_FILE_NAME), - ) - pconf = process_options(parser, opts, args) - server: typing.Any = None - if pconf.options.server: - try: - server = proxy.server.ProxyServer(pconf) - except exceptions.ServerException as v: - print(str(v), file=sys.stderr) - sys.exit(1) - else: - server = proxy.server.DummyServer(pconf) - - master.server = server - if args.options: - print(optmanager.dump_defaults(opts)) - sys.exit(0) - if args.commands: - master.commands.dump() - sys.exit(0) - opts.set(*args.setoptions, defer=True) - if extra: - opts.update(**extra(args)) - - loop = asyncio.get_event_loop() - for signame in ('SIGINT', 'SIGTERM'): - try: - loop.add_signal_handler(getattr(signal, signame), master.shutdown) - except NotImplementedError: - # Not supported on Windows - pass - - # Make sure that we catch KeyboardInterrupts on Windows. - # https://stackoverflow.com/a/36925722/934719 - if os.name == "nt": - async def wakeup(): - while True: - await asyncio.sleep(0.2) - asyncio.ensure_future(wakeup()) - - master.run() - except exceptions.OptionsError as e: - print("%s: %s" % (sys.argv[0], e), file=sys.stderr) - sys.exit(1) - except (KeyboardInterrupt, RuntimeError) as e: - pass - return master - - -def mitmproxy(args=None) -> typing.Optional[int]: # pragma: no cover - if os.name == "nt": - print("Error: mitmproxy's console interface is not supported on Windows. " - "You can run mitmdump or mitmweb instead.", file=sys.stderr) - return 1 - assert_utf8_env() - from mitmproxy.tools import console - run(console.master.ConsoleMaster, cmdline.mitmproxy, args) - return None - - -def mitmdump(args=None) -> typing.Optional[int]: # pragma: no cover - from mitmproxy.tools import dump - - def extra(args): - if args.filter_args: - v = " ".join(args.filter_args) - return dict( - save_stream_filter=v, - readfile_filter=v, - dumper_filter=v, - ) - return {} - - m = run(dump.DumpMaster, cmdline.mitmdump, args, extra) - if m and m.errorcheck.has_errored: # type: ignore - return 1 - return None - - -def mitmweb(args=None) -> typing.Optional[int]: # pragma: no cover - from mitmproxy.tools import web - run(web.master.WebMaster, cmdline.mitmweb, args) - return None + print("#" * 76, file=sys.stderr) + print("# mitmproxy requires Python 3.6 or higher! #", file=sys.stderr) + print("#" + " " * 74 + "#", file=sys.stderr) + print("# Please upgrade your Python intepreter or use our mitmproxy binaries from #", file=sys.stderr) + print("# https://mitmproxy.org. If your operating system does not include the #", file=sys.stderr) + print("# required Python version, you can try using pyenv or similar tools. #", file=sys.stderr) + print("#" * 76, file=sys.stderr) + sys.exit(1) +else: + from ._main import * # noqa diff --git a/tox.ini b/tox.ini index 46e365df2..9401b8f26 100644 --- a/tox.ini +++ b/tox.ini @@ -17,6 +17,14 @@ commands = {posargs} {env:CI_COMMANDS:python -c ""} +[testenv:py35] +whitelist_externals = + bash +deps = + -rrequirements.txt +commands = + bash -c "mitmdump --version 2>&1 | grep 'mitmproxy requires Python 3.6'" + [testenv:lint] commands = mitmdump --version