swyddfa / lsp-devtools

Tooling for working with language servers and clients.
https://lsp-devtools.readthedocs.io/en/latest/
47 stars 8 forks source link

`pytest-lsp`: See server's STDERR in pytest output #143

Closed tobb10001 closed 5 months ago

tobb10001 commented 5 months ago

Or: How to debug the server behind pytest?

For debugging, I usually

Unfortunately I haven't found a way to access the logs of my server with pytest-lsp in a nice way. I think it would be great to see the server's STDERR output in the console right next to the test results (or have an option for this behaviour), just as pytest does by default when working with pure Pyhon systems. I think this could work by just forwarding the server subprocess' STDERR to the test process STDERR.

My current workaround is to have my server log to a file and check that in another terminal, but that's kind of tedious.

tobb10001 commented 5 months ago

Tried to make this work, seems harder then I'd hoped.

I took the capturing that happens in LanguageClient.server_exit(), stored it into an instance variable, and output it after the yield in the fixture.

It seems like pytest only captures during the execution of the test case, not during a fixture.

Another option would be to not use stderr=subprocess.PIPE, but that's impractical because (a) that's done in pygls and can't be done here and (b) it would interfere with existing functionality.

alcarney commented 5 months ago

How to debug the server behind pytest?

I tend to forward the server's log output via window/logMessage notifications, pytest-lsp is usually able to attach those to failure reports.

But I agree, having pytest-lsp also expose the server's stderr would be useful :thinking: One approach that might work, would be to spawn an async task that continually calls stderr.readline() and prints the result to stderr...

alcarney commented 5 months ago

One approach that might work, would be to spawn an async task that continually calls stderr.readline() and prints the result to stderr...

Just tried it out, looks like it should work! I'll try and get something out to play with soon.

tobb10001 commented 5 months ago

Looks interesting, but unfortunately I can't get it to work properly on my end.

The test works, and if I run from the examples directory (manually renaming t_server.py to test_server.py), everything works as desired. However, on other examples it seems doesn't run as smoothly.

I created a test file for myself, which was based on the Getting started, where I just added

for _ in range(10):
    print("Hello stderr.\n", file=sys.stdout)

- with low numbers, I don't see anything in the output - with high numbers (I tried 1000), the test blocks indefinetely

Forget that, I typoded stderr as stdout, that explains why that didn't work...

However, for my actual project, I also don't see anything. (Should log about 20 lines in debug mode.)

I feel like something in this piping setup causes a deadlock, but unfortunately I'm way to unexperienced with asyncio to debug this.

I could find out that I didn't actually log to stderr here either, because my logging setup was incorrect.

I want to try out what happens when the coroutine is replaced by a thread. Don't know if it would be a good solution, but I wanna see what happens.

UPDATE: The thread solution doesn't seem to be so promising either. Since the subprocess is started by the asyncio machinery, the interface can't be easily accessed from another thread. At least not with my knowledge at least...

TL:DR: Sorry for this mess of a comment. I've tested the code in your PR and it works as desired.

alcarney commented 5 months ago

TL:DR: Sorry for this mess of a comment. I've tested the code in your PR and it works as desired.

No worries! It's exciting to see someone interested enough in this that they're willing to get hands on with it!

alcarney commented 5 months ago

v0.4.1 is out containing this functionality, let me know if you run into any issues