Some checks failed
Run linters on applied template / Python 3.13 lint and build (push) Failing after 39s
This is a FastAPI backend microservice template used with `copier` utility. Features of applied template are: - Configuration file processing logic - Metrics and tracing (both optional) configuration available - Debug endpoints - Database migration commands, prepared Alembic environment - Database usage example in ping_db endpoint - gitea sanity check pipeline
137 lines
3.7 KiB
Django/Jinja
137 lines
3.7 KiB
Django/Jinja
"""Executable application entrypoint is defined here."""
|
|
|
|
import os
|
|
import tempfile
|
|
import typing as tp
|
|
from pathlib import Path
|
|
|
|
import click
|
|
import uvicorn
|
|
from dotenv import load_dotenv
|
|
|
|
from .config import {{ProjectName}}Config, LoggingConfig
|
|
|
|
LogLevel = tp.Literal["TRACE", "DEBUG", "INFO", "WARNING", "ERROR"]
|
|
|
|
|
|
def _run_uvicorn(configuration: dict[str, tp.Any]) -> tp.NoReturn:
|
|
uvicorn.run(
|
|
"{{project_slug}}.fastapi_init:app",
|
|
**configuration,
|
|
)
|
|
|
|
|
|
@click.group()
|
|
def cli():
|
|
"{{project_name}} service executable script"
|
|
|
|
|
|
@cli.command("config-example")
|
|
@click.option(
|
|
"--config_path",
|
|
envvar="CONFIG_PATH",
|
|
default="config.yaml",
|
|
type=click.Path(dir_okay=False, path_type=Path),
|
|
show_default=True,
|
|
show_envvar=True,
|
|
help="Path to YAML configuration file",
|
|
)
|
|
def get_config_example(config_path: Path):
|
|
config = {{ProjectName}}Config.get_example()
|
|
config.dump(config_path)
|
|
|
|
|
|
@cli.command("launch")
|
|
@click.option(
|
|
"--port",
|
|
"-p",
|
|
envvar="PORT",
|
|
type=int,
|
|
show_envvar=True,
|
|
help="Service port number",
|
|
)
|
|
@click.option(
|
|
"--host",
|
|
envvar="HOST",
|
|
show_envvar=True,
|
|
help="Service HOST address",
|
|
)
|
|
@click.option(
|
|
"--logger_verbosity",
|
|
"-v",
|
|
type=click.Choice(("TRACE", "DEBUG", "INFO", "WARNING", "ERROR")),
|
|
envvar="LOGGER_VERBOSITY",
|
|
show_envvar=True,
|
|
help="Logger verbosity",
|
|
)
|
|
@click.option(
|
|
"--debug",
|
|
envvar="DEBUG",
|
|
is_flag=True,
|
|
help="Enable debug mode (auto-reload on change, traceback returned to user, etc.)",
|
|
)
|
|
@click.option(
|
|
"--config_path",
|
|
envvar="CONFIG_PATH",
|
|
default="config.yaml",
|
|
type=click.Path(exists=True, dir_okay=False, path_type=Path),
|
|
show_default=True,
|
|
show_envvar=True,
|
|
help="Path to YAML configuration file",
|
|
)
|
|
def launch(
|
|
port: int,
|
|
host: str,
|
|
logger_verbosity: LogLevel,
|
|
debug: bool,
|
|
config_path: Path,
|
|
):
|
|
"""
|
|
{{project_description}}
|
|
|
|
Performs configuration via config and command line + environment variables overrides.
|
|
"""
|
|
print(
|
|
"This is a simple method to run the API."
|
|
" You might want to use 'uvicorn {{project_slug}}.fastapi_init:app' instead"
|
|
)
|
|
|
|
config = {{ProjectName}}Config.load(config_path)
|
|
config.app.host = host or config.app.host
|
|
config.app.port = port or config.app.port
|
|
config.app.debug = debug or config.app.debug
|
|
config.logging = config.logging if logger_verbosity is None else LoggingConfig(level=logger_verbosity)
|
|
|
|
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
|
temp_yaml_config_path = temp_file.name
|
|
config.dump(temp_yaml_config_path)
|
|
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
|
temp_envfile_path = temp_file.name
|
|
with open(temp_envfile_path, "w", encoding="utf-8") as env_file:
|
|
env_file.write(f"CONFIG_PATH={temp_yaml_config_path}\n")
|
|
try:
|
|
uvicorn_config = {
|
|
"host": config.app.host,
|
|
"port": config.app.port,
|
|
"log_level": config.logging.level.lower(),
|
|
"env_file": temp_envfile_path,
|
|
}
|
|
if config.app.debug:
|
|
try:
|
|
_run_uvicorn(uvicorn_config | {"reload": True})
|
|
except: # pylint: disable=bare-except
|
|
print("Debug reload is not supported and will be disabled")
|
|
_run_uvicorn(uvicorn_config)
|
|
else:
|
|
_run_uvicorn(uvicorn_config)
|
|
finally:
|
|
if os.path.exists(temp_envfile_path):
|
|
os.remove(temp_envfile_path)
|
|
if os.path.exists(temp_yaml_config_path):
|
|
os.remove(temp_yaml_config_path)
|
|
|
|
|
|
if __name__ in ("__main__", "{{project_slug}}.__main__"):
|
|
load_dotenv(os.environ.get("ENVFILE", ".env"))
|
|
cli() # pylint: disable=no-value-for-parameter
|