swyddfa / esbonio

A language server for working with Sphinx projects.
https://docs.esbon.io/
135 stars 21 forks source link

sphinx-build command line parsing broken on Sphinx 8.1 #912

Closed alcarney closed 2 weeks ago

alcarney commented 3 weeks ago

Expected behavior

Esbonio's sphinx agent be able to parse a sphinx-build command correctly

Actual behavior

Esbonio rasies a cryptic error message when trying to launch a Sphinx process.

I'm still investigating but it appears that the latest release of Sphinx has broken the way esbonio tries to re-use Sphinx's command line parser.

Log output

[esbonio.SphinxManager] Unable to start SphinxClient: 'srcdir'
Traceback (most recent call last):
  File "/var/home/alex/Projects/swyddfa/esbonio/develop/.vscode/extensions/esbonio/bundled/libs/esbonio/server/features/sphinx_manager/client_subprocess.py", line 220, in start
    self.sphinx_info = await self.protocol.send_request_async(
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pygls.exceptions.JsonRpcInternalError: 'srcdir'

(Optional) Settings from conf.py

No response

alcarney commented 3 weeks ago

The relevant code:

        if args[0] == "sphinx-build":
            args = args[1:]

        # The easiest way to handle this is to just call sphinx-build but with
        # the Sphinx app object patched out - then we just use all the args it
        # was given!
        with mock.patch("sphinx.cmd.build.Sphinx") as m_Sphinx:
            sphinx_build(args)

        if m_Sphinx.call_args is None:
            return None

        signature = inspect.signature(Sphinx)
        keys = signature.parameters.keys()

        values = m_Sphinx.call_args[0]
        sphinx_args = {k: v for k, v in zip(keys, values)}

        if sphinx_args is None:
            return None

        # `sphinx_args` at this point is an empty dictionary!
        return cls(
            src_dir=sphinx_args["srcdir"],
            conf_dir=sphinx_args["confdir"],
            build_dir=sphinx_args["outdir"],
            builder_name=sphinx_args["buildername"],
            doctree_dir=sphinx_args["doctreedir"],
            config_overrides=sphinx_args.get("confoverrides", {}),
            force_full_build=sphinx_args.get("freshenv", False),
            keep_going=sphinx_args.get("keep_going", False),
            num_jobs=sphinx_args.get("parallel", 1),
            quiet=sphinx_args.get("status", 1) is None,
            silent=sphinx_args.get("warning", 1) is None,
            tags=sphinx_args.get("tags", []),
            verbosity=sphinx_args.get("verbosity", 0),
            warning_is_error=sphinx_args.get("warningiserror", False),
        )
alcarney commented 3 weeks ago

Sphinx switched from passing arguments positionally to using keywords, which changes the shape of the m_Sphinx.call_args object https://github.com/sphinx-doc/sphinx/commit/fadb6b10cb15d2a8ce336cee307dcb3ff64680bd#diff-4aea2ac365ab5325b530ac42efc67feac98db110e7f943ea13b5cf88f4260e59R334