On Dec 31, 2019, at 11:02, Soni L. <fakedme+py@gmail.com> wrote:
On 2019-12-31 3:56 p.m., Andrew Barnert wrote: On Dec 31, 2019, at 09:43, Soni L. <fakedme+py@gmail.com> wrote:
I would like this code to work, but currently python ignores __subclasscheck__ in many places where it checks for subclasses: class MM(type): def __subclasscheck__(self, subclass): return issubclass(subclass, type)
class M(type, metaclass=MM): pass class N(type): pass class C(metaclass=M): pass class D(metaclass=N): pass class E(C, D, metaclass=N): pass
Is the problem you’re trying to solve here, that you want to be able to override the metaclass conflict check in class definitions, so you can allow this?
If so, I think it makes sense. N is (trivially) a subclass of the N (the metaclass of D), and you’re making it a virtual subclass (via MM) of M (the metaclass of C). So there actually is no conflict if you take virtual subclasses into account; N is unambiguously the most derived metaclass.
(I suppose it would be ambiguous again if you also made M a (virtual or direct) subclass of N, but that’s probably consulting-adults territory: don’t do that, and if you do, the fact that the error message isn’t as meaningful as it could be isn’t a huge deal.)
But you said “many places”. Is there a wider class of places, that includes the metaclass conflict check, where virtual subclassing is ignored but (you think) shouldn’t be?
In the documentation for metaclass stuff, Python mentions "subtype" without referring to issubtype. I assume other parts of the docs also do similar things, but I haven't checked.
Well, there is no “issubtype”; only “issubclass”. Just like there is no “virtual subtype”, only “virtual subclass”. I think the docs may be deliberately using “subtype” rather than “subclass” for that reason. But if so, it isn’t exactly clear, because I don’t think “subtype” is actually defined anywhere, and “direct or indirect, but not virtual, subclass” certainly isn’t the only meaningful guess you could make. This is probably worth filing a docs bug on even if nothing else is changed to clarify what that’s supposed to mean. (With your change, the docs probably just need to change to say subclass.) I only see the word “subtype” a few other times, and they’re all declaring that some builtin is a subtype or some other thing, so it doesn’t matter whether it’s supposed to mean “subclass” or “non-virtual subclass” or some other thing. But I haven’t searched exhaustively. But meanwhile, even if you do fix this here, would it actually help? It would mean the interpreter can determine the most derived metaclass unambiguously, so it would call N.__prepare__ and eventually N('E', (C, D), {…stuff…}). Which would be fine if you overrode __new__ or meta __call__ or didn’t inherit from type, but you don’t do any of those, so that just calls type.__new__. And (again, I haven’t tested any of this…) doesn’t type.__new__ also need to work out the most derived subtype again? Or does that already work? And then, even if that works, wouldn’t that make it do things like call N.__init_subclass__(C) instead of type(C).__init_subclass__(C)? (That’s an abbreviation for the special method lookup rules, not literal code. And again, I haven’t tested any of this, so maybe I’m being stupid.)
However, I can imagine cases where virtual subtypes would cause problems. Namely, the C API and builtins. But, more importantly, it wouldn't work there anyway since you can't monkeypatch __instancecheck__ / __subclasscheck__ of built-ins in the first place.
I didn’t think of that. But you don’t have to monkeypatch them to hot that problem, you can write your own extension module with a class that defines __subclasscheck__, and what happens then? (Although consenting adults seems even more relevant here—as long as the answer isn’t “segfault without diagnostics”, whatever happens is probably fine…)