1

That's such a weird bug, if it's even a bug.

This works:

from PySide6.QtCore import QBuffer, QByteArray, QFile, QIODevice

from PySide6.QtGui import QImageReader


image_path = Path('/home/p10/testIAMAGAGADS.png')
file = QFile(image_path)
file.open(QIODevice.ReadOnly)
blob = file.readAll()
buffer = QBuffer(blob)
image_reader = QImageReader(buffer)

This crashes PySide6:

from pathlib import Path
from PySide6.QtCore import QBuffer, QByteArray, QFile, QIODevice

from PySide6.QtGui import QImageReader


image_path = Path('/home/p10/testIAMAGAGADS.png')
file = QFile(image_path)
file.open(QIODevice.ReadOnly)
blob = file.readAll()
image_reader = QImageReader(QBuffer(blob))

I would expect an object created in a specific scope (albeit passed as an argument) to stay alive at least till the end of that scope. PS: The same thing happens when I read the image from a file to a bytes object and pass it to QBuffer without binding it to a variable beforehand.

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
petko10
  • 115
  • 7
  • 1
    There's no bug or weirdness. You're not keeping a reference to the QBuffer object, so it gets garbage-collected. This is no different than doing e.g. `len(dict(a=1))`: the dict will be deleted after len returns. The image reader takes a pointer to a QIODEvice. If it tries to read from that device (via the pointer) after it's been deleted, a fatal error will occur. – ekhumoro Aug 20 '22 at 06:15
  • Ok, I would expect that when the image reader "takes a pointer" - it makes a reference, but since it's a C++ lib it apparently just takes the pointer. It would've been nice to have a proper error message for that though. PS: I would mark your reply as an accepted answer if you posted it – petko10 Aug 20 '22 at 09:08
  • @petko10 Qt already uses warnings when doing things that are discouraged or "wrong" on *its* side. But destroying an object that is being used creates a problem in memory access, and that is much worse. – musicamante Aug 20 '22 at 11:18
  • @petko10 I have expanded my comment to make a more complete answer. – ekhumoro Aug 20 '22 at 21:09

1 Answers1

2

There's no bug or weirdness here. The second example does not keep a reference to the QBuffer object, so it gets garbage-collected (by Python). This is no different than doing e.g. len(dict(a=1)): the dict will be deleted immediately after len returns. The image reader takes a pointer to a QIODEvice. If it tries to read from that device (via the pointer) after it's been deleted, a fatal error will occur.

The Qt docs will usually explicitly state whether ownership passes to the receiver, and there's no indication that QImageReader will do this. In fact, the docs include the following:

Note: QImageReader assumes exclusive control over the file or device that is assigned. Any attempts to modify the assigned file or device during the lifetime of the QImageReader object will yield undefined results.

One way to fix the second example would be to make the QBuffer a child of the QFile (since they both inherit QObject):

from pathlib import Path
from PySide6.QtCore import QBuffer, QByteArray, QFile, QIODevice
from PySide6.QtGui import QImageReader

image_path = Path('/home/p10/testIAMAGAGADS.png')
file = QFile(image_path)
file.open(QIODevice.OpenModeFlag.ReadOnly)
blob = file.readAll()
image_reader = QImageReader(QBuffer(blob, file))
image = image_reader.read()
print(image)

(NB: when the QFile is deleted, Qt will automatically delete all its children as well).

ekhumoro
  • 115,249
  • 20
  • 229
  • 336