Skip to content

Commit

Permalink
Improve separation of Uvicorn and Gunicorn servers
Browse files Browse the repository at this point in the history
- Make `process_manager` a required argument for `start.start_server()`
- Only set Gunicorn conf path when running Gunicorn

All unit tests pass, but when running Uvicorn, tests don't finish until
a manual keyboard interrupt is passed (`^C`). A context manager may be
needed to only run the server for a specified time.

```text
pytest
....................^C.^C.^C..........x.........                  [100%]
41 passed, 1 xfailed in 7.18s

pytest tests/test_start.py::TestStartServer::test_start_server_uvicorn \
  -vv
========================= test session starts =========================
platform darwin -- Python 3.8.5, pytest-6.0.1, py-1.9.0, pluggy-0.13.1
-- /Users/brendon/dev/inboard/.venv/bin/python3
cachedir: .pytest_cache
rootdir: /Users/brendon/dev/inboard, configfile: pyproject.toml
plugins: mock-3.3.1
collected 3 items

tests/test_start.py::TestStartServer::test_start_server_uvicorn[inboard
.app.base.main:app] ^CPASSED [ 33%]
tests/test_start.py::TestStartServer::test_start_server_uvicorn[inboard
.app.fastapibase.main:app] ^CPASSED [ 66%]
tests/test_start.py::TestStartServer::test_start_server_uvicorn[inboard
.app.starlettebase.main:app] ^CPASSED [100%]

========================== 3 passed in 9.96s ==========================
```
  • Loading branch information
br3ndonland committed Sep 6, 2020
1 parent 1c6a7ec commit 735618a
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 17 deletions.
10 changes: 3 additions & 7 deletions inboard/start.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,21 +93,18 @@ def run_pre_start_script(logger: Logger = logging.getLogger()) -> str:


def start_server(
process_manager: str,
app_module: str = str(os.getenv("APP_MODULE", "inboard.app.base.main:app")),
gunicorn_conf: str = str(
os.getenv("GUNICORN_CONF", "/app/inboard/gunicorn_conf.py")
),
logger: Logger = logging.getLogger(),
logging_conf_dict: Dict[str, Any] = None,
process_manager: str = str(os.getenv("PROCESS_MANAGER", "gunicorn")),
with_reload: bool = bool(os.getenv("WITH_RELOAD", False)),
worker_class: str = str(os.getenv("WORKER_CLASS", "uvicorn.workers.UvicornWorker")),
) -> None:
"""Start the Uvicorn or Gunicorn server."""
try:
if process_manager == "gunicorn":
logger.debug("Running Uvicorn with Gunicorn.")
gunicorn_conf_path = Path(gunicorn_conf)
gunicorn_conf_path = set_conf_path("gunicorn")
subprocess.run(
["gunicorn", "-k", worker_class, "-c", gunicorn_conf_path, app_module]
)
Expand All @@ -131,12 +128,11 @@ def start_server(
if __name__ == "__main__":
logger = logging.getLogger()
logging_conf_dict = configure_logging(logger=logger)
gunicorn_conf_path = set_conf_path("gunicorn")
app_module = set_app_module(logger=logger)
run_pre_start_script(logger=logger)
start_server(
str(os.getenv("PROCESS_MANAGER", "gunicorn")),
app_module=app_module,
gunicorn_conf=gunicorn_conf_path,
logger=logger,
logging_conf_dict=logging_conf_dict,
)
23 changes: 13 additions & 10 deletions tests/test_start.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,6 @@ class TestStartServer:
def test_start_server_uvicorn(
self,
app_module: str,
gunicorn_conf_path: Path,
logging_conf_dict: Dict[str, Any],
mock_logger: logging.Logger,
mocker: MockerFixture,
Expand All @@ -340,14 +339,17 @@ def test_start_server_uvicorn(
monkeypatch.setenv("LOG_FORMAT", "uvicorn")
monkeypatch.setenv("LOG_LEVEL", "debug")
monkeypatch.setenv("PROCESS_MANAGER", "uvicorn")
assert os.getenv("LOG_FORMAT") == "uvicorn"
assert os.getenv("LOG_LEVEL") == "debug"
assert os.getenv("PROCESS_MANAGER") == "uvicorn"
# TODO: need to send interrupt to stop server. Try with a context manager?
start.start_server(
str(os.getenv("PROCESS_MANAGER")),
app_module=app_module,
gunicorn_conf=str(gunicorn_conf_path),
logger=mock_logger,
logging_conf_dict=logging_conf_dict,
with_reload=False,
)
mock_logger.debug.assert_called_once_with("Running Uvicorn with Gunicorn.") # type: ignore[attr-defined] # noqa: E501
mock_logger.debug.assert_called_once_with("Running Uvicorn without Gunicorn.") # type: ignore[attr-defined] # noqa: E501

@pytest.mark.parametrize(
"app_module",
Expand All @@ -367,20 +369,23 @@ def test_start_server_uvicorn_gunicorn(
monkeypatch: MonkeyPatch,
) -> None:
"""Test `start.start_server` with Uvicorn managed by Gunicorn."""
assert os.getenv("GUNICORN_CONF", str(gunicorn_conf_path))
monkeypatch.setenv("LOG_FORMAT", "gunicorn")
monkeypatch.setenv("LOG_LEVEL", "debug")
monkeypatch.setenv("PROCESS_MANAGER", "gunicorn")
assert os.getenv("LOG_FORMAT") == "gunicorn"
assert os.getenv("LOG_LEVEL") == "debug"
assert os.getenv("PROCESS_MANAGER") == "gunicorn"
start.start_server(
str(os.getenv("PROCESS_MANAGER")),
app_module=app_module,
gunicorn_conf=str(gunicorn_conf_path),
logger=mock_logger,
logging_conf_dict=logging_conf_dict,
)
mock_logger.debug.assert_called_once_with("Running Uvicorn with Gunicorn.") # type: ignore[attr-defined] # noqa: E501

def test_start_server_uvicorn_incorrect_module(
self,
gunicorn_conf_path: Path,
logging_conf_dict: Dict[str, Any],
mock_logger: logging.Logger,
mocker: MockerFixture,
Expand All @@ -391,11 +396,10 @@ def test_start_server_uvicorn_incorrect_module(
monkeypatch.setenv("LOG_LEVEL", "debug")
monkeypatch.setenv("WITH_RELOAD", "false")
start.start_server(
"uvicorn",
app_module="incorrect.base.main:app",
gunicorn_conf=str(gunicorn_conf_path),
logger=mock_logger,
logging_conf_dict=logging_conf_dict,
process_manager="uvicorn",
)
logger_error_msg = "Error when starting server with start script:"
module_error_msg = "No module named incorrect.base.main:app"
Expand Down Expand Up @@ -428,11 +432,10 @@ def test_start_server_uvicorn_incorrect_process_manager(
monkeypatch.setenv("LOG_LEVEL", "debug")
monkeypatch.setenv("WITH_RELOAD", "false")
start.start_server(
"incorrect",
app_module=app_module,
gunicorn_conf=str(gunicorn_conf_path),
logger=mock_logger,
logging_conf_dict=logging_conf_dict,
process_manager="incorrect",
)
logger_error_msg = "Error when starting server with start script:"
process_error_msg = (
Expand Down

0 comments on commit 735618a

Please sign in to comment.