diff --git a/src/dynamodb_encryption_sdk/__init__.py b/src/dynamodb_encryption_sdk/__init__.py
index a7c66779..82a7c208 100644
--- a/src/dynamodb_encryption_sdk/__init__.py
+++ b/src/dynamodb_encryption_sdk/__init__.py
@@ -10,19 +10,14 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
-""""""
+"""DynamoDB Encryption Client."""
+from dynamodb_encryption_sdk.encrypted.client import EncryptedClient
 from dynamodb_encryption_sdk.encrypted.item import (
     decrypt_dynamodb_item, decrypt_python_item,
     encrypt_dynamodb_item, encrypt_python_item
 )
-
-# encrypt_item
-# encrypt_raw_item
-# decrypt_item
-# decrypt_raw_item
-# EncryptedTable
-# EncryptedResource
-# EncryptedClient
+from dynamodb_encryption_sdk.encrypted.resource import EncryptedResource
+from dynamodb_encryption_sdk.encrypted.table import EncryptedTable
 
 # TableConfiguration
 # MaterialDescription
@@ -30,5 +25,6 @@
 
 __all__ = (
     'decrypt_dynamodb_item', 'decrypt_python_item',
-    'encrypt_dynamodb_item', 'encrypt_python_item'
+    'encrypt_dynamodb_item', 'encrypt_python_item',
+    'EncryptedClient', 'EncryptedResource', 'EncryptedTable'
 )
diff --git a/src/dynamodb_encryption_sdk/delegated_keys/__init__.py b/src/dynamodb_encryption_sdk/delegated_keys/__init__.py
index 4b27071f..9d750fef 100644
--- a/src/dynamodb_encryption_sdk/delegated_keys/__init__.py
+++ b/src/dynamodb_encryption_sdk/delegated_keys/__init__.py
@@ -13,18 +13,27 @@
 """Delegated keys."""
 import abc
 try:  # Python 3.5.0 and 3.5.1 have incompatible typing modules
-    from typing import Dict, Text  # pylint: disable=unused-import
+    from typing import Dict, Text  # noqa pylint: disable=unused-import
 except ImportError:  # pragma: no cover
     # We only actually need these imports when running the mypy checks
     pass
 
 import six
 
-from dynamodb_encryption_sdk.identifiers import EncryptionKeyTypes
+from dynamodb_encryption_sdk.identifiers import EncryptionKeyTypes  # noqa pylint: disable=unused-import
 
 __all__ = ('DelegatedKey',)
 
 
+def _raise_not_implemented(method_name):
+    """Raises a standardized ``NotImplementedError`` to report that the specified method
+    is not supported.
+
+    :raises NotImplementedError: when called
+    """
+    raise NotImplementedError('"{}" is not supported by this DelegatedKey'.format(method_name))
+
+
 @six.add_metaclass(abc.ABCMeta)
 class DelegatedKey(object):
     """Delegated keys are black boxes that encrypt, decrypt, sign, and verify data and wrap
@@ -33,6 +42,7 @@ class DelegatedKey(object):
     Unless overridden by a subclass, any method that a delegated key does not implement raises
     a ``NotImplementedError`` detailing this.
     """
+
     #: Most delegated keys should not be used with RawCryptographicMaterials.
     allowed_for_raw_materials = False
 
@@ -41,16 +51,10 @@ def algorithm(self):
         # type: () -> Text
         """Text description of algorithm used by this delegated key."""
 
-    def _raise_not_implemented(self, method_name):
-        """Raises a standardized ``NotImplementedError`` to report that the specified method
-        is not supported.
-
-        :raises NotImplementedError: when called
-        """
-        raise NotImplementedError('"{}" is not supported by this DelegatedKey'.format(method_name))
-
     @classmethod
     def generate(cls, algorithm, key_length):
+        # type: (Text, int) -> None
+        # pylint: disable=unused-argument,no-self-use
         """Generate an instance of this DelegatedKey using the specified algorithm and key length.
 
         :param str algorithm: Text description of algorithm to be used
@@ -58,10 +62,11 @@ def generate(cls, algorithm, key_length):
         :returns: Generated delegated key
         :rtype: dynamodb_encryption_sdk.delegated_keys.DelegatedKey
         """
-        cls._raise_not_implemented('generate')
+        _raise_not_implemented('generate')
 
     def encrypt(self, algorithm, name, plaintext, additional_associated_data=None):
         # type: (Text, Text, bytes, Dict[Text, Text]) -> bytes
+        # pylint: disable=unused-argument,no-self-use
         """Encrypt data.
 
         :param str algorithm: Text description of algorithm to use to encrypt data
@@ -72,10 +77,11 @@ def encrypt(self, algorithm, name, plaintext, additional_associated_data=None):
         :returns: Encrypted ciphertext
         :rtype: bytes
         """
-        self._raise_not_implemented('encrypt')
+        _raise_not_implemented('encrypt')
 
     def decrypt(self, algorithm, name, ciphertext, additional_associated_data=None):
         # type: (Text, Text, bytes, Dict[Text, Text]) -> bytes
+        # pylint: disable=unused-argument,no-self-use
         """Encrypt data.
 
         :param str algorithm: Text description of algorithm to use to decrypt data
@@ -86,10 +92,11 @@ def decrypt(self, algorithm, name, ciphertext, additional_associated_data=None):
         :returns: Decrypted plaintext
         :rtype: bytes
         """
-        self._raise_not_implemented('decrypt')
+        _raise_not_implemented('decrypt')
 
     def wrap(self, algorithm, content_key, additional_associated_data=None):
         # type: (Text, bytes, Dict[Text, Text]) -> bytes
+        # pylint: disable=unused-argument,no-self-use
         """Wrap content key.
 
         :param str algorithm: Text description of algorithm to use to wrap key
@@ -99,10 +106,11 @@ def wrap(self, algorithm, content_key, additional_associated_data=None):
         :returns: Wrapped key
         :rtype: bytes
         """
-        self._raise_not_implemented('wrap')
+        _raise_not_implemented('wrap')
 
     def unwrap(self, algorithm, wrapped_key, wrapped_key_algorithm, wrapped_key_type, additional_associated_data=None):
         # type: (Text, bytes, Text, EncryptionKeyTypes, Dict[Text, Text]) -> DelegatedKey
+        # pylint: disable=unused-argument,no-self-use
         """Wrap content key.
 
         :param str algorithm: Text description of algorithm to use to unwrap key
@@ -115,10 +123,11 @@ def unwrap(self, algorithm, wrapped_key, wrapped_key_algorithm, wrapped_key_type
         :returns: Delegated key using unwrapped key
         :rtype: dynamodb_encryption_sdk.delegated_keys.DelegatedKey
         """
-        self._raise_not_implemented('unwrap')
+        _raise_not_implemented('unwrap')
 
     def sign(self, algorithm, data):
         # type: (Text, bytes) -> bytes
+        # pylint: disable=unused-argument,no-self-use
         """Sign data.
 
         :param str algorithm: Text description of algorithm to use to sign data
@@ -126,20 +135,22 @@ def sign(self, algorithm, data):
         :returns: Signature value
         :rtype: bytes
         """
-        self._raise_not_implemented('sign')
+        _raise_not_implemented('sign')
 
     def verify(self, algorithm, signature, data):
         # type: (Text, bytes, bytes) -> None
+        # pylint: disable=unused-argument,no-self-use
         """Sign data.
 
         :param str algorithm: Text description of algorithm to use to verify signature
         :param bytes signature: Signature to verify
         :param bytes data: Data over which to verify signature
         """
-        self._raise_not_implemented('verify')
+        _raise_not_implemented('verify')
 
     def signing_algorithm(self):
         # type: () -> Text
+        # pylint: disable=no-self-use
         """Provides a description that can inform an appropriate cryptographic materials
         provider about how to build a DelegatedKey for signature verification. If implemented,
         the return value of this method is included in the material description written to
@@ -148,4 +159,4 @@ def signing_algorithm(self):
         :returns: Signing algorithm identifier
         :rtype: str
         """
-        self._raise_not_implemented('signing_algorithm')
+        _raise_not_implemented('signing_algorithm')
diff --git a/src/dynamodb_encryption_sdk/delegated_keys/jce.py b/src/dynamodb_encryption_sdk/delegated_keys/jce.py
index c37ae98f..dd5c5469 100644
--- a/src/dynamodb_encryption_sdk/delegated_keys/jce.py
+++ b/src/dynamodb_encryption_sdk/delegated_keys/jce.py
@@ -20,10 +20,10 @@
 from cryptography.hazmat.primitives.asymmetric import rsa
 import six
 
-from . import DelegatedKey
 from dynamodb_encryption_sdk.exceptions import JceTransformationError, UnwrappingError
 from dynamodb_encryption_sdk.identifiers import EncryptionKeyTypes, KeyEncodingType, LOGGER_NAME
 from dynamodb_encryption_sdk.internal.crypto.jce_bridge import authentication, encryption, primitives
+from . import DelegatedKey
 
 __all__ = ('JceNameLocalDelegatedKey',)
 _LOGGER = logging.getLogger(LOGGER_NAME)
@@ -67,6 +67,7 @@ def _generate_rsa_key(key_length):
 
 @attr.s
 class JceNameLocalDelegatedKey(DelegatedKey):
+    # pylint: disable=too-many-instance-attributes
     """Delegated key that uses JCE StandardName algorithm values to determine behavior.
 
     :param bytes key: Raw key bytes
@@ -76,6 +77,7 @@ class JceNameLocalDelegatedKey(DelegatedKey):
     :param key_encoding: Identifies how the provided key is encoded
     :type key_encoding: dynamodb_encryption_sdk.identifiers.KeyEncodingTypes
     """
+
     key = attr.ib(validator=attr.validators.instance_of(bytes), repr=False)
     _algorithm = attr.ib(validator=attr.validators.instance_of(six.string_types))
     _key_type = attr.ib(validator=attr.validators.instance_of(EncryptionKeyTypes))
@@ -116,7 +118,11 @@ def __attrs_post_init__(self):
         except KeyError:
             pass
         else:
-            self.__key = key_transformer.load_key(self.key, self._key_type, self._key_encoding)
+            self.__key = key_transformer.load_key(  # attrs confuses pylint: disable=attribute-defined-outside-init
+                self.key,
+                self._key_type,
+                self._key_encoding
+            )
             self._enable_encryption()
             self._enable_wrap()
             return
@@ -129,7 +135,11 @@ def __attrs_post_init__(self):
         except KeyError:
             pass
         else:
-            self.__key = key_transformer.load_key(self.key, self._key_type, self._key_encoding)
+            self.__key = key_transformer.load_key(  # attrs confuses pylint: disable=attribute-defined-outside-init
+                self.key,
+                self._key_type,
+                self._key_encoding
+            )
             self._enable_authentication()
             return
 
@@ -172,6 +182,7 @@ def allowed_for_raw_materials(self):
 
     def _encrypt(self, algorithm, name, plaintext, additional_associated_data=None):
         # type: (Text, Text, bytes, Dict[Text, Text]) -> bytes
+        # pylint: disable=unused-argument
         """
         Encrypt data.
 
@@ -189,6 +200,7 @@ def _encrypt(self, algorithm, name, plaintext, additional_associated_data=None):
 
     def _decrypt(self, algorithm, name, ciphertext, additional_associated_data=None):
         # type: (Text, Text, bytes, Dict[Text, Text]) -> bytes
+        # pylint: disable=unused-argument
         """Encrypt data.
 
         :param str algorithm: Java StandardName transformation string of algorithm to use to decrypt data
@@ -204,6 +216,7 @@ def _decrypt(self, algorithm, name, ciphertext, additional_associated_data=None)
 
     def _wrap(self, algorithm, content_key, additional_associated_data=None):
         # type: (Text, bytes, Dict[Text, Text]) -> bytes
+        # pylint: disable=unused-argument
         """Wrap content key.
 
         :param str algorithm: Text description of algorithm to use to wrap key
@@ -220,6 +233,7 @@ def _wrap(self, algorithm, content_key, additional_associated_data=None):
 
     def _unwrap(self, algorithm, wrapped_key, wrapped_key_algorithm, wrapped_key_type, additional_associated_data=None):
         # type: (Text, bytes, Text, EncryptionKeyTypes, Dict[Text, Text]) -> DelegatedKey
+        # pylint: disable=unused-argument
         """Wrap content key.
 
         :param str algorithm: Text description of algorithm to use to unwrap key
diff --git a/src/dynamodb_encryption_sdk/encrypted/__init__.py b/src/dynamodb_encryption_sdk/encrypted/__init__.py
index ebd440fc..c9b24e7d 100644
--- a/src/dynamodb_encryption_sdk/encrypted/__init__.py
+++ b/src/dynamodb_encryption_sdk/encrypted/__init__.py
@@ -10,13 +10,21 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
-import attr
+"""Resources for encrypting items."""
 import copy
 
+import attr
+
+try:  # Python 3.5.0 and 3.5.1 have incompatible typing modules
+    from typing import Dict  # noqa pylint: disable=unused-import
+except ImportError:  # pragma: no cover
+    # We only actually need these imports when running the mypy checks
+    pass
+
 from dynamodb_encryption_sdk.exceptions import InvalidArgumentError
 from dynamodb_encryption_sdk.identifiers import ItemAction
 from dynamodb_encryption_sdk.material_providers import CryptographicMaterialsProvider
-from dynamodb_encryption_sdk.materials import DecryptionMaterials, EncryptionMaterials
+from dynamodb_encryption_sdk.materials import DecryptionMaterials, EncryptionMaterials  # noqa pylint: disable=unused-import
 from dynamodb_encryption_sdk.structures import AttributeActions, EncryptionContext
 
 __all__ = ('CryptoConfig',)
@@ -33,11 +41,13 @@ class CryptoConfig(object):
     :param attribute_actions: Description of what action should be taken for each attribute
     :type attribute_actions: dynamodb_encryption_sdk.structures.AttributeActions
     """
+
     materials_provider = attr.ib(validator=attr.validators.instance_of(CryptographicMaterialsProvider))
     encryption_context = attr.ib(validator=attr.validators.instance_of(EncryptionContext))
     attribute_actions = attr.ib(validator=attr.validators.instance_of(AttributeActions))
 
     def __attrs_post_init__(self):
+        # type: () -> None
         """Make sure that primary index attributes are not being encrypted."""
         if self.encryption_context.partition_key_name is not None:
             if self.attribute_actions.action(self.encryption_context.partition_key_name) is ItemAction.ENCRYPT_AND_SIGN:
@@ -48,6 +58,7 @@ def __attrs_post_init__(self):
                 raise InvalidArgumentError('Cannot encrypt sort key')
 
     def decryption_materials(self):
+        # type: () -> DecryptionMaterials
         """Load decryption materials from instance resources.
 
         :returns: Decryption materials
@@ -56,6 +67,7 @@ def decryption_materials(self):
         return self.materials_provider.decryption_materials(self.encryption_context)
 
     def encryption_materials(self):
+        # type: () -> EncryptionMaterials
         """Load encryption materials from instance resources.
 
         :returns: Encryption materials
@@ -64,6 +76,7 @@ def encryption_materials(self):
         return self.materials_provider.encryption_materials(self.encryption_context)
 
     def copy(self):
+        # type: () -> CryptoConfig
         """Return a copy of this instance with a copied instance of its encryption context.
 
         :returns: New CryptoConfig identical to this one
@@ -77,6 +90,7 @@ def copy(self):
 
 
 def validate_get_arguments(kwargs):
+    # type: (Dict[Text, Any]) -> None
     """Verify that attribute filtering parameters are not found in the request.
 
     :raises InvalidArgumentError: if banned parameters are found
diff --git a/src/dynamodb_encryption_sdk/encrypted/client.py b/src/dynamodb_encryption_sdk/encrypted/client.py
index 2145fc28..4c61674e 100644
--- a/src/dynamodb_encryption_sdk/encrypted/client.py
+++ b/src/dynamodb_encryption_sdk/encrypted/client.py
@@ -10,15 +10,15 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
-""""""
+"""High-level helper class to provide a familiar interface to encrypted tables."""
 import attr
 import botocore.client
 
-from . import CryptoConfig, validate_get_arguments
-from .item import decrypt_dynamodb_item, encrypt_dynamodb_item
 from dynamodb_encryption_sdk.internal.utils import TableInfoCache
 from dynamodb_encryption_sdk.material_providers import CryptographicMaterialsProvider
 from dynamodb_encryption_sdk.structures import AttributeActions, EncryptionContext
+from . import CryptoConfig, validate_get_arguments
+from .item import decrypt_dynamodb_item, encrypt_dynamodb_item
 
 __all__ = ('EncryptedClient',)
 
@@ -44,6 +44,7 @@ class EncryptedClient(object):
         We do not currently support the ``update_item`` method.
 
     """
+
     _client = attr.ib(validator=attr.validators.instance_of(botocore.client.BaseClient))
     _materials_provider = attr.ib(validator=attr.validators.instance_of(CryptographicMaterialsProvider))
     _attribute_actions = attr.ib(
diff --git a/src/dynamodb_encryption_sdk/encrypted/item.py b/src/dynamodb_encryption_sdk/encrypted/item.py
index b9cff8c0..bf34d15d 100644
--- a/src/dynamodb_encryption_sdk/encrypted/item.py
+++ b/src/dynamodb_encryption_sdk/encrypted/item.py
@@ -12,13 +12,11 @@
 # language governing permissions and limitations under the License.
 """Top-level functions for encrypting and decrypting DynamoDB items."""
 try:  # Python 3.5.0 and 3.5.1 have incompatible typing modules
-    from typing import Any, Callable, Dict  # pylint: disable=unused-import
-    from dynamodb_encryption_sdk.internal import dynamodb_types  # pylint: disable=unused-import
+    from dynamodb_encryption_sdk.internal import dynamodb_types  # noqa pylint: disable=unused-import
 except ImportError:  # pragma: no cover
     # We only actually need these imports when running the mypy checks
     pass
 
-from . import CryptoConfig
 from dynamodb_encryption_sdk.exceptions import DecryptionError, EncryptionError
 from dynamodb_encryption_sdk.identifiers import ItemAction
 from dynamodb_encryption_sdk.internal.crypto.authentication import sign_item, verify_item_signature
@@ -26,9 +24,11 @@
 from dynamodb_encryption_sdk.internal.formatting.material_description import (
     deserialize as deserialize_material_description, serialize as serialize_material_description
 )
-from dynamodb_encryption_sdk.internal.identifiers import MaterialDescriptionKeys, MaterialDescriptionValues
 from dynamodb_encryption_sdk.internal.formatting.transform import ddb_to_dict, dict_to_ddb
-from dynamodb_encryption_sdk.internal.identifiers import ReservedAttributes
+from dynamodb_encryption_sdk.internal.identifiers import (
+    MaterialDescriptionKeys, MaterialDescriptionValues, ReservedAttributes
+)
+from . import CryptoConfig  # noqa pylint: disable=unused-import
 
 __all__ = ('encrypt_dynamodb_item', 'encrypt_python_item', 'decrypt_dynamodb_item', 'decrypt_python_item')
 
diff --git a/src/dynamodb_encryption_sdk/encrypted/resource.py b/src/dynamodb_encryption_sdk/encrypted/resource.py
index 4b0e879f..3e49eb78 100644
--- a/src/dynamodb_encryption_sdk/encrypted/resource.py
+++ b/src/dynamodb_encryption_sdk/encrypted/resource.py
@@ -10,17 +10,17 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
-""""""
+"""High-level helper class to provide a familiar interface to encrypted tables."""
 import attr
 from boto3.resources.base import ServiceResource
 from boto3.resources.collection import CollectionManager
 
-from . import CryptoConfig, validate_get_arguments
-from .item import decrypt_python_item, encrypt_python_item
-from .table import EncryptedTable
 from dynamodb_encryption_sdk.internal.utils import TableInfoCache
 from dynamodb_encryption_sdk.material_providers import CryptographicMaterialsProvider
 from dynamodb_encryption_sdk.structures import AttributeActions, EncryptionContext
+from . import CryptoConfig, validate_get_arguments
+from .item import decrypt_python_item, encrypt_python_item
+from .table import EncryptedTable
 
 __all__ = ('EncryptedResource',)
 
@@ -40,6 +40,7 @@ class EncryptedTablesCollectionManager(object):
     :param table_info_cache: Local cache from which to obtain TableInfo data
     :type table_info_cache: dynamodb_encryption_sdk.internal.utils.TableInfoCache
     """
+
     _collection = attr.ib(validator=attr.validators.instance_of(CollectionManager))
     _materials_provider = attr.ib(validator=attr.validators.instance_of(CryptographicMaterialsProvider))
     _attribute_actions = attr.ib(validator=attr.validators.instance_of(AttributeActions))
@@ -124,6 +125,7 @@ class EncryptedResource(object):
     :param bool auto_refresh_table_indexes: Should we attempt to refresh information about table indexes?
         Requires ``dynamodb:DescribeTable`` permissions on each table. (default: True)
     """
+
     _resource = attr.ib(validator=attr.validators.instance_of(ServiceResource))
     _materials_provider = attr.ib(validator=attr.validators.instance_of(CryptographicMaterialsProvider))
     _attribute_actions = attr.ib(
diff --git a/src/dynamodb_encryption_sdk/encrypted/table.py b/src/dynamodb_encryption_sdk/encrypted/table.py
index cf0b9834..366056a9 100644
--- a/src/dynamodb_encryption_sdk/encrypted/table.py
+++ b/src/dynamodb_encryption_sdk/encrypted/table.py
@@ -14,10 +14,10 @@
 import attr
 from boto3.resources.base import ServiceResource
 
-from . import CryptoConfig, validate_get_arguments
-from .item import decrypt_python_item, encrypt_python_item
 from dynamodb_encryption_sdk.material_providers import CryptographicMaterialsProvider
 from dynamodb_encryption_sdk.structures import AttributeActions, EncryptionContext, TableInfo
+from . import CryptoConfig, validate_get_arguments
+from .item import decrypt_python_item, encrypt_python_item
 
 __all__ = ('EncryptedTable',)
 
@@ -52,6 +52,7 @@ class EncryptedTable(object):
     :param bool auto_refresh_table_indexes: Should we attempt to refresh information about table indexes?
         Requires ``dynamodb:DescribeTable`` permissions on each table. (default: True)
     """
+
     _table = attr.ib(validator=attr.validators.instance_of(ServiceResource))
     _materials_provider = attr.ib(validator=attr.validators.instance_of(CryptographicMaterialsProvider))
     _table_info = attr.ib(
diff --git a/src/dynamodb_encryption_sdk/exceptions.py b/src/dynamodb_encryption_sdk/exceptions.py
index 86364247..1191f09f 100644
--- a/src/dynamodb_encryption_sdk/exceptions.py
+++ b/src/dynamodb_encryption_sdk/exceptions.py
@@ -10,6 +10,7 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
+"""Exception classed for use in the DynamoDB Encryption Client."""
 
 
 class DynamodbEncryptionSdkError(Exception):
@@ -17,7 +18,7 @@ class DynamodbEncryptionSdkError(Exception):
 
 
 class InvalidArgumentError(DynamodbEncryptionSdkError):
-    """"""
+    """Raised when a general invalid argument is provided."""
 
 
 class SerializationError(DynamodbEncryptionSdkError):
@@ -36,53 +37,53 @@ class InvalidMaterialDescriptionVersionError(DeserializationError):
     """Raised when a material description is encountered with an invalid version."""
 
 
-class InvalidAlgorithmError(DynamodbEncryptionSdkError):
+class InvalidAlgorithmError(InvalidArgumentError):
     """Raised when an invalid algorithm identifier is encountered."""
 
 
 class JceTransformationError(DynamodbEncryptionSdkError):
-    """"""
+    """Otherwise undifferentiated errors encountered when attempting to read a JCE transformation."""
 
 
 class DelegatedKeyError(DynamodbEncryptionSdkError):
-    """"""
+    """Otherwise undifferentiated errors encountered by a DelegatedKey."""
 
 
 class DelegatedKeyEncryptionError(DelegatedKeyError):
-    """"""
+    """Raised when a DelegatedKey encounters an error during encryption."""
 
 
 class DelegatedKeyDecryptionError(DelegatedKeyError):
-    """"""
+    """Raised when a DelegatedKey encounters an error during decryption."""
 
 
 class AwsKmsMaterialsProviderError(DynamodbEncryptionSdkError):
-    """"""
+    """Otherwise undifferentiated errors encountered by the AwsKmsCryptographicMaterialsProvider."""
 
 
 class UnknownRegionError(AwsKmsMaterialsProviderError):
-    """"""
+    """Raised when the AwsKmsCryptographicMaterialsProvider is asked for an unknown region."""
 
 
 class DecryptionError(DynamodbEncryptionSdkError):
-    """"""
+    """Otherwise undifferentiated error encountered while decrypting data."""
 
 
 class UnwrappingError(DynamodbEncryptionSdkError):
-    """"""
+    """Otherwise undifferentiated error encountered while unwrapping a key."""
 
 
 class EncryptionError(DynamodbEncryptionSdkError):
-    """"""
+    """Otherwise undifferentiated error encountered while encrypting data."""
 
 
 class WrappingError(DynamodbEncryptionSdkError):
-    """"""
+    """Otherwise undifferentiated error encountered while wrapping a key."""
 
 
 class SigningError(DynamodbEncryptionSdkError):
-    """"""
+    """Otherwise undifferentiated error encountered while signing data."""
 
 
 class SignatureVerificationError(DynamodbEncryptionSdkError):
-    """"""
+    """Otherwise undifferentiated error encountered while verifying a signature."""
diff --git a/src/dynamodb_encryption_sdk/identifiers.py b/src/dynamodb_encryption_sdk/identifiers.py
index c3538644..48221c3b 100644
--- a/src/dynamodb_encryption_sdk/identifiers.py
+++ b/src/dynamodb_encryption_sdk/identifiers.py
@@ -10,6 +10,7 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
+"""Unique identifiers used by the DynamoDB Encryption Client."""
 from enum import Enum
 
 __all__ = ('LOGGER_NAME', 'ItemAction', 'EncryptionKeyTypes', 'KeyEncodingType')
@@ -20,22 +21,30 @@
 
 class ItemAction(Enum):
     """Possible actions to take on an item attribute."""
+
     DO_NOTHING = 0
     SIGN_ONLY = 1
     ENCRYPT_AND_SIGN = 2
 
     def __gt__(self, other):
+        # type: (ItemAction) -> bool
+        """Define ItemAction equality."""
         return not self.__lt__(other) and not self.__eq__(other)
 
     def __lt__(self, other):
+        # type: (ItemAction) -> bool
+        """Define ItemAction equality."""
         return self.value < other.value
 
     def __eq__(self, other):
+        # type: (ItemAction) -> bool
+        """Define ItemAction equality."""
         return self.value == other.value
 
 
 class EncryptionKeyTypes(Enum):
     """Supported types of encryption keys."""
+
     SYMMETRIC = 0
     PRIVATE = 1
     PUBLIC = 2
@@ -43,6 +52,7 @@ class EncryptionKeyTypes(Enum):
 
 class KeyEncodingType(Enum):
     """Supported key encoding schemes."""
+
     RAW = 0
     DER = 1
     PEM = 2
diff --git a/src/dynamodb_encryption_sdk/internal/crypto/__init__.py b/src/dynamodb_encryption_sdk/internal/crypto/__init__.py
index 22070dbc..f8e2c233 100644
--- a/src/dynamodb_encryption_sdk/internal/crypto/__init__.py
+++ b/src/dynamodb_encryption_sdk/internal/crypto/__init__.py
@@ -10,4 +10,4 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
-""""""
+"""Inner cryptographic components."""
diff --git a/src/dynamodb_encryption_sdk/internal/crypto/authentication.py b/src/dynamodb_encryption_sdk/internal/crypto/authentication.py
index a12db381..b1720704 100644
--- a/src/dynamodb_encryption_sdk/internal/crypto/authentication.py
+++ b/src/dynamodb_encryption_sdk/internal/crypto/authentication.py
@@ -14,12 +14,12 @@
 from cryptography.hazmat.backends import default_backend
 from cryptography.hazmat.primitives import hashes
 
-from dynamodb_encryption_sdk.delegated_keys import DelegatedKey
-from dynamodb_encryption_sdk.encrypted import CryptoConfig
+from dynamodb_encryption_sdk.delegated_keys import DelegatedKey  # noqa pylint: disable=unused-import
+from dynamodb_encryption_sdk.encrypted import CryptoConfig  # noqa pylint: disable=unused-import
 from dynamodb_encryption_sdk.identifiers import ItemAction
 from dynamodb_encryption_sdk.internal.formatting.serialize.attribute import serialize_attribute
 from dynamodb_encryption_sdk.internal.identifiers import SignatureValues, Tag, TEXT_ENCODING
-from dynamodb_encryption_sdk.structures import AttributeActions
+from dynamodb_encryption_sdk.structures import AttributeActions  # noqa pylint: disable=unused-import
 
 __all__ = ('sign_item', 'verify_item_signature')
 
diff --git a/src/dynamodb_encryption_sdk/internal/crypto/encryption.py b/src/dynamodb_encryption_sdk/internal/crypto/encryption.py
index 31cd2b37..5caf71f6 100644
--- a/src/dynamodb_encryption_sdk/internal/crypto/encryption.py
+++ b/src/dynamodb_encryption_sdk/internal/crypto/encryption.py
@@ -12,13 +12,13 @@
 # language governing permissions and limitations under the License.
 """Functions to handle encrypting and decrypting DynamoDB attributes."""
 try:  # Python 3.5.0 and 3.5.1 have incompatible typing modules
-    from typing import text  # pylint: disable=unused-import
-    from dynamodb_encryption_sdk.internal import dynamodb_types  # pylint: disable=unused-import
+    from typing import Text  # noqa pylint: disable=unused-import
+    from dynamodb_encryption_sdk.internal import dynamodb_types  # noqa pylint: disable=unused-import
 except ImportError:  # pragma: no cover
     # We only actually need these imports when running the mypy checks
     pass
 
-from dynamodb_encryption_sdk.delegated_keys import DelegatedKey
+from dynamodb_encryption_sdk.delegated_keys import DelegatedKey  # noqa pylint: disable=unused-import
 from dynamodb_encryption_sdk.internal.formatting.deserialize.attribute import deserialize_attribute
 from dynamodb_encryption_sdk.internal.formatting.serialize.attribute import serialize_attribute
 from dynamodb_encryption_sdk.internal.identifiers import Tag
diff --git a/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/__init__.py b/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/__init__.py
index 22070dbc..3275667b 100644
--- a/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/__init__.py
+++ b/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/__init__.py
@@ -10,4 +10,6 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
-""""""
+"""Components to provide cryptographic primitives based on JCE Standard Names.
+https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
+"""
diff --git a/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/authentication.py b/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/authentication.py
index 7c3bb366..6392345d 100644
--- a/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/authentication.py
+++ b/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/authentication.py
@@ -12,6 +12,7 @@
 # language governing permissions and limitations under the License.
 """Cryptographic authentication resources for JCE bridge."""
 import abc
+import logging
 
 import attr
 from cryptography.hazmat.backends import default_backend
@@ -19,11 +20,19 @@
 from cryptography.hazmat.primitives.asymmetric import padding, rsa
 import six
 
-from .primitives import load_rsa_key
+try:  # Python 3.5.0 and 3.5.1 have incompatible typing modules
+    from typing import Text  # noqa pylint: disable=unused-import
+except ImportError:  # pragma: no cover
+    # We only actually need these imports when running the mypy checks
+    pass
+
 from dynamodb_encryption_sdk.exceptions import InvalidAlgorithmError, SignatureVerificationError, SigningError
+from dynamodb_encryption_sdk.identifiers import EncryptionKeyTypes, KeyEncodingType, LOGGER_NAME
 from dynamodb_encryption_sdk.internal.validators import callable_validator
+from .primitives import load_rsa_key
 
 __all__ = ('JavaAuthenticator', 'JavaMac', 'JavaSignature', 'JAVA_AUTHENTICATOR')
+_LOGGER = logging.getLogger(LOGGER_NAME)
 
 
 @six.add_metaclass(abc.ABCMeta)
@@ -32,19 +41,50 @@ class JavaAuthenticator(object):
 
     @abc.abstractmethod
     def load_key(self, key, key_type, key_encoding):
-        """"""
+        # (bytes, EncryptionKeyTypes, KeyEncodingType) -> Any
+        # TODO: narrow down the output type
+        """Load a key from bytes.
+
+        :param bytes key: Raw key bytes to load
+        :param key_type: Type of key to load
+        :type key_type: dynamodb_encryption_sdk.identifiers.EncryptionKeyTypes
+        :param key_encoding: Encoding used to serialize ``key``
+        :type key_encoding: dynamodb_encryption_sdk.identifiers.KeyEncodingType
+        :returns: Loaded key
+        :rtype: bytes
+        """
 
     @abc.abstractmethod
     def validate_algorithm(self, algorithm):
-        """"""
+        # type: (Text) -> None
+        """Determine whether the requested algorithm name is compatible with this authenticator.
+
+        :param str algorithm: Algorithm name
+        :raises InvalidAlgorithmError: if specified algorithm name is not compatible with this authenticator
+        """
 
     @abc.abstractmethod
     def sign(self, key, data):
-        """"""
+        # type: (Any, bytes) -> bytes
+        """Sign ``data`` using loaded ``key``.
+
+        :param key: Loaded key
+        :param bytes data: Data to sign
+        :returns: Calculated signature
+        :rtype: bytes
+        :raises SigningError: if unable to sign ``data`` with ``key``
+        """
 
     @abc.abstractmethod
     def verify(self, key, signature, data):
-        """"""
+        # type: (Any, bytes, bytes) -> None
+        """Verify ``signature`` over ``data`` using ``key``.
+
+        :param key: Loaded key
+        :param bytes signature: Signature to verify
+        :param bytes data: Data over which to verify signature
+        :raises SignatureVerificationError: if unable to verify ``signature``
+        """
 
 
 @attr.s
@@ -54,12 +94,17 @@ class JavaMac(JavaAuthenticator):
     https://docs.oracle.com/javase/8/docs/api/javax/crypto/Mac.html
     https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Mac
     """
+
     java_name = attr.ib(validator=attr.validators.instance_of(six.string_types))
     algorithm_type = attr.ib(validator=callable_validator)
     hash_type = attr.ib(validator=callable_validator)
 
     def _build_hmac_signer(self, key):
-        """"""
+        # type: (bytes) -> Any
+        """Build HMAC signer using instance algorithm and hash type and ``key``.
+
+        :param bytes key: Key to use in signer
+        """
         return self.algorithm_type(
             key,
             self.hash_type(),
@@ -67,13 +112,28 @@ def _build_hmac_signer(self, key):
         )
 
     def load_key(self, key, key_type, key_encoding):
-        """"""
+        # (bytes, EncryptionKeyTypes, KeyEncodingType) -> bytes
+        """Load a raw key from bytes.
+
+        :param bytes key: Raw key bytes to load
+        :param key_type: Type of key to load
+        :type key_type: dynamodb_encryption_sdk.identifiers.EncryptionKeyTypes
+        :param key_encoding: Encoding used to serialize ``key``
+        :type key_encoding: dynamodb_encryption_sdk.identifiers.KeyEncodingType
+        :returns: Loaded key
+        :rtype: bytes
+        :raises ValueError: if ``key_type`` is not symmetric or ``key_encoding`` is not raw
+        """
+        if not (key_type is EncryptionKeyTypes.SYMMETRIC and key_encoding is KeyEncodingType.RAW):
+            raise ValueError('Key type must be symmetric and encoding must be raw.')
+
         return key
 
     def validate_algorithm(self, algorithm):
         # type: (Text) -> None
-        """Determine whether the requested algorithm name is compatible with this signature.
+        """Determine whether the requested algorithm name is compatible with this authenticator.
 
+        :param str algorithm: Algorithm name
         :raises InvalidAlgorithmError: if specified algorithm name is not compatible with this authenticator
         """
         if not algorithm.startswith(self.java_name):
@@ -88,25 +148,38 @@ def sign(self, key, data):
         # type: (bytes, bytes) -> bytes
         """Sign ``data`` using loaded ``key``.
 
-        :param bytes key: Raw HMAC key
+        :param bytes key: Loaded key
         :param bytes data: Data to sign
         :returns: Calculated signature
         :rtype: bytes
+        :raises SigningError: if unable to sign ``data`` with ``key``
         """
-        signer = self._build_hmac_signer(key)
-        signer.update(data)
-        return signer.finalize()
+        try:
+            signer = self._build_hmac_signer(key)
+            signer.update(data)
+            return signer.finalize()
+        except Exception:
+            message = 'Unable to sign data'
+            _LOGGER.exception(message)
+            raise SigningError(message)
 
     def verify(self, key, signature, data):
-        """
+        # type: (bytes, bytes, bytes) -> None
+        """Verify ``signature`` over ``data`` using ``key``.
 
-        :param bytes key: Raw HMAC key
+        :param bytes key: Loaded key
         :param bytes signature: Signature to verify
         :param bytes data: Data over which to verify signature
+        :raises SignatureVerificationError: if unable to verify ``signature``
         """
-        verifier = self._build_hmac_signer(key)
-        verifier.update(data)
-        verifier.verify(signature)
+        try:
+            verifier = self._build_hmac_signer(key)
+            verifier.update(data)
+            verifier.verify(signature)
+        except Exception:
+            message = 'Unable to verify signature'
+            _LOGGER.exception(message)
+            raise SignatureVerificationError(message)
 
 
 @attr.s
@@ -116,6 +189,7 @@ class JavaSignature(JavaAuthenticator):
     https://docs.oracle.com/javase/8/docs/api/java/security/Signature.html
     https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Signature
     """
+
     java_name = attr.ib(validator=attr.validators.instance_of(six.string_types))
     algorithm_type = attr.ib()
     hash_type = attr.ib(validator=callable_validator)
@@ -123,8 +197,9 @@ class JavaSignature(JavaAuthenticator):
 
     def validate_algorithm(self, algorithm):
         # type: (Text) -> None
-        """Determine whether the requested algorithm name is compatible with this signature.
+        """Determine whether the requested algorithm name is compatible with this authenticator.
 
+        :param str algorithm: Algorithm name
         :raises InvalidAlgorithmError: if specified algorithm name is not compatible with this authenticator
         """
         if not algorithm.endswith(self.java_name):
@@ -136,35 +211,77 @@ def validate_algorithm(self, algorithm):
             )
 
     def load_key(self, key, key_type, key_encoding):
-        """"""
+        # (bytes, EncryptionKeyTypes, KeyEncodingType) -> Any
+        # TODO: narrow down the output type
+        """Load a key object from the provided raw key bytes.
+
+        :param bytes key: Raw key bytes to load
+        :param key_type: Type of key to load
+        :type key_type: dynamodb_encryption_sdk.identifiers.EncryptionKeyTypes
+        :param key_encoding: Encoding used to serialize ``key``
+        :type key_encoding: dynamodb_encryption_sdk.identifiers.KeyEncodingType
+        :returns: Loaded key
+        :rtype: TODO:
+        :raises ValueError: if ``key_type`` and ``key_encoding`` are not a valid pairing
+        """
         return load_rsa_key(key, key_type, key_encoding)
 
     def sign(self, key, data):
-        """"""
+        # type: (Any, bytes) -> bytes
+        # TODO: narrow down the key type
+        """Sign ``data`` using loaded ``key``.
+
+        :param key: Loaded key
+        :type key: TODO:
+        :param bytes data: Data to sign
+        :returns: Calculated signature
+        :rtype: bytes
+        :raises SigningError: if unable to sign ``data`` with ``key``
+        """
         if hasattr(key, 'public_bytes'):
             raise SigningError('"sign" is not supported by public keys')
-        # TODO: normalize to SigningError
-        return key.sign(
-            data,
-            self.padding_type(),
-            self.hash_type()
-        )
+        try:
+            return key.sign(
+                data,
+                self.padding_type(),
+                self.hash_type()
+            )
+        except Exception:
+            message = 'Unable to sign data'
+            _LOGGER.exception(message)
+            raise SigningError(message)
 
     def verify(self, key, signature, data):
-        """"""
+        # type: (Any, bytes, bytes) -> None
+        # TODO: narrow down the key type
+        """Verify ``signature`` over ``data`` using ``key``.
+
+        :param key: Loaded key
+        :type key: TODO:
+        :param bytes signature: Signature to verify
+        :param bytes data: Data over which to verify signature
+        :raises SignatureVerificationError: if unable to verify ``signature``
+        """
         if hasattr(key, 'private_bytes'):
             _key = key.public_key()
         else:
             _key = key
-        # TODO: normalize to SignatureVerificationError
-        _key.verify(
-            signature,
-            data,
-            self.padding_type(),
-            self.hash_type()
-        )
+        try:
+            _key.verify(
+                signature,
+                data,
+                self.padding_type(),
+                self.hash_type()
+            )
+        except Exception:
+            message = 'Unable to verify signature'
+            _LOGGER.exception(message)
+            raise SignatureVerificationError(message)
 
 
+# Additional possible JCE names that we might support in the future if needed
+# HmacSHA1
+# SHA(1|224|256|384|512)with(|EC)DSA
 JAVA_AUTHENTICATOR = {
     'HmacSHA224': JavaMac('HmacSHA224', hmac.HMAC, hashes.SHA224),
     'HmacSHA256': JavaMac('HmacSHA256', hmac.HMAC, hashes.SHA256),
@@ -174,9 +291,4 @@ def verify(self, key, signature, data):
     'SHA256withRSA': JavaSignature('SHA256withRSA', rsa, hashes.SHA256, padding.PKCS1v15),
     'SHA384withRSA': JavaSignature('SHA384withRSA', rsa, hashes.SHA384, padding.PKCS1v15),
     'SHA512withRSA': JavaSignature('SHA512withRSA', rsa, hashes.SHA512, padding.PKCS1v15)
-    # TODO: should we support these?
-    # HmacMD5
-    # HmacSHA1
-    # (NONE|SHA(1|224|256|384|512))with(|EC)DSA
-    # (NONE|SHA1)withRSA
 }
diff --git a/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/encryption.py b/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/encryption.py
index 3704bb52..fa23e03f 100644
--- a/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/encryption.py
+++ b/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/encryption.py
@@ -13,10 +13,10 @@
 """Cipher resource for JCE bridge."""
 import attr
 
+from dynamodb_encryption_sdk.exceptions import JceTransformationError
 from .primitives import (
-    JAVA_ENCRYPTION_ALGORITHM, JavaEncryptionAlgorithm, JAVA_MODE, JavaMode, JAVA_PADDING, JavaPadding
+    JAVA_ENCRYPTION_ALGORITHM, JAVA_MODE, JAVA_PADDING, JavaEncryptionAlgorithm, JavaMode, JavaPadding
 )
-from dynamodb_encryption_sdk.exceptions import JceTransformationError
 
 __all__ = ('JavaCipher',)
 
@@ -31,6 +31,7 @@ class JavaCipher(object):
     :param mode: TODO:
     :param padding: TODO:
     """
+
     cipher = attr.ib(validator=attr.validators.instance_of(JavaEncryptionAlgorithm))
     mode = attr.ib(validator=attr.validators.instance_of(JavaMode))
     padding = attr.ib(validator=attr.validators.instance_of(JavaPadding))
diff --git a/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/primitives.py b/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/primitives.py
index 19220c22..d4a46b0f 100644
--- a/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/primitives.py
+++ b/src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/primitives.py
@@ -12,14 +12,14 @@
 # language governing permissions and limitations under the License.
 """Cryptographic primitive resources for JCE bridge."""
 import abc
-import attr
 import logging
 import os
 
+import attr
 from cryptography.hazmat.backends import default_backend
-from cryptography.hazmat.primitives import padding as symmetric_padding, hashes, serialization, keywrap
+from cryptography.hazmat.primitives import hashes, keywrap, padding as symmetric_padding, serialization
 from cryptography.hazmat.primitives.asymmetric import padding as asymmetric_padding, rsa
-from cryptography.hazmat.primitives.ciphers import algorithms, modes, Cipher
+from cryptography.hazmat.primitives.ciphers import algorithms, Cipher, modes
 import six
 
 from dynamodb_encryption_sdk.exceptions import (
@@ -79,8 +79,9 @@ def unpadder(self):
 
 @six.add_metaclass(abc.ABCMeta)
 class JavaPadding(object):
+    # pylint: disable=too-few-public-methods
     """Bridge the gap from the Java padding names and Python resources.
-        https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher
+    https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher
     """
 
     @abc.abstractmethod
@@ -90,7 +91,9 @@ def build(self, block_size):
 
 @attr.s
 class SimplePadding(JavaPadding):
+    # pylint: disable=too-few-public-methods
     """Padding types that do not require any preparation."""
+
     java_name = attr.ib(validator=attr.validators.instance_of(six.string_types))
     padding = attr.ib(validator=callable_validator)
 
@@ -106,7 +109,9 @@ def build(self, block_size=None):
 
 @attr.s
 class BlockSizePadding(JavaPadding):
+    # pylint: disable=too-few-public-methods
     """Padding types that require a block size input."""
+
     java_name = attr.ib(validator=attr.validators.instance_of(six.string_types))
     padding = attr.ib(validator=callable_validator)
 
@@ -122,6 +127,7 @@ def build(self, block_size):
 
 @attr.s
 class OaepPadding(JavaPadding):
+    # pylint: disable=too-few-public-methods
     """OAEP padding types. These require more complex setup.
 
     .. warning::
@@ -130,6 +136,7 @@ class OaepPadding(JavaPadding):
         The same hashing algorithm should be used by both OAEP and the MGF, but by default
         Java always uses SHA1 for the MGF.
     """
+
     java_name = attr.ib(validator=attr.validators.instance_of(six.string_types))
     padding = attr.ib(validator=callable_validator)
     digest = attr.ib(validator=callable_validator)
@@ -152,9 +159,11 @@ def build(self, block_size=None):
 
 @attr.s
 class JavaMode(object):
+    # pylint: disable=too-few-public-methods
     """Bridge the gap from the Java encryption mode names and Python resources.
-        https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher
+    https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher
     """
+
     java_name = attr.ib(validator=attr.validators.instance_of(six.string_types))
     mode = attr.ib(validator=callable_validator)
 
@@ -170,9 +179,11 @@ def build(self, iv):
 
 @attr.s
 class JavaEncryptionAlgorithm(object):
+    # pylint: disable=too-few-public-methods
     """Bridge the gap from the Java encryption algorithm names and Python resources.
     https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher
     """
+
     java_name = attr.ib(validator=attr.validators.instance_of(six.string_types))
     cipher = attr.ib()
 
@@ -284,6 +295,7 @@ def unwrap(self, wrapping_key, wrapped_key):
             raise UnwrappingError(error_message)
 
     def encrypt(self, key, data, mode, padding):
+        # this can be disabled by _disable_encryption, so pylint: disable=method-hidden
         """Encrypt data using the supplied values.
 
         :param bytes key: Loaded encryption key
@@ -315,6 +327,7 @@ def encrypt(self, key, data, mode, padding):
             raise EncryptionError(error_message)
 
     def decrypt(self, key, data, mode, padding):
+        # this can be disabled by _disable_encryption, so pylint: disable=method-hidden
         """Decrypt data using the supplied values.
 
         :param bytes key: Loaded decryption key
@@ -360,11 +373,23 @@ def decrypt(self, key, data, mode, padding):
 
 
 def load_rsa_key(key, key_type, key_encoding):
-    """"""
+    # (bytes, EncryptionKeyTypes, KeyEncodingType) -> Any
+    # TODO: narrow down the output type
+    """Load an RSA key object from the provided raw key bytes.
+
+    :param bytes key: Raw key bytes to load
+    :param key_type: Type of key to load
+    :type key_type: dynamodb_encryption_sdk.identifiers.EncryptionKeyTypes
+    :param key_encoding: Encoding used to serialize ``key``
+    :type key_encoding: dynamodb_encryption_sdk.identifiers.KeyEncodingType
+    :returns: Loaded key
+    :rtype: TODO:
+    :raises ValueError: if ``key_type`` and ``key_encoding`` are not a valid pairing
+    """
     try:
         loader = _RSA_KEY_LOADING[key_type][key_encoding]
     except KeyError:
-        raise Exception('Invalid key type: {}'.format(key_type))
+        raise ValueError('Invalid key type and encoding: {} and {}'.format(key_type, key_encoding))
 
     kwargs = dict(data=key, backend=default_backend())
     if key_type is EncryptionKeyTypes.PRIVATE:
@@ -374,7 +399,7 @@ def load_rsa_key(key, key_type, key_encoding):
 
 
 _KEY_LOADERS = {
-   rsa: load_rsa_key
+    rsa: load_rsa_key
 }
 
 
@@ -409,6 +434,7 @@ def load_key(self, key, key_type, key_encoding):
         return _KEY_LOADERS[self.cipher](key, key_type, key_encoding)
 
     def encrypt(self, key, data, mode, padding):
+        # pylint: disable=unused-argument,no-self-use
         """Encrypt data using the supplied values.
 
         :param bytes key: Loaded encryption key
@@ -432,6 +458,7 @@ def encrypt(self, key, data, mode, padding):
             raise EncryptionError(error_message)
 
     def decrypt(self, key, data, mode, padding):
+        # pylint: disable=unused-argument,no-self-use
         """Decrypt data using the supplied values.
 
         :param bytes key: Loaded decryption key
@@ -457,20 +484,12 @@ def decrypt(self, key, data, mode, padding):
     'RSA': JavaAsymmetricEncryptionAlgorithm('RSA', rsa),
     'AES': JavaSymmetricEncryptionAlgorithm('AES', algorithms.AES),
     'AESWrap': JavaSymmetricEncryptionAlgorithm('AESWrap', algorithms.AES)
-    # TODO: Should we support these?
-    # DES : pretty sure we don't want to support this
-    # DESede : pretty sure we don't want to support this
-    # 'BLOWFISH': JavaSymmetricEncryptionAlgorithm('Blowfish', algorithms.Blowfish)
 }
 JAVA_MODE = {
     'ECB': JavaMode('ECB', modes.ECB),
     'CBC': JavaMode('CBC', modes.CBC),
     'CTR': JavaMode('CTR', modes.CTR),
     'GCM': JavaMode('GCM', modes.GCM)
-    # TODO: Should we support these?
-    # 'OFB': JavaMode('OFB', modes.OFB)
-    # 'CFB': JavaMode('CFB', modes.CFB)
-    # 'CFB8': JavaMode('CFB8', modes.CFB8)
 }
 JAVA_PADDING = {
     'NoPadding': SimplePadding('NoPadding', _NoPadding),
diff --git a/src/dynamodb_encryption_sdk/internal/defaults.py b/src/dynamodb_encryption_sdk/internal/defaults.py
deleted file mode 100644
index ca55ca17..00000000
--- a/src/dynamodb_encryption_sdk/internal/defaults.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"). You
-# may not use this file except in compliance with the License. A copy of
-# the License is located at
-#
-# http://aws.amazon.com/apache2.0/
-#
-# or in the "license" file accompanying this file. This file is
-# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
-# ANY KIND, either express or implied. See the License for the specific
-# language governing permissions and limitations under the License.
-""""""
-__all__ = ('LOGGING_NAME', 'MATERIAL_DESCRIPTION_VERSION')
-
-LOGGING_NAME = 'dynamodb_encryption_sdk'
-MATERIAL_DESCRIPTION_VERSION = b'\00' * 4
diff --git a/src/dynamodb_encryption_sdk/internal/formatting/__init__.py b/src/dynamodb_encryption_sdk/internal/formatting/__init__.py
index 22070dbc..eec2d539 100644
--- a/src/dynamodb_encryption_sdk/internal/formatting/__init__.py
+++ b/src/dynamodb_encryption_sdk/internal/formatting/__init__.py
@@ -10,4 +10,4 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
-""""""
+"""Data formatting utilities for the DynamoDB Encryption Client."""
diff --git a/src/dynamodb_encryption_sdk/internal/formatting/deserialize/attribute.py b/src/dynamodb_encryption_sdk/internal/formatting/deserialize/attribute.py
index c1639037..0e303902 100644
--- a/src/dynamodb_encryption_sdk/internal/formatting/deserialize/attribute.py
+++ b/src/dynamodb_encryption_sdk/internal/formatting/deserialize/attribute.py
@@ -19,7 +19,7 @@
 
 try:  # Python 3.5.0 and 3.5.1 have incompatible typing modules
     from typing import Callable, Dict, List, Union  # noqa pylint: disable=unused-import
-    from dynamodb_encryption_sdk.internal import dynamodb_types  # noqa pylint: disable=unused-import
+    from dynamodb_encryption_sdk.internal import dynamodb_types  # noqa pylint: disable=unused-import,ungrouped-imports
 except ImportError:  # pragma: no cover
     # We only actually need these imports when running the mypy checks
     pass
@@ -27,13 +27,13 @@
 from boto3.dynamodb.types import Binary
 
 from dynamodb_encryption_sdk.exceptions import DeserializationError
-from dynamodb_encryption_sdk.internal.defaults import LOGGING_NAME
+from dynamodb_encryption_sdk.identifiers import LOGGER_NAME
 from dynamodb_encryption_sdk.internal.formatting.deserialize import decode_byte, decode_length, decode_tag, decode_value
 from dynamodb_encryption_sdk.internal.identifiers import Tag, TagValues, TEXT_ENCODING
 from dynamodb_encryption_sdk.internal.str_ops import to_str
 
 __all__ = ('deserialize_attribute',)
-_LOGGER = logging.getLogger(LOGGING_NAME)
+_LOGGER = logging.getLogger(LOGGER_NAME)
 
 
 def deserialize_attribute(serialized_attribute):  # noqa: C901 pylint: disable=too-many-locals
diff --git a/src/dynamodb_encryption_sdk/internal/formatting/material_description.py b/src/dynamodb_encryption_sdk/internal/formatting/material_description.py
index 45df93ff..ed3d76fa 100644
--- a/src/dynamodb_encryption_sdk/internal/formatting/material_description.py
+++ b/src/dynamodb_encryption_sdk/internal/formatting/material_description.py
@@ -15,15 +15,16 @@
 import logging
 import struct
 
-from .deserialize import decode_value, unpack_value
-from .serialize import encode_value
 from dynamodb_encryption_sdk.exceptions import InvalidMaterialDescriptionError, InvalidMaterialDescriptionVersionError
-from dynamodb_encryption_sdk.internal.defaults import LOGGING_NAME, MATERIAL_DESCRIPTION_VERSION
+from dynamodb_encryption_sdk.identifiers import LOGGER_NAME
 from dynamodb_encryption_sdk.internal.identifiers import Tag
 from dynamodb_encryption_sdk.internal.str_ops import to_bytes, to_str
+from .deserialize import decode_value, unpack_value
+from .serialize import encode_value
 
 __all__ = ('serialize', 'deserialize')
-_LOGGER = logging.getLogger(LOGGING_NAME)
+_LOGGER = logging.getLogger(LOGGER_NAME)
+_MATERIAL_DESCRIPTION_VERSION = b'\00' * 4
 
 
 def serialize(material_description):
@@ -34,7 +35,7 @@ def serialize(material_description):
     :returns: Serialized material description as a DynamoDB binary attribute value
     :rtype: dict
     """
-    material_description_bytes = bytearray(MATERIAL_DESCRIPTION_VERSION)
+    material_description_bytes = bytearray(_MATERIAL_DESCRIPTION_VERSION)
 
     # TODO: verify Java sorting order
     for name, value in sorted(material_description.items(), key=lambda x: x[0]):
@@ -42,10 +43,12 @@ def serialize(material_description):
             material_description_bytes.extend(encode_value(to_bytes(name)))
             material_description_bytes.extend(encode_value(to_bytes(value)))
         except (TypeError, struct.error):
-            raise InvalidMaterialDescriptionError('Invalid name or value in material description: "{name}"="{value}"'.format(
-                name=name,
-                value=value
-            ))
+            raise InvalidMaterialDescriptionError(
+                'Invalid name or value in material description: "{name}"="{value}"'.format(
+                    name=name,
+                    value=value
+                )
+            )
 
     return {Tag.BINARY.dynamodb_tag: bytes(material_description_bytes)}
 
@@ -99,5 +102,5 @@ def _read_version(material_description_bytes):
         message = 'Malformed material description version'
         _LOGGER.exception(message)
         raise InvalidMaterialDescriptionError(message)
-    if version != MATERIAL_DESCRIPTION_VERSION:
+    if version != _MATERIAL_DESCRIPTION_VERSION:
         raise InvalidMaterialDescriptionVersionError('Invalid material description version: {}'.format(repr(version)))
diff --git a/src/dynamodb_encryption_sdk/internal/formatting/serialize/__init__.py b/src/dynamodb_encryption_sdk/internal/formatting/serialize/__init__.py
index de34b905..0fcaa79e 100644
--- a/src/dynamodb_encryption_sdk/internal/formatting/serialize/__init__.py
+++ b/src/dynamodb_encryption_sdk/internal/formatting/serialize/__init__.py
@@ -14,7 +14,7 @@
 import struct
 
 try:  # Python 3.5.0 and 3.5.1 have incompatible typing modules
-    from typing import Sized  # pylint: disable=unused-import
+    from typing import Sized  # noqa pylint: disable=unused-import
 except ImportError:  # pragma: no cover
     # We only actually need these imports when running the mypy checks
     pass
diff --git a/src/dynamodb_encryption_sdk/internal/formatting/serialize/attribute.py b/src/dynamodb_encryption_sdk/internal/formatting/serialize/attribute.py
index 9ab0afd6..17729642 100644
--- a/src/dynamodb_encryption_sdk/internal/formatting/serialize/attribute.py
+++ b/src/dynamodb_encryption_sdk/internal/formatting/serialize/attribute.py
@@ -16,7 +16,7 @@
 
 try:  # Python 3.5.0 and 3.5.1 have incompatible typing modules
     from typing import Callable  # noqa pylint: disable=unused-import
-    from dynamodb_encryption_sdk.internal import dynamodb_types  # noqa pylint: disable=unused-import
+    from dynamodb_encryption_sdk.internal import dynamodb_types  # noqa pylint: disable=unused-import,ungrouped-imports
 except ImportError:  # pragma: no cover
     # We only actually need these imports when running the mypy checks
     pass
@@ -24,14 +24,14 @@
 from boto3.dynamodb.types import Binary, DYNAMODB_CONTEXT
 
 from dynamodb_encryption_sdk.exceptions import SerializationError
-from dynamodb_encryption_sdk.internal.defaults import LOGGING_NAME
+from dynamodb_encryption_sdk.identifiers import LOGGER_NAME
 from dynamodb_encryption_sdk.internal.formatting.serialize import encode_length, encode_value
 from dynamodb_encryption_sdk.internal.identifiers import Tag, TagValues
 from dynamodb_encryption_sdk.internal.str_ops import to_bytes
 from dynamodb_encryption_sdk.internal.utils import sorted_key_map
 
 __all__ = ('serialize_attribute',)
-_LOGGER = logging.getLogger(LOGGING_NAME)
+_LOGGER = logging.getLogger(LOGGER_NAME)
 _RESERVED = b'\x00'
 
 
diff --git a/src/dynamodb_encryption_sdk/internal/formatting/transform.py b/src/dynamodb_encryption_sdk/internal/formatting/transform.py
index ab8ac20a..8541639a 100644
--- a/src/dynamodb_encryption_sdk/internal/formatting/transform.py
+++ b/src/dynamodb_encryption_sdk/internal/formatting/transform.py
@@ -12,12 +12,12 @@
 # language governing permissions and limitations under the License.
 """Helper tools for translating between native and DynamoDB items."""
 try:  # Python 3.5.0 and 3.5.1 have incompatible typing modules
-    from typing import Any, Dict  # pylint: disable=unused-import
+    from typing import Any, Dict  # noqa pylint: disable=unused-import
 except ImportError:  # pragma: no cover
     # We only actually need these imports when running the mypy checks
     pass
 
-from boto3.dynamodb.types import TypeSerializer, TypeDeserializer
+from boto3.dynamodb.types import TypeDeserializer, TypeSerializer
 
 __all__ = ('dict_to_ddb', 'ddb_to_dict')
 
diff --git a/src/dynamodb_encryption_sdk/internal/identifiers.py b/src/dynamodb_encryption_sdk/internal/identifiers.py
index 55b08b5f..d0acd67a 100644
--- a/src/dynamodb_encryption_sdk/internal/identifiers.py
+++ b/src/dynamodb_encryption_sdk/internal/identifiers.py
@@ -10,11 +10,11 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
-""""""
+"""Unique identifiers for internal use only."""
 from enum import Enum
 
 try:  # Python 3.5.0 and 3.5.1 have incompatible typing modules
-    from typing import Any, ByteString, Dict, List, Text, Union  # pylint: disable=unused-import
+    from typing import Text  # noqa pylint: disable=unused-import
 except ImportError:  # pragma: no cover
     # We only actually need these imports when running the mypy checks
     pass
@@ -31,6 +31,7 @@
 
 class ReservedAttributes(Enum):
     """Item attributes reserved for use by DynamoDBEncryptionClient"""
+
     MATERIAL_DESCRIPTION = '*amzn-ddb-map-desc*'
     SIGNATURE = '*amzn-ddb-map-sig*'
 
@@ -64,6 +65,7 @@ def __init__(self, tag, dynamodb_tag, element_tag=None):
 
 class TagValues(Enum):
     """Static values to use when serializing attribute values."""
+
     FALSE = b'\x00'
     TRUE = b'\x01'
 
@@ -76,6 +78,7 @@ class SignatureValues(Enum):
         The only time we actually use these values, we use the SHA256 hash of the value, so
         we pre-compute these hashes here.
     """
+
     ENCRYPTED = (
         b'ENCRYPTED',
         b"9A\x15\xacN\xb0\x9a\xa4\x94)4\x88\x16\xb2\x03\x81'\xb0\xf9\xe3\xa5 7*\xe1\x00\xca\x19\xfb\x08\xfdP"
@@ -98,6 +101,7 @@ def __init__(self, raw, sha256):
 
 class MaterialDescriptionKeys(Enum):
     """Static keys for use when building and reading material descriptions."""
+
     ATTRIBUTE_ENCRYPTION_MODE = 'amzn-ddb-map-sym-mode'
     SIGNING_KEY_ALGORITHM = 'amzn-ddb-map-signingAlg'
     WRAPPED_DATA_KEY = 'amzn-ddb-env-key'
@@ -108,4 +112,5 @@ class MaterialDescriptionKeys(Enum):
 
 class MaterialDescriptionValues(Enum):
     """Static default values for use when building material descriptions."""
+
     CBC_PKCS5_ATTRIBUTE_ENCRYPTION = '/CBC/PKCS5Padding'
diff --git a/src/dynamodb_encryption_sdk/internal/utils.py b/src/dynamodb_encryption_sdk/internal/utils.py
index e64ffd28..ddcde4e0 100644
--- a/src/dynamodb_encryption_sdk/internal/utils.py
+++ b/src/dynamodb_encryption_sdk/internal/utils.py
@@ -10,13 +10,19 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
-""""""
+"""Otherwise undifferentiated utility resources."""
 import attr
 import botocore.client
 
 from dynamodb_encryption_sdk.internal.str_ops import to_bytes
 from dynamodb_encryption_sdk.structures import TableInfo
 
+try:  # Python 3.5.0 and 3.5.1 have incompatible typing modules
+    from typing import Dict, Text  # noqa pylint: disable=unused-import
+except ImportError:  # pragma: no cover
+    # We only actually need these imports when running the mypy checks
+    pass
+
 __all__ = ('sorted_key_map', 'TableInfoCache')
 
 
@@ -45,12 +51,13 @@ class TableInfoCache(object):
     :param bool auto_refresh_table_indexes: Should we attempt to refresh information about table indexes?
         Requires ``dynamodb:DescribeTable`` permissions on each table.
     """
+
     _client = attr.ib(validator=attr.validators.instance_of(botocore.client.BaseClient))
     _auto_refresh_table_indexes = attr.ib(validator=attr.validators.instance_of(bool))
 
     def __attrs_post_init__(self):
         """Set up the empty cache."""
-        self._all_tables_info = {}
+        self._all_tables_info = {}  # type: Dict[Text, TableInfo]
 
     def table_info(self, table_name):
         """Collect a TableInfo object for the specified table, creating and adding it to
diff --git a/src/dynamodb_encryption_sdk/internal/validators.py b/src/dynamodb_encryption_sdk/internal/validators.py
index e6c3bc04..3eb1c4b7 100644
--- a/src/dynamodb_encryption_sdk/internal/validators.py
+++ b/src/dynamodb_encryption_sdk/internal/validators.py
@@ -10,15 +10,16 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
-"""Custom attrs validators."""
+"""Custom validators for ``attrs``."""
 
 __all__ = ('dictionary_validator', 'iterable_validator')
 
 
 def dictionary_validator(key_type, value_type):
-    """attrs validator that performs deep type checking of dictionaries."""
+    """Validator for ``attrs`` that performs deep type checking of dictionaries."""
 
     def _validate_dictionary(instance, attribute, value):
+        # pylint: disable=unused-argument
         """Validate that a dictionary is structured as expected.
 
         :raises TypeError: if ``value`` is not a dictionary
@@ -45,9 +46,10 @@ def _validate_dictionary(instance, attribute, value):
 
 
 def iterable_validator(iterable_type, member_type):
-    """attrs validator that performs deep type checking of iterables."""
+    """Validator for ``attrs`` that performs deep type checking of iterables."""
 
     def _validate_tuple(instance, attribute, value):
+        # pylint: disable=unused-argument
         """Validate that a dictionary is structured as expected.
 
         :raises TypeError: if ``value`` is not of ``iterable_type`` type
@@ -70,6 +72,7 @@ def _validate_tuple(instance, attribute, value):
 
 
 def callable_validator(instance, attribute, value):
+    # pylint: disable=unused-argument
     """Validate that an attribute value is callable.
 
     :raises TypeError: if ``value`` is not callable
diff --git a/src/dynamodb_encryption_sdk/material_providers/__init__.py b/src/dynamodb_encryption_sdk/material_providers/__init__.py
index 02830208..82735d17 100644
--- a/src/dynamodb_encryption_sdk/material_providers/__init__.py
+++ b/src/dynamodb_encryption_sdk/material_providers/__init__.py
@@ -11,8 +11,8 @@
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
 """Cryptographic materials providers."""
-from dynamodb_encryption_sdk.materials import DecryptionMaterials, EncryptionMaterials
-from dynamodb_encryption_sdk.structures import EncryptionContext
+from dynamodb_encryption_sdk.materials import DecryptionMaterials, EncryptionMaterials  # noqa pylint: disable=unused-import
+from dynamodb_encryption_sdk.structures import EncryptionContext  # noqa pylint: disable=unused-import
 
 __all__ = ('CryptographicMaterialsProvider',)
 
@@ -22,6 +22,7 @@ class CryptographicMaterialsProvider(object):
 
     def decryption_materials(self, encryption_context):
         # type: (EncryptionContext) -> DecryptionMaterials
+        # pylint: disable=unused-argument,no-self-use
         """Return decryption materials.
 
         :param encryption_context: Encryption context for request
@@ -32,6 +33,7 @@ def decryption_materials(self, encryption_context):
 
     def encryption_materials(self, encryption_context):
         # type: (EncryptionContext) -> EncryptionMaterials
+        # pylint: disable=unused-argument,no-self-use
         """Return encryption materials.
 
         :param encryption_context: Encryption context for request
@@ -41,6 +43,8 @@ def encryption_materials(self, encryption_context):
         raise AttributeError('No encryption materials available')
 
     def refresh(self):
+        # type: () -> None
+        # pylint: disable=unused-argument,no-self-use
         """Ask this instance to refresh the cryptographic materials.
 
         .. note::
diff --git a/src/dynamodb_encryption_sdk/material_providers/aws_kms.py b/src/dynamodb_encryption_sdk/material_providers/aws_kms.py
index a3063cb8..32f0eda6 100644
--- a/src/dynamodb_encryption_sdk/material_providers/aws_kms.py
+++ b/src/dynamodb_encryption_sdk/material_providers/aws_kms.py
@@ -12,28 +12,34 @@
 # language governing permissions and limitations under the License.
 """Cryptographic materials provider for use with the AWS Key Management Service (KMS)."""
 from __future__ import division
+
 import base64
 from enum import Enum
 
 import attr
-from cryptography.hazmat.backends import default_backend
-from cryptography.hazmat.primitives import hashes
-from cryptography.hazmat.primitives.kdf.hkdf import HKDF
 import boto3
 import botocore.client
 import botocore.session
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.kdf.hkdf import HKDF
 import six
 
-from . import CryptographicMaterialsProvider
+try:  # Python 3.5.0 and 3.5.1 have incompatible typing modules
+    from dynamodb_encryption_sdk.internal import dynamodb_types  # noqa pylint: disable=unused-import
+except ImportError:  # pragma: no cover
+    # We only actually need these imports when running the mypy checks
+    pass
+
 from dynamodb_encryption_sdk.delegated_keys.jce import JceNameLocalDelegatedKey
 from dynamodb_encryption_sdk.exceptions import UnknownRegionError, UnwrappingError, WrappingError
 from dynamodb_encryption_sdk.identifiers import EncryptionKeyTypes, KeyEncodingType
-from dynamodb_encryption_sdk.internal import dynamodb_types
 from dynamodb_encryption_sdk.internal.identifiers import MaterialDescriptionKeys, TEXT_ENCODING
 from dynamodb_encryption_sdk.internal.str_ops import to_bytes, to_str
 from dynamodb_encryption_sdk.internal.validators import dictionary_validator, iterable_validator
 from dynamodb_encryption_sdk.materials.raw import RawDecryptionMaterials, RawEncryptionMaterials
-from dynamodb_encryption_sdk.structures import EncryptionContext
+from dynamodb_encryption_sdk.structures import EncryptionContext  # noqa pylint: disable=unused-import
+from . import CryptographicMaterialsProvider
 
 __all__ = ('AwsKmsCryptographicMaterialsProvider',)
 
@@ -49,12 +55,14 @@
 
 class HkdfInfo(Enum):
     """Info strings used for HKDF calculations."""
+
     ENCRYPTION = b'Encryption'
     SIGNING = b'Signing'
 
 
 class EncryptionContextKeys(Enum):
     """Special keys for use in the AWS KMS encryption context."""
+
     CONTENT_ENCRYPTION_ALGORITHM = '*' + MaterialDescriptionKeys.CONTENT_ENCRYPTION_ALGORITHM.value + '*'
     SIGNATURE_ALGORITHM = '*' + MaterialDescriptionKeys.ITEM_SIGNATURE_ALGORITHM.value + '*'
     TABLE_NAME = '*aws-kms-table*'
@@ -62,12 +70,14 @@ class EncryptionContextKeys(Enum):
 
 @attr.s
 class KeyInfo(object):
+    # pylint: disable=too-few-public-methods
     """Identifying information for a specific key and how it should be used.
 
     :param str description: algorithm identifier joined with key length in bits
     :param str algorithm: algorithm identifier
     :param int length: Key length in bits
     """
+
     description = attr.ib(validator=attr.validators.instance_of(six.string_types))
     algorithm = attr.ib(validator=attr.validators.instance_of(six.string_types))
     length = attr.ib(validator=attr.validators.instance_of(six.integer_types))
@@ -112,6 +122,7 @@ class AwsKmsCryptographicMaterialsProvider(CryptographicMaterialsProvider):
     :param dict regional_clients: Dictionary mapping AWS region names to pre-configured boto3
         KMS clients (optional)
     """
+
     _key_id = attr.ib(validator=attr.validators.instance_of(six.string_types))
     _botocore_session = attr.ib(
         validator=attr.validators.instance_of(botocore.session.Session),
@@ -133,19 +144,19 @@ class AwsKmsCryptographicMaterialsProvider(CryptographicMaterialsProvider):
     def __attrs_post_init__(self):
         # type: () -> None
         """Load the content and signing key info."""
-        self._content_key_info = KeyInfo.from_material_description(
+        self._content_key_info = KeyInfo.from_material_description(  # pylint: disable=attribute-defined-outside-init
             material_description=self._material_description,
             description_key=MaterialDescriptionKeys.CONTENT_ENCRYPTION_ALGORITHM.value,
             default_algorithm=_DEFAULT_CONTENT_ENCRYPTION_ALGORITHM,
             default_key_length=_DEFAULT_CONTENT_KEY_LENGTH
         )
-        self._signing_key_info = KeyInfo.from_material_description(
+        self._signing_key_info = KeyInfo.from_material_description(  # pylint: disable=attribute-defined-outside-init
             material_description=self._material_description,
             description_key=MaterialDescriptionKeys.ITEM_SIGNATURE_ALGORITHM.value,
             default_algorithm=_DEFAULT_SIGNING_ALGORITHM,
             default_key_length=_DEFAULT_SIGNING_KEY_LENGTH
         )
-        self._regional_clients = {}
+        self._regional_clients = {}  # type: Dict[Text, botocore.client.BaseClient]  # noqa pylint: disable=attribute-defined-outside-init
 
     def _add_regional_client(self, region_name):
         # type: (Text) -> None
@@ -181,6 +192,7 @@ def _client(self, key_id):
 
     def _select_key_id(self, encryption_context):
         # type: (EncryptionContext) -> Text
+        # pylint: disable=unused-argument
         """Select the desired key id.
 
         .. note::
@@ -197,6 +209,8 @@ def _select_key_id(self, encryption_context):
         return self._key_id
 
     def _validate_key_id(self, key_id, encryption_context):
+        # type: (EncryptionContext) -> None
+        # pylint: disable=unused-argument,no-self-use
         """Validate the selected key id.
 
         .. note::
diff --git a/src/dynamodb_encryption_sdk/material_providers/static.py b/src/dynamodb_encryption_sdk/material_providers/static.py
index 88d032dd..3c1ab28a 100644
--- a/src/dynamodb_encryption_sdk/material_providers/static.py
+++ b/src/dynamodb_encryption_sdk/material_providers/static.py
@@ -13,9 +13,9 @@
 """Cryptographic materials provider for use with pre-configured encryption and decryption materials."""
 import attr
 
-from . import CryptographicMaterialsProvider
 from dynamodb_encryption_sdk.materials import DecryptionMaterials, EncryptionMaterials
-from dynamodb_encryption_sdk.structures import EncryptionContext
+from dynamodb_encryption_sdk.structures import EncryptionContext  # noqa pylint: disable=unused-import
+from . import CryptographicMaterialsProvider
 
 __all__ = ('StaticCryptographicMaterialsProvider',)
 
@@ -29,6 +29,7 @@ class StaticCryptographicMaterialsProvider(CryptographicMaterialsProvider):
     :param encryption_materials: Encryption materials to provide (optional)
     :type encryption_materials: dynamodb_encryption_sdk.materials.EncryptionMaterials
     """
+
     _decryption_materials = attr.ib(
         validator=attr.validators.optional(attr.validators.instance_of(DecryptionMaterials)),
         default=None
@@ -47,7 +48,7 @@ def decryption_materials(self, encryption_context):
         :raises AttributeError: if no decryption materials are available
         """
         if self._decryption_materials is None:
-            super(StaticCryptographicMaterialsProvider, self).decryption_materials(encryption_context)
+            return super(StaticCryptographicMaterialsProvider, self).decryption_materials(encryption_context)
 
         return self._decryption_materials
 
@@ -60,6 +61,6 @@ def encryption_materials(self, encryption_context):
         :raises AttributeError: if no encryption materials are available
         """
         if self._encryption_materials is None:
-            super(StaticCryptographicMaterialsProvider, self).encryption_materials(encryption_context)
+            return super(StaticCryptographicMaterialsProvider, self).encryption_materials(encryption_context)
 
         return self._encryption_materials
diff --git a/src/dynamodb_encryption_sdk/material_providers/wrapped.py b/src/dynamodb_encryption_sdk/material_providers/wrapped.py
index 91ce7368..96c3c025 100644
--- a/src/dynamodb_encryption_sdk/material_providers/wrapped.py
+++ b/src/dynamodb_encryption_sdk/material_providers/wrapped.py
@@ -13,11 +13,11 @@
 """Cryptographic materials provider to use ephemeral content encryption keys wrapped by delegated keys."""
 import attr
 
-from . import CryptographicMaterialsProvider
 from dynamodb_encryption_sdk.delegated_keys import DelegatedKey
 from dynamodb_encryption_sdk.exceptions import UnwrappingError, WrappingError
 from dynamodb_encryption_sdk.materials.wrapped import WrappedCryptographicMaterials
-from dynamodb_encryption_sdk.structures import EncryptionContext
+from dynamodb_encryption_sdk.structures import EncryptionContext  # noqa pylint: disable=unused-import
+from . import CryptographicMaterialsProvider
 
 __all__ = ('WrappedCryptographicMaterialsProvider',)
 
@@ -43,6 +43,7 @@ class WrappedCryptographicMaterialsProvider(CryptographicMaterialsProvider):
         ``unwrapping_key`` must be provided if providing decryption materials or loading
         materials from material description
     """
+
     _signing_key = attr.ib(validator=attr.validators.instance_of(DelegatedKey))
     _wrapping_key = attr.ib(
         validator=attr.validators.optional(attr.validators.instance_of(DelegatedKey)),
@@ -54,6 +55,7 @@ class WrappedCryptographicMaterialsProvider(CryptographicMaterialsProvider):
     )
 
     def _build_materials(self, encryption_context):
+        # type: (EncryptionContext) -> WrappedCryptographicMaterials
         """Construct
 
         :param encryption_context: Encryption context for request
diff --git a/src/dynamodb_encryption_sdk/materials/__init__.py b/src/dynamodb_encryption_sdk/materials/__init__.py
index 50edf40a..a1c73e82 100644
--- a/src/dynamodb_encryption_sdk/materials/__init__.py
+++ b/src/dynamodb_encryption_sdk/materials/__init__.py
@@ -10,18 +10,18 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
-""""""
+"""Cryptographic materials are containers that provide delegated keys for cryptographic operations."""
 import abc
 
 try:  # Python 3.5.0 and 3.5.1 have incompatible typing modules
-    from mypy_extensions import NoReturn
+    from mypy_extensions import NoReturn  # noqa pylint: disable=unused-import
 except ImportError:  # pragma: no cover
     # We only actually need these imports when running the mypy checks
     pass
 
 import six
 
-from dynamodb_encryption_sdk.delegated_keys import DelegatedKey
+from dynamodb_encryption_sdk.delegated_keys import DelegatedKey  # noqa pylint: disable=unused-import
 
 __all__ = ('CryptographicMaterials', 'EncryptionMaterials', 'DecryptionMaterials')
 
@@ -82,18 +82,20 @@ class EncryptionMaterials(CryptographicMaterials):
     @property
     def decryption_key(self):
         # type: () -> NoReturn
-        """
+        """Encryption materials do not provide decryption keys.
+
         :raises NotImplementedError: because encryption materials do not contain decryption keys
         """
-        raise NotImplementedError('EncryptionMaterials do not provide decryption keys.')
+        raise NotImplementedError('Encryption materials do not provide decryption keys.')
 
     @property
     def verification_key(self):
         # type: () -> NoReturn
-        """
+        """Encryption materials do not provide verification keys.
+
         :raises NotImplementedError: because encryption materials do not contain verification keys
         """
-        raise NotImplementedError('EncryptionMaterials do not provide verification keys.')
+        raise NotImplementedError('Encryption materials do not provide verification keys.')
 
 
 class DecryptionMaterials(CryptographicMaterials):
@@ -102,15 +104,17 @@ class DecryptionMaterials(CryptographicMaterials):
     @property
     def encryption_key(self):
         # type: () -> NoReturn
-        """
+        """Decryption materials do not provide encryption keys.
+
         :raises NotImplementedError: because decryption materials do not contain encryption keys
         """
-        raise NotImplementedError('EncryptionMaterials do not provide encryption keys.')
+        raise NotImplementedError('Decryption materials do not provide encryption keys.')
 
     @property
     def signing_key(self):
         # type: () -> NoReturn
-        """
+        """Decryption materials do not provide signing keys.
+
         :raises NotImplementedError: because decryption materials do not contain signing keys
         """
-        raise NotImplementedError('EncryptionMaterials do not provide signing keys.')
+        raise NotImplementedError('Decryption materials do not provide signing keys.')
diff --git a/src/dynamodb_encryption_sdk/materials/raw.py b/src/dynamodb_encryption_sdk/materials/raw.py
index e72adcdd..f8aea5b4 100644
--- a/src/dynamodb_encryption_sdk/materials/raw.py
+++ b/src/dynamodb_encryption_sdk/materials/raw.py
@@ -36,6 +36,7 @@
 
 @attr.s
 class RawEncryptionMaterials(EncryptionMaterials):
+    # inheritance confuses pylint: disable=abstract-method
     """Encryption materials for use directly with delegated keys.
 
     .. note::
@@ -48,6 +49,7 @@ class RawEncryptionMaterials(EncryptionMaterials):
     :type encryption_key: dynamodb_encryption_sdk.delegated_keys.DelegatedKey
     :param dict material_description: Material description to use with these cryptographic materials
     """
+
     _signing_key = attr.ib(validator=attr.validators.instance_of(DelegatedKey))
     _encryption_key = attr.ib(validator=attr.validators.instance_of(DelegatedKey))
     _material_description = attr.ib(
@@ -96,6 +98,7 @@ def encryption_key(self):
 
 @attr.s
 class RawDecryptionMaterials(DecryptionMaterials):
+    # inheritance confuses pylint: disable=abstract-method
     """Encryption materials for use directly with delegated keys.
 
     .. note::
@@ -108,6 +111,7 @@ class RawDecryptionMaterials(DecryptionMaterials):
     :type decryption_key: dynamodb_encryption_sdk.delegated_keys.DelegatedKey
     :param dict material_description: Material description to use with these cryptographic materials
     """
+
     _verification_key = attr.ib(validator=attr.validators.instance_of(DelegatedKey))
     _decryption_key = attr.ib(validator=attr.validators.instance_of(DelegatedKey))
     _material_description = attr.ib(
diff --git a/src/dynamodb_encryption_sdk/materials/wrapped.py b/src/dynamodb_encryption_sdk/materials/wrapped.py
index 22f70800..070d47f9 100644
--- a/src/dynamodb_encryption_sdk/materials/wrapped.py
+++ b/src/dynamodb_encryption_sdk/materials/wrapped.py
@@ -12,6 +12,7 @@
 # language governing permissions and limitations under the License.
 """Cryptographic materials to use ephemeral content encryption keys wrapped by delegated keys."""
 from __future__ import division
+
 import base64
 import copy
 
@@ -26,7 +27,7 @@
 from dynamodb_encryption_sdk.internal.validators import dictionary_validator
 from dynamodb_encryption_sdk.materials import CryptographicMaterials
 
-__all__ = ('WrappedRawCryptographicMaterials',)
+__all__ = ('WrappedCryptographicMaterials',)
 _DEFAULT_CONTENT_ENCRYPTION_ALGORITHM = 'AES/256'
 _WRAPPING_TRANSFORMATION = {
     'AES': 'AESWrap',
@@ -57,6 +58,7 @@ class WrappedCryptographicMaterials(CryptographicMaterials):
 
     :param dict material_description: Material description to use with these cryptographic materials
     """
+
     _signing_key = attr.ib(validator=attr.validators.instance_of(DelegatedKey))
     _wrapping_key = attr.ib(
         validator=attr.validators.optional(attr.validators.instance_of(DelegatedKey)),
@@ -74,17 +76,18 @@ class WrappedCryptographicMaterials(CryptographicMaterials):
 
     def __attrs_post_init__(self):
         """Prepare the content key."""
-        self._content_key_algorithm = self.material_description.get(
+        self._content_key_algorithm = self.material_description.get(  # pylint: disable=attribute-defined-outside-init
             MaterialDescriptionKeys.CONTENT_ENCRYPTION_ALGORITHM.value,
             _DEFAULT_CONTENT_ENCRYPTION_ALGORITHM
         )
 
         if MaterialDescriptionKeys.WRAPPED_DATA_KEY.value in self.material_description:
-            self._content_key = self._content_key_from_material_description()
+            self._content_key = self._content_key_from_material_description()  # noqa pylint: disable=attribute-defined-outside-init
         else:
-            self._content_key, self._material_description = self._generate_content_key()
+            self._content_key, self._material_description = self._generate_content_key()  # noqa pylint: disable=attribute-defined-outside-init
 
-    def _wrapping_transformation(self, algorithm):
+    @staticmethod
+    def _wrapping_transformation(algorithm):
         """Convert the specified algorithm name to the desired wrapping algorithm transformation.
 
         :param str algorithm: Algorithm name
diff --git a/src/dynamodb_encryption_sdk/structures.py b/src/dynamodb_encryption_sdk/structures.py
index 979095f4..b1d4f6cc 100644
--- a/src/dynamodb_encryption_sdk/structures.py
+++ b/src/dynamodb_encryption_sdk/structures.py
@@ -10,10 +10,10 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
-""""""
-import attr
+"""Common structures used by the DynamoDB Encryption Client."""
 import copy
 
+import attr
 import six
 
 from dynamodb_encryption_sdk.exceptions import InvalidArgumentError
@@ -24,7 +24,7 @@
 __all__ = ('EncryptionContext', 'AttributeActions', 'TableIndex', 'TableInfo')
 
 
-def _validate_attribute_values_are_ddb_items(instance, attribute, value):
+def _validate_attribute_values_are_ddb_items(instance, attribute, value):  # pylint: disable=unused-argument
     """Validate that dictionary values in ``value`` match the structure of DynamoDB JSON
     items.
 
@@ -40,6 +40,7 @@ def _validate_attribute_values_are_ddb_items(instance, attribute, value):
 
 @attr.s
 class EncryptionContext(object):
+    # pylint: disable=too-few-public-methods
     """Additional information about an encryption request.
 
     :param str table_name: Table name
@@ -48,6 +49,7 @@ class EncryptionContext(object):
     :param dict attributes: Plaintext item attributes
     :param dict material_description: Material description to use with this request
     """
+
     table_name = attr.ib(
         validator=attr.validators.optional(attr.validators.instance_of(six.string_types)),
         default=None
@@ -82,6 +84,7 @@ class AttributeActions(object):
     :type default_action: dynamodb_encryption_sdk.identifiers.ItemAction
     :param dict attribute_actions: Dictionary mapping attribute names to specific actions
     """
+
     default_action = attr.ib(
         validator=attr.validators.instance_of(ItemAction),
         default=ItemAction.ENCRYPT_AND_SIGN
@@ -101,7 +104,8 @@ def __attrs_post_init__(self):
         # Enums are not hashable, but their names are unique
         _unique_actions = set([self.default_action.name])
         _unique_actions.update(set([action.name for action in self.attribute_actions.values()]))
-        self.take_no_actions = _unique_actions == set([ItemAction.DO_NOTHING.name])
+        no_actions = _unique_actions == set([ItemAction.DO_NOTHING.name])
+        self.take_no_actions = no_actions  # attrs confuses pylint: disable=attribute-defined-outside-init
 
     def action(self, attribute_name):
         # (text) -> ItemAction
@@ -159,11 +163,13 @@ def __add__(self, other):
 
 @attr.s
 class TableIndex(object):
+    # pylint: disable=too-few-public-methods
     """Describes a table index.
 
     :param str partition: Name of the partition attribute
     :param str sort: Name of the sort attribute (optional)
     """
+
     partition = attr.ib(validator=attr.validators.instance_of(six.string_types))
     sort = attr.ib(
         validator=attr.validators.optional(attr.validators.instance_of(six.string_types)),
@@ -172,7 +178,7 @@ class TableIndex(object):
 
     def __attrs_post_init__(self):
         """Set the ``attributes`` attribute for ease of access later."""
-        self.attributes = set([self.partition])
+        self.attributes = set([self.partition])  # attrs confuses pylint: disable=attribute-defined-outside-init
         if self.sort is not None:
             self.attributes.add(self.sort)
 
@@ -204,7 +210,7 @@ def from_key_schema(cls, key_schema):
 
 @attr.s
 class TableInfo(object):
-    """Description of a DynamoDB table.
+    """Describes a DynamoDB table.
 
     :param str name: Table name
     :param bool all_encrypting_secondary_indexes: Should we allow secondary index attributes to be encrypted?
@@ -213,6 +219,7 @@ class TableInfo(object):
     :param secondary_indexes: Set of TableIndex objects describing any secondary indexes
     :type secondary_indexes: set of dynamodb_encryption_sdk.structures.TableIndex
     """
+
     name = attr.ib(validator=attr.validators.instance_of(six.string_types))
     _primary_index = attr.ib(
         validator=attr.validators.optional(attr.validators.instance_of(TableIndex)),
diff --git a/src/pylintrc b/src/pylintrc
index de56ef0f..43812946 100644
--- a/src/pylintrc
+++ b/src/pylintrc
@@ -1,6 +1,10 @@
 [BASIC]
 # Allow function names up to 50 characters
 function-rgx = [a-z_][a-z0-9_]{2,50}$
+# Whitelist argument names: iv
+argument-rgx = ([a-z_][a-z0-9_]{2,30}$)|(^iv$)
+# Whitelist variable names: iv
+variable-rgx = ([a-z_][a-z0-9_]{2,30}$)|(^iv$)
 
 [DESIGN]
 max-args = 10