Repository: wtforms/wtforms (python)

Issue:
Code breaks on latest release
### Actual Behavior

```python

from wtforms.widgets import HTMLString
ImportError: cannot import name 'HTMLString'

```
### Environment
* Python version: 3.6.8
* wtforms version: 2.3.0


Pull request description:
Ascending compatibility with markupsafe
Brings back `HTMLString` and `escape_html` as aliases for `markupsafe.Markup` and `markupsafe.escape`. When used, those functions raise a `DeprecationWarning`. This lets a bit of time for users to do the switch.

Fixes #581

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 2564e64..ffb2f7b 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -8,6 +8,10 @@ Unreleased
 
 -   Fixed a bug when :class:`~wtforms.fields.core.SelectField` choices
     is ``None``. :issue:`572, 585`
+-   Restored :class:`~widgets.core.HTMLString` and
+    :func:`~widgets.core.escape_html` as aliases for MarkupSafe
+    functions. Their use shows a ``DeprecationWarning``. :issue:`581`,
+    :pr:`583`
 
 
 Version 2.3.0
diff --git a/wtforms/widgets/core.py b/wtforms/widgets/core.py
index 0ef4dd2..bf1fdb4 100644
--- a/wtforms/widgets/core.py
+++ b/wtforms/widgets/core.py
@@ -3,14 +3,36 @@ from __future__ import unicode_literals
 from markupsafe import escape, Markup
 
 from wtforms.compat import text_type, iteritems
+import warnings
+
 
 __all__ = (
     'CheckboxInput', 'FileInput', 'HiddenInput', 'ListWidget', 'PasswordInput',
     'RadioInput', 'Select', 'SubmitInput', 'TableWidget', 'TextArea',
-    'TextInput', 'Option'
+    'TextInput', 'Option', 'HTMLString', 'escape_html',
 )
 
 
+def HTMLString(*args, **kwargs):
+    warnings.warn(
+        "'HTMLString' will be removed in WTForms 3.0. Use"
+        " 'markupsafe.Markup' instead.",
+        DeprecationWarning,
+        stacklevel=2,
+    )
+    return Markup(*args, **kwargs)
+
+
+def escape_html(*args, **kwargs):
+    warnings.warn(
+        "'escape_html' will be removed in WTForms 3.0. Use"
+        " 'markupsafe.escape' instead.",
+        DeprecationWarning,
+        stacklevel=2,
+    )
+    return escape(*args, **kwargs)
+
+
 def html_params(**kwargs):
     """
     Generate HTML attribute syntax from inputted keyword arguments.


Test patch:
diff --git a/tests/deprecations.py b/tests/deprecations.py
new file mode 100644
index 0000000..b4c43d0
--- /dev/null
+++ b/tests/deprecations.py
@@ -0,0 +1,21 @@
+import warnings
+import unittest
+
+from wtforms.widgets import HTMLString, escape_html
+import markupsafe
+
+
+class DeprecationTest(unittest.TestCase):
+    def test_htmlstring(self):
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter("always")
+            self.assertEqual(markupsafe.Markup("foobar"), HTMLString("foobar"))
+            self.assertEqual(len(w), 1)
+            assert issubclass(w[-1].category, DeprecationWarning)
+
+    def test_escape(self):
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter("always")
+            self.assertEqual(markupsafe.escape("foobar"), escape_html("foobar"))
+            self.assertEqual(len(w), 1)
+            assert issubclass(w[-1].category, DeprecationWarning)
diff --git a/tests/runtests.py b/tests/runtests.py
index 78dfb1e..c877368 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -3,7 +3,7 @@ import os
 import sys
 from unittest import defaultTestLoader, TextTestRunner, TestSuite
 
-TESTS = ('form', 'fields', 'validators', 'widgets', 'webob_wrapper', 'csrf', 'ext_csrf', 'i18n')
+TESTS = ('form', 'fields', 'validators', 'widgets', 'webob_wrapper', 'csrf', 'ext_csrf', 'i18n', 'deprecations')
 
 OPTIONAL_TESTS = ('ext_django.tests', 'ext_sqlalchemy', 'ext_dateutil', 'locale_babel')