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