[PyQt] QSettings with normal and "bundled" apps

David Cortesi davecortesi at gmail.com
Tue Jan 27 15:37:42 GMT 2015


The following relates to Python3.4, PyQt5.4, and OS X 10.10.

While trying to bundle an app for distribution I found that
the behavior of QSettings differs between the app when run by
CPython from the command line, and when it runs in a “bundle”
as prepared by cx_freeze[1] OR by PyInstaller[2].

The apps made by either bundling method fail in the
identical way. That is why I am bringing this to the PyQt
list first. I have reduced it to a bare minimum of code
in hopes someone can suggest what to investigate next.

[14:04:48 scratch] cat writer.py
from PyQt5.QtCore import QCoreApplication, QSettings
app = QCoreApplication([])
app.setOrganizationName("BOGUS_NAME")
app.setOrganizationDomain("bogosity.com")
app.setApplicationName("BOGUS_APP")
settings = QSettings()
settings.clear()
settings.setValue('b',b'\xde\xad\xbe\xef')

[14:04:53 scratch] cat reader.py
from PyQt5.QtCore import QCoreApplication, QSettings
app = QCoreApplication([])
app.setOrganizationName("BOGUS_NAME")
app.setOrganizationDomain("bogosity.com")
app.setApplicationName("BOGUS_APP")
settings = QSettings() # open settings file
assert b'\xde\xad\xbe\xef' == settings.value('b',b'\x00')

All is nominal when run from the command line:

[14:04:56 scratch] python3 writer.py
[14:05:01 scratch] python3 reader.py

The cx_freeze reader fails.

[14:05:23 scratch] cxwork/dist/reader
Traceback (most recent call last):
  File "reader.py", line 7, in <module>
    assert b'\xde\xad\xbe\xef' == settings.value('b',b'\x00')
TypeError: unable to convert a QVariant back to a Python object


The pyinstaller reader also fails the same way.

[14:05:41 scratch] piwork/dist/reader/reader
Traceback (most recent call last):
  File "<string>", line 7, in <module>
TypeError: unable to convert a QVariant back to a Python object

Remake the Settings file using a bundled writer:

[14:05:48 scratch] rm ~/Library/Preferences/com.bogosity.BOGUS_APP.plist
[14:06:12 scratch] piwork/dist/writer/writer

Now the normal reader fails also.

[14:06:23 scratch] python3 reader.py
Traceback (most recent call last):
  File "reader.py", line 7, in <module>
    assert b'\xde\xad\xbe\xef' == settings.value('b',b'\x00')
TypeError: unable to convert a QVariant back to a Python object

But so do the bundled readers

[14:06:30 scratch] piwork/dist/reader/reader
Traceback (most recent call last):
  File "<string>", line 7, in <module>
TypeError: unable to convert a QVariant back to a Python object

The cxfreeze reader and writer tests are the same, omitted.

To recap: the Settings plist written by the “normal” writer
can be read only by the “normal” reader. The plist written
by either bundled writer cannot be read by ANY reader.

So is there a difference in output of a normal versus a bundled writer?


[14:07:04 scratch] rm ~/Library/Preferences/com.bogosity.BOGUS_APP.plist
[14:07:11 scratch] python3 writer.py
[14:07:16 scratch] hexdump -C
~/Library/Preferences/com.bogosity.BOGUS_APP.plist
00000000  62 70 6c 69 73 74 30 30  d1 01 02 51 62 6f 10 2f
|bplist00...Qbo./|
00000010  00 40 00 56 00 61 00 72  00 69 00 61 00 6e 00 74
|. at .V.a.r.i.a.n.t|
00000020  00 28 00 00 00 00 00 00  00 7f 00 00 00 00 00 00
|.(..............|
00000030  00 0e 00 50 00 79 00 51  00 74 00 5f 00 50 00 79
|...P.y.Q.t._.P.y|
00000040  00 4f 00 62 00 6a 00 65  00 63 00 74 00 00 00 00
|.O.b.j.e.c.t....|
00000050  00 00 00 00 00 0b 00 80  00 03 00 43 00 04 00 de
|...........C....|
00000060  00 ad 00 be 00 ef 00 71  00 00 00 2e 00 29 08 0b
|.......q.....)..|
00000070  0d 00 00 00 00 00 00 01  01 00 00 00 00 00 00 00
|................|
00000080  03 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
|................|
00000090  6e                                                |n|



[14:07:23 scratch] rm ~/Library/Preferences/com.bogosity.BOGUS_APP.plist
[14:07:28 scratch] piwork/dist/writer/writer
[14:07:37 scratch] hexdump -C
~/Library/Preferences/com.bogosity.BOGUS_APP.plist
00000000  62 70 6c 69 73 74 30 30  d1 01 02 51 62 5f 10 24
|bplist00...Qb_.$|
00000010  40 56 61 72 69 61 6e 74  28 00 00 00 7f 00 00 00
|@Variant(.......|
00000020  0e 50 79 51 74 5f 50 79  4f 62 6a 65 63 74 00 00
|.PyQt_PyObject..|
00000030  00 00 00 29 08 0b 0d 00  00 00 00 00 00 01 01 00
|...)............|
00000040  00 00 00 00 00 00 03 00  00 00 00 00 00 00 00 00
|................|
00000050  00 00 00 00 00 00 34                              |......4|

One immediate difference appears: the strings in the “normal”
plist are in UTF-16(?), those in the “bundled” plist are ASCII.
Another is that the byte string values "deadbeef" which are visible
in the normal plist (offset 05f forward) are not visible in the other.

This would explain why the “normal” reader cannot read the
“bundled” plist - if it expected 16-bit chars. It might explain why the
"bundled" reader can't read the "normal" plist (if it expects ASCII)
and why it can't read a "bundled" plist (if the item value is
munged/omitted).

I need to make a "bundled" app work with QSettings, and
would love any suggestions as to what to look at next.

Thanks for your attention.

[1] http://cx-freeze.sourceforge.net/
[2] https://github.com/pyinstaller/pyinstaller/wiki
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.riverbankcomputing.com/pipermail/pyqt/attachments/20150127/89b6c072/attachment.html>


More information about the PyQt mailing list