Repository: labelle-org/labelle (language: python)

Issue:
Arch AUR installation fails during testing
# Installation log
```bash
$ yay -S labelle
AUR Explicit (1): labelle-1.3.1-1
:: PKGBUILD up to date, skipping download: labelle
  1 labelle                          (Build Files Exist)
==> Packages to cleanBuild?
==> [N]one [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)
==> 
  1 labelle                          (Build Files Exist)
==> Diffs to show?
==> [N]one [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)
==> 
==> Making package: labelle 1.3.1-1 (Sun Oct 13 17:54:16 2024)
==> Retrieving sources...
  -> Found labelle-1.3.1.tar.gz
  -> Found 91-dymo-labelmanager-pnp.rules
  -> Found dymo-labelmanager-pnp.conf
==> WARNING: Skipping verification of source file PGP signatures.
==> Validating source files with sha512sums...
    labelle-1.3.1.tar.gz ... Passed
    91-dymo-labelmanager-pnp.rules ... Passed
    dymo-labelmanager-pnp.conf ... Passed
:: (1/1) Parsing SRCINFO: labelle
==> Making package: labelle 1.3.1-1 (Sun Oct 13 17:54:16 2024)
==> Checking runtime dependencies...
==> Checking buildtime dependencies...
==> Retrieving sources...
  -> Found labelle-1.3.1.tar.gz
  -> Found 91-dymo-labelmanager-pnp.rules
  -> Found dymo-labelmanager-pnp.conf
==> Validating source files with sha512sums...
    labelle-1.3.1.tar.gz ... Passed
    91-dymo-labelmanager-pnp.rules ... Passed
    dymo-labelmanager-pnp.conf ... Passed
==> Removing existing $srcdir/ directory...
==> Extracting sources...
  -> Extracting labelle-1.3.1.tar.gz with bsdtar
bsdtar: Failed to set default locale
==> Starting prepare()...
==> Sources are ready.
==> Making package: labelle 1.3.1-1 (Sun Oct 13 17:54:18 2024)
==> Checking runtime dependencies...
==> Checking buildtime dependencies...
==> WARNING: Using existing $srcdir/ tree
==> Removing existing $pkgdir/ directory...
==> Starting build()...
* Getting build dependencies for wheel...
* Building wheel...
Successfully built labelle-1.3.1-py3-none-any.whl
==> Starting check()...
Running unit tests
======================================================================= test session starts =======================================================================
platform linux -- Python 3.12.7, pytest-8.3.3, pluggy-1.5.0
PySide6 6.7.3 -- Qt runtime 6.7.3 -- Qt compiled 6.7.3
rootdir: /home/sakulflee/.cache/yay/labelle/src/labelle-1.3.1
configfile: pyproject.toml
plugins: typeguard-4.3.0, qt-4.4.0, image-diff-0.0.11
collected 49 items                                                                                                                                                

src/labelle/gui/tests/test_gui.py dbus-daemon[40560]: [session uid=1000 pid=40560] Activating service name='org.freedesktop.portal.Desktop' requested by ':1.0' (uid=1000 pid=40580 comm="/usr/bin/python /usr/bin/pytest src")
dbus-daemon[40560]: [session uid=1000 pid=40560] Activating service name='org.freedesktop.portal.Documents' requested by ':1.1' (uid=1000 pid=40585 comm="/usr/lib/xdg-desktop-portal")
dbus-daemon[40560]: [session uid=1000 pid=40560] Activating service name='org.freedesktop.impl.portal.PermissionStore' requested by ':1.2' (uid=1000 pid=40591 comm="/usr/lib/xdg-document-portal")
dbus-daemon[40560]: [session uid=1000 pid=40560] Successfully activated service 'org.freedesktop.impl.portal.PermissionStore'
dbus-daemon[40560]: [session uid=1000 pid=40560] Successfully activated service 'org.freedesktop.portal.Documents'
fusermount3: failed to access mountpoint /run/user/1000/doc: Permission denied
error: fuse init failed: Can't mount path /run/user/1000/doc
dbus-daemon[40560]: [session uid=1000 pid=40560] Activating service name='org.freedesktop.impl.portal.desktop.kde' requested by ':1.1' (uid=1000 pid=40585 comm="/usr/lib/xdg-desktop-portal")
Detected locale "C" with character encoding "ANSI_X3.4-1968", which is not UTF-8.
Qt depends on a UTF-8 locale, and has switched to "C.UTF-8" instead.
If this causes problems, reconfigure your locale. See the locale(1) manual
for more information.
dbus-daemon[40560]: [session uid=1000 pid=40560] Successfully activated service 'org.freedesktop.impl.portal.desktop.kde'
dbus-daemon[40560]: [session uid=1000 pid=40560] Successfully activated service 'org.freedesktop.portal.Desktop'
F                                                                                                                         [  2%]
src/labelle/lib/render_engines/tests/test_render_engines.py ................................................                                                [100%]

============================================================================ FAILURES =============================================================================
________________________________________________________________________ test_main_window _________________________________________________________________________

qtbot = <pytestqt.qtbot.QtBot object at 0x75c3f95805f0>

    def test_main_window(qtbot):
        widget = LabelleWindow()
>       qtbot.addWidget(widget)

src/labelle/gui/tests/test_gui.py:9: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pytestqt.qtbot.QtBot object at 0x75c3f95805f0>, widget = <labelle.gui.gui.LabelleWindow object at 0x75c3f95cafd0>

    def addWidget(self, widget, *, before_close_func=None):
        """
        Adds a widget to be tracked by this bot. This is not required, but will ensure that the
        widget gets closed by the end of the test, so it is highly recommended.
    
        :param QWidget widget:
            Widget to keep track of.
    
        :kwparam before_close_func:
            A function that receives the widget as single parameter, which is called just before
            the ``.close()`` method gets called.
    
        .. note:: This method is also available as ``add_widget`` (pep-8 alias)
        """
        if not isinstance(widget, qt_api.QtWidgets.QWidget):
>           raise TypeError("Need to pass a QWidget to addWidget!")
E           TypeError: Need to pass a QWidget to addWidget!

/usr/lib/python3.12/site-packages/pytestqt/qtbot.py:188: TypeError
===================================================================== short test summary info =====================================================================
FAILED src/labelle/gui/tests/test_gui.py::test_main_window - TypeError: Need to pass a QWidget to addWidget!
================================================================== 1 failed, 48 passed in 1.21s ===================================================================
==> ERROR: A failure occurred in check().
    Aborting...
 -> error making: labelle-exit status 4
 -> Failed to install the following packages. Manual intervention is required:
labelle - exit status 4
```

# Linux version
```
$ uname -a
Linux _ 6.6.56-1-lts #1 SMP PREEMPT_DYNAMIC Thu, 10 Oct 2024 12:04:53 +0000 x86_64 GNU/Linux
```

# QT Version
```bash
$ qmake --version
QMake version 3.1
Using Qt version 5.15.15 in /usr/lib

$ qmake6 --version
QMake version 3.1
Using Qt version 6.7.3 in /usr/lib
```


Discussion / hints:
Thanks @SakulFlee for the report! I don't actually know anything about ArchLinux or AUR. Would you be able to show me how to reproduce this issue in a Docker container? Or perhaps instead raise this issue on AUR?

Just based on the stack trace, I'm guessing that pytestqt might be using a different version of Qt than Labelle. When I debug locally, I get that `qt_api.QtWidgets.QWidget` is `<class 'PyQt6.QtWidgets.QWidget'>`. Correspondingly, `type(widget).mro()` is

```python
[<class 'labelle.gui.gui.LabelleWindow'>, <class 'PyQt6.QtWidgets.QWidget'>, <class 'PyQt6.QtCore.QObject'>, <class 'PyQt6.sip.wrapper'>, <class 'PyQt6.QtGui.QPaintDevice'>, <class 'PyQt6.sip.simplewrapper'>, <class 'object'>]
```

so it's a match.
CC @claui
Oh my bad, I was assuming you are maintaining the AUR.
I just tried replicating it under Docker, but it worked there.
The only difference I can find is what the test session reports:
```bash
# Local:
platform linux -- Python 3.12.7, pytest-8.3.3, pluggy-1.5.0
PySide6 6.7.3 -- Qt runtime 6.7.3 -- Qt compiled 6.7.3
rootdir: /home/sakulflee/.cache/yay/labelle/src/labelle-1.3.1
configfile: pyproject.toml
plugins: qt-4.4.0, image-diff-0.0.11

# Docker:
platform linux -- Python 3.12.7, pytest-8.3.3, pluggy-1.5.0
PyQt6 6.7.1 -- Qt runtime 6.7.3 -- Qt compiled 6.7.3
rootdir: /home/build/.cache/yay/labelle/src/labelle-1.3.1
configfile: pyproject.toml
plugins: qt-4.4.0, image-diff-0.0.11
```

It seems to be `PySide6 6.7.3` vs `PyQt6 6.7.1` what's causing the issue.
I don't know enough about QT to know what this does or how to change it,  Thanks for your help anyways :)
Actually, looking closer into this just now: There seems to be some incompatibility with your test and `PySide2`.
It works fine on `PyQT6` though...

If you want to reproduce the issue:
Start a docker container with arch (`docker run -it --rm archlinux:latest`), then:

```bash
# Sync packages
pacman -Sy

# Install some requirements
pacman -S base-devel git sudo

# Create sudo group and enable it
groupadd sudo
echo "build ALL=(ALL:ALL) ALL" >> /etc/sudoers

# Add user (AUR packages cannot be installed as root)
useradd --create-home build
usermod -aG sudo build
passwd build # pick a password

# Install an AUR tool, like yay (https://github.com/Jguer/yay)
su build
cd
git clone https://aur.archlinux.org/yay.git
cd yay
makepkg -si
# enter password when asked

# Attempt installing Labelle
yay -S --noconfirm pyside6 # <- Introduces the bug! If removed it installs pyqt6 instead which works fine during testing
yay -S --noconfirm labelle # Simply press enter when questions appear or enter password, when asked observe error.
```


Fix patch:
diff --git a/pyproject.toml b/pyproject.toml
index f11d546d..4f25a7f5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -89,6 +89,9 @@ replacement = '<img src="https://raw.githubusercontent.com/labelle-org/labelle/m
 pattern = '\[(.*?)\]\(((?!https?://)\S+)\)'
 replacement = '[\1](https://github.com/labelle-org/labelle/tree/main/\g<2>)'
 
+[tool.pytest.ini_options]
+qt_api = "pyqt6"
+
 [tool.tox]
 legacy_tox_ini = """
 [tox]


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index c20a4469..3e7faad0 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -23,8 +23,18 @@ jobs:
         python-version: ${{ matrix.python-version }}
     - name: Install dependencies
       run: |
-        sudo apt-get install -y libegl1 libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 x11-utils libgl1 libdbus-1-3 libxcb-cursor0
-        /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1920x1200x24 -ac +extension GLX
+        sudo apt-get update && sudo apt-get install -y \
+            libegl1 libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 \
+            libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 \
+            libxcb-xinerama0 libxcb-xfixes0 x11-utils libgl1 \
+            libdbus-1-3 libxcb-cursor0
+        /sbin/start-stop-daemon --start --quiet \
+            --pidfile /tmp/custom_xvfb_99.pid \
+            --make-pidfile \
+            --background \
+            --exec /usr/bin/Xvfb \
+            -- \
+            :99 -screen 0 1920x1200x24 -ac +extension GLX
         python -m pip install --upgrade pip
         pip install tox tox-gh-actions
     - name: Test with tox
diff --git a/src/labelle/gui/tests/test_gui.py b/src/labelle/gui/tests/test_gui.py
index 3008f74a..b0c41dd2 100644
--- a/src/labelle/gui/tests/test_gui.py
+++ b/src/labelle/gui/tests/test_gui.py
@@ -1,10 +1,12 @@
+from pytestqt.qtbot import QtBot
+
 from labelle.gui.gui import LabelleWindow
 from labelle.gui.q_label_widgets import (
     TextDymoLabelWidget,
 )
 
 
-def test_main_window(qtbot):
+def test_main_window(qtbot: QtBot):
     widget = LabelleWindow()
     qtbot.addWidget(widget)