We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
JsProxy does not handle Error correctly
Example:
```python
from js import Error
e = Error.new("Hi")
raise e
```
Raises: `TypeError: exceptions must derive from BaseException`.
This came up in https://github.com/iodide-project/pyodide/pull/880#pullrequestreview-555341317. I will try to fix this.

--- 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/pyodide-py/pyodide/__init__.py`
Content:
```
1 from ._base import open_url, eval_code, find_imports, as_nested_list
2 from .console import get_completions
3 
4 __version__ = "0.15.0"
5 
6 __all__ = ["open_url", "eval_code", "find_imports", "as_nested_list", "get_completions"]
7
```

Path: `src/pyodide-py/pyodide/_base.py`
Content:
```
1 """
2 A library of helper utilities for connecting Python to the browser environment.
3 """
4 
5 import ast
6 from io import StringIO
7 from textwrap import dedent
8 from typing import Dict, List, Any
9 
10 
11 def open_url(url: str) -> StringIO:
12     """
13     Fetches a given URL
14 
15     Parameters
16     ----------
17     url
18        URL to fetch
19 
20     Returns
21     -------
22     a io.StringIO object with the contents of the URL.
23     """
24     from js import XMLHttpRequest
25 
26     req = XMLHttpRequest.new()
27     req.open("GET", url, False)
28     req.send(None)
29     return StringIO(req.response)
30 
31 
32 def eval_code(code: str, ns: Dict[str, Any]) -> None:
33     """Runs a code string
34 
35     The last part of the provided code may be an expression.
36 
37     Parameters
38     ----------
39     code
40        the Python code to run.
41     ns
42        `locals()` or `globals()` context where to execute code.
43 
44     Returns
45     -------
46     None
47     """
48     # handle mis-indented input from multi-line strings
49     code = dedent(code)
50 
51     mod = ast.parse(code)
52     if len(mod.body) == 0:
53         return None
54 
55     expr: Any
56     if isinstance(mod.body[-1], ast.Expr):
57         expr = ast.Expression(mod.body[-1].value)
58         del mod.body[-1]
59     else:
60         expr = None
61 
62     if len(mod.body):
63         exec(compile(mod, "<exec>", mode="exec"), ns, ns)
64     if expr is not None:
65         return eval(compile(expr, "<eval>", mode="eval"), ns, ns)
66     else:
67         return None
68 
69 
70 def find_imports(code: str) -> List[str]:
71     """
72     Finds the imports in a string of code
73 
74     Parameters
75     ----------
76     code
77        the Python code to run.
78 
79     Returns
80     -------
81     A list of module names that are imported in the code.
82 
83     Examples
84     --------
85     >>> from pyodide import find_imports
86     >>> code = "import numpy as np; import scipy.stats"
87     >>> find_imports(code)
88     ['numpy', 'scipy']
89     """
90     # handle mis-indented input from multi-line strings
91     code = dedent(code)
92 
93     mod = ast.parse(code)
94     imports = set()
95     for node in ast.walk(mod):
96         if isinstance(node, ast.Import):
97             for name in node.names:
98                 node_name = name.name
99                 imports.add(node_name.split(".")[0])
100         elif isinstance(node, ast.ImportFrom):
101             module_name = node.module
102             if module_name is None:
103                 continue
104             imports.add(module_name.split(".")[0])
105     return list(sorted(imports))
106 
107 
108 def as_nested_list(obj) -> List:
109     """Convert a nested JS array to nested Python list.
110 
111     Assumes a Javascript object is made of (possibly nested) arrays and
112     converts them to nested Python lists.
113 
114     Parameters
115     ----------
116     obj
117        a Javscript object made of nested arrays.
118 
119     Returns
120     -------
121     Python list, or a nested Python list
122     """
123     try:
124         it = iter(obj)
125         return [as_nested_list(x) for x in it]
126     except TypeError:
127         return obj
128
```
--- 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/pyodide-py/pyodide/__init__.py b/src/pyodide-py/pyodide/__init__.py
--- a/src/pyodide-py/pyodide/__init__.py
+++ b/src/pyodide-py/pyodide/__init__.py
@@ -1,6 +1,13 @@
-from ._base import open_url, eval_code, find_imports, as_nested_list
+from ._base import open_url, eval_code, find_imports, as_nested_list, JsException
 from .console import get_completions
 
 __version__ = "0.15.0"
 
-__all__ = ["open_url", "eval_code", "find_imports", "as_nested_list", "get_completions"]
+__all__ = [
+    "open_url",
+    "eval_code",
+    "find_imports",
+    "as_nested_list",
+    "get_completions",
+    "JsException",
+]
diff --git a/src/pyodide-py/pyodide/_base.py b/src/pyodide-py/pyodide/_base.py
--- a/src/pyodide-py/pyodide/_base.py
+++ b/src/pyodide-py/pyodide/_base.py
@@ -1,6 +1,8 @@
 """
 A library of helper utilities for connecting Python to the browser environment.
 """
+# Added by C:
+# JsException (from jsproxy.c)
 
 import ast
 from io import StringIO
@@ -8,6 +10,16 @@
 from typing import Dict, List, Any
 
 
+class JsException(Exception):
+    """
+    A wrapper around a Javascript Error to allow the Error to be thrown in Python.
+    """
+
+    # This gets overwritten in jsproxy.c, it is just here for autodoc and humans
+    # reading this file.
+    pass
+
+
 def open_url(url: str) -> StringIO:
     """
     Fetches a given URL

{"golden_diff": "diff --git a/src/pyodide-py/pyodide/__init__.py b/src/pyodide-py/pyodide/__init__.py\n--- a/src/pyodide-py/pyodide/__init__.py\n+++ b/src/pyodide-py/pyodide/__init__.py\n@@ -1,6 +1,13 @@\n-from ._base import open_url, eval_code, find_imports, as_nested_list\n+from ._base import open_url, eval_code, find_imports, as_nested_list, JsException\n from .console import get_completions\n \n __version__ = \"0.15.0\"\n \n-__all__ = [\"open_url\", \"eval_code\", \"find_imports\", \"as_nested_list\", \"get_completions\"]\n+__all__ = [\n+    \"open_url\",\n+    \"eval_code\",\n+    \"find_imports\",\n+    \"as_nested_list\",\n+    \"get_completions\",\n+    \"JsException\",\n+]\ndiff --git a/src/pyodide-py/pyodide/_base.py b/src/pyodide-py/pyodide/_base.py\n--- a/src/pyodide-py/pyodide/_base.py\n+++ b/src/pyodide-py/pyodide/_base.py\n@@ -1,6 +1,8 @@\n \"\"\"\n A library of helper utilities for connecting Python to the browser environment.\n \"\"\"\n+# Added by C:\n+# JsException (from jsproxy.c)\n \n import ast\n from io import StringIO\n@@ -8,6 +10,16 @@\n from typing import Dict, List, Any\n \n \n+class JsException(Exception):\n+    \"\"\"\n+    A wrapper around a Javascript Error to allow the Error to be thrown in Python.\n+    \"\"\"\n+\n+    # This gets overwritten in jsproxy.c, it is just here for autodoc and humans\n+    # reading this file.\n+    pass\n+\n+\n def open_url(url: str) -> StringIO:\n     \"\"\"\n     Fetches a given URL\n", "issue": "JsProxy does not handle Error correctly\nExample:\r\n```python\r\nfrom js import Error\r\ne = Error.new(\"Hi\")\r\nraise e\r\n```\r\nRaises: `TypeError: exceptions must derive from BaseException`.\r\nThis came up in https://github.com/iodide-project/pyodide/pull/880#pullrequestreview-555341317. I will try to fix this.\n", "before_files": [{"content": "from ._base import open_url, eval_code, find_imports, as_nested_list\nfrom .console import get_completions\n\n__version__ = \"0.15.0\"\n\n__all__ = [\"open_url\", \"eval_code\", \"find_imports\", \"as_nested_list\", \"get_completions\"]\n", "path": "src/pyodide-py/pyodide/__init__.py"}, {"content": "\"\"\"\nA library of helper utilities for connecting Python to the browser environment.\n\"\"\"\n\nimport ast\nfrom io import StringIO\nfrom textwrap import dedent\nfrom typing import Dict, List, Any\n\n\ndef open_url(url: str) -> StringIO:\n    \"\"\"\n    Fetches a given URL\n\n    Parameters\n    ----------\n    url\n       URL to fetch\n\n    Returns\n    -------\n    a io.StringIO object with the contents of the URL.\n    \"\"\"\n    from js import XMLHttpRequest\n\n    req = XMLHttpRequest.new()\n    req.open(\"GET\", url, False)\n    req.send(None)\n    return StringIO(req.response)\n\n\ndef eval_code(code: str, ns: Dict[str, Any]) -> None:\n    \"\"\"Runs a code string\n\n    The last part of the provided code may be an expression.\n\n    Parameters\n    ----------\n    code\n       the Python code to run.\n    ns\n       `locals()` or `globals()` context where to execute code.\n\n    Returns\n    -------\n    None\n    \"\"\"\n    # handle mis-indented input from multi-line strings\n    code = dedent(code)\n\n    mod = ast.parse(code)\n    if len(mod.body) == 0:\n        return None\n\n    expr: Any\n    if isinstance(mod.body[-1], ast.Expr):\n        expr = ast.Expression(mod.body[-1].value)\n        del mod.body[-1]\n    else:\n        expr = None\n\n    if len(mod.body):\n        exec(compile(mod, \"<exec>\", mode=\"exec\"), ns, ns)\n    if expr is not None:\n        return eval(compile(expr, \"<eval>\", mode=\"eval\"), ns, ns)\n    else:\n        return None\n\n\ndef find_imports(code: str) -> List[str]:\n    \"\"\"\n    Finds the imports in a string of code\n\n    Parameters\n    ----------\n    code\n       the Python code to run.\n\n    Returns\n    -------\n    A list of module names that are imported in the code.\n\n    Examples\n    --------\n    >>> from pyodide import find_imports\n    >>> code = \"import numpy as np; import scipy.stats\"\n    >>> find_imports(code)\n    ['numpy', 'scipy']\n    \"\"\"\n    # handle mis-indented input from multi-line strings\n    code = dedent(code)\n\n    mod = ast.parse(code)\n    imports = set()\n    for node in ast.walk(mod):\n        if isinstance(node, ast.Import):\n            for name in node.names:\n                node_name = name.name\n                imports.add(node_name.split(\".\")[0])\n        elif isinstance(node, ast.ImportFrom):\n            module_name = node.module\n            if module_name is None:\n                continue\n            imports.add(module_name.split(\".\")[0])\n    return list(sorted(imports))\n\n\ndef as_nested_list(obj) -> List:\n    \"\"\"Convert a nested JS array to nested Python list.\n\n    Assumes a Javascript object is made of (possibly nested) arrays and\n    converts them to nested Python lists.\n\n    Parameters\n    ----------\n    obj\n       a Javscript object made of nested arrays.\n\n    Returns\n    -------\n    Python list, or a nested Python list\n    \"\"\"\n    try:\n        it = iter(obj)\n        return [as_nested_list(x) for x in it]\n    except TypeError:\n        return obj\n", "path": "src/pyodide-py/pyodide/_base.py"}], "after_files": [{"content": "from ._base import open_url, eval_code, find_imports, as_nested_list, JsException\nfrom .console import get_completions\n\n__version__ = \"0.15.0\"\n\n__all__ = [\n    \"open_url\",\n    \"eval_code\",\n    \"find_imports\",\n    \"as_nested_list\",\n    \"get_completions\",\n    \"JsException\",\n]\n", "path": "src/pyodide-py/pyodide/__init__.py"}, {"content": "\"\"\"\nA library of helper utilities for connecting Python to the browser environment.\n\"\"\"\n# Added by C:\n# JsException (from jsproxy.c)\n\nimport ast\nfrom io import StringIO\nfrom textwrap import dedent\nfrom typing import Dict, List, Any\n\n\nclass JsException(Exception):\n    \"\"\"\n    A wrapper around a Javascript Error to allow the Error to be thrown in Python.\n    \"\"\"\n\n    # This gets overwritten in jsproxy.c, it is just here for autodoc and humans\n    # reading this file.\n    pass\n\n\ndef open_url(url: str) -> StringIO:\n    \"\"\"\n    Fetches a given URL\n\n    Parameters\n    ----------\n    url\n       URL to fetch\n\n    Returns\n    -------\n    a io.StringIO object with the contents of the URL.\n    \"\"\"\n    from js import XMLHttpRequest\n\n    req = XMLHttpRequest.new()\n    req.open(\"GET\", url, False)\n    req.send(None)\n    return StringIO(req.response)\n\n\ndef eval_code(code: str, ns: Dict[str, Any]) -> None:\n    \"\"\"Runs a code string\n\n    The last part of the provided code may be an expression.\n\n    Parameters\n    ----------\n    code\n       the Python code to run.\n    ns\n       `locals()` or `globals()` context where to execute code.\n\n    Returns\n    -------\n    None\n    \"\"\"\n    # handle mis-indented input from multi-line strings\n    code = dedent(code)\n\n    mod = ast.parse(code)\n    if len(mod.body) == 0:\n        return None\n\n    expr: Any\n    if isinstance(mod.body[-1], ast.Expr):\n        expr = ast.Expression(mod.body[-1].value)\n        del mod.body[-1]\n    else:\n        expr = None\n\n    if len(mod.body):\n        exec(compile(mod, \"<exec>\", mode=\"exec\"), ns, ns)\n    if expr is not None:\n        return eval(compile(expr, \"<eval>\", mode=\"eval\"), ns, ns)\n    else:\n        return None\n\n\ndef find_imports(code: str) -> List[str]:\n    \"\"\"\n    Finds the imports in a string of code\n\n    Parameters\n    ----------\n    code\n       the Python code to run.\n\n    Returns\n    -------\n    A list of module names that are imported in the code.\n\n    Examples\n    --------\n    >>> from pyodide import find_imports\n    >>> code = \"import numpy as np; import scipy.stats\"\n    >>> find_imports(code)\n    ['numpy', 'scipy']\n    \"\"\"\n    # handle mis-indented input from multi-line strings\n    code = dedent(code)\n\n    mod = ast.parse(code)\n    imports = set()\n    for node in ast.walk(mod):\n        if isinstance(node, ast.Import):\n            for name in node.names:\n                node_name = name.name\n                imports.add(node_name.split(\".\")[0])\n        elif isinstance(node, ast.ImportFrom):\n            module_name = node.module\n            if module_name is None:\n                continue\n            imports.add(module_name.split(\".\")[0])\n    return list(sorted(imports))\n\n\ndef as_nested_list(obj) -> List:\n    \"\"\"Convert a nested JS array to nested Python list.\n\n    Assumes a Javascript object is made of (possibly nested) arrays and\n    converts them to nested Python lists.\n\n    Parameters\n    ----------\n    obj\n       a Javscript object made of nested arrays.\n\n    Returns\n    -------\n    Python list, or a nested Python list\n    \"\"\"\n    try:\n        it = iter(obj)\n        return [as_nested_list(x) for x in it]\n    except TypeError:\n        return obj\n", "path": "src/pyodide-py/pyodide/_base.py"}]}