Skip to content

Traitlet API #48

@SylvainCorlay

Description

@SylvainCorlay

Since Matplotlib's folks are starting to write traitlet-based APIs, we probably need to think of a roadmap for the future of the library if it is to be more widely adopted by the Scipy community.

There are some improvements that we could easily make without splitting traitlets in two different repos:


1. Deprecate trait attribute declaration with TraitType types instead of TraitType instances

class Foo(HasTraits):
    bar = Int    # deprecated
    baz = Int()  # ok
    alpha = List(trait=Int)     # deprecated
    alpha = List(trait=Int())   # ok

(Implemented in #51 and #55 - merged)


2. Like in Atom, separate the metadata from the keyword arguments in TraitType's constructor.

x = Int(allow_none=True, sync=True)      # deprecated
x = Int(allow_none=True).tag(sync=True)  # ok

(Implemented in #53 - merged)


3. A replacement for the cumbersome on_trait_change in the future, with a more convenient signature and a simpler name.

  • We now use observe/ unobserve method instead of using a remove=True/False argument with on_trait_change.

  • observe takes 1 positional argument (the handler), and two keyword arguments, names and type.

  • The callbacks take a single change dictionary argument, containing

    {
        'owner': the HasTraits instance,
        'old': the old trait attribute value,
        'new': the new trait attribute value,
        'name': the name of the changing attribute,
    }
  • A new @observe decorator to register methods as trait change handlers.

(Implemented in #61 - merged)


4. Deprecate the custom cross-validation magic methods _*bar*_validate to the benefit of a @validate('bar') decorator.

(Implemented in #73 - merged)


5. Since the base trait type now inherits from BaseDescriptor and other descriptors are defined to work well with HasTraits, we could make the following changes:
- rename MetaHasTraits into MetaHasDescriptors and deprecate the old name.
- introduce a base class to HasTraits called HasDescriptors.

(Implemented in #70 - merged)


6. Deprecate the default-value initializer magic methods _*bar*_default to the benefit of a @default('bar') decorator.

(Implemented in #114 - merged)


7. What is the best place for a repository of extra trait types for common types in the scipy stack such as ` numpy arrays, pandas/xray data structures, and their (binary) serialization functions for widgets (or other clients of comms) and ipyparallel?

It would make sense to propose a reference implementation of those, otherwise we will see multiple competing implementation emerge in different projects.

Besides, it is unlikely that such things would be accepted in core Pandas and numpy as of now...

(Scipy Trait Types Incubator Proposal)


8. Would it make sense to have a once version of on_trait_change (now observe)?

(There seems to be mixed opinions on this. Deferred.)


9. A common pattern when observing an object is the following:

foo.observe(do_something) # register to future change notifications.
do_something()            # act on the current value right away.

maybe we could facilitate this by adding a boolean argument to observe, stating whether to also run the callback right-away or not.

This would especially be useful when registering observer with the decorator syntax.


10. One thing that we had in mind in the long-run for widgets is having finer-grained events for containers, such as List and Dict. The ability to create other descriptors than trait types, possibly outside of the traitlets repository could enable experiments in this directions, like an integration of @jdfreder 's eventful dicts and lists.

One idea that could be in the scope of traitlets though is to add an attribute to the change dictionary indicating the type of notification that is being sent.

{
    'owner': the HasTraits instance,
    'old': the old trait attribute value,
    'new': the new trait attribute value,
    'name': the name of the changing attribute,
    'type': the type of notification being sent,
}

The last attribute could be used to define notification types corresponding to operational transforms.

Then, the @observe decorator would then have a 'type' keyword argument, (defaulting to None), to filter by notification type.

class Foo(HasTraits):
    bar = Int()
    baz = EventfulList()

    @observe('bar')  # Filter to only observe trait changes
    def handler_bar(self, change):
        pass

    @observe('baz ', type='element_change')  # Register to element change notifications for `baz`
    def handler_baz(self, change):
        pass

    @observe('bar', 'baz', type=All)  # register to all notification types for `bar` and `baz` 
    def handler_all(self, change):
        pass

The only thing to do to enable this possibility would be to add a type item in the dictionary and have the current implementation of observe filter on the notification type.

(Implemented in #83 - merged)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions