import os import sys import tomllib # The Django package lives at /mnemosyne//. Adding the # outer mnemosyne/ directory to sys.path lets autodoc resolve every app # (themis, library, llm_manager, mcp_server) and the project settings module. sys.path.insert(0, os.path.abspath('../../mnemosyne')) os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mnemosyne.settings') # Load real .env if present (local dev). In CI there is none and that's fine — # settings.py provides a default for every env var it reads, so the import # succeeds either way. _repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) _env_file = os.path.join(_repo_root, 'mnemosyne', '.env') if os.path.exists(_env_file): with open(_env_file) as _f: for _line in _f: _line = _line.strip() if not _line or _line.startswith('#') or '=' not in _line: continue _key, _val = _line.split('=', 1) os.environ.setdefault(_key.strip(), _val.strip()) import django # noqa: E402 django.setup() # Sphinx autodoc calls repr() on every class attribute it documents. Django's # QuerySet.__repr__ executes a SELECT against the database — which doc builds # have no business doing. Intercept object_description so QuerySet instances # render as a static string instead. Mnemosyne's themis app has at least one # DRF viewset with a class-level queryset attribute, so this matters. from django.db.models.query import QuerySet # noqa: E402 import sphinx.util.inspect as _sphinx_inspect # noqa: E402 _orig_object_description = _sphinx_inspect.object_description def _safe_object_description(obj, *args, **kwargs): if isinstance(obj, QuerySet): return f'' return _orig_object_description(obj, *args, **kwargs) _sphinx_inspect.object_description = _safe_object_description # ── Sphinx configuration ────────────────────────────────────────────────── project = 'Mnemosyne' copyright = '2026, Mnemosyne Team' author = 'Mnemosyne Team' with open(os.path.join(_repo_root, 'pyproject.toml'), 'rb') as _f: release = tomllib.load(_f)['project']['version'] extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', 'sphinx.ext.intersphinx', 'sphinx_autodoc_typehints', 'sphinxcontrib.httpdomain', 'sphinxcontrib.mermaid', 'myst_parser', ] source_suffix = {'.rst': 'restructuredtext', '.md': 'markdown'} myst_enable_extensions = ['colon_fence', 'deflist', 'tasklist', 'attrs_inline'] myst_heading_anchors = 4 autodoc_default_options = { 'members': True, 'member-order': 'bysource', 'special-members': '__init__', 'undoc-members': True, 'exclude-members': '__weakref__', } autodoc_inherit_docstrings = False napoleon_use_ivar = True intersphinx_mapping = { 'python': ('https://docs.python.org/3', None), 'django': ('https://docs.djangoproject.com/en/stable/', 'https://docs.djangoproject.com/en/stable/_objects/'), } html_theme = 'sphinx_rtd_theme' html_static_path = ['_static'] html_theme_options = { 'navigation_depth': 4, 'collapse_navigation': False, 'sticky_navigation': True, 'includehidden': True, 'titles_only': False, }