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 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.3.0.tar.gz (18.9 kB view details)

Uploaded Source

Built Distribution

spectate-0.3.0-py3-none-any.whl (21.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: spectate-0.3.0.tar.gz
  • Upload date:
  • Size: 18.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.0.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.6.8

File hashes

Hashes for spectate-0.3.0.tar.gz
Algorithm Hash digest
SHA256 1734874443d4d14509d5028ccc4f8f4ce8c85feb6edfcbdbd490fbb509e160f2
MD5 7fe37a4a18977e70d0f0ef9201a2f872
BLAKE2b-256 de21b9c2c7e308b54d660946cff33973cb11bed2afac5cd25ac91812dd2bcd13

See more details on using hashes here.

File details

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

File metadata

  • Download URL: spectate-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 21.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.0.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.6.8

File hashes

Hashes for spectate-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 18227d0e71dbe14a8f544f96f861c3b2038b43d4491d3e92137711ed936383c1
MD5 5387a52e9c6df69126c6fa8c1d1ddf4b
BLAKE2b-256 b7d70f11225286a1006e4ee1f9e5f29c5823a2827b737a0ae80aa7ffd7c1d7fa

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