Skip to content

Commit

Permalink
Seriously edit custom types docs, fix subtle bug in Vector types #13
Browse files Browse the repository at this point in the history
  • Loading branch information
numberoverzero committed Jul 9, 2016
1 parent e0c3f8e commit 8088ad7
Show file tree
Hide file tree
Showing 5 changed files with 359 additions and 258 deletions.
27 changes: 13 additions & 14 deletions bloop/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ def extract_key(key_shape, item):


def dump_key(engine, obj):
"""
dump the hash (and range, if there is one) key(s) of an object into
"""dump the hash (and range, if there is one) key(s) of an object into
a dynamo-friendly format.
returns {dynamo_name: {type: value} for dynamo_name in hash/range keys}
Expand All @@ -69,7 +68,8 @@ def dump_key(engine, obj):
def config(engine, key, value):
"""Return a given config value unless it's None.
In that case, fall back to the engine's config value."""
In that case, fall back to the engine's config value.
"""
if value is None:
return engine.config[key]
return value
Expand All @@ -87,11 +87,11 @@ def __init__(self, client=None, type_engine=None, **config):
self.config = dict(DEFAULT_CONFIG)
self.config.update(config)

def _dump(self, model, obj, context=None):
""" Return a dict of the obj in DynamoDB format """
def _dump(self, model, obj, context=None, **kwargs):
"""Return a dict of the obj in DynamoDB format"""
try:
context = context or {"engine": self}
return context["engine"].type_engine.dump(model, obj, context=context)
return context["engine"].type_engine.dump(model, obj, context=context, **kwargs)
except declare.DeclareException:
# Best-effort check for a more helpful message
if isinstance(model, bloop.model.ModelMetaclass):
Expand All @@ -100,27 +100,27 @@ def _dump(self, model, obj, context=None):
raise ValueError("Failed to dump unknown model {}".format(model))

def _instance(self, model):
""" Return an instance of a given model """
"""Return an instance of a given model"""
return self._load(model, {})

def _load(self, model, value, context=None):
def _load(self, model, value, context=None, **kwargs):
try:
context = context or {"engine": self}
return context["engine"].type_engine.load(model, value, context=context)
return context["engine"].type_engine.load(model, value, context=context, **kwargs)
except declare.DeclareException:
# Best-effort check for a more helpful message
if isinstance(model, bloop.model.ModelMetaclass):
raise bloop.exceptions.UnboundModel("load", model, None)
else:
raise ValueError("Failed to load unknown model {}".format(model))

def _update(self, obj, attrs, expected, context=None):
""" Push values by dynamo_name into an object """
def _update(self, obj, attrs, expected, context=None, **kwargs):
"""Push values by dynamo_name into an object"""
context = context or {"engine": self}
for column in expected:
value = attrs.get(column.dynamo_name, None)
if value is not None:
value = context["engine"]._load(column.typedef, value, context=context)
value = context["engine"]._load(column.typedef, value, context=context, **kwargs)
setattr(obj, column.model_name, value)

def bind(self, *, base):
Expand Down Expand Up @@ -186,8 +186,7 @@ def delete(self, objs, *, condition=None, atomic=None):
bloop.tracking.clear(obj)

def load(self, objs, consistent=None):
"""
Populate objects from dynamodb, optionally using consistent reads.
"""Populate objects from dynamodb, optionally using consistent reads.
If any objects are not found, raises NotModified with the attribute
`objects` containing a list of the objects that were not loaded.
Expand Down
6 changes: 3 additions & 3 deletions bloop/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,19 +117,19 @@ def _load(cls, attrs, *, context, **kwargs):
for column in cls.Meta.columns:
if column.dynamo_name in attrs:
expected.add(column)
context["engine"]._update(obj, attrs, expected)
context["engine"]._update(obj, attrs, expected, **kwargs)
return obj

@classmethod
def _dump(cls, obj, *, context, **kwargs):
""" obj -> dict """
attrs = {}
engine = context["engine"].type_engine
type_engine = context["engine"].type_engine
for column in cls.Meta.columns:
value = getattr(obj, column.model_name, None)
# Missing expected column - None is equivalent to empty
if value is not None:
attrs[column.dynamo_name] = engine.dump(column.typedef, value, context=context)
attrs[column.dynamo_name] = type_engine.dump(column.typedef, value, context=context, **kwargs)
return attrs

def __str__(self): # pragma: no cover
Expand Down
61 changes: 36 additions & 25 deletions bloop/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,11 @@ def _dump(self, value, **kwargs):
return {self.backing_type: None}
return {self.backing_type: self.dynamo_dump(value, **kwargs)}

def _register(self, engine):
"""Called when the type is registered with an engine."""
super()._register(engine)

def dynamo_load(self, value, *, context, **kwargs):
return value
raise NotImplementedError()

def dynamo_dump(self, value, *, context, **kwargs):
return value
raise NotImplementedError()

def __repr__(self, *a, **kw): # pragma: no cover
return "{}(python_type={}, backing_type={})".format(
Expand All @@ -58,19 +54,20 @@ class String(Type):
python_type = str
backing_type = STRING

# No need to override dynamo_load, since all values come in as strings
def dynamo_load(self, value, *, context, **kwargs):
return value

def dynamo_dump(self, value, *, context=None, **kwargs):
def dynamo_dump(self, value, *, context, **kwargs):
return str(value)


class UUID(String):
python_type = uuid.UUID

def dynamo_load(self, value, *, context=None, **kwargs):
def dynamo_load(self, value, *, context, **kwargs):
return uuid.UUID(value)

def dynamo_dump(self, value, *, context=None, **kwargs):
def dynamo_dump(self, value, *, context, **kwargs):
return str(value)


Expand Down Expand Up @@ -181,7 +178,7 @@ def __init__(self, typedef=None):
if typedef is None:
raise TypeError("Sets requires a type")
self.typedef = type_instance(typedef)
if typedef.backing_type not in ["N", "S", "B"]:
if typedef.backing_type not in {"N", "S", "B"}:
raise TypeError("Set's typedef must be backed by one of N/S/B but was '{}'".format(typedef.backing_type))
self.backing_type = typedef.backing_type + "S"
super().__init__()
Expand All @@ -191,12 +188,16 @@ def _register(self, engine):
engine.register(self.typedef)

def dynamo_load(self, value, *, context, **kwargs):
load = self.typedef.dynamo_load
return set(load(v, context=context, **kwargs) for v in value)
# local lookup in a tight loop
load = context["engine"].type_engine.load
typedef = self.typedef
return set(load(typedef, v, context=context, **kwargs) for v in value)

def dynamo_dump(self, value, *, context, **kwargs):
dump = self.typedef.dynamo_dump
return [dump(v, context=context, **kwargs) for v in sorted(value)]
# local lookup in a tight loop
dump = context["engine"].type_engine.dump
typedef = self.typedef
return [dump(typedef, v, context=context, **kwargs) for v in sorted(value)]


class Boolean(Type):
Expand Down Expand Up @@ -229,19 +230,21 @@ def _register(self, engine):

def dynamo_load(self, values, *, context, **kwargs):
obj = {}
load = context["engine"].type_engine.load
for key, typedef in self.types.items():
value = values.get(key, None)
if value is not None:
value = typedef._load(value, context=context, **kwargs)
value = load(typedef, value, context=context, **kwargs)
obj[key] = value
return obj

def dynamo_dump(self, values, *, context, **kwargs):
obj = {}
dump = context["engine"].type_engine.dump
for key, typedef in self.types.items():
value = values.get(key, None)
if value is not None:
value = typedef._dump(value, context=context, **kwargs)
value = dump(typedef, value, context=context, **kwargs)
# Never push a literal `None` back to DynamoDB
if value is not None:
obj[key] = value
Expand Down Expand Up @@ -269,12 +272,16 @@ def _register(self, engine):
engine.register(self.typedef)

def dynamo_load(self, values, *, context, **kwargs):
load = self.typedef._load
return {k: load(v, context=context, **kwargs) for k, v in values.items()}
# local lookup in a tight loop
load = context["engine"].type_engine.load
typedef = self.typedef
return {k: load(typedef, v, context=context, **kwargs) for k, v in values.items()}

def dynamo_dump(self, values, *, context, **kwargs):
dump = self.typedef._dump
return {k: dump(v, context=context, **kwargs) for k, v in values.items()}
# local lookup in a tight loop
dump = context["engine"].type_engine.dump
typedef = self.typedef
return {k: dump(typedef, v, context=context, **kwargs) for k, v in values.items()}


class List(Type):
Expand All @@ -296,9 +303,13 @@ def _register(self, engine):
engine.register(self.typedef)

def dynamo_load(self, value, *, context, **kwargs):
load = self.typedef._load
return [load(v, context=context, **kwargs) for v in value]
# local lookup in a tight loop
load = context["engine"].type_engine.load
typedef = self.typedef
return [load(typedef, v, context=context, **kwargs) for v in value]

def dynamo_dump(self, value, *, context, **kwargs):
dump = self.typedef._dump
return [dump(v, context=context, **kwargs) for v in value]
# local lookup in a tight loop
dump = context["engine"].type_engine.dump
typedef = self.typedef
return [dump(typedef, v, context=context, **kwargs) for v in value]
Loading

0 comments on commit 8088ad7

Please sign in to comment.