The Python metaclass data model allows the mro() method to be overloaded. This is a genuinely useful feature for dealing with metaclasses whose classes must support inheritance. See "Example" below.
I wrote a mypy plugin for type-checking instances of my metaclass. I was able to model its semantics almost correctly with the existing plugin hooks, except for one thing — customizing the MRO.
The best way I can get my type-checker unblocked is by adding a plugin hook for customizing the MRO. My idea is roughly this:
- Move
calculate_class_mro to be a member of SemanticAnalyzerPass2
- Add a plugin hook that gets to customize the computed MRO.
@JukkaL, does this sound like a feature you would take?
Example of needing to customize the MRO:
- My metaclass
M injects a implementation-detail base M_classname_base into each class it creates. M's semantics demand that M_classname_base be at the very end of the MRO (to allow overloading of its features).
- Let us define
class A(metaclass=M) and class B(A).
- Each class injects its own base:
M_A_base and M_B_base. That means that B actually has both implementation-detail bases.
- Unfortunately,
M_A_base and M_B_base conflict, and M_B_base ends up after M_A_base in the MRO, which is the opposite of what you want (child must overload parent, not vice versa).
- At runtime,
M.mro() can simply remove the A-produced base from B's MRO, and all is well.
- In current
mypy, removing M_A_base from B's MRO seems impossible.
Cc: @carljm, if you're curious.
The Python metaclass data model allows the
mro()method to be overloaded. This is a genuinely useful feature for dealing with metaclasses whose classes must support inheritance. See "Example" below.I wrote a
mypyplugin for type-checking instances of my metaclass. I was able to model its semantics almost correctly with the existing plugin hooks, except for one thing — customizing the MRO.The best way I can get my type-checker unblocked is by adding a plugin hook for customizing the MRO. My idea is roughly this:
calculate_class_mroto be a member ofSemanticAnalyzerPass2@JukkaL, does this sound like a feature you would take?
Example of needing to customize the MRO:
Minjects a implementation-detail baseM_classname_baseinto each class it creates.M's semantics demand thatM_classname_basebe at the very end of the MRO (to allow overloading of its features).class A(metaclass=M)andclass B(A).M_A_baseandM_B_base. That means thatBactually has both implementation-detail bases.M_A_baseandM_B_baseconflict, andM_B_baseends up afterM_A_basein the MRO, which is the opposite of what you want (child must overload parent, not vice versa).M.mro()can simply remove theA-produced base fromB's MRO, and all is well.mypy, removingM_A_basefromB's MRO seems impossible.Cc: @carljm, if you're curious.