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,