Repository: spyder-ide/spyder

Issue:
Spyder crashes after Monitor Scale change
## Description

Spyder crashes after closing ("dismiss") the Monitor Scale change detected pop-up on remote machine.

### What steps will reproduce the problem?

<!--- You can use Markdown here --->

Specific scenario:

1. Connect to a Remote Desktop with one monitor scale size. 
2. Open Spyder on that Remote Desktop machine.
3. Close Remote Desktop connection (keep Spyder open on the remote machine).
4. Re-connect to the same Remote Desktop with a *different* monitor scale size (e.g. a different local machine).
5. Spyder produces Monitor Scale change detected pop-up. Click dismiss. 
6. Spyder will crash/close with no error thrown.

Note: monitor scale change pop up seems to work normally when I move Spyder (in the RD window) between monitors on the local machine. 

Pure speculation: could it be that there are three monitor scale changes going on (local machine #1 > remote machine (when RD connection from LM1 is closed) > local machine #2) which is what's causing the issue?





## Versions

* Spyder version: 4.1.5 
* Python version: 3.8.3
* Qt version: 5.9.7
* PyQt5 version: 5.9.2
* Operating System: Windows 10

### Dependencies

```

# Mandatory:
atomicwrites >=1.2.0           :  1.4.0 (OK)
chardet >=2.0.0                :  3.0.4 (OK)
cloudpickle >=0.5.0            :  1.6.0 (OK)
diff_match_patch >=20181111    :  20200713 (OK)
intervaltree                   :  None (OK)
IPython >=4.0                  :  7.18.1 (OK)
jedi =0.17.1                   :  0.17.1 (OK)
nbconvert >=4.0                :  5.6.1 (OK)
numpydoc >=0.6.0               :  1.1.0 (OK)
paramiko >=2.4.0               :  2.7.2 (OK)
parso =0.7.0                   :  0.7.0 (OK)
pexpect >=4.4.0                :  4.8.0 (OK)
pickleshare >=0.4              :  0.7.5 (OK)
psutil >=5.3                   :  5.7.2 (OK)
pygments >=2.0                 :  2.6.1 (OK)
pylint >=1.0                   :  2.6.0 (OK)
pyls >=0.34.0;<1.0.0           :  0.34.1 (OK)
qdarkstyle >=2.8               :  2.8.1 (OK)
qtawesome >=0.5.7              :  0.7.2 (OK)
qtconsole >=4.6.0              :  4.7.6 (OK)
qtpy >=1.5.0                   :  1.9.0 (OK)
rtree >=0.8.3                  :  0.9.4 (OK)
sphinx >=0.6.6                 :  3.2.1 (OK)
spyder_kernels >=1.9.4;<1.10.0 :  1.9.4 (OK)
watchdog                       :  None (OK)
zmq >=17                       :  19.0.1 (OK)

# Optional:
cython >=0.21                  :  0.29.21 (OK)
matplotlib >=2.0.0             :  3.3.1 (OK)
numpy >=1.7                    :  1.19.1 (OK)
pandas >=0.13.1                :  1.1.1 (OK)
scipy >=0.17.0                 :  1.5.0 (OK)
sympy >=0.7.3                  :  1.6.2 (OK)
```



Discussion:
I believe this is related, rather than a seperate issue - so am following up with this.

Sometimes I will get the scale change pop up appear twice. After dismissing both, Spyder closes/crashes.

<img width="1080" alt="Screenshot 2020-11-20 104123" src="https://user-images.githubusercontent.com/63111813/99791434-86c47500-2b1d-11eb-97a9-914b5e85026e.png">

(Edits to image for privacy/security)

Hi @Caseyb87. Thanks for reporting this! @dalthviz can you please try to reproduce this on Windows?
@Caseyb87 could you run Spyder from cmd in you remote machine and check if any traceback appears in the console? And thanks for the info :)

Also, if I manage to get a way to test this I will let you know @juanis2112 (currently I don't have access to a remote windows machine to check with a remote desktop connection). @andfoy you have access to remote windows machines to check this?
> 
> 
> @Caseyb87 could you run Spyder from cmd in you remote machine and check if any traceback appears in the console? And thanks for the info :)

No - nothing appears in cmd (I ran through Anaconda prompt). If there are any other debugging steps I can take or log files that might contain anything useful do let me know.


@Caseyb87 maybe running `spyder --debug-info verbose` from the Anaconda Prompt could show something.

Any new info is greatly appreciated :)
> 
> @Caseyb87 maybe running `spyder --debug-info verbose` from the Anaconda Prompt could show something.
> 
> Any new info is greatly appreciated :)

Looks like something got recorded. This was triggered after clicking "dismiss" on the prompt (the previous line was just an autosave). Spyder then crash/closed.

2020-11-24 16:27:12,753 [DEBUG] [urllib3.connectionpool] -> http://127.0.0.1:46624 "GET /clientapi/status?filename=_removed for privacy_ HTTP/1.1" 200 121
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
Traceback (most recent call last):
  File "C:\Users\ _username_ \Anaconda3\lib\site-packages\spyder\app\mainwindow.py", line 1414, in handle_new_screen
    self.screen.logicalDotsPerInchChanged.connect(
AttributeError: 'NoneType' object has no attribute 'logicalDotsPerInchChanged'
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information

Hopefully that's helpful!
Thanks @Caseyb87 !
I have this problem too - it is real. Thanks for trying to resolve.
Confirmed still present in 4.2.0 (unsurprisingly) - also enabling the "Enable auto high res DPI" setting does not prevent the crash.
HI! @ccordoba12 why removing it from milestone ? It will be resolved in 4.2.2 ?
I am having the same issue on two W10 devices (with remote desktop access).
VERY annoying bug! because a day-long work has disappeared ...
> 
> 
> HI! @ccordoba12 why removing it from milestone ? It will be resolved in 4.2.2 ?
> I am having the same issue on two W10 devices (with remote desktop access).
> VERY annoying bug! because a day-long work has disappeared ...

I guess because they don't plan to fix it in the upcoming release - which is a shame as this is a major bug, but probably only for quite a limited set of users. 

I think preventing the crash shouldn't be too hard, just check that self.screen is not None I guess. Or just wrap this whole block in a try and catch. Fixing why self.screen is NoneType may be harder but may not be necessary.
Hi @arnaud-nt2i  sorry to hear that :/ and yes as @Caseyb87 said, basically we will not be working on this for the next release. However if [someone else wants to work on it](https://github.com/spyder-ide/spyder/blob/master/CONTRIBUTING.md#setting-up-a-development-environment) (for example if you guys @Caseyb87 or @arnaud-nt2i want to try to fix the issue) we could review the work and add it to the next release! (if finished before the next release of course).

So, let us know if you guys want to try to implement the fix! We will more than happy to guide/help you and review the work :D 
@Caseyb87, I thought this was harder to solve, but you're right, we could catch the error and pass. So we'll try to solve it for 4.2.2

@dalthviz, do you have a way to connect remotely to another Windows machine to test this?
> @dalthviz, do you have a way to connect remotely to another Windows machine to test this?

Nop that's one of the main reasons why I was unable to reproduce this and find the root cause of the issue (I was unable to reproduce this by using just a local Spyder instance)
Ok, I think we can ask @CAM-Gerlach to give us a hand with that. @CAM-Gerlach, are you able to connect to a Windows machine using Remote Desktop?
I can RDP into machines with Spyder installed, but the remote machines are configured by the admin to log out the connected account if I terminate the RDP connection. Therefore, I was not able to reproduce the specific problem, since it seems to require reconnecting to the same logged in session after terminating the connection previously.

You'll need someone with access to a Windows machine over RDP that does not have the setting configured to log the user out automatically when disconnecting RDP so they can re-connect to the same session, or else that has control/admin on a RDP-acessable machine where they can set this setting. Alternatively, the user could try to find a way to reproduce the issue without re-connecting to the same active session (e.g. while still connected over RDP, like forcing a monitor scale change locally), but this may simply not be possible due to the nature of the problem.

This is a pretty niche case; I'm sorry I don't have a better solution.
I think I can offer the required rdp setup to test e.g. a pr branch, but I am not sure if I could consistently reproduce the issue in the first place, let me check that first.
@CAM-Gerlach, I see, too bad then.

@bilderbuchi, thanks for offering your help!
OK, I can reproduce the issue with the version from Anaconda. 

I have set up a test environment with the current master branch (5.0.0.dev0 Commit: 42774fb6e), and the behaviour is different: 
On clicking "Dismiss", Spyder does not crash and rescales text displayed (weirdly small, I might say). I can click into editor and python console, and even hover over buttons in the GUI, but as soon as I move/click to the menu bar, Spyder crashes. :-(

(The only weird thing is, although I installed as per instructions, spyder_kernels is too old (1.10.1 vs 2.0.0dev), I don't know if that is an issue.

I am not confident that a try/except around it all will actually fix this issue, as `handle_new_screen()` will basically become a no-op - I don't know the consequences of basically not "handling" a new screen. 
Also, depending on what happens in the [block before our problem area](https://github.com/spyder-ide/spyder/blob/42774fb6e64ae7e2a34c0809628d51abb269acfd/spyder/app/mainwindow.py#L1549-L1554), we might end up with `self.screen.logicalDotsPerInchChanged` disconnected, whatever that means.
Maybe related issues #11903, #11997.

Btw, your [installation instructions](https://github.com/spyder-ide/spyder/blob/master/CONTRIBUTING.md#installing-dependencies) for contributors end up installin pyqt twice, once from conda, and once on `pip install -e .` I guess this is because conda and pip package names are different, which I find quite weird.
I just confirmed that checking out tag v4.2.1 shows the same behaviour as reported, i.e. Spyder crash upon clicking "Dismiss" in the monitor rescale dialog.

I have confirmed that `handle_new_screen` hasn't changed between v4.2.1 and 42774fb, so the change seems to have happened elsewhere, but I have no chance of hunting this down and have to defer this to someone actually familiar with the code.
> I am not confident that a try/except around it all will actually fix this issue, as `handle_new_screen()` will basically become a no-op - I don't know the consequences of basically not "handling" a new screen.

From my, very limited, understanding of the code. Not much? Doesn't the "handling" we're talking about just tell the user to restart Spyder? So if we catch, it just doesn't tell the user to restart. Not ideal but a quick fix to the prevent the crash.

Thank you for helping with this @bilderbuchi - I will also try and get this set up and test what a try/except actually results in.
From the docstring ("Connect DPI signals for new screen") Spyder might end up with not taking the proper action when dpi changes. That could of course mean just a little degradation, in that the screen size and dpi are mismatched. Maybe a similar thing happens already, judging from my observed behaviour above.
Thanks to @bilderbuchi and @Caseyb87 for your diligent help on this; we really appreciate it.

> I am not confident that a try/except around it all will actually fix this issue, as handle_new_screen() will basically become a no-op - I don't know the consequences of basically not "handling" a new screen.

As far as I can tell, in terms of useful functionality it just shows the message and offers to restart, but @ccordoba12 would know more about that. However, as indicated by the fact that Spyder soon crashes thereafter in the `master` branch, I'm not sure this would avoid the underlying issue (but it would be worth a shot).

> I have confirmed that handle_new_screen hasn't changed between v4.2.1 and 42774fb, so the change seems to have happened elsewhere, but I have no chance of hunting this down and have to defer this to someone actually familiar with the code.

There have been many major changes between the branches and the underlying problem likely relates to something in the lower-level Qt code, so that's a very tricky affair indeed—though, if I were to guess, the crash clicking the message may be avoided but the crash further on may also be present on the 4.x release branch, just obscured by the former.

> Btw, your installation instructions for contributors end up installing pyqt twice, once from conda, and once on pip install -e . I guess this is because conda and pip package names are different, which I find quite weird.

Thanks for pointing that out. Actually, the two commands given in that section are supposed to be either/or (as are a number of others in that guide), i.e. you run the `conda` one if using conda or the `pip` one if using pip, but I can see how that could potentially be unclear based on the language written. I'll submit a quick PR later today to clarify that, thanks.

> From the docstring ("Connect DPI signals for new screen") Spyder might end up with not taking the proper action when dpi changes. That could of course mean just a little degradation, in that the screen size and dpi are mismatche

To take meaningful action Spyder needs to be restarted, after which it rescales the UI based on the monitor scale. But often times (at least in the past) this indication has been spurious, and the scaling being a little off is far superior to an outright crash to desktop.
> and the scaling being a little off is far superior to an outright crash to desktop.

indeed! especially if the crash happens because you log in with a machine with the wrong desktop resolution..

> Actually, the two commands given in that section are supposed to be either/or (as are a number of others in that guide), i.e. you run the conda one if using conda or the pip one if using pip, but I can see how that could potentially be unclear based on the language written. I'll submit a quick PR later today to clarify that, thanks.

ah, so do you mean in case you go with `conda install` for the requirements, you then have to use `python bootstrap.py` for running spyder, as only `pip install -e .` installs the `spyder` entrypoint you can call to open spyder? Even though the instructions mention "without installing it into your environment", which you just did? Or do you use another command to start spyder?

I think I want to say, the methods for installing dependencies and running spyder are also connected!

Also, NB, I did *not* install spyder-kernel 2.*, and Spyder points that out on startup. I can't rule out that that somehow triggers a crash.
I have not yet followed the rather "involved" [instructions](https://github.com/spyder-ide/spyder/blob/master/CONTRIBUTING.md#making-contributions-that-depend-on-pull-requests-in-spyder-kernels) for that. (Also, instead of using the standard git-submodule to pull that into the spyder repo, installing some random git-subrepo project into my .bashrc?? Ahhh, I think I'll pass :-P)
> ah, so do you mean in case you go with conda install for the requirements, you then have to use python bootstrap.py for running spyder, as only pip install -e . installs the spyder entrypoint you can call to open spyder?

Regardless of the install method, `python bootstrap.py` is the recommended and officially supported method for running Spyder from a development branch clone, as it does several additional steps including installing the development Spyder-Kernels and PyLS versions (which is likely why, if you're not using it, you're seeing the wrong Spyder-Kernels version), performs a few other tasks of varying importance and enables a number of additional startup options. I'm not sure exactly where in the contributing guide we specify using the entrypoint instead of the `bootstrap.py` script if using pip. 

> Even though the instructions mention "without installing it into your environment", which you just did?

Until recently, we had `pip install -r requirements/pip.txt` that performed the same function as the conda install instead of `pip install -e .`, so the entry point was not installed either way, but apparently this was changed to just use the `setup.py` deps, I presume for maintainability (due to the different package names/versions with pip/conda requiring different requirements files for pip and conda, reducing maintainability and increasing duplication). We can certainly update this sentence to be more clear.

> I think I want to say, the methods for installing dependencies and running spyder are also connected!

Loosely, but the intention in the contributing guide is to specify `python bootstrap.py` as the recommended, supported method of opening Spyder regardless of the install approach. If there's a way we can be clearer about that, we'd be happy to hear your feedback.

> Also, NB, I did not install spyder-kernel 2.*, and Spyder points that out on startup.

It should be installed for you automatically by `bootstrap.py`. Following those instructions should only be necessary if you're developing complex changes that require modifications in Spyder and Spyder-Kernels.

> (Also, instead of using the standard git-submodule to pull that into the spyder repo, installing some random git-subrepo project into my .bashrc?? Ahhh, I think I'll pass :-P)

Not my choice; some of the other devs have a preference for it over Git submodules. But you don't have to use it; AFAIK unless things have changed drastically, you can simply clone the spyder-kernels repo and `pip install -e .` it into whatever environment you have dev Spyder running it, which itself is only necessary if you make cross-dependent changes.
ah alright, I did not take that from the instructions, got distracted by the qualifier "without installing it into your environment" (I guess that part could be dropped/modified). I'll use the bootstrap method from now on! :-)

You don't specify using the entrypoint, it's what I used because that's the main option apparent to me when using `pip install -e .`.

> should only be necessary if you're developing complex changes that require modifications in Spyder and Spyder-Kernels.

Sorry, I read sloppily, on the second pass this is mentioned in the related docs already. I apologize for the confusion!


> I were to guess, the crash clicking the message may be avoided but the crash further on may also be present on the 4.x release branch, just obscured by the former.

I can confirm your hunch on 4.2.1. - if I return early from `handle_new_screen()` if `screen is None`, I can avoid the crash on clicking Dismiss, but the click in the menu bar still crashes Spyder (which generates no extra messages on the debug log).
Same behaviour on the master from above (42774fb).
> I can confirm your hunch on 4.2.1. - if I return early from handle_new_screen() if screen is None

I thought this was enough. What do you suggest instead?
@CAM-Gerlach suspected that the present bug (crudely avoided with an early return) actually masks another bug, which is a subsequent crash when clicking the menu bar. This is what I quickly checked for and could confirm.
So, even implementing the early return would not yield much progress as we would quickly encounter the second crashing bug. I can offer no suggestion for how to fix that second bug.

I cannot judge the consequences of the early return workaround - a try-except-pass pattern like is already present in `handle_new_screen` (especially without any logging) is a code smell to me, but obviously is considered OK in this codebase, so my intuition about a "proper" fix would surely be off.
Huh, I noticed that 
* after pressing the Dismiss button I get "QPaintDevice::metrics: Device has no metric information" in the console (but not with a timestamp, so presumably emitted in another way that through your normal logging
* the process apparently actually exits with 0, so it does not seem to be a "crash" per se - maybe Qt catches and swallows the exception?
I wonder if "screen" is being referenced elsewhere? Thus, a None value for screen ends up causing the follow-on crash when Spyder is interacted with?
Another interesting avenue of investigation: how does the screen argument of our failing function call end up being None instead of whatever appropriate object, causing the present bug?
so, `handle_new_screen` is invoked only by a connected QWindow.[screenChanged(QScreen screen)](https://doc.qt.io/qt-5/qwindow.html#screenChanged) signal: 
> This signal is emitted when a window's screen changes, either by being set explicitly with setScreen(), or automatically when the window's screen is removed.

I suspect that we get a `screen` argument of `None` if there is (currently? temporarily?) no screen or the screen is removed during the resolution change? Is that useful? Can we work around that?
 
I guess if the screen is `None` from the signal, the handler function could ignore the new nonexistent screen since probably when an actual screen is available the signal will trigger again (so adding a validation in the `handle_new_screen` function could help)
I think that is what I did by putting
```
if screen is None:
    return
```
in the beginning of the method. Unfortunately, the crash/close when clicking the menu then occurs - do you think those two are unrelated?
Um that's strange, and are you able to capture any kind of traceback in the console/terminal/cmd after Spyder crashes/closes when running with `--debug-info verbose`?

My guess is that somehow GUI objects are getting destroyed when disconnecting and reconnecting through RDP but not totally sure (maybe such destruction is related with the messages about `QPaintDevice::metrics: Device has no metric information`)
`python .\bootstrap.py` (for running master) does not recognize `--debug-info verbose`, only `--debug`. 
No traceback, hence my suspicion that any thrown exception gets swallowed by Qt internally.
When triggering the bug with the early return in place, pressing "Dismiss" (no crash) and then clicking the menu, the last couple of pages from the --debug log:
```
2021-01-13 20:23:29,863 [DEBUG] [spyder.plugins.editor.utils.autosave] -> Autosave triggered
2021-01-13 20:23:32,704 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:32,704 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:33,694 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:33,694 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:34,696 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:34,696 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:34,696 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:34,696 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:35,706 [INFO] [spyder.plugins.completion.languageserver.plugin] -> Automatic restart attempt 5 for python...
2021-01-13 20:23:35,706 [DEBUG] [spyder.plugins.editor.widgets.codeeditor] -> Stopping completion services for C:\Users\buchner\.spyder-py3-dev\temp.py
2021-01-13 20:23:35,706 [INFO] [spyder.plugins.completion.languageserver.plugin] -> Stopping LSP client for python...
2021-01-13 20:23:35,706 [INFO] [spyder.plugins.completion.languageserver.client] -> Stopping python client...
2021-01-13 20:23:35,706 [INFO] [spyder.plugins.completion.languageserver.plugin] -> Starting LSP client for python...
2021-01-13 20:23:35,716 [INFO] [spyder.plugins.completion.languageserver.client] -> Starting server: C:\Continuum\anaconda3\envs\spyder-14273\python.exe -m pyls --host 127.0.0.1 --port 2087 --tcp --check-parent-process --log-file C:\Users\buchner\.spyder-py3-dev\lsp_logs\server_python_17508.log -vv
2021-01-13 20:23:35,726 [INFO] [spyder.plugins.completion.languageserver.client] -> Starting transport for python: C:\Continuum\anaconda3\envs\spyder-14273\python.exe -u \\<snip>\Python\spyder\spyder\plugins\completion\languageserver\transport\main.py --folder C:\Users\buchner\.spyder-py3-dev\lsp_paths\root_path --transport-debug 3 --server-host 127.0.0.1 --server-port 2087  --zmq-in-port 59398 --zmq-out-port 59399 --external-server
2021-01-13 20:23:35,736 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP python client started!
2021-01-13 20:23:35,736 [INFO] [spyder.plugins.completion.languageserver.plugin] -> Restart failed!
2021-01-13 20:23:35,736 [INFO] [spyder.plugins.completion.languageserver.client] -> Stopping python client...
2021-01-13 20:23:36,696 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:36,696 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:37,688 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:37,688 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:37,688 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:37,688 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:38,709 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:38,709 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:39,689 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:39,689 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:40,700 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:40,700 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:40,700 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:40,700 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:41,706 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:41,706 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:42,696 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:42,696 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:43,696 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:43,696 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:43,696 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:43,696 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:45,498 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:45,499 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:45,688 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:45,688 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:46,699 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:46,699 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:46,700 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:46,700 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:47,709 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:47,709 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
2021-01-13 20:23:48,696 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:48,696 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:49,692 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:49,692 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
2021-01-13 20:23:49,692 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:49,692 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
2021-01-13 20:23:49,965 [DEBUG] [urllib3.connectionpool] -> Starting new HTTP connection (3): 127.0.0.1:46624
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
QPaintDevice::metrics: Device has no metric information
2021-01-13 20:23:50,710 [DEBUG] [spyder.plugins.completion.languageserver.client] -> Transport layer for python is down!!
2021-01-13 20:23:50,710 [DEBUG] [spyder.plugins.completion.languageserver.client] -> LSP server for python is down!!
```

You'll notice that the `QPaintDevice` messages have no timestamp/loglevel decoration, and that there is no traceback at the end, just the process ending (seemingly normally), the next line is just the shell prompt. I'm not sure if the LSP stuff is related, I think on some executions/runs the log is not spammed like that.
What happens if you wrap the section https://github.com/spyder-ide/spyder/blob/a3a35fd6ea769cdfc6b2308f8475bffe332914dc/spyder/app/mainwindow.py#L1535-L1541 in a try? 

I was just wondering if the `screen is None` is causing problems elsewhere, not just in handle_new_screen().

If the error is caught there then Spyder should (maybe?) just act like the screen never changed?
I was thinking something similar too @Caseyb87.

However, searching a little bit I found that maybe this could be Qt related:

* https://bugreports.qt.io/browse/QTBUG-84161?jql=text%20~%20%22Device%20has%20no%20metric%20information%22 (although is not reported for Windows)

Also regarding the messages about the LSP seems like that is also failing (the LSP crashes and is not being able to restart)
> `python .\bootstrap.py` (for running master) does not recognize `--debug-info verbose`, only `--debug`. 

Sorry it was unclear—you need to pass that option through to Spyder, not the bootstrap.py script, via ` python bootstrap.py -- --debug-info verbose`. The bootstrap.py script also has a debug option, which IIRC its equivalent, and its a relic from a time when Spyder itself didn't have that option and the semantics were different from what they are now, just an on-off switch instead of multiple levels.

python bootstrap.py -- --debug-info verbose

> Unfortunately, the crash/close when clicking the menu then occurs - do you think those two are unrelated?

@dalthviz 's Qt knowledge vastly exceeds mine, but FWIW, the evidence available is consistent with some problem caused by the user session's screen being disconnected for some time, and then another reconnected; as this is a pretty niche case on Windows (compared to *nix, with X11 forwarding and lots of screenless boxes) its possible that simply no one has reported it in this case. I'm not sure what exactly we can do about it at the Spyder level, as it seems its something at the Qt level given we're not seeing an exception traceback in Spyder and its instead just crashing to desktop, and the core GUI of the menu bar etc. is handled by Qt. I only see calls to the `screen` object (`self.screen` in the code relating to checking for a DPI change, which shouldn't be triggered in that scenario once its try-excepted.
> What happens if you wrap the section in a try?

@bilderbuchi, instead of doing that, please replace

```python
if not sys.platform == 'darwin':
```

by

```python
if False:
```

That would avoid executing that `if` block at all. I don't think it'll work, but at least we'll be sure if it has some influence on the crash.
> https://bugreports.qt.io/browse/QTBUG-84161?jql=text%20~%20%22Device%20has%20no%20metric%20information%22 

I also found this bug while searching for possible solutions. So this problem looks like a bug in Qt, and in that case we won't be able to do anything about it.

Basically, it seems the screen is removed by Windows after you logout from Remote desktop and recreated again after you start a new session.
> 
> 
> > https://bugreports.qt.io/browse/QTBUG-84161?jql=text%20~%20%22Device%20has%20no%20metric%20information%22
> 
> I also found this bug while searching for possible solutions. So this problem looks like a bug in Qt, and in that case we won't be able to do anything about it.

I'll be interested to see if avoiding that `if` block works. But I guess what you're saying is that even if Spyder is changed to "handle" `screen is None`, Qt (and thus also Spyder) may still crash? 

This should, of course, be reported to Qt. Probably best by one of the Spyder developers?

> Basically, it seems the screen is removed by Windows after you logout from Remote desktop and recreated again after you start a new session.

But only if you start a new session in from a new local machine. That's the weird part. It all works fine if you start the new session from the same local machine (or at least same display). 


> if False:

with this change and the early return in place, I get neither the screen change dialog, nor can I trigger any crashes anymore.

> It all works fine if you start the new session from the same local machine (or at least same display).

The repro procedure I have been using is using a laptop (1080p display) with an external monitor attached (1680x1050) to connect to a remote PC (1080p display). By switching on either only the laptop screen or only the external monitor when connecting to the remote PC, I can force a change in resolution and trigger the bug.
> 
> 
> > if False:
> 
> with this change and the early return in place, I get neither the screen change dialog, nor can I trigger any crashes anymore.

Fantastic! So Spyder can handle this (by just ignoring it), even if it is a bug with Qt.

> > It all works fine if you start the new session from the same local machine (or at least same display).
> 
> The repro procedure I have been using is using a laptop (1080p display) with an external monitor attached (1680x1050) to connect to a remote PC (1080p display). By switching on either only the laptop screen or only the external monitor when connecting to the remote PC, I can force a change in resolution and trigger the bug.

Ah OK - that makes sense, it's the display (not the device) that is important. 

I've managed to get a dev version of Spyder set up myself too. I can confirm @bilderbuchi 's observations in that skipping that block entirely results in no crashing. 

Additionally, if lines 1556-1558 are commented out:
https://github.com/spyder-ide/spyder/blob/72e9952d5e3f6b7c9d32c636f319e56dd1e35659/spyder/app/mainwindow.py#L1556-L1558

then Spyder does not crash but will throw the following error:

```python 
if self.current_dpi != screen.logicalDotsPerInch():
AttributeError: 'MainWindow' object has no attribute 'current_dpi'
```

I tried to add some condition code to prevent those lines being executed but couldn't get it to work. Guess I'm checking the wrong thing. For example:

```python
if not sys.platform == 'darwin':
            window = self.window().windowHandle()
            if window.screen() is not None:
                        window.screenChanged.connect(self.handle_new_screen)
                        self.screen = self.window().windowHandle().screen()
                        self.current_dpi = self.screen.logicalDotsPerInch()
                        self.screen.logicalDotsPerInchChanged.connect(
                            self.show_dpi_change_message)
```
did not work. Spyder still showed the dpi change messages and then crashed after clicking dismiss.
Additionally, I think I figured out why the dpi change message is being shown twice. 

https://github.com/spyder-ide/spyder/blob/72e9952d5e3f6b7c9d32c636f319e56dd1e35659/spyder/app/mainwindow.py#L1554

calls handle_new_screen which can then call `show_dpi_change_message`

https://github.com/spyder-ide/spyder/blob/72e9952d5e3f6b7c9d32c636f319e56dd1e35659/spyder/app/mainwindow.py#L1575-L1576

but

https://github.com/spyder-ide/spyder/blob/72e9952d5e3f6b7c9d32c636f319e56dd1e35659/spyder/app/mainwindow.py#L1557-L1558

also calls `show_dpi_change_message`.

Perhaps this behaviour is OK in normal situations but I guess due to this particular issue, we end up with the `show_dpi_change_message` being shown twice.
Thanks @bilderbuchi and @Caseyb87 for the info regarding this! Seems like now we have more clues about how to prevent the crash to happen :) Maybe the condition to check needs to be regarding if we are currently running in a RDP session (although I'm unsure how we can achieve such validation :/)
I can reproduce something similar with Version 4.1.5 AND 4.2.0  on Windows 10 20H2Build 19042.746.

Sypder is installed through Anaconda.

I opened Spyder on my secondary screen, through thunderbolt dock.

Unplugged the laptop. Spyder opens on a non-existing screen. Some dialogs are shown (the update to 4.2.0) dialogue pops up on the only aptop screen, but as soon as the main window opens (animation zooming out of task bar sometimes shows the window moving to the screen on the left), spyder crashes and disappears from the task bar.

No processes to be seen in task explorer.

So, no remote desktop, only physical screens, but similar issues. 

`Spyder --debug-info verbose --report-segfault`

It crashes right after the urrlib3 call (left out lots of lines!)
```
...
...
2021-02-02 10:37:09,809 [DEBUG] [spyder.plugins.completion.fallback.actor] -> Perform request textDocument/documentSymbol with id 2
2021-02-02 10:37:09,835 [DEBUG] [spyder.plugins.completion.plugin] -> Completion plugin: Request 1 Got response from lsp
2021-02-02 10:37:09,836 [DEBUG] [spyder.plugins.completion.plugin] -> Completion plugin: Request 1 removed
2021-02-02 10:37:09,837 [DEBUG] [spyder.plugins.completion.plugin] -> Gather responses for textDocument/foldingRange
2021-02-02 10:37:09,969 [DEBUG] [spyder.plugins.completion.plugin] -> Completion plugin: Request 2 Got response from lsp
2021-02-02 10:37:09,969 [DEBUG] [spyder.plugins.completion.plugin] -> Completion plugin: Request 2 removed
2021-02-02 10:37:09,969 [DEBUG] [spyder.plugins.completion.plugin] -> Gather responses for textDocument/documentSymbol
2021-02-02 10:37:11,315 [DEBUG] [urllib3.connectionpool] -> Starting new HTTP connection (3): 127.0.0.1:46624
2021-02-02 10:37:13,332 [DEBUG] [spyder.plugins.completion.plugin] -> Completion plugin: Request 0 Got response from kite
2021-02-02 10:37:13,337 [DEBUG] [urllib3.connectionpool] -> Starting new HTTP connection (4): 127.0.0.1:46624
```

No segfault-reporter popping up!
Just downgraded to 3.3.6.

Windows stayed open, i could move it to the laptop screen. Closed Sypder 3.3.6 gracefully.

updated to 4.1.5.

segfault reporter pops up. then crash, spyder goes away

Tried --reset and --defaults, but 4.1.5 still always crashes some seconds after showing up on the main screen.




Resolving patch:
diff --git a/spyder/app/mainwindow.py b/spyder/app/mainwindow.py
--- a/spyder/app/mainwindow.py
+++ b/spyder/app/mainwindow.py
@@ -1332,7 +1332,6 @@ def post_visible_setup(self):
         # Fixes spyder-ide/spyder#3887.
         self.menuBar().raise_()
 
-        # Handle DPI scale and window changes to show a restart message
         # Handle DPI scale and window changes to show a restart message.
         # Don't activate this functionality on macOS because it's being
         # triggered in the wrong situations.
@@ -1340,85 +1339,78 @@ def post_visible_setup(self):
         if not sys.platform == 'darwin':
             window = self.window().windowHandle()
             window.screenChanged.connect(self.handle_new_screen)
-            self.screen = self.window().windowHandle().screen()
-            self.current_dpi = self.screen.logicalDotsPerInch()
-            self.screen.logicalDotsPerInchChanged.connect(
+            screen = self.window().windowHandle().screen()
+            self.current_dpi = screen.logicalDotsPerInch()
+            screen.logicalDotsPerInchChanged.connect(
                 self.show_dpi_change_message)
 
         # Notify that the setup of the mainwindow was finished
         self.is_setting_up = False
         self.sig_setup_finished.emit()
 
-    def handle_new_screen(self, screen):
+    def handle_new_screen(self, new_screen):
         """Connect DPI signals for new screen."""
-        try:
-            self.screen.logicalDotsPerInchChanged.disconnect(
-                self.show_dpi_change_message)
-        except (TypeError, RuntimeError):
-            # See spyder-ide/spyder#11903 and spyder-ide/spyder#11997
-            pass
-        self.screen = screen
-        self.screen.logicalDotsPerInchChanged.connect(
-            self.show_dpi_change_message)
-        if self.current_dpi != screen.logicalDotsPerInch():
-            self.show_dpi_change_message(screen.logicalDotsPerInch())
-
-    def show_dpi_change_message(self, dpi):
-        """Show message to restart Spyder since the DPI scale changed."""
-        self.screen.logicalDotsPerInchChanged.disconnect(
-            self.show_dpi_change_message)
-
-        if self.current_dpi == dpi:
-            # Reconnect DPI scale changes to show a restart message
-            self.screen.logicalDotsPerInchChanged.connect(
-                self.show_dpi_change_message)
-            return
-        if not self.show_dpi_message:
-            return
-
-        # Check the window state to not show the message if the window
-        # is in fullscreen mode.
-        window = self.window().windowHandle()
-        if (window.windowState() == Qt.WindowFullScreen and
-                sys.platform == 'darwin'):
-            return
-
-        dismiss_box = QCheckBox(
-            _("Hide this message during the current session")
-        )
+        if new_screen is not None:
+            new_screen_dpi = new_screen.logicalDotsPerInch()
+            if self.current_dpi != new_screen_dpi:
+                self.show_dpi_change_message(new_screen_dpi)
+            else:
+                new_screen.logicalDotsPerInchChanged.connect(
+                    self.show_dpi_change_message)
 
-        msgbox = QMessageBox(self)
-        msgbox.setIcon(QMessageBox.Warning)
-        msgbox.setText(
-            _("A monitor scale change was detected. <br><br>"
-              "We recommend restarting Spyder to ensure that it's properly "
-              "displayed. If you don't want to do that, please be sure to "
-              "activate the option<br><br><tt>Enable auto high DPI scaling"
-              "</tt><br><br>in <tt>Preferences > General > Interface</tt>, "
-              "in case Spyder is not displayed correctly.<br><br>"
-              "Do you want to restart Spyder?"))
-        restart_button = msgbox.addButton(_('Restart now'), QMessageBox.NoRole)
-        dismiss_button = msgbox.addButton(_('Dismiss'), QMessageBox.NoRole)
-        msgbox.setCheckBox(dismiss_box)
-        msgbox.setDefaultButton(dismiss_button)
-        msgbox.exec_()
-
-        if dismiss_box.isChecked():
+    def handle_dpi_change_response(self, result, dpi):
+        """Handle dpi change message dialog result."""
+        if self.dpi_change_dismiss_box.isChecked():
             self.show_dpi_message = False
-
-        if msgbox.clickedButton() == restart_button:
-            # Activate HDPI auto-scaling option since is needed for a proper
-            # display when using OS scaling
+            self.dpi_change_dismiss_box = None
+        if result == 0:  # Restart button was clicked
+            # Activate HDPI auto-scaling option since is needed for a
+            # proper display when using OS scaling
             CONF.set('main', 'normal_screen_resolution', False)
             CONF.set('main', 'high_dpi_scaling', True)
             CONF.set('main', 'high_dpi_custom_scale_factor', False)
             self.restart()
         else:
-            # Reconnect DPI scale changes to show a restart message
-            # also update current dpi for future checks
+            # Update current dpi for future checks
             self.current_dpi = dpi
-            self.screen.logicalDotsPerInchChanged.connect(
-                self.show_dpi_change_message)
+
+    def show_dpi_change_message(self, dpi):
+        """Show message to restart Spyder since the DPI scale changed."""
+        if not self.show_dpi_message:
+            return
+
+        if self.current_dpi != dpi:
+            # Check the window state to not show the message if the window
+            # is in fullscreen mode.
+            window = self.window().windowHandle()
+            if (window.windowState() == Qt.WindowFullScreen and
+                    sys.platform == 'darwin'):
+                return
+
+            self.dpi_change_dismiss_box = QCheckBox(
+                _("Hide this message during the current session"),
+                self
+            )
+
+            msgbox = QMessageBox(self)
+            msgbox.setIcon(QMessageBox.Warning)
+            msgbox.setText(
+                _
+                ("A monitor scale change was detected. <br><br>"
+                 "We recommend restarting Spyder to ensure that it's properly "
+                 "displayed. If you don't want to do that, please be sure to "
+                 "activate the option<br><br><tt>Enable auto high DPI scaling"
+                 "</tt><br><br>in <tt>Preferences > General > Interface</tt>, "
+                 "in case Spyder is not displayed correctly.<br><br>"
+                 "Do you want to restart Spyder?"))
+            msgbox.addButton(_('Restart now'), QMessageBox.NoRole)
+            dismiss_button = msgbox.addButton(
+                _('Dismiss'), QMessageBox.NoRole)
+            msgbox.setCheckBox(self.dpi_change_dismiss_box)
+            msgbox.setDefaultButton(dismiss_button)
+            msgbox.finished.connect(
+                lambda result: self.handle_dpi_change_response(result, dpi))
+            msgbox.open()
 
     def set_window_title(self):
         """Set window title."""


Test patch: