Integrate automatic redis startup with the project

This commit is contained in:
Antoine Bartuccio 2025-02-17 15:58:53 +01:00
parent 6841d96455
commit ba6e2a6402
13 changed files with 104 additions and 28 deletions

View File

@ -8,4 +8,10 @@ SECRET_KEY=(4sjxvhz@m5$0a$j0_pqicnc$s!vbve)z+&++m%g%bjhlz4+g2
DATABASE_URL=sqlite:///db.sqlite3
#DATABASE_URL=postgres://user:password@127.0.0.1:5432/sith
CACHE_URL=redis://127.0.0.1:6379/0
REDIS_PORT=7963
CACHE_URL=redis://127.0.0.1:${REDIS_PORT}/0
# Used to select which other services to run alongside
# runserver and pytest
PROCFILE_RUNSERVER=Procfile.dev
PROCFILE_PYTEST=Procfile.pytest

6
.gitignore vendored
View File

@ -22,3 +22,9 @@ node_modules/
# compiled documentation
site/
.env
### Redis ###
# Ignore redis binary dump (dump.rdb) files
*.rdb

View File

@ -1 +0,0 @@
bundler: npm run $BUNDLER_MODE

2
Procfile.dev Normal file
View File

@ -0,0 +1,2 @@
bundler: npm run serve
redis: redis-server --port $REDIS_PORT

1
Procfile.pytest Normal file
View File

@ -0,0 +1 @@
redis: redis-server --port $REDIS_PORT

View File

@ -13,13 +13,25 @@
# OR WITHIN THE LOCAL FILE "LICENSE"
#
#
import os
import sys
from django.utils.autoreload import DJANGO_AUTORELOAD_ENV
from processes.composer import start_composer, stop_composer
from sith.environ import env
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sith.settings")
from django.core.management import execute_from_command_line
if (
os.environ.get(DJANGO_AUTORELOAD_ENV) is None
and (procfile := env.str("PROCFILE_RUNSERVER", None)) is not None
):
start_composer(procfile)
execute_from_command_line(sys.argv)
stop_composer()

26
processes/composer.py Normal file
View File

@ -0,0 +1,26 @@
import os
import signal
import subprocess
import sys
import psutil
COMPOSER_PID = "COMPOSER_PID"
def start_composer(procfile: str):
"""Starts the composer and stores the PID as an environment variable
This allows for running smoothly with the django reloader
"""
process = subprocess.Popen(
[sys.executable, "-m", "honcho", "-f", procfile, "start"],
)
os.environ[COMPOSER_PID] = str(process.pid)
def stop_composer():
"""Stops the composer if it was started before"""
if (pid := os.environ.get(COMPOSER_PID, None)) is not None:
process = psutil.Process(int(pid))
process.send_signal(signal.SIGTERM)
process.wait()

View File

@ -48,6 +48,7 @@ dependencies = [
"environs[django]<15.0.0,>=14.1.0",
"requests>=2.32.3",
"honcho>=2.0.0",
"psutil>=7.0.0",
]
[project.urls]
@ -125,6 +126,9 @@ ignore = [
[tool.ruff.lint.pydocstyle]
convention = "google"
[project.entry-points.pytest11]
sith = "sith.pytest"
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "sith.settings"
python_files = ["tests.py", "test_*.py", "*_tests.py"]

4
sith/environ.py Normal file
View File

@ -0,0 +1,4 @@
from environs import Env
env = Env()
_ = env.read_env()

23
sith/pytest.py Normal file
View File

@ -0,0 +1,23 @@
import pytest
from processes.composer import start_composer, stop_composer
from .environ import env
# pytest-django uses the load_initial_conftest hook
# it's the first hook loaded by pytest and can only
# be defined in a proper pytest plugin
# To use the composer before pytest-django loads
# we need to define this hook and thus create a proper
# pytest plugin. We can't just use conftest.py
@pytest.hookimpl(tryfirst=True)
def pytest_load_initial_conftests(early_config, parser, args):
"""Hook that loads the composer before the pytest-django plugin"""
if (procfile := env.str("PROCFILE_PYTEST", None)) is not None:
start_composer(procfile)
def pytest_unconfigure(config):
stop_composer()

View File

@ -42,14 +42,11 @@ from pathlib import Path
import sentry_sdk
from dateutil.relativedelta import relativedelta
from django.utils.translation import gettext_lazy as _
from environs import Env
from sentry_sdk.integrations.django import DjangoIntegration
from .environ import env
from .honeypot import custom_honeypot_error
env = Env()
env.read_env()
BASE_DIR = Path(__file__).parent.parent.resolve()
# Quick-start development settings - unsuitable for production

View File

@ -1,13 +1,6 @@
import logging
import os
import subprocess
import sys
from django.conf import settings
from django.contrib.staticfiles.management.commands.runserver import (
Command as Runserver,
)
from django.utils.autoreload import DJANGO_AUTORELOAD_ENV
from staticfiles.processors import OpenApi
@ -16,19 +9,5 @@ class Command(Runserver):
"""Light wrapper around default runserver that integrates javascirpt auto bundling."""
def run(self, **options):
# OpenApi generation needs to be before the bundler
OpenApi.compile()
# Run all other web processes but only if debug mode is enabled
# Also protects from re-launching the server if django reloads it
if os.environ.get(DJANGO_AUTORELOAD_ENV) is None:
logging.getLogger("django").info("Running complementary processes")
with subprocess.Popen(
[sys.executable, "-m", "honcho", "start"],
env={
**os.environ,
**{"BUNDLER_MODE": "serve" if settings.DEBUG else "compile"},
},
):
super().run(**options)
return
super().run(**options)

17
uv.lock generated
View File

@ -1104,6 +1104,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a9/6a/fd08d94654f7e67c52ca30523a178b3f8ccc4237fce4be90d39c938a831a/prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e", size = 386595 },
]
[[package]]
name = "psutil"
version = "7.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 },
{ url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 },
{ url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 },
{ url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 },
{ url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 },
{ url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 },
{ url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 },
]
[[package]]
name = "psycopg"
version = "3.2.3"
@ -1520,6 +1535,7 @@ dependencies = [
{ name = "mistune" },
{ name = "phonenumbers" },
{ name = "pillow" },
{ name = "psutil" },
{ name = "pydantic-extra-types" },
{ name = "python-dateutil" },
{ name = "redis", extra = ["hiredis"] },
@ -1581,6 +1597,7 @@ requires-dist = [
{ name = "mistune", specifier = ">=3.0.2,<4.0.0" },
{ name = "phonenumbers", specifier = ">=8.13.52,<9.0.0" },
{ name = "pillow", specifier = ">=11.0.0,<12.0.0" },
{ name = "psutil", specifier = ">=7.0.0" },
{ name = "pydantic-extra-types", specifier = ">=2.10.1,<3.0.0" },
{ name = "python-dateutil", specifier = ">=2.9.0.post0,<3.0.0.0" },
{ name = "redis", extras = ["hiredis"], specifier = ">=5.2.0,<6.0.0" },