We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Error processing 'rule_notify' on 'QuotaMailPlugin': 'int' object has no attribute 'lower'
https://beta.getsentry.com/sentry/sentry/group/56738068/

```
AttributeError: 'int' object has no attribute 'lower'
(11 additional frame(s) were not displayed)
...
  File "sentry/interfaces/user.py", line 99, in to_email_html
    return render_to_string('sentry/partial/interfaces/user_email.html', context)
  File "sentry/web/helpers.py", line 122, in render_to_string
    return loader.render_to_string(template, context)
  File "templatetag_sugar/node.py", line 19, in render
    return self.function(context, *args, **kwargs)
  File "sentry/templatetags/sentry_helpers.py", line 314, in gravatar_url
    return get_gravatar_url(email, size, default)
  File "sentry/utils/avatar.py", line 18, in get_gravatar_url
    hashlib.md5(email.lower()).hexdigest())

Stacktrace (most recent call last):

Error processing 'rule_notify' on 'QuotaMailPlugin': 'int' object has no attribute 'lower'

File "sentry/partial/interfaces/user_email.html", line 7

  <tr>
    {% if user_email %}
      <td style="width:84px;padding-top:10px;vertical-align:top">
        <img src="{% gravatar_url user_email size 64 %}">
      </td>
    {% endif %}
    <td>
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/sentry/interfaces/user.py`
Content:
```
1 """
2 sentry.interfaces.user
3 ~~~~~~~~~~~~~~~~~~~~~~
4 
5 :copyright: (c) 2010-2014 by the Sentry Team, see AUTHORS for more details.
6 :license: BSD, see LICENSE for more details.
7 """
8 from __future__ import absolute_import
9 
10 __all__ = ('User',)
11 
12 from sentry.interfaces.base import Interface
13 from sentry.utils.safe import trim, trim_dict
14 from sentry.web.helpers import render_to_string
15 from ipaddr import IPAddress
16 
17 
18 def validate_ip(value, required=True):
19     if not required and not value:
20         return
21 
22     # will raise a ValueError
23     IPAddress(value)
24     return value
25 
26 
27 class User(Interface):
28     """
29     An interface which describes the authenticated User for a request.
30 
31     You should provide **at least** either an `id` (a unique identifier for
32     an authenticated user) or `ip_address` (their IP address).
33 
34     All other attributes are optional.
35 
36     >>> {
37     >>>     "id": "unique_id",
38     >>>     "username": "my_user",
39     >>>     "email": "foo@example.com"
40     >>>     "ip_address": "127.0.0.1",
41     >>>     "optional": "value"
42     >>> }
43     """
44     @classmethod
45     def to_python(cls, data):
46         data = data.copy()
47 
48         extra_data = data.pop('data', data)
49         if not isinstance(extra_data, dict):
50             extra_data = {}
51 
52         kwargs = {
53             'id': trim(data.pop('id', None), 128),
54             'email': trim(data.pop('email', None), 128),
55             'username': trim(data.pop('username', None), 128),
56             'ip_address': validate_ip(data.pop('ip_address', None), False),
57         }
58 
59         kwargs['data'] = trim_dict(extra_data)
60         return cls(**kwargs)
61 
62     def get_api_context(self, is_public=False):
63         return {
64             'id': self.id,
65             'email': self.email,
66             'username': self.username,
67             'ipAddress': self.ip_address,
68             'data': self.data,
69         }
70 
71     def get_path(self):
72         return 'sentry.interfaces.User'
73 
74     def get_hash(self):
75         return []
76 
77     def to_email_html(self, event, **kwargs):
78         context = {
79             'user_id': self.id,
80             'user_email': self.email,
81             'user_username': self.username,
82             'user_ip_address': self.ip_address,
83             'user_data': self.data,
84         }
85         return render_to_string('sentry/partial/interfaces/user_email.html', context)
86
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/sentry/interfaces/user.py b/src/sentry/interfaces/user.py
--- a/src/sentry/interfaces/user.py
+++ b/src/sentry/interfaces/user.py
@@ -15,6 +15,19 @@
 from ipaddr import IPAddress
 
 
+def validate_email(value, required=True):
+    if not required and not value:
+        return
+
+    if not isinstance(value, basestring):
+        raise TypeError('object of type %r is not an email address' % type(value).__name__)
+
+    # safe to assume an email address at least has a @ in it.
+    if '@' not in value:
+        raise ValueError('malformed email address')
+    return value
+
+
 def validate_ip(value, required=True):
     if not required and not value:
         return
@@ -51,7 +64,7 @@
 
         kwargs = {
             'id': trim(data.pop('id', None), 128),
-            'email': trim(data.pop('email', None), 128),
+            'email': trim(validate_email(data.pop('email', None), False), 128),
             'username': trim(data.pop('username', None), 128),
             'ip_address': validate_ip(data.pop('ip_address', None), False),
         }

{"golden_diff": "diff --git a/src/sentry/interfaces/user.py b/src/sentry/interfaces/user.py\n--- a/src/sentry/interfaces/user.py\n+++ b/src/sentry/interfaces/user.py\n@@ -15,6 +15,19 @@\n from ipaddr import IPAddress\n \n \n+def validate_email(value, required=True):\n+    if not required and not value:\n+        return\n+\n+    if not isinstance(value, basestring):\n+        raise TypeError('object of type %r is not an email address' % type(value).__name__)\n+\n+    # safe to assume an email address at least has a @ in it.\n+    if '@' not in value:\n+        raise ValueError('malformed email address')\n+    return value\n+\n+\n def validate_ip(value, required=True):\n     if not required and not value:\n         return\n@@ -51,7 +64,7 @@\n \n         kwargs = {\n             'id': trim(data.pop('id', None), 128),\n-            'email': trim(data.pop('email', None), 128),\n+            'email': trim(validate_email(data.pop('email', None), False), 128),\n             'username': trim(data.pop('username', None), 128),\n             'ip_address': validate_ip(data.pop('ip_address', None), False),\n         }\n", "issue": "Error processing 'rule_notify' on 'QuotaMailPlugin': 'int' object has no attribute 'lower'\nhttps://beta.getsentry.com/sentry/sentry/group/56738068/\n\n```\nAttributeError: 'int' object has no attribute 'lower'\n(11 additional frame(s) were not displayed)\n...\n  File \"sentry/interfaces/user.py\", line 99, in to_email_html\n    return render_to_string('sentry/partial/interfaces/user_email.html', context)\n  File \"sentry/web/helpers.py\", line 122, in render_to_string\n    return loader.render_to_string(template, context)\n  File \"templatetag_sugar/node.py\", line 19, in render\n    return self.function(context, *args, **kwargs)\n  File \"sentry/templatetags/sentry_helpers.py\", line 314, in gravatar_url\n    return get_gravatar_url(email, size, default)\n  File \"sentry/utils/avatar.py\", line 18, in get_gravatar_url\n    hashlib.md5(email.lower()).hexdigest())\n\nStacktrace (most recent call last):\n\nError processing 'rule_notify' on 'QuotaMailPlugin': 'int' object has no attribute 'lower'\n\nFile \"sentry/partial/interfaces/user_email.html\", line 7\n\n  <tr>\n    {% if user_email %}\n      <td style=\"width:84px;padding-top:10px;vertical-align:top\">\n        <img src=\"{% gravatar_url user_email size 64 %}\">\n      </td>\n    {% endif %}\n    <td>\n```\n\n", "before_files": [{"content": "\"\"\"\nsentry.interfaces.user\n~~~~~~~~~~~~~~~~~~~~~~\n\n:copyright: (c) 2010-2014 by the Sentry Team, see AUTHORS for more details.\n:license: BSD, see LICENSE for more details.\n\"\"\"\nfrom __future__ import absolute_import\n\n__all__ = ('User',)\n\nfrom sentry.interfaces.base import Interface\nfrom sentry.utils.safe import trim, trim_dict\nfrom sentry.web.helpers import render_to_string\nfrom ipaddr import IPAddress\n\n\ndef validate_ip(value, required=True):\n    if not required and not value:\n        return\n\n    # will raise a ValueError\n    IPAddress(value)\n    return value\n\n\nclass User(Interface):\n    \"\"\"\n    An interface which describes the authenticated User for a request.\n\n    You should provide **at least** either an `id` (a unique identifier for\n    an authenticated user) or `ip_address` (their IP address).\n\n    All other attributes are optional.\n\n    >>> {\n    >>>     \"id\": \"unique_id\",\n    >>>     \"username\": \"my_user\",\n    >>>     \"email\": \"foo@example.com\"\n    >>>     \"ip_address\": \"127.0.0.1\",\n    >>>     \"optional\": \"value\"\n    >>> }\n    \"\"\"\n    @classmethod\n    def to_python(cls, data):\n        data = data.copy()\n\n        extra_data = data.pop('data', data)\n        if not isinstance(extra_data, dict):\n            extra_data = {}\n\n        kwargs = {\n            'id': trim(data.pop('id', None), 128),\n            'email': trim(data.pop('email', None), 128),\n            'username': trim(data.pop('username', None), 128),\n            'ip_address': validate_ip(data.pop('ip_address', None), False),\n        }\n\n        kwargs['data'] = trim_dict(extra_data)\n        return cls(**kwargs)\n\n    def get_api_context(self, is_public=False):\n        return {\n            'id': self.id,\n            'email': self.email,\n            'username': self.username,\n            'ipAddress': self.ip_address,\n            'data': self.data,\n        }\n\n    def get_path(self):\n        return 'sentry.interfaces.User'\n\n    def get_hash(self):\n        return []\n\n    def to_email_html(self, event, **kwargs):\n        context = {\n            'user_id': self.id,\n            'user_email': self.email,\n            'user_username': self.username,\n            'user_ip_address': self.ip_address,\n            'user_data': self.data,\n        }\n        return render_to_string('sentry/partial/interfaces/user_email.html', context)\n", "path": "src/sentry/interfaces/user.py"}], "after_files": [{"content": "\"\"\"\nsentry.interfaces.user\n~~~~~~~~~~~~~~~~~~~~~~\n\n:copyright: (c) 2010-2014 by the Sentry Team, see AUTHORS for more details.\n:license: BSD, see LICENSE for more details.\n\"\"\"\nfrom __future__ import absolute_import\n\n__all__ = ('User',)\n\nfrom sentry.interfaces.base import Interface\nfrom sentry.utils.safe import trim, trim_dict\nfrom sentry.web.helpers import render_to_string\nfrom ipaddr import IPAddress\n\n\ndef validate_email(value, required=True):\n    if not required and not value:\n        return\n\n    if not isinstance(value, basestring):\n        raise TypeError('object of type %r is not an email address' % type(value).__name__)\n\n    # safe to assume an email address at least has a @ in it.\n    if '@' not in value:\n        raise ValueError('malformed email address')\n    return value\n\n\ndef validate_ip(value, required=True):\n    if not required and not value:\n        return\n\n    # will raise a ValueError\n    IPAddress(value)\n    return value\n\n\nclass User(Interface):\n    \"\"\"\n    An interface which describes the authenticated User for a request.\n\n    You should provide **at least** either an `id` (a unique identifier for\n    an authenticated user) or `ip_address` (their IP address).\n\n    All other attributes are optional.\n\n    >>> {\n    >>>     \"id\": \"unique_id\",\n    >>>     \"username\": \"my_user\",\n    >>>     \"email\": \"foo@example.com\"\n    >>>     \"ip_address\": \"127.0.0.1\",\n    >>>     \"optional\": \"value\"\n    >>> }\n    \"\"\"\n    @classmethod\n    def to_python(cls, data):\n        data = data.copy()\n\n        extra_data = data.pop('data', data)\n        if not isinstance(extra_data, dict):\n            extra_data = {}\n\n        kwargs = {\n            'id': trim(data.pop('id', None), 128),\n            'email': trim(validate_email(data.pop('email', None), False), 128),\n            'username': trim(data.pop('username', None), 128),\n            'ip_address': validate_ip(data.pop('ip_address', None), False),\n        }\n\n        kwargs['data'] = trim_dict(extra_data)\n        return cls(**kwargs)\n\n    def get_api_context(self, is_public=False):\n        return {\n            'id': self.id,\n            'email': self.email,\n            'username': self.username,\n            'ipAddress': self.ip_address,\n            'data': self.data,\n        }\n\n    def get_path(self):\n        return 'sentry.interfaces.User'\n\n    def get_hash(self):\n        return []\n\n    def to_email_html(self, event, **kwargs):\n        context = {\n            'user_id': self.id,\n            'user_email': self.email,\n            'user_username': self.username,\n            'user_ip_address': self.ip_address,\n            'user_data': self.data,\n        }\n        return render_to_string('sentry/partial/interfaces/user_email.html', context)\n", "path": "src/sentry/interfaces/user.py"}]}