Repository: fohrloop/wakepy

Issue:
v.0.10.1 cannot inhibit on KDE Plasma 6.3.4 (keep.running mode)
cli returns this:

```
Wakepy could not activate the "keep.running" mode. This might occur because of a
bug or because your current platform is not yet supported or your system is
missing required software.

Check if there is already a related issue in the issue tracker at
https://github.com/fohrloop/wakepy/issues/ and if not, please create a new one.

Include the following:
- wakepy version: 0.10.1
- Mode: keep.running
- Python version: 3.12.8 (main, Dec 19 2024, 14:33:20) [Clang 18.1.8 ]
- os.name: posix
- sys.platform: linux
- platform.system(): Linux
- platform.release(): 6.14.2-arch1-1
- platform.machine(): x86_64
- sysconfig.get_platform(): linux-x86_64
- DESKTOP_SESSION: plasma
- (/etc/os-release) NAME: "Arch Linux"
- (/etc/os-release) PRETTY_NAME: "Arch Linux"
- (/etc/os-release) ID: arch
- (/etc/os-release) BUILD_ID: rolling
- (/etc/os-release) PRIVACY_POLICY_URL:
"https://terms.archlinux.org/docs/privacy-policy/"
```

python `with keep.running()` outputs this warning:
```
Method usage results, in order (highest priority first):
[(FAIL @PLATFORM_SUPPORT, caffeinate, ""), (FAIL @REQUIREMENTS, org.freedesktop.PowerManagement, "Running on KDE but could not detect KDE Plasma version."), (FAIL @ACTIVATION, org.gnome.SessionManager, "DBusErrorResponse(Message(Header(<Endianness.little: 1>, <MessageType.error: 3>, <MessageFlag.no_reply_expected: 1>, 1, 32, 4294967295, fields={<HeaderFields.reply_serial: 5>: 2, <HeaderFields.sender: 7>: 'org.freedesktop.DBus', <HeaderFields.error_name: 4>: 'org.freedesktop.DBus.Error.ServiceUnknown', <HeaderFields.signature: 8>: 's', <HeaderFields.destination: 6>: ':1.2774192'}), ('The name is not activatable',)))"), (FAIL @PLATFORM_SUPPORT, SetThreadExecutionState, "")]
  warnings.warn(result.get_failure_text(), ActivationWarning)
```


I've got Kde Plasma 6.3.4

Hints from issue comments:
@fohrloop: Thanks for the bug report! The output of the `with keep.running()` is especially useful. 

On KDE, wakepy should use the [org.freedesktop.PowerManagement](https://wakepy.readthedocs.io/stable/methods-reference.html#org-freedesktop-powermanagement) for the keep.running mode. But because KDE < 5.12.90 had a bug which made the keep.running mode also prevent screen lock, wakepy refuses to use the org.freedesktop.PowerManagement with KDE < 5.12.90 (as that would be a security issue. Otherwise wakepy could just blindly try to use it).

The KDE Plasma version is checked with `src/wakepy/methods/freedesktop.py::_get_kde_plasma_version()`. And that basically runs:

```python
    out = subprocess.getoutput("plasmashell --version")
    mtch = re.match("plasmashell ([0-9][0-9.]*)$", out)
```

I'm guessing that 

```
plasmashell --version
``` 
does not work on your system. Can you confirm that? Are you aware of any command which would tell the version of your KDE Plasma from command line (or DBus)?
@fohrloop: I'm just testing with the latest Manjaro with KDE Plasma, and there the plasmashell command works:

![Image](https://github.com/user-attachments/assets/af39911b-9525-4f45-ac37-358e90725334)

Are you running some sort of minimal install? How did you confirm the system is actually running KDE Plasma 6.3.4?  What kind of output do you get for `ps -e | grep -i plasma` ? 

```
$ ps -e | grep -i plasma                                                                        
   1275 ?        00:00:00 startplasma-x11
   1401 ?        00:00:22 plasmashell
```@materemias: Hi,

entering the command you mentioned I see
```
> plasmashell --version
plasmashell 6.3.4
QThreadStorage: entry 3 destroyed before end of thread 0x581f80656540
QThreadStorage: entry 2 destroyed before end of thread 0x581f80656540
```
so i guess the non normal exit causes the issue

the second command returns
```
 ps -e | grep -i plasma
   6303 tty1     00:00:00 startplasma-way
   6676 ?        02:54:31 plasmashell
 115790 ?        00:00:14 plasma-browser-
```@materemias: this seems to be a [bug in Qt ](https://bugreports.qt.io/browse/QTBUG-133206), maybe you could read the output even if the exit code is not 0@fohrloop: @materemias well that simplifies the fix greatly! Thanks for providing the details. Clearly you have plasmashell running and KDE Plasma 6.3.4. in use. I wonder if the "$" in the end of the regex is actually the problem. 

What's the output for you for

```python
>>> import subprocess
>>> subprocess.getoutput("plasmashell --version")
```
?@materemias: ```
QThreadStorage: entry 3 destroyed before end of thread 0x61b4ee73d540
QThreadStorage: entry 2 destroyed before end of thread 0x61b4ee73d540
plasmashell 6.3.4
```
maybe worth checking for a line starting with plasmashell@fohrloop: Thanks! I've created a PR for this: https://github.com/fohrloop/wakepy/pull/417 

Looking forward for merging and publishing as soon as possible.@fohrloop: I've packaged the changes in wakepy 0.10.2 which I just published to PyPI. I'm assuming that will fix the issue.

Waiting for a confirmation before closing this.@fohrloop: @materemias did you have time to test the version 0.10.2?@materemias: yes, it is working great, thanks for the fix!

Solution patch:
diff --git a/docs/source/changelog.md b/docs/source/changelog.md
index 53b36eb..627ce98 100644
--- a/docs/source/changelog.md
+++ b/docs/source/changelog.md
@@ -3,6 +3,9 @@
 ## wakepy x.x.x
 🗓️ unreleased
 
+### 🐞 Bug fixes
+- Fix keep.running mode on KDE Plasma 6.x.x, when `plasmashell --version` output contained extra lines, such as "QThreadStorage: entry 3 destroyed before end of thread 0x61b4ee73d540" ([#417](https://github.com/fohrloop/wakepy/pull/417))
+
 ### 👷 Maintenance
 - Disallow TODO comments ([#410](https://github.com/fohrloop/wakepy/pull/410))
 
diff --git a/src/wakepy/methods/freedesktop.py b/src/wakepy/methods/freedesktop.py
index e881dcd..bf6ba54 100644
--- a/src/wakepy/methods/freedesktop.py
+++ b/src/wakepy/methods/freedesktop.py
@@ -233,12 +233,16 @@ def _get_kde_plasma_version() -> Optional[Tuple[int, ...]]:
         If no KDE Plasma is found, returns None.
 
     """
-    # returns for example 'plasmashell 5.27.9'
-    out = subprocess.getoutput("plasmashell --version")
-    mtch = re.match("plasmashell ([0-9][0-9.]*)$", out)
-    if mtch is None:
+    # returns for example 'plasmashell 5.27.9' but may also return some
+    # additional text. See: https://github.com/fohrloop/wakepy/issues/415
+    output = subprocess.getoutput("plasmashell --version")
+    for line in output.splitlines():
+        match = re.match(".*plasmashell ([0-9.]*).*", line)
+        if match:
+            break
+    else:
         return None
 
-    versionstring = mtch.group(1)
+    versionstring = match.group(1)
     versiontuple = tuple(int(x) for x in versionstring.split("."))
     return versiontuple


Test patch:
diff --git a/tests/unit/test_methods/test_freedesktop.py b/tests/unit/test_methods/test_freedesktop.py
index 51845d4..1767d17 100644
--- a/tests/unit/test_methods/test_freedesktop.py
+++ b/tests/unit/test_methods/test_freedesktop.py
@@ -249,6 +249,37 @@ class TestGetKDEPlasmaVersion:
         ):
             assert _get_kde_plasma_version() == (4, 5, 6)
 
+    def test_success_with_additional_text_same_line(self):
+        with patch(
+            "wakepy.methods.freedesktop.subprocess.getoutput",
+            return_value="FOO plasmashell 4.5.6post-123 BAR",
+        ):
+            assert _get_kde_plasma_version() == (4, 5, 6)
+
+    def test_success_qthread_storage_pre(self):
+        # Test for bug: https://github.com/fohrloop/wakepy/issues/415
+        with patch(
+            "wakepy.methods.freedesktop.subprocess.getoutput",
+            return_value="QThreadStorage: entry 3 destroyed before end of thread 0x61b4ee73d540\nQThreadStorage: entry 2 destroyed before end of thread 0x61b4ee73d540\nplasmashell 6.3.4",  # noqa: E501
+        ):
+            assert _get_kde_plasma_version() == (6, 3, 4)
+
+    def test_success_qthread_storage_post(self):
+        # Test for bug: https://github.com/fohrloop/wakepy/issues/415
+        with patch(
+            "wakepy.methods.freedesktop.subprocess.getoutput",
+            return_value="plasmashell 6.3.4\nQThreadStorage: entry 3 destroyed before end of thread 0x61b4ee73d540\nQThreadStorage: entry 2 destroyed before end of thread 0x61b4ee73d540",  # noqa: E501
+        ):
+            assert _get_kde_plasma_version() == (6, 3, 4)
+
+    def test_success_qthread_storage_mid(self):
+        # Test for bug: https://github.com/fohrloop/wakepy/issues/415
+        with patch(
+            "wakepy.methods.freedesktop.subprocess.getoutput",
+            return_value="QThreadStorage: entry 3 destroyed before end of thread 0x61b4ee73d540\nplasmashell 6.3.4\nQThreadStorage: entry 2 destroyed before end of thread 0x61b4ee73d540",  # noqa: E501
+        ):
+            assert _get_kde_plasma_version() == (6, 3, 4)
+
     def test_bad_output(self):
         with patch(
             "wakepy.methods.freedesktop.subprocess.getoutput",