Execute a parser script's parse() function via Node.js. The frame_input is serialised as JSON and injected directly into the generated wrapper snippet, so both string and array inputs work without any type-specific encoding gymnastics. Args: script_name: Filename insid
(script_name: str, frame_input)
| 18 | |
| 19 | |
| 20 | def run_parser(script_name: str, frame_input) -> list: |
| 21 | """ |
| 22 | Execute a parser script's parse() function via Node.js. |
| 23 | |
| 24 | The frame_input is serialised as JSON and injected directly into the |
| 25 | generated wrapper snippet, so both string and array inputs work without |
| 26 | any type-specific encoding gymnastics. |
| 27 | |
| 28 | Args: |
| 29 | script_name: Filename inside app/rcc/scripts/parser/js/ (e.g. "comma_separated.js") |
| 30 | frame_input: String or list to pass as the frame argument to parse() |
| 31 | |
| 32 | Returns: |
| 33 | The value returned by parse(), deserialised from JSON. |
| 34 | |
| 35 | Raises: |
| 36 | RuntimeError: If Node.js exits with a non-zero status. |
| 37 | json.JSONDecodeError: If the output cannot be parsed. |
| 38 | """ |
| 39 | script_path = SCRIPTS_DIR / script_name |
| 40 | script_source = script_path.read_text(encoding="utf-8") |
| 41 | |
| 42 | runner = f"""{script_source} |
| 43 | var _result = parse({json.dumps(frame_input)}); |
| 44 | console.log(JSON.stringify(_result)); |
| 45 | """ |
| 46 | # Feed the program over stdin ("node -") instead of "node -e <src>": passing a |
| 47 | # large script as an argv string trips the Windows 32767-char command-line limit |
| 48 | # (subprocess surfaces it as "environment variable is longer than 32767"). |
| 49 | # |
| 50 | # Pin encoding="utf-8": the parser scripts carry non-ASCII characters (e.g. the |
| 51 | # U+2192 arrow in comments). Without this, text mode decodes/encodes with the |
| 52 | # locale codec, which is cp1252 on the Windows runners and raises |
| 53 | # UnicodeEncodeError when the script source crosses the subprocess boundary. |
| 54 | result = subprocess.run( |
| 55 | ["node", "-"], |
| 56 | input=runner, |
| 57 | capture_output=True, |
| 58 | text=True, |
| 59 | encoding="utf-8", |
| 60 | timeout=30, |
| 61 | ) |
| 62 | |
| 63 | if result.returncode != 0: |
| 64 | raise RuntimeError( |
| 65 | f"Node.js exited {result.returncode} for {script_name}:\n{result.stderr}" |
| 66 | ) |
| 67 | |
| 68 | # Take the last non-empty line; earlier lines may be console.log() debug output |
| 69 | last_line = result.stdout.strip().splitlines()[-1] |
| 70 | return json.loads(last_line) |
| 71 | |
| 72 | |
| 73 | @pytest.fixture |
no test coverage detected