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 ofFalse
. 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'