# Commenting out this since it is not in my env
# from tortoise.contrib.sanic import register_tortoise
import argparse
import os
from pathlib import Path
import toml
from sanic import Sanic
from sanic.log import logger
from sanic.utils import str_to_bool
# Commenting out this since it is not in my env
# from .api.user import User
# from .api.location import Location, AdminLocation
# from .api.checkin import CheckIn
# from .api.score import Score
# from .api.settings import Settings
# from .api.qr import QR
# from .api.auth import auth
# from . import config
# from .cors import add_cors_headers
# from .options import setup_options
# Moving this into the factory
# app = Sanic("Hiking-Game-Backend")
def _shutdown(code=0):
logger.info("Shutdown server")
exit(code)
def _load_config(path: Path):
# Changes made for POC
# Commenting out since I don't have these objects
# # Init config file if required
# if not os.path.isfile(path):
# config.init(path)
# logger.info(f"Initialized configuration file {path}.")
# logger.info("Please configure and restart the server.")
# _shutdown(0)
# # Load configuration
# try:
# cfg = toml.load(path)
# except Exception:
# logger.exception("Failed to load configuration. Check configuration"
# f"file {path}")
# _shutdown(1)
logger.info(f"Successfully loaded configuration from {path}.")
# return cfg
return {
"general": {
"dev": True,
},
"database": {
"host": "",
"name": "",
"port": "",
},
}
def _hydrate(args):
parser = argparse.ArgumentParser()
parser.add_argument(
"-m",
"--migrate",
# Using `str_to_bool` instead of action=store_true
type=str_to_bool,
default=False,
)
parser.add_argument(
"-c",
"--config",
type=str,
# default=config.PATH,
default=Path(),
)
known, _ = parser.parse_known_args(
args=[f"--{k}={v}" for k, v in args._get_kwargs()]
)
for key, value in known._get_kwargs():
setattr(args, key, value)
def main(args):
# This line is newly moved from global
app = Sanic("Hiking-Game-Backend")
# Instead of using argparse, we can just accept arbitrary arguments
# passed to the CLI. The only downside here, is that it does not accept
# things like store_true. So, we will fake it with _hydrate
_hydrate(args)
cfg = _load_config(args.config)
# Begin with server startup sequence
logger.info("Starting server...")
# Connect to database
logger.info(
f"Connect to database {cfg['database']['name']}"
+ f"({cfg['database']['host']}:{cfg['database']['port']})"
)
if args.migrate:
logger.info("Database will be initialized after connecting to it.")
# Removing this since it is not in my env
# db = cfg["database"]
# register_tortoise(
# app,
# db_url=f"postgres://{db['user']}:{db['password']}@{db['host']}:" +
# f"{db['port']}/{db['name']}",
# modules={"models": ["backend.database"]},
# generate_schemas=args.migrate
# )
logger.info("Successfully connected to database.")
# Commenting out your routes and replacing with one generic for POC
# app.blueprint(auth)
# app.add_route(User.as_view(), "/api/user/<user_id>/")
# app.add_route(Location.as_view(), "/api/location/")
# app.add_route(AdminLocation.as_view(), "/api/adminlocation/")
# app.add_route(CheckIn.as_view(), "/api/checkin/<uuid>")
# app.add_route(Score.as_view(), "/api/score/")
# app.add_route(Settings.as_view(), "/api/settings/")
# app.add_route(QR.as_view(), "/api/qr/")
app.get("/")(lambda _: ...)
# Make config fields accessible from all endpoints
app.config.cfg = cfg
dev = cfg["general"]["dev"]
# Commenting out since I don't have these funcs
if dev:
logger.info("Adding CORS headers.")
# # Add OPTIONS handlers to any route that is missing it
# app.register_listener(setup_options, "before_server_start")
# # Fill in CORS headers
# app.register_middleware(add_cors_headers, "response")
# REMOVE THIS
# Instead, you will add them as CLI arguments
# app.run(
# host=cfg["web"]["host"],
# port=cfg["web"]["port"],
# dev=True,
# access_log=True,
# )
return app
So, I messed around with it a bit. To make it work I moved app
instantiation into the factory, removed app.run
and then returned app
. That is essentially what you will need.
The bigger change though is with your use of argparse
. Sanic CLI will accept arbitrary arguments passed to it. But, unfortunately it does not give you the complete power of argparse
to declare their types. I was trying to take advantage of that, not sure if this solution meets your needs or not.
You could run something like this:
python -m sanic backend.__main__:main \
--factory \
--config=/path/to/somewhere \
--migrate=y \
--port=9876 \
--dev