Andrew Barnert wrote:
On Oct 20, 2019, at 03:36, Steve Jorgensen stevej@stevej.name wrote:
And there are multiple PyPI projects that build Django choices on top of Enum, which show that the only thing you need is to override __getitem__ to replace its mapping behavior with sequence behavior. I believe this was also one of the examples of what you can build for yourself on top of EnumMeta in Nick Coghlan’s blog post explaining the design of enum. Maybe the additional functionality in Enum gets in your way for some reason I don’t understand, that doesn’t affect other people who want Django choices. But otherwise, I don’t see how it really is any different. And again, none of this different-from-Enum behavior is relevant to your problem anyway. The fact that you think it is, and think it can’t be done with Enum, makes it harder to understand where the actual problem is. Maybe you’re not just trying to do the same magic that all the autoenum/simplenum/etc. packages on PyPI and the PoC code from the PEP and the section in Nick’s blog post all do (in a couple different ways), or maybe it’s insufficiently magic for your purposes, but without knowing how and why it’s different or insufficient it’s impossible to know whether there’s an actual limitation to fix in Python, or just a problem in your design or implementation that could be easily fixed to work in existing Python.
So… You're exactly right. I just had a very limited understanding of what enum was about and had not dug into it deeply enough to find out how flexible it really is. Here's an implementation that I came up with in a short hacking session using enum. Tests: from choices import ChoiceEnum, Label as L def test_creates_auto_valued_auto_labeled_choices(): class Food(ChoiceEnum): APPLE = () ICED_TEA = () assert tuple(tuple(fc) for fc in Food) == ( ('APPLE', 'Apple'), ('ICED_TEA', 'Iced Tea'), ) def test_creates_auto_labeled_choices_with_given_values(): class Food(ChoiceEnum): APPLE = 1 ICED_TEA = 2 assert tuple(tuple(fc) for fc in Food) == ( (1, 'Apple'), (2, 'Iced Tea'), ) def test_creates_choices_with_given_values_and_labels(): class Food(ChoiceEnum): CHICKEN_MCNUGGETS = (1, 'Chicken McNuggets') CHICKEN_PROVENCAL = (2, 'Chicken Provençal') assert tuple(tuple(fc) for fc in Food) == ( (1, 'Chicken McNuggets'), (2, 'Chicken Provençal'), ) def test_creates_auto_valued_choices_with_given_values(): class Food(ChoiceEnum): CHX_MN = L('Chicken McNuggets') CHX_PV = L('Chicken Provençal') assert tuple(tuple(fc) for fc in Food) == ( ('CHX_MN', 'Chicken McNuggets'), ('CHX_PV', 'Chicken Provençal'), ) Implementation: from enum import Enum class ChoiceEnum(Enum): def __init__(self, src=None, label=None): super().__init__() if isinstance(src, Label): value = None label = str(src) else: value = src self._value_ = self.name if value is None else value self.label = label or _label_from_name(self.name) def __getitem__(self, idx): if idx == 0: return self.value elif idx == 1: return self.label else: raise IndexError('Index value must be 0 or 1') def __len__(self): return 2 class Label(str): pass def _label_from_name(name): return name.replace('_', ' ').title()