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.

classification
Title: `dir` on Enum subclass doesn't expose parent class attributes
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.4, Python 3.5
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: ethan.furman Nosy List: cool-RR, ethan.furman, python-dev
Priority: normal Keywords:

Created on 2014-09-27 14:57 by cool-RR, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (14)
msg227681 - (view) Author: Ram Rachum (cool-RR) * Date: 2014-09-27 14:57
Calling `dir` on an enum subclass shows only the contents of that class, not its parent classes. In normal classes, you can do this: 

    Python 3.4.0 (v3.4.0:04f714765c13, Mar 16 2014, 19:25:23) [MSC v.1600 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> class A:
    ...   x = lambda self: 7
    ...
    >>> class B(a): pass
    ...
    >>> assert 'x' in dir(B)

But in enum subclasses, it fails:
    
    >>> import enum
    >>> class A(enum.Enum):
    ...   x = lambda self: 7
    ...
    >>> class B(A):
    ...   pass
    ...
    >>> assert 'x' in dir(B)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AssertionError
    >>>

Looks like the `__dir__` implementation needs to be tweaked.
msg227739 - (view) Author: Ram Rachum (cool-RR) * Date: 2014-09-27 21:58
Ethan, I saw you just marked this as "test needed". I just gave you code to reproduce this problem. Isn't that sufficient? Or you want me to do add it to Python's test suite?
msg227744 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2014-09-28 00:21
More just a note to myself to research this further, to see exactly what does and does not get inherited.  Any tests added will likely be more comprehensive than your example.
msg229006 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2014-10-10 15:02
Sample code:
--------------------------------------------------------------------------------
class AutoEnum(enum.Enum):
    """
    Automatically numbers enum members starting from 1.

    Includes support for a custom docstring per member.
    """
    __last_number__ = 0
    def __new__(cls, *args):
        """
        Ignores arguments (will be handled in __init__.
        """
        value = cls.__last_number__ + 1
        cls.__last_number__ = value
        obj = object.__new__(cls)
        obj._value_ = value
        return obj
    def __init__(self, *args):
        """
        Can handle 0 or 1 argument; more requires a custom __init__.
        0  = auto-number w/o docstring
        1  = auto-number w/ docstring
        2+ = needs custom __init__
        """
        if len(args) == 1 and isinstance(args[0], str):
            self.__doc__ = args[0]
        elif args:
            raise TypeError('%s not dealt with -- need custom __init__' % (args,))

class AddressSegment(AutoEnum):
    misc = "not currently tracked"
    ordinal = "N S E W NE NW SE SW"
    secondary = "apt bldg floor etc"
    street = "st ave blvd etc"

def huh(self):
    return 'did I show up?'
--------------------------------------------------------------------------------

The 'huh' function, shown above, does *not* show up in a dir of either AutoEnum nor AddressSegment, regardless of which class it is defined in -- this is because a directory of an Enum class is meant to show ['__class__', '__doc__', '__members__', '__module__'] + whatever Enum members have been defined.

However, a directory of an Enum member should show any normal methods* available on that member.  Currently, if 'huh' is defined in the final class it will show up, but if defined in a superclass it will not.

That is the bug.


* 'normal method' means any method not prefixed by any underscores.
msg229007 - (view) Author: Ram Rachum (cool-RR) * Date: 2014-10-10 15:07
Ethan: I'm very confused by your example. `huh` isn't even a method, why would it be included? Or are you saying "if it was a method"? I'm really not sure what your example is adding over mine, given that it's 10 times longer and more complex. I'm not sure why you'd want to demonstrate the bug in a 50-line code sample that has non-trivial logic when it's demonstrable in a simple 5-line code sample.
msg229010 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2014-10-10 15:18
Yes, the 'huh' function would have to be in either AutoEnum or AddressSegment to be included in the dir of an AddressSegment member.

Here's a shorter example:

(this works)
----------------------------------------------------
class SuperEnum(Enum):
    pass

class SubEnum(SuperEnum):
    sample = 5
    def visible(self):
        return "saw me, right?"

'visible' in dir(SubEnum.sample)
----------------------------------------------------

(this does not work)
----------------------------------------------------
class SuperEnum(Enum):
    def invisible(self):
        return "but you didn't see me!"

class SubEnum(SuperEnum):
    sample = 5

'invisible' in dir(SubEnum.sample)
----------------------------------------------------

Sorry for the noise.

Do please note that the dir where 'visible' showed up was on the Enum member, not the Enum class.
msg229307 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2014-10-14 15:59
New changeset cd2ede7f2ff5 by Ethan Furman in branch '3.4':
Issue22506: added methods on base Enum class now show up in dir of Enum subclass (3.4)
https://hg.python.org/cpython/rev/cd2ede7f2ff5

New changeset 424fbf011176 by Ethan Furman in branch 'default':
Issue22506: merge from 3.4
https://hg.python.org/cpython/rev/424fbf011176
msg229771 - (view) Author: Ram Rachum (cool-RR) * Date: 2014-10-21 18:20
Thanks for the fix Ethan. Will you also push this to the backport on PyPI?
msg229773 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2014-10-21 18:33
Thanks for the reminder!

...

Done.
msg229774 - (view) Author: Ram Rachum (cool-RR) * Date: 2014-10-21 18:44
Thanks!

Two questions:

1. Is there a GitHub repo for enum34? Couldn't find a link to it on the PyPI page.

2. Aren't 'name' and 'value' already included in `added_behavior`?
msg229775 - (view) Author: Ram Rachum (cool-RR) * Date: 2014-10-21 18:46
Also, aren't you excluding a lot of important magic methods from that `dir`?
msg229780 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2014-10-21 20:09
> 1. Is there a GitHub repo for enum34? Couldn't find a link to it on the PyPI page.

The repo is at:  https://bitbucket.org/stoneleaf/enum34


> 2. Aren't 'name' and 'value' already included in `added_behavior`?

They didn't used to be, but they are now.  I'll fix that (eventually).


> Also, aren't you excluding a lot of important magic methods from that `dir`?

We decided the dunder methods were not interesting, so declined to include them in the listing.  And no, we are not changing our minds on that.  ;)
msg229785 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2014-10-21 20:40
New changeset 9038b63dad52 by Ethan Furman in branch 'default':
Issue22506: remove name & value from __dir__ as they now show up automatically
https://hg.python.org/cpython/rev/9038b63dad52
msg410486 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2022-01-13 12:34
Ram asked:
---------
> Also, aren't you excluding a lot of important magic methods from that `dir`?

Ethan replied:
-------------
> We decided the dunder methods were not interesting, so declined to include
> them in the listing.  And no, we are not changing our minds on that.  ;)

------------------------------------------------------------------------------

UPDATE:  We have changed our minds, but only for enums that have a data type, such as `int` or `str`, mixed in.  This behavior should be in 3.11.
History
Date User Action Args
2022-04-11 14:58:08adminsetgithub: 66696
2022-01-13 12:34:16ethan.furmansetmessages: + msg410486
2014-10-21 20:40:59python-devsetmessages: + msg229785
2014-10-21 20:09:10ethan.furmansetmessages: + msg229780
2014-10-21 18:46:45cool-RRsetmessages: + msg229775
2014-10-21 18:44:19cool-RRsetmessages: + msg229774
2014-10-21 18:33:55ethan.furmansetmessages: + msg229773
2014-10-21 18:20:33cool-RRsetmessages: + msg229771
2014-10-14 16:01:02ethan.furmansetstatus: open -> closed
assignee: ethan.furman
stage: test needed -> resolved
resolution: fixed
versions: + Python 3.5
2014-10-14 15:59:54python-devsetnosy: + python-dev
messages: + msg229307
2014-10-10 15:18:53ethan.furmansetmessages: + msg229010
2014-10-10 15:07:36cool-RRsetmessages: + msg229007
2014-10-10 15:02:22ethan.furmansetmessages: + msg229006
2014-09-28 00:21:01ethan.furmansetmessages: + msg227744
2014-09-27 21:58:18cool-RRsetmessages: + msg227739
2014-09-27 21:55:23ethan.furmansetstage: test needed
2014-09-27 20:44:48ethan.furmansetnosy: + ethan.furman
2014-09-27 14:57:25cool-RRcreate