Repository: pyodide/pyodide

Issue:
Soundness error in python2js_buffer
This bug goes back at least to version 0.15.0. It's been messing up my error handling code.
First:
```python
import numpy as np
x = np.array([['string1', 'string2'], ['string3', 'string4']])
```
Second: type `x<enter>1<enter>x<enter>1<enter>`.


Discussion:
In v0.15.0 or v0.16.1 it first says:
```python
SystemError: <JsBoundMethod object at 0x4de7010> returned a result with an error set

Error: TypeError: Internal error: Invalid type to convert to array buffer.

The above exception was the direct cause of the following exception:

SystemError: <JsBoundMethod object at 0x4de7010> returned a result with an error set
string1,string2,string3,string4
```
Then on "1<enter>" it says:
```python
File "<console>", line 1
    x
    ^
SyntaxError: multiple statements found while compiling a single statement
```

Resolving patch:
diff --git a/conftest.py b/conftest.py
--- a/conftest.py
+++ b/conftest.py
@@ -108,7 +108,18 @@ def run_js(self, code):
             Error.stackTraceLimit = Infinity;
             let run = () => { %s }
             try {
-                return [0, run()]
+                let result = run();
+                if(pyodide && pyodide._module && pyodide._module._PyErr_Occurred()){
+                    try {
+                        pyodide._module._pythonexc2js();
+                    } catch(e){
+                        console.error(`Python exited with error flag set! Error was:\n{e.message}`);
+                        // Don't put original error message in new one: we want
+                        // "pytest.raises(xxx, match=msg)" to fail
+                        throw new Error(`Python exited with error flag set!`);
+                    }
+                }
+                return [0, result]
             } catch (e) {
                 return [1, e.toString(), e.stack];
             }
@@ -130,11 +141,22 @@ def run_js_async(self, code):
             let cb = arguments[arguments.length - 1];
             let run = async () => { %s }
             (async () => {
-                try {{
-                    cb([0, await run()]);
-                }} catch (e) {{
+                try {
+                    let result = await run();
+                    if(pyodide && pyodide._module && pyodide._module._PyErr_Occurred()){
+                        try {
+                            pyodide._module._pythonexc2js();
+                        } catch(e){
+                            console.error(`Python exited with error flag set! Error was:\n{e.message}`);
+                            // Don't put original error message in new one: we want
+                            // "pytest.raises(xxx, match=msg)" to fail
+                            throw new Error(`Python exited with error flag set!`);
+                        }
+                    }
+                    cb([0, result]);
+                } catch (e) {
                     cb([1, e.toString(), e.stack]);
-                }}
+                }
             })()
         """
 


Test patch:
diff --git a/packages/numpy/test_numpy.py b/packages/numpy/test_numpy.py
--- a/packages/numpy/test_numpy.py
+++ b/packages/numpy/test_numpy.py
@@ -113,14 +113,12 @@ def test_py2js_buffer_clear_error_flag(selenium):
     selenium.load_package("numpy")
     selenium.run("import numpy as np")
     selenium.run("x = np.array([['string1', 'string2'], ['string3', 'string4']])")
-    assert (
-        selenium.run_js(
-            """
-            pyodide.globals.x
-            return pyodide._module._PyErr_Occurred();
-            """
-        )
-        == 0
+    selenium.run_js(
+        """
+        pyodide.globals.x
+        // Implicit assertion: this doesn't leave python error indicator set
+        // (automatically checked in conftest.py)
+        """
     )
 
 
diff --git a/src/tests/test_typeconversions.py b/src/tests/test_typeconversions.py
--- a/src/tests/test_typeconversions.py
+++ b/src/tests/test_typeconversions.py
@@ -334,13 +334,21 @@ def test_memoryview_conversion(selenium):
         """
         import array
         a = array.array("Q", [1,2,3])
+        b = array.array("u", "123")
         """
     )
     selenium.run_js(
         """
         pyodide.globals.a
-        if(pyodide._module._PyErr_Occurred()){
-            pyodide._module._pythonexc2js();
-        }
+        // Implicit assertion: this doesn't leave python error indicator set
+        // (automatically checked in conftest.py)
+        """
+    )
+
+    selenium.run_js(
+        """
+        pyodide.globals.b
+        // Implicit assertion: this doesn't leave python error indicator set
+        // (automatically checked in conftest.py)
         """
     )