LowVoltage/actions/next_gen_mixins.py
# coding: utf8
# Copyright 2014-2015 Vincent Jacques <vincent@vincent-jacques.net>
import numbers
import inspect
import LowVoltage.exceptions as _exn
import LowVoltage.testing as _tst
from LowVoltage.variadic import variadic
from .conversion import _convert_value_to_db, _convert_dict_to_db
class ScalarParameter(object):
def __init__(self, name, parent, value):
self._name = name
self._parent = parent
self.set(value)
def set(self, value):
if value is None:
self._value = None
else:
self._value = self._convert(value)
return self._parent
class MandatoryScalarParameter(ScalarParameter):
@property
def payload(self):
if self._value is None:
raise _exn.BuilderError("Mandatory parameter {} is missing.".format(self._name))
else:
return {self._name: self._value}
class OptionalScalarParameter(ScalarParameter):
def __init__(self, name, parent):
super(OptionalScalarParameter, self).__init__(name, parent, None)
@property
def payload(self):
data = {}
if self._value is not None:
data[self._name] = self._value
return data
class OptionalDictParameter(object):
def __init__(self, name, parent):
self._name = name
self._parent = parent
self._values = {}
def add(self, key, value):
self._values[key] = self._convert(value)
return self._parent
@property
def payload(self):
data = {}
if len(self._values) != 0:
data[self._name] = self._values
return data
def plain_parameter_mixin(typ):
class PlainParameterMixin(object):
def _convert(self, s):
if isinstance(s, typ):
return s
else:
raise TypeError("Parameter {} must be a {}.".format(self._name, typ.__name__))
return PlainParameterMixin
class ItemParameterMixin(object):
def _convert(self, item):
if isinstance(item, dict):
return _convert_dict_to_db(item)
else:
raise TypeError("Parameter {} must be a dict.".format(self._name))
class ValueParameterMixin(object):
def _convert(self, value):
return _convert_value_to_db(value)
class IntParameterMixin(plain_parameter_mixin(numbers.Integral)): pass
class BoolParameterMixin(plain_parameter_mixin(bool)): pass
class StringParameterMixin(plain_parameter_mixin(basestring)): pass
class OptionalItemParameter(OptionalScalarParameter, ItemParameterMixin): pass
class OptionalIntParameter(OptionalScalarParameter, IntParameterMixin): pass
class OptionalBoolParameter(OptionalScalarParameter, BoolParameterMixin): pass
class OptionalStringParameter(OptionalScalarParameter, StringParameterMixin): pass
class OptionalDictOfStringParameter(OptionalDictParameter, StringParameterMixin): pass
class OptionalDictOfValueParameter(OptionalDictParameter, ValueParameterMixin): pass
class MandatoryItemParameter(MandatoryScalarParameter, ItemParameterMixin): pass
class MandatoryIntParameter(MandatoryScalarParameter, IntParameterMixin): pass
class MandatoryBoolParameter(MandatoryScalarParameter, BoolParameterMixin): pass
class MandatoryStringParameter(MandatoryScalarParameter, StringParameterMixin): pass
class TableName(MandatoryStringParameter):
def __init__(self, parent, value):
super(TableName, self).__init__("TableName", parent, value)
def set(self, table_name):
"""
Set TableName. Mandatory, can also be set in the constructor.
"""
return super(TableName, self).set(table_name)
class Key(MandatoryItemParameter):
def __init__(self, parent, value):
super(Key, self).__init__("Key", parent, value)
def set(self, key):
"""
Set Key. Mandatory, can also be set in the constructor.
"""
return super(Key, self).set(key)
class Item(MandatoryItemParameter):
def __init__(self, parent, value):
super(Item, self).__init__("Item", parent, value)
def set(self, item):
"""
Set Item. Mandatory, can also be set in the constructor.
"""
return super(Item, self).set(item)
class IndexName(OptionalStringParameter):
def __init__(self, parent):
super(IndexName, self).__init__("IndexName", parent)
def set(self, index_name):
"""
Set IndexName. The request will use this index instead of the table key.
"""
return super(IndexName, self).set(index_name)
class ConditionExpression(OptionalStringParameter):
def __init__(self, parent):
super(ConditionExpression, self).__init__("ConditionExpression", parent)
def set(self, expression):
"""
Set the ConditionExpression, making the request conditional.
It will raise a :exc:`.ConditionalCheckFailedException` if the condition is not met.
"""
return super(ConditionExpression, self).set(expression)
class ConsistentRead(OptionalBoolParameter):
def __init__(self, parent):
super(ConsistentRead, self).__init__("ConsistentRead", parent)
def false(self):
"""
Set ConsistentRead to False.
The request will use eventually consistent reads.
"""
return self.set(False)
def true(self):
"""
Set ConsistentRead to True.
The request will use strong consistent reads.
"""
return self.set(True)
class ExclusiveStartKey(OptionalItemParameter):
def __init__(self, parent):
super(ExclusiveStartKey, self).__init__("ExclusiveStartKey", parent)
def set(self, key):
"""
Set ExclusiveStartKey. The request will only scan items that are after this key.
This is typically the :attr:`~{}Response.last_evaluated_key` of a previous response.
"""
return super(ExclusiveStartKey, self).set(key)
class ExpressionAttributeNames(OptionalDictOfStringParameter):
def __init__(self, parent):
super(ExpressionAttributeNames, self).__init__("ExpressionAttributeNames", parent)
def add(self, synonym, name):
"""
Add a synonym for an attribute name to ExpressionAttributeNames.
Useful for attributes whose names don't play well with ProjectionExpression, ConditionExpression or UpdateExpression because they contain a dot or brackets.
"""
return super(ExpressionAttributeNames, self).add("#" + synonym, name)
class ExpressionAttributeValues(OptionalDictOfValueParameter):
def __init__(self, parent):
super(ExpressionAttributeValues, self).__init__("ExpressionAttributeValues", parent)
def add(self, name, value):
"""
Add a named value to ExpressionAttributeValues.
"""
return super(ExpressionAttributeValues, self).add(":" + name, value)
class FilterExpression(OptionalStringParameter):
def __init__(self, parent):
super(FilterExpression, self).__init__("FilterExpression", parent)
def set(self, expression):
"""
Set the FilterExpression. The response will contain only items that match.
"""
return super(FilterExpression, self).set(expression)
class Limit(OptionalIntParameter):
def __init__(self, parent):
super(Limit, self).__init__("Limit", parent)
def set(self, limit):
"""
Set Limit. The request will scan at most this number of items.
"""
return super(Limit, self).set(limit)
class ProjectionExpression(object):
def __init__(self, parent):
self.__names = []
self.__parent = parent
@property
def payload(self):
data = {}
if len(self.__names) != 0:
data["ProjectionExpression"] = ", ".join(self.__names)
return data
@variadic(basestring)
def add(self, *names):
"""
Add name(s) to ProjectionExpression.
The request will return only projected attributes.
"""
self.__names.extend(names)
return self.__parent
class ReturnConsumedCapacity(OptionalStringParameter):
def __init__(self, parent):
super(ReturnConsumedCapacity, self).__init__("ReturnConsumedCapacity", parent)
def indexes(self):
"""
Set ReturnConsumedCapacity to INDEXES.
The response will contain the capacity consumed by this request detailled on the table and the indexes.
"""
return self.set("INDEXES")
def none(self):
"""
Set ReturnConsumedCapacity to NONE.
The response will not contain the capacity consumed by this request.
"""
return self.set("NONE")
def total(self):
"""
Set ReturnConsumedCapacity to TOTAL.
The response will contain the total capacity consumed by this request.
"""
return self.set("TOTAL")
class ReturnItemCollectionMetrics(OptionalStringParameter):
def __init__(self, parent):
super(ReturnItemCollectionMetrics, self).__init__("ReturnItemCollectionMetrics", parent)
def none(self):
"""
Set ReturnItemCollectionMetrics to NONE.
The response will not contain any item collection metrics.
"""
return self.set("NONE")
def size(self):
"""
Set ReturnItemCollectionMetrics to SIZE.
If the table has a local secondary index, the response will contain metrics about the size of item collections that were touched.
"""
return self.set("SIZE")
class ReturnValues(OptionalStringParameter):
def __init__(self, parent):
super(ReturnValues, self).__init__("ReturnValues", parent)
def all_new(self):
"""
Set ReturnValues to ALL_NEW.
The response will contain all the attributes of the item in its new state.
"""
return self.set("ALL_NEW")
def all_old(self):
"""
Set ReturnValues to ALL_OLD.
The response will contain all the attributes of the item in its previous state.
"""
return self.set("ALL_OLD")
def none(self):
"""
Set ReturnValues to NONE.
The response will not include the attributes of the item.
"""
return self.set("NONE")
def updated_new(self):
"""
Set ReturnValues to UPDATED_NEW.
The response will contain the just-updated attributes of the item in its new state.
"""
return self.set("UPDATED_NEW")
def updated_old(self):
"""
Set ReturnValues to UPDATED_OLD.
The response will contain the just-updated attributes of the item in its previous state.
"""
return self.set("UPDATED_OLD")
class Select(OptionalStringParameter):
def __init__(self, parent):
super(Select, self).__init__("Select", parent)
def all_attributes(self):
"""
Set Select to ALL_ATTRIBUTES. The response will contain all attributes of the matching items.
"""
return self.set("ALL_ATTRIBUTES")
def all_projected_attributes(self):
"""
Set Select to ALL_PROJECTED_ATTRIBUTES. Usable only when querying an index.
The response will contain the attributes of the matching items that are projected on the index.
"""
return self.set("ALL_PROJECTED_ATTRIBUTES")
def count(self):
"""
Set Select to COUNT. The response will contain only the count of matching items.
"""
return self.set("COUNT")
def proxy(*proxy_args):
bases = {
"condition_expression": ConditionExpression.set,
"consistent_read_false": ConsistentRead.false,
"consistent_read_true": ConsistentRead.true,
"exclusive_start_key": ExclusiveStartKey.set,
"expression_attribute_name": ExpressionAttributeNames.add,
"expression_attribute_value": ExpressionAttributeValues.add,
"filter_expression": FilterExpression.set,
"index_name": IndexName.set,
"item": Item.set,
"key": Key.set,
"limit": Limit.set,
"project": ProjectionExpression.add,
"return_consumed_capacity_indexes": ReturnConsumedCapacity.indexes,
"return_consumed_capacity_none": ReturnConsumedCapacity.none,
"return_consumed_capacity_total": ReturnConsumedCapacity.total,
"return_item_collection_metrics_none": ReturnItemCollectionMetrics.none,
"return_item_collection_metrics_size": ReturnItemCollectionMetrics.size,
"return_values_all_new": ReturnValues.all_new,
"return_values_all_old": ReturnValues.all_old,
"return_values_none": ReturnValues.none,
"return_values_updated_new": ReturnValues.updated_new,
"return_values_updated_old": ReturnValues.updated_old,
"select_all_attributes": Select.all_attributes,
"select_all_projected_attributes": Select.all_projected_attributes,
"select_count": Select.count,
"table_name": TableName.set,
}
def patch(method, format_args):
base = bases[method.__name__]
assert inspect.getargspec(method) == inspect.getargspec(base), method
method.__doc__ = base.__doc__.format(*format_args) + "\n" + method.__doc__
if len(proxy_args) != 1 or isinstance(proxy_args[0], basestring):
def decorator(method):
patch(method, proxy_args)
return method
return decorator
else:
method = proxy_args[0]
patch(method, ())
return method