mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-05 20:14:47 +00:00
- CI installs the backend requirements from docsgpt/; the old cd into
application/ silently installed nothing.
- The root .dockerignore re-admits only application/__init__.py. An upgraded
checkout may still hold gitignored application/{inputs,indexes,vectors,.env}
from the old layout, and the directory rule shipped them into the image.
- The compose files keep the host bind mounts on application/{indexes,inputs,
vectors}, so an upgrade does not start with empty data. The move comes with
the packaging work, together with an upgrade note.
- The alias loader puts the real docsgpt spec back on the shared module object
after import (the import machinery stamped the alias spec on it, which made
importlib.reload rename the module and skip re-execution) and delegates
get_code/get_source/get_filename to the target loader, so
python -m application.<name> runs.
- Each legacy application.* task name is registered as its own task object,
a subclass carrying the old name. Registering the same object under two
keys made Celery's tracer log every run under whichever name it built last.
- The redbeat key prefix stays redbeat:docsgpt:; the three schedule_syncs
entries get stable names instead. redbeat tracks its static entries and
deletes the ones that vanish from beat_schedule at start-up, and rewrites the
task path of named entries in place, so neither a prefix bump nor a cleanup
pass is needed (checked against redbeat 2.4.2 with a seeded Redis).
95 lines
4.4 KiB
Python
95 lines
4.4 KiB
Python
"""The ``application`` alias and the legacy Celery task names survive the rename to ``docsgpt``."""
|
|
|
|
import importlib
|
|
import runpy
|
|
import sys
|
|
import warnings
|
|
|
|
import pytest
|
|
|
|
|
|
def _quiet_import(name):
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("ignore", FutureWarning)
|
|
return importlib.import_module(name)
|
|
|
|
|
|
class TestApplicationAlias:
|
|
def test_old_import_is_the_same_module_object(self):
|
|
old = _quiet_import("application.vectorstore.model_registry")
|
|
new = importlib.import_module("docsgpt.vectorstore.model_registry")
|
|
assert old is new
|
|
assert sys.modules["application.vectorstore.model_registry"] is new
|
|
|
|
def test_old_package_is_the_new_package(self):
|
|
_quiet_import("application")
|
|
assert sys.modules["application"] is importlib.import_module("docsgpt")
|
|
|
|
def test_alias_keeps_the_real_spec(self):
|
|
"""Importing through the alias must not rename the shared module object."""
|
|
module = _quiet_import("application.vectorstore.model_registry")
|
|
assert module.__spec__.name == "docsgpt.vectorstore.model_registry"
|
|
assert module.__name__ == "docsgpt.vectorstore.model_registry"
|
|
|
|
def test_reload_works_on_the_shared_module(self):
|
|
# docsgpt.version has no shared constants, so re-executing it in place is harmless.
|
|
_quiet_import("application.version")
|
|
module = importlib.import_module("docsgpt.version")
|
|
reloaded = importlib.reload(module)
|
|
assert reloaded is module
|
|
assert reloaded.__spec__.name == "docsgpt.version"
|
|
assert reloaded.get_version() == module.__version__
|
|
|
|
def test_python_dash_m_through_the_alias(self):
|
|
"""``python -m application.x`` goes through runpy, which needs the loader's get_code.
|
|
|
|
A real ``python -m`` starts a fresh interpreter, so the target module is
|
|
not yet in ``sys.modules`` under the alias name when runpy looks it up;
|
|
drop any entry an earlier test left so the lookup reaches the finder.
|
|
"""
|
|
_quiet_import("application")
|
|
sys.modules.pop("application.vectorstore.model_registry", None)
|
|
globals_ = runpy.run_module("application.vectorstore.model_registry", run_name="__main__", alter_sys=True)
|
|
assert "DEFAULT_NEW_INSTALL" in globals_
|
|
|
|
def test_alias_warns_when_first_imported(self):
|
|
"""The shim warns once per process: on the import that executes it."""
|
|
sys.meta_path[:] = [f for f in sys.meta_path if type(f).__name__ != "_AliasFinder"]
|
|
for name in [m for m in sys.modules if m == "application" or m.startswith("application.")]:
|
|
del sys.modules[name]
|
|
with pytest.warns(FutureWarning, match="renamed to 'docsgpt'"):
|
|
importlib.import_module("application")
|
|
assert sys.modules["application"] is importlib.import_module("docsgpt")
|
|
|
|
def test_missing_module_still_raises(self):
|
|
_quiet_import("application")
|
|
with pytest.raises(ModuleNotFoundError):
|
|
importlib.import_module("application.no_such_module")
|
|
|
|
|
|
class TestLegacyTaskNames:
|
|
def test_every_task_answers_to_its_old_name_as_its_own_object(self):
|
|
from docsgpt.celery_init import LEGACY_TASK_PREFIX, celery, register_legacy_task_names
|
|
|
|
importlib.import_module("docsgpt.api.user.tasks")
|
|
importlib.import_module("docsgpt.vectorstore.embeddings_tasks")
|
|
register_legacy_task_names(celery)
|
|
new_names = [n for n in celery.tasks if n.startswith("docsgpt.")]
|
|
assert new_names, "no docsgpt.* tasks registered"
|
|
for name in new_names:
|
|
legacy_name = LEGACY_TASK_PREFIX + name[len("docsgpt."):]
|
|
canonical, legacy = celery.tasks[name], celery.tasks[legacy_name]
|
|
assert legacy is not canonical, "an alias must be its own task object (its own tracer)"
|
|
assert isinstance(legacy, type(canonical))
|
|
assert legacy.name == legacy_name
|
|
assert canonical.name == name
|
|
assert register_legacy_task_names(celery) == 0, "second call must be idempotent"
|
|
|
|
def test_routes_and_reclaim_list_use_new_names(self):
|
|
from docsgpt import celeryconfig
|
|
from docsgpt.celery_init import _NO_RECLAIM_TASKS
|
|
|
|
assert all(k.startswith("docsgpt.") for k in celeryconfig.task_routes)
|
|
assert all(k.startswith("docsgpt.") for k in _NO_RECLAIM_TASKS)
|
|
assert celeryconfig.redbeat_key_prefix == "redbeat:docsgpt:", "the prefix must stay: redbeat updates named entries in place"
|