prompt: 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()
  
  ```
  
golden_diff: 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),
           }
  
verification_info: {"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"}]}