{"action":"created","issue":{"url":"https://api.github.com/repos/ipython/traitlets/issues/61","labels_url":"https://api.github.com/repos/ipython/traitlets/issues/61/labels{/name}","comments_url":"https://api.github.com/repos/ipython/traitlets/issues/61/comments","events_url":"https://api.github.com/repos/ipython/traitlets/issues/61/events","html_url":"https://github.com/ipython/traitlets/pull/61","id":99024558,"number":61,"title":"[WIP] Deprecate on_trait_change and magic change handlers","user":{"login":"SylvainCorlay","id":2397974,"avatar_url":"https://avatars.githubusercontent.com/u/2397974?v=3","gravatar_id":"","url":"https://api.github.com/users/SylvainCorlay","html_url":"https://github.com/SylvainCorlay","followers_url":"https://api.github.com/users/SylvainCorlay/followers","following_url":"https://api.github.com/users/SylvainCorlay/following{/other_user}","gists_url":"https://api.github.com/users/SylvainCorlay/gists{/gist_id}","starred_url":"https://api.github.com/users/SylvainCorlay/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SylvainCorlay/subscriptions","organizations_url":"https://api.github.com/users/SylvainCorlay/orgs","repos_url":"https://api.github.com/users/SylvainCorlay/repos","events_url":"https://api.github.com/users/SylvainCorlay/events{/privacy}","received_events_url":"https://api.github.com/users/SylvainCorlay/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":7,"created_at":"2015-08-04T18:00:34Z","updated_at":"2015-08-04T22:37:18Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/ipython/traitlets/pulls/61","html_url":"https://github.com/ipython/traitlets/pull/61","diff_url":"https://github.com/ipython/traitlets/pull/61.diff","patch_url":"https://github.com/ipython/traitlets/pull/61.patch"},"body":"*Work in progress.*\r\n\r\n####  Deprecation of `on_trait_change` and the signature of change handlers\r\n\r\nAs of now, this PR deprecates `on_trait_change` and the magic `_*foo*_changed` handlers, to the benefit of a new `observe` method. The signature of the handlers passed to `observe` is either\r\n - no argument\r\n - a single `dict` argument containing  'name', 'old', 'new' and 'obj', the HasTraits instance emitting the object.\r\n\r\nSee example below.\r\n\r\n```Python\r\nfrom __future__ import print_function\r\nfrom traitlets import *\r\n\r\nclass Foo(HasTraits):\r\n    bar = Int()\r\n    \r\n    def test_bar(self):\r\n        print('foobar')\r\n    \r\nfoo = Foo()    \r\n\r\ndef cb(change):\r\n    print(change)\r\n    \r\nfoo.observe(cb)\r\nfoo.bar = 3\r\n```\r\n\r\n#### Implementation of an `observe` decorator like in [Atom](https://github.com/nucleic/atom):\r\nOn the other side, atom implements a `observe` decorator \r\n\r\n```Python\r\nclass Person(Atom):\r\n    age = Int()\r\n\r\n    @observe('age')\r\n    def debug_print(self, change):\r\n        print(change['object'].last_name)\r\n```\r\n\r\nThe problem is that the registration of the callback requires access `self` attribute, which is not available when the decorator is called. The way it works in atom is that the `@observe('age')` decorator replaces the `debug_print` method with a placeholder object `ObserveHandler` holding the method to be registered, which is to be picked up and unwrapped by the `AtomMeta` metaclass when instantiating the instance of (the counterpart to) HasTraits.\r\n\r\nRather than augmenting the metaclass, I could achieve the same result by making our `ObserveHandler` inherit from `BaseDescriptor`, and implement the custom logic in `instance_init`.\r\n\r\n#### Example with traitlets:\r\n\r\n```Python\r\nclass Foo(HasTraits):\r\n    bar = Int()\r\n    \r\n    @observe('bar')\r\n    def test(self):\r\n        print('foobar')\r\n\r\nfoo = Foo()\r\nfoo.bar = 2\r\n```"},"comment":{"url":"https://api.github.com/repos/ipython/traitlets/issues/comments/127781172","html_url":"https://github.com/ipython/traitlets/pull/61#issuecomment-127781172","issue_url":"https://api.github.com/repos/ipython/traitlets/issues/61","id":127781172,"user":{"login":"jasongrout","id":192614,"avatar_url":"https://avatars.githubusercontent.com/u/192614?v=3","gravatar_id":"","url":"https://api.github.com/users/jasongrout","html_url":"https://github.com/jasongrout","followers_url":"https://api.github.com/users/jasongrout/followers","following_url":"https://api.github.com/users/jasongrout/following{/other_user}","gists_url":"https://api.github.com/users/jasongrout/gists{/gist_id}","starred_url":"https://api.github.com/users/jasongrout/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jasongrout/subscriptions","organizations_url":"https://api.github.com/users/jasongrout/orgs","repos_url":"https://api.github.com/users/jasongrout/repos","events_url":"https://api.github.com/users/jasongrout/events{/privacy}","received_events_url":"https://api.github.com/users/jasongrout/received_events","type":"User","site_admin":false},"created_at":"2015-08-04T22:37:18Z","updated_at":"2015-08-04T22:37:18Z","body":"@minrk, @ellisonbg - when you guys have a minute, it'd be great to hear your comments."}}