import bloop.model
import bloop.dynamo
import bloop.expression
import declare
import botocore
import boto3


class Engine(object):
    model = None

    def __init__(self, namespace=None):
        # Unique namespace so the type engine for multiple bloop Engines
        # won't have the same TypeDefinitions
        self.dynamodb_client = boto3.client("dynamodb")
        self.type_engine = declare.TypeEngine.unique()
        self.model = bloop.model.BaseModel(self)
        self.models = []

    def register(self, model):
        self.models.append(model)
        self.type_engine.register(model)
        columns = model.__meta__['dynamo.columns']
        for column in columns:
            self.type_engine.register(column.typedef)
        self.type_engine.bind()

    def __load__(self, model, value):
        return self.type_engine.load(model, value)

    def __dump__(self, model, value):
        return self.type_engine.dump(model, value)

    def bind(self):
        ''' Create tables for all models that have been registered '''
        for model in self.models:
            try:
                table = bloop.dynamo.describe_model(model)
                self.dynamodb_client.create_table(**table)
            except botocore.exceptions.ClientError as error:
                # Raise unless the table already exists
                error_code = error.response['Error']['Code']
                if error_code != 'ResourceInUseException':
                    raise error

@@ -100,16 +100,389 @@
 t boto3%0A
+missing = object()%0A%0A%0Adef dump_key(engine, column, key):%0A    '''%0A    key is a %7Bmodel_name: value%7D dict%0A%0A    returns %7Bdynamo_name: %7Btype: value%7D%7D or %7B%7D%0A    '''%0A    if not column:%0A        return %7B%7D%0A%0A    typedef = column.typedef%0A    key_value = key%5Bcolumn.model_name%5D%0A    dynamo_value = engine.type_engine.dump(typedef, key_value)%0A    return %7Bcolumn.dynamo_name: dynamo_value%7D%0A
 %0A%0Aclass 
@@ -1844,8 +1844,1306 @@
 e error%0A
+%0A    def get(self, model, consistent_read=False, **key):%0A        '''%0A        Return a single instance of the model, or raise KeyError%0A%0A        key must specify values for the hash key and,%0A        if the model has one, the range key.%0A%0A        Example%0A        -------%0A        engine = Engine()%0A%0A        class HashOnly(engine.model):%0A            user_id = Column(NumberType, hash_key=True)%0A%0A        class HashAndRange(engine.model):%0A            user_id = Column(NumberType, hash_key=True)%0A            game_title = Column(StringType, range_key=True)%0A%0A        engine.get(HashOnly, user_id=101)%0A        engine.get(HashOnly, user_id=101, game='Starship X')%0A        '''%0A        meta = model.__meta__%0A        table_name = meta%5B'dynamo.table.name'%5D%0A%0A        hash_key = meta%5B'dynamo.table.hash_key'%5D%0A        range_key = meta%5B'dynamo.table.range_key'%5D%0A%0A        dynamo_key = %7B%7D%0A        dynamo_key.update(dump_key(self, hash_key, key))%0A        dynamo_key.update(dump_key(self, range_key, key))%0A%0A        dynamo_item = self.dynamodb_client.get_item(%0A            TableName=table_name, Key=dynamo_key,%0A            ConsistentRead=consistent_read).get(%22Item%22, missing)%0A%0A        if dynamo_item is missing:%0A            raise KeyError(%22No item found for %7B%7D%22.format(key))%0A        return self.__load__(model, dynamo_item)%0A

Add dump_key helper, implement engine.get