[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