We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
add_signal_handler is strictly UNIX only. 
https://github.com/mitmproxy/mitmproxy/blob/4e126c0fbaafffd23e1a80926de6a99c897f9af0/mitmproxy/tools/main.py#L124
According to the `asyncio`  documentation for [Python 3.6](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.add_signal_handler) , `add_signal_handler` is supported for UNIX system only. I haven't experienced or tested this issue but it maybe a potential risk since mitmproxy supports multiple platforms. 

If someone has a Windows OS and/or some knowledge about asyncio maybe you could verify this.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mitmproxy/tools/main.py`
Content:
```
1 from __future__ import print_function  # this is here for the version check to work on Python 2.
2 
3 import asyncio
4 import sys
5 
6 if sys.version_info < (3, 6):
7     # This must be before any mitmproxy imports, as they already break!
8     # Keep all other imports below with the 'noqa' magic comment.
9     print("#" * 49, file=sys.stderr)
10     print("# mitmproxy requires Python 3.6 or higher! #", file=sys.stderr)
11     print("#" * 49, file=sys.stderr)
12 
13 import argparse  # noqa
14 import os  # noqa
15 import signal  # noqa
16 import typing  # noqa
17 
18 from mitmproxy.tools import cmdline  # noqa
19 from mitmproxy import exceptions, master  # noqa
20 from mitmproxy import options  # noqa
21 from mitmproxy import optmanager  # noqa
22 from mitmproxy import proxy  # noqa
23 from mitmproxy import log  # noqa
24 from mitmproxy.utils import debug, arg_check  # noqa
25 
26 
27 def assert_utf8_env():
28     spec = ""
29     for i in ["LANG", "LC_CTYPE", "LC_ALL"]:
30         spec += os.environ.get(i, "").lower()
31     if "utf" not in spec:
32         print(
33             "Error: mitmproxy requires a UTF console environment.",
34             file=sys.stderr
35         )
36         print(
37             "Set your LANG environment variable to something like en_US.UTF-8",
38             file=sys.stderr
39         )
40         sys.exit(1)
41 
42 
43 def process_options(parser, opts, args):
44     if args.version:
45         print(debug.dump_system_info())
46         sys.exit(0)
47     if args.quiet or args.options or args.commands:
48         # also reduce log verbosity if --options or --commands is passed,
49         # we don't want log messages from regular startup then.
50         args.termlog_verbosity = 'error'
51         args.flow_detail = 0
52     if args.verbose:
53         args.termlog_verbosity = 'debug'
54         args.flow_detail = 2
55 
56     adict = {}
57     for n in dir(args):
58         if n in opts:
59             adict[n] = getattr(args, n)
60     opts.merge(adict)
61 
62     return proxy.config.ProxyConfig(opts)
63 
64 
65 def run(
66         master_cls: typing.Type[master.Master],
67         make_parser: typing.Callable[[options.Options], argparse.ArgumentParser],
68         arguments: typing.Sequence[str],
69         extra: typing.Callable[[typing.Any], dict] = None
70 ):  # pragma: no cover
71     """
72         extra: Extra argument processing callable which returns a dict of
73         options.
74     """
75     debug.register_info_dumpers()
76 
77     opts = options.Options()
78     master = master_cls(opts)
79 
80     parser = make_parser(opts)
81 
82     # To make migration from 2.x to 3.0 bearable.
83     if "-R" in sys.argv and sys.argv[sys.argv.index("-R") + 1].startswith("http"):
84         print("-R is used for specifying replacements.\n"
85               "To use mitmproxy in reverse mode please use --mode reverse:SPEC instead")
86 
87     try:
88         args = parser.parse_args(arguments)
89     except SystemExit:
90         arg_check.check()
91         sys.exit(1)
92     try:
93         unknown = optmanager.load_paths(opts, args.conf)
94         pconf = process_options(parser, opts, args)
95         server: typing.Any = None
96         if pconf.options.server:
97             try:
98                 server = proxy.server.ProxyServer(pconf)
99             except exceptions.ServerException as v:
100                 print(str(v), file=sys.stderr)
101                 sys.exit(1)
102         else:
103             server = proxy.server.DummyServer(pconf)
104 
105         master.server = server
106         master.addons.trigger("configure", opts.keys())
107         master.addons.trigger("tick")
108         opts.update_known(**unknown)
109         if args.options:
110             print(optmanager.dump_defaults(opts))
111             sys.exit(0)
112         if args.commands:
113             master.commands.dump()
114             sys.exit(0)
115         opts.set(*args.setoptions)
116         if extra:
117             opts.update(**extra(args))
118 
119         def cleankill(*args, **kwargs):
120             master.shutdown()
121         signal.signal(signal.SIGTERM, cleankill)
122         loop = asyncio.get_event_loop()
123         for signame in ('SIGINT', 'SIGTERM'):
124             loop.add_signal_handler(getattr(signal, signame), master.shutdown)
125         master.run()
126     except exceptions.OptionsError as e:
127         print("%s: %s" % (sys.argv[0], e), file=sys.stderr)
128         sys.exit(1)
129     except (KeyboardInterrupt, RuntimeError) as e:
130         pass
131     return master
132 
133 
134 def mitmproxy(args=None):  # pragma: no cover
135     if os.name == "nt":
136         print("Error: mitmproxy's console interface is not supported on Windows. "
137               "You can run mitmdump or mitmweb instead.", file=sys.stderr)
138         sys.exit(1)
139 
140     assert_utf8_env()
141 
142     from mitmproxy.tools import console
143     return run(console.master.ConsoleMaster, cmdline.mitmproxy, args)
144 
145 
146 def mitmdump(args=None):  # pragma: no cover
147     from mitmproxy.tools import dump
148 
149     def extra(args):
150         if args.filter_args:
151             v = " ".join(args.filter_args)
152             return dict(
153                 save_stream_filter=v,
154                 readfile_filter=v,
155                 dumper_filter=v,
156             )
157         return {}
158 
159     m = run(dump.DumpMaster, cmdline.mitmdump, args, extra)
160     if m and m.errorcheck.has_errored:
161         sys.exit(1)
162     return m
163 
164 
165 def mitmweb(args=None):  # pragma: no cover
166     from mitmproxy.tools import web
167     return run(web.master.WebMaster, cmdline.mitmweb, args)
168
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mitmproxy/tools/main.py b/mitmproxy/tools/main.py
--- a/mitmproxy/tools/main.py
+++ b/mitmproxy/tools/main.py
@@ -67,7 +67,7 @@
         make_parser: typing.Callable[[options.Options], argparse.ArgumentParser],
         arguments: typing.Sequence[str],
         extra: typing.Callable[[typing.Any], dict] = None
-):  # pragma: no cover
+) -> master.Master:  # pragma: no cover
     """
         extra: Extra argument processing callable which returns a dict of
         options.
@@ -121,7 +121,11 @@
         signal.signal(signal.SIGTERM, cleankill)
         loop = asyncio.get_event_loop()
         for signame in ('SIGINT', 'SIGTERM'):
-            loop.add_signal_handler(getattr(signal, signame), master.shutdown)
+            try:
+                loop.add_signal_handler(getattr(signal, signame), master.shutdown)
+            except NotImplementedError:
+                # Not supported on Windows
+                pass
         master.run()
     except exceptions.OptionsError as e:
         print("%s: %s" % (sys.argv[0], e), file=sys.stderr)
@@ -131,19 +135,18 @@
     return master
 
 
-def mitmproxy(args=None):  # pragma: no cover
+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)
-        sys.exit(1)
-
+        return 1
     assert_utf8_env()
-
     from mitmproxy.tools import console
-    return run(console.master.ConsoleMaster, cmdline.mitmproxy, args)
+    run(console.master.ConsoleMaster, cmdline.mitmproxy, args)
+    return None
 
 
-def mitmdump(args=None):  # pragma: no cover
+def mitmdump(args=None) -> typing.Optional[int]:  # pragma: no cover
     from mitmproxy.tools import dump
 
     def extra(args):
@@ -157,11 +160,12 @@
         return {}
 
     m = run(dump.DumpMaster, cmdline.mitmdump, args, extra)
-    if m and m.errorcheck.has_errored:
-        sys.exit(1)
-    return m
+    if m and m.errorcheck.has_errored:  # type: ignore
+        return 1
+    return None
 
 
-def mitmweb(args=None):  # pragma: no cover
+def mitmweb(args=None) -> typing.Optional[int]:  # pragma: no cover
     from mitmproxy.tools import web
-    return run(web.master.WebMaster, cmdline.mitmweb, args)
+    run(web.master.WebMaster, cmdline.mitmweb, args)
+    return None

{"golden_diff": "diff --git a/mitmproxy/tools/main.py b/mitmproxy/tools/main.py\n--- a/mitmproxy/tools/main.py\n+++ b/mitmproxy/tools/main.py\n@@ -67,7 +67,7 @@\n         make_parser: typing.Callable[[options.Options], argparse.ArgumentParser],\n         arguments: typing.Sequence[str],\n         extra: typing.Callable[[typing.Any], dict] = None\n-):  # pragma: no cover\n+) -> master.Master:  # pragma: no cover\n     \"\"\"\n         extra: Extra argument processing callable which returns a dict of\n         options.\n@@ -121,7 +121,11 @@\n         signal.signal(signal.SIGTERM, cleankill)\n         loop = asyncio.get_event_loop()\n         for signame in ('SIGINT', 'SIGTERM'):\n-            loop.add_signal_handler(getattr(signal, signame), master.shutdown)\n+            try:\n+                loop.add_signal_handler(getattr(signal, signame), master.shutdown)\n+            except NotImplementedError:\n+                # Not supported on Windows\n+                pass\n         master.run()\n     except exceptions.OptionsError as e:\n         print(\"%s: %s\" % (sys.argv[0], e), file=sys.stderr)\n@@ -131,19 +135,18 @@\n     return master\n \n \n-def mitmproxy(args=None):  # pragma: no cover\n+def mitmproxy(args=None) -> typing.Optional[int]:  # pragma: no cover\n     if os.name == \"nt\":\n         print(\"Error: mitmproxy's console interface is not supported on Windows. \"\n               \"You can run mitmdump or mitmweb instead.\", file=sys.stderr)\n-        sys.exit(1)\n-\n+        return 1\n     assert_utf8_env()\n-\n     from mitmproxy.tools import console\n-    return run(console.master.ConsoleMaster, cmdline.mitmproxy, args)\n+    run(console.master.ConsoleMaster, cmdline.mitmproxy, args)\n+    return None\n \n \n-def mitmdump(args=None):  # pragma: no cover\n+def mitmdump(args=None) -> typing.Optional[int]:  # pragma: no cover\n     from mitmproxy.tools import dump\n \n     def extra(args):\n@@ -157,11 +160,12 @@\n         return {}\n \n     m = run(dump.DumpMaster, cmdline.mitmdump, args, extra)\n-    if m and m.errorcheck.has_errored:\n-        sys.exit(1)\n-    return m\n+    if m and m.errorcheck.has_errored:  # type: ignore\n+        return 1\n+    return None\n \n \n-def mitmweb(args=None):  # pragma: no cover\n+def mitmweb(args=None) -> typing.Optional[int]:  # pragma: no cover\n     from mitmproxy.tools import web\n-    return run(web.master.WebMaster, cmdline.mitmweb, args)\n+    run(web.master.WebMaster, cmdline.mitmweb, args)\n+    return None\n", "issue": "add_signal_handler is strictly UNIX only. \nhttps://github.com/mitmproxy/mitmproxy/blob/4e126c0fbaafffd23e1a80926de6a99c897f9af0/mitmproxy/tools/main.py#L124\r\nAccording to the `asyncio`  documentation for [Python 3.6](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.add_signal_handler) , `add_signal_handler` is supported for UNIX system only. I haven't experienced or tested this issue but it maybe a potential risk since mitmproxy supports multiple platforms. \r\n\r\nIf someone has a Windows OS and/or some knowledge about asyncio maybe you could verify this.\n", "before_files": [{"content": "from __future__ import print_function  # this is here for the version check to work on Python 2.\n\nimport asyncio\nimport sys\n\nif sys.version_info < (3, 6):\n    # This must be before any mitmproxy imports, as they already break!\n    # Keep all other imports below with the 'noqa' magic comment.\n    print(\"#\" * 49, file=sys.stderr)\n    print(\"# mitmproxy requires Python 3.6 or higher! #\", file=sys.stderr)\n    print(\"#\" * 49, file=sys.stderr)\n\nimport argparse  # noqa\nimport os  # noqa\nimport signal  # noqa\nimport typing  # noqa\n\nfrom mitmproxy.tools import cmdline  # noqa\nfrom mitmproxy import exceptions, master  # noqa\nfrom mitmproxy import options  # noqa\nfrom mitmproxy import optmanager  # noqa\nfrom mitmproxy import proxy  # noqa\nfrom mitmproxy import log  # noqa\nfrom mitmproxy.utils import debug, arg_check  # noqa\n\n\ndef assert_utf8_env():\n    spec = \"\"\n    for i in [\"LANG\", \"LC_CTYPE\", \"LC_ALL\"]:\n        spec += os.environ.get(i, \"\").lower()\n    if \"utf\" not in spec:\n        print(\n            \"Error: mitmproxy requires a UTF console environment.\",\n            file=sys.stderr\n        )\n        print(\n            \"Set your LANG environment variable to something like en_US.UTF-8\",\n            file=sys.stderr\n        )\n        sys.exit(1)\n\n\ndef process_options(parser, opts, args):\n    if args.version:\n        print(debug.dump_system_info())\n        sys.exit(0)\n    if args.quiet or args.options or args.commands:\n        # also reduce log verbosity if --options or --commands is passed,\n        # we don't want log messages from regular startup then.\n        args.termlog_verbosity = 'error'\n        args.flow_detail = 0\n    if args.verbose:\n        args.termlog_verbosity = 'debug'\n        args.flow_detail = 2\n\n    adict = {}\n    for n in dir(args):\n        if n in opts:\n            adict[n] = getattr(args, n)\n    opts.merge(adict)\n\n    return proxy.config.ProxyConfig(opts)\n\n\ndef run(\n        master_cls: typing.Type[master.Master],\n        make_parser: typing.Callable[[options.Options], argparse.ArgumentParser],\n        arguments: typing.Sequence[str],\n        extra: typing.Callable[[typing.Any], dict] = None\n):  # pragma: no cover\n    \"\"\"\n        extra: Extra argument processing callable which returns a dict of\n        options.\n    \"\"\"\n    debug.register_info_dumpers()\n\n    opts = options.Options()\n    master = master_cls(opts)\n\n    parser = make_parser(opts)\n\n    # To make migration from 2.x to 3.0 bearable.\n    if \"-R\" in sys.argv and sys.argv[sys.argv.index(\"-R\") + 1].startswith(\"http\"):\n        print(\"-R is used for specifying replacements.\\n\"\n              \"To use mitmproxy in reverse mode please use --mode reverse:SPEC instead\")\n\n    try:\n        args = parser.parse_args(arguments)\n    except SystemExit:\n        arg_check.check()\n        sys.exit(1)\n    try:\n        unknown = optmanager.load_paths(opts, args.conf)\n        pconf = process_options(parser, opts, args)\n        server: typing.Any = None\n        if pconf.options.server:\n            try:\n                server = proxy.server.ProxyServer(pconf)\n            except exceptions.ServerException as v:\n                print(str(v), file=sys.stderr)\n                sys.exit(1)\n        else:\n            server = proxy.server.DummyServer(pconf)\n\n        master.server = server\n        master.addons.trigger(\"configure\", opts.keys())\n        master.addons.trigger(\"tick\")\n        opts.update_known(**unknown)\n        if args.options:\n            print(optmanager.dump_defaults(opts))\n            sys.exit(0)\n        if args.commands:\n            master.commands.dump()\n            sys.exit(0)\n        opts.set(*args.setoptions)\n        if extra:\n            opts.update(**extra(args))\n\n        def cleankill(*args, **kwargs):\n            master.shutdown()\n        signal.signal(signal.SIGTERM, cleankill)\n        loop = asyncio.get_event_loop()\n        for signame in ('SIGINT', 'SIGTERM'):\n            loop.add_signal_handler(getattr(signal, signame), master.shutdown)\n        master.run()\n    except exceptions.OptionsError as e:\n        print(\"%s: %s\" % (sys.argv[0], e), file=sys.stderr)\n        sys.exit(1)\n    except (KeyboardInterrupt, RuntimeError) as e:\n        pass\n    return master\n\n\ndef mitmproxy(args=None):  # pragma: no cover\n    if os.name == \"nt\":\n        print(\"Error: mitmproxy's console interface is not supported on Windows. \"\n              \"You can run mitmdump or mitmweb instead.\", file=sys.stderr)\n        sys.exit(1)\n\n    assert_utf8_env()\n\n    from mitmproxy.tools import console\n    return run(console.master.ConsoleMaster, cmdline.mitmproxy, args)\n\n\ndef mitmdump(args=None):  # pragma: no cover\n    from mitmproxy.tools import dump\n\n    def extra(args):\n        if args.filter_args:\n            v = \" \".join(args.filter_args)\n            return dict(\n                save_stream_filter=v,\n                readfile_filter=v,\n                dumper_filter=v,\n            )\n        return {}\n\n    m = run(dump.DumpMaster, cmdline.mitmdump, args, extra)\n    if m and m.errorcheck.has_errored:\n        sys.exit(1)\n    return m\n\n\ndef mitmweb(args=None):  # pragma: no cover\n    from mitmproxy.tools import web\n    return run(web.master.WebMaster, cmdline.mitmweb, args)\n", "path": "mitmproxy/tools/main.py"}], "after_files": [{"content": "from __future__ import print_function  # this is here for the version check to work on Python 2.\n\nimport asyncio\nimport sys\n\nif sys.version_info < (3, 6):\n    # This must be before any mitmproxy imports, as they already break!\n    # Keep all other imports below with the 'noqa' magic comment.\n    print(\"#\" * 49, file=sys.stderr)\n    print(\"# mitmproxy requires Python 3.6 or higher! #\", file=sys.stderr)\n    print(\"#\" * 49, file=sys.stderr)\n\nimport argparse  # noqa\nimport os  # noqa\nimport signal  # noqa\nimport typing  # noqa\n\nfrom mitmproxy.tools import cmdline  # noqa\nfrom mitmproxy import exceptions, master  # noqa\nfrom mitmproxy import options  # noqa\nfrom mitmproxy import optmanager  # noqa\nfrom mitmproxy import proxy  # noqa\nfrom mitmproxy import log  # noqa\nfrom mitmproxy.utils import debug, arg_check  # noqa\n\n\ndef assert_utf8_env():\n    spec = \"\"\n    for i in [\"LANG\", \"LC_CTYPE\", \"LC_ALL\"]:\n        spec += os.environ.get(i, \"\").lower()\n    if \"utf\" not in spec:\n        print(\n            \"Error: mitmproxy requires a UTF console environment.\",\n            file=sys.stderr\n        )\n        print(\n            \"Set your LANG environment variable to something like en_US.UTF-8\",\n            file=sys.stderr\n        )\n        sys.exit(1)\n\n\ndef process_options(parser, opts, args):\n    if args.version:\n        print(debug.dump_system_info())\n        sys.exit(0)\n    if args.quiet or args.options or args.commands:\n        # also reduce log verbosity if --options or --commands is passed,\n        # we don't want log messages from regular startup then.\n        args.termlog_verbosity = 'error'\n        args.flow_detail = 0\n    if args.verbose:\n        args.termlog_verbosity = 'debug'\n        args.flow_detail = 2\n\n    adict = {}\n    for n in dir(args):\n        if n in opts:\n            adict[n] = getattr(args, n)\n    opts.merge(adict)\n\n    return proxy.config.ProxyConfig(opts)\n\n\ndef run(\n        master_cls: typing.Type[master.Master],\n        make_parser: typing.Callable[[options.Options], argparse.ArgumentParser],\n        arguments: typing.Sequence[str],\n        extra: typing.Callable[[typing.Any], dict] = None\n) -> master.Master:  # pragma: no cover\n    \"\"\"\n        extra: Extra argument processing callable which returns a dict of\n        options.\n    \"\"\"\n    debug.register_info_dumpers()\n\n    opts = options.Options()\n    master = master_cls(opts)\n\n    parser = make_parser(opts)\n\n    # To make migration from 2.x to 3.0 bearable.\n    if \"-R\" in sys.argv and sys.argv[sys.argv.index(\"-R\") + 1].startswith(\"http\"):\n        print(\"-R is used for specifying replacements.\\n\"\n              \"To use mitmproxy in reverse mode please use --mode reverse:SPEC instead\")\n\n    try:\n        args = parser.parse_args(arguments)\n    except SystemExit:\n        arg_check.check()\n        sys.exit(1)\n    try:\n        unknown = optmanager.load_paths(opts, args.conf)\n        pconf = process_options(parser, opts, args)\n        server: typing.Any = None\n        if pconf.options.server:\n            try:\n                server = proxy.server.ProxyServer(pconf)\n            except exceptions.ServerException as v:\n                print(str(v), file=sys.stderr)\n                sys.exit(1)\n        else:\n            server = proxy.server.DummyServer(pconf)\n\n        master.server = server\n        master.addons.trigger(\"configure\", opts.keys())\n        master.addons.trigger(\"tick\")\n        opts.update_known(**unknown)\n        if args.options:\n            print(optmanager.dump_defaults(opts))\n            sys.exit(0)\n        if args.commands:\n            master.commands.dump()\n            sys.exit(0)\n        opts.set(*args.setoptions)\n        if extra:\n            opts.update(**extra(args))\n\n        def cleankill(*args, **kwargs):\n            master.shutdown()\n        signal.signal(signal.SIGTERM, cleankill)\n        loop = asyncio.get_event_loop()\n        for signame in ('SIGINT', 'SIGTERM'):\n            try:\n                loop.add_signal_handler(getattr(signal, signame), master.shutdown)\n            except NotImplementedError:\n                # Not supported on Windows\n                pass\n        master.run()\n    except exceptions.OptionsError as e:\n        print(\"%s: %s\" % (sys.argv[0], e), file=sys.stderr)\n        sys.exit(1)\n    except (KeyboardInterrupt, RuntimeError) as e:\n        pass\n    return master\n\n\ndef mitmproxy(args=None) -> typing.Optional[int]:  # pragma: no cover\n    if os.name == \"nt\":\n        print(\"Error: mitmproxy's console interface is not supported on Windows. \"\n              \"You can run mitmdump or mitmweb instead.\", file=sys.stderr)\n        return 1\n    assert_utf8_env()\n    from mitmproxy.tools import console\n    run(console.master.ConsoleMaster, cmdline.mitmproxy, args)\n    return None\n\n\ndef mitmdump(args=None) -> typing.Optional[int]:  # pragma: no cover\n    from mitmproxy.tools import dump\n\n    def extra(args):\n        if args.filter_args:\n            v = \" \".join(args.filter_args)\n            return dict(\n                save_stream_filter=v,\n                readfile_filter=v,\n                dumper_filter=v,\n            )\n        return {}\n\n    m = run(dump.DumpMaster, cmdline.mitmdump, args, extra)\n    if m and m.errorcheck.has_errored:  # type: ignore\n        return 1\n    return None\n\n\ndef mitmweb(args=None) -> typing.Optional[int]:  # pragma: no cover\n    from mitmproxy.tools import web\n    run(web.master.WebMaster, cmdline.mitmweb, args)\n    return None\n", "path": "mitmproxy/tools/main.py"}]}