Skip to content

Commit

Permalink
lsp: Use fallback env when possible
Browse files Browse the repository at this point in the history
If a client provides the `esbonio.sphinx.fallbackEnv`, repurpose the server's Python interpreter
to launch the Sphinx agent.
  • Loading branch information
alcarney committed Oct 16, 2024
1 parent a5dc6de commit 5ca8a10
Showing 1 changed file with 54 additions and 19 deletions.
73 changes: 54 additions & 19 deletions lib/esbonio/esbonio/server/features/sphinx_manager/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import importlib.util
import logging
import pathlib
import sys
from typing import Any
from typing import Optional

Expand Down Expand Up @@ -60,6 +61,12 @@ class SphinxConfig:
cwd: str = attrs.field(default="${scopeFsPath}")
"""The working directory to use."""

fallback_env: str | None = attrs.field(default=None)
"""Location of the fallback environment to use.
Intended to be used by clients to handle the case where the user has not configured
``python_command`` themselves."""

python_path: list[pathlib.Path] = attrs.field(factory=list)
"""The value of ``PYTHONPATH`` to use when injecting the sphinx agent into the
target environment"""
Expand Down Expand Up @@ -89,8 +96,8 @@ def resolve(
The fully resolved config object to use.
If ``None``, a valid configuration could not be created.
"""
python_path = self._resolve_python_path(logger)
if len(python_path) == 0:
python_command, python_path = self._resolve_python(logger)
if len(python_path) == 0 or len(python_command) == 0:
return None

cwd = self._resolve_cwd(uri, workspace, logger)
Expand All @@ -109,7 +116,7 @@ def resolve(
config_overrides=self.config_overrides,
cwd=cwd,
env_passthrough=self.env_passthrough,
python_command=self.python_command,
python_command=python_command,
build_command=build_command,
python_path=python_path,
)
Expand Down Expand Up @@ -158,12 +165,25 @@ def _resolve_cwd(

return None

def _resolve_python_path(self, logger: logging.Logger) -> list[pathlib.Path]:
"""Return the list of paths to put on the sphinx agent's ``PYTHONPATH``
def _resolve_python(
self, logger: logging.Logger
) -> tuple[list[str], list[pathlib.Path]]:
"""Return the python configuration to use when launching the sphinx agent.
The first element of the returned tuple is the command to use when running the
sphinx agent. This could be as simple as the path to the python interpreter in a
particular virtual environment or a complex command such as
``hatch -e docs run python``.
Using the ``PYTHONPATH`` environment variable, we can inject additional Python
packages into the user's Python environment. This method will locate the
installation path of the sphinx agent and return it.
packages into the user's Python environment. This method also locates the
installation path of the sphinx agent and returns it in the second element of the
tuple.
Finally, if the user has not configured a python environment and the client has
set the ``fallback_env`` option, this method will construct a command based on
the current interpreter to create an isolated environment based on
``fallback_env``.
Parameters
----------
Expand All @@ -172,19 +192,34 @@ def _resolve_python_path(self, logger: logging.Logger) -> list[pathlib.Path]:
Returns
-------
List[pathlib.Path]
The list of paths to Python packages to inject into the sphinx agent's target
environment. If empty, the ``esbonio.sphinx_agent`` package was not found.
tuple[list[str], list[pathlib.Path]]
A tuple of the form ``(python_command, python_path)``.
"""
if len(self.python_path) > 0:
return self.python_path

if (sphinx_agent := get_module_path("esbonio.sphinx_agent")) is None:
logger.error("Unable to locate the sphinx agent")
return []

python_path = [sphinx_agent]
return python_path
if len(python_path := list(self.python_path)) == 0:
if (sphinx_agent := get_module_path("esbonio.sphinx_agent")) is None:
logger.error("Unable to locate the sphinx agent")
return [], []

python_path.append(sphinx_agent)

if len(python_command := list(self.python_command)) == 0:
if self.fallback_env is None:
logger.error("No python command configured")
return [], []

if not (fallback_env := pathlib.Path(self.fallback_env)).exists():
logger.error(
"Provided fallback environment %s does not exist", fallback_env
)
return [], []

# Since the client has provided a fallback environment we can isolate the
# current Python interpreter from its environment and reuse it.
logger.debug("Using fallback environment")
python_path.append(fallback_env)
python_command.extend([sys.executable, "-S"])

return python_command, python_path

def _resolve_build_command(self, uri: Uri, logger: logging.Logger) -> list[str]:
"""Return the ``sphinx-build`` command to use.
Expand Down

0 comments on commit 5ca8a10

Please sign in to comment.