[BUG] Fix _get_class_flags dropping Mixin flags under multiple inheritance#540
Merged
fkiraly merged 7 commits intosktime:mainfrom Apr 19, 2026
Merged
Conversation
…tance (sktime#539) The MRO iteration in _FlagManager._get_class_flags previously used: inspect.getmro(cls)[:-2] This assumed the last two MRO entries are always 'BaseObject' and 'object'. That assumption is broken under multiple inheritance. Python's C3 linearisation can place a user-defined Mixin class immediately before 'object', making it the second-to-last entry. The [:-2] slice then silently drops that Mixin and all flags it defines. Minimal reproduction: class MyMixin: _flags = {'mixin_tag': 42} class MyObj(BaseObject, MyMixin): _flags = {'own_tag': 1} # MRO: MyObj -> BaseObject -> _FlagManager -> MyMixin -> object # [:-2] produces: MyObj -> BaseObject -> _FlagManager # MyMixin (and 'mixin_tag') is permanently dropped. Fix: iterate the full MRO and skip only 'object' via an explicit identity check. 'object' is the only class that can never carry user-defined flags, so it is the only safe sentinel to exclude. Changes: - skbase/base/_tagmanager.py: replace [:-2] slice with explicit 'if parent_class is object: continue' guard; add a detailed comment explaining the root cause so the fix is not accidentally reverted. - skbase/tests/test_meta.py: add missing _FlagManager import (needed by the existing MRO assertion) and add test_get_class_flags_preserves_mixin_tags as a regression test that pins the fix and will loudly fail if the [:-2] slice is reintroduced.
for more information, see https://pre-commit.ci
fkiraly
reviewed
Apr 12, 2026
| # Slicing with [:-2] drops `MyMixin`, permanently losing any flags it | ||
| # defines. The only safe sentinel is `object` itself (which never carries | ||
| # user-defined flags), so we skip it explicitly and iterate the rest. | ||
| for parent_class in reversed(inspect.getmro(cls)): |
Contributor
There was a problem hiding this comment.
if we want to make this general, do we not want to drop it entirely?
Also, please do not add comments that explain the previous situation, they do not make sense to a reader of the code base. AIs often add comments that reference a change.
- Removed the detailed historical inline comment from the MRO fix in `_get_class_flags` - Added `test_class_tags_match_mro` to `TestAllObjects` to verify the MRO invariant holds for all dynamic estimator lookups, preventing future regressions. - Simplified the standalone bug reproduction test docstring.
for more information, see https://pre-commit.ci
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #540 +/- ##
==========================================
- Coverage 85.07% 84.03% -1.04%
==========================================
Files 45 52 +7
Lines 3015 3934 +919
==========================================
+ Hits 2565 3306 +741
- Misses 450 628 +178 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #539 - _FlagManager._get_class_flags dropped flags from user-defined Mixin classes under multiple inheritance due to a hardcoded [:-2] MRO slice.
Root Cause
The previous code assumed the last two MRO entries are always BaseObject and object:
Under Python's C3 linearisation, class MyObj(BaseObject, MyMixin) produces:
MyMixin sits at position [-2], so the slice permanently drops it and all its flags - silently, with no error.
Fix
Iterate the full MRO and skip only object via an explicit identity check:
object is always the last entry and can never carry user-defined flags, making it the only safe sentinel to exclude.
Changes
Testing