prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Crash in pure_eval
  This happened while we were experiencing a DB outage:
  ```
    File "/usr/local/lib/python3.8/dist-packages/asyncpg/connection.py", line 443, in fetch
      return await self._execute(query, args, 0, timeout)
    File "/usr/local/lib/python3.8/dist-packages/asyncpg/connection.py", line 1445, in _execute
      result, _ = await self.__execute(
    File "/server/athenian/api/db.py", line 191, in _asyncpg_execute
      result = await self._execute_original(query, args, limit, timeout, return_status)
    File "/usr/local/lib/python3.8/dist-packages/asyncpg/connection.py", line 1454, in __execute
      return await self._do_execute(query, executor, timeout)
    File "/usr/local/lib/python3.8/dist-packages/asyncpg/connection.py", line 1476, in _do_execute
      result = await executor(stmt, None)
    File "asyncpg/protocol/protocol.pyx", line 196, in bind_execute
      return await waiter
  asyncpg.exceptions.ConnectionDoesNotExistError: connection was closed in the middle of operation
  
  During handling of the above exception, another exception occurred:
  
  Traceback (most recent call last):
    File "/usr/local/lib/python3.8/dist-packages/sentry_sdk/scope.py", line 353, in apply_to_event
      new_event = event_processor(event, hint)
    File "/usr/local/lib/python3.8/dist-packages/sentry_sdk/integrations/pure_eval.py", line 79, in add_executing_info
      pure_eval_frame(tb.tb_frame) or sentry_frame["vars"]
    File "/usr/local/lib/python3.8/dist-packages/sentry_sdk/integrations/pure_eval.py", line 128, in pure_eval_frame
      expressions.sort(key=closeness, reverse=True)
    File "/usr/local/lib/python3.8/dist-packages/sentry_sdk/integrations/pure_eval.py", line 113, in closeness
      nodes_before_stmt = [
    File "/usr/local/lib/python3.8/dist-packages/sentry_sdk/integrations/pure_eval.py", line 114, in <listcomp>
      node for node in nodes if node.first_token.startpos < stmt.last_token.endpos
  AttributeError: 'Name' object has no attribute 'first_token'
  ```
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `sentry_sdk/integrations/pure_eval.py`
  Content:
  ```
  1 from __future__ import absolute_import
  2 
  3 import ast
  4 
  5 from sentry_sdk import Hub, serializer
  6 from sentry_sdk._types import MYPY
  7 from sentry_sdk.integrations import Integration, DidNotEnable
  8 from sentry_sdk.scope import add_global_event_processor
  9 from sentry_sdk.utils import walk_exception_chain, iter_stacks
  10 
  11 if MYPY:
  12     from typing import Optional, Dict, Any, Tuple, List
  13     from types import FrameType
  14 
  15     from sentry_sdk._types import Event, Hint
  16 
  17 try:
  18     import executing
  19 except ImportError:
  20     raise DidNotEnable("executing is not installed")
  21 
  22 try:
  23     import pure_eval
  24 except ImportError:
  25     raise DidNotEnable("pure_eval is not installed")
  26 
  27 try:
  28     # Used implicitly, just testing it's available
  29     import asttokens  # noqa
  30 except ImportError:
  31     raise DidNotEnable("asttokens is not installed")
  32 
  33 
  34 class PureEvalIntegration(Integration):
  35     identifier = "pure_eval"
  36 
  37     @staticmethod
  38     def setup_once():
  39         # type: () -> None
  40 
  41         @add_global_event_processor
  42         def add_executing_info(event, hint):
  43             # type: (Event, Optional[Hint]) -> Optional[Event]
  44             if Hub.current.get_integration(PureEvalIntegration) is None:
  45                 return event
  46 
  47             if hint is None:
  48                 return event
  49 
  50             exc_info = hint.get("exc_info", None)
  51 
  52             if exc_info is None:
  53                 return event
  54 
  55             exception = event.get("exception", None)
  56 
  57             if exception is None:
  58                 return event
  59 
  60             values = exception.get("values", None)
  61 
  62             if values is None:
  63                 return event
  64 
  65             for exception, (_exc_type, _exc_value, exc_tb) in zip(
  66                 reversed(values), walk_exception_chain(exc_info)
  67             ):
  68                 sentry_frames = [
  69                     frame
  70                     for frame in exception.get("stacktrace", {}).get("frames", [])
  71                     if frame.get("function")
  72                 ]
  73                 tbs = list(iter_stacks(exc_tb))
  74                 if len(sentry_frames) != len(tbs):
  75                     continue
  76 
  77                 for sentry_frame, tb in zip(sentry_frames, tbs):
  78                     sentry_frame["vars"] = (
  79                         pure_eval_frame(tb.tb_frame) or sentry_frame["vars"]
  80                     )
  81             return event
  82 
  83 
  84 def pure_eval_frame(frame):
  85     # type: (FrameType) -> Dict[str, Any]
  86     source = executing.Source.for_frame(frame)
  87     if not source.tree:
  88         return {}
  89 
  90     statements = source.statements_at_line(frame.f_lineno)
  91     if not statements:
  92         return {}
  93 
  94     scope = stmt = list(statements)[0]
  95     while True:
  96         # Get the parent first in case the original statement is already
  97         # a function definition, e.g. if we're calling a decorator
  98         # In that case we still want the surrounding scope, not that function
  99         scope = scope.parent
  100         if isinstance(scope, (ast.FunctionDef, ast.ClassDef, ast.Module)):
  101             break
  102 
  103     evaluator = pure_eval.Evaluator.from_frame(frame)
  104     expressions = evaluator.interesting_expressions_grouped(scope)
  105 
  106     def closeness(expression):
  107         # type: (Tuple[List[Any], Any]) -> int
  108         # Prioritise expressions with a node closer to the statement executed
  109         # without being after that statement
  110         # A higher return value is better - the expression will appear
  111         # earlier in the list of values and is less likely to be trimmed
  112         nodes, _value = expression
  113         nodes_before_stmt = [
  114             node for node in nodes if node.first_token.startpos < stmt.last_token.endpos
  115         ]
  116         if nodes_before_stmt:
  117             # The position of the last node before or in the statement
  118             return max(node.first_token.startpos for node in nodes_before_stmt)
  119         else:
  120             # The position of the first node after the statement
  121             # Negative means it's always lower priority than nodes that come before
  122             # Less negative means closer to the statement and higher priority
  123             return -min(node.first_token.startpos for node in nodes)
  124 
  125     # This adds the first_token and last_token attributes to nodes
  126     atok = source.asttokens()
  127 
  128     expressions.sort(key=closeness, reverse=True)
  129     return {
  130         atok.get_text(nodes[0]): value
  131         for nodes, value in expressions[: serializer.MAX_DATABAG_BREADTH]
  132     }
  133
  ```
  --- 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/sentry_sdk/integrations/pure_eval.py b/sentry_sdk/integrations/pure_eval.py
  --- a/sentry_sdk/integrations/pure_eval.py
  +++ b/sentry_sdk/integrations/pure_eval.py
  @@ -104,23 +104,29 @@
       expressions = evaluator.interesting_expressions_grouped(scope)
   
       def closeness(expression):
  -        # type: (Tuple[List[Any], Any]) -> int
  +        # type: (Tuple[List[Any], Any]) -> Tuple[int, int]
           # Prioritise expressions with a node closer to the statement executed
           # without being after that statement
           # A higher return value is better - the expression will appear
           # earlier in the list of values and is less likely to be trimmed
           nodes, _value = expression
  +
  +        def start(n):
  +            # type: (ast.expr) -> Tuple[int, int]
  +            return (n.lineno, n.col_offset)
  +
           nodes_before_stmt = [
  -            node for node in nodes if node.first_token.startpos < stmt.last_token.endpos
  +            node for node in nodes if start(node) < stmt.last_token.end
           ]
           if nodes_before_stmt:
               # The position of the last node before or in the statement
  -            return max(node.first_token.startpos for node in nodes_before_stmt)
  +            return max(start(node) for node in nodes_before_stmt)
           else:
               # The position of the first node after the statement
               # Negative means it's always lower priority than nodes that come before
               # Less negative means closer to the statement and higher priority
  -            return -min(node.first_token.startpos for node in nodes)
  +            lineno, col_offset = min(start(node) for node in nodes)
  +            return (-lineno, -col_offset)
   
       # This adds the first_token and last_token attributes to nodes
       atok = source.asttokens()
  
verification_info: {"golden_diff": "diff --git a/sentry_sdk/integrations/pure_eval.py b/sentry_sdk/integrations/pure_eval.py\n--- a/sentry_sdk/integrations/pure_eval.py\n+++ b/sentry_sdk/integrations/pure_eval.py\n@@ -104,23 +104,29 @@\n     expressions = evaluator.interesting_expressions_grouped(scope)\n \n     def closeness(expression):\n-        # type: (Tuple[List[Any], Any]) -> int\n+        # type: (Tuple[List[Any], Any]) -> Tuple[int, int]\n         # Prioritise expressions with a node closer to the statement executed\n         # without being after that statement\n         # A higher return value is better - the expression will appear\n         # earlier in the list of values and is less likely to be trimmed\n         nodes, _value = expression\n+\n+        def start(n):\n+            # type: (ast.expr) -> Tuple[int, int]\n+            return (n.lineno, n.col_offset)\n+\n         nodes_before_stmt = [\n-            node for node in nodes if node.first_token.startpos < stmt.last_token.endpos\n+            node for node in nodes if start(node) < stmt.last_token.end\n         ]\n         if nodes_before_stmt:\n             # The position of the last node before or in the statement\n-            return max(node.first_token.startpos for node in nodes_before_stmt)\n+            return max(start(node) for node in nodes_before_stmt)\n         else:\n             # The position of the first node after the statement\n             # Negative means it's always lower priority than nodes that come before\n             # Less negative means closer to the statement and higher priority\n-            return -min(node.first_token.startpos for node in nodes)\n+            lineno, col_offset = min(start(node) for node in nodes)\n+            return (-lineno, -col_offset)\n \n     # This adds the first_token and last_token attributes to nodes\n     atok = source.asttokens()\n", "issue": "Crash in pure_eval\nThis happened while we were experiencing a DB outage:\r\n```\r\n  File \"/usr/local/lib/python3.8/dist-packages/asyncpg/connection.py\", line 443, in fetch\r\n    return await self._execute(query, args, 0, timeout)\r\n  File \"/usr/local/lib/python3.8/dist-packages/asyncpg/connection.py\", line 1445, in _execute\r\n    result, _ = await self.__execute(\r\n  File \"/server/athenian/api/db.py\", line 191, in _asyncpg_execute\r\n    result = await self._execute_original(query, args, limit, timeout, return_status)\r\n  File \"/usr/local/lib/python3.8/dist-packages/asyncpg/connection.py\", line 1454, in __execute\r\n    return await self._do_execute(query, executor, timeout)\r\n  File \"/usr/local/lib/python3.8/dist-packages/asyncpg/connection.py\", line 1476, in _do_execute\r\n    result = await executor(stmt, None)\r\n  File \"asyncpg/protocol/protocol.pyx\", line 196, in bind_execute\r\n    return await waiter\r\nasyncpg.exceptions.ConnectionDoesNotExistError: connection was closed in the middle of operation\r\n\r\nDuring handling of the above exception, another exception occurred:\r\n\r\nTraceback (most recent call last):\r\n  File \"/usr/local/lib/python3.8/dist-packages/sentry_sdk/scope.py\", line 353, in apply_to_event\r\n    new_event = event_processor(event, hint)\r\n  File \"/usr/local/lib/python3.8/dist-packages/sentry_sdk/integrations/pure_eval.py\", line 79, in add_executing_info\r\n    pure_eval_frame(tb.tb_frame) or sentry_frame[\"vars\"]\r\n  File \"/usr/local/lib/python3.8/dist-packages/sentry_sdk/integrations/pure_eval.py\", line 128, in pure_eval_frame\r\n    expressions.sort(key=closeness, reverse=True)\r\n  File \"/usr/local/lib/python3.8/dist-packages/sentry_sdk/integrations/pure_eval.py\", line 113, in closeness\r\n    nodes_before_stmt = [\r\n  File \"/usr/local/lib/python3.8/dist-packages/sentry_sdk/integrations/pure_eval.py\", line 114, in <listcomp>\r\n    node for node in nodes if node.first_token.startpos < stmt.last_token.endpos\r\nAttributeError: 'Name' object has no attribute 'first_token'\r\n```\n", "before_files": [{"content": "from __future__ import absolute_import\n\nimport ast\n\nfrom sentry_sdk import Hub, serializer\nfrom sentry_sdk._types import MYPY\nfrom sentry_sdk.integrations import Integration, DidNotEnable\nfrom sentry_sdk.scope import add_global_event_processor\nfrom sentry_sdk.utils import walk_exception_chain, iter_stacks\n\nif MYPY:\n    from typing import Optional, Dict, Any, Tuple, List\n    from types import FrameType\n\n    from sentry_sdk._types import Event, Hint\n\ntry:\n    import executing\nexcept ImportError:\n    raise DidNotEnable(\"executing is not installed\")\n\ntry:\n    import pure_eval\nexcept ImportError:\n    raise DidNotEnable(\"pure_eval is not installed\")\n\ntry:\n    # Used implicitly, just testing it's available\n    import asttokens  # noqa\nexcept ImportError:\n    raise DidNotEnable(\"asttokens is not installed\")\n\n\nclass PureEvalIntegration(Integration):\n    identifier = \"pure_eval\"\n\n    @staticmethod\n    def setup_once():\n        # type: () -> None\n\n        @add_global_event_processor\n        def add_executing_info(event, hint):\n            # type: (Event, Optional[Hint]) -> Optional[Event]\n            if Hub.current.get_integration(PureEvalIntegration) is None:\n                return event\n\n            if hint is None:\n                return event\n\n            exc_info = hint.get(\"exc_info\", None)\n\n            if exc_info is None:\n                return event\n\n            exception = event.get(\"exception\", None)\n\n            if exception is None:\n                return event\n\n            values = exception.get(\"values\", None)\n\n            if values is None:\n                return event\n\n            for exception, (_exc_type, _exc_value, exc_tb) in zip(\n                reversed(values), walk_exception_chain(exc_info)\n            ):\n                sentry_frames = [\n                    frame\n                    for frame in exception.get(\"stacktrace\", {}).get(\"frames\", [])\n                    if frame.get(\"function\")\n                ]\n                tbs = list(iter_stacks(exc_tb))\n                if len(sentry_frames) != len(tbs):\n                    continue\n\n                for sentry_frame, tb in zip(sentry_frames, tbs):\n                    sentry_frame[\"vars\"] = (\n                        pure_eval_frame(tb.tb_frame) or sentry_frame[\"vars\"]\n                    )\n            return event\n\n\ndef pure_eval_frame(frame):\n    # type: (FrameType) -> Dict[str, Any]\n    source = executing.Source.for_frame(frame)\n    if not source.tree:\n        return {}\n\n    statements = source.statements_at_line(frame.f_lineno)\n    if not statements:\n        return {}\n\n    scope = stmt = list(statements)[0]\n    while True:\n        # Get the parent first in case the original statement is already\n        # a function definition, e.g. if we're calling a decorator\n        # In that case we still want the surrounding scope, not that function\n        scope = scope.parent\n        if isinstance(scope, (ast.FunctionDef, ast.ClassDef, ast.Module)):\n            break\n\n    evaluator = pure_eval.Evaluator.from_frame(frame)\n    expressions = evaluator.interesting_expressions_grouped(scope)\n\n    def closeness(expression):\n        # type: (Tuple[List[Any], Any]) -> int\n        # Prioritise expressions with a node closer to the statement executed\n        # without being after that statement\n        # A higher return value is better - the expression will appear\n        # earlier in the list of values and is less likely to be trimmed\n        nodes, _value = expression\n        nodes_before_stmt = [\n            node for node in nodes if node.first_token.startpos < stmt.last_token.endpos\n        ]\n        if nodes_before_stmt:\n            # The position of the last node before or in the statement\n            return max(node.first_token.startpos for node in nodes_before_stmt)\n        else:\n            # The position of the first node after the statement\n            # Negative means it's always lower priority than nodes that come before\n            # Less negative means closer to the statement and higher priority\n            return -min(node.first_token.startpos for node in nodes)\n\n    # This adds the first_token and last_token attributes to nodes\n    atok = source.asttokens()\n\n    expressions.sort(key=closeness, reverse=True)\n    return {\n        atok.get_text(nodes[0]): value\n        for nodes, value in expressions[: serializer.MAX_DATABAG_BREADTH]\n    }\n", "path": "sentry_sdk/integrations/pure_eval.py"}], "after_files": [{"content": "from __future__ import absolute_import\n\nimport ast\n\nfrom sentry_sdk import Hub, serializer\nfrom sentry_sdk._types import MYPY\nfrom sentry_sdk.integrations import Integration, DidNotEnable\nfrom sentry_sdk.scope import add_global_event_processor\nfrom sentry_sdk.utils import walk_exception_chain, iter_stacks\n\nif MYPY:\n    from typing import Optional, Dict, Any, Tuple, List\n    from types import FrameType\n\n    from sentry_sdk._types import Event, Hint\n\ntry:\n    import executing\nexcept ImportError:\n    raise DidNotEnable(\"executing is not installed\")\n\ntry:\n    import pure_eval\nexcept ImportError:\n    raise DidNotEnable(\"pure_eval is not installed\")\n\ntry:\n    # Used implicitly, just testing it's available\n    import asttokens  # noqa\nexcept ImportError:\n    raise DidNotEnable(\"asttokens is not installed\")\n\n\nclass PureEvalIntegration(Integration):\n    identifier = \"pure_eval\"\n\n    @staticmethod\n    def setup_once():\n        # type: () -> None\n\n        @add_global_event_processor\n        def add_executing_info(event, hint):\n            # type: (Event, Optional[Hint]) -> Optional[Event]\n            if Hub.current.get_integration(PureEvalIntegration) is None:\n                return event\n\n            if hint is None:\n                return event\n\n            exc_info = hint.get(\"exc_info\", None)\n\n            if exc_info is None:\n                return event\n\n            exception = event.get(\"exception\", None)\n\n            if exception is None:\n                return event\n\n            values = exception.get(\"values\", None)\n\n            if values is None:\n                return event\n\n            for exception, (_exc_type, _exc_value, exc_tb) in zip(\n                reversed(values), walk_exception_chain(exc_info)\n            ):\n                sentry_frames = [\n                    frame\n                    for frame in exception.get(\"stacktrace\", {}).get(\"frames\", [])\n                    if frame.get(\"function\")\n                ]\n                tbs = list(iter_stacks(exc_tb))\n                if len(sentry_frames) != len(tbs):\n                    continue\n\n                for sentry_frame, tb in zip(sentry_frames, tbs):\n                    sentry_frame[\"vars\"] = (\n                        pure_eval_frame(tb.tb_frame) or sentry_frame[\"vars\"]\n                    )\n            return event\n\n\ndef pure_eval_frame(frame):\n    # type: (FrameType) -> Dict[str, Any]\n    source = executing.Source.for_frame(frame)\n    if not source.tree:\n        return {}\n\n    statements = source.statements_at_line(frame.f_lineno)\n    if not statements:\n        return {}\n\n    scope = stmt = list(statements)[0]\n    while True:\n        # Get the parent first in case the original statement is already\n        # a function definition, e.g. if we're calling a decorator\n        # In that case we still want the surrounding scope, not that function\n        scope = scope.parent\n        if isinstance(scope, (ast.FunctionDef, ast.ClassDef, ast.Module)):\n            break\n\n    evaluator = pure_eval.Evaluator.from_frame(frame)\n    expressions = evaluator.interesting_expressions_grouped(scope)\n\n    def closeness(expression):\n        # type: (Tuple[List[Any], Any]) -> Tuple[int, int]\n        # Prioritise expressions with a node closer to the statement executed\n        # without being after that statement\n        # A higher return value is better - the expression will appear\n        # earlier in the list of values and is less likely to be trimmed\n        nodes, _value = expression\n\n        def start(n):\n            # type: (ast.expr) -> Tuple[int, int]\n            return (n.lineno, n.col_offset)\n\n        nodes_before_stmt = [\n            node for node in nodes if start(node) < stmt.last_token.end\n        ]\n        if nodes_before_stmt:\n            # The position of the last node before or in the statement\n            return max(start(node) for node in nodes_before_stmt)\n        else:\n            # The position of the first node after the statement\n            # Negative means it's always lower priority than nodes that come before\n            # Less negative means closer to the statement and higher priority\n            lineno, col_offset = min(start(node) for node in nodes)\n            return (-lineno, -col_offset)\n\n    # This adds the first_token and last_token attributes to nodes\n    atok = source.asttokens()\n\n    expressions.sort(key=closeness, reverse=True)\n    return {\n        atok.get_text(nodes[0]): value\n        for nodes, value in expressions[: serializer.MAX_DATABAG_BREADTH]\n    }\n", "path": "sentry_sdk/integrations/pure_eval.py"}]}