Utility classes

class coaster.utils.classes.NameTitle(name, title)
name

Alias for field number 0

title

Alias for field number 1

class coaster.utils.classes.LabeledEnum[source]

Labeled enumerations. Declarate an enumeration with values and labels (for use in UI):

>>> class MY_ENUM(LabeledEnum):
...     FIRST = (1, "First")
...     THIRD = (3, "Third")
...     SECOND = (2, "Second")

LabeledEnum will convert any attribute that is a 2-tuple into a value and label pair. Access values as direct attributes of the enumeration:

>>> MY_ENUM.FIRST
1
>>> MY_ENUM.SECOND
2
>>> MY_ENUM.THIRD
3

Access labels via dictionary lookup on the enumeration:

>>> MY_ENUM[MY_ENUM.FIRST]
'First'
>>> MY_ENUM[2]
'Second'
>>> MY_ENUM.get(3)
'Third'
>>> MY_ENUM.get(4) is None
True

Retrieve a full list of values and labels with .items(). Definition order is preserved in Python 3.x, but not in 2.x:

>>> sorted(MY_ENUM.items())
[(1, 'First'), (2, 'Second'), (3, 'Third')]
>>> sorted(MY_ENUM.keys())
[1, 2, 3]
>>> sorted(MY_ENUM.values())
['First', 'Second', 'Third']

However, if you really want ordering in Python 2.x, add an __order__ list. Anything not in it will default to Python’s ordering:

>>> class RSVP(LabeledEnum):
...     RSVP_Y = ('Y', "Yes")
...     RSVP_N = ('N', "No")
...     RSVP_M = ('M', "Maybe")
...     RSVP_U = ('U', "Unknown")
...     RSVP_A = ('A', "Awaiting")
...     __order__ = (RSVP_Y, RSVP_N, RSVP_M, RSVP_A)

>>> RSVP.items()
[('Y', 'Yes'), ('N', 'No'), ('M', 'Maybe'), ('A', 'Awaiting'), ('U', 'Unknown')]

Three value tuples are assumed to be (value, name, title) and the name and title are converted into NameTitle(name, title):

>>> class NAME_ENUM(LabeledEnum):
...     FIRST = (1, 'first', "First")
...     THIRD = (3, 'third', "Third")
...     SECOND = (2, 'second', "Second")
...     __order__ = (FIRST, SECOND, THIRD)

>>> NAME_ENUM.FIRST
1
>>> NAME_ENUM[NAME_ENUM.FIRST]
NameTitle(name='first', title='First')
>>> NAME_ENUM[NAME_ENUM.SECOND].name
'second'
>>> NAME_ENUM[NAME_ENUM.THIRD].title
'Third'

To make it easier to use with forms and to hide the actual values, a list of (name, title) pairs is available:

>>> NAME_ENUM.nametitles()
[('first', 'First'), ('second', 'Second'), ('third', 'Third')]

Given a name, the value can be looked up:

>>> NAME_ENUM.value_for('first')
1
>>> NAME_ENUM.value_for('second')
2

Values can be grouped together using a set, for performing “in” operations. These do not have labels and cannot be accessed via dictionary access:

>>> class RSVP_EXTRA(LabeledEnum):
...     RSVP_Y = ('Y', "Yes")
...     RSVP_N = ('N', "No")
...     RSVP_M = ('M', "Maybe")
...     RSVP_U = ('U', "Unknown")
...     RSVP_A = ('A', "Awaiting")
...     __order__ = (RSVP_Y, RSVP_N, RSVP_M, RSVP_U, RSVP_A)
...     UNCERTAIN = {RSVP_M, RSVP_U, 'A'}

>>> isinstance(RSVP_EXTRA.UNCERTAIN, set)
True
>>> sorted(RSVP_EXTRA.UNCERTAIN)
['A', 'M', 'U']
>>> 'N' in RSVP_EXTRA.UNCERTAIN
False
>>> 'M' in RSVP_EXTRA.UNCERTAIN
True
>>> RSVP_EXTRA.RSVP_U in RSVP_EXTRA.UNCERTAIN
True

Labels are stored internally in a dictionary named __labels__, mapping the value to the label. Symbol names are stored in __names__, mapping name to the value. The label dictionary will only contain values processed using the tuple syntax, which excludes grouped values, while the names dictionary will contain both, but will exclude anything else found in the class that could not be processed (use __dict__ for everything):

>>> list(RSVP_EXTRA.__labels__.keys())
['Y', 'N', 'M', 'U', 'A']
>>> list(RSVP_EXTRA.__names__.keys())
['RSVP_Y', 'RSVP_N', 'RSVP_M', 'RSVP_U', 'RSVP_A', 'UNCERTAIN']
class coaster.utils.classes.InspectableSet(members=())[source]

Given a set, mimics a read-only dictionary where the items are keys and have a value of True, and any other key has a value of False. Also supports attribute access. Useful in templates to simplify membership inspection:

>>> myset = InspectableSet({'member', 'other'})
>>> 'member' in myset
True
>>> 'random' in myset
False
>>> myset.member
True
>>> myset.random
False
>>> myset['member']
True
>>> myset['random']
False
>>> joinset = myset | {'added'}
>>> isinstance(joinset, InspectableSet)
True
>>> joinset = joinset | InspectableSet({'inspectable'})
>>> isinstance(joinset, InspectableSet)
True
>>> 'member' in joinset
True
>>> 'other' in joinset
True
>>> 'added' in joinset
True
>>> 'inspectable' in joinset
True
>>> emptyset = InspectableSet()
>>> len(emptyset)
0
class coaster.utils.classes.classmethodproperty(func)[source]

Class method decorator to make class methods behave like properties:

>>> class Foo:
...     @classmethodproperty
...     def test(cls):
...         return repr(cls)
...

Works on classes:

>>> Foo.test
"<class 'coaster.utils.classes.Foo'>"

Works on class instances:

>>> Foo().test
"<class 'coaster.utils.classes.Foo'>"

Works on subclasses too:

>>> class Bar(Foo):
...     pass
...
>>> Bar.test
"<class 'coaster.utils.classes.Bar'>"
>>> Bar().test
"<class 'coaster.utils.classes.Bar'>"

Due to limitations in Python’s descriptor API, classmethodproperty can block write and delete access on an instance…

>>> Foo().test = 'bar'
Traceback (most recent call last):
AttributeError: test is read-only
>>> del Foo().test
Traceback (most recent call last):
AttributeError: test is read-only

…but not on the class itself:

>>> Foo.test = 'bar'
>>> Foo.test
'bar'