Repository: qtile/qtile

Issue:
Scratchpad failed to unsubscribe
# Issue description
I opened previously #1936
I did test with latest commit at the moment with default config and added scratchpad group to the end.

  ```python
...
# java that happens to be on java's whitelist.
wmname = "LG3D"

# Scratchpad
from libqtile.config import ScratchPad, DropDown

groups.append(
    ScratchPad("scratchpad", [
        DropDown("term", "st", x=0.25, y=0.25, width=0.5, height=0.5, on_focus_lost_hide=True),
    ]),
)

keys.extend([
        Key([], 'F12', lazy.group['scratchpad'].dropdown_toggle("term")),
])
  ```

# Qtile version

commit 4e741da4b8126eb78fb9508e916cab350647f08e

# Details

To reproduce - logout with `Ctrl Super q` (lazy.shutdown). And then log messages will appear on next login.

 ```bash
2020-10-25 13:53:26,699 ERROR libqtile scratchpad.py:unsubscribe():L134 Scratchpad failed to unsubscribe on_focus_change: Tried to unsubscribe a hook that was not currently subscribed
Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/libqtile/hook.py", line 365, in _subscribe
    lst.remove(func)
ValueError: list.remove(x): x not in list

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/libqtile/scratchpad.py", line 132, in unsubscribe
    hook.unsubscribe.client_focus(self.on_focus_change)
  File "/usr/lib/python3.8/site-packages/libqtile/hook.py", line 235, in client_focus
    return self._subscribe("client_focus", func)
  File "/usr/lib/python3.8/site-packages/libqtile/hook.py", line 367, in _subscribe
    raise utils.QtileError(
libqtile.utils.QtileError: Tried to unsubscribe a hook that was not currently subscribed
2020-10-25 13:53:26,700 ERROR libqtile scratchpad.py:unsubscribe():L139 Scratchpad failed to unsubscribe on_focus_change: Tried to unsubscribe a hook that was not currently subscribed
Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/libqtile/hook.py", line 365, in _subscribe
    lst.remove(func)
ValueError: list.remove(x): x not in list

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/libqtile/scratchpad.py", line 137, in unsubscribe
    hook.unsubscribe.setgroup(self.on_focus_change)
  File "/usr/lib/python3.8/site-packages/libqtile/hook.py", line 115, in setgroup
    return self._subscribe("setgroup", func)
  File "/usr/lib/python3.8/site-packages/libqtile/hook.py", line 367, in _subscribe
    raise utils.QtileError(
libqtile.utils.QtileError: Tried to unsubscribe a hook that was not currently subscribed
  ```


Discussion:
I can confirm this, I have a lot of these messages in my log, too.
So.... even with commit b9489850becd7977143b05a48d406c12ce83d7af, you get these
messages, but _only_ after you completely shut down Qtile, then start it up
again?

On 201025 02:37, tcld wrote:
>I can confirm this, I have a lot of these messages in my log, too.
>
>-- 
>You are receiving this because you are subscribed to this thread.
>Reply to this email directly or view it on GitHub:
>https://github.com/qtile/qtile/issues/1942#issuecomment-716119148

Yes, I am using that commit. The last time the message was logged after a simple restart, not a shutdown/restart of Qtile. I'll have to see if I can find a way to reproduce it, though, since it doesn't happen on every restart for me.

Note: The steps given by @leschalu above work to reproduce the issue. (shutdown+restart)
I wonder in that case if it's due to scratchpad windows staying in their
scratchpad state across restarts. The new qtile instance probably has the
scratchpads running as before, except because it wasn't this instance that
actually set up the scratchpad, no hooks were subscribed. This only applies to
dropdown windows whose `on_focus_lost_hide` is True. Does it only happen when a
scratchpad dropdown is visible when the restart is triggered, and now when they
are all hidden on the scratchpad group?

On 201025 05:02, tcld wrote:
>Yes, I am using that commit. The last time the message was logged after a simple restart, not a shutdown/restart of Qtile. I'll have to see if I can find a way to reproduce it, though, since it doesn't happen on every restart for me.
>
>-- 
>You are receiving this because you commented.
>Reply to this email directly or view it on GitHub:
>https://github.com/qtile/qtile/issues/1942#issuecomment-716136716

For me it happens even if the scratchpad isn't visible at the time of restart. I probably had to open it once before, though.
@m-col During session no errors which is good. The log will be empty if scratchpad window wasn't opened at all. To reproduce it has to be opened at least once. Session restart won't trigger it only shutdown(through lazy.shutdown or killall -u $USER). And it's doesn't matter if the terminal was hidden or not.
This happens as we subscribe to client_focus and setgroup only when on_focus_lost_hide is set but we unsubscribe uncoditionally. We have check if on_focus_lost_hide is set before trying to call unsubscribe.
@senthilbaboo are you on master (or at least after b9489850becd7977143b05a48d406c12ce83d7af)? That commit made the subscribe conditional upon `on_focus_lost_hide` but despite that it seems the error message sometimes still appears.
Sorry about that. I just saw the thing that I was pointing out got fixed.
No probs! Although this is low priority, I'm stumped by it and not sure how to identify the problem :D 
@leschalu @tcld do you get this on current master (aa66a3debc64557db39f79d2df8d44573be5227e)? I'm actually no longer able to reproduce, so maybe it got fixed as a side effect of a recent refactoring commit. 
With latest master it still fills the same log but only when you logout and scratchpad window hidden. Doesn't matter if logout with killall -u $USER or lazy.shutdown()

Resolving patch:
diff --git a/libqtile/scratchpad.py b/libqtile/scratchpad.py
--- a/libqtile/scratchpad.py
+++ b/libqtile/scratchpad.py
@@ -18,8 +18,7 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
-from libqtile import group, hook, utils, window
-from libqtile.log_utils import logger
+from libqtile import group, hook, window
 
 
 class WindowVisibilityToggler:
@@ -127,17 +126,9 @@ def hide(self):
 
     def unsubscribe(self):
         """unsubscribe all hooks"""
-        if self.on_focus_lost_hide:
-            try:
-                hook.unsubscribe.client_focus(self.on_focus_change)
-            except utils.QtileError as err:
-                logger.exception("Scratchpad failed to unsubscribe on_focus_change"
-                                 ": %s" % err)
-            try:
-                hook.unsubscribe.setgroup(self.on_focus_change)
-            except utils.QtileError as err:
-                logger.exception("Scratchpad failed to unsubscribe on_focus_change"
-                                 ": %s" % err)
+        if self.on_focus_lost_hide and (self.visible or self.shown):
+            hook.unsubscribe.client_focus(self.on_focus_change)
+            hook.unsubscribe.setgroup(self.on_focus_change)
 
     def on_focus_change(self, *args, **kwargs):
         """


Test patch: