swyddfa / esbonio

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

Print statement in Sphinx event callback breaks esbonio #832

Closed HeinischValentin closed 2 weeks ago

HeinischValentin commented 2 weeks ago

Expected behavior

I am working on a RST project inside VSCode, with the live preview setup. I have a minimal venv with Sphinx and esbonio installed. Inside my conf.py I connect some function to a Sphinx event and call the "print()" function inside the callback. With Sphinx version 7.2.6 everything works fine. I update to Sphinx 7.3.0 and expect the project to build.

Actual behavior

After updating to Sphinx version 7.3.0, esbonio is stuck building. When I try to restart the language server, it shuts down completely. When I remove the print statement from the callback function, esbonio works fine again. Print statements e.g. in the "setup" function of my conf.py do not break esbonio.

Log output

[Log - 10:46:16 AM] Server start command: d:\projects\rst\TestProjects\api-return-table\.venv\Scripts\python.exe -m esbonio
[Log - 10:46:16 AM] LanguageClientOptions: {"documentSelector":[{"scheme":"file","language":"restructuredtext"},{"scheme":"file","language":"python"}],"initializationOptions":{"sphinx":{"srcDir":null,"confDir":"${workspaceFolder}","forceFullBuild":true,"numJobs":1,"buildDir":"${workspaceFolder}/_build"},"server":{"logLevel":"debug","logFilter":[],"hideSphinxOutput":false,"enableScrollSync":true}},"outputChannel":{"name":"Esbonio Language Server"}}
[Log - 10:46:16 AM] Starting Language Server
[esbonio.lsp] Loaded extension 'esbonio.lsp.directives'
[esbonio.lsp] Loaded extension 'esbonio.lsp.roles'
[esbonio.lsp] Loaded extension 'esbonio.lsp.rst.directives'
[esbonio.lsp] Loaded extension 'esbonio.lsp.rst.roles'
[esbonio.lsp] Loaded extension 'esbonio.lsp.sphinx.autodoc'
[esbonio.lsp] Loaded extension 'esbonio.lsp.sphinx.codeblocks'
[esbonio.lsp] Loaded extension 'esbonio.lsp.sphinx.domains'
[esbonio.lsp] Loaded extension 'esbonio.lsp.sphinx.directives'
[esbonio.lsp] Loaded extension 'esbonio.lsp.sphinx.images'
[esbonio.lsp] Loaded extension 'esbonio.lsp.sphinx.includes'
[esbonio.lsp] Loaded extension 'esbonio.lsp.sphinx.roles'
[esbonio.lsp] User Config {
  "buildDir": "${workspaceFolder}/_build",
  "confDir": "${workspaceFolder}",
  "forceFullBuild": true
}
[esbonio.lsp] Workspace Folder: 'file:///d%3A/projects/rst/TestProjects/api-return-table/spec'
[esbonio.lsp] Sphinx Args {
  "buildername": "html",
  "confdir": "D:\\projects\\rst\\TestProjects\\api-return-table\\spec",
  "confoverrides": {},
  "doctreedir": "D:\\projects\\rst\\TestProjects\\api-return-table\\spec\\_build\\doctrees",
  "freshenv": true,
  "keep_going": false,
  "outdir": "D:\\projects\\rst\\TestProjects\\api-return-table\\spec\\_build\\html",
  "parallel": 1,
  "srcdir": "D:\\projects\\rst\\TestProjects\\api-return-table\\spec",
  "status": null,
  "tags": [],
  "verbosity": 0,
  "warning": null,
  "warningiserror": false
}
Running Sphinx v7.3.0
[esbonio.lsp] Skipping extension 'sphinx.addnodes', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.builders.changes', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.builders.epub3', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.ext.mathjax', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.builders.html.transforms', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.builders.html', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.builders.dirhtml', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.builders.dummy', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.builders.gettext', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.builders.latex.transforms', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.builders.latex', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.builders.linkcheck', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.builders.manpage', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.builders.singlehtml', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.builders.texinfo', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.builders.text', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.builders.xml', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.config', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.domains.c', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.domains.changeset', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.domains.citation', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.domains.cpp', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.domains.index', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.domains.javascript', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.domains.math', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.directives', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.domains.python', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.domains.rst', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.domains.std', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.directives.code', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.directives.other', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.directives.patches', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.extension', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.parsers', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.registry', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.roles', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.transforms', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.transforms.compact_bullet_list', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.transforms.i18n', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.transforms.references', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.transforms.post_transforms', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.transforms.post_transforms.code', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.transforms.post_transforms.images', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.versioning', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.environment.collectors.dependencies', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.environment.collectors.asset', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.environment.collectors.metadata', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.environment.collectors.title', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinx.environment.collectors.toctree', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'alabaster', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinxcontrib.applehelp', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinxcontrib.devhelp', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinxcontrib.htmlhelp', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinxcontrib.serializinghtml', missing 'esbonio_setup' fuction
[esbonio.lsp] Skipping extension 'sphinxcontrib.qthelp', missing 'esbonio_setup' fuction
[esbonio.lsp] Building...
[Log - 10:46:17 AM] Build start.
building [mo]: targets for 0 po files that are out of date
writing output...

building [html]: targets for 1 source files that are out of date
updating environment:
[new config]
1 added, 0 changed, 0 removed    <- STUCK HERE UNTIL RESTART
[Log - 10:57:17 AM] ==================== RESTARTING SERVER =====================
Server shutting down. No more requests!
Server shutting down. No more requests!
Server shutting down. No more requests!
Server shutting down. No more requests!

(Optional) Settings from conf.py

def test_before_read_docs(a, b, c):
    print("test")
    pass

def setup(app) -> dict[str, str | bool]:
    app.connect("env-before-read-docs", test_before_read_docs)
    return {"version": "0.1", "parallel_read_safe": False}
alcarney commented 2 weeks ago

With Sphinx version 7.2.6 everything works fine

To be honest, I'm surprised this worked for you before.

The most common way for a language server like esbonio to communicate with an editor is to print() messages to stdout. Unfortunately, this means nothing else in the server process can use print() as it will interfere with the communication between client and server - as you are seeing here.

The upcoming 1.0 release will move Sphinx out into a separate process which opens up the possibility to use a different communication mechanism. However the easiest solution for the time being is to work around this.

You could either use a logger from Sphinx's logging system

from sphinx.util.logging import getLogger

logger = getLogger("name")

def test_before_read_docs(a, b, c):
    logger.info("test")
    pass

def setup(app) -> dict[str, str | bool]:
    app.connect("env-before-read-docs", test_before_read_docs)
    return {"version": "0.1", "parallel_read_safe": False}

Or you could try printing to stderr instead

import sys

def test_before_read_docs(a, b, c):
    print(test", file=sys.stderr)
    pass

def setup(app) -> dict[str, str | bool]:
    app.connect("env-before-read-docs", test_before_read_docs)
    return {"version": "0.1", "parallel_read_safe": False}

Hope that helps!

HeinischValentin commented 2 weeks ago

Switching from using print to the sphinx logger solves the issue, thank you very much! I am also very confused why this problem only showed up since the latest Sphinx release. Again, thanks for your help.