python_runner_wsh.py (3006B)
1 # This Source Code Form is subject to the terms of the Mozilla Public 2 # License, v. 2.0. If a copy of the MPL was not distributed with this 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5 """A pywebsocket3 handler which runs arbitrary Python code and returns the 6 result. 7 This is used to test OS specific accessibility APIs which can't be tested in JS. 8 It is intended to be called from JS browser tests. 9 """ 10 11 import math 12 import os 13 import sys 14 import traceback 15 16 from mod_pywebsocket import msgutil 17 from mozfile import json 18 19 20 def web_socket_do_extra_handshake(request): 21 pass 22 23 24 def web_socket_transfer_data(request): 25 def send(*args): 26 """Send a response to the client as a JSON array.""" 27 msgutil.send_message(request, json.dumps(args)) 28 29 cleanNamespace = {} 30 testDir = None 31 if sys.platform == "win32": 32 testDir = "windows" 33 elif sys.platform == "linux": 34 testDir = "atk" 35 if testDir: 36 sys.path.append( 37 os.path.join( 38 os.getcwd(), "browser", "accessible", "tests", "browser", testDir 39 ) 40 ) 41 try: 42 import a11y_setup 43 44 a11y_setup.setup() 45 cleanNamespace = a11y_setup.__dict__ 46 setupExc = None 47 except Exception: 48 setupExc = traceback.format_exc() 49 sys.path.pop() 50 51 def info(message): 52 """Log an info message.""" 53 send("info", str(message)) 54 55 cleanNamespace["info"] = info 56 namespace = cleanNamespace.copy() 57 58 # Keep handling messages until the WebSocket is closed. 59 while True: 60 code = msgutil.receive_message(request) 61 if not code: 62 return 63 if code == "__reset__": 64 namespace = cleanNamespace.copy() 65 send("return", None) 66 continue 67 68 if setupExc: 69 # a11y_setup failed. Report an exception immediately. 70 send("exception", setupExc) 71 continue 72 73 # Wrap the code in a function called run(). This allows the code to 74 # return a result by simply using the return statement. 75 if "\n" not in code and not code.lstrip().startswith("return "): 76 # Single line without return. Assume this is an expression. We use 77 # a lambda to return the result. 78 code = f"run = lambda: {code}" 79 else: 80 lines = ["def run():"] 81 # Indent each line inside the function. 82 lines.extend(f" {line}" for line in code.splitlines()) 83 code = "\n".join(lines) 84 try: 85 # Execute this Python code, which will define the run() function. 86 exec(code, namespace) 87 # Run the function we just defined. 88 ret = namespace["run"]() 89 if isinstance(ret, float) and math.isnan(ret): 90 # NaN can't be serialized by JSON. 91 ret = None 92 send("return", ret) 93 except Exception: 94 send("exception", traceback.format_exc())