simple-module-background-tasks 0.0.8__tar.gz → 0.0.10__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/PKG-INFO +5 -5
  2. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/module.py +6 -0
  3. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/sync_db.py +31 -3
  4. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/pyproject.toml +5 -5
  5. simple_module_background_tasks-0.0.10/tests/test_sync_db.py +60 -0
  6. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/.gitignore +0 -0
  7. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/LICENSE +0 -0
  8. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/README.md +0 -0
  9. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/__init__.py +0 -0
  10. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/_signal_support.py +0 -0
  11. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/celery_app.py +0 -0
  12. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/constants.py +0 -0
  13. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/contracts/__init__.py +0 -0
  14. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/contracts/events.py +0 -0
  15. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/contracts/schemas.py +0 -0
  16. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/deps.py +0 -0
  17. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/endpoints/__init__.py +0 -0
  18. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/endpoints/api_admin.py +0 -0
  19. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/endpoints/views.py +0 -0
  20. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/locales/en.json +0 -0
  21. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/models.py +0 -0
  22. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/pages/Detail.tsx +0 -0
  23. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/pages/Index.tsx +0 -0
  24. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/pages/Workers.tsx +0 -0
  25. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/pages/components/ExecutionRow.tsx +0 -0
  26. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/pages/components/RetryConfirmDialog.tsx +0 -0
  27. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/pages/constants.ts +0 -0
  28. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/pages/retry.ts +0 -0
  29. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/py.typed +0 -0
  30. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/service.py +0 -0
  31. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/services.py +0 -0
  32. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/settings.py +0 -0
  33. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/signals.py +0 -0
  34. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/tasks.py +0 -0
  35. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/background_tasks/worker_inspector.py +0 -0
  36. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/package.json +0 -0
  37. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/tests/conftest.py +0 -0
  38. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/tests/test_admin_api.py +0 -0
  39. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/tests/test_bg_service.py +0 -0
  40. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/tests/test_signals.py +0 -0
  41. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/tests/test_worker_inspector.py +0 -0
  42. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/tests/test_workers_endpoints.py +0 -0
  43. {simple_module_background_tasks-0.0.8 → simple_module_background_tasks-0.0.10}/tsconfig.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simple_module_background_tasks
3
- Version: 0.0.8
3
+ Version: 0.0.10
4
4
  Summary: Celery + Redis task queue with admin UI for monitoring and retrying failed/stuck tasks
5
5
  Project-URL: Homepage, https://github.com/antosubash/simple_module_python
6
6
  Project-URL: Repository, https://github.com/antosubash/simple_module_python
@@ -23,10 +23,10 @@ Classifier: Typing :: Typed
23
23
  Requires-Python: >=3.12
24
24
  Requires-Dist: celery[redis]>=5.4
25
25
  Requires-Dist: redis>=5
26
- Requires-Dist: simple-module-core==0.0.8
27
- Requires-Dist: simple-module-db==0.0.8
28
- Requires-Dist: simple-module-hosting==0.0.8
29
- Requires-Dist: simple-module-settings==0.0.8
26
+ Requires-Dist: simple-module-core==0.0.10
27
+ Requires-Dist: simple-module-db==0.0.10
28
+ Requires-Dist: simple-module-hosting==0.0.10
29
+ Requires-Dist: simple-module-settings==0.0.10
30
30
  Description-Content-Type: text/markdown
31
31
 
32
32
  # simple_module_background_tasks
@@ -96,8 +96,14 @@ class BackgroundTasksModule(ModuleBase):
96
96
 
97
97
  from background_tasks.celery_app import build_celery
98
98
  from background_tasks.signals import bind_event_bus
99
+ from background_tasks.sync_db import set_database_url
99
100
 
100
101
  services = app.state.background_tasks
102
+ # Pin the sync engine to the same URL the host's async settings
103
+ # resolved — pydantic-settings reads ``.env`` but never propagates
104
+ # to ``os.environ``, so signals would otherwise fall back to the
105
+ # SQLite default and silently drop ``TaskExecution`` rows.
106
+ set_database_url(app.state.sm.settings.database_url)
101
107
  # build_celery imports `signals` for side effects and runs
102
108
  # `autodiscover_tasks` across every installed module.
103
109
  services.celery = build_celery(services.settings)
@@ -26,6 +26,7 @@ logger = logging.getLogger(__name__)
26
26
 
27
27
  _engine: Engine | None = None
28
28
  _session_factory: sessionmaker[Session] | None = None
29
+ _url_override: str | None = None
29
30
 
30
31
 
31
32
  def _sync_url(async_url: str) -> str:
@@ -37,9 +38,35 @@ def _sync_url(async_url: str) -> str:
37
38
  return async_url.replace("+aiosqlite", "").replace("+asyncpg", "+psycopg2")
38
39
 
39
40
 
41
+ def set_database_url(url: str | None) -> None:
42
+ """Pin the URL used to build the sync engine.
43
+
44
+ The web process loads ``.env`` via pydantic-settings, but those values
45
+ never land in ``os.environ`` — so reading ``SM_DATABASE_URL`` directly
46
+ from the env can silently drop us back to the SQLite default while the
47
+ rest of the app uses Postgres. ``BackgroundTasksModule.on_startup``
48
+ calls this with the resolved ``settings.database_url`` so signals use
49
+ the same DB the app is on. Pass ``None`` to clear the override (used in
50
+ tests + on shutdown).
51
+ """
52
+ global _url_override, _engine, _session_factory
53
+ if _url_override == url:
54
+ return
55
+ _url_override = url
56
+ if _engine is not None:
57
+ _engine.dispose()
58
+ _engine = None
59
+ _session_factory = None
60
+
61
+
62
+ def _resolve_url() -> str:
63
+ if _url_override is not None:
64
+ return _url_override
65
+ return os.environ.get("SM_DATABASE_URL", "sqlite:///./app.db")
66
+
67
+
40
68
  def _build_engine() -> Engine:
41
- url = os.environ.get("SM_DATABASE_URL", "sqlite:///./app.db")
42
- sync_url = _sync_url(url)
69
+ sync_url = _sync_url(_resolve_url())
43
70
  # Small pool — signals fire sequentially per worker process.
44
71
  return create_engine(sync_url, pool_pre_ping=True, pool_size=2, max_overflow=3)
45
72
 
@@ -60,11 +87,12 @@ def dispose_sync_engine() -> None:
60
87
  restarts within one process (test runners, uvicorn dev reload) don't
61
88
  accumulate engines against the old DB URL.
62
89
  """
63
- global _engine, _session_factory
90
+ global _engine, _session_factory, _url_override
64
91
  if _engine is not None:
65
92
  _engine.dispose()
66
93
  _engine = None
67
94
  _session_factory = None
95
+ _url_override = None
68
96
 
69
97
 
70
98
  @contextmanager
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "simple_module_background_tasks"
3
- version = "0.0.8"
3
+ version = "0.0.10"
4
4
  description = "Celery + Redis task queue with admin UI for monitoring and retrying failed/stuck tasks"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -21,10 +21,10 @@ classifiers = [
21
21
  "Typing :: Typed",
22
22
  ]
23
23
  dependencies = [
24
- "simple_module_core==0.0.8",
25
- "simple_module_db==0.0.8",
26
- "simple_module_hosting==0.0.8",
27
- "simple_module_settings==0.0.8",
24
+ "simple_module_core==0.0.10",
25
+ "simple_module_db==0.0.10",
26
+ "simple_module_hosting==0.0.10",
27
+ "simple_module_settings==0.0.10",
28
28
  "celery[redis]>=5.4",
29
29
  "redis>=5",
30
30
  ]
@@ -0,0 +1,60 @@
1
+ """Tests for the sync session factory's URL resolution.
2
+
3
+ The web process loads ``.env`` via pydantic-settings but never propagates the
4
+ result to ``os.environ``. ``set_database_url`` lets the module's
5
+ ``on_startup`` pin the sync engine to whatever the host settings resolved,
6
+ so signals don't silently fall back to SQLite while the rest of the app
7
+ talks to Postgres.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from collections.abc import Iterator
13
+
14
+ import pytest
15
+ from background_tasks import sync_db
16
+
17
+
18
+ @pytest.fixture(autouse=True)
19
+ def _reset_sync_db() -> Iterator[None]:
20
+ sync_db.dispose_sync_engine()
21
+ yield
22
+ sync_db.dispose_sync_engine()
23
+
24
+
25
+ def test_resolve_url_falls_back_to_env(monkeypatch: pytest.MonkeyPatch) -> None:
26
+ monkeypatch.setenv("SM_DATABASE_URL", "sqlite:///./from-env.db")
27
+ sync_db.set_database_url(None)
28
+
29
+ assert sync_db._resolve_url() == "sqlite:///./from-env.db"
30
+
31
+
32
+ def test_set_database_url_overrides_env(monkeypatch: pytest.MonkeyPatch) -> None:
33
+ monkeypatch.setenv("SM_DATABASE_URL", "sqlite:///./from-env.db")
34
+ sync_db.set_database_url("postgresql+asyncpg://u:p@h/db")
35
+
36
+ assert sync_db._resolve_url() == "postgresql+asyncpg://u:p@h/db"
37
+
38
+
39
+ def test_set_database_url_resets_engine_when_url_changes(tmp_path) -> None:
40
+ db_a = tmp_path / "a.db"
41
+ db_b = tmp_path / "b.db"
42
+ sync_db.set_database_url(f"sqlite:///{db_a}")
43
+ factory_a = sync_db.get_sync_session_factory()
44
+ sync_db.set_database_url(f"sqlite:///{db_a}")
45
+ factory_a_again = sync_db.get_sync_session_factory()
46
+ assert factory_a is factory_a_again, "same URL must reuse the cached factory"
47
+
48
+ sync_db.set_database_url(f"sqlite:///{db_b}")
49
+ factory_b = sync_db.get_sync_session_factory()
50
+ assert factory_b is not factory_a, "URL change must dispose the old engine"
51
+
52
+
53
+ def test_dispose_sync_engine_clears_url_override(tmp_path) -> None:
54
+ url = f"sqlite:///{tmp_path / 'pinned.db'}"
55
+ sync_db.set_database_url(url)
56
+ assert sync_db._url_override == url
57
+
58
+ sync_db.dispose_sync_engine()
59
+
60
+ assert sync_db._url_override is None