Subject: bug#74738: 31.0.50; Freezes in Python-mode on some Python file when searching or scrolling

From: kobarity <[email-masked]>
Date: Sun, 15 Dec 2024 23:26:05 +0900

rehan malak wrote:
> thanks kobarity, with your patch, I confirm that :
> - I can open gef.py and scroll
> - I get the correct font-lock-string-face in the f-string after a "#" in the format

Thank you for your confirmation.

Stefan Monnier wrote:
> > The mechanism of the hang is still unclear, but I found the cause of
> > the font-lock malfunction: when using `up-list' to find the
> > corresponding closing brace for an opening brace in an f-string, if
> > the format specifier contains "#", the rest of the string is
> > considered a comment and the search fails.  Therefore, it can be
> > worked around by temporarily binding `parse-sexp-ignore-comments' to
> > nil, as in the attached patch.
> 
> Maybe a cleaner solution would be to locally use a different
> syntax-table (where # doesn't start a comment), but I'm not sure it's
> worth the trouble.  So I installed your patch into `master`, thank you.

Thank you.  I agree with you about the syntax-table.

> Maybe we should also temporarily setup a narrowing so `up-list` can't
> look past `send`.
> 
> BTW, beside removing the `font-lock-string-face` from the `face`
> property, `python--font-lock-f-strings` should probably also add
> a special face for the ":<FMT>" thingies found (optionally) at the end.
> 
> > Hangs on gef.py can also be avoided with this patch, but it assumes
> > that the braces are properly closed.  If you remove the closing braces
> > of expressions containing "#", it will eventually hang.
> 
> Hmm...

Narrowing might improve it, but what about binding
`forward-sexp-function' to nil as well, as in the attached patch?  It
seems to resolve the hang caused by the unbalanced braces in
f-strings.

1. wget https://raw.githubusercontent.com/hugsy/gef/refs/heads/main/gef.py
2. sed -i -e 's/\(:#\d*x\)}/\1/g' gef.py
3. emacs -Q gef.py
4. Scroll down the file.

I don't think `python-nav-forward-sexp' is necessary here, since the
inside of the braces should be an expression, not statements.
>From bb2c1a3e03416532fb97d6fa7ef276c5feebb356 Mon Sep 17 00:00:00 2001
From: kobarity <[email-masked]>
Date: Sun, 15 Dec 2024 23:02:51 +0900
Subject: [PATCH] Fix hangs caused by unbalanced braces in Python f-strings

* lisp/progmodes/python.el (python--font-lock-f-strings):
Temporarily bind forward-sexp-function to nil when calling
'up-list'.  (Bug#74738)
---
 lisp/progmodes/python.el | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 5729b12573f..493eab5e611 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -623,7 +623,8 @@ python--font-lock-f-strings
               (forward-char 1)          ;Just skip over {{
             (let ((beg (match-beginning 0))
                   (end (condition-case nil
-                           (let ((parse-sexp-ignore-comments))
+                           (let ((forward-sexp-function)
+                                 (parse-sexp-ignore-comments))
                              (up-list 1)
                              (min send (point)))
                          (scan-error send))))
-- 
2.43.0