Bash everything until it all works again
diff --git a/src/waitress/__init__.py b/src/waitress/__init__.py index 39f43ae..bbb99da 100644 --- a/src/waitress/__init__.py +++ b/src/waitress/__init__.py
@@ -3,14 +3,14 @@ from waitress.server import create_server -def serve(app, opts, **kw): +def serve(app, **kw): _server = kw.pop("_server", create_server) # test shim _quiet = kw.pop("_quiet", False) # test shim _profile = kw.pop("_profile", False) # test shim if not _quiet: # pragma: no cover # idempotent if logging has already been set up logging.basicConfig() - server = _server(app, opts, **kw) + server = _server(app, **kw) if not _quiet: # pragma: no cover server.print_listen("Serving on http://{}:{}") if _profile: # pragma: no cover @@ -20,7 +20,7 @@ def serve_paste(app, global_conf, **kw): - serve(app, {}, **kw) + serve(app, **kw) return 0
diff --git a/src/waitress/adjustments.py b/src/waitress/adjustments.py index 185bd82..afc1efa 100644 --- a/src/waitress/adjustments.py +++ b/src/waitress/adjustments.py
@@ -11,9 +11,8 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -"""Adjustments are tunable parameters. -""" -import getopt +"""Adjustments are tunable parameters.""" + import socket import warnings @@ -43,7 +42,7 @@ def asoctal(s): """Convert the given octal string to an actual number.""" - return int(s, 8) + return s if isinstance(s, int) else int(s, 8) def aslist_cronly(value): @@ -56,6 +55,8 @@ """Return a list of strings, separating the input based on newlines and, if flatten=True (the default), also split on spaces within each line.""" + if isinstance(value, list): + return value values = aslist_cronly(value) result = [] for value in values: @@ -289,7 +290,7 @@ # (or when using the Proxy settings, without forwarding a Host header) server_name = "waitress.invalid" - def __init__(self, opts, **kw): + def __init__(self, **kw): if "listen" in kw and ("host" in kw or "port" in kw): raise ValueError("host or port may not be set if listen is set.") @@ -313,12 +314,6 @@ "send_bytes will be removed in a future release", DeprecationWarning ) - # opts are already validated, so that we set them as is - for k, v in opts.items(): - if k not in self._param_map: - raise ValueError("Unknown adjustment %r" % k) - setattr(self, k, v) - for k, v in kw.items(): if k not in self._param_map: raise ValueError("Unknown adjustment %r" % k) @@ -453,47 +448,6 @@ self.check_sockets(self.sockets) @classmethod - def parse_args(cls, argv): - """Pre-parse command line arguments for input into __init__. Note that - this does not cast values into adjustment types, it just creates a - dictionary suitable for passing into __init__, where __init__ does the - casting. - """ - long_opts = ["help", "call"] - for opt, cast in cls._params: - opt = opt.replace("_", "-") - if cast is asbool: - long_opts.append(opt) - long_opts.append("no-" + opt) - else: - long_opts.append(opt + "=") - - kw = { - "help": False, - "call": False, - } - - opts, args = getopt.getopt(argv, "", long_opts) - for opt, value in opts: - param = opt.lstrip("-").replace("-", "_") - - if param == "listen": - kw["listen"] = "{} {}".format(kw.get("listen", ""), value) - continue - - if param.startswith("no_"): - param = param[3:] - kw[param] = "false" - elif param in ("help", "call"): - kw[param] = True - elif cls._param_map[param] is asbool: - kw[param] = "true" - else: - kw[param] = value - - return kw, args - - @classmethod def check_sockets(cls, sockets): has_unix_socket = False has_inet_socket = False
diff --git a/src/waitress/runner.py b/src/waitress/runner.py index b69d9ca..5056943 100644 --- a/src/waitress/runner.py +++ b/src/waitress/runner.py
@@ -12,9 +12,8 @@ # ############################################################################## """Command line runner.""" -import warnings -import re -import getopt + +from argparse import ArgumentParser, BooleanOptionalAction import logging import operator as op import os @@ -22,33 +21,11 @@ import pathlib import pkgutil import sys -from argparse import ArgumentParser, BooleanOptionalAction -from dataclasses import dataclass -from ipaddress import ip_address from urllib.parse import urlparse -import importlib -from waitress import serve -from waitress.adjustments import Adjustments + +from waitress import adjustments, serve from waitress.utilities import logger - -def show_exception(stream): - exc_type, exc_value = sys.exc_info()[:2] - args = getattr(exc_value, "args", None) - print( - ("There was an exception ({}) importing your module.\n").format( - exc_type.__name__, - ), - file=stream, - ) - if args: - print("It had these arguments: ", file=stream) - for idx, arg in enumerate(args, start=1): - print(f"{idx}. {arg}\n", file=stream) - else: - print("It had no arguments.", file=stream) - - host_and_port = op.attrgetter("hostname", "port") @@ -57,66 +34,39 @@ res = urlparse(f"scheme://{value}") if not all(host_and_port(res)): - raise ValueError("Not a socket! Should HOST:PORT", value) + raise ValueError("Not a valid host and port! Should HOST:PORT", value) - return str(ip_address(res.hostname)), str(res.port) + return res.hostname, str(res.port) -def _validate_opts(opts): - if hasattr(opts, 'listen') and (hasattr(opts, 'host') or hasattr(opts, 'port')): - raise ValueError("host or port may not be set if listen is set.") - - if hasattr(opts, 'unix_socket') and (hasattr(opts, 'host') or hasattr(opts, 'port')): - raise ValueError("unix_socket may not be set if host or port is set") - - if hasattr(opts, 'unix_socket') and hasattr(opts, 'listen'): - raise ValueError("unix_socket may not be set if listen is set") - - -@dataclass(frozen=True) -class DEFAULTS: - BACKLOG = 1024 - HOST = "0.0.0.0" - IDENT = "waitress" - PORT = 8080 - THREADS = 4 - UNIX_SOCKET_PERMS = "600" - URL_SCHEME = "http" - # fmt: off - ASYNCORE_LOOP_TIMEOUT = 1 # second - CHANNEL_TIMEOUT = 120 # seconds - CHANNEL_REQUEST_LOOKAHEAD = 0 - CLEANUP_INTERVAL = 30 # seconds - CONNECTION_LIMIT = 100 - INBUF_HIGH_WATERMARK = 16777216 # 16 MB - INBUF_OVERFLOW = 524288 # 512 KB - MAX_REQUEST_BODY_SIZE = 1073741824 # 1 GB - MAX_REQUEST_HEADER_SIZE = 262144 # 256 KB - OUTBUF_HIGH_WATERMARK = 16777216 # 16 MB - OUTBUF_OVERFLOW = 1048576 # 1 MB - RECV_BYTES = 8192 # 8 KB - SEND_BYTES = 18000 - # fmt: on - - -def run(argv=sys.argv, _serve=serve): - """Command line runner.""" +def make_parser(): parser = ArgumentParser() # Standard options parser.add_argument( - "--app", - required=True, - help="Specify WSGI application to run. Required. Can be passed at any position.", - ) - parser.add_argument( "--call", action="store_true", help="Call the given object to get the WSGI application.", ) - parser.add_argument( + listener = parser.add_mutually_exclusive_group(required=False) + listener.add_argument( + "--listen", + action="append", + type=_valid_socket, + metavar="HOST:PORT", + help="Tell waitress to listen on an ip port combination.", + ) + listener.add_argument( + "--unix-socket", + type=pathlib.Path, + help="""Path of Unix socket. If a socket path is specified, a +Unix domain socket is made instead of the usual inet domain socket. + +Not available on Windows.""", + ) + listener.add_argument( "--host", - type=ip_address, - default=DEFAULTS.HOST, + type=str, + default=adjustments.Adjustments.host, help="""Hostname or IP address on which to listen. Note: may not be used together with `--listen`. @@ -125,42 +75,32 @@ parser.add_argument( "--port", type=int, - default=DEFAULTS.PORT, - help="""TCP port on which to listen. -Note: may not be used together with `--listen`. + default=adjustments.Adjustments.port, + help="""TCP port on which to listen. Ignored if --listen or --unix-socket are used. Default is %(default)s.""", ) - parser.add_argument( - "--listen", - type=_valid_socket, - action='append', - help="Tell waitress to listen on an ip port combination.", - ) - parser.add_argument( - "--ipv4", action=BooleanOptionalAction, help="Toggle on/off IPv4 support." - ) - parser.add_argument( - "--ipv6", action=BooleanOptionalAction, help="Toggle on/off IPv6 support." - ) - parser.add_argument( - "--unix-socket", - type=pathlib.Path, - help="""Path of Unix socket. If a socket path is specified, a -Unix domain socket is made instead of the usual inet domain socket. -Not available on Windows.""", + parser.add_argument( + "--ipv4", + action=BooleanOptionalAction, + help="Toggle on/off IPv4 support.", + ) + parser.add_argument( + "--ipv6", + action=BooleanOptionalAction, + help="Toggle on/off IPv6 support.", ) parser.add_argument( "--unix-socket-perms", - type=lambda v: int(v, base=8), - default=DEFAULTS.UNIX_SOCKET_PERMS, + type=adjustments.asoctal, + default=oct(adjustments.Adjustments.unix_socket_perms), help="""Octal permissions to use for the Unix domain socket. Default is %(default)s.""", ) parser.add_argument( "--url-scheme", - default=DEFAULTS.URL_SCHEME, + default=adjustments.Adjustments.url_scheme, help="Default `wsgi.url_scheme` value. Default is %(default)r.", ) parser.add_argument( @@ -173,7 +113,7 @@ ) parser.add_argument( "--ident", - default=DEFAULTS.IDENT, + default=adjustments.Adjustments.ident, help="""Server identity used in the 'Server' header in responses. Default is %(default)r.""", ) @@ -181,37 +121,36 @@ parser.add_argument( "--threads", type=int, - default=DEFAULTS.THREADS, + default=adjustments.Adjustments.threads, help="""Number of threads used to process application logic. Default is %(default)s.""", ) parser.add_argument( "--backlog", type=int, - default=DEFAULTS.BACKLOG, + default=adjustments.Adjustments.backlog, help="""Connection backlog for the server. Default is %(default)s.""", ) parser.add_argument( "--recv-bytes", type=int, - default=DEFAULTS.RECV_BYTES, + default=adjustments.Adjustments.recv_bytes, help="""Number of bytes to request when calling `socket.recv()`. Default is %(default)s bytes.""", ) parser.add_argument( "--send-bytes", type=int, - default=DEFAULTS.SEND_BYTES, + default=adjustments.Adjustments.send_bytes, help="""Number of bytes to send to `socket.send()`. Note: multiples of 9000 should avoid partly-filled TCP packets. Default is %(default)s bytes.""", - ) parser.add_argument( "--outbuf-overflow", type=int, - default=DEFAULTS.OUTBUF_OVERFLOW, + default=adjustments.Adjustments.outbuf_overflow, help="""A temporary file should be created if the pending output is larger than this. @@ -220,7 +159,7 @@ parser.add_argument( "--outbuf-high-watermark", type=int, - default=DEFAULTS.OUTBUF_HIGH_WATERMARK, + default=adjustments.Adjustments.outbuf_high_watermark, help="""The `app_iter` will pause when pending output is larger than this value and will resume once enough data is written to the socket to fall below this threshold. @@ -230,7 +169,7 @@ parser.add_argument( "--inbuf-overflow", type=int, - default=DEFAULTS.INBUF_OVERFLOW, + default=adjustments.Adjustments.inbuf_overflow, help="""A temporary file should be created if the pending input is larger than this. @@ -239,7 +178,7 @@ parser.add_argument( "--connection-limit", type=int, - default=DEFAULTS.CONNECTION_LIMIT, + default=adjustments.Adjustments.connection_limit, help="""Stop creating new channels if too many are already active. Default is %(default)s.""", @@ -247,7 +186,7 @@ parser.add_argument( "--cleanup-interval", type=int, - default=DEFAULTS.CLEANUP_INTERVAL, + default=adjustments.Adjustments.cleanup_interval, help="""Minimum seconds between cleaning up inactive channels. See `--channel-timeout` option. @@ -256,7 +195,7 @@ parser.add_argument( "--channel-timeout", type=int, - default=DEFAULTS.CHANNEL_TIMEOUT, + default=adjustments.Adjustments.channel_timeout, help="""Maximum number of seconds to leave inactive connections open. 'Inactive' is defined as 'has received no data from the client and has sent no data to the client'. @@ -266,21 +205,21 @@ parser.add_argument( "--max-request-header-size", type=int, - default=DEFAULTS.MAX_REQUEST_HEADER_SIZE, + default=adjustments.Adjustments.max_request_header_size, help="""Maximum size of all request headers combined. Default is %(default)s bytes.""", ) parser.add_argument( "--max-request-body-size", type=int, - default=DEFAULTS.MAX_REQUEST_BODY_SIZE, + default=adjustments.Adjustments.max_request_body_size, help="""Maximum size of request body. Default is %(default)s bytes.""", ) parser.add_argument( "--asyncore-loop-timeout", type=int, - default=DEFAULTS.ASYNCORE_LOOP_TIMEOUT, + default=adjustments.Adjustments.asyncore_loop_timeout, help="""The timeout value in seconds passed to `asyncore.loop()`. Default is %(default)s.""", ) @@ -295,7 +234,7 @@ parser.add_argument( "--channel-request-lookahead", type=int, - default=DEFAULTS.CHANNEL_REQUEST_LOOKAHEAD, + default=adjustments.Adjustments.channel_request_lookahead, help="""Allows channels to stay readable and buffer more requests up to the given maximum even if a request is already being processed. This allows detecting if a client closed the connection while its request is being processed. @@ -319,6 +258,27 @@ Default is 'no'.""", ) + # This hack is needed to support the use of a flag and the legacy + # positional syntax. + parser.add_argument( + "--app", + required=False, + action="append", + help="Specify WSGI application to run. Required, but can be given without the flag for backward compatibility.", + ) + parser.add_argument( + "app", + nargs="?", + action="append", + metavar="APP", + help="Legacy method for specifying the WSGI application to run.", + ) + return parser + + +def run(argv=sys.argv, _serve=serve): + """Command line runner.""" + parser = make_parser() args = parser.parse_args(argv[1:]) # set a default level for the logger only if it hasn't been set explicitly @@ -330,26 +290,29 @@ # Add the current directory onto sys.path sys.path.append(os.getcwd()) + apps = list(filter(None, args.app)) + if len(apps) != 1: + print("Error: Specify one and only one WSGI application", file=sys.stderr) + parser.print_help(file=sys.stderr) + return 1 + app_name = apps[0] + del args.app + # Get the WSGI function. try: - app = pkgutil.resolve_name(args.app) - except ImportError: - print(f"Bad module {module!r}", file=sys.stderr) + app = pkgutil.resolve_name(app_name) + except (ImportError, AttributeError, ValueError) as exc: + print(f"Error: {exc}", file=sys.stderr) parser.print_help(file=sys.stderr) - show_exception(sys.stderr) - return 1 - except (AttributeError, ValueError): - print(f"Bad object name {obj_name!r}", file=sys.stderr) - parser.print_help(file=sys.stderr) - show_exception(sys.stderr) return 1 if args.call: app = app() del args.call + if args.listen: + del args.port + del args.host - from pprint import pprint as pp; pp(vars(args)) opts = {k: v for k, v in vars(args).items() if v is not None} - _validate_opts(opts) - _serve(app, opts) + _serve(app, **opts) return 0
diff --git a/src/waitress/server.py b/src/waitress/server.py index 75a35bf..2f10560 100644 --- a/src/waitress/server.py +++ b/src/waitress/server.py
@@ -30,7 +30,6 @@ def create_server( application, - opts, # dict map=None, _start=True, # test shim _sock=None, # test shim @@ -42,7 +41,7 @@ 'The "app" passed to ``create_server`` was ``None``. You forgot ' "to return a WSGI app within your application." ) - adj = Adjustments(opts, **kw) + adj = Adjustments(**kw) if map is None: # pragma: nocover map = {} @@ -192,7 +191,7 @@ **kw, ): if adj is None: - adj = Adjustments({}, **kw) + adj = Adjustments(**kw) if adj.trusted_proxy or adj.clear_untrusted_proxy_headers: # wrap the application to deal with proxy headers
diff --git a/tests/test_adjustments.py b/tests/test_adjustments.py index 9b7cf43..25957ce 100644 --- a/tests/test_adjustments.py +++ b/tests/test_adjustments.py
@@ -103,7 +103,7 @@ def _makeOne(self, **kw): from waitress.adjustments import Adjustments - return Adjustments({}, **kw) + return Adjustments(**kw) def test_goodvars(self): inst = self._makeOne( @@ -385,93 +385,13 @@ self.assertEqual(inst.ident, "specific_header") -class TestCLI(unittest.TestCase): - def parse(self, argv): - from waitress.adjustments import Adjustments - - return Adjustments.parse_args(argv) - - def assertDictContainsSubset(self, subset, dictionary): - self.assertTrue(set(subset.items()) <= set(dictionary.items())) - - def test_noargs(self): - opts, args = self.parse([]) - self.assertDictEqual(opts, {"call": False, "help": False}) - self.assertSequenceEqual(args, []) - - def test_help(self): - opts, args = self.parse(["--help"]) - self.assertDictEqual(opts, {"call": False, "help": True}) - self.assertSequenceEqual(args, []) - - def test_call(self): - opts, args = self.parse(["--call"]) - self.assertDictEqual(opts, {"call": True, "help": False}) - self.assertSequenceEqual(args, []) - - def test_both(self): - opts, args = self.parse(["--call", "--help"]) - self.assertDictEqual(opts, {"call": True, "help": True}) - self.assertSequenceEqual(args, []) - - def test_positive_boolean(self): - opts, args = self.parse(["--expose-tracebacks"]) - self.assertDictContainsSubset({"expose_tracebacks": "true"}, opts) - self.assertSequenceEqual(args, []) - - def test_negative_boolean(self): - opts, args = self.parse(["--no-expose-tracebacks"]) - self.assertDictContainsSubset({"expose_tracebacks": "false"}, opts) - self.assertSequenceEqual(args, []) - - def test_cast_params(self): - opts, args = self.parse( - ["--host=localhost", "--port=80", "--unix-socket-perms=777"] - ) - self.assertDictContainsSubset( - { - "host": "localhost", - "port": "80", - "unix_socket_perms": "777", - }, - opts, - ) - self.assertSequenceEqual(args, []) - - def test_listen_params(self): - opts, args = self.parse( - [ - "--listen=test:80", - ] - ) - - self.assertDictContainsSubset({"listen": " test:80"}, opts) - self.assertSequenceEqual(args, []) - - def test_multiple_listen_params(self): - opts, args = self.parse( - [ - "--listen=test:80", - "--listen=test:8080", - ] - ) - - self.assertDictContainsSubset({"listen": " test:80 test:8080"}, opts) - self.assertSequenceEqual(args, []) - - def test_bad_param(self): - import getopt - - self.assertRaises(getopt.GetoptError, self.parse, ["--no-host"]) - - if hasattr(socket, "AF_UNIX"): class TestUnixSocket(unittest.TestCase): def _makeOne(self, **kw): from waitress.adjustments import Adjustments - return Adjustments({}, **kw) + return Adjustments(**kw) def test_dont_mix_internet_and_unix_sockets(self): sockets = [
diff --git a/tests/test_functional.py b/tests/test_functional.py index 5c65801..d3f1b37 100644 --- a/tests/test_functional.py +++ b/tests/test_functional.py
@@ -3,7 +3,6 @@ import logging import multiprocessing import os -import signal import socket import string import subprocess
diff --git a/tests/test_init.py b/tests/test_init.py index 7c6e10d..58b5c2e 100644 --- a/tests/test_init.py +++ b/tests/test_init.py
@@ -5,7 +5,7 @@ def _callFUT(self, app, **kw): from waitress import serve - return serve(app, {}, **kw) + return serve(app, **kw) def test_it(self): server = DummyServerFactory() @@ -34,7 +34,7 @@ class DummyServerFactory: ran = False - def __call__(self, app, opts, **kw): + def __call__(self, app, **kw): self.adj = DummyAdj(kw) self.app = app self.kw = kw
diff --git a/tests/test_parser.py b/tests/test_parser.py index 5560a52..4195295 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py
@@ -35,7 +35,7 @@ class TestHTTPRequestParser(unittest.TestCase): def setUp(self): - my_adj = Adjustments({}) + my_adj = Adjustments() self.parser = HTTPRequestParser(my_adj) def test_get_body_stream_None(self): @@ -582,7 +582,7 @@ class TestHTTPRequestParserIntegration(unittest.TestCase): def setUp(self): - my_adj = Adjustments({}) + my_adj = Adjustments() self.parser = HTTPRequestParser(my_adj) def feed(self, data):
diff --git a/tests/test_runner.py b/tests/test_runner.py index 2b91f1f..39109f7 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py
@@ -3,48 +3,43 @@ import sys import unittest -from waitress import runner +from waitress import adjustments, runner def test_valid_socket(): - assert runner._valid_socket('0.0.0.0:42') == ('0.0.0.0', '42') - assert runner._valid_socket('[2001:db8::1]:42') == ('2001:db8::1', '42') + assert runner._valid_socket("0.0.0.0:42") == ("0.0.0.0", "42") + assert runner._valid_socket("[2001:db8::1]:42") == ("2001:db8::1", "42") class Test_run(unittest.TestCase): def match_output(self, argv, code, regex): argv = ["waitress-serve"] + argv with capture() as captured: - self.assertEqual(runner.run(argv=argv), code) + try: + self.assertEqual(runner.run(argv=argv), code) + except SystemExit as exit: + self.assertEqual(exit.code, code) self.assertRegex(captured.getvalue(), regex) captured.close() - def test_bad(self): - self.match_output(["--app=foo:bar", "--bad-opt"], 2, "error: unrecognized arguments: waitress-serve --bad-opt") - - def test_help(self): - self.match_output(["--help"], 0, "^usage: waitress-serve") - def test_no_app(self): - self.match_output([], 2, "error: the following arguments are required: --app") + self.match_output([], 1, "^Error: Specify one and only one WSGI application") def test_multiple_apps_app(self): - self.match_output(["--app", "a:a", "--app", "b:b"], 1, "^Error: Specify one application only") + self.match_output( + ["--app", "a:a", "--app", "b:b"], + 1, + "^Error: Specify one and only one WSGI application", + ) def test_bad_apps_app(self): self.match_output(["--app", "a"], 1, "^Error: No module named 'a'") def test_bad_app_module(self): - self.match_output(["--app", "nonexistent:a"], 1, "^Error: No module named 'nonexistent'") - self.match_output( ["--app", "nonexistent:a"], 1, - ( - r"There was an exception \((ImportError|ModuleNotFoundError)\) " - "importing your module.\n\nIt had these arguments: \n" - "1. No module named '?nonexistent'?" - ), + "^Error: No module named 'nonexistent'", ) def test_cwd_added_to_path(self): @@ -57,7 +52,8 @@ os.chdir(os.path.dirname(__file__)) argv = [ "waitress-serve", - "--app", "fixtureapps.runner:app", + "--app", + "fixtureapps.runner:app", ] self.assertEqual(runner.run(argv=argv, _serve=null_serve), 0) finally: @@ -76,7 +72,7 @@ def check_server(app, **kw): self.assertIs(app, _apps.app) - self.assertDictEqual(kw, {"port": "80"}) + self.assertEqual(kw["port"], 80) argv = [ "waitress-serve", @@ -85,12 +81,30 @@ ] self.assertEqual(runner.run(argv=argv, _serve=check_server), 0) + def test_good_listen(self): + from tests.fixtureapps import runner as _apps + + def check_server(app, **kw): + self.assertIs(app, _apps.app) + adj = adjustments.Adjustments(**kw) + self.assertListEqual( + [entry[3] for entry in adj.listen], + [("127.0.0.1", 80)], + ) + + argv = [ + "waitress-serve", + "--listen=127.0.0.1:80", + "--app=tests.fixtureapps.runner:app", + ] + self.assertEqual(runner.run(argv=argv, _serve=check_server), 0) + def test_returned_app(self): from tests.fixtureapps import runner as _apps def check_server(app, **kw): self.assertIs(app, _apps.app) - self.assertDictEqual(kw, {"port": "80"}) + self.assertEqual(kw["port"], 80) argv = [ "waitress-serve", @@ -100,36 +114,48 @@ ] self.assertEqual(runner.run(argv=argv, _serve=check_server), 0) - -class Test_helper(unittest.TestCase): - def test_exception_logging(self): - from waitress.runner import show_exception - - regex = ( - r"There was an exception \(ImportError\) importing your module." - r"\n\nIt had these arguments: \n1. My reason" + def test_bad_listen(self): + self.match_output( + [ + "--listen=foo/bar", + "--app=tests.fixtureapps.runner:app", + ], + 2, + "error: argument --listen: invalid _valid_socket value: 'foo/bar'", ) - with capture() as captured: - try: - raise ImportError("My reason") - except ImportError: - self.assertIsNone(show_exception(sys.stderr)) - self.assertRegex(captured.getvalue(), regex) - captured.close() - - regex = ( - r"There was an exception \(ImportError\) importing your module." - r"\n\nIt had no arguments." + def test_inet(self): + self.match_output( + [ + "--listen=127.0.0.1:8080", + "--host=127.0.0.1", + "--app=tests.fixtureapps.runner:app", + ], + 2, + "error: argument --host: not allowed with argument --listen", ) - with capture() as captured: - try: - raise ImportError - except ImportError: - self.assertIsNone(show_exception(sys.stderr)) - self.assertRegex(captured.getvalue(), regex) - captured.close() + def test_inet_and_unix_socket(self): + self.match_output( + [ + "--host=127.0.0.1", + "--unix-socket=/tmp/waitress.sock", + "--app=tests.fixtureapps.runner:app", + ], + 2, + "error: argument --unix-socket: not allowed with argument --host", + ) + + def test_listen_and_unix_socket(self): + self.match_output( + [ + "--listen=127.0.0.1:8080", + "--unix-socket=/tmp/waitress.sock", + "--app=tests.fixtureapps.runner:app", + ], + 2, + "error: argument --unix-socket: not allowed with argument --listen", + ) @contextlib.contextmanager
diff --git a/tests/test_server.py b/tests/test_server.py index 006bf10..cede49a 100644 --- a/tests/test_server.py +++ b/tests/test_server.py
@@ -22,7 +22,6 @@ self.inst = create_server( application, - {}, host=host, port=port, map=map, @@ -58,7 +57,6 @@ self.inst = create_server( app, - {}, listen=listen, map=map, _dispatcher=task_dispatcher, @@ -84,7 +82,6 @@ _sockets = sockets self.inst = create_server( application, - {}, map=map, _dispatcher=_dispatcher, _start=_start, @@ -325,7 +322,6 @@ self.inst = create_server( dummy_app, - {}, map={}, _start=_start, _sock=_sock, @@ -352,7 +348,6 @@ _sockets = sockets self.inst = create_server( application, - {}, map=map, _dispatcher=_dispatcher, _start=_start,