diff --git a/README.md b/README.md index 9d383c9..4dcb9a3 100644 --- a/README.md +++ b/README.md @@ -47,3 +47,13 @@ Notes: into documentation for ReadTheDocs works as expected. For more information, see the Python Project Template documentation on [Sphinx and Python Notebooks](https://lincc-ppt.readthedocs.io/en/latest/practices/sphinx.html#python-notebooks) + +## Running the TAP server with gunicorn + +Install the optional server extra (or install `gunicorn` separately), then run gunicorn +against the provided WSGI callable: + +``` +pip install 'hats-tap[server]' +gunicorn "hats_tap.tap_server:application" --bind 0.0.0.0:43213 +``` diff --git a/pyproject.toml b/pyproject.toml index 92fabb0..e8c54a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,9 @@ dev = [ "pytest-cov", # Used to report total code coverage "ruff", # Used for static linting of files ] +server = [ + "gunicorn>=22.0.0", # Optional WSGI server for production use +] [build-system] requires = [ diff --git a/src/hats_tap/tap_server.py b/src/hats_tap/tap_server.py index 1cc8073..c8379d6 100644 --- a/src/hats_tap/tap_server.py +++ b/src/hats_tap/tap_server.py @@ -27,6 +27,15 @@ app = Flask(__name__) +def create_app(): + """Return the Flask application for use with WSGI servers such as gunicorn.""" + return app + + +# Default WSGI callable name expected by gunicorn when no object is specified. +application = app + + # Log what the client is actually sending @app.before_request def log_request_info(): diff --git a/tests/hats_tap/test_tap_server_wsgi.py b/tests/hats_tap/test_tap_server_wsgi.py new file mode 100644 index 0000000..786623b --- /dev/null +++ b/tests/hats_tap/test_tap_server_wsgi.py @@ -0,0 +1,10 @@ +"""Tests for WSGI compatibility of the TAP server.""" + +from hats_tap import tap_server + + +def test_wsgi_app_exports(): + """The tap_server module should expose a WSGI callable for gunicorn.""" + wsgi_app = tap_server.create_app() + assert wsgi_app is tap_server.app + assert tap_server.application is tap_server.app