forked from ManageIQ/integration_tests
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathupdate.py
80 lines (58 loc) · 2.04 KB
/
update.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
from contextlib import contextmanager
from copy import copy
from cfme.web_ui import fill
def public_fields(o):
"""
Returns: a dict of fields whose name don't start with underscore.
"""
if not hasattr(o, '__dict__'):
return dict()
return dict((key, value) for key, value in o.__dict__.iteritems()
if not key.startswith('_'))
def all_public_fields_equal(a, b):
return public_fields(a) == public_fields(b)
def updates(old, new):
"""
Return a dict of fields that are different between old and new.
"""
d = {}
o = public_fields(old)
for k, v in public_fields(new).items():
if not v == o[k]:
d[k] = v
return d
class Updateable(object):
"""
A mixin that helps make an object easily updateable. Two Updateables
are equal if all their public fields are equal.
"""
def __eq__(self, other):
return all_public_fields_equal(self, other)
@contextmanager
def update(o, **kwargs):
"""
Update an object and then sync it with an external application.
It will copy the object into whatever is named in the 'as'
clause, run the 'with' code block (which presumably alters the
object). Then the update() method on the original object will be
called with a dict containing only changed fields, and kwargs
passed to this function.
If an exception is thrown by update(), the original object will be restored,
otherwise the updated object will be returned.
Usage:
with update(myrecord):
myrecord.lastname = 'Smith'
"""
cp = copy(o)
# let the block presumably mutate o
yield
# swap the states of o and cp so that cp is the updated one
o.__dict__, cp.__dict__ = cp.__dict__, o.__dict__
o_updates = updates(o, cp)
if o_updates:
o.update(o_updates, **kwargs)
# if update succeeds, have o reflect the changes that are now in cp
o.__dict__ = cp.__dict__
@fill.method((object, Updateable))
def _fill_with_updateable(o, u, **kw):
fill(o, u.__dict__, **kw)