Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loading Quantity from PyQt6 QSettings not working #1961

Open
loenard97 opened this issue Apr 5, 2024 · 0 comments
Open

Loading Quantity from PyQt6 QSettings not working #1961

loenard97 opened this issue Apr 5, 2024 · 0 comments

Comments

@loenard97
Copy link

I would like to save a Quantity with the built-in QSettings tools of PyQt6 in a .ini file. QSettings automatically pickles and unpickles the Quantity, but calling a function wrapped by a @ureg.wraps decorator fails unexpectedly.

I have the following setting in the .ini file (located at QSettings().fileName()):

[General]
my_value_1=@Variant(\0\0\0\x7f\0\0\0\xePyQt_PyObject\0\0\0\0\xbd\x80\x4\x95\xb2\0\0\0\0\0\0\0\x8c\x4pint\x94\x8c\x12_unpickle_quantity\x94\x93\x94\x8c\x1apint.facets.plain.quantity\x94\x8c\rPlainQuantity\x94\x93\x94G?`bM\xd2\xf1\xa9\xfc\x8c\tpint.util\x94\x8c\xeUnitsContainer\x94\x93\x94)\x81\x94h\x6\x8c\x5udict\x94\x93\x94)\x81\x94\x8c\x6second\x94K\x1sK\x1\x8c\bbuiltins\x94\x8c\x5\x66loat\x94\x93\x94\x87\x94\x62\x87\x94R\x94.)

Here is a minimal example that tries to load this setting:

import sys
import pint
from pint import UnitRegistry
from PyQt6.QtCore import QSettings
from PyQt6.QtWidgets import QApplication

ureg = UnitRegistry()


# Some random function with units wrapped around it
@ureg.wraps(None, "second")
def wrapped_func(value):
    print(value)

# QSettings setup
app = QApplication(sys.argv)
app.setOrganizationName("Test")
app.setApplicationName("test")
QSettings.setDefaultFormat(QSettings.Format.IniFormat)
settings = QSettings()
print("QSettings file path", settings.fileName())

print("\nRegular quantity working as expected")
regular_quantity = 0.002 * ureg.second
print(regular_quantity)                                 # 0.002 second
print(type(regular_quantity))                           # <class 'pint.Quantity'>
print(isinstance(regular_quantity, pint.Quantity))      # True
print(isinstance(regular_quantity, ureg.Quantity))      # True
print(regular_quantity.__class__.__mro__)

print("\nLoading a quantity from QSettings")
unpickled_quantity = settings.value("my_value_1")
print(unpickled_quantity)                               # 0.002 second
print(type(unpickled_quantity))                         # <class 'pint.Quantity'>
print(isinstance(unpickled_quantity, pint.Quantity))    # True (as expected)
print(isinstance(unpickled_quantity, ureg.Quantity))    # False (for some reason?)
print(unpickled_quantity.__class__.__mro__)             # Exactly the same MRO as regular_quantity above

# The unit wrapper checks for isinstance(unpickled_quantity, ureg.Quantity)
# Because this is False the following function call throws a ValueError:
# ValueError: A wrapped function using strict=True requires quantity or a string for all arguments with not None units. (error found for second, 0.002 second)
wrapped_func(unpickled_quantity)

QSettings does unpickle the saved value correctly, but the check isinstance(unpickled_quantity, ureg.Quantity) returns False, even though the value is of type <class 'pint.Quantity'>. If I then try to use this value as an argument for a function with a @ureg.wraps decorator, the decorator checks for isinstance(unpickled_quantity, ureg.Quantity) in line 150 in registry_helpers.py, which returns False and therefore the decorator fails to accept my value as a Quantity, even though it is of type Quantity.

This if check in registry_helpers.py line 150 should return True, but does not:

        # third pass: convert other arguments
        for ndx in unit_args_ndx:
            if isinstance(values[ndx], ureg.Quantity):

I found out that UnitRegistry.Quantity is just a TypeAlias of Quantity. But that confused me even more, because I don't see why isinstance should return differently for pint.Quantity and ureg.Quantity if they are the same type. The Method Resolution Orders are even exactly the same for both regular_quantity and unpickled_quantity, but somehow the isinstance checks are different!

What makes this even more confusing is that I can't seem to reproduce the .ini file that causes this error. The .ini file I showed at the top is produced by a larger program that consistently produces this bug. But when I try to write a minimal program that saves a .ini file itself, the bug does not appear and I don't see any difference between the two files.

As a work around I can save my values without Quantities for now, but that kind of defeats the purpose why we are using pint in our original program.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant