PyQt6: QEvent.type() returns int instead of QEvent.Type

Florian Bruhin me at the-compiler.org
Tue May 3 14:22:48 BST 2022


On Sun, May 01, 2022 at 03:13:12PM +0100, Phil Thompson wrote:
> 
> On 28/04/2022 20:16, Florian Bruhin wrote:
> > On Thu, Apr 28, 2022 at 09:42:46AM +0100, Phil Thompson wrote:
> > > So if we can solve the pickle problem I think I'd be Ok to take this
> > > approach.
> > 
> > I think the most elegant way to do this is via the _missing_ classmethod
> > on enums. This is public API:
> > https://docs.python.org/3/library/enum.html#supported-sunder-names
> > 
> > The drawback about the approach is that it's possible to do
> > SomePyQtEnum(1337) and that happily returns a value, so at this point,
> > the cpp2py() can probably just be implemented in _missing_ altogether.
> > 
> > See the attached file. The _missing_ method needs to use cpp2py so that
> > even values which haven't been passed through cpp2py before work fine.
> > 
> > This means I had to rewrite cpp2py slightly to avoid triggering
> > _missing_. The __members__ attribute is documented too:
> > https://docs.python.org/3/library/enum.html#iteration
> > 
> > Alternatively, "if cpp in list(etype):" could be used, but that builds
> > up an unnecessary list ("cpp in etype" will not work, raising a
> > TypeError).
> > 
> > If we didn't want to use missing (so that SomePyQtEnum(1337) does not
> > work), I suppose it can be solved by defining __reduce_ex__ on the enum
> > type somehow, but I tried, and it never was called (only enum.py's was).
> 
> I had come to the same conclusion that the only way to support pickle was to
> use a sub-class of Enum and re-implement _missing_. I had hoped to be able
> to use a regular Enum (and IntEnum).

You can register a pickle function via copyreg.pickle instead:
https://docs.python.org/3/library/copyreg.html#copyreg.pickle

This seems to work:

    copyreg.pickle(SomePyQtEnum, lambda py: (cpp2py, (SomePyQtEnum, py2cpp(py))))

It's the equivalent of defining an equivalent __reduce__ on the class:
SomePyQtEnum now gets pickled in a way that
cpp2py(SomePyQtEnum, raw_value) is called when unpickling, where
raw_value is py2cpp(py).

See the attached file for a complete example.

Florian

-- 
            me at the-compiler.org | https://www.qutebrowser.org 
       https://bruhin.software/ | https://github.com/sponsors/The-Compiler/
       GPG: 916E B0C8 FD55 A072 | https://the-compiler.org/pubkey.asc
             I love long mails! | https://email.is-not-s.ms/
-------------- next part --------------
import copyreg
import pickle

from enum import Enum


class SomePyQtEnum(Enum):

    one = 1
    two = 2


def cpp2py(etype, cpp):
    print(f"cpp2py {etype=} {cpp=}")
    if cpp in etype.__members__:
        return etype(cpp)

    try:
        return etype._sip_unknowns[cpp]
    except KeyError:
        pass
    except AttributeError:
        etype._sip_unknowns = {}

    member = object.__new__(etype)
    member._name_ = f"sip_unknown_{cpp}"
    member._value_ = cpp

    etype._sip_unknowns[cpp] = member

    return member


def py2cpp(py):
    return py.value


e1 = cpp2py(SomePyQtEnum, 1)
e2 = cpp2py(SomePyQtEnum, 2)
e3minus = cpp2py(SomePyQtEnum, -3)
e4 = cpp2py(SomePyQtEnum, 4)
e4a = cpp2py(SomePyQtEnum, 4)

print(e1)
print(e2)
print(e3minus)
print(e4)
print(e4a)
print(e4 is e4a)

print(py2cpp(e1))
print(py2cpp(e2))
print(py2cpp(e3minus))
print(py2cpp(e4))


copyreg.pickle(SomePyQtEnum, lambda py: (cpp2py, (SomePyQtEnum, py2cpp(py))))

dump = pickle.dumps(cpp2py(SomePyQtEnum, 4))
print(pickle.loads(dump))

# Try loading a value that has not been passed through cpp2py first.
# Generated via print(pickle.dumps(cpp2py(SomePyQtEnum, 5)))
print(pickle.loads(b'\x80\x04\x950\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x06cpp2py\x94\x93\x94h\x00\x8c\x0cSomePyQtEnum\x94\x93\x94K\x05\x86\x94R\x94.'))
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20220503/82bf47ad/attachment.sig>


More information about the PyQt mailing list