tri.token provides enriched enum functionality. tri.token enum structures are declared using:
- TokenAttribute: overridable attribute definitions with support for dynamic values.
- Token: holds TokenAttributes objects.
- TokenContainer: holds Token objects.
In other words: a Token is an enum which has TokenInstance members. Token instances are declared within TokenContainer(s).
from tri_token import Token, TokenAttribute, TokenContainer, PRESENT
class Taste(Token):
name = TokenAttribute()
display_name = TokenAttribute(value=lambda name, **_: name.upper() + '!!')
opinion = TokenAttribute()
class Tastes(TokenContainer):
vanilla = Taste()
pecan_nut = Taste(display_name="pecan nutz", opinion="Tasty")
# A TokenContainer is a collection of Token objects.
assert Tastes.vanilla in Tastes
# The order of Token objects in a TokenContainer is by order of declaration.
assert list(Tastes) == [Tastes.vanilla, Tastes.pecan_nut]
assert list(Tastes) != [Tastes.pecan_nut, Tastes.vanilla]
# Magic for 'name' TokenAttribute. It is set automatically from the token declaration within it's container.
assert Tastes.vanilla.name == "vanilla"
# A TokenAttribute will have a None value if not set during Token instantiation.
assert Tastes.vanilla.opinion is None
# A TokenAttribute can have a dynamic value, derived from the invocation to the callable
# set as 'value' in the TokenAttribute definition
# (see declaration of 'display_name' TokenAttribute further up in the code).
# The real value of the token attribute will be the return value of an invocation to said callable.
# The invocation will receive the values of all other token attributes passed as keyword arguments.
assert Tastes.vanilla.display_name == "VANILLA!!"
# TokenAttribute dynamic value behavior is overridden/not used if value is set explicitly during Token instantiation.
assert Tastes.pecan_nut.display_name == "pecan nutz"
# A TokenContainer can be rendered as csv, excel, rst etc
assert """\
+--------------+---------+
| display_name | opinion |
+==============+=========+
| VANILLA!! | |
+--------------+---------+
| pecan nutz | Tasty |
+--------------+---------+\
""" == Tastes.to_rst(['display_name', 'opinion'])
# A TokenAttribute may be declared as having optional dynamic values.
# That is, we want these dynamic attributes to be evaluated sometimes, but not always.
# In the example below, we want some superheroes to have homes, but not others.
SUPERHERO_HOMES = {'superman': 'Fortress of Solitude',
'batman': 'Batcave'}
class Superhero(Token):
name = TokenAttribute()
home = TokenAttribute(optional_value=lambda name, **_: SUPERHERO_HOMES[name])
# The PRESENT special value is used during Token instantiation to decide what
# optional token attributes should be evaluated.
class Superheroes(TokenContainer):
batman = Superhero(home=PRESENT)
hawkman = Superhero()
wonder_woman = Superhero(home='Themyscira')
# Batman has a home, but poor Hawkman does not.
assert Superheroes.batman.home == 'Batcave'
assert Superheroes.hawkman.home is None
# Just as with dynamic attributes, the logic for TokenAttribute optional dynamic values is overridden
if value is set explicitly during Token instantiation.
assert Superheroes.wonder_woman.home = 'Themyscira'
# As a shortcut, PRESENT for specific optional token attributes may be assigned to
# variables, and used in declarations, for enhanced readability.
# This is useful when one has tokens with many attributes declared using dynamic values,
# but we don't want all of them to be evaluated in all tokens.
home = PRESENT('home')
class Superheroes(TokenContainer):
batman = Superhero(home)
hawkman = Superhero()
# Again, Batman has a home, but poor Hawkman does not.
assert Superheroes.batman.home == 'Batcave'
assert Superheroes.hawkman.home is None
class FooToken(Token):
foo = TokenAttribute(value=lambda **kwargs: 'foo_value')
class BarToken(Token):
bar = TokenAttribute()
class FieToken(FooToken, BarToken):
fie = TokenAttribute()
class FooBarFieTokenContainer(TokenContainer):
t = FieToken(fie=3)
assert dict(FooBarFieTokenContainer.t) == {'foo': 'foo_value', 'bar': None, 'name': 't', 'fie': 3}
class MyToken(Token):
name = TokenAttribute()
stuff = TokenAttribute()
class MyTokens(TokenContainer):
foo = MyToken(stuff='Hello')
bar = MyToken(stuff='World')
assert MyTokens.foo in MyTokens
class MoreTokens(MyTokens):
boink = MyToken(stuff='Other Stuff')
assert MyTokens.foo in MoreTokens
assert list(MoreTokens) == [MyTokens.foo, MyTokens.bar, MoreTokens.boink]
assert MoreTokens.foo is MyTokens.foo
For more tri.token examples, please have a look at the contents of tests/test_tokens.py in the installation directory.
You need tox installed then just make test.
BSD