prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  async-detection: nonlocal does not raise syntaxerror
  See https://github.com/ipython/ipython/issues/11303#issuecomment-421297197
  
  ```
  In [1]: x = 1
     ...: def f():
     ...:     nonlocal x
     ...:     x = 10000
  ```
  Should raise but does not.
  
  It's minor, but may be good to fix as behavior is likely undefined.
  async-detection: nonlocal does not raise syntaxerror
  See https://github.com/ipython/ipython/issues/11303#issuecomment-421297197
  
  ```
  In [1]: x = 1
     ...: def f():
     ...:     nonlocal x
     ...:     x = 10000
  ```
  Should raise but does not.
  
  It's minor, but may be good to fix as behavior is likely undefined.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `IPython/core/async_helpers.py`
  Content:
  ```
  1 """
  2 Async helper function that are invalid syntax on Python 3.5 and below.
  3 
  4 This code is best effort, and may have edge cases not behaving as expected. In
  5 particular it contain a number of heuristics to detect whether code is
  6 effectively async and need to run in an event loop or not.
  7 
  8 Some constructs (like top-level `return`, or `yield`) are taken care of
  9 explicitly to actually raise a SyntaxError and stay as close as possible to
  10 Python semantics.
  11 """
  12 
  13 
  14 import ast
  15 import sys
  16 from textwrap import dedent, indent
  17 
  18 
  19 class _AsyncIORunner:
  20 
  21     def __call__(self, coro):
  22         """
  23         Handler for asyncio autoawait
  24         """
  25         import asyncio
  26 
  27         return asyncio.get_event_loop().run_until_complete(coro)
  28 
  29     def __str__(self):
  30         return 'asyncio'
  31 
  32 _asyncio_runner = _AsyncIORunner()
  33 
  34 
  35 def _curio_runner(coroutine):
  36     """
  37     handler for curio autoawait
  38     """
  39     import curio
  40 
  41     return curio.run(coroutine)
  42 
  43 
  44 def _trio_runner(async_fn):
  45     import trio
  46 
  47     async def loc(coro):
  48         """
  49         We need the dummy no-op async def to protect from
  50         trio's internal. See https://github.com/python-trio/trio/issues/89
  51         """
  52         return await coro
  53 
  54     return trio.run(loc, async_fn)
  55 
  56 
  57 def _pseudo_sync_runner(coro):
  58     """
  59     A runner that does not really allow async execution, and just advance the coroutine.
  60 
  61     See discussion in https://github.com/python-trio/trio/issues/608,
  62 
  63     Credit to Nathaniel Smith
  64 
  65     """
  66     try:
  67         coro.send(None)
  68     except StopIteration as exc:
  69         return exc.value
  70     else:
  71         # TODO: do not raise but return an execution result with the right info.
  72         raise RuntimeError(
  73             "{coro_name!r} needs a real async loop".format(coro_name=coro.__name__)
  74         )
  75 
  76 
  77 def _asyncify(code: str) -> str:
  78     """wrap code in async def definition.
  79 
  80     And setup a bit of context to run it later.
  81     """
  82     res = dedent(
  83         """
  84     async def __wrapper__():
  85         try:
  86     {usercode}
  87         finally:
  88             locals()
  89     """
  90     ).format(usercode=indent(code, " " * 8))
  91     return res
  92 
  93 
  94 class _AsyncSyntaxErrorVisitor(ast.NodeVisitor):
  95     """
  96     Find syntax errors that would be an error in an async repl, but because
  97     the implementation involves wrapping the repl in an async function, it
  98     is erroneously allowed (e.g. yield or return at the top level)
  99     """
  100 
  101     def generic_visit(self, node):
  102         func_types = (ast.FunctionDef, ast.AsyncFunctionDef)
  103         invalid_types = (ast.Return, ast.Yield, ast.YieldFrom)
  104 
  105         if isinstance(node, func_types):
  106             return  # Don't recurse into functions
  107         elif isinstance(node, invalid_types):
  108             raise SyntaxError()
  109         else:
  110             super().generic_visit(node)
  111 
  112 
  113 def _async_parse_cell(cell: str) -> ast.AST:
  114     """
  115     This is a compatibility shim for pre-3.7 when async outside of a function
  116     is a syntax error at the parse stage.
  117 
  118     It will return an abstract syntax tree parsed as if async and await outside
  119     of a function were not a syntax error.
  120     """
  121     if sys.version_info < (3, 7):
  122         # Prior to 3.7 you need to asyncify before parse
  123         wrapped_parse_tree = ast.parse(_asyncify(cell))
  124         return wrapped_parse_tree.body[0].body[0]
  125     else:
  126         return ast.parse(cell)
  127 
  128 
  129 def _should_be_async(cell: str) -> bool:
  130     """Detect if a block of code need to be wrapped in an `async def`
  131 
  132     Attempt to parse the block of code, it it compile we're fine.
  133     Otherwise we  wrap if and try to compile.
  134 
  135     If it works, assume it should be async. Otherwise Return False.
  136 
  137     Not handled yet: If the block of code has a return statement as  the top
  138     level, it will be seen as async. This is a know limitation.
  139     """
  140 
  141     try:
  142         # we can't limit ourself to ast.parse, as it __accepts__ to parse on
  143         # 3.7+, but just does not _compile_
  144         compile(cell, "<>", "exec")
  145         return False
  146     except SyntaxError:
  147         try:
  148             parse_tree = _async_parse_cell(cell)
  149 
  150             # Raise a SyntaxError if there are top-level return or yields
  151             v = _AsyncSyntaxErrorVisitor()
  152             v.visit(parse_tree)
  153 
  154         except SyntaxError:
  155             return False
  156         return True
  157     return False
  158
  ```
  --- 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/IPython/core/async_helpers.py b/IPython/core/async_helpers.py
  --- a/IPython/core/async_helpers.py
  +++ b/IPython/core/async_helpers.py
  @@ -97,14 +97,22 @@
       the implementation involves wrapping the repl in an async function, it
       is erroneously allowed (e.g. yield or return at the top level)
       """
  +    def __init__(self):
  +        self.depth = 0
  +        super().__init__()
   
       def generic_visit(self, node):
           func_types = (ast.FunctionDef, ast.AsyncFunctionDef)
  -        invalid_types = (ast.Return, ast.Yield, ast.YieldFrom)
  -
  -        if isinstance(node, func_types):
  -            return  # Don't recurse into functions
  -        elif isinstance(node, invalid_types):
  +        invalid_types_by_depth = {
  +            0: (ast.Return, ast.Yield, ast.YieldFrom),
  +            1: (ast.Nonlocal,)
  +        }
  +
  +        should_traverse = self.depth < max(invalid_types_by_depth.keys())
  +        if isinstance(node, func_types) and should_traverse:
  +            self.depth += 1
  +            super().generic_visit(node)
  +        elif isinstance(node, invalid_types_by_depth[self.depth]):
               raise SyntaxError()
           else:
               super().generic_visit(node)
  
verification_info: {"golden_diff": "diff --git a/IPython/core/async_helpers.py b/IPython/core/async_helpers.py\n--- a/IPython/core/async_helpers.py\n+++ b/IPython/core/async_helpers.py\n@@ -97,14 +97,22 @@\n     the implementation involves wrapping the repl in an async function, it\n     is erroneously allowed (e.g. yield or return at the top level)\n     \"\"\"\n+    def __init__(self):\n+        self.depth = 0\n+        super().__init__()\n \n     def generic_visit(self, node):\n         func_types = (ast.FunctionDef, ast.AsyncFunctionDef)\n-        invalid_types = (ast.Return, ast.Yield, ast.YieldFrom)\n-\n-        if isinstance(node, func_types):\n-            return  # Don't recurse into functions\n-        elif isinstance(node, invalid_types):\n+        invalid_types_by_depth = {\n+            0: (ast.Return, ast.Yield, ast.YieldFrom),\n+            1: (ast.Nonlocal,)\n+        }\n+\n+        should_traverse = self.depth < max(invalid_types_by_depth.keys())\n+        if isinstance(node, func_types) and should_traverse:\n+            self.depth += 1\n+            super().generic_visit(node)\n+        elif isinstance(node, invalid_types_by_depth[self.depth]):\n             raise SyntaxError()\n         else:\n             super().generic_visit(node)\n", "issue": "async-detection: nonlocal does not raise syntaxerror\nSee https://github.com/ipython/ipython/issues/11303#issuecomment-421297197\r\n\r\n```\r\nIn [1]: x = 1\r\n   ...: def f():\r\n   ...:     nonlocal x\r\n   ...:     x = 10000\r\n```\r\nShould raise but does not.\r\n\r\nIt's minor, but may be good to fix as behavior is likely undefined.\nasync-detection: nonlocal does not raise syntaxerror\nSee https://github.com/ipython/ipython/issues/11303#issuecomment-421297197\r\n\r\n```\r\nIn [1]: x = 1\r\n   ...: def f():\r\n   ...:     nonlocal x\r\n   ...:     x = 10000\r\n```\r\nShould raise but does not.\r\n\r\nIt's minor, but may be good to fix as behavior is likely undefined.\n", "before_files": [{"content": "\"\"\"\nAsync helper function that are invalid syntax on Python 3.5 and below.\n\nThis code is best effort, and may have edge cases not behaving as expected. In\nparticular it contain a number of heuristics to detect whether code is\neffectively async and need to run in an event loop or not.\n\nSome constructs (like top-level `return`, or `yield`) are taken care of\nexplicitly to actually raise a SyntaxError and stay as close as possible to\nPython semantics.\n\"\"\"\n\n\nimport ast\nimport sys\nfrom textwrap import dedent, indent\n\n\nclass _AsyncIORunner:\n\n    def __call__(self, coro):\n        \"\"\"\n        Handler for asyncio autoawait\n        \"\"\"\n        import asyncio\n\n        return asyncio.get_event_loop().run_until_complete(coro)\n\n    def __str__(self):\n        return 'asyncio'\n\n_asyncio_runner = _AsyncIORunner()\n\n\ndef _curio_runner(coroutine):\n    \"\"\"\n    handler for curio autoawait\n    \"\"\"\n    import curio\n\n    return curio.run(coroutine)\n\n\ndef _trio_runner(async_fn):\n    import trio\n\n    async def loc(coro):\n        \"\"\"\n        We need the dummy no-op async def to protect from\n        trio's internal. See https://github.com/python-trio/trio/issues/89\n        \"\"\"\n        return await coro\n\n    return trio.run(loc, async_fn)\n\n\ndef _pseudo_sync_runner(coro):\n    \"\"\"\n    A runner that does not really allow async execution, and just advance the coroutine.\n\n    See discussion in https://github.com/python-trio/trio/issues/608,\n\n    Credit to Nathaniel Smith\n\n    \"\"\"\n    try:\n        coro.send(None)\n    except StopIteration as exc:\n        return exc.value\n    else:\n        # TODO: do not raise but return an execution result with the right info.\n        raise RuntimeError(\n            \"{coro_name!r} needs a real async loop\".format(coro_name=coro.__name__)\n        )\n\n\ndef _asyncify(code: str) -> str:\n    \"\"\"wrap code in async def definition.\n\n    And setup a bit of context to run it later.\n    \"\"\"\n    res = dedent(\n        \"\"\"\n    async def __wrapper__():\n        try:\n    {usercode}\n        finally:\n            locals()\n    \"\"\"\n    ).format(usercode=indent(code, \" \" * 8))\n    return res\n\n\nclass _AsyncSyntaxErrorVisitor(ast.NodeVisitor):\n    \"\"\"\n    Find syntax errors that would be an error in an async repl, but because\n    the implementation involves wrapping the repl in an async function, it\n    is erroneously allowed (e.g. yield or return at the top level)\n    \"\"\"\n\n    def generic_visit(self, node):\n        func_types = (ast.FunctionDef, ast.AsyncFunctionDef)\n        invalid_types = (ast.Return, ast.Yield, ast.YieldFrom)\n\n        if isinstance(node, func_types):\n            return  # Don't recurse into functions\n        elif isinstance(node, invalid_types):\n            raise SyntaxError()\n        else:\n            super().generic_visit(node)\n\n\ndef _async_parse_cell(cell: str) -> ast.AST:\n    \"\"\"\n    This is a compatibility shim for pre-3.7 when async outside of a function\n    is a syntax error at the parse stage.\n\n    It will return an abstract syntax tree parsed as if async and await outside\n    of a function were not a syntax error.\n    \"\"\"\n    if sys.version_info < (3, 7):\n        # Prior to 3.7 you need to asyncify before parse\n        wrapped_parse_tree = ast.parse(_asyncify(cell))\n        return wrapped_parse_tree.body[0].body[0]\n    else:\n        return ast.parse(cell)\n\n\ndef _should_be_async(cell: str) -> bool:\n    \"\"\"Detect if a block of code need to be wrapped in an `async def`\n\n    Attempt to parse the block of code, it it compile we're fine.\n    Otherwise we  wrap if and try to compile.\n\n    If it works, assume it should be async. Otherwise Return False.\n\n    Not handled yet: If the block of code has a return statement as  the top\n    level, it will be seen as async. This is a know limitation.\n    \"\"\"\n\n    try:\n        # we can't limit ourself to ast.parse, as it __accepts__ to parse on\n        # 3.7+, but just does not _compile_\n        compile(cell, \"<>\", \"exec\")\n        return False\n    except SyntaxError:\n        try:\n            parse_tree = _async_parse_cell(cell)\n\n            # Raise a SyntaxError if there are top-level return or yields\n            v = _AsyncSyntaxErrorVisitor()\n            v.visit(parse_tree)\n\n        except SyntaxError:\n            return False\n        return True\n    return False\n", "path": "IPython/core/async_helpers.py"}], "after_files": [{"content": "\"\"\"\nAsync helper function that are invalid syntax on Python 3.5 and below.\n\nThis code is best effort, and may have edge cases not behaving as expected. In\nparticular it contain a number of heuristics to detect whether code is\neffectively async and need to run in an event loop or not.\n\nSome constructs (like top-level `return`, or `yield`) are taken care of\nexplicitly to actually raise a SyntaxError and stay as close as possible to\nPython semantics.\n\"\"\"\n\n\nimport ast\nimport sys\nfrom textwrap import dedent, indent\n\n\nclass _AsyncIORunner:\n\n    def __call__(self, coro):\n        \"\"\"\n        Handler for asyncio autoawait\n        \"\"\"\n        import asyncio\n\n        return asyncio.get_event_loop().run_until_complete(coro)\n\n    def __str__(self):\n        return 'asyncio'\n\n_asyncio_runner = _AsyncIORunner()\n\n\ndef _curio_runner(coroutine):\n    \"\"\"\n    handler for curio autoawait\n    \"\"\"\n    import curio\n\n    return curio.run(coroutine)\n\n\ndef _trio_runner(async_fn):\n    import trio\n\n    async def loc(coro):\n        \"\"\"\n        We need the dummy no-op async def to protect from\n        trio's internal. See https://github.com/python-trio/trio/issues/89\n        \"\"\"\n        return await coro\n\n    return trio.run(loc, async_fn)\n\n\ndef _pseudo_sync_runner(coro):\n    \"\"\"\n    A runner that does not really allow async execution, and just advance the coroutine.\n\n    See discussion in https://github.com/python-trio/trio/issues/608,\n\n    Credit to Nathaniel Smith\n\n    \"\"\"\n    try:\n        coro.send(None)\n    except StopIteration as exc:\n        return exc.value\n    else:\n        # TODO: do not raise but return an execution result with the right info.\n        raise RuntimeError(\n            \"{coro_name!r} needs a real async loop\".format(coro_name=coro.__name__)\n        )\n\n\ndef _asyncify(code: str) -> str:\n    \"\"\"wrap code in async def definition.\n\n    And setup a bit of context to run it later.\n    \"\"\"\n    res = dedent(\n        \"\"\"\n    async def __wrapper__():\n        try:\n    {usercode}\n        finally:\n            locals()\n    \"\"\"\n    ).format(usercode=indent(code, \" \" * 8))\n    return res\n\n\nclass _AsyncSyntaxErrorVisitor(ast.NodeVisitor):\n    \"\"\"\n    Find syntax errors that would be an error in an async repl, but because\n    the implementation involves wrapping the repl in an async function, it\n    is erroneously allowed (e.g. yield or return at the top level)\n    \"\"\"\n    def __init__(self):\n        self.depth = 0\n        super().__init__()\n\n    def generic_visit(self, node):\n        func_types = (ast.FunctionDef, ast.AsyncFunctionDef)\n        invalid_types_by_depth = {\n            0: (ast.Return, ast.Yield, ast.YieldFrom),\n            1: (ast.Nonlocal,)\n        }\n\n        should_traverse = self.depth < max(invalid_types_by_depth.keys())\n        if isinstance(node, func_types) and should_traverse:\n            self.depth += 1\n            super().generic_visit(node)\n        elif isinstance(node, invalid_types_by_depth[self.depth]):\n            raise SyntaxError()\n        else:\n            super().generic_visit(node)\n\n\ndef _async_parse_cell(cell: str) -> ast.AST:\n    \"\"\"\n    This is a compatibility shim for pre-3.7 when async outside of a function\n    is a syntax error at the parse stage.\n\n    It will return an abstract syntax tree parsed as if async and await outside\n    of a function were not a syntax error.\n    \"\"\"\n    if sys.version_info < (3, 7):\n        # Prior to 3.7 you need to asyncify before parse\n        wrapped_parse_tree = ast.parse(_asyncify(cell))\n        return wrapped_parse_tree.body[0].body[0]\n    else:\n        return ast.parse(cell)\n\n\ndef _should_be_async(cell: str) -> bool:\n    \"\"\"Detect if a block of code need to be wrapped in an `async def`\n\n    Attempt to parse the block of code, it it compile we're fine.\n    Otherwise we  wrap if and try to compile.\n\n    If it works, assume it should be async. Otherwise Return False.\n\n    Not handled yet: If the block of code has a return statement as  the top\n    level, it will be seen as async. This is a know limitation.\n    \"\"\"\n\n    try:\n        # we can't limit ourself to ast.parse, as it __accepts__ to parse on\n        # 3.7+, but just does not _compile_\n        compile(cell, \"<>\", \"exec\")\n        return False\n    except SyntaxError:\n        try:\n            parse_tree = _async_parse_cell(cell)\n\n            # Raise a SyntaxError if there are top-level return or yields\n            v = _AsyncSyntaxErrorVisitor()\n            v.visit(parse_tree)\n\n        except SyntaxError:\n            return False\n        return True\n    return False\n", "path": "IPython/core/async_helpers.py"}]}