"""Nox sessions for backend tasks. Run with `nox -s ` from the backend directory. """ import os import shutil import sys from pathlib import Path import nox from nox_poetry import Session from nox_poetry import session package = "backend" python_versions = ["3.13"] nox.needs_version = ">= 2025.5.1" nox.options.sessions = ( "precommit", "safety", "mypy", "tests", "typeguard", "xdoctest", "docs-build", ) @session(name="pre-commit", python=python_versions[0]) def precommit(session: Session) -> None: """Run pre-commit checks by delegating to the root noxfile. Args: session: The Nox session object. """ session.run("nox", "-f", "../noxfile.py", "-s", "pre-commit", external=True) package = "backend" @session(python=python_versions[0]) def safety(session: Session) -> None: """Scan dependencies for insecure packages. Args: session: The Nox session object. """ requirements = session.poetry.export_requirements() session.install("safety") session.run("safety", "scan", "--full-report", f"--file={requirements}") @session(python=python_versions) def mypy(session: Session) -> None: """Type-check using mypy. Args: session: The Nox session object. """ args = session.posargs or ["src", "tests", "docs/conf.py"] session.install(".") session.install("mypy", "pytest", "pytest-asyncio") session.run("mypy", *args) if not session.posargs: session.run("mypy", f"--python-executable={sys.executable}", "noxfile.py") @session(python=python_versions) def tests(session: Session) -> None: """Run the test suite. Args: session: The Nox session object. """ session.install(".") session.install( "coverage[toml]", "pytest", "pygments", "pytest-asyncio", "python-dotenv", "pytest-dotenv", ) # Load the .env file (change path if needed) # pytest-dotenv will automatically load the .env file before tests try: session.run("coverage", "run", "--parallel", "-m", "pytest", *session.posargs) finally: if session.interactive: session.notify("coverage", posargs=[]) @session(python=python_versions[0]) def coverage(session: Session) -> None: """Produce the coverage report. Args: session: The Nox session object. """ args = session.posargs or ["report", "--fail-under=90"] session.install("coverage[toml]") if not session.posargs and any(Path().glob(".coverage.*")): session.run("coverage", "combine") session.run("coverage", *args) @session(python=python_versions[0]) def typeguard(session: Session) -> None: """Runtime type checking using Typeguard. Args: session: The Nox session object. """ session.install(".") session.install("pytest", "typeguard", "pygments", "pytest-asyncio") session.run("pytest", f"--typeguard-packages={package}", *session.posargs) @session(python=python_versions) def xdoctest(session: Session) -> None: """Run examples with xdoctest. Args: session: The Nox session object. """ if session.posargs: args = [package, *session.posargs] else: args = [f"--modname={package}", "--command=all"] if "FORCE_COLOR" in os.environ: args.append("--colored=1") session.install(".") session.install("xdoctest[colors]") session.run("python", "-m", "xdoctest", *args) @session(name="docs-build", python=python_versions[0]) def docs_build(session: Session) -> None: """Build the documentation. Args: session: The Nox session object. """ args = session.posargs or ["docs", "docs/_build"] if not session.posargs and "FORCE_COLOR" in os.environ: args.insert(0, "--color") session.install(".") session.install("sphinx", "sphinx-click", "furo", "myst-parser") build_dir = Path("docs", "_build") if build_dir.exists(): shutil.rmtree(build_dir) session.run("sphinx-build", *args) @session(python=python_versions[0]) def docs(session: Session) -> None: """Build and serve the documentation with live reloading on file changes. Args: session: The Nox session object. """ args = session.posargs or ["--open-browser", "docs", "docs/_build"] session.install(".") session.install("sphinx", "sphinx-autobuild", "sphinx-click", "furo", "myst-parser") build_dir = Path("docs", "_build") if build_dir.exists(): shutil.rmtree(build_dir) session.run("sphinx-autobuild", *args)