Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracking Element Changes #211

Closed
rmorshea opened this issue Apr 27, 2016 · 4 comments
Closed

Tracking Element Changes #211

rmorshea opened this issue Apr 27, 2016 · 4 comments

Comments

@rmorshea
Copy link
Contributor

rmorshea commented Apr 27, 2016

I did a bit of thinking on how to track element changes in things like lists and dicts. I came up with spectate. It has a few limitations, and might be a bit hacky, but it allows you to create classes whose instances have tracked methods.

The instances of a WatchedList (as in this example) would tell you when their items are changed:

WatchedList = WatchedType('WatchedList', list, ListSpectator, '__setitem__')
wl = WatchedList([1, 2, 3])

# will notify when
# an item changes
wl[0] = 2
# the spectator will print this
'__setitem__ : {0: 1} -> {0: 2}'
@SylvainCorlay
Copy link
Member

Well, we migrated to the new observe API with custom event types in order to support the type of approach that is proposed in #278.

@rmorshea
Copy link
Contributor Author

@SylvainCorlay, this idea of a WatchedType wouldn't be a full replacement for your pr, but would instead be an alternative to #278's edict where each of the required methods was rewritten in order to catch changes. I've actually written an example that describes an "eventful dict" using WatchedType.

@rmorshea
Copy link
Contributor Author

rmorshea commented Sep 6, 2016

@SylvainCorlay, I'll soon push a commit that allows for this kind of work flow (I'll post on your main thread when I'm do):

EventfulList = watched_type("EventfulList", list, '__setitem__')

def pass_on_args(inst, call):
    return call.args

def print_element_change(inst, answer):
    # answer.before = pass_on_args(call)
    index, old = answer.before
    new = inst[index]
    if new != old:
        print("{%s: %s} -> {%s, %s}" % (index, old, index, inst[index]))

elist = EventfulList([1, 2, 3])
elist.notify_before(__setitem__=pass_on_args)
elist.notify_after(__setitem__=print_element_change)

elist[0] = 1
# No print
elist[0] = -1
# PRINTS: '{0: 1} -> {0: -1}'

This represents a generic way to establish callbacks for any method on any type of instance.

I think it is a very powerful, and useful callback tool that avoids the need for hard coded classes like edict or elist.

While this does not replace an EventfulDict trait type, it would be the internal foundation of one.

@rmorshea
Copy link
Contributor Author

rmorshea commented Sep 18, 2016

After #317 is merged, this could be a working example:

class HasEvents(HasTraits):

    elist = Eventful(List(), '__setitem__')

    @event(on='elist.__setitem__')
    def elist_set_callbacks(self):
        def before(inst, call):
            m = "set from %s to %s at list index %s"
            print(m % (inst[call.args[0]], call.args[1], call.args[0]))
        return (before, None)

he = HasEvents()
he.elist = [1]
he.elist[0] = 2

Prints: set from 1 to 2 at list index 0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants