@@ -635,10 +635,60 @@ def __delattr__(cls, attr):
635
635
super ().__delattr__ (attr )
636
636
637
637
def __dir__ (self ):
638
- return (
639
- ['__class__' , '__doc__' , '__members__' , '__module__' ]
640
- + self ._member_names_
641
- )
638
+ # Start off with the desired result for dir(Enum)
639
+ cls_dir = {'__class__' , '__doc__' , '__members__' , '__module__' }
640
+ add_to_dir = cls_dir .add
641
+ mro = self .__mro__
642
+ this_module = globals ().values ()
643
+ is_from_this_module = lambda cls : any (cls is thing for thing in this_module )
644
+ first_enum_base = next (cls for cls in mro if is_from_this_module (cls ))
645
+ enum_dict = Enum .__dict__
646
+ sentinel = object ()
647
+ # special-case __new__
648
+ ignored = {'__new__' , * filter (_is_sunder , enum_dict )}
649
+ add_to_ignored = ignored .add
650
+
651
+ # We want these added to __dir__
652
+ # if and only if they have been user-overridden
653
+ enum_dunders = set (filter (_is_dunder , enum_dict ))
654
+
655
+ # special-case __new__
656
+ if self .__new__ is not first_enum_base .__new__ :
657
+ add_to_dir ('__new__' )
658
+
659
+ for cls in mro :
660
+ # Ignore any classes defined in this module
661
+ if cls is object or is_from_this_module (cls ):
662
+ continue
663
+
664
+ cls_lookup = cls .__dict__
665
+
666
+ # If not an instance of EnumType,
667
+ # ensure all attributes excluded from that class's `dir()` are ignored here.
668
+ if not isinstance (cls , EnumType ):
669
+ cls_lookup = set (cls_lookup ).intersection (dir (cls ))
670
+
671
+ for attr_name in cls_lookup :
672
+ # Already seen it? Carry on
673
+ if attr_name in cls_dir or attr_name in ignored :
674
+ continue
675
+ # Sunders defined in Enum.__dict__ are already in `ignored`,
676
+ # But sunders defined in a subclass won't be (we want all sunders excluded).
677
+ elif _is_sunder (attr_name ):
678
+ add_to_ignored (attr_name )
679
+ # Not an "enum dunder"? Add it to dir() output.
680
+ elif attr_name not in enum_dunders :
681
+ add_to_dir (attr_name )
682
+ # Is an "enum dunder", and is defined by a class from enum.py? Ignore it.
683
+ elif getattr (self , attr_name ) is getattr (first_enum_base , attr_name , sentinel ):
684
+ add_to_ignored (attr_name )
685
+ # Is an "enum dunder", and is either user-defined or defined by a mixin class?
686
+ # Add it to dir() output.
687
+ else :
688
+ add_to_dir (attr_name )
689
+
690
+ # sort the output before returning it, so that the result is deterministic.
691
+ return sorted (cls_dir )
642
692
643
693
def __getattr__ (cls , name ):
644
694
"""
@@ -985,13 +1035,10 @@ def __dir__(self):
985
1035
"""
986
1036
Returns all members and all public methods
987
1037
"""
988
- added_behavior = [
989
- m
990
- for cls in self .__class__ .mro ()
991
- for m in cls .__dict__
992
- if m [0 ] != '_' and m not in self ._member_map_
993
- ] + [m for m in self .__dict__ if m [0 ] != '_' ]
994
- return (['__class__' , '__doc__' , '__module__' ] + added_behavior )
1038
+ cls = type (self )
1039
+ to_exclude = {'__members__' , '__init__' , '__new__' , * cls ._member_names_ }
1040
+ filtered_self_dict = (name for name in self .__dict__ if not name .startswith ('_' ))
1041
+ return sorted ({'name' , 'value' , * dir (cls ), * filtered_self_dict } - to_exclude )
995
1042
996
1043
def __format__ (self , format_spec ):
997
1044
"""
0 commit comments