nucleic / atom Goto Github PK
View Code? Open in Web Editor NEWMemory efficient Python objects
Home Page: https://atom.readthedocs.io/en/latest/
License: Other
Memory efficient Python objects
Home Page: https://atom.readthedocs.io/en/latest/
License: Other
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.
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
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+?
What is Delegator
?
What is Signal
?
What is Event
?
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()
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?
When retrieving the value of a Dict member whose value is Typed, one gets a _DictProxy object which makes it impossible to attribute it to another Dict member as the type check fails. The linked gist demonstrate the issue : https://gist.github.com/MatthieuDartiailh/10398423. Simply using dict on the retrieved value can be used as a workaround.
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 ?
Running this on windows results in a dll load failed on import of catom. I definitely have the pyd file on my pythonpath. The only thing I could think of is that there is not an "initcatom" function (seems to be specified here http://docs.python.org/2/faq/windows.html#is-a-pyd-file-the-same-as-a-dll)
Provide a way to query the observers installed with .observe
so that we don't need to keep around extra state, implemented in C++.
This has been a long missing feature and is responsible for a bug. The implementation should be taken from the 1.0.0.dev branch
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.
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)
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." },
Should the Atom docs exemplify how to create a constant member (one that can be set once, but never modified)?
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
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
The project is meant for this and this will allow to centralize the c compatibility layer.
The welcome page has the text "default model binding behaviour for the Enaml UI framework." but the Enaml link is broken.
Also a quick question: is this enaml different from this one?
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.
Hello,
I have see this message while using 0.5.0. What to use instead of Unicode atm for not de deprecated in next version ? Haven't found informations so far.
Thanks
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}
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
Compilation of atom-0.5.1 on Python 3.9.0b1 fails with
atom/src/atomlist.cpp(711): error C3861: '_PyStack_UnpackDict': identifier not found
Line 711 in 0ab56a9
Looks like _PyStack_UnpackDict
was removed from Python's headers and made a static function in python/cpython#14517
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.
If I use enaml, it makes it easy to observe and propagate changes in a model which subclasses Atom, with enaml's built-in operators. (<<, >>, ::, :=)
How do I use Atoms in Python code? How do I add a listener to observe change events?
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.
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.
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 ?
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.
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'
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?
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.
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$ `
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')
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 );
The function def v1_deprecated(warning=None) is attempting to get the func_name attribute of a function in Python3. There is no func_name attribute in Python3 you have to use f.name. See below.
f.func_name
AttributeError: 'function' object has no attribute 'func_name'
f.__name__
'CreateClassFromXMLString'
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
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?
One of my most common bugs is forgetting to rename one of these methods after changing the member name, or just mistyping the name. Hence I would love to have a check for that. Alternativly, allowing the attrs @x.default
aproach to mark the default factory would also be a solution.
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
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
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)
Consider inferring members and behavior from python type hint syntax instead of atom.api types.
Please clarify your intent here. I have no idea what you are trying to do.
Originally posted by @MatthieuDartiailh in #115 (comment)
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.
The implementation should be taken from the 1.0.0.dev branch
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 ?
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
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.