Skip to content


Repository files navigation




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).

Basic usage

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 == "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'])

Optional token attributes

# 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

TokenAttribute inheritance

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}

TokenAttribute container inheritance

class MyToken(Token):

    name = TokenAttribute()
    stuff = TokenAttribute()

class MyTokens(TokenContainer):

    foo = MyToken(stuff='Hello')
    bar = MyToken(stuff='World')

assert in MyTokens

class MoreTokens(MyTokens):
    boink = MyToken(stuff='Other Stuff')

assert in MoreTokens

assert list(MoreTokens) == [,, MoreTokens.boink]
assert is

For more tri.token examples, please have a look at the contents of tests/ in the installation directory.

Running tests

You need tox installed then just make test.