Skip to content

Instantly share code, notes, and snippets.

@victorusachev
Last active March 1, 2020 21:49
Show Gist options
  • Save victorusachev/bd8ea118a228b5bba194ac3bac8909f1 to your computer and use it in GitHub Desktop.
Save victorusachev/bd8ea118a228b5bba194ac3bac8909f1 to your computer and use it in GitHub Desktop.
from collections import OrderedDict
empty = object()
class Item:
"""
Descriptor
Usage:
>>> class SomethingType:
>>> FIRST_TYPE = Item()
>>> SECOND_TYPE = Item('special')
>>> assert SomethingType.FIRST_TYPE == 'first_type'
>>> assert SomethingType.SECOND_TYPE == 'special'
"""
def __init__(self, value: str = empty):
self._value = value
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
self._value = value
def __set_name__(self, owner, name):
if not name.isupper():
raise NameError(
f'{owner.__qualname__}.{name} must be in uppercase'
)
if self._value is empty:
self._value = name.lower()
class ChoiceMeta(type):
_items: OrderedDict
def __new__(mcs, name, bases, attrs):
attrs['_items'] = mcs._get_items(bases, attrs)
cls = super(ChoiceMeta, mcs).__new__(mcs, name, bases, attrs)
return cls
@classmethod
def _get_items(mcs, bases, attrs):
items = [
(name, item)
for name, item in attrs.items()
if isinstance(item, Item)
]
known = set(attrs)
base_items = [
(known.add(name) or name, item)
for base in bases if hasattr(base, '_items')
for name, item in base._items.items() if name not in known
]
return OrderedDict(base_items + items)
def __iter__(cls):
for k, v in cls._items.items():
if isinstance(v, Item):
yield (k, getattr(cls, k))
@property
def choices(cls):
return list(iter(cls))
class Choice(metaclass=ChoiceMeta):
@property
def choices(self):
return self.__class__.choices
if __name__ == '__main__':
class BaseChoice(Choice):
BASE = Item('special')
class ModeChoice(BaseChoice):
ADDRESS = Item()
KEY = Item()
print(BaseChoice.choices)
print(BaseChoice().choices)
print(BaseChoice.BASE)
print(BaseChoice().BASE)
print(ModeChoice.choices)
print(ModeChoice().choices)
print(ModeChoice.BASE)
print(ModeChoice.ADDRESS)
print(ModeChoice.KEY)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment