Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ The project has two goals:
Supported Schema Validation Libraries
-------------------------------------

* `Schematics`_ >= 2.1.0
* `Marshmallow`_ >= 2.15.1
* `Schematics`_ >= 2.1.1
* `Marshmallow`_ >= 4.2.0


Example
Expand Down
3 changes: 0 additions & 3 deletions dynamorm/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import logging

import six

log = logging.getLogger(__name__)


Expand All @@ -15,7 +13,6 @@ class DynaModelException(DynamoException):
"""Base exception for DynaModel problems"""


@six.python_2_unicode_compatible
class ValidationError(DynaModelException):
"""Schema validation failed"""

Expand Down
27 changes: 11 additions & 16 deletions dynamorm/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
import logging
import sys

import six

from .exceptions import DynaModelException
from .indexes import Index
from .relationships import Relationship
Expand Down Expand Up @@ -66,13 +64,13 @@ def should_transform(inner_class):
# collect our indexes & relationships
indexes = dict(
(name, val)
for name, val in six.iteritems(attrs)
for name, val in attrs.items()
if inspect.isclass(val) and issubclass(val, Index)
)

attrs["relationships"] = dict(
(name, val)
for name, val in six.iteritems(attrs)
for name, val in attrs.items()
if isinstance(val, Relationship)
)

Expand Down Expand Up @@ -119,20 +117,19 @@ def should_transform(inner_class):

# Put the instantiated indexes back into our attrs. We instantiate the Index class that's in the attrs and
# provide the actual Index object from our table as the parameter.
for name, klass in six.iteritems(indexes):
for name, klass in indexes.items():
index = klass(model, model.Table.indexes[klass.name])
setattr(model, name, index)

for relationship in six.itervalues(model.relationships):
for relationship in model.relationships.values():
relationship.set_this_model(model)

model_prepared.send(model)

return model


@six.add_metaclass(DynaModelMeta)
class DynaModel(object):
class DynaModel(object, metaclass=DynaModelMeta):
"""``DynaModel`` is the base class all of your models will extend from. This model definition encapsulates the
parameters used to create and manage the table as well as the schema for validating and marshalling data into object
attributes. It will also hold any custom business logic you need for your objects.
Expand Down Expand Up @@ -206,7 +203,7 @@ def __init__(self, partial=False, **raw):
# from raw (since it would be ignored when validating anyway), and instead leverage the relationship to
# determine if we should add any new values to raw to represent the relationship
relationships = {}
for name, relationship in six.iteritems(self.relationships):
for name, relationship in self.relationships.items():
new_value = raw.pop(name, None)
if new_value is not None:
relationships[name] = new_value
Expand All @@ -219,10 +216,10 @@ def __init__(self, partial=False, **raw):
self._validated_data = self.Schema.dynamorm_validate(
raw, partial=partial, native=True
)
for k, v in six.iteritems(self._validated_data):
for k, v in self._validated_data.items():
setattr(self, k, v)

for k, v in six.iteritems(relationships):
for k, v in relationships.items():
setattr(self, k, v)

post_init.send(self.__class__, instance=self, partial=partial, raw=raw)
Expand Down Expand Up @@ -298,9 +295,7 @@ def update_item(cls, conditions=None, update_item_kwargs=None, **kwargs):
kwargs.update(
dict(
(k, v)
for k, v in six.iteritems(
cls.Schema.dynamorm_validate(kwargs, partial=True)
)
for k, v in cls.Schema.dynamorm_validate(kwargs, partial=True).items()
if k in kwargs
)
)
Expand Down Expand Up @@ -473,7 +468,7 @@ def save(self, partial=False, unique=False, return_all=False, **kwargs):
# TODO: Support the __ syntax to do deeply nested updates
updates = dict(
(k, getattr(self, k))
for k, v in six.iteritems(self._validated_data)
for k, v in self._validated_data.items()
if getattr(self, k) != v
)

Expand Down Expand Up @@ -561,7 +556,7 @@ def update(

# update our local attrs to match what we updated
partial_model = self.new_from_raw(resp["Attributes"], partial=True)
for key, _ in six.iteritems(resp["Attributes"]):
for key, _ in resp["Attributes"].items():
# elsewhere in Dynamorm, models can be created without all fields (non-"strict" mode in Schematics),
# so we drop unknown keys here to be consistent
if hasattr(partial_model, key):
Expand Down
5 changes: 1 addition & 4 deletions dynamorm/relationships.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,9 @@ class Schema:
)
"""

import six

from .signals import pre_save, post_save, pre_update, post_update


@six.python_2_unicode_compatible
class DefaultBackReference(object):
"""When given a relationship the string representation of this will be a "python" string name of the model the
relationship exists on.
Expand Down Expand Up @@ -251,7 +248,7 @@ def __set__(self, obj, new_instance):
raise TypeError("%s is not an instance of %s", new_instance, self.other)

query = self.query(obj)
for key, val in six.iteritems(query):
for key, val in query.items():
setattr(new_instance, key, val)

self.other_inst = new_instance
Expand Down
41 changes: 18 additions & 23 deletions dynamorm/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,10 @@
import time
import warnings
from collections import defaultdict, OrderedDict

try:
from collections.abc import Iterable, Mapping
except ImportError:
from collections import Iterable, Mapping
from collections.abc import Iterable, Mapping

import boto3
import botocore
import six

from boto3.dynamodb.conditions import Key, Attr
from dynamorm.exceptions import (
Expand Down Expand Up @@ -195,7 +190,7 @@ def __init__(self, schema, indexes=None):

self.indexes = {}
if indexes:
for name, klass in six.iteritems(indexes):
for name, klass in indexes.items():
# Our indexes are just uninstantiated classes, but what we are interested in is what their parent class
# name is. We can reach into the MRO to find that out, and then determine our own index type.
index_type = klass.__mro__[1].__name__
Expand All @@ -207,7 +202,7 @@ def __init__(self, schema, indexes=None):
name,
(index_class,),
dict(
(k, v) for k, v in six.iteritems(klass.__dict__) if k[0] != "_"
(k, v) for k, v in klass.__dict__.items() if k[0] != "_"
),
)

Expand Down Expand Up @@ -243,7 +238,7 @@ def get_resource(cls, **kwargs):

boto3_session = boto3.Session(**(cls.session_kwargs or {}))

for key, val in six.iteritems(cls.resource_kwargs or {}):
for key, val in (cls.resource_kwargs or {}).items():
kwargs.setdefault(key, val)

# allow for dict based resource config that we convert into a botocore Config object
Expand Down Expand Up @@ -301,7 +296,7 @@ def index_attribute_fields(self, index_name=None):
"""Return the attribute fields for a given index, or all indexes if omitted"""
fields = set()

for index in six.itervalues(self.indexes):
for index in self.indexes.values():
if index_name and index.name != index_name:
continue

Expand Down Expand Up @@ -355,7 +350,7 @@ def create_table(self, wait=True):
)

index_args = defaultdict(list)
for index in six.itervalues(self.indexes):
for index in self.indexes.values():
index_args[index.ARG_KEY].append(index.index_args)

log.info("Creating table %s", self.name)
Expand Down Expand Up @@ -462,7 +457,7 @@ def do_update(**kwargs):
self.name,
dict(
(k, v)
for k, v in six.iteritems(table.provisioned_throughput)
for k, v in table.provisioned_throughput.items()
if k.endswith("Units")
),
self.provisioned_throughput,
Expand Down Expand Up @@ -505,7 +500,7 @@ def do_update(**kwargs):

existing_indexes[index["IndexName"]] = index

for index in six.itervalues(self.indexes):
for index in self.indexes.values():
if index.name in existing_indexes:
current_capacity = existing_indexes[index.name]["ProvisionedThroughput"]
if (index.read and index.write) and (
Expand Down Expand Up @@ -641,7 +636,7 @@ def get_update_expr_for_key(self, id_, parts):
for part_id, part_name in enumerate(parts)
]
)
field_name = ".".join(six.iterkeys(field_expr_names))
field_name = ".".join(field_expr_names.keys())

return (
UPDATE_FUNCTION_TEMPLATES[function].format(
Expand All @@ -654,7 +649,7 @@ def get_update_expr_for_key(self, id_, parts):
def update(self, update_item_kwargs=None, conditions=None, **kwargs):
# copy update_item_kwargs, so that we don't mutate the original later on
update_item_kwargs = dict(
(k, v) for k, v in six.iteritems(update_item_kwargs or {})
(k, v) for k, v in (update_item_kwargs or {}).items()
)
conditions = conditions or {}
update_fields = []
Expand Down Expand Up @@ -722,12 +717,12 @@ def update(self, update_item_kwargs=None, conditions=None, **kwargs):
def get_batch(self, keys, consistent=False, attrs=None, batch_get_kwargs=None):
# copy batch_get_kwargs, so that we don't mutate the original later on
batch_get_kwargs = dict(
(k, v) for k, v in six.iteritems(batch_get_kwargs or {})
(k, v) for k, v in (batch_get_kwargs or {}).items()
)

batch_get_kwargs["Keys"] = []
for kwargs in keys:
for k, v in six.iteritems(kwargs):
for k, v in kwargs.items():
if k not in self.schema.dynamorm_fields():
raise InvalidSchemaField(
"{0} does not exist in the schema fields".format(k)
Expand Down Expand Up @@ -757,9 +752,9 @@ def get_batch(self, keys, consistent=False, attrs=None, batch_get_kwargs=None):

def get(self, consistent=False, get_item_kwargs=None, **kwargs):
# copy get_item_kwargs, so that we don't mutate the original later on
get_item_kwargs = dict((k, v) for k, v in six.iteritems(get_item_kwargs or {}))
get_item_kwargs = dict((k, v) for k, v in (get_item_kwargs or {}).items())

for k, v in six.iteritems(kwargs):
for k, v in kwargs.items():
if k not in self.schema.dynamorm_fields():
raise InvalidSchemaField(
"{0} does not exist in the schema fields".format(k)
Expand All @@ -777,7 +772,7 @@ def get(self, consistent=False, get_item_kwargs=None, **kwargs):
def query(self, *args, **kwargs):
# copy query_kwargs, so that we don't mutate the original later on
query_kwargs = dict(
(k, v) for k, v in six.iteritems(kwargs.pop("query_kwargs", {}))
(k, v) for k, v in kwargs.pop("query_kwargs", {}).items()
)
filter_kwargs = {}

Expand Down Expand Up @@ -830,7 +825,7 @@ def query(self, *args, **kwargs):
def scan(self, *args, **kwargs):
# copy scan_kwargs, so that we don't mutate the original later on
scan_kwargs = dict(
(k, v) for k, v in six.iteritems(kwargs.pop("scan_kwargs", {}))
(k, v) for k, v in kwargs.pop("scan_kwargs", {}).items()
)

filter_expression = Q(**kwargs)
Expand All @@ -856,7 +851,7 @@ def remove_nones(in_dict):
try:
return dict(
(key, remove_nones(val))
for key, val in six.iteritems(in_dict)
for key, val in in_dict.items()
if val is not None
)
except (ValueError, AttributeError):
Expand Down Expand Up @@ -914,7 +909,7 @@ def Q(**mapping):
return expression


class ReadIterator(six.Iterator):
class ReadIterator:
"""ReadIterator provides an iterator object that wraps a model and a method (either scan or query).

Since it is an object we can attach attributes and functions to it that are useful to the caller.
Expand Down
13 changes: 6 additions & 7 deletions dynamorm/types/_marshmallow.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import six
from pkg_resources import parse_version
from packaging.version import Version

from marshmallow import Schema as MarshmallowSchema
from marshmallow import Schema as MarshmallowSchema, EXCLUDE
from marshmallow.exceptions import MarshmallowError
from marshmallow import fields, __version__ as marshmallow_version

from .base import DynamORMSchema
from ..exceptions import ValidationError

# Define different validation logic depending on the version of marshmallow we're using
if parse_version(marshmallow_version) >= parse_version("3.0.0a1"):
if Version(marshmallow_version) >= Version("3.0.0a1"):

def _validate(cls, obj, partial=False, native=False):
"""Validate using a Marshmallow v3+ schema"""
try:
if native:
data = cls().load(obj, partial=partial, unknown="EXCLUDE")
data = cls().load(obj, partial=partial, unknown=EXCLUDE)
else:
data = cls(partial=partial, unknown="EXCLUDE").dump(obj)
data = cls(partial=partial, unknown=EXCLUDE).dump(obj)
except MarshmallowError as e:
raise ValidationError(obj, cls.__name__, e)
return data
Expand Down Expand Up @@ -60,7 +59,7 @@ def dynamorm_validate(cls, obj, partial=False, native=False):
# When asking for partial native objects (during model init) we want to return None values
# This ensures our object has all attributes and we can track partial saves properly
if partial and native:
for name in six.iterkeys(cls().fields):
for name in cls().fields.keys():
if name not in data:
data[name] = None

Expand Down
3 changes: 0 additions & 3 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
[wheel]
universal=1

[aliases]
test=pytest

Expand Down
Loading