Git Product home page Git Product logo

atom's People

Contributors

blink1073 avatar clamdad avatar dependabot[bot] avatar dwillmer avatar frmdstryr avatar marcelotrevisani avatar matthieudartiailh avatar mrakitin avatar peterazmanov avatar scarabeusiv avatar sccolbert avatar tillsten avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

atom's Issues

Observers should not raise

Observers should not raise as it can lead to unexpected side effect. @sccolbert would prefer to follow the pattern adopted in phosphor and simply print the exception if one is raised. This is addressed by #71 which is pending review.

Custom initialization of Atom members

Hi,

I've been enjoying playing with atom/enaml for python desktop apps, but I can't find docs on how to do things like this:

from atom.api import Atom, Int

class Square(Atom):
    size = Int(2)
    area = size**2  # Doesn't work.

Is it possible? Should I override __init__ , or are there ways to set up constructor factory methods?

Cheers

More descriptive errors for Instance members

Currently on python 2.7 a TypeError shows a detailed description of what the error is:

  File "some/path/models.py", line 505, in clone
    clone = Job(**self.members())
TypeError: The 'copy_spacing' member on the 'Job' object must be of type 'list'. Got object of type 'ContainerList' instead.

On 3.6 the error is much less useful,

  File "some/path/models.py", line 505, in clone
    clone = Job(**self.members())
TypeError: invalid instance type

Could the same error reporting from 2.7 be used on 3+?

Numpy array

Is Atom compatible with Numpy arrays? I would like to do something like the example below.

from atom.api import NumpyArray

class TrainHistory(Atom):
    time = NumpyArray()
    location = NumpyArray()

How to "pretty-print" or get a human-readable representation of an object?

I have scanned the docs, but I see no way to get a human-readable representation of my objects. I tried the following:

        logger.debug(f"Unopened trade = {new_trade.members()}. Opening")

but that prints the keys but not the values.

Is there not something which recursively pretty-prints an Atom instance?

Question: AtomList gc flags

Looking at the code for atomlist I noticed it does not set the flags used for containers (while typedlist in 1.0.0.dev set it). From my understanding that could prevent gc collection in the case of cyclic references. If it is so it would worth setting that flag and adding tp_traverse and tp_clear. Could you comment about the pertinences of such changes @sccolbert ?

Single dotted observer extension not working for sub-atoms

The following code does not print the message 'dog first name changed':

from atom.api import Atom, Unicode, observe

class Dog(Atom):
    first_name = Unicode()

class Person(Atom):
    first_name = Unicode()
    dog = Dog(first_name='Fido')

    @observe('dog.first_name')
    def print_dog_first_name(self, change):
        print 'Dog first name changed:', change['value']

bob = Person(first_name='Robert')
bob.dog.first_name = 'Scruffy'

The issue is in the 363 line in atom.py:

for handler in decorated:
    for name, attr in handler.pairs:
        if name in members:  # line 363
            member = clone_if_needed(members[name])
            observer = handler.funcname
            if attr is not None:

"dog" is not a member, because in line 277, Dog is not a Member, but an Atom:

 for key, value in dct.iteritems():
     if isinstance(value, Member):  # line 277
        if value in owned_members:

Now, if we allow Atoms to be included in the decorator loop starting on line 361, we have a problem: the observer is expecting the function to reside in its own class, as in line 151:

owner = change['object']
handler = getattr(owner, self.funcname) # line 151
if isinstance(old, Atom):

In this case, owner would be the Dog object, which does not have the required method.
We could try and pass an instance to ExtendedObserver, but we do not have an instance in __new__. We could create an alias to the method in Person, but it is an unbound method in __new__.

The only solution I see is to late-bind the Observer until we have an instance, in the __init__ function. I hope I am wrong.

Property members are not observable

I like to use atom not just for its memory efficiency, but for how easy it makes to implement observable objects (and then link up to enaml).

I cannot seem to observe the atom.Property member. Below is an adapted version from the examples, and none of the observer functions trigger. I'm using atom 0.4.1 (from pypi) on python 3.7.

Are Properties not meant to be observable? If so, what am I missing?

from atom.api import Atom, Unicode, Property, Int, observe


class Person(Atom):
    """ A simple class representing a person object.
    """
    first_name = Unicode()

    age = Property()
    _age = Int(40)

    def _get_age(self):
        return self._age

    def _set_age(self, age):
        if age < 100 and age > 0:
            self._age = age

    def _observe_age(self, change):
        print('seen in STATIC observer: {}'.format(change))

    @observe('age')
    def _another_age_observer(self, change):
        print('seen in DECORATED observer: {}'.format(change))


def watcher_func(change):
    print('seen in EXTERNAL observer: {}'.format(change))

if __name__ == '__main__':
    bob = Person(first_name='Bob')
    bob.observe('age', watcher_func)

    print(bob.age)
    bob.age = -10
    print(bob.age)
    bob.age = 20
    print(bob.age)

Feature : add a tag method to Member to define metadata

On Enaml mailing list you proposed to add to Member a tag method to define metadata on Atom attributes easily using the following syntax : a = Unicode().tag(pref = 'sync'). I tried to implement it in C++ (see below). I can call it on a member outside of an Atom object fine but when I call it when defining Atom's members the python interpreter just crash.I fear the reference counting is wrong but I suspect I am missing something in how the initialisation of an atom class works.

Thanks
Matthieu

PS : calling tag in Atom init also causes issues.

static Member*
Member_tag( Member* self, PyObject* args, PyObject* kwargs )
{
PyObject* meta = self->metadata;
if ( !meta )
meta = kwargs;
else
PyDict_Update(meta, kwargs);
self->metadata = meta;
return self;
}

In PyMethodDef
{ "tag", ( PyCFunction )Member_tag, METH_VARARGS | METH_KEYWORDS,
"Update the metadata dictionnary with the provided dictionary." },

Build fails on OS X 10.6 with gcc 4.2.1

Hey Chris,

'python setup.py build' fails on my standard OS X install due to the masks in member.h being interpreted as ints. Error message is

In file included from atom/src/catommodule.cpp:10:
atom/src/member.h:49: error: integer constant is too large for ‘long’ type
atom/src/member.h:60: error: integer constant is too large for ‘long’ type
atom/src/member.h:71: error: integer constant is too large for ‘long’ type
atom/src/member.h:82: error: integer constant is too large for ‘long’ type
atom/src/member.h:93: error: integer constant is too large for ‘long’ type
atom/src/member.h:104: error: integer constant is too large for ‘long’ type
atom/src/member.h:115: error: integer constant is too large for ‘long’ type
error: command 'gcc' failed with exit status 1

They need coercing into uint64/unsigned long long on some platforms - i've forked and committed a fix in /dwillmer/atom, so we now have a working build, but it assumes UINT64_C() is available, and i can only test on mac and gentoo.

I guess we have to make a choice between using UINT64_C and appending ULL to the mask - can you test this on other platforms?

Cheers,
Dave

Add "Array" Value Type

I use Arrays in Traits to represent images, point clouds, etc. Here is an implementation in Atom. I can submit a pull request if you like. If so, would you like it in its own module? It does not seem to fit with scalars.

from atom.api import Atom, Value, PostGetAttr


class Array(Value):
    """ A value of type `np.ndarray`.

    """
    __slots__ = ()

    def __init__(self, default=None, factory=None):
        super(Array, self).__init__(default, factory)
        mode = PostGetAttr.MemberMethod_ObjectValue
        self.set_post_getattr_mode(mode, 'post_getattr')

    def post_getattr(self, owner, value):
        import numpy as np
        if not isinstance(value, np.ndarray):
            new = np.array(value)
            setattr(owner, self.name, new)
            return new
        else:
            return value

List and atomclist don't implement get

So there is not way to safely probe an index and get a default value if needed.

There is default values on containers but container creation is not always in our hand to specify a default value.

Is there a way to check if a value has been set without triggering default creation?

I'm trying to speed up widget creation in enaml-native.

The toolkit objects init_widget methods are a few top hitters in the profiling results. They do a lot of if d.<attr>: checks to set initial widget state. Most of the time a majority of these attributes are never set and creating the unused default values take time.

A simple profilng example shows that creating defaults for all attributes vs only commonly used ones takes about 10x longer.

Is there any way (or would it be easy to add a way) to check if an attribute is set without triggering default creation? Ex, something like `is_default(d,'<member_name>') ?

from atom.api import Atom, Int, Unicode, Bool, Float, Tuple, Enum, Event
from enaml.core.declarative import d_

class View(Atom):
    """ View is a view group that displays
        child views in relative positions.

    """
    #: Describes how the child views are positioned.
    #: Defaults to Gravity.START | Gravity.TOP.

    alpha = d_(Int())

    background_color = d_(Unicode())

    bottom = d_(Int())

    camera_distance = d_(Float())

    clickable = d_(Bool())

    #: Called when view is clicked
    clicked = d_(Event(), writable=False)

    clip_bounds = d_(Tuple(int, default=(0, 0, 0, 0)))

    clip_to_outline = d_(Bool())

    content_description = d_(Unicode())

    content_clickable = d_(Bool())

    drawing_cache_background_color = d_(Int())

    drawing_cache_enabled = d_(Bool())

    drawing_cache_quality = d_(Bool())

    duplicate_parent_state_enabled = d_(Bool())

    elevation = d_(Float())

    fading_edge_length = d_(Int())

    filter_touches_when_obscured = d_(Bool())

    fits_system_windows = d_(Bool())

    focusable = d_(Bool())

    focusable_in_touch_mode = d_(Bool())

    hovered = d_(Bool())

    important_for_accessibility = d_(Int())

    keeps_screen_on = d_(Bool())

    label_for = d_(Int())

    layout_width = d_(Unicode())#Enum('', 'fill_parent', 'match_parent', 'wrap_content'))

    layout_height = d_(Unicode())#Enum('', 'fill_parent', 'match_parent', 'wrap_content'))

    layout_direction = d_(Enum('ltr', 'rtl', 'inherit', 'locale'))

    left = d_(Int())

    long_clickable = d_(Bool())

    nested_scrolling_enabled = d_(Bool())

    #: Left, top, right, bottom
    margins = d_(Tuple(int))

    over_scroll_mode = d_(Int())

    #: Left, top, right, bottom
    padding = d_(Tuple(int))

    padding_relative = d_(Tuple(int))

    pivot_x = d_(Float())

    pivot_y = d_(Float())

    pressed = d_(Bool())

    right = d_(Int())

    rotation = d_(Float())

    rotation_x = d_(Float())

    rotation_y = d_(Float())

    save_enabled = d_(Bool())

    save_from_parent_enabled = d_(Bool())

    scale_x = d_(Float())

    scale_y = d_(Float())

    scroll_bar_default_delay_before_fade = d_(Int())

    scroll_bar_fade_duration = d_(Int())

    scroll_bar_size = d_(Int())

    scroll_bar_style = d_(Int())

    scroll_container = d_(Bool())

    scroll_indicators = d_(Tuple(int))

    scroll_x = d_(Int())

    scroll_y = d_(Int())

    scrollbar_fading_enabled = d_(Int())

    selected = d_(Bool())

    sound_effects_enabled = d_(Bool())

    system_ui_visibility = d_(Int())

    text_alignment = d_(Int())

    text_direction = d_(Int())

    top = d_(Int())

    transition_name = d_(Unicode())

    translation_x = d_(Float())

    translation_y = d_(Float())

    translation_z = d_(Float())

    vertical_fading_edge_enabled = d_(Bool())

    vertical_scroll_bar_enabled = d_(Bool())

    vertical_scroll_bar_position = d_(Int())

    will_not_cache_drawing = d_(Bool())

    will_not_draw = d_(Bool())

    x = d_(Float())

    y = d_(Float())

    z = d_(Float())
    def all(self):
        # Test creating all all defaults 
        for k in self.members():
            getattr(self,k)

    def common(self):
        #: Test only commonly used ones
        for k in ['layout_width', 'layout_height', 
                  'clickable', 'padding', 'margins']:
            getattr(self,k)
            

%timeit View().all()
100000 loops, best of 3: 11.8 µs per loop

%timeit View().common()
The slowest run took 7.47 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 1.34 µs per loop

07-06 09:59:18.407 17157-17187/app I/pybridge:          237116 function calls (234072 primitive calls) in 2.286 seconds
07-06 09:59:18.407 17157-17187/app I/pybridge:    Ordered by: cumulative time
07-06 09:59:18.407 17157-17187/app I/pybridge:    List reduced from 1332 to 400 due to restriction <0.29999999999999999>
07-06 09:59:18.407 17157-17187/app I/pybridge:    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
07-06 09:59:18.407 17157-17187/app I/pybridge:         1    0.008    0.008    2.295    2.295 /data/user/0/app/assets/python/main.py:22(main)
07-06 09:59:18.407 17157-17187/app I/pybridge:         1    0.000    0.000    1.014    1.014 /data/user/0/app/assets/python/enamlnative/android/app.py:138(show_view)
07-06 09:59:18.407 17157-17187/app I/pybridge:         1    0.000    0.000    1.014    1.014 /data/user/0/app/assets/python/enamlnative/android/app.py:142(get_view)
07-06 09:59:18.407 17157-17187/app I/pybridge:     119/1    0.006    0.000    0.862    0.862 /data/user/0/app/assets/python/site-packages/enaml/widgets/toolkit_object.py:201(activate_proxy)
07-06 09:59:18.407 17157-17187/app I/pybridge:       119    0.001    0.000    0.810    0.007 /data/user/0/app/assets/python/site-packages/enaml/widgets/toolkit_object.py:218(activate_top_down)
07-06 09:59:18.407 17157-17187/app I/pybridge:       119    0.001    0.000    0.809    0.007 /data/user/0/app/assets/python/enamlnative/android/android_toolkit_object.py:80(activate_top_down)
07-06 09:59:18.407 17157-17187/app I/pybridge:         1    0.000    0.000    0.686    0.686 /data/user/0/app/assets/python/site-packages/enaml/core/import_hooks.py:125(load_module)
07-06 09:59:18.407 17157-17187/app I/pybridge:         1    0.003    0.003    0.671    0.671 /data/user/0/app/assets/python/view.enaml:0()
07-06 09:59:18.408 17157-17187/app I/pybridge:       106    0.037    0.000    0.548    0.005 /data/user/0/app/assets/python/enamlnative/android/android_text_view.py:103(init_widget)
07-06 09:59:18.408 17157-17187/app I/pybridge:         1    0.001    0.001    0.369    0.369 /data/user/0/app/assets/python/site-packages/enaml/__init__.py:36(imports)
07-06 09:59:18.408 17157-17187/app I/pybridge:         1    0.008    0.008    0.367    0.367 /data/user/0/app/assets/python/site-packages/enaml/core/import_hooks.py:8(<module>)
07-06 09:59:18.408 17157-17187/app I/pybridge:       113    0.038    0.000    0.348    0.003 /data/user/0/app/assets/python/enamlnative/android/android_view.py:110(init_widget)
07-06 09:59:18.408 17157-17187/app I/pybridge:       903    0.014    0.000    0.299    0.000 /data/user/0/app/assets/python/enamlnative/android/app.py:160(send_event)
07-06 09:59:18.408 17157-17187/app I/pybridge:       903    0.009    0.000    0.284    0.000 /data/user/0/app/assets/python/enamlnative/android/app.py:210(timed_call)
07-06 09:59:18.408 17157-17187/app I/pybridge:         1    0.008    0.008    0.283    0.283 /data/user/0/app/assets/python/site-packages/enaml/core/parser.py:8(<module>)
07-06 09:59:18.408 17157-17187/app I/pybridge:       138    0.003    0.000    0.274    0.002 /data/user/0/app/assets/python/site-packages/enaml/core/compiler_helpers.py:548(run_operator)
07-06 09:59:18.408 17157-17187/app I/pybridge:        46    0.001    0.000    0.267    0.006 /data/user/0/app/assets/python/stdlib/re.py:192(compile)
07-06 09:59:18.408 17157-17187/app I/pybridge:        46    0.004    0.000    0.267    0.006 /data/user/0/app/assets/python/stdlib/re.py:230(_compile)
07-06 09:59:18.408 17157-17187/app I/pybridge:        46    0.002    0.000    0.263    0.006 /data/user/0/app/assets/python/stdlib/sre_compile.py:567(compile)
07-06 09:59:18.408 17157-17187/app I/pybridge:         1    0.000    0.000    0.253    0.253 /data/user/0/app/assets/python/view.enaml:103(ContentView)
07-06 09:59:18.408 17157-17187/app I/pybridge:         1    0.010    0.010    0.237    0.237 /data/user/0/app/assets/python/enamlnative/widgets/api.py:11(<module>)
07-06 09:59:18.408 17157-17187/app I/pybridge:       141    0.032    0.000    0.215    0.002 /data/user/0/app/assets/python/site-packages/enaml/core/byteplay.py:646(to_code)
07-06 09:59:18.408 17157-17187/app I/pybridge:       124    0.009    0.000    0.210    0.002 /data/user/0/app/assets/python/enamlnative/android/bridge.py:209(__init__)
07-06 09:59:18.408 17157-17187/app I/pybridge:       776    0.046    0.000    0.190    0.000 /data/user/0/app/assets/python/enamlnative/android/bridge.py:97(__call__)
07-06 09:59:18.408 17157-17187/app I/pybridge:         3    0.000    0.000    0.161    0.054 /data/user/0/app/assets/python/enamlnative/android/android_linear_layout.py:44(create_widget)
07-06 09:59:18.408 17157-17187/app I/pybridge:         1    0.002    0.002    0.159    0.159 /data/user/0/app/assets/python/enamlnative/android/app.py:98(_default_loop)
07-06 09:59:18.408 17157-17187/app I/pybridge:         1    0.005    0.005    0.156    0.156 /data/user/0/app/assets/python/site-packages/tornado/ioloop.py:27(<module>)
07-06 09:59:18.408 17157-17187/app I/pybridge:     119/1    0.006    0.000    0.152    0.152 /data/user/0/app/assets/python/site-packages/enaml/widgets/toolkit_object.py:147(initialize)
07-06 09:59:18.408 17157-17187/app I/pybridge:        25    0.001    0.000    0.151    0.006 /data/user/0/app/assets/python/site-packages/enaml/core/operators.py:185(op_subscribe)
07-06 09:59:18.408 17157-17187/app I/pybridge:        25    0.001    0.000    0.150    0.006 /data/user/0/app/assets/python/site-packages/enaml/core/operators.py:73(gen_tracer)
07-06 09:59:18.408 17157-17187/app I/pybridge:   187/186    0.070    0.000    0.148    0.001 /data/user/0/app/assets/python/site-packages/atom/atom.py:176(__new__)
07-06 09:59:18.408 17157-17187/app I/pybridge:         1    0.000    0.000    0.146    0.146 /data/user/0/app/assets/python/site-packages/ply/yacc.py:3214(yacc)
07-06 09:59:18.408 17157-17187/app I/pybridge:         1    0.004    0.004    0.145    0.145 /data/user/0/app/assets/python/site-packages/tornado/concurrent.py:23(<module>)
07-06 09:59:18.408 17157-17187/app I/pybridge:        46    0.001    0.000    0.144    0.003 /data/user/0/app/assets/python/stdlib/sre_parse.py:706(parse)
07-06 09:59:18.408 17157-17187/app I/pybridge:    227/46    0.008    0.000    0.141    0.003 /data/user/0/app/assets/python/stdlib/sre_parse.py:317(_parse_sub)
07-06 09:59:18.408 17157-17187/app I/pybridge:    327/49    0.052    0.000    0.140    0.003 /data/user/0/app/assets/python/stdlib/sre_parse.py:395(_parse)
07-06 09:59:18.408 17157-17187/app I/pybridge:         1    0.001    0.001    0.133    0.133 /data/user/0/app/assets/python/enamlnative/widgets/linear_layout.py:11(<module>)
07-06 09:59:18.408 17157-17187/app I/pybridge: 4339/4328    0.049    0.000    0.132    0.000 /data/user/0/app/assets/python/site-packages/enaml/core/declarative_meta.py:22(__call__)
07-06 09:59:18.408 17157-17187/app I/pybridge:         1    0.001    0.001    0.129    0.129 /data/user/0/app/assets/python/enamlnative/widgets/view_group.py:11(<module>)
07-06 09:59:18.408 17157-17187/app I/pybridge:     122/1    0.004    0.000    0.127    0.127 /data/user/0/app/assets/python/site-packages/enaml/core/declarative.py:103(initialize)
07-06 09:59:18.408 17157-17187/app I/pybridge:         1    0.001    0.001    0.125    0.125 /data/user/0/app/assets/python/enamlnative/widgets/view.py:11(<module>)
07-06 09:59:18.408 17157-17187/app I/pybridge:      3922    0.036    0.000    0.124    0.000 /data/user/0/app/assets/python/enamlnative/widgets/button.py:47(_update_proxy)
07-06 09:59:18.408 17157-17187/app I/pybridge:         1    0.006    0.006    0.121    0.121 /data/user/0/app/assets/python/site-packages/ply/yacc.py:62(<module>)
07-06 09:59:18.408 17157-17187/app I/pybridge:       141    0.021    0.000    0.120    0.001 /data/user/0/app/assets/python/site-packages/enaml/core/byteplay.py:439(_compute_stacksize)
07-06 09:59:18.408 17157-17187/app I/pybridge:        46    0.001    0.000    0.116    0.003 /data/user/0/app/assets/python/stdlib/sre_compile.py:552(_code)
07-06 09:59:18.408 17157-17187/app I/pybridge:       903    0.014    0.000    0.116    0.000 /data/user/0/app/assets/python/site-packages/tornado/ioloop.py:509(call_later)
07-06 09:59:18.408 17157-17187/app I/pybridge:         1    0.004    0.004    0.111    0.111 /data/user/0/app/assets/python/site-packages/enaml/widgets/widget.py:8(<module>)
07-06 09:59:18.408 17157-17187/app I/pybridge:         1    0.002    0.002    0.110    0.110 /data/user/0/app/assets/python/stdlib/inspect.py:25(<module>)
07-06 09:59:18.408 17157-17187/app I/pybridge:       113    0.027    0.000    0.106    0.001 /data/user/0/app/assets/python/enamlnative/android/android_widget.py:46(init_widget)
07-06 09:59:18.408 17157-17187/app I/pybridge:       162    0.001    0.000    0.106    0.001 /data/user/0/app/assets/python/site-packages/enaml/core/import_hooks.py:112(find_module)
07-06 09:59:18.408 17157-17187/app I/pybridge:         9    0.001    0.000    0.102    0.011 /data/user/0/app/assets/python/site-packages/enaml/core/pattern.py:33(initialize)
07-06 09:59:18.408 17157-17187/app I/pybridge:     22840    0.084    0.000    0.102    0.000 {isinstance}

sorted by time

07-06 09:59:18.769 17157-17187/app I/pybridge:          237116 function calls (234072 primitive calls) in 2.286 seconds
07-06 09:59:18.769 17157-17187/app I/pybridge:    Ordered by: internal time
07-06 09:59:18.769 17157-17187/app I/pybridge:    List reduced from 1332 to 400 due to restriction <0.29999999999999999>
07-06 09:59:18.769 17157-17187/app I/pybridge:    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
07-06 09:59:18.769 17157-17187/app I/pybridge:     22840    0.084    0.000    0.102    0.000 {isinstance}
07-06 09:59:18.769 17157-17187/app I/pybridge:   187/186    0.070    0.000    0.148    0.001 /data/user/0/app/assets/python/site-packages/atom/atom.py:176(__new__)
07-06 09:59:18.769 17157-17187/app I/pybridge:         1    0.060    0.060    0.063    0.063 /data/user/0/app/assets/python/site-packages/enaml/core/parse_tab/parsetab.py:4(<module>)
07-06 09:59:18.769 17157-17187/app I/pybridge:    327/49    0.052    0.000    0.140    0.003 /data/user/0/app/assets/python/stdlib/sre_parse.py:395(_parse)
07-06 09:59:18.769 17157-17187/app I/pybridge: 4339/4328    0.049    0.000    0.132    0.000 /data/user/0/app/assets/python/site-packages/enaml/core/declarative_meta.py:22(__call__)
07-06 09:59:18.769 17157-17187/app I/pybridge:       776    0.046    0.000    0.190    0.000 /data/user/0/app/assets/python/enamlnative/android/bridge.py:97(__call__)
07-06 09:59:18.769 17157-17187/app I/pybridge:       113    0.038    0.000    0.348    0.003 /data/user/0/app/assets/python/enamlnative/android/android_view.py:110(init_widget)
07-06 09:59:18.769 17157-17187/app I/pybridge:    674/46    0.038    0.000    0.093    0.002 /data/user/0/app/assets/python/stdlib/sre_compile.py:64(_compile)
07-06 09:59:18.769 17157-17187/app I/pybridge:       106    0.037    0.000    0.548    0.005 /data/user/0/app/assets/python/enamlnative/android/android_text_view.py:103(init_widget)
07-06 09:59:18.769 17157-17187/app I/pybridge: 1078/1077    0.036    0.000    0.039    0.000 {built-in method __new__ of type object at 0x9b851f80}
07-06 09:59:18.769 17157-17187/app I/pybridge:      3922    0.036    0.000    0.124    0.000 /data/user/0/app/assets/python/enamlnative/widgets/button.py:47(_update_proxy)
07-06 09:59:18.769 17157-17187/app I/pybridge:      1466    0.035    0.000    0.077    0.000 {method 'extend' of 'list' objects}
07-06 09:59:18.769 17157-17187/app I/pybridge:       141    0.032    0.000    0.215    0.002 /data/user/0/app/assets/python/site-packages/enaml/core/byteplay.py:646(to_code)
07-06 09:59:18.769 17157-17187/app I/pybridge: 4339/4328    0.029    0.000    0.062    0.000 /data/user/0/app/assets/python/site-packages/enaml/core/expression_engine.py:157(read)
07-06 09:59:18.769 17157-17187/app I/pybridge:       903    0.029    0.000    0.044    0.000 {_heapq.heappop}
07-06 09:59:18.769 17157-17187/app I/pybridge:      4074    0.028    0.000    0.039    0.000 /data/user/0/app/assets/python/site-packages/enaml/widgets/widget.py:141(_update_proxy)
07-06 09:59:18.769 17157-17187/app I/pybridge:       113    0.027    0.000    0.106    0.001 /data/user/0/app/assets/python/enamlnative/android/android_widget.py:46(init_widget)
07-06 09:59:18.769 17157-17187/app I/pybridge:      3922    0.027    0.000    0.089    0.000 /data/user/0/app/assets/python/enamlnative/widgets/text_view.py:139(_update_proxy)
07-06 09:59:18.769 17157-17187/app I/pybridge:      4074    0.026    0.000    0.064    0.000 /data/user/0/app/assets/python/enamlnative/widgets/view.py:258(_update_proxy)
07-06 09:59:18.769 17157-17187/app I/pybridge:        81    0.025    0.000    0.080    0.001 /data/user/0/app/assets/python/site-packages/enaml/core/import_hooks.py:399(locate_module)
07-06 09:59:18.769 17157-17187/app I/pybridge:        10    0.023    0.002    0.031    0.003 /data/user/0/app/assets/python/stdlib/collections.py:293(namedtuple)
07-06 09:59:18.769 17157-17187/app I/pybridge:     14493    0.023    0.000    0.023    0.000 {method 'append' of 'list' objects}
07-06 09:59:18.769 17157-17187/app I/pybridge:      2507    0.023    0.000    0.043    0.000 /data/user/0/app/assets/python/site-packages/enaml/core/byteplay.py:473(get_next_stacks)
07-06 09:59:18.769 17157-17187/app I/pybridge:      2977    0.022    0.000    0.028    0.000 /data/user/0/app/assets/python/stdlib/sre_parse.py:193(__next)
07-06 09:59:18.769 17157-17187/app I/pybridge:      3051    0.022    0.000    0.022    0.000 {hasattr}
07-06 09:59:18.769 17157-17187/app I/pybridge:      4586    0.022    0.000    0.024    0.000 /data/user/0/app/assets/python/site-packages/enaml/core/declarative_meta.py:49(declarative_change_handler)
07-06 09:59:18.769 17157-17187/app I/pybridge:       141    0.021    0.000    0.120    0.001 /data/user/0/app/assets/python/site-packages/enaml/core/byteplay.py:439(_compute_stacksize)
07-06 09:59:18.769 17157-17187/app I/pybridge: 18108/17679    0.019    0.000    0.022    0.000 {len}
07-06 09:59:18.769 17157-17187/app I/pybridge:       903    0.019    0.000    0.098    0.000 /data/user/0/app/assets/python/site-packages/tornado/ioloop.py:916(call_at)
07-06 09:59:18.769 17157-17187/app I/pybridge:       141    0.019    0.000    0.034    0.000 /data/user/0/app/assets/python/site-packages/enaml/core/byteplay.py:319(from_code)
07-06 09:59:18.770 17157-17187/app I/pybridge:      8703    0.018    0.000    0.018    0.000 /data/user/0/app/assets/python/site-packages/tornado/ioloop.py:971(__lt__)
07-06 09:59:18.770 17157-17187/app I/pybridge:         7    0.018    0.003    0.019    0.003 /data/user/0/app/assets/python/site-packages/jnius/reflect.py:369(load_spec)
07-06 09:59:18.770 17157-17187/app I/pybridge:       903    0.018    0.000    0.043    0.000 /data/user/0/app/assets/python/site-packages/tornado/ioloop.py:960(__init__)
07-06 09:59:18.770 17157-17187/app I/pybridge:      1303    0.017    0.000    0.017    0.000 {zip}
07-06 09:59:18.770 17157-17187/app I/pybridge:      3099    0.017    0.000    0.028    0.000 /data/user/0/app/assets/python/stdlib/sre_parse.py:141(__getitem__)
07-06 09:59:18.770 17157-17187/app I/pybridge:   993/371    0.016    0.000    0.019    0.000 /data/user/0/app/assets/python/stdlib/sre_parse.py:151(getwidth)
07-06 09:59:18.770 17157-17187/app I/pybridge:       905    0.015    0.000    0.025    0.000 /data/user/0/app/assets/python/site-packages/tornado/stack_context.py:255(wrap)
07-06 09:59:18.770 17157-17187/app I/pybridge:         1    0.015    0.015    0.023    0.023 /data/user/0/app/assets/python/site-packages/atom/atom.py:8(<module>)
07-06 09:59:18.770 17157-17187/app I/pybridge:         1    0.015    0.015    0.045    0.045 /data/user/0/app/assets/python/site-packages/ply/yacc.py:3127(get_pfunctions)
07-06 09:59:18.770 17157-17187/app I/pybridge:      5565    0.014    0.000    0.014    0.000 {method 'startswith' of 'str' objects}
07-06 09:59:18.770 17157-17187/app I/pybridge:       903    0.014    0.000    0.116    0.000 /data/user/0/app/assets/python/site-packages/tornado/ioloop.py:509(call_later)
07-06 09:59:18.770 17157-17187/app I/pybridge:       903    0.014    0.000    0.299    0.000 /data/user/0/app/assets/python/enamlnative/android/app.py:160(send_event)
07-06 09:59:18.770 17157-17187/app I/pybridge:         1    0.014    0.014    0.014    0.014 {marshal.load}

default values bypass _observe_ method on creation

Am I missing something or is this a bug?

In [210]: from atom.api import *

In [211]: class Foo(Atom):
   .....:     f = Float()
   .....:     def _default_f(self):
   .....:         return 10.123456789
   .....:     def _observe_f(self, val):
   .....:         if len(str(val['value']).split('.')[-1]) > 4:
   .....:             self.f = round(val['value'], 4)
   .....:

In [212]: foo = Foo()

In [213]: foo.f
Out[213]: 10.123456789

In [214]: foo.f
Out[214]: 10.1235

Using like lazy-initing dict equivalent?

Is it possible? After reading docs i'm thinking - no (without major rewriting). But maybe i miss something?

This useful when i have huge 'tree' of dicts and some of them newer will have childs - but ofc eat memory.

Support for Py3K

I saw the new branch, and I would like to help if I can. Atom and Enaml are my only blockers from moving to Py3K. So please, delegate some grunt work my way.

Backporting some of atom-1.0.0dev features

Are they any feature that could be backported from the atom-1.0.0dev branch without breaking change and with a neat gain ? The typeddict, typedset and Set member look interesting to me but I don't know if some internal machinery could not be backported too.

Add a member validating fixed size inhomogeneous tuple

Currently the Tuple member can only validate tuple which are homogeneous in type and we do not validate the length. This is at odd with typing.Tuple which can be used for fixed sized tuple and in-homogeneous types. We could add a member mimicking typing.Tuple.

Is such a feature of interest to others ?

'set_default' gets overwritten after the first instance in combination with ForwardSubclass

With Atom 0.3.9, from PyPi.

from atom.api import Atom, ForwardSubclass, set_default

class A(object):
    pass

class B(A):
    pass

class Foo(Atom):
    attribute = ForwardSubclass(lambda: A)

class Bar(Foo):
    attribute = set_default(B)

a = Bar()
print(a.attribute)
b = Bar()
print(b.attribute)
c = Bar()
print(c.attribute)

My expected output is:

<class '__main__.B'>
<class '__main__.B'>
<class '__main__.B'>

Instead I get:

<class '__main__.B'>
<class '__main__.A'>
<class '__main__.A'>

With Subclass, instead of ForwardSubclass it functions correctly.

Add an observer helper method similar to "HasTraits.on_trait_change"

This is the last feature request, I promise (for now). Thanks for bearing with me, I am really enjoying Atom overall.
Again, this could be broken into two functions in lieu of the remove parameter. This is a complement to sync_value, but uses a callback function. I would use this to add an observer to a non-Atom object, or to react to a change in value in one Atom from a disconnected Atom.
I can submit a pull request for this as well. Perhaps put all of the observation-related stuff in observe.py?

from atom.api import observe


def on_value_change(atom, names, handler, remove=False):
    """Track changes to a set of value(s) on atom with a handler.

    Parameters
    ----------
    atom : Atom
        The source object.
    *names : str
        The str names of the attributes to observe on the object.
        These must be of the form 'foo' or 'foo.bar'.
    handler : callable
        The callable which handles the changes.
    remove: bool, optional (False)
        If True, remove the observation.
    """
    members = atom.members()
    hdlr = observe(names)
    for name, attr in hdlr.pairs:
        if name in members:
            if attr:
                obj = getattr(atom, name)
                member = obj.get_member(attr)
            else:
                member = members[name]
            existing = None
            for obs in member.static_observers():
                if obs == handler:
                    existing = obs
            if remove and existing:
                member.remove_static_observer(existing)
            elif not existing:
                member.add_static_observer(handler)


from atom.api import Atom, Unicode


class Person(Atom):

    name = Unicode("Bob Smith")


def callback(change):
    print 'callback', change['value']


bob = Person()
on_value_change(bob, 'name', callback)
bob.name = 'Richard Jackson'
on_value_change(bob, 'name', callback, remove=True)
bob.name = 'Pete Thompson'

atom 0.5.0 breaks enaml

The current release of atom does not work with the current release of enaml (see inkcut/inkcut#238).

Can you please pull the 0.5.0 release until a compatible version of enaml is also released or release a compatible version of enaml?

Release 0.7.0

The release 0.7.0.rc1 is on PyPI. I will delay the final release because it appears I may have some Qt issues for python 3.10 on enaml (but I will have to double check). If you have to time to test this (with enaml main) you are very much welcome to do so.

can't install atom on macOS mojave

Rodrigos-Mac:atom rodgomesc$ sudo python setup.py  install
/Users/rodgomesc/miniconda3/lib/python3.7/site-packages/setuptools/dist.py:398: UserWarning: Normalizing '0.4.2.dev' to '0.4.2.dev0'
  normalized_version,
running install
running bdist_egg
running egg_info
creating atom.egg-info
writing atom.egg-info/PKG-INFO
writing dependency_links to atom.egg-info/dependency_links.txt
writing requirements to atom.egg-info/requires.txt
writing top-level names to atom.egg-info/top_level.txt
writing manifest file 'atom.egg-info/SOURCES.txt'
reading manifest file 'atom.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: no files found matching '*.txt'
warning: no files found matching '*.md'
warning: no files found matching '*.rst' under directory 'docs'
warning: no files found matching '*.png' under directory 'docs'
warning: no files found matching '*.py' under directory 'docs'
warning: no files found matching '*.txt' under directory 'licenses'
no previously-included directories found matching '.git'
no previously-included directories found matching 'docs/build'
no previously-included directories found matching 'dist'
no previously-included directories found matching 'build'
writing manifest file 'atom.egg-info/SOURCES.txt'
installing library code to build/bdist.macosx-10.7-x86_64/egg
running install_lib
running build_py
creating build
creating build/lib.macosx-10.7-x86_64-3.7
creating build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/instance.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/tuple.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/typed.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/event.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/subclass.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/version.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/compat.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/signal.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/list.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/property.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/delegator.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/coerced.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/__init__.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/containerlist.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/api.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/dict.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/enum.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/scalars.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/intenum.py -> build/lib.macosx-10.7-x86_64-3.7/atom
copying atom/atom.py -> build/lib.macosx-10.7-x86_64-3.7/atom
creating build/lib.macosx-10.7-x86_64-3.7/atom/datastructures
copying atom/datastructures/__init__.py -> build/lib.macosx-10.7-x86_64-3.7/atom/datastructures
copying atom/datastructures/api.py -> build/lib.macosx-10.7-x86_64-3.7/atom/datastructures
running build_ext
building 'atom.catom' extension
creating build/temp.macosx-10.7-x86_64-3.7
creating build/temp.macosx-10.7-x86_64-3.7/atom
creating build/temp.macosx-10.7-x86_64-3.7/atom/src
gcc -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I/Users/rodgomesc/miniconda3/include -arch x86_64 -I/Users/rodgomesc/miniconda3/include -arch x86_64 -I/Users/rodgomesc/miniconda3/include/python3.7m -c atom/src/atomlist.cpp -o build/temp.macosx-10.7-x86_64-3.7/atom/src/atomlist.o
warning: include path for stdlibc++ headers not found; pass '-std=libc++' on the
      command line to use the libc++ standard library instead
      [-Wstdlibcxx-not-found]
In file included from atom/src/atomlist.cpp:8:
In file included from atom/src/atomlist.h:9:
atom/src/pythonhelpers.h:12:10: fatal error: 'string' file not found
#include <string>
         ^~~~~~~~
1 warning and 1 error generated.
error: command 'gcc' failed with exit status 1
Rodrigos-Mac:atom rodgomesc$ `

Add message filtering to observers

It would be useful to filter out messages before calling observers. This can either be a new decorator or an argument to the observe decorator/function:

@observe(...)
@filter_on('update')
def _change_handler(self, change):
    ....

or

obj.observe('...', handler, filter_on='update') 

Add a FloatRange

Sorry, me again. I would like to use Ranges that are floating point values.

from atom.api import Atom, Range

class Test(Atom):
    coef = Range(-1.0, 1.0, 0.0)

t = Test()
assert t.coef == 0.0

I went ahead and implemented it as a replacement to the existing Range, just to see if it would work. If you like, I can implement FloatRange and submit a PR.

diff --git a/atom/src/validatebehavior.cpp b/atom/src/validatebehavior.cpp
index b9d356d..44f1ecb 100644
--- a/atom/src/validatebehavior.cpp
+++ b/atom/src/validatebehavior.cpp
@@ -82,12 +82,12 @@ Member::check_context( Validate::Mode mode, PyObject* context )
             }
             PyObject* start = PyTuple_GET_ITEM( context, 0 );
             PyObject* end = PyTuple_GET_ITEM( context, 1 );
-            if( start != Py_None && !PyInt_Check( start ) )
+            if( start != Py_None && !PyFloat_Check( start ) )
             {
                 py_expected_type_fail( context, "2-tuple of int or None" );
                 return false;
             }
-            if( end != Py_None && !PyInt_Check( end ) )
+            if( end != Py_None && !PyFloat_Check( end ) )
             {
                 py_expected_type_fail( context, "2-tuple of int or None" );
                 return false;
@@ -486,19 +486,19 @@ callable_handler( Member* member, CAtom* atom, PyObject* oldvalue, PyObject static PyObject*
 range_handler( Member* member, CAtom* atom, PyObject* oldvalue, PyObject* newvalue )
 {
-    if( !PyInt_Check( newvalue ) )
+    if( !PyFloat_Check( newvalue ) )
         return validate_type_fail( member, atom, newvalue, "int" );
     PyObject* low = PyTuple_GET_ITEM( member->validate_context, 0 );
     PyObject* high = PyTuple_GET_ITEM( member->validate_context, 1 );
-    long value = PyInt_AS_LONG( newvalue );
+    double value = PyFloat_AsDouble( newvalue );
     if( low != Py_None )
     {
-        if( PyInt_AS_LONG( low ) > value )
+        if( PyFloat_AsDouble( low ) > value )
             return py_type_fail( "range value too small" );
     }
     if( high != Py_None )
     {
-        if( PyInt_AS_LONG( high ) < value )
+        if( PyFloat_AsDouble( high ) < value )
             return py_type_fail( "range value too large" );
     }
     return newref( newvalue );

Multiple references to same member object to not work correctly

According to this source code comment in AtomMeta

# Walk the dict a second time to collect the class members. This
# assigns the name and the index to the member. If a member is
# overriding an existing member, the memory index of the old
# member is reused and any static observers are copied over.
for key, value in dct.iteritems():
     if isinstance(value, Member):
         if value in owned_members:  # foo = bar = Baz()
             value = value.clone()

This should work, but doesn't:

In [1]: from atom.api import *

In [2]: class Foo(Atom):
   ...:     a = b = Float()
   ...:

In [3]: f = Foo()

In [4]: f.a = 12

In [5]: f.b = 42

In [6]: f.a
Out[6]: 42.0

In [7]: f.b
Out[7]: 42.0

post-setattr for Dict

I have the following code:

import atom.api as atom

class Counter(atom.Atom):

    content = atom.Dict(atom.Str(), atom.Int())

    @atom.cached_property
    def sum(self):
        return sum(self.content.values())

    def _post_setattr_content(self, old, new):
        print("old", old)
        print("new", new)
        self.get_member('content').reset(self)

def test_counter():
    s = Counter()
    assert s.sum == 0
    s.content["foo"] = 1
    assert s.sum == 1

But unfortunately _post_setattr_content is not called when the dict is updated. Is there an alternative way to trigger the reset when the dict is updated?

Using pandas DataFrame with atom==0.5.1 gives SystemError returned a result with an error set when value changes

After upgrading to atom==0.5.1 I'm getting SystemError: ... returned a result with an error set when any observer is attached to a model or attr that contains a pandas DataFrame in enaml. If I downgrade to 0.4.3 everything works fine (even with the most recent enaml).

I believe this is because the pandas DataFrame has a custom __bool__ function that returns an error...

To reproduce:

import pandas as pd                                                                                                                                                                                                                                                     
from atom.api import Atom, Instance                                                                                                                                                                                                                                     

class Data(Atom): 
    dataframe = Instance(pd.DataFrame)                                                                                                                                                                                                                                                                          

d = Data()                                                                                                                                                                                                                                                              
d.observe('dataframe', print)                                                                                                                                                                                                                                           
d.dataframe = pd.DataFrame()                                                                                                                                                                                                                                            
# {'type': 'create', 'object': <__main__.Data object at 0x7fbb293adcd0>, 'name': 'dataframe', 'value': Empty DataFrame
# Columns: []
# Index: []}

d.dataframe = pd.DataFrame()                                                                                                                                                                                                                                            
# ---------------------------------------------------------------------------
# ValueError                                Traceback (most recent call last)
# ~/miniconda3/envs/test/lib/python3.7/site-packages/pandas/core/generic.py in __nonzero__(self)
#   1478         raise ValueError(
#-> 1479             f"The truth value of a {type(self).__name__} is ambiguous. "
#   1480             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
#
# ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
#
# The above exception was the direct cause of the following exception:
#
# SystemError                               Traceback (most recent call last)
# SystemError: PyEval_EvalFrameEx returned a result with an error set

Installing with pip fails

when I try "pip install atom" as administrator on Win10, it errors out:

Command ""c:\program files (x86)\python35-32\python.exe" -u -c "import setuptools, tokenize;__file__='C:\\Users\\John\\AppData\\Local\\Temp\\pip-build-3m_c0r5p\\atom\\setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record C:\Users\John\AppData\Local\Temp\pip-l8e3y4fb-record\install-record.txt --single-version-externally-managed --compile" failed with error code 1 in C:\Users\John\AppData\Local\Temp\pip-build-3m_c0r5p\atom\

full log: https://puu.sh/st2gc.txt

Cannot use an Atom method as a Qt Slot

The following yields TypeError: cannot create weak reference to 'ATest' object.
Is there a way to do this without using a proxy?

from PyQt4 import QtCore
from atom.api import Atom


class QTest(QtCore.QObject):
    sigTest = QtCore.Signal()


class ATest(Atom):

    def fire():
        pass

a = ATest()
q = QTest()
q.sigTest.connect(a.fire)

Wanted: a way to define derived expressions or properties, so that updates can be observed/subscribed.

Wanted: a way to define derived expressions or properties, so that updates can be observed/subscribed.

Moved here from the enaml issue nucleic/enaml#67


The << operator is great for putting expressions into the view. For instance if the model contains a property that is a floating point number, the view can decide how to format it and will automatically be updated.

But I would like to have the auto-update feature of subscription expressions in the model as well; there are derived calculations which depend on the raw model properties that should be managed by the model, not by the view.

Is there a way to do this?

example class:

class ItemWithQuantityDiscount(Atom):
  quantity = Int()
  basePrice = Float()
  def getQuantityDiscount(self):
      # some complicated formula goes here, depends on quantity
  def getUnitPrice(self):
     return self.basePrice * self.getQuantityDiscount()

and in the view I would like to have:

 Label:
    text << '${0:2f} each'.format(model.getUnitPrice())

From http://docs.enthought.com/enaml/instructional/tut_hello_world.html (yeah, i know, that's the old enaml, but I couldn't find the new docs)

Subscription. RHS can be any expression. The expression will be parsed for dependencies, and any dependency which is a trait attribute on a HasTraits class will have a listener attached. When the listener fires, the expression will be re-evaluated and the value of the view property will be updated.

Add type validation to ReadOnly and Constant

Currently ReadOnly and Constant do not allow to validate their values without a custom method which I find hinder readability in particular with the introduction of static typing. We could introduce a kind argument with the same behavior as in Instance.
For Constant, it would make sense to make it a keyword only argument (and factory could be too), since using a literal default does not call for also specifying a type. For ReadOnly, I would prefer to have kind as first positional argument since I see specifying a default for a ReadOnly member as less common, however it would be a breaking change (easy to fix though).

Any opinion on this ?

atom.core ImportError

Hi, I`m trying to import atom.data, just like in Google docs (https://developers.google.com/google-apps/contacts/v3/) and Always getting the same error:

Traceback (most recent call last):
  File "/Users/Denis/Development/contacts/contacts/client.py", line 4, in <module>
    import atom.data
  File "/Users/Denis/Development/contacts/env/lib/python2.7/site-packages/atom/data.py", line 24, in <module>
    import atom.core
ImportError: No module named core

Process finished with exit code 1

Here is my code:

from __future__ import division
from __future__ import absolute_import
import atom.data

Python 2.7.10. I also tried to install package with pip and setup.py. What did I do wrong? There is only one solution, that works for me: http://stackoverflow.com/questions/30740786/import-error-with-atom-module

PS: I`m sorry for my bad English

Items appended to List objects do not trigger informers. Make this clearer.

Consider this (untested example) code:

class Owner(atom):
    name = Str()
    pets = List()

If John gets a new dog, I can add it with this line:

john.pets = john.pets + ["Fido"]

an observer will be notified of the change.

However, if I add it with this line:

john.pets.append("Fido")

a new list will be created, the original list won't change, and the observer will not be informed.

I believe this is working as was originally designed, but this subtlety is not clear. It is a trap for newbies that I fell into and wasted a few hours on trying to work out why my Enaml GUI wasn't updating.

If it doesn't make sense to make this an observer trigger, then at least add warnings in the comments and documentation to save others from falling for the same trap.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.