Skip to content

Commit 390a159

Browse files
committed
Restructure prepare_server to work similar to make_app
Also, modularize, add comments, load config earlier.
1 parent 302584d commit 390a159

File tree

4 files changed

+52
-78
lines changed

4 files changed

+52
-78
lines changed

docker-compose.base.yml

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ services:
4444
SWDD_HASH_KEY: key
4545
MAIL_CONFIRM_URL: "http://localhost/sipa/register/confirm/{}"
4646
PASSWORD_RESET_URL: "http://localhost/sipa/reset-password/{}"
47+
# alternative: `scripts.server_run:prepare_server(echo=True)`
48+
FLASK_APP: scripts.server_run:prepare_server
4749
FLASK_ENV: development
4850
dev:
4951
extends: dev-base

docker-compose.dev.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ services:
2525
- app
2626
ports:
2727
- "5000:5000"
28-
command: ["http", "--debug"]
28+
command: ["shell", "flask", "run", "--debug", "--host=0.0.0.0"]
2929
healthcheck:
3030
test: curl --fail http://localhost:5000
3131
interval: 2s

scripts/server_run.py

+46-75
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,24 @@
33
# This file is part of the Pycroft project and licensed under the terms of
44
# the Apache License, Version 2.0. See the LICENSE file for details.
55

6-
import argparse
76
import logging
87
import os
98
import sys
109
import time
11-
from collections.abc import Callable
1210

1311
from babel.support import Translations
1412
from flask import g, request
1513
from flask.globals import request_ctx
14+
from sqlalchemy import create_engine
1615
from sqlalchemy.orm import scoped_session, sessionmaker
16+
from sqlalchemy.ext.declarative import DeferredReflection
1717
from werkzeug.middleware.profiler import ProfilerMiddleware
1818

1919
import pycroft
2020
import web
2121
from pycroft.helpers.i18n import set_translation_lookup, get_locale
2222
from pycroft.model.session import set_scoped_session
23-
from scripts.connection import try_create_connection, get_connection_string
23+
from scripts.connection import get_connection_string
2424
from scripts.schema import AlembicHelper, SchemaStrategist
2525
from web import make_app, PycroftFlask
2626

@@ -30,103 +30,74 @@
3030
)
3131

3232

33-
def prepare_server(args) -> tuple[PycroftFlask, Callable]:
34-
"""returns both the prepared app and a callback executing `app.run`"""
35-
if args.echo:
33+
def prepare_server(echo=False) -> PycroftFlask:
34+
if echo:
3635
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
3736
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
3837
logging.getLogger('sqlalchemy.pool').setLevel(logging.DEBUG)
39-
app = make_app(args.debug)
40-
4138
logging.getLogger('pycroft').addHandler(default_handler)
4239

43-
wait_for_db: bool = args.wait_for_database
44-
45-
connection_string = get_connection_string()
46-
connection, engine = try_create_connection(connection_string, wait_for_db, app.logger,
47-
reflections=False)
48-
49-
state = AlembicHelper(connection)
50-
if args.force_schema_create:
51-
strategy = SchemaStrategist(state).create_then_stamp
52-
else:
53-
strategy = SchemaStrategist(state).determine_schema_strategy()
54-
strategy()
55-
56-
@app.before_request
57-
def get_time():
58-
g.request_time = time.time()
59-
60-
@app.teardown_request
61-
def time_response(exception=None):
62-
request_time = g.pop('request_time', None)
63-
64-
if request_time:
65-
time_taken = time.time() - request_time
66-
if time_taken > 0.5:
67-
app.logger.warning(
68-
"Response took %s seconds for request %s",
69-
time_taken, request.full_path,
70-
)
71-
72-
connection, engine = try_create_connection(connection_string, wait_for_db, app.logger,
73-
args.profile)
40+
app = make_app()
41+
# TODO rename to `default_config.toml`
42+
app.config.from_pyfile("flask.cfg")
7443

44+
engine = create_engine(get_connection_string())
45+
with engine.connect() as connection:
46+
_ensure_schema_up_to_date(connection)
47+
_setup_simple_profiling(app)
48+
DeferredReflection.prepare(engine) # TODO for what is this used?
7549
set_scoped_session(
7650
scoped_session(
7751
sessionmaker(bind=engine),
7852
scopefunc=lambda: request_ctx._get_current_object(),
7953
)
8054
)
55+
_setup_translations()
56+
if app.config.get("PROFILE", False):
57+
app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[30])
58+
return app
8159

60+
61+
def _ensure_schema_up_to_date(connection):
62+
state = AlembicHelper(connection)
63+
strategy = SchemaStrategist(state).determine_schema_strategy()
64+
strategy()
65+
66+
67+
def _setup_translations():
8268
def lookup_translation():
8369
ctx = request_ctx
8470
if ctx is None:
8571
return None
86-
translations = getattr(ctx, 'pycroft_translations', None)
72+
translations = getattr(ctx, "pycroft_translations", None)
8773
if translations is None:
8874
translations = Translations()
8975
for module in (pycroft, web):
76+
# TODO this has a bug. The intention is to merge
77+
# `pycroft.translations` and `web.translations`,
78+
# but the `os.path.dirname(…)` result is nowhere used.
9079
os.path.dirname(module.__file__)
91-
dirname = os.path.join(ctx.app.root_path, 'translations')
80+
dirname = os.path.join(ctx.app.root_path, "translations")
9281
translations.merge(Translations.load(dirname, [get_locale()]))
9382
ctx.pycroft_translations = translations
9483
return translations
9584

9685
set_translation_lookup(lookup_translation)
97-
app.config.from_pyfile('flask.cfg')
98-
if args.profile:
99-
app.config['PROFILE'] = True
100-
app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[30])
101-
return app, lambda: app.run(
102-
debug=args.debug,
103-
port=args.port,
104-
host=args.host,
105-
threaded=True,
106-
exclude_patterns=["**/lib/python3*"],
107-
)
10886

10987

110-
def create_parser() -> argparse.ArgumentParser:
111-
parser = argparse.ArgumentParser(description="Pycroft launcher")
112-
parser.add_argument("--debug", action="store_true",
113-
help="run in debug mode")
114-
parser.add_argument("--echo", action="store_true",
115-
help="log sqlalchemy actions")
116-
parser.add_argument("--profile", action="store_true",
117-
help="profile and log sql queries")
118-
parser.add_argument("--exposed", action="store_const", const='0.0.0.0',
119-
dest='host', help="expose server on network")
120-
parser.add_argument("-p", "--port", action="store",
121-
help="port to run Pycroft on", type=int, default=5000)
122-
parser.add_argument("-w", "--wait-for-database", type=int, default=30,
123-
help="Maximum time to wait for database to become "
124-
"available. Use 0 to wait forever.")
125-
parser.add_argument("--force-schema-create", action='store_true')
126-
return parser
127-
128-
129-
app, run_callable = prepare_server(create_parser().parse_args())
130-
131-
if __name__ == "__main__":
132-
run_callable()
88+
def _setup_simple_profiling(app):
89+
@app.before_request
90+
def get_time():
91+
g.request_time = time.time()
92+
93+
@app.teardown_request
94+
def time_response(exception=None):
95+
request_time = g.pop('request_time', None)
96+
97+
if request_time:
98+
time_taken = time.time() - request_time
99+
if time_taken > 0.5:
100+
app.logger.warning(
101+
"Response took %s seconds for request %s",
102+
time_taken, request.full_path,
103+
)

web/app.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,9 @@ def maybe_add_config_from_env(self, keys: t.Iterable[str]) -> None:
8383
self.logger.debug("Config key %s successfuly read from environment", key)
8484

8585

86-
def make_app(debug: bool = False, hades_logs: bool = True) -> PycroftFlask:
86+
def make_app(hades_logs: bool = True) -> PycroftFlask:
8787
"""Create and configure the main? Flask app object"""
8888
app = PycroftFlask(__name__)
89-
app.debug = debug
9089

9190
# initialization code
9291
login_manager.init_app(app)
@@ -104,6 +103,8 @@ def make_app(debug: bool = False, hades_logs: bool = True) -> PycroftFlask:
104103
template_filters.register_filters(app)
105104
template_tests.register_checks(app)
106105

106+
# NOTE: this is _only_ used for its datetime formatting capabilities,
107+
# for translations we have our own babel interface in `pycroft.helpers.i18n.babel`!
107108
Babel(app)
108109
if hades_logs:
109110
try:

0 commit comments

Comments
 (0)