Skip to content

Commit

Permalink
sphinx-agent: Expose the AST for conf.py via an attribute
Browse files Browse the repository at this point in the history
This allows us to parse the file once in a central location
  • Loading branch information
alcarney committed Oct 20, 2024
1 parent 603fe1d commit f2e77a5
Showing 1 changed file with 31 additions and 13 deletions.
44 changes: 31 additions & 13 deletions lib/esbonio/esbonio/sphinx_agent/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
if typing.TYPE_CHECKING:
from typing import IO
from typing import Any
from typing import Literal

RoleDefinition = tuple[str, Any, list[types.Role.TargetProvider]]

Expand All @@ -43,15 +44,41 @@ class Esbonio:
log: DiagnosticFilter

def __init__(self, dbpath: pathlib.Path, app: _Sphinx):
self.app: _Sphinx = app
self.db = Database(dbpath)
self.log = DiagnosticFilter(app)

self._roles: list[RoleDefinition] = []
"""Roles captured during Sphinx startup."""

self._config_ast: ast.Module | Literal[False] | None = None
"""The parsed AST of the user's conf.py file.
If ``False``, we already tried parsing the module and were unable to."""

self.diagnostics: dict[types.Uri, set[types.Diagnostic]] = {}
"""Recorded diagnostics."""

@property
def config_uri(self) -> types.Uri:
return types.Uri.for_file(pathlib.Path(self.app.confdir, "conf.py"))

@property
def config_ast(self) -> ast.Module | None:
"""Return the AST for the user's conf.py (if possible)"""

if self._config_ast is not None:
return self._config_ast or None # convert ``False`` to ``None``

try:
conf_py = pathlib.Path(self.app.confdir, "conf.py")
self._config_ast = ast.parse(source=conf_py.read_text())
return self._config_ast
except Exception as exc:
logger.debug("Unable to parse user's conf.py: %s", exc)
self._config_ast = False

return None

def add_role(
self,
name: str,
Expand Down Expand Up @@ -138,22 +165,13 @@ def _report_missing_extension(self, extname: str, exc: Exception):
Parameters
----------
extname
The name of the extension that caused the excetion
The name of the extension that caused the exception
exc
The exception instance
"""

if not isinstance(cause := exc.__cause__, ImportError):
return

# Parse the user's config file
# TODO: Move this somewhere more central.
try:
conf_py = pathlib.Path(self.confdir, "conf.py")
config = ast.parse(source=conf_py.read_text())
except Exception:
logger.debug("Unable to parse user's conf.py")
if (config := self.esbonio.config_ast) is None:
return

# Now attempt to find the soure location of the extenison.
Expand All @@ -162,10 +180,10 @@ def _report_missing_extension(self, extname: str, exc: Exception):
return

diagnostic = types.Diagnostic(
range=range_, message=str(cause), severity=types.DiagnosticSeverity.Error
range=range_, message=f"{exc}", severity=types.DiagnosticSeverity.Error
)

uri = types.Uri.for_file(conf_py)
uri = self.esbonio.config_uri
logger.debug("Adding diagnostic %s: %s", uri, diagnostic)
self.esbonio.diagnostics.setdefault(uri, set()).add(diagnostic)

Expand Down

0 comments on commit f2e77a5

Please sign in to comment.