Skip to content

Commit

Permalink
Add a standalone server application
Browse files Browse the repository at this point in the history
Until now I have been running this with Flask in development mode. But
at some point, I would want to deploy it. How do you deploy a Flask
application? Gunicorn or Waitress (behind Nginx) seem like the simplest
options, and Waitress has no dependencies except for the stdlib, so
let's go with it for now.

It has a nice way to run things, I can just add a main in my app.py that
starts Waitress.

Now the only hairy problem to solve is configuration. 12-factor
environment variables look nice at first, but with the Procfile, the Nix
shell with PG* variables, and sometimes people throwing in .env files
too ... in my experience, it ends up being a mess after all. Instead I
think I'll add a simple toml file, and add a default configuration in
the repository that works out of the box for a development checkout.
  • Loading branch information
ruuda committed Mar 2, 2023
1 parent b74df04 commit b1c7485
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 3 deletions.
3 changes: 3 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
graft hanson/static
graft hanson/templates
global-exclude *.pyc
37 changes: 37 additions & 0 deletions app.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#!/usr/bin/env python3

# Hanson -- Self-hosted prediction market app
# Copyright 2022 Ruud van Asseldonk
#
Expand Down Expand Up @@ -29,3 +31,38 @@
@app.errorhandler(NotLoggedInError) # type: ignore
def handle_not_logged_in(_: NotLoggedInError) -> Response:
return Response.redirect_see_other("/login")


def main() -> None:
"""
This app.py is designed to run under Flask in development mode, you can run
with "python -m flask run". In that case, this main function is not called.
For a production deployment, we use Waitress as the WSGI server. Then we can
start from app.py as the entry point.
"""
import os
import waitress

# TODO: Maybe put this in a config file after all?
# The current values are misleading because we don't use those!
host = os.getenv("HOST", "0.0.0.0")
port = int(os.getenv("PORT", "5000"))
pg_host = os.getenv("PGHOST", f"{os.getcwd()}/run/db_dev")
pg_database = os.getenv("PGDATABASE", "hanson")
pg_user = os.getenv("PGUSER", "hanson_app")
pg_password = os.getenv("PGPASSWORD", "hanson_app")

print("Configuration:")
print(f" BIND={host}")
print(f" PORT={port}")
print(f" PGDATABASE={pg_database}")
print(f" PGUSER={pg_user}")
print(f" PGPASSWORD is not printed here")
print(f" PGHOST={pg_host}")
print("Starting server ...")
waitress.serve(app, host=host, port=port)


if __name__ == "__main__":
main()
26 changes: 23 additions & 3 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
ps.flask
ps.jinja2
ps.psycopg2
ps.waitress
];
developmentDependencies = ps: [
# Mypy goes with the pythonPackages, we don't use the top-level pkgs.mypy,
Expand All @@ -24,9 +25,26 @@
ps.mypy
ps.pytest
];
python = pkgs.python3.withPackages (ps:

python = pkgs.python3.override {
packageOverrides = self: super: {
hanson = self.buildPythonPackage rec {
pname = "hanson";
version = "0.0.0";
src = ./.;
propagatedBuildInputs = runtimeDependencies self;
};
};
};

# For development, we want a Python that has all our dependent packages,
# but not Hanson itself as a package.
pythonDev = pkgs.python3.withPackages (ps:
(runtimeDependencies ps) ++ (developmentDependencies ps)
);

hanson = python.pkgs.toPythonApplication python.pkgs.hanson;

in
{
devShells = {
Expand All @@ -38,7 +56,7 @@
pkgs.overmind
pkgs.postgresql_14
pkgs.mkdocs
python
pythonDev
];

LOCALE_ARCHIVE = "${pkgs.glibcLocales}/lib/locale/locale-archive";
Expand All @@ -50,7 +68,9 @@
};
};

packages = {};
packages = {
default = hanson;
};
}
);
}
19 changes: 19 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from setuptools import find_packages, setup

setup(
name="hanson",
version="0.0.0",
packages=find_packages(),
include_package_data=True,
install_requires=[
"click",
"flask",
"jinja2",
"psycopg2",
"waitress",
],
scripts=[
"app.py",
"cli.py",
],
)

0 comments on commit b1c7485

Please sign in to comment.