blob: 5886caa38889affb31d15b65795c3e20b2e6963c [file]
#!/usr/bin/env -S uv run --script
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import sys
import logging
import pathlib
_LOGGER = logging.getLogger(__name__)
DATE_FORMAT = '%m/%d %H:%M'
THIS_DIR = pathlib.Path(__file__).resolve().parent
CERTS_DIR = THIS_DIR / 'certs'
def create_app():
"Create the asfquart app and its endpoints."
### is this really needed right now?
# Avoid OIDC
import asfquart.generics
asfquart.generics.OAUTH_URL_INIT = (
'https://oauth.apache.org/auth?state=%s&redirect_uri=%s'
)
asfquart.generics.OAUTH_URL_CALLBACK = 'https://oauth.apache.org/token?code=%s'
app = asfquart.construct('steve', app_dir=THIS_DIR, static_folder=None)
# Now that we have an APP, import modules that will add page
# and API endpoints into the APP.
import pages # noqa: F401
import api # noqa: F401
return app # also available as asfquart.APP now
def run_standalone():
"Run as a standalone server."
logging.basicConfig(
level=logging.DEBUG,
style='{',
format='[{asctime}|{levelname}|{name}] {message}',
datefmt=DATE_FORMAT,
)
# Switch some loggers to INFO, rather than DEBUG
logging.getLogger('selector_events').setLevel(logging.INFO)
logging.getLogger('hpack').setLevel(logging.INFO)
logging.getLogger('sslproto').setLevel(logging.INFO)
### above is good, but leaks stuff. This quiets things. too much?
### other way to approach: what in asyncio do we need to observe?
logging.getLogger('asyncio').setLevel(logging.INFO)
_LOGGER.info(' ** Run-mode: Standalone')
# Set up the STeVe app, then we'll start it up.
app = create_app()
# Note: "pages" imports "steve.election". Pull that package into
# our local namespace.
steve = sys.modules['steve']
# There are other things to watch, and cause a reload.
extra_files = {
steve.election.QUERIES,
}
kwargs = {}
if app.cfg.server.certfile:
kwargs['certfile'] = CERTS_DIR / app.cfg.server.certfile
kwargs['keyfile'] = CERTS_DIR / app.cfg.server.keyfile
extra_files.update((kwargs['certfile'], kwargs['keyfile']))
# Spool up the app!
app.runx(port=app.cfg.server.port, extra_files=extra_files, **kwargs)
# print('LOGGERS:', sorted(_LOGGER.manager.loggerDict.keys()))
# print(_LOGGER.manager.loggerDict['sslproto'])
def run_asgi():
"Run as an ASGI process; eg. Hypercorn"
# NOTE: no-op if Hypercorn has set up the root logger.
logging.basicConfig(
level=logging.DEBUG,
style='{',
format='[{asctime}|{levelname}|{name}] {message}',
datefmt=DATE_FORMAT,
)
# Make some loggers explicitly INFO, rather than default to DEBUG.
logging.getLogger('watchfiles.main').setLevel(logging.INFO)
# Configure our app's loggers (rather than root logger default).
_LOGGER.setLevel(logging.DEBUG)
# Okay. Time to be a Hypercorn-based ASGI app.
_LOGGER.info(' ** Run-mode: ASGI')
global steve_app
steve_app = create_app()
if __name__ == '__main__':
# $ ./main.py
run_standalone()
else:
# Using Hypercorn:
#
# $ uv run python -m hypercorn main:steve_app
#
# NOTE: without our extended shutdown_trigger, we cannot reload
# or restart on .py changes, extra_files changes, or respond to
# SIGUSR2 to restart. Hypercorn will respond to SIGTERM/SIGINT
# and shutdown.
run_asgi()