Skip to content

Commit

Permalink
Merge branch 'feature/atomic-counter'
Browse files Browse the repository at this point in the history
  • Loading branch information
numberoverzero committed Jun 14, 2019
2 parents 44e902c + 2a07bf2 commit 0d0932e
Show file tree
Hide file tree
Showing 5 changed files with 19 additions and 16 deletions.
5 changes: 4 additions & 1 deletion bloop/conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import weakref
from typing import Any, Set

from .actions import ActionType, unwrap, wrap
from .actions import Action, ActionType, unwrap, wrap
from .exceptions import InvalidCondition
from .signals import (
object_deleted,
Expand Down Expand Up @@ -79,6 +79,9 @@ def sync(self, obj, engine) -> None:
value = getattr(obj, column.name, None)
# noinspection PyProtectedMember
value = engine._dump(column.typedef, value)
# add/delete are relative values, we can't expect a specific value from them
if isinstance(value, Action) and value.type in {ActionType.Add, ActionType.Delete}:
continue
condition = column == value
# The renderer shouldn't try to dump the value again.
# We're dumping immediately in case the value is mutable,
Expand Down
6 changes: 2 additions & 4 deletions bloop/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,7 @@ def real_dump(v):
# TODO in 3.0 the code path will simplify by first calling ``value = actions.wrap(value)``
# but for 2.4 we don't return an Action unless one is passed
if isinstance(value, actions.Action):
value.value = real_dump(value.value)
return value
return value.type.new_action(real_dump(value.value))

return real_dump(value)

Expand Down Expand Up @@ -660,8 +659,7 @@ def real_dump(v):
# TODO in 3.0 the code path will simplify by first calling ``value = actions.wrap(value)``
# but for 2.4 we don't return an Action unless one is passed
if isinstance(value, actions.Action):
value.value = real_dump(value.value)
return value
return value.type.new_action(real_dump(value.value))
else:
return real_dump(value)

Expand Down
11 changes: 6 additions & 5 deletions tests/unit/test_conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,15 +205,16 @@ def test_on_saved(engine):
"""Saving is equivalent to loading w.r.t. tracking.
The state after saving is snapshotted for future atomic operations."""
user = User(name="foo", age=3)
user = User(name="foo", email="[email protected]", age=actions.add(1))
object_saved.send(engine, engine=engine, obj=user)

# Since "name" and "age" were the only marked columns saved to DynamoDB,
# they are the only columns that must match for an atomic save. The
# state of the other columns wasn't specified, so it's not safe to
# Since "name" and "email" were the only marked columns saved to DynamoDB,
# they are the only columns that must match for an atomic save. Since age is a relative value,
# we don't know what value it should have (the user probably wanted to save with sync=True here).
# The state of the other columns wasn't specified, so it's not safe to
# assume the intended value (missing vs empty)
assert global_tracking.get_snapshot(user) == (
User.age.is_({"N": "3"}) &
User.email.is_({"S": "[email protected]"}) &
User.name.is_({"S": "foo"})
)

Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ def test_save_unknown_sync(engine, sync):

@pytest.mark.parametrize("sync", ["new", "old"])
def test_save_sync(engine, session, sync):
"""Engine.save(sync='old'|'new') the retured values should be loaded and the object should not be marked dirty"""
"""Engine.save(sync='old'|'new') the returned values should be loaded and the object should not be marked dirty"""

session.save_item.return_value = {
"id": {"S": "user_id"},
Expand Down
11 changes: 6 additions & 5 deletions tests/unit/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,11 @@ class MyType(Type):
def dynamo_dump(self, value, *, context, **kwargs):
return 3

action = actions.Action(action_type, "foo")
action = action_type.new_action("foo")
typedef = MyType()
same = typedef._dump(action, context=None)
assert same is action
assert action.value == {"placeholder": 3}
assert same.type is action_type
assert same.value == {"placeholder": 3}


def test_string():
Expand Down Expand Up @@ -509,9 +509,10 @@ def test_dynamic_path_uses_singleton():
def test_dynamic_dump_action(action_type, value):
"""In 2.4 when an action is passed, the action is returned with a dumped value"""
typedef = DynamicType()
action = actions.Action(action_type, value)
action = action_type.new_action(value)
v = typedef._dump(action, context=None)
assert v is action
assert isinstance(v, actions.Action)
assert v.type is action_type
assert isinstance(v.value, dict)


Expand Down

0 comments on commit 0d0932e

Please sign in to comment.