|
197 | 197 |
|
198 | 198 | import re |
199 | 199 | import os |
| 200 | +import pexpect |
200 | 201 |
|
201 | 202 | import sage.interfaces.abc |
202 | 203 |
|
@@ -353,6 +354,16 @@ def _start(self): |
353 | 354 | self.eval(FRICAS_LINENUMBER_OFF_CODE, reformat=False) |
354 | 355 | for line in FRICAS_HELPER_CODE: |
355 | 356 | self.eval(line, reformat=False) |
| 357 | + # FriCAS compiled with GCL may output function declaration messages |
| 358 | + # asynchronously after the prompt appears. We need to consume any |
| 359 | + # such buffered output to keep the expect interface synchronized. |
| 360 | + # See https://github.com/sagemath/sage/issues/40569 |
| 361 | + E = self._expect |
| 362 | + for _ in range(3): |
| 363 | + try: |
| 364 | + E.expect(self._prompt, timeout=0.2) |
| 365 | + except pexpect.TIMEOUT: |
| 366 | + break |
356 | 367 | # register translations between SymbolicRing and FriCAS Expression |
357 | 368 | self._register_symbols() |
358 | 369 |
|
@@ -568,6 +579,11 @@ def _check_errors(self, line, output): |
568 | 579 | m = re.search(r"\|startKeyedMsg\|\n(.*)\n\|endOfKeyedMsg\|", |
569 | 580 | output, flags=re.DOTALL) |
570 | 581 | if m: |
| 582 | + # Ignore informational messages about function declarations |
| 583 | + # being added to workspace (these are not errors) |
| 584 | + msg_content = m.group(1) |
| 585 | + if "has been added to" in msg_content and "workspace" in msg_content: |
| 586 | + return |
571 | 587 | replacements = [('|startKeyedMsg|\n', ''), |
572 | 588 | ('|endOfKeyedMsg|', '')] |
573 | 589 | for old, new in replacements: |
@@ -896,6 +912,33 @@ def _repr_(self): |
896 | 912 | """ |
897 | 913 | return "FriCAS" |
898 | 914 |
|
| 915 | + def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if_needed=True): |
| 916 | + """ |
| 917 | + Evaluate a single line of code. |
| 918 | +
|
| 919 | + This method overrides :meth:`sage.interfaces.expect.Expect._eval_line` |
| 920 | + to handle FriCAS compiled with GCL, which may output a double prompt |
| 921 | + after each command due to buffering issues. |
| 922 | +
|
| 923 | + See https://github.com/sagemath/sage/issues/40569 |
| 924 | +
|
| 925 | + TESTS:: |
| 926 | +
|
| 927 | + sage: fricas.eval('1+1') |
| 928 | + '\n 2\n' |
| 929 | + """ |
| 930 | + result = Expect._eval_line(self, line, allow_use_file=allow_use_file, |
| 931 | + wait_for_prompt=wait_for_prompt, |
| 932 | + restart_if_needed=restart_if_needed) |
| 933 | + # FriCAS compiled with GCL may send an extra prompt after each command. |
| 934 | + # We try to consume it to keep the interface synchronized. |
| 935 | + if wait_for_prompt and self._expect is not None: |
| 936 | + try: |
| 937 | + self._expect.expect(self._prompt, timeout=0.1) |
| 938 | + except pexpect.TIMEOUT: |
| 939 | + pass |
| 940 | + return result |
| 941 | + |
899 | 942 | def __reduce__(self): |
900 | 943 | """ |
901 | 944 | EXAMPLES:: |
|
0 commit comments