API

class spectate.models.Dict(*args: Any, **kwargs: Any)[source]

A spectate.mvc enabled dict.

clear()None.  Remove all items from D.
pop(k[, d])v, remove specified key and return the corresponding value.

If key is not found, d is returned if given, otherwise KeyError is raised

setdefault(key, default=None, /)

Insert key with a value of default if key is not in the dictionary.

Return the value for key if key is in the dictionary, else default.

update([E, ]**F)None.  Update D from dict/iterable E and F.

If E is present and has a .keys() method, then does: for k in E: D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]

class spectate.models.List(*args: Any, **kwargs: Any)[source]

A spectate.mvc enabled list.

append(object, /)

Append object to the end of the list.

clear()

Remove all items from list.

extend(iterable, /)

Extend list by appending elements from the iterable.

insert(index, object, /)

Insert object before index.

pop(index=-1, /)

Remove and return item at index (default last).

Raises IndexError if list is empty or index is out of range.

remove(value, /)

Remove first occurrence of value.

Raises ValueError if the value is not present.

reverse()

Reverse IN PLACE.

sort(*, key=None, reverse=False)

Stable sort IN PLACE.

class spectate.models.Object(*args: Any, **kwargs: Any)[source]

A spectat.mvc enabled object.

class spectate.models.Set(*args: Any, **kwargs: Any)[source]

A spectate.mvc enabled set.

add()

Add an element to a set.

This has no effect if the element is already present.

clear()

Remove all elements from this set.

difference_update()

Remove all elements of another set from this set.

discard()

Remove an element from a set if it is a member.

If the element is not a member, do nothing.

intersection_update()

Update a set with the intersection of itself and another.

pop()

Remove and return an arbitrary set element. Raises KeyError if the set is empty.

remove()

Remove an element from a set; it must be a member.

If the element is not a member, raise a KeyError.

symmetric_difference_update()

Update a set with the symmetric difference of itself and another.

update()

Update a set with the union of itself and others.

class spectate.models.Structure(*args: Any, **kwargs: Any)[source]
class spectate.base.Control(methods, *, before=None, after=None)[source]

An object used to define control methods on a Model

A “control” method on a Model is one which reacts to another method being called. For example there is a control method on the List which responds when append() is called.

A control method is a slightly modified beforeback or afterback that accepts an extra notify argument. These are added to a control object by calling Control.before() or Control.after() respectively. The notify arugment is a function which allows a control method to send messages to views that are registered to a Model.

Parameters
  • methods (Union[list, tuple, str]) – The names of the methods on the model which this control will react to When they are calthrough the Nodeled. This is either a comma seperated string, or a list of strings.

  • before (Union[Callable, str, None]) – A control method that reacts before any of the given methods are called. If given as a callable, then that function will be used as the callback. If given as a string, then the control will look up a method with that name when reacting (useful when subclassing).

  • after (Union[Callable, str, None]) – A control method that reacts after any of the given methods are alled. If given as a callable, then that function will be used as the callback. If given as a string, then the control will look up a method with that name when reacting (useful when subclassing).

Examples

Control methods are registered to a Control with a str or function. A string may refer to the name of a method on a Model while a function should be decorated under the same name as the Control object to preserve the namespace.

from spectate import mvc

class X(mvc.Model):

    _control_method = mvc.Control("method").before("_control_before_method")

    def _control_before_method(self, call, notify):
        print("before")

    # Note how the method uses the same name. It
    # would be redundant to use a different one.
    @_control_a.after
    def _control_method(self, answer, notify):
        print("after")

    def method(self):
        print("during")

x = X()
x.method()
before
during
after
class spectate.base.Model(*args: Any, **kwargs: Any)[source]

An object that can be controlled and viewed.

Users should define Control methods and then view() the change events those controls emit. This process starts by defining controls on a subclass of Model.

Examples

from specate import mvc

class Object(Model):

    _control_attr_change = Control(
        "__setattr__, __delattr__",
        before="_control_before_attr_change",
        after="_control_after_attr_change",
    )

    def __init__(self, *args, **kwargs):
        for k, v in dict(*args, **kwargs).items():
            setattr(self, k, v)

    def _control_before_attr_change(self, call, notify):
        return call["args"][0], getattr(self, call["args"][0], Undefined)

    def _control_after_attr_change(self, answer, notify):
        attr, old = answer["before"]
        new = getattr(self, attr, Undefined)
        if new != old:
            notify(attr=attr, old=old, new=new)

o = Object()

@mvc.view(o)
def printer(o, events):
    for e in events:
        print(e)

Attach all of the source’s present and future view functions to the targets.

Parameters
  • source (Model) – The model whose view functions will be attached to the targets.

  • targets (Model) – The models that will acquire the source’s view functions.

Return type

None

spectate.base.notifier(model)[source]

Manually send notifications to the given model.

Parameters

model (Model) – The model whose views will recieve notifications

Return type

Iterator[Callable[…, None]]

Returns

A function whose keyword arguments become event data.

Example

m = Model()

@view(m)
def printer(m, events):
    for e in events:
        print(e)

with notifier(m) as notify:
    # the view should print out this event
    notify(x=1, y=2)

Remove all of the source’s present and future view functions from the targets.

Parameters
  • source (Model) – The model whose view functions will be removed from the targets.

  • targets (Model) – The models that will no longer share view functions with the source.

Return type

None

spectate.base.unview(model, function)[source]

Remove a view callbcak from a model.

Parameters
  • model (Model) – The model which contains the view function.

  • function (Callable[[Model, Tuple[Dict[str, Any], …]], None]) – The callable which was registered to the model as a view.

Raises

ValueError – If the given function is not a view of the given model.

Return type

None

spectate.base.view(model: Model)Callable[[_F], _F][source]
spectate.base.view(model: Model, function: Callable[[Model, Tuple[Dict[str, Any], ]], None])None

A decorator for registering a callback to a model

Parameters

model (Model) – the model object whose changes the callback should respond to.

Examples

from spectate import mvc

items = mvc.List()

@mvc.view(items)
def printer(items, events):
    for e in events:
        print(e)

items.append(1)
Return type

Optional[Callable[[~_F], ~_F]]

spectate.base.views(model)[source]

Return a model’s views keyed on what events they respond to.

Model views are added by calling view() on a model.

Return type

List[Callable[[Model, Tuple[Dict[str, Any], …]], None]]

spectate.events.hold(model, reducer=None)[source]

Temporarilly withold change events in a modifiable list.

All changes that are captured within a “hold” context are forwarded to a list which is yielded to the user before being sent to views of the given model. If desired, the user may modify the list of events before the context is left in order to change the events that are ultimately sent to the model’s views.

Parameters
  • model (Model) – The model object whose change events will be temporarilly witheld.

  • reducer (Optional[Callable[[Model, List[Dict[str, Any]]], List[Dict[str, Any]]]]) – A function for modifying the events list at the end of the context. Its signature is (model, events) -> new_events where model is the given model, events is the complete list of events produced in the context, and the returned new_events is a list of events that will actuall be distributed to views.

Notes

All changes witheld from views will be sent as a single notification. For example if you view a specate.mvc.models.List and its append() method is called three times within a hold() context,

Examples

Note how the event from l.append(1) is omitted from the printed statements.

from spectate import mvc

l = mvc.List()

mvc.view(d, lambda d, e: list(map(print, e)))

with mvc.hold(l) as events:
    l.append(1)
    l.append(2)

    del events[0]
{'index': 1, 'old': Undefined, 'new': 2}
Return type

Iterator[List[Dict[str, Any]]]

spectate.events.mute(model)[source]

Block a model’s views from being notified.

All changes within a “mute” context will be blocked. No content is yielded to the user as in hold(), and the views of the model are never notified that changes took place.

Parameters

mode – The model whose change events will be blocked.

Examples

The view is never called due to the mute() context:

from spectate import mvc

l = mvc.List()

@mvc.view(l)
def raises(events):
    raise ValueError("Events occured!")

with mvc.mute(l):
    l.append(1)
Return type

Iterator[None]

spectate.events.rollback(model, undo=None, reducer=None)[source]

Withold events if an error occurs.

Generall operate

Parameters
  • model (Model) – The model object whose change events may be witheld.

  • undo (Optional[Callable[[Model, Tuple[Dict[str, Any], …], Exception], None]]) – An optional function for reversing any changes that may have taken place. Its signature is (model, events, error) where model is the given model, events is a list of all the events that took place, and error is the exception that was riased. Any changes that you make to the model within this function will not produce events.

Examples

Simple supression of events:

from spectate import mvc

d = mvc.Dict()

@mvc.view(d)
def should_not_be_called(d, events):
    # we never call this view
    assert False

try:
    with mvc.rollback(d):
        d["a"] = 1
        d["b"]  # key doesn't exist
except KeyError:
    pass

Undo changes for a dictionary:

from spectate import mvc

def undo_dict_changes(model, events, error):
    seen = set()
    for e in reversed(events):
        if e.old is mvc.Undefined:
            del model[e.key]
        else:
            model[e.key] = e.old

try:
    with mvc.rollback(d, undo=undo_dict_changes):
        d["a"] = 1
        d["b"] = 2
        print(d)
        d["c"]
except KeyError:
    pass
print(d)
{'a': 1, 'b': 2}
{}
Return type

Iterator[None]

A modules which exports specate’s Model-View-Controller utilities in a common namespace

For more info: