Skip to main content

Create classes whose instances have tracked methods

Project description

Build Status Documentation Status Version Info

Spectate

A library for Python 2 and 3 that can track changes to mutable data types.

With spectate complicated protocols for managing updates, don't need to be the outward responsibility of a user, and can instead be done automagically in the background. For instance, syncing the state between a server and client can controlled by spectate so user's don't have to.

Install

  • stable : pip install spectate
  • master : pip install git+https://github.com/rmorshea/spectate.git#egg=spectate
  • developer : git clone https://github.com/rmorshea/spectate && cd spectate/ && pip install -e . -r requirements.txt

Usage

from spectate import expose, watch

Expose any desired method of a class so it can be watched.

@expose('increment', 'decrement')
class Counter(object):

    def __init__(self):
        self.x = 0

    def increment(self, amount):
        self.x += amount

    def decrement(self, amount):
        self.x -= amount

Create an instance of the new watchable class, and get its spectator.

counter = Counter()
spectator = watch(counter)

Register a callback to the methods you exposed.

def changed(counter, answer):
    print(counter.x)

spectator.callback('increment', after=changed)
spectator.callback('decrement', after=changed)

Normal usage of the exposed methods will trigger your callback.

counter.increment(1)
counter.decrement(2)
counter.increment(3)
counter.decrement(4)

And thus print out the following:

1
-1
2
-2

... see more examples.

Kinds of Callbacks

Callbacks are registered to specific methods in pairs - one will be triggered before, and the other after, a call to that method is made. These two callbacks are referred to as "beforebacks" and "afterbacks" respectively. Defining both a beforeback and an afterback in each pair is not required, but doing so allows a beforeback to pass data to its corresponding afterback.

Beforebacks

Have a signature of (instance, call)

  • instance is the owner of the method
  • call is a dict with the keys
    • 'name' - the name of the method which was called
    • 'args' - the arguments which that method will call
    • 'kwargs' - the keywords which that method will call
  • Can return a value which gets passed on to its respective afterback, or a Closure that itself gets treated as an afterback.

Afterbacks

Have a signature of (instance, answer)

  • instance is the owner of the method
  • answer is a dict with the keys
    • 'name' - the name of the method which was called
    • 'value' - the value returned by the method
    • 'before' - the value returned by the respective beforeback

Closures

Have a signature of (value)

  • 'value' - the value returned by the method
  • All other information is already contained in the closures scope.
  • Should not return anything.

Under The Hood

Methods are tracked by using expose or expose_as to create a new class with MethodSpectator descriptors in the place of specified methods. Then, a user will create a Spectator using watch which is stored on the instance under the attribute _instance_spectator. When a MethodSpectator is accessed through an instance, the descriptor will return a wrapper that will redirect to Spectator.wrapper, which triggers the beforebacks and afterbacks registered to the instance.

The Bleeding Edge

The following user facing features are only available on master, untested and subject to frequent breaking changes!

An MVC Framework

If you're using Python 3.6 or greater, spectate provides an experimental Model-View-Controller (MVC) framework within the spectate.mvc package. Out of the box spectate.mvc provides three basic model types for list, dict, and set (Python's three built-in types that are mutable):

from spectate import mvc


d = mvc.Dict()
l = mvc.List()
s = mvc.Set()


@mvc.view(d)
@mvc.view(l)
@mvc.view(s)
def printer(event):
    print(event)


d['a'] = 1
l.append(2)
s.add(3)
{'key': 'a', 'old': Undefined, 'new': 1}
{'index': 0, 'old': Undefined, 'new': 2}
{'new': {3}, 'old': set()}

For most users these built-in types should be enough, however if you're adventurous, then you can define your own mvc.Model types.

Let's reconsider the simple Counter example from above, and see how we might reimplement that use case with spectate.mvc. To begin we must first create a class which inherits from mvc.Model - a base class where we can define mvc.control methods. These controls ultimately notify views which are hooked into the model:

from spectate import mvc


class Counter(mvc.Model):

    def __init__(self):
        self.x = 0

    def increment(self, amount):
        self.x += amount

    def decrement(self, amount):
        self.x -= amount

    # define a control for incrementing and decrementing
    _control_change = mvc.Control('increment', 'decrement')

    # register a beforeback to the control
    @_control_change.before
    def _control_change(self, call, notify):
        return self.x

    # register an afterback to the control
    @_control_change.after
    def _control_change(self, answer, notify):
        # Send an "event" dictionary to the Counter's views.
        notify(old=answer.before, new=self.x)

Once we've defined our Model and its control methods, we can then use mvc.view as we saw above with List, Dict, and Set:

counter = Counter()


@mvc.view(counter)
def printer(event):
    print(event)


counter.increment(1)
counter.decrement(2)
counter.increment(3)
counter.decrement(4)
{'old': 0, 'new': 1}
{'old': 1, 'new': -1}
{'old': -1, 'new': 2}
{'old': 2, 'new': -2}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

spectate-0.2.1.tar.gz (20.6 kB view details)

Uploaded Source

Built Distribution

spectate-0.2.1-py3-none-any.whl (20.2 kB view details)

Uploaded Python 3

File details

Details for the file spectate-0.2.1.tar.gz.

File metadata

  • Download URL: spectate-0.2.1.tar.gz
  • Upload date:
  • Size: 20.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No

File hashes

Hashes for spectate-0.2.1.tar.gz
Algorithm Hash digest
SHA256 2e5551f394586e239d028d0ffca86f78447ac1ee65a97a431d0cbd24b2a27653
MD5 078ad8d2401d89c6a80436cd98a33ed0
BLAKE2b-256 b88e4c2f72b0f7d753b5af71cd6de7e2b62702437fa8f4c98760f2b43d07a368

See more details on using hashes here.

File details

Details for the file spectate-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for spectate-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a1eb552429852d430e2e17e8f7894ea68d0a3c805eb727956350f9b2fbb1effa
MD5 815751adab5efb820231abc65bbf6642
BLAKE2b-256 0f80ea920abf37e9539b138ce40ea46235c4b554487fb7f76555134d2fa14046

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page