Traitlite

Traitlite is a package which provides simple descriptors which you can use as class attributes to provide functionality such as type checking, variable validation, callbacks, making a variable read-only, etc. It does not require you to subclass anything in order to start using it.

Basic Example

In order to use a trait, simply define it as a class attribute for your variable.

from traitlite import TypeChecked
class Foo:
    bar = TypeChecked(int)

    def __init__(self, bar):
        self.bar = bar

Foo(3) # Is fine
Foo(3.0) # Raises exception

Accessing your variable is done just like with any normal variable.

foo = Foo(3)
print(foo.bar) # 3

To get the actual traitlite instance, access the class attribute (capital F in Foo).

print(Foo.bar) # <traitlite.traits.TypeChecked object at 0x10f1334a8>

To use different properties at the same time, simply add the traits to your variable.

from traitlite import ReadOnly, TypeChecked
class Foo:
    bar = TypeChecked(int) + ReadOnly()

    def __init__(self, bar):
        self.bar = bar

Foo(3.0) # Raises exception
foo = Foo(3) # Okay
foo.b = 2 # Raises exception because of read-only

Traits Documentation

class traitlite.ReadOnly

A trait which makes an attribute read-only after it has been set for the first time.

from traitlite import ReadOnly

class Foo:
    bar = ReadOnly()

    def __init__(self, bar):
        self.bar = bar # Setting it the first time is allowed

foo = Foo(3)
foo.bar = 4 # This raises an exception
class traitlite.TypeChecked(type_: Type[object])

A trait which performs a type check whenever the attribute is given a new value.

from traitlite import TypeChecked

class Foo:
    bar = TypeChecked(int)

    def __init__(self, bar):
        self.bar = bar

foo = Foo(3) # This is okay
foo = Foo(3.0) # This raises an exception
class traitlite.HasCallback(callbacks: Optional[List[Callable[Value, None]]] = None)

A trait which introduces callbacks which are called after the given attribute has been given a new value. The callbacks are callable objects which take the new value as an argument.

from traitlite import HasCallback

class Foo:
    bar = HasCallback()

def print_value(value):
    print('New value is:', value)

foo = Foo()

# We have to use the class here instead of the instance, and the
# instance is passed as the first argument.
Foo.bar.add_callback(foo, print_value)

foo.bar = 3 # New value is: 3

Additionally, a list of default callbacks can be passed to the constructor:

from traitlite import HasCallback

def print_value(value):
    print('New value is:', value)

class Foo:
    bar = HasCallback([print_value])

foo = Foo()

foo.bar = 3 # New value is: 3
add_callback(obj: Owner, func: Callable[Value, None]) → None

Adds a callback to be called after the value is changed. The callback must be a callable object which takes the new value as its argument.

For example, a callback which prints the new value would be:

def print_value(value):
    print('New value:', value)

ObjClass.add_callback(obj, print_value)

Note: The callable passed to add_callback() must have a signature, i.e. builtin functions like max cannot be used directly, but must be wrapped in a lambda.

static check_callback(func: Callable[Value, None]) → None

Raises an exception if the given callback function is not compatible with this trait.

Parameters:func (Callable[[Value], None]) – A compatible callback function.
class traitlite.HasCallbackDelta(callbacks: Optional[List[Callable[[Value, Value], None]]] = None)

A trait which introduces callbacks which are called after the given attribute has been given a new value. The callbacks are callable objects which take the old and new values as an argument.

from traitlite import HasCallbackDelta

class Foo:
    bar = HasCallbackDelta()

def print_value(old_value, new_value):
    print('Old value: {}, New value: {}'.format(
        old_value, new_value))

foo = Foo()

# We have to use the class here instead of the instance, and the
# instance is passed as the first argument.
Foo.bar.add_callback(foo, print_value)

foo.bar = 3 # Old value: None, New value: 3
foo.bar = 4 # Old value: 3, New value: 4

Additionally, a list of default callbacks can be passed to the constructor:

from traitlite import HasCallbackDelta

def print_value(old_value, new_value):
    print('Old value: {}, New value: {}'.format(
        old_value, new_value))

class Foo:
    bar = HasCallbackDelta([print_value])

foo = Foo()

foo.bar = 3 # Old value: None, New value: 3
foo.bar = 4 # Old value: 3, New value: 4
add_callback(obj: Owner, func: Callable[[Value, Value], None]) → None

Adds a callback to be called after the value is changed. The callback must be a callable object which takes the new and old values as its arguments.

For example, a callback which prints the old and new values would be:

def print_value(old_value, new_value):
    print('Old value:', old_value)
    print('New value:', new_value)

ObjClass.add_callback(obj, print_value)

Note: The callable passed to add_callback() must have a signature, i.e. builtin functions like max cannot be used directly, but must be wrapped in a lambda.

static check_callback(func: Callable[[Value, Value], None]) → None

Raises an exception if the given callback function is not compatible with this trait.

Parameters:func (Callable[[Value, Value], None]) – A compatible callback function.
class traitlite.HasValidator(validators: Optional[List[Callable[Value, Value]]] = None)

A trait which introduces validators which are called before the given attribute is given a new value. The validators take the new value as a single argument and must return the value which should be used.

from traitlite import HasValidator

class Foo:
    bar = HasValidator()

foo = Foo()

# We have to use the class here instead of the instance, and the
# instance is passed as the first argument.
Foo.bar.add_validator(foo, lambda x: max(0, x))
Foo.bar.add_validator(foo, lambda x: min(10, x))

foo.bar = 3
print(foo.bar) # 3
foo.bar = -1
print(foo.bar) # 0
foo.bar = 11
print(foo.bar) # 10

Additionally, a list of default validators can be passed to the constructor:

from traitlite import HasValidator

class Foo:
    bar = HasValidator([lambda x: max(0, x)])

foo = Foo()

foo.bar = 3
print(foo.bar) # 3
foo.bar = -1
print(foo.bar) # 0
add_validator(obj: Owner, func: Callable[Value, Value]) → None

Adds a validator to be called before the value is changed. The validator must be a callable object which takes the new value as its argument and must return the value which should be used.

For example, a validator which makes sure that the value is always greater or equal to zero would be:

ObjClass.add_validator(obj, lambda x: max(0, x))

Note: The callable passed to add_validator() must have a signature, i.e. builtin functions like max cannot be used directly, but must be wrapped in a lambda.

static check_validator(func: Callable[Value, Value])

Raises an exception if the given validator function is not compatible with this trait.

Parameters:func (Callable[[Value], Value]) – A compatible validator function.
class traitlite.HasValidatorDelta(validators: Optional[List[Callable[[Value, Value], Value]]] = None)

A trait which introduces validators which are called before the given attribute is given a new value. The validators take the new value as a single argument and must return the value which should be used.

from traitlite import HasValidatorDelta

class Foo:
    bar = HasValidatorDelta()

    def __init__(self):
        self.bar = 0

foo = Foo()

# We have to use the class here instead of the instance, and the
# instance is passed as the first argument.
Foo.bar.add_validator(foo, lambda x, y: max(x, y))

foo.bar = 3
print(foo.bar) # 3
foo.bar = 2
print(foo.bar) # 3
foo.bar = 4
print(foo.bar) # 4

Additionally, a list of default validators can be passed to the constructor:

from traitlite import HasValidatorDelta

class Foo:
    bar = HasValidatorDelta([lambda old, new: max(old or 0, new)])

foo = Foo()

foo.bar = 3
print(foo.bar) # 3
foo.bar = -1
print(foo.bar) # 3
add_validator(obj: Owner, func: Callable[[Value, Value], Value]) → None

Adds a validator to be called before the value is changed. The validator must be a callable object which takes the old and new values as its arguments and must return the value which should be used.

For example, a validator which only accepts increases in value would be:

ObjClass.add_validator(obj, lambda x, y: max(x, y))

Note: The callable passed to add_validator() must have a signature, i.e. builtin functions like max cannot be used directly, but must be wrapped in a lambda.

static check_validator(func: Callable[[Value, Value], Value])

Raises an exception if the given validator function is not compatible with this trait.

Parameters:func (Callable[[Value], Value]) – A compatible validator function.

Debug Traits

class traitlite.debug.BreakOnChange(callback: Callable[Value, bool])

A trait which starts the debugger when its value causes the specified callback to return True. The method called to start the debugger is the one set in the PYTHONBREAKPOINT environment variable, or pdb.set_trace if the variable is not defined.

from traitlite import BreakOnChange

class Foo:
    bar = BreakOnChange(lambda value: value < 0)

    def __init__(self, bar):
        self.bar = bar

foo = Foo(3)
foo.bar = 2 # breakpoint() is NOT called here.
foo.bar = -1 # breakpoint() is called here.
class traitlite.debug.BreakOnChangeDelta(callback: Callable[[Value, Value], bool])

A trait which starts the debugger when its old and new values cause the specified callback to return True. The method called to start the debugger is the one set in the PYTHONBREAKPOINT environment variable, or pdb.set_trace if the variable is not defined.

from traitlite import BreakOnChangeDelta

class Foo:
    bar = BreakOnChangeDelta(lambda old, new: old is not None and new < old)

    def __init__(self, bar):
        self.bar = bar

foo = Foo(3)
foo.bar = 5 # The new value is higher, so breakpoint is NOT called.
foo.bar = 4 # The new value is lower, so breakpoint is called.
class traitlite.debug.BreakOnRead

A trait which starts the debugger when it is accessed. The method called to start the debugger is the one set in the PYTHONBREAKPOINT environment variable, or pdb.set_trace if the variable is not defined.

from traitlite import BreakOnRead

class Foo:
    bar = BreakOnRead()

    def __init__(self, bar):
        self.bar = bar

foo = Foo(3)
print(foo.bar) # breakpoint() is called here.
class traitlite.debug.BreakOnWrite(ignore_initial: bool = False)

A trait which starts the debugger when it is accessed. The method called to start the debugger is the one set in the PYTHONBREAKPOINT environment variable, or pdb.set_trace if the variable is not defined.

from traitlite import BreakOnWrite

class Foo:
    bar = BreakOnWrite(ignore_initial=True)

    def __init__(self, bar):
        self.bar = bar

foo = Foo(3) # breakpoint is not called because of ignore_initial=True
foo.bar = 4 # breakpoint() is called here.