From b2b763448f726ee952743596e9a34fcb154bdb12 Mon Sep 17 00:00:00 2001
From: Gregor MacGregor <Timothy.J.Clifford@gmail.com>
Date: Fri, 6 Sep 2013 13:24:52 -0500
Subject: [PATCH] Fixed #20841 -- Added messages to NotImplementedErrors

Thanks joseph at vertstudios.com for the suggestion.
---
 django/contrib/admin/filters.py               | 12 ++---
 django/contrib/auth/hashers.py                |  6 +--
 django/contrib/auth/models.py                 | 12 ++---
 .../contrib/comments/templatetags/comments.py |  3 +-
 django/contrib/gis/db/backends/base.py        | 10 ++---
 django/contrib/messages/storage/base.py       |  4 +-
 django/contrib/sessions/backends/base.py      | 12 ++---
 django/contrib/staticfiles/finders.py         |  4 +-
 django/core/cache/backends/base.py            | 10 ++---
 django/core/files/storage.py                  | 16 +++----
 django/core/files/uploadhandler.py            |  4 +-
 django/core/mail/backends/base.py             |  2 +-
 django/core/management/base.py                |  8 ++--
 django/core/serializers/base.py               | 12 ++---
 django/db/backends/__init__.py                | 44 +++++++++----------
 django/db/backends/schema.py                  |  2 +-
 django/db/migrations/operations/base.py       |  6 +--
 django/forms/widgets.py                       |  2 +-
 django/template/base.py                       |  4 +-
 django/template/loader.py                     |  2 +-
 django/utils/archive.py                       |  4 +-
 django/utils/dateformat.py                    |  2 +-
 django/utils/feedgenerator.py                 |  2 +-
 django/utils/functional.py                    |  2 +-
 django/utils/regex_helper.py                  |  2 +-
 tests/mail/tests.py                           |  4 +-
 26 files changed, 96 insertions(+), 95 deletions(-)

diff --git a/django/contrib/admin/filters.py b/django/contrib/admin/filters.py
index 2a9fb2a8f9..7bba3a797f 100644
--- a/django/contrib/admin/filters.py
+++ b/django/contrib/admin/filters.py
@@ -33,26 +33,26 @@ class ListFilter(object):
         """
         Returns True if some choices would be output for this filter.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of ListFilter must provide a has_output() method')
 
     def choices(self, cl):
         """
         Returns choices ready to be output in the template.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of ListFilter must provide a choices() method')
 
     def queryset(self, request, queryset):
         """
         Returns the filtered queryset.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of ListFilter must provide a queryset() method')
 
     def expected_parameters(self):
         """
         Returns the list of parameter names that are expected from the
         request's query string and that will be used by this filter.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of ListFilter must provide an expected_parameters() method')
 
 
 class SimpleListFilter(ListFilter):
@@ -89,7 +89,9 @@ class SimpleListFilter(ListFilter):
         """
         Must be overridden to return a list of tuples (value, verbose value)
         """
-        raise NotImplementedError
+        raise NotImplementedError(
+            'The SimpleListFilter.lookups() method must be overridden to '
+            'return a list of tuples (value, verbose value)')
 
     def expected_parameters(self):
         return [self.parameter_name]
diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py
index 41e3deff67..3b598959bc 100644
--- a/django/contrib/auth/hashers.py
+++ b/django/contrib/auth/hashers.py
@@ -192,7 +192,7 @@ class BasePasswordHasher(object):
         """
         Checks if the given password is correct
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BasePasswordHasher must provide a verify() method')
 
     def encode(self, password, salt):
         """
@@ -201,7 +201,7 @@ class BasePasswordHasher(object):
         The result is normally formatted as "algorithm$salt$hash" and
         must be fewer than 128 characters.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BasePasswordHasher must provide an encode() method')
 
     def safe_summary(self, encoded):
         """
@@ -210,7 +210,7 @@ class BasePasswordHasher(object):
         The result is a dictionary and will be used where the password field
         must be displayed to construct a safe representation of the password.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BasePasswordHasher must provide a safe_summary() method')
 
 
 class PBKDF2PasswordHasher(BasePasswordHasher):
diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py
index 4c88e7d1ba..8db304f620 100644
--- a/django/contrib/auth/models.py
+++ b/django/contrib/auth/models.py
@@ -245,10 +245,10 @@ class AbstractBaseUser(models.Model):
         return is_password_usable(self.password)
 
     def get_full_name(self):
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_full_name() method')
 
     def get_short_name(self):
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_short_name() method.')
 
 
 # A few helper functions for common logic between User and AnonymousUser.
@@ -441,16 +441,16 @@ class AnonymousUser(object):
         return 1  # instances always return the same hash value
 
     def save(self):
-        raise NotImplementedError
+        raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")
 
     def delete(self):
-        raise NotImplementedError
+        raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")
 
     def set_password(self, raw_password):
-        raise NotImplementedError
+        raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")
 
     def check_password(self, raw_password):
-        raise NotImplementedError
+        raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")
 
     def _get_groups(self):
         return self._groups
diff --git a/django/contrib/comments/templatetags/comments.py b/django/contrib/comments/templatetags/comments.py
index 2b2cea5f20..44ac3d588b 100644
--- a/django/contrib/comments/templatetags/comments.py
+++ b/django/contrib/comments/templatetags/comments.py
@@ -112,7 +112,7 @@ class BaseCommentNode(six.with_metaclass(RenameBaseCommentNodeMethods, template.
 
     def get_context_value_from_queryset(self, context, qs):
         """Subclasses should override this."""
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseCommentNode must provide a get_context_value_from_queryset() method')
 
 class CommentListNode(BaseCommentNode):
     """Insert a list of comments into the context."""
@@ -338,4 +338,3 @@ def get_comment_permalink(comment, anchor_pattern=None):
     if anchor_pattern:
         return comment.get_absolute_url(anchor_pattern)
     return comment.get_absolute_url()
-
diff --git a/django/contrib/gis/db/backends/base.py b/django/contrib/gis/db/backends/base.py
index 7db7ce51ba..ae2c920923 100644
--- a/django/contrib/gis/db/backends/base.py
+++ b/django/contrib/gis/db/backends/base.py
@@ -101,7 +101,7 @@ class BaseSpatialOperations(object):
         Returns the database column type for the geometry field on
         the spatial backend.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseSpatialOperations must provide a geo_db_type() method')
 
     def get_distance(self, f, value, lookup_type):
         """
@@ -117,7 +117,7 @@ class BaseSpatialOperations(object):
         stored procedure call to the transformation function of the spatial
         backend.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseSpatialOperations must provide a geo_db_placeholder() method')
 
     def get_expression_column(self, evaluator):
         """
@@ -134,14 +134,14 @@ class BaseSpatialOperations(object):
         raise NotImplementedError('Aggregate support not implemented for this spatial backend.')
 
     def spatial_lookup_sql(self, lvalue, lookup_type, value, field):
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseSpatialOperations must a provide spatial_lookup_sql() method')
 
     # Routines for getting the OGC-compliant models.
     def geometry_columns(self):
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseSpatialOperations must a provide geometry_columns() method')
 
     def spatial_ref_sys(self):
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseSpatialOperations must a provide spatial_ref_sys() method')
 
 @python_2_unicode_compatible
 class SpatialRefSysMixin(object):
diff --git a/django/contrib/messages/storage/base.py b/django/contrib/messages/storage/base.py
index 7fe8a077ed..c021fbd2ae 100644
--- a/django/contrib/messages/storage/base.py
+++ b/django/contrib/messages/storage/base.py
@@ -105,7 +105,7 @@ class BaseStorage(object):
         just containing no messages) then ``None`` should be returned in
         place of ``messages``.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BaseStorage must provide a _get() method')
 
     def _store(self, messages, response, *args, **kwargs):
         """
@@ -116,7 +116,7 @@ class BaseStorage(object):
 
         **This method must be implemented by a subclass.**
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BaseStorage must provide a _store() method')
 
     def _prepare_messages(self, messages):
         """
diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py
index 7f5e958a60..08e79530f4 100644
--- a/django/contrib/sessions/backends/base.py
+++ b/django/contrib/sessions/backends/base.py
@@ -284,7 +284,7 @@ class SessionBase(object):
         """
         Returns True if the given session_key already exists.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of SessionBase must provide an exists() method')
 
     def create(self):
         """
@@ -292,7 +292,7 @@ class SessionBase(object):
         a unique key and will have saved the result once (with empty data)
         before the method returns.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of SessionBase must provide a create() method')
 
     def save(self, must_create=False):
         """
@@ -300,20 +300,20 @@ class SessionBase(object):
         is created (otherwise a CreateError exception is raised). Otherwise,
         save() can update an existing object with the same key.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of SessionBase must provide a save() method')
 
     def delete(self, session_key=None):
         """
         Deletes the session data under this key. If the key is None, the
         current session key value is used.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of SessionBase must provide a delete() method')
 
     def load(self):
         """
         Loads the session data and returns a dictionary.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of SessionBase must provide a load() method')
 
     @classmethod
     def clear_expired(cls):
@@ -324,4 +324,4 @@ class SessionBase(object):
         NotImplementedError. If it isn't necessary, because the backend has
         a built-in expiration mechanism, it should be a no-op.
         """
-        raise NotImplementedError
+        raise NotImplementedError('This backend does not support clear_expired().')
diff --git a/django/contrib/staticfiles/finders.py b/django/contrib/staticfiles/finders.py
index d4efd1a8d8..3d93d2a447 100644
--- a/django/contrib/staticfiles/finders.py
+++ b/django/contrib/staticfiles/finders.py
@@ -28,7 +28,7 @@ class BaseFinder(object):
         the first found file path will be returned; if set
         to ``True`` a list of all found files paths is returned.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BaseFinder must provide a find() method')
 
     def list(self, ignore_patterns):
         """
@@ -36,7 +36,7 @@ class BaseFinder(object):
         a two item iterable consisting of the relative path and storage
         instance.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BaseFinder must provide a list() method')
 
 
 class FileSystemFinder(BaseFinder):
diff --git a/django/core/cache/backends/base.py b/django/core/cache/backends/base.py
index deb98e7714..db76192354 100644
--- a/django/core/cache/backends/base.py
+++ b/django/core/cache/backends/base.py
@@ -96,27 +96,27 @@ class BaseCache(object):
 
         Returns True if the value was stored, False otherwise.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseCache must provide an add() method')
 
     def get(self, key, default=None, version=None):
         """
         Fetch a given key from the cache. If the key does not exist, return
         default, which itself defaults to None.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseCache must provide a get() method')
 
     def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None):
         """
         Set a value in the cache. If timeout is given, that timeout will be
         used for the key; otherwise the default cache timeout will be used.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseCache must provide a set() method')
 
     def delete(self, key, version=None):
         """
         Delete a key from the cache, failing silently.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseCache must provide a delete() method')
 
     def get_many(self, keys, version=None):
         """
@@ -190,7 +190,7 @@ class BaseCache(object):
 
     def clear(self):
         """Remove *all* values from the cache at once."""
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseCache must provide a clear() method')
 
     def validate_key(self, key):
         """
diff --git a/django/core/files/storage.py b/django/core/files/storage.py
index b9f3e01f0d..b42981cb5e 100644
--- a/django/core/files/storage.py
+++ b/django/core/files/storage.py
@@ -92,55 +92,55 @@ class Storage(object):
         """
         Deletes the specified file from the storage system.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of Storage must provide a delete() method')
 
     def exists(self, name):
         """
         Returns True if a file referened by the given name already exists in the
         storage system, or False if the name is available for a new file.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of Storage must provide a exists() method')
 
     def listdir(self, path):
         """
         Lists the contents of the specified path, returning a 2-tuple of lists;
         the first item being directories, the second item being files.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of Storage must provide a listdir() method')
 
     def size(self, name):
         """
         Returns the total size, in bytes, of the file specified by name.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of Storage must provide a size() method')
 
     def url(self, name):
         """
         Returns an absolute URL where the file's contents can be accessed
         directly by a Web browser.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of Storage must provide a url() method')
 
     def accessed_time(self, name):
         """
         Returns the last accessed time (as datetime object) of the file
         specified by name.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of Storage must provide an accessed_time() method')
 
     def created_time(self, name):
         """
         Returns the creation time (as datetime object) of the file
         specified by name.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of Storage must provide a created_time() method')
 
     def modified_time(self, name):
         """
         Returns the last modified time (as datetime object) of the file
         specified by name.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of Storage must provide a modified_time() method')
 
 class FileSystemStorage(Storage):
     """
diff --git a/django/core/files/uploadhandler.py b/django/core/files/uploadhandler.py
index 6739b26e0c..914e2c51fa 100644
--- a/django/core/files/uploadhandler.py
+++ b/django/core/files/uploadhandler.py
@@ -104,7 +104,7 @@ class FileUploadHandler(object):
         Receive data from the streamed upload parser. ``start`` is the position
         in the file of the chunk.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of FileUploadHandler must provide a recieve_data_chunk() method')
 
     def file_complete(self, file_size):
         """
@@ -113,7 +113,7 @@ class FileUploadHandler(object):
 
         Subclasses should return a valid ``UploadedFile`` object.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of FileUploadHandler must provide a file_complete() method')
 
     def upload_complete(self):
         """
diff --git a/django/core/mail/backends/base.py b/django/core/mail/backends/base.py
index 9a3092849d..2be4510363 100644
--- a/django/core/mail/backends/base.py
+++ b/django/core/mail/backends/base.py
@@ -36,4 +36,4 @@ class BaseEmailBackend(object):
         Sends one or more EmailMessage objects and returns the number of email
         messages sent.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseEmailBackend must override send_messages() method')
diff --git a/django/core/management/base.py b/django/core/management/base.py
index 9c8940e99f..1aba53dd01 100644
--- a/django/core/management/base.py
+++ b/django/core/management/base.py
@@ -325,7 +325,7 @@ class BaseCommand(object):
         this method.
 
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BaseCommand must provide a handle() method')
 
 
 class AppCommand(BaseCommand):
@@ -361,7 +361,7 @@ class AppCommand(BaseCommand):
         the command line.
 
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of AppCommand must provide a handle_app() method')
 
 
 class LabelCommand(BaseCommand):
@@ -397,7 +397,7 @@ class LabelCommand(BaseCommand):
         string as given on the command line.
 
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of LabelCommand must provide a handle_label() method')
 
 
 class NoArgsCommand(BaseCommand):
@@ -423,4 +423,4 @@ class NoArgsCommand(BaseCommand):
         Perform this command's actions.
 
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of NoArgsCommand must provide a handle_noargs() method')
diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py
index cd4f7ffb2b..da93c3154f 100644
--- a/django/core/serializers/base.py
+++ b/django/core/serializers/base.py
@@ -65,7 +65,7 @@ class Serializer(object):
         """
         Called when serializing of the queryset starts.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of Serializer must provide a start_serialization() method')
 
     def end_serialization(self):
         """
@@ -77,7 +77,7 @@ class Serializer(object):
         """
         Called when serializing of an object starts.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of Serializer must provide a start_object() method')
 
     def end_object(self, obj):
         """
@@ -89,19 +89,19 @@ class Serializer(object):
         """
         Called to handle each individual (non-relational) field on an object.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of Serializer must provide an handle_field() method')
 
     def handle_fk_field(self, obj, field):
         """
         Called to handle a ForeignKey field.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of Serializer must provide an handle_fk_field() method')
 
     def handle_m2m_field(self, obj, field):
         """
         Called to handle a ManyToManyField.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of Serializer must provide an handle_m2m_field() method')
 
     def getvalue(self):
         """
@@ -135,7 +135,7 @@ class Deserializer(six.Iterator):
 
     def __next__(self):
         """Iteration iterface -- return the next item in the stream"""
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of Deserializer must provide a __next__() method')
 
 class DeserializedObject(object):
     """
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
index 6b1489d128..d22fea5ec2 100644
--- a/django/db/backends/__init__.py
+++ b/django/db/backends/__init__.py
@@ -84,19 +84,19 @@ class BaseDatabaseWrapper(object):
 
     def get_connection_params(self):
         """Returns a dict of parameters suitable for get_new_connection."""
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a get_connection_params() method')
 
     def get_new_connection(self, conn_params):
         """Opens a connection to the database."""
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a get_new_connection() method')
 
     def init_connection_state(self):
         """Initializes the database connection settings."""
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseDatabaseWrapper may require an init_connection_state() method')
 
     def create_cursor(self):
         """Creates a cursor. Assumes that a connection is established."""
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a create_cursor() method')
 
     ##### Backend-specific methods for creating connections #####
 
@@ -262,7 +262,7 @@ class BaseDatabaseWrapper(object):
         """
         Backend-specific implementation to enable or disable autocommit.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a _set_autocommit() method')
 
     ##### Generic transaction management methods #####
 
@@ -440,7 +440,7 @@ class BaseDatabaseWrapper(object):
         Tests if the database connection is usable.
         This function may assume that self.connection is not None.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseDatabaseWrapper may require an is_usable() method')
 
     def close_if_unusable_or_obsolete(self):
         """
@@ -519,11 +519,11 @@ class BaseDatabaseWrapper(object):
         """
         Only required when autocommits_when_autocommit_is_off = True.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a _start_transaction_under_autocommit() method')
 
     def schema_editor(self, *args, **kwargs):
         "Returns a new instance of this backend's SchemaEditor"
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a schema_editor() method')
 
 
 class BaseDatabaseFeatures(object):
@@ -741,13 +741,13 @@ class BaseDatabaseOperations(object):
         Given a lookup_type of 'year', 'month' or 'day', returns the SQL that
         extracts a value from the given date field field_name.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BaseDatabaseOperations may require a date_extract_sql() method')
 
     def date_interval_sql(self, sql, connector, timedelta):
         """
         Implements the date interval functionality for expressions
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BaseDatabaseOperations may require a date_interval_sql() method')
 
     def date_trunc_sql(self, lookup_type, field_name):
         """
@@ -755,7 +755,7 @@ class BaseDatabaseOperations(object):
         truncates the given date field field_name to a date object with only
         the given specificity.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BaseDatabaseOperations may require a datetrunc_sql() method')
 
     def datetime_cast_sql(self):
         """
@@ -772,7 +772,7 @@ class BaseDatabaseOperations(object):
         'second', returns the SQL that extracts a value from the given
         datetime field field_name, and a tuple of parameters.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BaseDatabaseOperations may require a datetime_extract_sql() method')
 
     def datetime_trunc_sql(self, lookup_type, field_name, tzname):
         """
@@ -781,7 +781,7 @@ class BaseDatabaseOperations(object):
         field_name to a datetime object with only the given specificity, and
         a tuple of parameters.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BaseDatabaseOperations may require a datetime_trunk_sql() method')
 
     def deferrable_sql(self):
         """
@@ -916,7 +916,7 @@ class BaseDatabaseOperations(object):
         Returns the value to use for the LIMIT when we are wanting "LIMIT
         infinity". Returns None if the limit clause can be omitted in this case.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseDatabaseOperations may require a no_limit_value() method')
 
     def pk_default_value(self):
         """
@@ -956,7 +956,7 @@ class BaseDatabaseOperations(object):
         Returns a quoted version of the given table, index or column name. Does
         not quote the given name if it's already been quoted.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BaseDatabaseOperations may require a quote_name() method')
 
     def quote_parameter(self, value):
         """
@@ -982,7 +982,7 @@ class BaseDatabaseOperations(object):
         If the feature is not supported (or part of it is not supported), a
         NotImplementedError exception can be raised.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseDatabaseOperations may require a regex_lookup() method')
 
     def savepoint_create_sql(self, sid):
         """
@@ -1028,7 +1028,7 @@ class BaseDatabaseOperations(object):
         to tables with foreign keys pointing the tables being truncated.
         PostgreSQL requires a cascade even if these tables are empty.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BaseDatabaseOperations must provide a sql_flush() method')
 
     def sequence_reset_by_name_sql(self, style, sequences):
         """
@@ -1245,7 +1245,7 @@ class BaseDatabaseIntrospection(object):
         Returns an unsorted list of names of all tables that exist in the
         database.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_table_list() method')
 
     def django_table_names(self, only_existing=False):
         """
@@ -1322,7 +1322,7 @@ class BaseDatabaseIntrospection(object):
         Backends can override this to return a list of (column_name, referenced_table_name,
         referenced_column_name) for all key columns in given table.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_key_columns() method')
 
     def get_primary_key_column(self, cursor, table_name):
         """
@@ -1342,7 +1342,7 @@ class BaseDatabaseIntrospection(object):
 
         Only single-column indexes are introspected.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_indexes() method')
 
     def get_constraints(self, cursor, table_name):
         """
@@ -1361,7 +1361,7 @@ class BaseDatabaseIntrospection(object):
         Some backends may return special constraint names that don't exist
         if they don't name constraints of a certain type (e.g. SQLite)
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_constraints() method')
 
 
 class BaseDatabaseClient(object):
@@ -1378,7 +1378,7 @@ class BaseDatabaseClient(object):
         self.connection = connection
 
     def runshell(self):
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BaseDatabaseClient must provide a runshell() method')
 
 
 class BaseDatabaseValidation(object):
diff --git a/django/db/backends/schema.py b/django/db/backends/schema.py
index 5f0bf0a28b..bc529f47e0 100644
--- a/django/db/backends/schema.py
+++ b/django/db/backends/schema.py
@@ -148,7 +148,7 @@ class BaseDatabaseSchemaEditor(object):
         """
         Only used for backends which have requires_literal_defaults feature
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of BaseDatabaseSchemaEditor for backends which have requires_literal_defaults must provide a prepare_default() method')
 
     def effective_default(self, field):
         """
diff --git a/django/db/migrations/operations/base.py b/django/db/migrations/operations/base.py
index dcdb1ad30b..217c6ee843 100644
--- a/django/db/migrations/operations/base.py
+++ b/django/db/migrations/operations/base.py
@@ -38,14 +38,14 @@ class Operation(object):
         Takes the state from the previous migration, and mutates it
         so that it matches what this migration would perform.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of Operation must provide a state_forwards() method')
 
     def database_forwards(self, app_label, schema_editor, from_state, to_state):
         """
         Performs the mutation on the database schema in the normal
         (forwards) direction.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of Operation must provide a database_forwards() method')
 
     def database_backwards(self, app_label, schema_editor, from_state, to_state):
         """
@@ -53,7 +53,7 @@ class Operation(object):
         direction - e.g. if this were CreateModel, it would in fact
         drop the model's table.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of Operation must provide a database_backwards() method')
 
     def describe(self):
         """
diff --git a/django/forms/widgets.py b/django/forms/widgets.py
index 26f8d312e4..ea701bdcf9 100644
--- a/django/forms/widgets.py
+++ b/django/forms/widgets.py
@@ -190,7 +190,7 @@ class Widget(six.with_metaclass(MediaDefiningClass)):
         The 'value' given is not guaranteed to be valid input, so subclass
         implementations should program defensively.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of Widget must provide a render() method')
 
     def build_attrs(self, extra_attrs=None, **kwargs):
         "Helper function for building an attribute dictionary."
diff --git a/django/template/base.py b/django/template/base.py
index 1265040fc0..c6ab7790a0 100644
--- a/django/template/base.py
+++ b/django/template/base.py
@@ -99,7 +99,7 @@ class Origin(object):
         self.name = name
 
     def reload(self):
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of Origin must provide a reload() method')
 
     def __str__(self):
         return self.name
@@ -385,7 +385,7 @@ class TokenParser(object):
         """
         Overload this method to do the actual parsing and return the result.
         """
-        raise NotImplementedError()
+        raise NotImplementedError('subclasses of Tokenparser must provide a top() method')
 
     def more(self):
         """
diff --git a/django/template/loader.py b/django/template/loader.py
index 44b8f600fb..30d3271960 100644
--- a/django/template/loader.py
+++ b/django/template/loader.py
@@ -61,7 +61,7 @@ class BaseLoader(object):
         name.
 
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseLoader must provide a load_template_source() method')
 
     def reset(self):
         """
diff --git a/django/utils/archive.py b/django/utils/archive.py
index 0faf1fa781..0a95fa84a6 100644
--- a/django/utils/archive.py
+++ b/django/utils/archive.py
@@ -126,10 +126,10 @@ class BaseArchive(object):
         return True
 
     def extract(self):
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseArchive must provide an extract() method')
 
     def list(self):
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseArchive must provide a list() method')
 
 
 class TarArchive(BaseArchive):
diff --git a/django/utils/dateformat.py b/django/utils/dateformat.py
index 85eb975f84..3c235d9867 100644
--- a/django/utils/dateformat.py
+++ b/django/utils/dateformat.py
@@ -65,7 +65,7 @@ class TimeFormat(Formatter):
 
     def B(self):
         "Swatch Internet time"
-        raise NotImplementedError
+        raise NotImplementedError('may be implemented in a future release')
 
     def e(self):
         """
diff --git a/django/utils/feedgenerator.py b/django/utils/feedgenerator.py
index e632845a84..f7fec5e7f9 100644
--- a/django/utils/feedgenerator.py
+++ b/django/utils/feedgenerator.py
@@ -177,7 +177,7 @@ class SyndicationFeed(object):
         Outputs the feed in the given encoding to outfile, which is a file-like
         object. Subclasses should override this.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of SyndicationFeed must provide a write() method')
 
     def writeString(self, encoding):
         """
diff --git a/django/utils/functional.py b/django/utils/functional.py
index bfd59e5340..65ec4b53c4 100644
--- a/django/utils/functional.py
+++ b/django/utils/functional.py
@@ -257,7 +257,7 @@ class LazyObject(object):
         """
         Must be implemented by subclasses to initialise the wrapped object.
         """
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of LazyObject must provide a _setup() method')
 
     # Introspection support
     __dir__ = new_method_proxy(dir)
diff --git a/django/utils/regex_helper.py b/django/utils/regex_helper.py
index 7b40d141de..449bb8da42 100644
--- a/django/utils/regex_helper.py
+++ b/django/utils/regex_helper.py
@@ -92,7 +92,7 @@ def normalize(pattern):
                 result.append(".")
             elif ch == '|':
                 # FIXME: One day we'll should do this, but not in 1.0.
-                raise NotImplementedError
+                raise NotImplementedError('Awaiting Implementation')
             elif ch == "^":
                 pass
             elif ch == '$':
diff --git a/tests/mail/tests.py b/tests/mail/tests.py
index bb57ca37ff..7bef1a3bb3 100644
--- a/tests/mail/tests.py
+++ b/tests/mail/tests.py
@@ -443,10 +443,10 @@ class BaseEmailBackendTests(HeadersCheckMixin, object):
             self.assertEqual(first[:len(second)], second, "First string doesn't start with the second.")
 
     def get_mailbox_content(self):
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseEmailBackendTests must provide a get_mailbox_content() method')
 
     def flush_mailbox(self):
-        raise NotImplementedError
+        raise NotImplementedError('subclasses of BaseEmailBackendTests may require a flush_mailbox() method')
 
     def get_the_message(self):
         mailbox = self.get_mailbox_content()