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()

```

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)

{"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"}]}