Coverage for atlas_ecommerce/app.py: 80%
15 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-25 16:36 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-25 16:36 +0000
1"""Service entrypoint.
3Run as ``python -m atlas_ecommerce.app [environment]``, matching auth-proxy. The
4positional argument sets ``ENVIRONMENT``, which in turn selects the ``.env`` file
5and the GCP secret set. An ENVIRONMENT already in the process environment wins,
6so container deployments do not need the argument.
8Secrets are deliberately not touched here. Loading them would need a throwaway
9event loop, leaving the cache bound to a loop uvicorn then replaces; the lifespan
10loads them once, in the loop that actually serves requests, and a failure there
11still aborts startup.
12"""
14from argparse import ArgumentParser
15import logging
16from os import environ
17from sys import stdout
18from typing import Final
20from uvicorn import run as uvicorn_run
22from atlas_ecommerce.config import get_logfire_token, get_settings
23from atlas_ecommerce.main import app, configure_logfire
25ENVIRONMENTS: Final[tuple[str, ...]] = ("dev", "stg", "prod", "test")
26DEFAULT_PORT: Final[int] = 8005
28logger = logging.getLogger(__name__)
31def parse_args() -> str:
32 parser = ArgumentParser(description="Run the Atlas ecommerce service.")
33 parser.add_argument(
34 "environment",
35 type=str,
36 nargs="?",
37 default="dev",
38 choices=ENVIRONMENTS,
39 help="Environment to run the app in (default: dev)",
40 )
41 return parser.parse_args().environment
44def main() -> None: # pragma: no cover
45 logging.basicConfig(level=logging.INFO, format="INFO: %(message)s", stream=stdout)
47 environment = parse_args()
48 if not environ.get("ENVIRONMENT"):
49 environ["ENVIRONMENT"] = environment
51 # Settings may have been cached during import, before ENVIRONMENT was set.
52 get_settings.cache_clear()
53 settings = get_settings()
55 logger.info("Starting Atlas ecommerce in %s", settings.environment.upper())
57 # Before uvicorn, never from the lifespan: instrumenting a started app is a
58 # silent no-op. 2026-08-31, after prod ran a day with zero spans.
59 configure_logfire(app, get_logfire_token(), settings.environment)
61 uvicorn_run(
62 app,
63 host="0.0.0.0",
64 port=int(environ.get("PORT", DEFAULT_PORT)),
65 log_level="info",
66 timeout_keep_alive=60,
67 )
70if __name__ == "__main__": # pragma: no cover
71 main()