Issue23276
This issue tracker has been migrated to GitHub,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2015-01-19 22:22 by devkid, last changed 2022-04-11 14:58 by admin. This issue is now closed.
Messages (6) | |||
---|---|---|---|
msg234329 - (view) | Author: Alfred Krohmer (devkid) | Date: 2015-01-19 22:22 | |
The following code: import traceback import sys from PyQt5.QtCore import Qt class MetaA(type): pass class A(metaclass=MetaA): pass class MetaB(type): pass class B(metaclass=MetaB): pass for ClassB in B, Qt: print("Trying class %s" % (ClassB.__name__, )) class MyMeta(type(A), type(ClassB)): def __setattr__(cls, key, value): print(cls) super(type, cls).__setattr__(key, value) class MyClass(A, ClassB, metaclass=MyMeta): pass try: setattr(MyClass, 'abc', 123) except: traceback.print_exc(file=sys.stdout) try: type.__setattr__(MyClass, 'test', 42) except: traceback.print_exc(file=sys.stdout) Fails with the following output: Trying class B <class '__main__.MyClass'> Traceback (most recent call last): File "test3.py", line 31, in <module> setattr(MyClass, 'abc', 123) File "test3.py", line 25, in __setattr__ super(type, cls).__setattr__(key, value) TypeError: can't apply this __setattr__ to type object Trying class Qt <class '__main__.MyClass'> Traceback (most recent call last): File "test3.py", line 31, in <module> setattr(MyClass, 'abc', 123) File "test3.py", line 25, in __setattr__ super(type, cls).__setattr__(key, value) TypeError: can't apply this __setattr__ to sip.wrappertype object Traceback (most recent call last): File "test3.py", line 36, in <module> type.__setattr__(MyClass, 'test', 42) TypeError: can't apply this __setattr__ to sip.wrappertype object The metaclass of a class should be able to update its class' __dict__ my calling super(type, cls).__setattr__ (there is no other way known to me to do this). Furthermore, if subclassing an external class, like Qt, it should be possible to use type.__setattr__(MyClass, ...) externally to change the class' attributes. The error is caused by the hackcheck function in objects/typeobject.c. |
|||
msg234333 - (view) | Author: Eryk Sun (eryksun) * | Date: 2015-01-20 01:08 | |
> super(type, cls).__setattr__(key, value) In your case, super(type, cls).__setattr__ references object.__setattr__. >>> super(type, MyClass).__setattr__.__objclass__ <class 'object'> That's from the method resolution order (__mro__): >>> print(*MyMeta.__mro__, sep='\n') <class '__main__.MyMeta'> <class '__main__.MetaA'> <class '__main__.MetaB'> <class 'type'> <class 'object'> Instead use super(MyMeta, cls), or in Python 3 just use super() in a method (under the hood the function uses a closure variable named __class__). >>> super(MyMeta, MyClass).__setattr__.__objclass__ <class 'type'> > type.__setattr__(MyClass, 'test', 42) The above won't work for a Qt subclass. You need __setattr__ from sip.wrappertype. >>> type.__setattr__(QtClass, 'test', 42) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can't apply this __setattr__ to sip.wrappertype object >>> print(*QtMeta.__mro__, sep='\n') <class '__main__.QtMeta'> <class '__main__.MetaA'> <class 'sip.wrappertype'> <class 'type'> <class 'object'> >>> super(QtMeta, QtClass).__setattr__.__objclass__ <class 'sip.wrappertype'> >>> super(QtMeta, QtClass).__setattr__('test', 42) >>> QtClass.test 42 |
|||
msg234359 - (view) | Author: Alfred Krohmer (devkid) | Date: 2015-01-20 08:40 | |
Can you elaborate what QtClass and QtMeta is in your case? My original example was reduced to a minimal case and seems to work with your suggestions. The complete example involving SQLalchemy is here: http://stackoverflow.com/questions/28032928/sqlalchemy-multiple-base-classes-not-working and does, however, not work. If I try to do # ... def __setattr__(cls, key, value): super(type(QMediaPlaylist), cls).__setattr__(cls, key, value) return The program segfaults when instantiating the Playlist class. However, this approach seems a little bit strange to me anyhow. The same happens when I try to do: # ... def __setattr__(cls, key, value): super(type(base), cls).__setattr__(cls, key, value) return I think that comes from PyQt specific attributes SQLalchemy is trying to set / replace. So, coming back to the original question, how can I actually set an attribute of my class Playlist from within its metaclass without involving the parent classes of the subclass (type(base) and type(QMediaPlaylist))? Because the __setattr__ from PyQt won't work (segfault) and the one from SQLalchemy does stupid stuff. |
|||
msg234369 - (view) | Author: Eryk Sun (eryksun) * | Date: 2015-01-20 11:24 | |
> def __setattr__(cls, key, value): > super(type(QMediaPlaylist), cls).__setattr__(cls, key, value) > return > > The program segfaults when instantiating the Playlist class. I'd expect a TypeError because of the extra cls argument. It's already a bound method. FYI, the above finds the next metaclass after type(QMediaPlaylist) in PlaylistMeta.__mro__. It happens that type(QMediaPlaylist) inherits __setattr__ from the next in line (sip.wrappertype), so by a stroke of luck it 'works' (not really since this skips the incompatible sqlalchemy __setattr__). Consider making a playlist class that *has* a SQL table, not one that *is* a SQL table, i.e. use composition instead of inheritance. That sidesteps the incompatible metaclasses. |
|||
msg234390 - (view) | Author: Alfred Krohmer (devkid) | Date: 2015-01-20 20:26 | |
> I'd expect a TypeError because of the extra cls argument. It's already a bound method. Sorry, that was a typo. > Consider making a playlist class that *has* a SQL table, not one that *is* a SQL table, i.e. use composition instead of inheritance. That sidesteps the incompatible metaclasses. That would be indeed a solution, but not for the original problem. I think I have an example now that makes my point clear. The following code works as it should: import traceback import sys class MyMeta(type): def __setattr__(cls, key, value): print("OK") class MyClass(metaclass=MyMeta): pass MyClass.abc = 12 # outputs "OK" try: print(MyClass.abc) except: traceback.print_exc(file=sys.stdout) # exception comes here as expected type.__setattr__(MyClass, 'test', 42) # outputs nothing print(MyClass.test) # outputs "42" If I get this right, this should be **valid code** (and it should **not** be a bug, that this actually works). However, above define MyMeta like following: from PyQt5.QtMultimedia import QMediaPlaylist class MyMeta(type(QMediaPlaylist)): def __setattr__(cls, key, value): print("OK") And you get: TypeError: can't apply this __setattr__ to PyQt5.QtCore.pyqtWrapperType object I think that this actually **is** unexpected behaviour. I'm **not** trying to apply __setattr__ to PyQt5.QtCore.pyqtWrapperType but to MyClass! |
|||
msg380561 - (view) | Author: Irit Katriel (iritkatriel) * | Date: 2020-11-08 18:59 | |
I tried to look up what pyqtWrapperType is and found that it has been removed from QtCore. Is this issue still relevant? |
History | |||
---|---|---|---|
Date | User | Action | Args |
2022-04-11 14:58:12 | admin | set | github: 67465 |
2020-11-30 19:31:04 | iritkatriel | set | status: pending -> closed resolution: out of date stage: resolved |
2020-11-08 18:59:46 | iritkatriel | set | status: open -> pending nosy: + iritkatriel messages: + msg380561 |
2015-01-20 20:26:45 | devkid | set | messages: + msg234390 |
2015-01-20 11:24:49 | eryksun | set | messages: + msg234369 |
2015-01-20 08:40:20 | devkid | set | messages: + msg234359 |
2015-01-20 01:08:38 | eryksun | set | nosy:
+ eryksun messages: + msg234333 |
2015-01-19 22:22:19 | devkid | create |