We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Minor fix: add parameter for shallowCopyToJavascript 
Follow #1202 



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `conftest.py`
Content:
```
1 """
2 Various common utilities for testing.
3 """
4 
5 import contextlib
6 import multiprocessing
7 import textwrap
8 import tempfile
9 import time
10 import os
11 import pathlib
12 import queue
13 import sys
14 import shutil
15 
16 ROOT_PATH = pathlib.Path(__file__).parents[0].resolve()
17 TEST_PATH = ROOT_PATH / "src" / "tests"
18 BUILD_PATH = ROOT_PATH / "build"
19 
20 sys.path.append(str(ROOT_PATH))
21 
22 from pyodide_build._fixes import _selenium_is_connectable  # noqa: E402
23 import selenium.webdriver.common.utils  # noqa: E402
24 
25 # XXX: Temporary fix for ConnectionError in selenium
26 
27 selenium.webdriver.common.utils.is_connectable = _selenium_is_connectable
28 
29 try:
30     import pytest
31 
32     def pytest_addoption(parser):
33         group = parser.getgroup("general")
34         group.addoption(
35             "--build-dir",
36             action="store",
37             default=BUILD_PATH,
38             help="Path to the build directory",
39         )
40         group.addoption(
41             "--run-xfail",
42             action="store_true",
43             help="If provided, tests marked as xfail will be run",
44         )
45 
46 
47 except ImportError:
48     pytest = None  # type: ignore
49 
50 
51 class JavascriptException(Exception):
52     def __init__(self, msg, stack):
53         self.msg = msg
54         self.stack = stack
55         # In chrome the stack contains the message
56         if self.stack and self.stack.startswith(self.msg):
57             self.msg = ""
58 
59     def __str__(self):
60         return "\n\n".join(x for x in [self.msg, self.stack] if x)
61 
62 
63 class SeleniumWrapper:
64     JavascriptException = JavascriptException
65 
66     def __init__(
67         self, server_port, server_hostname="127.0.0.1", server_log=None, build_dir=None
68     ):
69         if build_dir is None:
70             build_dir = BUILD_PATH
71 
72         self.driver = self.get_driver()
73         self.server_port = server_port
74         self.server_hostname = server_hostname
75         self.server_log = server_log
76 
77         if not (pathlib.Path(build_dir) / "test.html").exists():
78             # selenium does not expose HTTP response codes
79             raise ValueError(
80                 f"{(build_dir / 'test.html').resolve()} " f"does not exist!"
81             )
82         self.driver.get(f"http://{server_hostname}:{server_port}/test.html")
83         self.run_js("Error.stackTraceLimit = Infinity")
84         self.run_js("await languagePluginLoader")
85 
86     @property
87     def logs(self):
88         logs = self.driver.execute_script("return window.logs")
89         if logs is not None:
90             return "\n".join(str(x) for x in logs)
91         else:
92             return ""
93 
94     def clean_logs(self):
95         self.driver.execute_script("window.logs = []")
96 
97     def run(self, code):
98         return self.run_js(
99             f"""
100             let result = pyodide.runPython({code!r});
101             if(result && result.deepCopyToJavascript){{
102                 let converted_result = result.deepCopyToJavascript();
103                 result.destroy();
104                 return converted_result;
105             }}
106             return result;
107             """
108         )
109 
110     def run_async(self, code):
111         return self.run_js(
112             f"""
113             let result = await pyodide.runPythonAsync({code!r});
114             if(result && result.deepCopyToJavascript){{
115                 let converted_result = result.deepCopyToJavascript();
116                 result.destroy();
117                 return converted_result;
118             }}
119             return result;
120             """
121         )
122 
123     def run_js(self, code):
124         if isinstance(code, str) and code.startswith("\n"):
125             # we have a multiline string, fix indentation
126             code = textwrap.dedent(code)
127 
128         wrapper = """
129             let cb = arguments[arguments.length - 1];
130             let run = async () => { %s }
131             (async () => {
132                 try {
133                     let result = await run();
134                     if(pyodide && pyodide._module && pyodide._module._PyErr_Occurred()){
135                         try {
136                             pyodide._module._pythonexc2js();
137                         } catch(e){
138                             console.error(`Python exited with error flag set! Error was:\n{e.message}`);
139                             // Don't put original error message in new one: we want
140                             // "pytest.raises(xxx, match=msg)" to fail
141                             throw new Error(`Python exited with error flag set!`);
142                         }
143                     }
144                     cb([0, result]);
145                 } catch (e) {
146                     cb([1, e.toString(), e.stack]);
147                 }
148             })()
149         """
150 
151         retval = self.driver.execute_async_script(wrapper % code)
152 
153         if retval[0] == 0:
154             return retval[1]
155         else:
156             raise JavascriptException(retval[1], retval[2])
157 
158     def run_webworker(self, code):
159         if isinstance(code, str) and code.startswith("\n"):
160             # we have a multiline string, fix indentation
161             code = textwrap.dedent(code)
162 
163         return self.run_js(
164             """
165             let worker = new Worker( '{}' );
166             worker.postMessage({{ python: {!r} }});
167             return new Promise((res, rej) => {{
168                 worker.onerror = e => rej(e);
169                 worker.onmessage = e => {{
170                     if (e.data.results) {{
171                        res(e.data.results);
172                     }} else {{
173                        rej(e.data.error);
174                     }}
175                 }};
176             }})
177             """.format(
178                 f"http://{self.server_hostname}:{self.server_port}/webworker_dev.js",
179                 code,
180             )
181         )
182 
183     def load_package(self, packages):
184         self.run_js("await pyodide.loadPackage({!r})".format(packages))
185 
186     @property
187     def urls(self):
188         for handle in self.driver.window_handles:
189             self.driver.switch_to.window(handle)
190             yield self.driver.current_url
191 
192 
193 class FirefoxWrapper(SeleniumWrapper):
194 
195     browser = "firefox"
196 
197     def get_driver(self):
198         from selenium.webdriver import Firefox
199         from selenium.webdriver.firefox.options import Options
200 
201         options = Options()
202         options.add_argument("-headless")
203 
204         return Firefox(executable_path="geckodriver", options=options)
205 
206 
207 class ChromeWrapper(SeleniumWrapper):
208 
209     browser = "chrome"
210 
211     def get_driver(self):
212         from selenium.webdriver import Chrome
213         from selenium.webdriver.chrome.options import Options
214 
215         options = Options()
216         options.add_argument("--headless")
217         options.add_argument("--no-sandbox")
218 
219         return Chrome(options=options)
220 
221 
222 if pytest is not None:
223 
224     @pytest.fixture(params=["firefox", "chrome"])
225     def selenium_standalone(request, web_server_main):
226         server_hostname, server_port, server_log = web_server_main
227         if request.param == "firefox":
228             cls = FirefoxWrapper
229         elif request.param == "chrome":
230             cls = ChromeWrapper
231         selenium = cls(
232             build_dir=request.config.option.build_dir,
233             server_port=server_port,
234             server_hostname=server_hostname,
235             server_log=server_log,
236         )
237         try:
238             yield selenium
239         finally:
240             print(selenium.logs)
241             selenium.driver.quit()
242 
243     @pytest.fixture(params=["firefox", "chrome"], scope="module")
244     def _selenium_cached(request, web_server_main):
245         # Cached selenium instance. This is a copy-paste of
246         # selenium_standalone to avoid fixture scope issues
247         server_hostname, server_port, server_log = web_server_main
248         if request.param == "firefox":
249             cls = FirefoxWrapper
250         elif request.param == "chrome":
251             cls = ChromeWrapper
252         selenium = cls(
253             build_dir=request.config.option.build_dir,
254             server_port=server_port,
255             server_hostname=server_hostname,
256             server_log=server_log,
257         )
258         try:
259             yield selenium
260         finally:
261             selenium.driver.quit()
262 
263     @pytest.fixture
264     def selenium(_selenium_cached):
265         # selenium instance cached at the module level
266         try:
267             _selenium_cached.clean_logs()
268             yield _selenium_cached
269         finally:
270             print(_selenium_cached.logs)
271 
272 
273 @pytest.fixture(scope="session")
274 def web_server_main(request):
275     """Web server that serves files in the build/ directory"""
276     with spawn_web_server(request.config.option.build_dir) as output:
277         yield output
278 
279 
280 @pytest.fixture(scope="session")
281 def web_server_secondary(request):
282     """Secondary web server that serves files build/ directory"""
283     with spawn_web_server(request.config.option.build_dir) as output:
284         yield output
285 
286 
287 @pytest.fixture(scope="session")
288 def web_server_tst_data(request):
289     """Web server that serves files in the src/tests/data/ directory"""
290     with spawn_web_server(TEST_PATH / "data") as output:
291         yield output
292 
293 
294 @contextlib.contextmanager
295 def spawn_web_server(build_dir=None):
296 
297     if build_dir is None:
298         build_dir = BUILD_PATH
299 
300     tmp_dir = tempfile.mkdtemp()
301     log_path = pathlib.Path(tmp_dir) / "http-server.log"
302     q = multiprocessing.Queue()
303     p = multiprocessing.Process(target=run_web_server, args=(q, log_path, build_dir))
304 
305     try:
306         p.start()
307         port = q.get()
308         hostname = "127.0.0.1"
309 
310         print(
311             f"Spawning webserver at http://{hostname}:{port} "
312             f"(see logs in {log_path})"
313         )
314         yield hostname, port, log_path
315     finally:
316         q.put("TERMINATE")
317         p.join()
318         shutil.rmtree(tmp_dir)
319 
320 
321 def run_web_server(q, log_filepath, build_dir):
322     """Start the HTTP web server
323 
324     Parameters
325     ----------
326     q : Queue
327       communication queue
328     log_path : pathlib.Path
329       path to the file where to store the logs
330     """
331     import http.server
332     import socketserver
333 
334     os.chdir(build_dir)
335 
336     log_fh = log_filepath.open("w", buffering=1)
337     sys.stdout = log_fh
338     sys.stderr = log_fh
339 
340     test_prefix = "/src/tests/"
341 
342     class Handler(http.server.SimpleHTTPRequestHandler):
343         def log_message(self, format_, *args):
344             print(
345                 "[%s] source: %s:%s - %s"
346                 % (self.log_date_time_string(), *self.client_address, format_ % args)
347             )
348 
349         def end_headers(self):
350             # Enable Cross-Origin Resource Sharing (CORS)
351             self.send_header("Access-Control-Allow-Origin", "*")
352             super().end_headers()
353 
354     with socketserver.TCPServer(("", 0), Handler) as httpd:
355         host, port = httpd.server_address
356         print(f"Starting webserver at http://{host}:{port}")
357         httpd.server_name = "test-server"
358         httpd.server_port = port
359         q.put(port)
360 
361         def service_actions():
362             try:
363                 if q.get(False) == "TERMINATE":
364                     print("Stopping server...")
365                     sys.exit(0)
366             except queue.Empty:
367                 pass
368 
369         httpd.service_actions = service_actions
370         httpd.serve_forever()
371 
372 
373 if (
374     __name__ == "__main__"
375     and multiprocessing.current_process().name == "MainProcess"
376     and not hasattr(sys, "_pytest_session")
377 ):
378     with spawn_web_server():
379         # run forever
380         while True:
381             time.sleep(1)
382
```
--- 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/conftest.py b/conftest.py
--- a/conftest.py
+++ b/conftest.py
@@ -98,8 +98,8 @@
         return self.run_js(
             f"""
             let result = pyodide.runPython({code!r});
-            if(result && result.deepCopyToJavascript){{
-                let converted_result = result.deepCopyToJavascript();
+            if(result && result.toJs){{
+                let converted_result = result.toJs();
                 result.destroy();
                 return converted_result;
             }}
@@ -111,8 +111,8 @@
         return self.run_js(
             f"""
             let result = await pyodide.runPythonAsync({code!r});
-            if(result && result.deepCopyToJavascript){{
-                let converted_result = result.deepCopyToJavascript();
+            if(result && result.toJs){{
+                let converted_result = result.toJs();
                 result.destroy();
                 return converted_result;
             }}

{"golden_diff": "diff --git a/conftest.py b/conftest.py\n--- a/conftest.py\n+++ b/conftest.py\n@@ -98,8 +98,8 @@\n         return self.run_js(\n             f\"\"\"\n             let result = pyodide.runPython({code!r});\n-            if(result && result.deepCopyToJavascript){{\n-                let converted_result = result.deepCopyToJavascript();\n+            if(result && result.toJs){{\n+                let converted_result = result.toJs();\n                 result.destroy();\n                 return converted_result;\n             }}\n@@ -111,8 +111,8 @@\n         return self.run_js(\n             f\"\"\"\n             let result = await pyodide.runPythonAsync({code!r});\n-            if(result && result.deepCopyToJavascript){{\n-                let converted_result = result.deepCopyToJavascript();\n+            if(result && result.toJs){{\n+                let converted_result = result.toJs();\n                 result.destroy();\n                 return converted_result;\n             }}\n", "issue": "Minor fix: add parameter for shallowCopyToJavascript \nFollow #1202 \r\n\r\n\n", "before_files": [{"content": "\"\"\"\nVarious common utilities for testing.\n\"\"\"\n\nimport contextlib\nimport multiprocessing\nimport textwrap\nimport tempfile\nimport time\nimport os\nimport pathlib\nimport queue\nimport sys\nimport shutil\n\nROOT_PATH = pathlib.Path(__file__).parents[0].resolve()\nTEST_PATH = ROOT_PATH / \"src\" / \"tests\"\nBUILD_PATH = ROOT_PATH / \"build\"\n\nsys.path.append(str(ROOT_PATH))\n\nfrom pyodide_build._fixes import _selenium_is_connectable  # noqa: E402\nimport selenium.webdriver.common.utils  # noqa: E402\n\n# XXX: Temporary fix for ConnectionError in selenium\n\nselenium.webdriver.common.utils.is_connectable = _selenium_is_connectable\n\ntry:\n    import pytest\n\n    def pytest_addoption(parser):\n        group = parser.getgroup(\"general\")\n        group.addoption(\n            \"--build-dir\",\n            action=\"store\",\n            default=BUILD_PATH,\n            help=\"Path to the build directory\",\n        )\n        group.addoption(\n            \"--run-xfail\",\n            action=\"store_true\",\n            help=\"If provided, tests marked as xfail will be run\",\n        )\n\n\nexcept ImportError:\n    pytest = None  # type: ignore\n\n\nclass JavascriptException(Exception):\n    def __init__(self, msg, stack):\n        self.msg = msg\n        self.stack = stack\n        # In chrome the stack contains the message\n        if self.stack and self.stack.startswith(self.msg):\n            self.msg = \"\"\n\n    def __str__(self):\n        return \"\\n\\n\".join(x for x in [self.msg, self.stack] if x)\n\n\nclass SeleniumWrapper:\n    JavascriptException = JavascriptException\n\n    def __init__(\n        self, server_port, server_hostname=\"127.0.0.1\", server_log=None, build_dir=None\n    ):\n        if build_dir is None:\n            build_dir = BUILD_PATH\n\n        self.driver = self.get_driver()\n        self.server_port = server_port\n        self.server_hostname = server_hostname\n        self.server_log = server_log\n\n        if not (pathlib.Path(build_dir) / \"test.html\").exists():\n            # selenium does not expose HTTP response codes\n            raise ValueError(\n                f\"{(build_dir / 'test.html').resolve()} \" f\"does not exist!\"\n            )\n        self.driver.get(f\"http://{server_hostname}:{server_port}/test.html\")\n        self.run_js(\"Error.stackTraceLimit = Infinity\")\n        self.run_js(\"await languagePluginLoader\")\n\n    @property\n    def logs(self):\n        logs = self.driver.execute_script(\"return window.logs\")\n        if logs is not None:\n            return \"\\n\".join(str(x) for x in logs)\n        else:\n            return \"\"\n\n    def clean_logs(self):\n        self.driver.execute_script(\"window.logs = []\")\n\n    def run(self, code):\n        return self.run_js(\n            f\"\"\"\n            let result = pyodide.runPython({code!r});\n            if(result && result.deepCopyToJavascript){{\n                let converted_result = result.deepCopyToJavascript();\n                result.destroy();\n                return converted_result;\n            }}\n            return result;\n            \"\"\"\n        )\n\n    def run_async(self, code):\n        return self.run_js(\n            f\"\"\"\n            let result = await pyodide.runPythonAsync({code!r});\n            if(result && result.deepCopyToJavascript){{\n                let converted_result = result.deepCopyToJavascript();\n                result.destroy();\n                return converted_result;\n            }}\n            return result;\n            \"\"\"\n        )\n\n    def run_js(self, code):\n        if isinstance(code, str) and code.startswith(\"\\n\"):\n            # we have a multiline string, fix indentation\n            code = textwrap.dedent(code)\n\n        wrapper = \"\"\"\n            let cb = arguments[arguments.length - 1];\n            let run = async () => { %s }\n            (async () => {\n                try {\n                    let result = await run();\n                    if(pyodide && pyodide._module && pyodide._module._PyErr_Occurred()){\n                        try {\n                            pyodide._module._pythonexc2js();\n                        } catch(e){\n                            console.error(`Python exited with error flag set! Error was:\\n{e.message}`);\n                            // Don't put original error message in new one: we want\n                            // \"pytest.raises(xxx, match=msg)\" to fail\n                            throw new Error(`Python exited with error flag set!`);\n                        }\n                    }\n                    cb([0, result]);\n                } catch (e) {\n                    cb([1, e.toString(), e.stack]);\n                }\n            })()\n        \"\"\"\n\n        retval = self.driver.execute_async_script(wrapper % code)\n\n        if retval[0] == 0:\n            return retval[1]\n        else:\n            raise JavascriptException(retval[1], retval[2])\n\n    def run_webworker(self, code):\n        if isinstance(code, str) and code.startswith(\"\\n\"):\n            # we have a multiline string, fix indentation\n            code = textwrap.dedent(code)\n\n        return self.run_js(\n            \"\"\"\n            let worker = new Worker( '{}' );\n            worker.postMessage({{ python: {!r} }});\n            return new Promise((res, rej) => {{\n                worker.onerror = e => rej(e);\n                worker.onmessage = e => {{\n                    if (e.data.results) {{\n                       res(e.data.results);\n                    }} else {{\n                       rej(e.data.error);\n                    }}\n                }};\n            }})\n            \"\"\".format(\n                f\"http://{self.server_hostname}:{self.server_port}/webworker_dev.js\",\n                code,\n            )\n        )\n\n    def load_package(self, packages):\n        self.run_js(\"await pyodide.loadPackage({!r})\".format(packages))\n\n    @property\n    def urls(self):\n        for handle in self.driver.window_handles:\n            self.driver.switch_to.window(handle)\n            yield self.driver.current_url\n\n\nclass FirefoxWrapper(SeleniumWrapper):\n\n    browser = \"firefox\"\n\n    def get_driver(self):\n        from selenium.webdriver import Firefox\n        from selenium.webdriver.firefox.options import Options\n\n        options = Options()\n        options.add_argument(\"-headless\")\n\n        return Firefox(executable_path=\"geckodriver\", options=options)\n\n\nclass ChromeWrapper(SeleniumWrapper):\n\n    browser = \"chrome\"\n\n    def get_driver(self):\n        from selenium.webdriver import Chrome\n        from selenium.webdriver.chrome.options import Options\n\n        options = Options()\n        options.add_argument(\"--headless\")\n        options.add_argument(\"--no-sandbox\")\n\n        return Chrome(options=options)\n\n\nif pytest is not None:\n\n    @pytest.fixture(params=[\"firefox\", \"chrome\"])\n    def selenium_standalone(request, web_server_main):\n        server_hostname, server_port, server_log = web_server_main\n        if request.param == \"firefox\":\n            cls = FirefoxWrapper\n        elif request.param == \"chrome\":\n            cls = ChromeWrapper\n        selenium = cls(\n            build_dir=request.config.option.build_dir,\n            server_port=server_port,\n            server_hostname=server_hostname,\n            server_log=server_log,\n        )\n        try:\n            yield selenium\n        finally:\n            print(selenium.logs)\n            selenium.driver.quit()\n\n    @pytest.fixture(params=[\"firefox\", \"chrome\"], scope=\"module\")\n    def _selenium_cached(request, web_server_main):\n        # Cached selenium instance. This is a copy-paste of\n        # selenium_standalone to avoid fixture scope issues\n        server_hostname, server_port, server_log = web_server_main\n        if request.param == \"firefox\":\n            cls = FirefoxWrapper\n        elif request.param == \"chrome\":\n            cls = ChromeWrapper\n        selenium = cls(\n            build_dir=request.config.option.build_dir,\n            server_port=server_port,\n            server_hostname=server_hostname,\n            server_log=server_log,\n        )\n        try:\n            yield selenium\n        finally:\n            selenium.driver.quit()\n\n    @pytest.fixture\n    def selenium(_selenium_cached):\n        # selenium instance cached at the module level\n        try:\n            _selenium_cached.clean_logs()\n            yield _selenium_cached\n        finally:\n            print(_selenium_cached.logs)\n\n\n@pytest.fixture(scope=\"session\")\ndef web_server_main(request):\n    \"\"\"Web server that serves files in the build/ directory\"\"\"\n    with spawn_web_server(request.config.option.build_dir) as output:\n        yield output\n\n\n@pytest.fixture(scope=\"session\")\ndef web_server_secondary(request):\n    \"\"\"Secondary web server that serves files build/ directory\"\"\"\n    with spawn_web_server(request.config.option.build_dir) as output:\n        yield output\n\n\n@pytest.fixture(scope=\"session\")\ndef web_server_tst_data(request):\n    \"\"\"Web server that serves files in the src/tests/data/ directory\"\"\"\n    with spawn_web_server(TEST_PATH / \"data\") as output:\n        yield output\n\n\n@contextlib.contextmanager\ndef spawn_web_server(build_dir=None):\n\n    if build_dir is None:\n        build_dir = BUILD_PATH\n\n    tmp_dir = tempfile.mkdtemp()\n    log_path = pathlib.Path(tmp_dir) / \"http-server.log\"\n    q = multiprocessing.Queue()\n    p = multiprocessing.Process(target=run_web_server, args=(q, log_path, build_dir))\n\n    try:\n        p.start()\n        port = q.get()\n        hostname = \"127.0.0.1\"\n\n        print(\n            f\"Spawning webserver at http://{hostname}:{port} \"\n            f\"(see logs in {log_path})\"\n        )\n        yield hostname, port, log_path\n    finally:\n        q.put(\"TERMINATE\")\n        p.join()\n        shutil.rmtree(tmp_dir)\n\n\ndef run_web_server(q, log_filepath, build_dir):\n    \"\"\"Start the HTTP web server\n\n    Parameters\n    ----------\n    q : Queue\n      communication queue\n    log_path : pathlib.Path\n      path to the file where to store the logs\n    \"\"\"\n    import http.server\n    import socketserver\n\n    os.chdir(build_dir)\n\n    log_fh = log_filepath.open(\"w\", buffering=1)\n    sys.stdout = log_fh\n    sys.stderr = log_fh\n\n    test_prefix = \"/src/tests/\"\n\n    class Handler(http.server.SimpleHTTPRequestHandler):\n        def log_message(self, format_, *args):\n            print(\n                \"[%s] source: %s:%s - %s\"\n                % (self.log_date_time_string(), *self.client_address, format_ % args)\n            )\n\n        def end_headers(self):\n            # Enable Cross-Origin Resource Sharing (CORS)\n            self.send_header(\"Access-Control-Allow-Origin\", \"*\")\n            super().end_headers()\n\n    with socketserver.TCPServer((\"\", 0), Handler) as httpd:\n        host, port = httpd.server_address\n        print(f\"Starting webserver at http://{host}:{port}\")\n        httpd.server_name = \"test-server\"\n        httpd.server_port = port\n        q.put(port)\n\n        def service_actions():\n            try:\n                if q.get(False) == \"TERMINATE\":\n                    print(\"Stopping server...\")\n                    sys.exit(0)\n            except queue.Empty:\n                pass\n\n        httpd.service_actions = service_actions\n        httpd.serve_forever()\n\n\nif (\n    __name__ == \"__main__\"\n    and multiprocessing.current_process().name == \"MainProcess\"\n    and not hasattr(sys, \"_pytest_session\")\n):\n    with spawn_web_server():\n        # run forever\n        while True:\n            time.sleep(1)\n", "path": "conftest.py"}], "after_files": [{"content": "\"\"\"\nVarious common utilities for testing.\n\"\"\"\n\nimport contextlib\nimport multiprocessing\nimport textwrap\nimport tempfile\nimport time\nimport os\nimport pathlib\nimport queue\nimport sys\nimport shutil\n\nROOT_PATH = pathlib.Path(__file__).parents[0].resolve()\nTEST_PATH = ROOT_PATH / \"src\" / \"tests\"\nBUILD_PATH = ROOT_PATH / \"build\"\n\nsys.path.append(str(ROOT_PATH))\n\nfrom pyodide_build._fixes import _selenium_is_connectable  # noqa: E402\nimport selenium.webdriver.common.utils  # noqa: E402\n\n# XXX: Temporary fix for ConnectionError in selenium\n\nselenium.webdriver.common.utils.is_connectable = _selenium_is_connectable\n\ntry:\n    import pytest\n\n    def pytest_addoption(parser):\n        group = parser.getgroup(\"general\")\n        group.addoption(\n            \"--build-dir\",\n            action=\"store\",\n            default=BUILD_PATH,\n            help=\"Path to the build directory\",\n        )\n        group.addoption(\n            \"--run-xfail\",\n            action=\"store_true\",\n            help=\"If provided, tests marked as xfail will be run\",\n        )\n\n\nexcept ImportError:\n    pytest = None  # type: ignore\n\n\nclass JavascriptException(Exception):\n    def __init__(self, msg, stack):\n        self.msg = msg\n        self.stack = stack\n        # In chrome the stack contains the message\n        if self.stack and self.stack.startswith(self.msg):\n            self.msg = \"\"\n\n    def __str__(self):\n        return \"\\n\\n\".join(x for x in [self.msg, self.stack] if x)\n\n\nclass SeleniumWrapper:\n    JavascriptException = JavascriptException\n\n    def __init__(\n        self, server_port, server_hostname=\"127.0.0.1\", server_log=None, build_dir=None\n    ):\n        if build_dir is None:\n            build_dir = BUILD_PATH\n\n        self.driver = self.get_driver()\n        self.server_port = server_port\n        self.server_hostname = server_hostname\n        self.server_log = server_log\n\n        if not (pathlib.Path(build_dir) / \"test.html\").exists():\n            # selenium does not expose HTTP response codes\n            raise ValueError(\n                f\"{(build_dir / 'test.html').resolve()} \" f\"does not exist!\"\n            )\n        self.driver.get(f\"http://{server_hostname}:{server_port}/test.html\")\n        self.run_js(\"Error.stackTraceLimit = Infinity\")\n        self.run_js(\"await languagePluginLoader\")\n\n    @property\n    def logs(self):\n        logs = self.driver.execute_script(\"return window.logs\")\n        if logs is not None:\n            return \"\\n\".join(str(x) for x in logs)\n        else:\n            return \"\"\n\n    def clean_logs(self):\n        self.driver.execute_script(\"window.logs = []\")\n\n    def run(self, code):\n        return self.run_js(\n            f\"\"\"\n            let result = pyodide.runPython({code!r});\n            if(result && result.toJs){{\n                let converted_result = result.toJs();\n                result.destroy();\n                return converted_result;\n            }}\n            return result;\n            \"\"\"\n        )\n\n    def run_async(self, code):\n        return self.run_js(\n            f\"\"\"\n            let result = await pyodide.runPythonAsync({code!r});\n            if(result && result.toJs){{\n                let converted_result = result.toJs();\n                result.destroy();\n                return converted_result;\n            }}\n            return result;\n            \"\"\"\n        )\n\n    def run_js(self, code):\n        if isinstance(code, str) and code.startswith(\"\\n\"):\n            # we have a multiline string, fix indentation\n            code = textwrap.dedent(code)\n\n        wrapper = \"\"\"\n            let cb = arguments[arguments.length - 1];\n            let run = async () => { %s }\n            (async () => {\n                try {\n                    let result = await run();\n                    if(pyodide && pyodide._module && pyodide._module._PyErr_Occurred()){\n                        try {\n                            pyodide._module._pythonexc2js();\n                        } catch(e){\n                            console.error(`Python exited with error flag set! Error was:\\n{e.message}`);\n                            // Don't put original error message in new one: we want\n                            // \"pytest.raises(xxx, match=msg)\" to fail\n                            throw new Error(`Python exited with error flag set!`);\n                        }\n                    }\n                    cb([0, result]);\n                } catch (e) {\n                    cb([1, e.toString(), e.stack]);\n                }\n            })()\n        \"\"\"\n\n        retval = self.driver.execute_async_script(wrapper % code)\n\n        if retval[0] == 0:\n            return retval[1]\n        else:\n            raise JavascriptException(retval[1], retval[2])\n\n    def run_webworker(self, code):\n        if isinstance(code, str) and code.startswith(\"\\n\"):\n            # we have a multiline string, fix indentation\n            code = textwrap.dedent(code)\n\n        return self.run_js(\n            \"\"\"\n            let worker = new Worker( '{}' );\n            worker.postMessage({{ python: {!r} }});\n            return new Promise((res, rej) => {{\n                worker.onerror = e => rej(e);\n                worker.onmessage = e => {{\n                    if (e.data.results) {{\n                       res(e.data.results);\n                    }} else {{\n                       rej(e.data.error);\n                    }}\n                }};\n            }})\n            \"\"\".format(\n                f\"http://{self.server_hostname}:{self.server_port}/webworker_dev.js\",\n                code,\n            )\n        )\n\n    def load_package(self, packages):\n        self.run_js(\"await pyodide.loadPackage({!r})\".format(packages))\n\n    @property\n    def urls(self):\n        for handle in self.driver.window_handles:\n            self.driver.switch_to.window(handle)\n            yield self.driver.current_url\n\n\nclass FirefoxWrapper(SeleniumWrapper):\n\n    browser = \"firefox\"\n\n    def get_driver(self):\n        from selenium.webdriver import Firefox\n        from selenium.webdriver.firefox.options import Options\n\n        options = Options()\n        options.add_argument(\"-headless\")\n\n        return Firefox(executable_path=\"geckodriver\", options=options)\n\n\nclass ChromeWrapper(SeleniumWrapper):\n\n    browser = \"chrome\"\n\n    def get_driver(self):\n        from selenium.webdriver import Chrome\n        from selenium.webdriver.chrome.options import Options\n\n        options = Options()\n        options.add_argument(\"--headless\")\n        options.add_argument(\"--no-sandbox\")\n\n        return Chrome(options=options)\n\n\nif pytest is not None:\n\n    @pytest.fixture(params=[\"firefox\", \"chrome\"])\n    def selenium_standalone(request, web_server_main):\n        server_hostname, server_port, server_log = web_server_main\n        if request.param == \"firefox\":\n            cls = FirefoxWrapper\n        elif request.param == \"chrome\":\n            cls = ChromeWrapper\n        selenium = cls(\n            build_dir=request.config.option.build_dir,\n            server_port=server_port,\n            server_hostname=server_hostname,\n            server_log=server_log,\n        )\n        try:\n            yield selenium\n        finally:\n            print(selenium.logs)\n            selenium.driver.quit()\n\n    @pytest.fixture(params=[\"firefox\", \"chrome\"], scope=\"module\")\n    def _selenium_cached(request, web_server_main):\n        # Cached selenium instance. This is a copy-paste of\n        # selenium_standalone to avoid fixture scope issues\n        server_hostname, server_port, server_log = web_server_main\n        if request.param == \"firefox\":\n            cls = FirefoxWrapper\n        elif request.param == \"chrome\":\n            cls = ChromeWrapper\n        selenium = cls(\n            build_dir=request.config.option.build_dir,\n            server_port=server_port,\n            server_hostname=server_hostname,\n            server_log=server_log,\n        )\n        try:\n            yield selenium\n        finally:\n            selenium.driver.quit()\n\n    @pytest.fixture\n    def selenium(_selenium_cached):\n        # selenium instance cached at the module level\n        try:\n            _selenium_cached.clean_logs()\n            yield _selenium_cached\n        finally:\n            print(_selenium_cached.logs)\n\n\n@pytest.fixture(scope=\"session\")\ndef web_server_main(request):\n    \"\"\"Web server that serves files in the build/ directory\"\"\"\n    with spawn_web_server(request.config.option.build_dir) as output:\n        yield output\n\n\n@pytest.fixture(scope=\"session\")\ndef web_server_secondary(request):\n    \"\"\"Secondary web server that serves files build/ directory\"\"\"\n    with spawn_web_server(request.config.option.build_dir) as output:\n        yield output\n\n\n@pytest.fixture(scope=\"session\")\ndef web_server_tst_data(request):\n    \"\"\"Web server that serves files in the src/tests/data/ directory\"\"\"\n    with spawn_web_server(TEST_PATH / \"data\") as output:\n        yield output\n\n\n@contextlib.contextmanager\ndef spawn_web_server(build_dir=None):\n\n    if build_dir is None:\n        build_dir = BUILD_PATH\n\n    tmp_dir = tempfile.mkdtemp()\n    log_path = pathlib.Path(tmp_dir) / \"http-server.log\"\n    q = multiprocessing.Queue()\n    p = multiprocessing.Process(target=run_web_server, args=(q, log_path, build_dir))\n\n    try:\n        p.start()\n        port = q.get()\n        hostname = \"127.0.0.1\"\n\n        print(\n            f\"Spawning webserver at http://{hostname}:{port} \"\n            f\"(see logs in {log_path})\"\n        )\n        yield hostname, port, log_path\n    finally:\n        q.put(\"TERMINATE\")\n        p.join()\n        shutil.rmtree(tmp_dir)\n\n\ndef run_web_server(q, log_filepath, build_dir):\n    \"\"\"Start the HTTP web server\n\n    Parameters\n    ----------\n    q : Queue\n      communication queue\n    log_path : pathlib.Path\n      path to the file where to store the logs\n    \"\"\"\n    import http.server\n    import socketserver\n\n    os.chdir(build_dir)\n\n    log_fh = log_filepath.open(\"w\", buffering=1)\n    sys.stdout = log_fh\n    sys.stderr = log_fh\n\n    test_prefix = \"/src/tests/\"\n\n    class Handler(http.server.SimpleHTTPRequestHandler):\n        def log_message(self, format_, *args):\n            print(\n                \"[%s] source: %s:%s - %s\"\n                % (self.log_date_time_string(), *self.client_address, format_ % args)\n            )\n\n        def end_headers(self):\n            # Enable Cross-Origin Resource Sharing (CORS)\n            self.send_header(\"Access-Control-Allow-Origin\", \"*\")\n            super().end_headers()\n\n    with socketserver.TCPServer((\"\", 0), Handler) as httpd:\n        host, port = httpd.server_address\n        print(f\"Starting webserver at http://{host}:{port}\")\n        httpd.server_name = \"test-server\"\n        httpd.server_port = port\n        q.put(port)\n\n        def service_actions():\n            try:\n                if q.get(False) == \"TERMINATE\":\n                    print(\"Stopping server...\")\n                    sys.exit(0)\n            except queue.Empty:\n                pass\n\n        httpd.service_actions = service_actions\n        httpd.serve_forever()\n\n\nif (\n    __name__ == \"__main__\"\n    and multiprocessing.current_process().name == \"MainProcess\"\n    and not hasattr(sys, \"_pytest_session\")\n):\n    with spawn_web_server():\n        # run forever\n        while True:\n            time.sleep(1)\n", "path": "conftest.py"}]}