Skip to content

Decide the fate of enum.bin #136763

Description

@azerty-svg

First, this module defines a bin function that is not present in the documentation. Such a method should be documented or made private. Indeed, it would feel weird for a user to use from enum import * and have the standard bin() function overriden.

The commit that introcuced this function is #7aaeb2a
This function only appears to have be used by the commit for Flag.missing, where it's present two or three times

Thus, i guess this function was created for internal code refactorisation.
This is a normal use for a function. However, a function used for this should not be available to an importer of enum. It should especially not be able to cause problems in the code of the importer.

I suggest renaming the function _bin and replacing existing occurences of call to this function by a call to its new name.

Broadlier speaking, I do not see a valid reason for the import of builtins in the enum module, because it is bad practice: the only use case is builtin functions having been redefined inside the code, which is a source of confusions and errors and therefore not good practice.

This probably means they're other problems like this in the enum module. One I found is the decorator @property. I did not investigated this one further.

Activity

  1. added
    stdlibStandard Library Python modules in the Lib/ directory
    docsDocumentation in the Doc dir
    on Jul 18, 2025
  2. StanFromIreland commented on Jul 18, 2025

    @StanFromIreland
    Member

    Please do not throw multiple things into one issue. The comment can just be a PR if you have a suggestion. For the function, I use this issue.

    CC @ethanfurman

  3. changed the title [-]A few things to correct in the enum module[/-] [+]Undocumented bin() in enum[/+] on Jul 18, 2025
  4. picnixz commented on Jul 18, 2025

    @picnixz
    Member

    Indeed, it would feel weird for a user to use from enum import * and have the standard bin() function overriden

    This wouldn't happen as the module defines an __all__ which, in this case, doesn't re-export bin. So users importing the module wouldn't be annoyed (or they must access it explicitly). Now, I don't know if it's was an oversight that we didn't document bin or not. Ethan, has bin ever been considered part of the public API?

    The nonsense in this code, present in the class EnumCheck, is the comment about powers of 1.

    This is indeed a typo, probably because it was copy-pasted from above.

    Broadlier speaking, I do not see a valid reason for the import of builtins in the enum module, because it is bad practice: the only use case is builtin functions having been redefined inside the code, which is a source of confusions and errors and therefore not good practice.

    Sometimes practility beats purity. If the function was meant to be public, enum.bin is clearer than anything else (it's really the same as bin with a few tweaks; calling enum_bin would be overly verbose IMO).

  5. changed the title [-]Undocumented bin() in enum[/-] [+]Undocumented `enum.bin`[/+] on Jul 18, 2025
  6. azerty-svg commented on Jul 18, 2025

    @azerty-svg
    Author

    I understand, but it doesn't feel to me like it's meant to be public in this case. Because this function is not the kind of things one would expect to find in a module about enumerations.

  7. picnixz commented on Jul 18, 2025

    @picnixz
    Member

    As a utility function that is meant only for enums, this is the place where one would expect. It can be a helper for people who want to subclass the enum type itself. However, if it was meant to be private, we need to first deprecate it (it's publicly named and we could break stuff) and the make it private. If it was meant to be part of the public API, then we should document it. I don't really know whether it was meant to be private or not though.

    Concerning the import to builtins, I think it's really because of enum.property, which is however documented: https://docs.python.org/3/library/enum.html#enum.property, so this is why we need to import builtins to be able to use it internally (here, we can't change the name of this exposed function without going through a deprecation period and probably a Discourse post and lots of discussions).

  8. changed the title [-]Undocumented `enum.bin`[/-] [+]Decide the fate of `enum.bin`[/+] on Jul 18, 2025
  9. azerty-svg commented on Jul 18, 2025

    @azerty-svg
    Author

    If we document enum.bin, I think we can use the docstring. It looks explaining enough to me:
    `"""
    Like built-in bin(), except negative values are represented in
    twos-compliment, and the leading bit always indicates sign
    (0=positive, 1=negative).

    >>> bin(10)
    '0b0 1010'
    >>> bin(~10)   # ~10 is -11
    '0b1 0101'
    """`
    

    We would just need to correct the spelling mistake: it's two's complement and not compliment

  10. picnixz commented on Jul 18, 2025

    @picnixz
    Member

    Yes, but I first want to know if it was meant to be public (which is why I changed the issue's title).

  11. azerty-svg commented on Jul 18, 2025

    @azerty-svg
    Author

    We could maybe ask @ethanfurman , who made the initial commit. His last commit to enum.py is from July, 10. And it look's on his contribution activity that he's still active

  12. azerty-svg commented on Jul 18, 2025

    @azerty-svg
    Author

    Though he may not remember what he thought back in 2021, date of the initial commit

  13. azerty-svg commented on Jul 18, 2025

    @azerty-svg
    Author

    Looking at previous discussion, I do not see a mention of enum.bin in the initial commit nor in the pull request that came along ( #24215)

  14. azerty-svg commented on Jul 18, 2025

    @azerty-svg
    Author

    For information: I splitted this issue in 2. one fro enum.bin (this one) and another one for the comment

  15. ethanfurman commented on Jul 18, 2025

    @ethanfurman
    Member

    When a module has an __all__, that is the public API. Any functions not in __all__ do not need a leading underscore. I do intend for enum.bin() to become public at some point -- I suppose 3.15 will do.

  16. self-assigned this
    on Jul 18, 2025
  17. picnixz commented on Jul 19, 2025

    @picnixz
    Member

    Actually, I just saw that property is exposed in __all__. May I suggest that we don't expose bin like that as it can shadows built-ins? I don't think it has ever been an issue in practice but this should probably avoided (the OP did mention the possibility for bin to shadow the built-in bin).

    Unfortunately, I don't think we can change property being in __all__ in a backwards compatible manner as we can't really know whether someone is having from enum import * and @property somewhere, without knowing that this @property is actually @enum.property (maybe their tests don't cover it).

    But if we could avoid making the same thing with bin, I think it would be preferrable (enum can definitely be imported in an util.py file that uses bin for some reasons as well).

  18. ethanfurman commented on Jul 19, 2025

    @ethanfurman
    Member

    Using from some_module import * on a module not designed for that usage is bad practice, and the enum module was not designed with that usage in mind.

  19. picnixz commented on Jul 19, 2025

    @picnixz
    Member

    But the fact that it has an __all__ means that it should be supported right? or at least that's what I always thought. Maybe we should document that it is not recommended to do it (I don't know if it's not already documented)

  20. serhiy-storchaka commented on Jul 19, 2025

    @serhiy-storchaka
    Member

    __all__ has more than one function. It specifies the names for star-import, and it also specifies the public interface shown in pydoc. So names can be added to __all__ for the sake of pydoc. It does not mean that using star-import with that module is recommended.

    On the other hand, sometimes (in rare cases) public names are intentionally not added to __all__ if using star-import with a module is common.

  21. picnixz commented on Jul 19, 2025

    @picnixz
    Member

    I actually thought that from enum import * was a quite well-used pattern as it's common for enum to be imported with two members (the type and auto), no members (and use enum.*) or more than two members (in which case we have a very long list of members and maybe using enum.* would make the line length overflows).

    However, https://grep.app/search?q=from%20enum%20import%20* shows that the pattern is quite rare actually! so maybe we don't even need to document it and we can leave the warning to linters instead (as their warnings are usually read more often that the online docs I think).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

docsDocumentation in the Doc dirstdlibStandard Library Python modules in the Lib/ directory

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions