# 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