* bug#74738: 31.0.50; Freezes in Python-mode on some Python file when searching or scrolling @ 2024-12-08 13:05 rehan malak 2024-12-08 17:37 ` Eli Zaretskii 0 siblings, 1 reply; 5+ messages in thread From: rehan malak @ 2024-12-08 13:05 UTC (permalink / raw) To: 74738 [-- Attachment #1: Type: text/plain, Size: 8963 bytes --] Hi, I can reproduce systematically this freeze dealing with a 10000lines Python file : wget https://raw.githubusercontent.com/hugsy/gef/refs/heads/main/gef.py emacs -Q gef.py PageDown several times or scrolling with the mouse Emacs freezes, Ctrl-g not working, CPU 100% It works also by searching : Ctrl-s show RET then Ctrl-s several times With a minimal .emacs : (set debug-on-error t) (set debug-on-quit t) and before scrolling M-x profiler-start RET then in an external terminal pkill -SIGUSR2 emacs I get the backtrace : Debugger entered--entering a function: * #f(compiled-function () #<bytecode -0x179dcd1db31182ae>)() syntax-ppss() python-syntax-context-type() python-nav-forward-block(-1) python-nav-backward-block() python-nav-beginning-of-block() python-nav-end-of-block() python-info-statement-ends-block-p() python-info-end-of-block-p() python-nav--forward-sexp(1 nil nil) python-nav-forward-sexp(1) forward-sexp(1) up-list(1) python--font-lock-f-strings(30419) font-lock-fontify-keywords-region(28914 30419 nil) font-lock-default-fontify-region(28914 30414 nil) font-lock-fontify-region(28914 30414) #f(compiled-function (fun) #<bytecode -0x8a96d65e1315e71>)(font-lock-fontify-region) run-hook-wrapped(#f(compiled-function (fun) #<bytecode -0x8a96d65e1315e71>) font-lock-fontify-region) jit-lock--run-functions(28914 30414) jit-lock-fontify-now(28914 30414) jit-lock-function(28914) and can look at profiler report M-x profiler-stop M-x profiler-report showing time spent in syntax-ppss 17770 88% - jit-lock-function 17770 88% - jit-lock-fontify-now 17770 88% - jit-lock--run-functions 17770 88% - run-hook-wrapped 17770 88% - #<byte-code-function FA0> 17770 88% - font-lock-fontify-region 17770 88% - font-lock-default-fontify-region 17690 87% - font-lock-fontify-keywords-region 17678 87% - python--font-lock-f-strings 17674 87% - up-list 17674 87% - forward-sexp 17674 87% - python-nav-forward-sexp 17674 87% - python-nav--forward-sexp 9253 45% - python-info-end-of-block-p 9245 45% - python-info-statement-ends-block-p 9221 45% - python-nav-end-of-block 8609 42% - python-nav-beginning-of-block 8557 42% - python-nav-backward-block 8557 42% - python-nav-forward-block 8005 39% - python-syntax-context-type 7997 39% - syntax-ppss 7097 35% parse-partial-sexp 753 3% + #<byte-code-function A23> 376 1% + python-nav-beginning-of-statement 132 0% re-search-backward 24 0% looking-at 28 0% current-indentation 8 0% + python-nav-beginning-of-statement 4 0% python-info-current-line-empty-p 4 0% + python-info-current-line-comment-p 604 2% + python-nav-end-of-statement 4 0% + python-info-current-line-comment-p 4 0% current-indentation 24 0% + python-nav-end-of-statement 8 0% + python-info-end-of-statement-p 8178 40% - python-info-statement-ends-block-p 8150 40% - python-nav-end-of-block 7754 38% - python-nav-beginning-of-block 7738 38% - python-nav-backward-block 7734 38% - python-nav-forward-block 7178 35% - python-syntax-context-type 7174 35% - syntax-ppss 6978 34% parse-partial-sexp 8 0% make-closure 8 0% #<byte-code-function B1E> 4 0% syntax-table 4 0% syntax-ppss--update-stats 412 2% + python-nav-beginning-of-statement 92 0% re-search-backward 44 0% looking-at 12 0% current-indentation 4 0% looking-at 380 1% + python-nav-end-of-statement 16 0% + python-util-forward-comment 28 0% + python-nav-end-of-statement 151 0% + python-nav-end-of-block 28 0% + python-info-beginning-of-block-p 24 0% + python-info-end-of-statement-p 20 0% + python-info-beginning-of-statement-p 8 0% + python-syntax-context-type 8 0% + python-info-statement-starts-block-p 4 0% + syntax-ppss 8 0% + #<byte-code-function 7E0> 4 0% + #<byte-code-function 740> 80 0% + font-lock-fontify-syntactically-region 2279 11% Automatic GC 73 0% + redisplay_internal (C function) 40 0% + command-execute 8 0% + ... 4 0% + mouse--click-1-maybe-follows-link other people talking about python/profiling/syntax-ppss : https://www.reddit.com/r/emacs/comments/z0oye9/emacs_freezes_when_opening_a_python_file/ https://www.reddit.com/r/emacs/comments/9h7onq/large_python_files_hanging_with_an_unmatched_quote/ Please tell me how I can help ? Please tell me if there is a workaround without losing python syntax color ? Thanks Rehan In GNU Emacs 31.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.43, cairo version 1.18.2) of 2024-12-08 built on cndl Repository revision: b953bcb17047998c9e41cede7c5e5ffec22209b2 Repository branch: master System Description: Debian GNU/Linux trixie/sid Configured using: 'configure --prefix=/home/jean/local/emacs --with-pgtk' Configured features: CAIRO DBUS FREETYPE GLIB GMP GNUTLS GSETTINGS HARFBUZZ JPEG LIBSELINUX LIBXML2 MODULES NOTIFY INOTIFY PDUMPER PGTK PNG SECCOMP SOUND THREADS TIFF TOOLKIT_SCROLL_BARS WEBP XIM GTK3 ZLIB Important settings: value of $LANG: en_US.UTF-8 value of $XMODIFIERS: @im=ibus locale-coding-system: utf-8-unix Major mode: Python Minor modes in effect: tooltip-mode: t global-eldoc-mode: t eldoc-mode: t show-paren-mode: t electric-indent-mode: t mouse-wheel-mode: t tool-bar-mode: t menu-bar-mode: t file-name-shadow-mode: t global-font-lock-mode: t font-lock-mode: t blink-cursor-mode: t minibuffer-regexp-mode: t line-number-mode: t transient-mark-mode: t auto-composition-mode: t auto-encryption-mode: t auto-compression-mode: t Load-path shadows: None found. Features: (shadow sort mail-extr emacsbug message mailcap yank-media puny dired dired-loaddefs rfc822 mml mml-sec password-cache epa derived epg rfc6068 epg-config gnus-util text-property-search time-date mm-decode mm-bodies mm-encode mail-parse rfc2231 mailabbrev gmm-utils mailheader sendmail rfc2047 rfc2045 ietf-drums mm-util mail-prsvr mail-utils cl-seq python rx project byte-opt gv bytecomp byte-compile compat pcase treesit comint subr-x ansi-osc ring cl-loaddefs cl-lib ansi-color rmc iso-transl tooltip cconv eldoc paren electric uniquify ediff-hook vc-hooks lisp-float-type elisp-mode mwheel term/pgtk-win pgtk-win term/common-win touch-screen pgtk-dnd tool-bar dnd fontset image regexp-opt fringe tabulated-list replace newcomment text-mode lisp-mode prog-mode register page tab-bar menu-bar rfn-eshadow isearch easymenu timer select scroll-bar mouse jit-lock font-lock syntax font-core term/tty-colors frame minibuffer nadvice seq simple cl-generic indonesian philippine cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech european ethiopic indian cyrillic chinese composite emoji-zwj charscript charprop case-table epa-hook jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button loaddefs theme-loaddefs faces cus-face macroexp files window text-properties overlay sha1 md5 base64 format env code-pages mule custom widget keymap hashtable-print-readable backquote threads dbusbind inotify dynamic-setting system-font-setting font-render-setting cairo gtk pgtk multi-tty move-toolbar make-network-process emacs) Memory information: ((conses 16 60995 13527) (symbols 48 7517 0) (strings 32 19122 1493) (string-bytes 1 515162) (vectors 16 13554) (vector-slots 8 145472 8433) (floats 8 26 2) (intervals 56 436 0) (buffers 984 12)) [-- Attachment #2: Type: text/html, Size: 10952 bytes --] ^ permalink raw reply [flat|nested] 5+ messages in thread
* bug#74738: 31.0.50; Freezes in Python-mode on some Python file when searching or scrolling 2024-12-08 13:05 bug#74738: 31.0.50; Freezes in Python-mode on some Python file when searching or scrolling rehan malak @ 2024-12-08 17:37 ` Eli Zaretskii 2024-12-09 14:58 ` rehan malak 2024-12-09 15:04 ` kobarity 0 siblings, 2 replies; 5+ messages in thread From: Eli Zaretskii @ 2024-12-08 17:37 UTC (permalink / raw) To: rehan malak, Stefan Monnier, kobarity; +Cc: 74738 > From: rehan malak <rehan.malak@gmail.com> > Date: Sun, 8 Dec 2024 14:05:36 +0100 > > I can reproduce systematically this freeze dealing with a 10000lines > Python file : > > wget https://raw.githubusercontent.com/hugsy/gef/refs/heads/main/gef.py > emacs -Q gef.py A much smaller reproducer is attached below. Just visiting it freezes Emacs. But if I change the line marked below: @classmethod def is_valid(cls, _: pathlib.Path) -> bool: raise NotImplementedError def __str__(self) -> str: >>>>> return f"{self.name}('{self.path.absolute()}', entry @ {self.entry_point:#x})" to say this instead: return self.name the problem goes away. So it is something in that complex expression that trips syntax-ppss. Stefan and kobarity, any suggestions or ideas? > PageDown several times or scrolling with the mouse > > Emacs freezes, Ctrl-g not working, CPU 100% > > It works also by searching : Ctrl-s show RET > then Ctrl-s several times > > With a minimal .emacs : > > (set debug-on-error t) > (set debug-on-quit t) > > and before scrolling > > M-x profiler-start RET > > then in an external terminal > > pkill -SIGUSR2 emacs > > I get the backtrace : > > Debugger entered--entering a function: > * #f(compiled-function () #<bytecode -0x179dcd1db31182ae>)() > syntax-ppss() > python-syntax-context-type() > python-nav-forward-block(-1) > python-nav-backward-block() > python-nav-beginning-of-block() > python-nav-end-of-block() > python-info-statement-ends-block-p() > python-info-end-of-block-p() > python-nav--forward-sexp(1 nil nil) > python-nav-forward-sexp(1) > forward-sexp(1) > up-list(1) > python--font-lock-f-strings(30419) > font-lock-fontify-keywords-region(28914 30419 nil) > font-lock-default-fontify-region(28914 30414 nil) > font-lock-fontify-region(28914 30414) > #f(compiled-function (fun) #<bytecode -0x8a96d65e1315e71>)(font-lock-fontify-region) > run-hook-wrapped(#f(compiled-function (fun) #<bytecode -0x8a96d65e1315e71>) font-lock-fontify-region) > jit-lock--run-functions(28914 30414) > jit-lock-fontify-now(28914 30414) > jit-lock-function(28914) I see something different, in GDB: Lisp Backtrace: "parse-partial-sexp" (0x9f1a788) "syntax-ppss" (0x9f1a710) "python-info-line-ends-backslash-p" (0x9f1a6b8) "python-nav-end-of-statement" (0x9f1a670) "python-nav-end-of-block" (0x9f1a630) "python-info-statement-ends-block-p" (0x9f1a610) "python-info-end-of-block-p" (0x9f1a5c0) "python-nav--forward-sexp" (0x9f1a548) "python-nav-forward-sexp" (0x9f1a4f8) "forward-sexp" (0x9f1a4a8) "up-list" (0x9f1a438) "python--font-lock-f-strings" (0x9f1a3a0) "font-lock-fontify-keywords-region" (0x9f1a308) "font-lock-default-fontify-region" (0x9f1a2a0) "font-lock-fontify-region" (0x9f1a230) 0xb9117d0 PVEC_CLOSURE "run-hook-wrapped" (0x9f1a1c0) "jit-lock--run-functions" (0x9f1a0e8) "jit-lock-fontify-now" (0x9f1a058) "jit-lock-function" (0x5ffba98) "redisplay_internal (C function)" (0x0) Here's the file with which I can reproduce the problem? class FileFormat: name: str path: pathlib.Path entry_point: int checksec: dict[str, bool] sections: list[FileFormatSection] def __init__(self, path: str | pathlib.Path) -> None: raise NotImplementedError def __init_subclass__(cls: Type["FileFormat"], **kwargs): global __registered_file_formats__ super().__init_subclass__(**kwargs) required_attributes = ("name", "entry_point", "is_valid", "checksec",) for attr in required_attributes: if not hasattr(cls, attr): raise NotImplementedError(f"File format '{cls.__name__}' is invalid: missing attribute '{attr}'") __registered_file_formats__.add(cls) return @classmethod def is_valid(cls, _: pathlib.Path) -> bool: raise NotImplementedError def __str__(self) -> str: return f"{self.name}('{self.path.absolute()}', entry @ {self.entry_point:#x})" class Elf(FileFormat): """Basic ELF parsing. Ref: - http://www.skyfree.org/linux/references/ELF_Format.pdf - https://refspecs.linuxfoundation.org/elf/elfspec_ppc.pdf - https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html """ class Class(enum.Enum): ELF_32_BITS = 0x01 ELF_64_BITS = 0x02 ELF_MAGIC = 0x7f454c46 class Abi(enum.Enum): X86_64 = 0x3e X86_32 = 0x03 ARM = 0x28 MIPS = 0x08 POWERPC = 0x14 POWERPC64 = 0x15 SPARC = 0x02 SPARC64 = 0x2b AARCH64 = 0xb7 RISCV = 0xf3 IA64 = 0x32 M68K = 0x04 class Type(enum.Enum): ET_RELOC = 1 ET_EXEC = 2 ET_DYN = 3 ET_CORE = 4 class OsAbi(enum.Enum): SYSTEMV = 0x00 HPUX = 0x01 NETBSD = 0x02 LINUX = 0x03 SOLARIS = 0x06 AIX = 0x07 IRIX = 0x08 FREEBSD = 0x09 OPENBSD = 0x0C e_magic: int = ELF_MAGIC e_class: "Elf.Class" = Class.ELF_32_BITS e_endianness: Endianness = Endianness.LITTLE_ENDIAN e_eiversion: int e_osabi: "Elf.OsAbi" e_abiversion: int e_pad: bytes e_type: "Elf.Type" = Type.ET_EXEC e_machine: Abi = Abi.X86_32 e_version: int e_entry: int e_phoff: int e_shoff: int e_flags: int e_ehsize: int e_phentsize: int e_phnum: int e_shentsize: int e_shnum: int e_shstrndx: int path: pathlib.Path phdrs : list["Phdr"] shdrs : list["Shdr"] name: str = "ELF" __checksec : dict[str, bool] def __init__(self, path: str | pathlib.Path) -> None: """Instantiate an ELF object. A valid ELF must be provided, or an exception will be thrown.""" if isinstance(path, str): self.path = pathlib.Path(path).expanduser() elif isinstance(path, pathlib.Path): self.path = path else: raise TypeError if not self.path.exists(): raise FileNotFoundError(f"'{self.path}' not found/readable, most gef features will not work") self.__checksec = {} with self.path.open("rb") as self.fd: # off 0x0 self.e_magic, e_class, e_endianness, self.e_eiversion = self.read_and_unpack(">IBBB") if self.e_magic != Elf.ELF_MAGIC: # The ELF is corrupted, GDB won't handle it, no point going further raise RuntimeError("Not a valid ELF file (magic)") self.e_class, self.e_endianness = Elf.Class(e_class), Endianness(e_endianness) if self.e_endianness != gef.arch.endianness: warn("Unexpected endianness for architecture") endian = self.e_endianness # off 0x7 e_osabi, self.e_abiversion = self.read_and_unpack(f"{endian}BB") self.e_osabi = Elf.OsAbi(e_osabi) # off 0x9 self.e_pad = self.read(7) # off 0x10 e_type, e_machine, self.e_version = self.read_and_unpack(f"{endian}HHI") self.e_type, self.e_machine = Elf.Type(e_type), Elf.Abi(e_machine) # off 0x18 if self.e_class == Elf.Class.ELF_64_BITS: self.e_entry, self.e_phoff, self.e_shoff = self.read_and_unpack(f"{endian}QQQ") else: self.e_entry, self.e_phoff, self.e_shoff = self.read_and_unpack(f"{endian}III") self.e_flags, self.e_ehsize, self.e_phentsize, self.e_phnum = self.read_and_unpack(f"{endian}IHHH") self.e_shentsize, self.e_shnum, self.e_shstrndx = self.read_and_unpack(f"{endian}HHH") self.phdrs = [] for i in range(self.e_phnum): self.phdrs.append(Phdr(self, self.e_phoff + self.e_phentsize * i)) self.shdrs = [] for i in range(self.e_shnum): self.shdrs.append(Shdr(self, self.e_shoff + self.e_shentsize * i)) return def read(self, size: int) -> bytes: return self.fd.read(size) def read_and_unpack(self, fmt: str) -> tuple[Any, ...]: size = struct.calcsize(fmt) data = self.fd.read(size) return struct.unpack(fmt, data) def seek(self, off: int) -> None: self.fd.seek(off, 0) def __str__(self) -> str: return f"ELF('{self.path.absolute()}', {self.e_class.name}, {self.e_machine.name})" def __repr__(self) -> str: return f"ELF('{self.path.absolute()}', {self.e_class.name}, {self.e_machine.name})" @property def entry_point(self) -> int: return self.e_entry @classmethod def is_valid(cls, path: pathlib.Path) -> bool: return u32(path.open("rb").read(4), e = Endianness.BIG_ENDIAN) == Elf.ELF_MAGIC @property def checksec(self) -> dict[str, bool]: """Check the security property of the ELF binary. The following properties are: - Canary - NX - PIE - Fortify - Partial/Full RelRO. Return a dict() with the different keys mentioned above, and the boolean associated whether the protection was found.""" if not self.__checksec: def __check_security_property(opt: str, filename: str, pattern: str) -> bool: cmd = [readelf,] cmd += opt.split() cmd += [filename,] lines = gef_execute_external(cmd, as_list=True) for line in lines: if re.search(pattern, line): return True return False abspath = str(self.path.absolute()) readelf = gef.session.constants["readelf"] self.__checksec["Canary"] = __check_security_property("-rs", abspath, r"__stack_chk_fail") is True has_gnu_stack = __check_security_property("-W -l", abspath, r"GNU_STACK") is True if has_gnu_stack: self.__checksec["NX"] = __check_security_property("-W -l", abspath, r"GNU_STACK.*RWE") is False else: self.__checksec["NX"] = False self.__checksec["PIE"] = __check_security_property("-h", abspath, r":.*EXEC") is False self.__checksec["Fortify"] = __check_security_property("-s", abspath, r"_chk@GLIBC") is True self.__checksec["Partial RelRO"] = __check_security_property("-l", abspath, r"GNU_RELRO") is True self.__checksec["Full RelRO"] = self.__checksec["Partial RelRO"] and __check_security_property("-d", abspath, r"BIND_NOW") is True return self.__checksec @classproperty @deprecated("use `Elf.Abi.X86_64`") def X86_64(cls) -> int: return Elf.Abi.X86_64.value # pylint: disable=no-self-argument @classproperty @deprecated("use `Elf.Abi.X86_32`") def X86_32(cls) -> int : return Elf.Abi.X86_32.value # pylint: disable=no-self-argument @classproperty @deprecated("use `Elf.Abi.ARM`") def ARM(cls) -> int : return Elf.Abi.ARM.value # pylint: disable=no-self-argument @classproperty @deprecated("use `Elf.Abi.MIPS`") def MIPS(cls) -> int : return Elf.Abi.MIPS.value # pylint: disable=no-self-argument @classproperty @deprecated("use `Elf.Abi.POWERPC`") def POWERPC(cls) -> int : return Elf.Abi.POWERPC.value # pylint: disable=no-self-argument @classproperty @deprecated("use `Elf.Abi.POWERPC64`") def POWERPC64(cls) -> int : return Elf.Abi.POWERPC64.value # pylint: disable=no-self-argument @classproperty @deprecated("use `Elf.Abi.SPARC`") def SPARC(cls) -> int : return Elf.Abi.SPARC.value # pylint: disable=no-self-argument @classproperty @deprecated("use `Elf.Abi.SPARC64`") def SPARC64(cls) -> int : return Elf.Abi.SPARC64.value # pylint: disable=no-self-argument @classproperty @deprecated("use `Elf.Abi.AARCH64`") def AARCH64(cls) -> int : return Elf.Abi.AARCH64.value # pylint: disable=no-self-argument @classproperty @deprecated("use `Elf.Abi.RISCV`") def RISCV(cls) -> int : return Elf.Abi.RISCV.value # pylint: disable=no-self-argument ^ permalink raw reply [flat|nested] 5+ messages in thread
* bug#74738: 31.0.50; Freezes in Python-mode on some Python file when searching or scrolling 2024-12-08 17:37 ` Eli Zaretskii @ 2024-12-09 14:58 ` rehan malak 2024-12-11 14:24 ` kobarity 2024-12-09 15:04 ` kobarity 1 sibling, 1 reply; 5+ messages in thread From: rehan malak @ 2024-12-09 14:58 UTC (permalink / raw) To: Eli Zaretskii; +Cc: kobarity, Stefan Monnier, 74738 [-- Attachment #1: Type: text/plain, Size: 14703 bytes --] Hi, Deleting all the "#" in the f-string of the gef.py file removes the problem on my side. sed -i -e 's/#0{align/0{align/g' gef.py sed -i -e 's/#0{width/0{width/g' gef.py sed -i -e 's/#07x}/07x}/g' gef.py sed -i -e 's/#06x}/06x}/g' gef.py sed -i -e 's/#04x}/04x}/g' gef.py sed -i -e 's/#4x}/4x}/g' gef.py sed -i -e 's/#8x}/8x}/g' gef.py sed -i -e 's/#10x}/10x}/g' gef.py sed -i -e 's/#x}/x}/g' gef.py Your smaller example Eli also contains a f-string with the "#". This python f-string format specifier is described here: https://docs.python.org/3/library/string.html#format-specification-mini-language Is this format specifier supported from the beginning ? For example : value = 0xab print(f"{value:x} is a value") => color syntax : *is a value* has the color defined by font-lock-string-face while value = 0xab print(f"{value:#x} is a value") => color are messed up... *is* has color defined by font-lock-keyword-face and *a value* has color defined by default face Best, Rehan On Sun, Dec 8, 2024 at 6:37 PM Eli Zaretskii <eliz@gnu.org> wrote: > > From: rehan malak <rehan.malak@gmail.com> > > Date: Sun, 8 Dec 2024 14:05:36 +0100 > > > > I can reproduce systematically this freeze dealing with a 10000lines > > Python file : > > > > wget https://raw.githubusercontent.com/hugsy/gef/refs/heads/main/gef.py > > emacs -Q gef.py > > A much smaller reproducer is attached below. Just visiting it freezes > Emacs. But if I change the line marked below: > > @classmethod > def is_valid(cls, _: pathlib.Path) -> bool: > raise NotImplementedError > > def __str__(self) -> str: > >>>>> return f"{self.name}('{self.path.absolute()}', entry @ > {self.entry_point:#x})" > > to say this instead: > > return self.name > > the problem goes away. So it is something in that complex expression > that trips syntax-ppss. > > Stefan and kobarity, any suggestions or ideas? > > > PageDown several times or scrolling with the mouse > > > > Emacs freezes, Ctrl-g not working, CPU 100% > > > > It works also by searching : Ctrl-s show RET > > then Ctrl-s several times > > > > With a minimal .emacs : > > > > (set debug-on-error t) > > (set debug-on-quit t) > > > > and before scrolling > > > > M-x profiler-start RET > > > > then in an external terminal > > > > pkill -SIGUSR2 emacs > > > > I get the backtrace : > > > > Debugger entered--entering a function: > > * #f(compiled-function () #<bytecode -0x179dcd1db31182ae>)() > > syntax-ppss() > > python-syntax-context-type() > > python-nav-forward-block(-1) > > python-nav-backward-block() > > python-nav-beginning-of-block() > > python-nav-end-of-block() > > python-info-statement-ends-block-p() > > python-info-end-of-block-p() > > python-nav--forward-sexp(1 nil nil) > > python-nav-forward-sexp(1) > > forward-sexp(1) > > up-list(1) > > python--font-lock-f-strings(30419) > > font-lock-fontify-keywords-region(28914 30419 nil) > > font-lock-default-fontify-region(28914 30414 nil) > > font-lock-fontify-region(28914 30414) > > #f(compiled-function (fun) #<bytecode > -0x8a96d65e1315e71>)(font-lock-fontify-region) > > run-hook-wrapped(#f(compiled-function (fun) #<bytecode > -0x8a96d65e1315e71>) font-lock-fontify-region) > > jit-lock--run-functions(28914 30414) > > jit-lock-fontify-now(28914 30414) > > jit-lock-function(28914) > > I see something different, in GDB: > > Lisp Backtrace: > "parse-partial-sexp" (0x9f1a788) > "syntax-ppss" (0x9f1a710) > "python-info-line-ends-backslash-p" (0x9f1a6b8) > "python-nav-end-of-statement" (0x9f1a670) > "python-nav-end-of-block" (0x9f1a630) > "python-info-statement-ends-block-p" (0x9f1a610) > "python-info-end-of-block-p" (0x9f1a5c0) > "python-nav--forward-sexp" (0x9f1a548) > "python-nav-forward-sexp" (0x9f1a4f8) > "forward-sexp" (0x9f1a4a8) > "up-list" (0x9f1a438) > "python--font-lock-f-strings" (0x9f1a3a0) > "font-lock-fontify-keywords-region" (0x9f1a308) > "font-lock-default-fontify-region" (0x9f1a2a0) > "font-lock-fontify-region" (0x9f1a230) > 0xb9117d0 PVEC_CLOSURE > "run-hook-wrapped" (0x9f1a1c0) > "jit-lock--run-functions" (0x9f1a0e8) > "jit-lock-fontify-now" (0x9f1a058) > "jit-lock-function" (0x5ffba98) > "redisplay_internal (C function)" (0x0) > > Here's the file with which I can reproduce the problem? > > > class FileFormat: > name: str > path: pathlib.Path > entry_point: int > checksec: dict[str, bool] > sections: list[FileFormatSection] > > def __init__(self, path: str | pathlib.Path) -> None: > raise NotImplementedError > > def __init_subclass__(cls: Type["FileFormat"], **kwargs): > global __registered_file_formats__ > super().__init_subclass__(**kwargs) > required_attributes = ("name", "entry_point", "is_valid", > "checksec",) > for attr in required_attributes: > if not hasattr(cls, attr): > raise NotImplementedError(f"File format '{cls.__name__}' > is invalid: missing attribute '{attr}'") > __registered_file_formats__.add(cls) > return > > @classmethod > def is_valid(cls, _: pathlib.Path) -> bool: > raise NotImplementedError > > def __str__(self) -> str: > return f"{self.name}('{self.path.absolute()}', entry @ > {self.entry_point:#x})" > > > class Elf(FileFormat): > """Basic ELF parsing. > Ref: > - http://www.skyfree.org/linux/references/ELF_Format.pdf > - https://refspecs.linuxfoundation.org/elf/elfspec_ppc.pdf > - https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html > """ > class Class(enum.Enum): > ELF_32_BITS = 0x01 > ELF_64_BITS = 0x02 > > ELF_MAGIC = 0x7f454c46 > > class Abi(enum.Enum): > X86_64 = 0x3e > X86_32 = 0x03 > ARM = 0x28 > MIPS = 0x08 > POWERPC = 0x14 > POWERPC64 = 0x15 > SPARC = 0x02 > SPARC64 = 0x2b > AARCH64 = 0xb7 > RISCV = 0xf3 > IA64 = 0x32 > M68K = 0x04 > > class Type(enum.Enum): > ET_RELOC = 1 > ET_EXEC = 2 > ET_DYN = 3 > ET_CORE = 4 > > class OsAbi(enum.Enum): > SYSTEMV = 0x00 > HPUX = 0x01 > NETBSD = 0x02 > LINUX = 0x03 > SOLARIS = 0x06 > AIX = 0x07 > IRIX = 0x08 > FREEBSD = 0x09 > OPENBSD = 0x0C > > e_magic: int = ELF_MAGIC > e_class: "Elf.Class" = Class.ELF_32_BITS > e_endianness: Endianness = Endianness.LITTLE_ENDIAN > e_eiversion: int > e_osabi: "Elf.OsAbi" > e_abiversion: int > e_pad: bytes > e_type: "Elf.Type" = Type.ET_EXEC > e_machine: Abi = Abi.X86_32 > e_version: int > e_entry: int > e_phoff: int > e_shoff: int > e_flags: int > e_ehsize: int > e_phentsize: int > e_phnum: int > e_shentsize: int > e_shnum: int > e_shstrndx: int > > path: pathlib.Path > phdrs : list["Phdr"] > shdrs : list["Shdr"] > name: str = "ELF" > > __checksec : dict[str, bool] > > def __init__(self, path: str | pathlib.Path) -> None: > """Instantiate an ELF object. A valid ELF must be provided, or an > exception will be thrown.""" > > if isinstance(path, str): > self.path = pathlib.Path(path).expanduser() > elif isinstance(path, pathlib.Path): > self.path = path > else: > raise TypeError > > if not self.path.exists(): > raise FileNotFoundError(f"'{self.path}' not found/readable, > most gef features will not work") > > self.__checksec = {} > > with self.path.open("rb") as self.fd: > # off 0x0 > self.e_magic, e_class, e_endianness, self.e_eiversion = > self.read_and_unpack(">IBBB") > if self.e_magic != Elf.ELF_MAGIC: > # The ELF is corrupted, GDB won't handle it, no point > going further > raise RuntimeError("Not a valid ELF file (magic)") > > self.e_class, self.e_endianness = Elf.Class(e_class), > Endianness(e_endianness) > > if self.e_endianness != gef.arch.endianness: > warn("Unexpected endianness for architecture") > > endian = self.e_endianness > > # off 0x7 > e_osabi, self.e_abiversion = > self.read_and_unpack(f"{endian}BB") > self.e_osabi = Elf.OsAbi(e_osabi) > > # off 0x9 > self.e_pad = self.read(7) > > # off 0x10 > e_type, e_machine, self.e_version = > self.read_and_unpack(f"{endian}HHI") > self.e_type, self.e_machine = Elf.Type(e_type), > Elf.Abi(e_machine) > > # off 0x18 > if self.e_class == Elf.Class.ELF_64_BITS: > self.e_entry, self.e_phoff, self.e_shoff = > self.read_and_unpack(f"{endian}QQQ") > else: > self.e_entry, self.e_phoff, self.e_shoff = > self.read_and_unpack(f"{endian}III") > > self.e_flags, self.e_ehsize, self.e_phentsize, self.e_phnum = > self.read_and_unpack(f"{endian}IHHH") > self.e_shentsize, self.e_shnum, self.e_shstrndx = > self.read_and_unpack(f"{endian}HHH") > > self.phdrs = [] > for i in range(self.e_phnum): > self.phdrs.append(Phdr(self, self.e_phoff + > self.e_phentsize * i)) > > self.shdrs = [] > for i in range(self.e_shnum): > self.shdrs.append(Shdr(self, self.e_shoff + > self.e_shentsize * i)) > return > > def read(self, size: int) -> bytes: > return self.fd.read(size) > > def read_and_unpack(self, fmt: str) -> tuple[Any, ...]: > size = struct.calcsize(fmt) > data = self.fd.read(size) > return struct.unpack(fmt, data) > > def seek(self, off: int) -> None: > self.fd.seek(off, 0) > > def __str__(self) -> str: > return f"ELF('{self.path.absolute()}', {self.e_class.name}, { > self.e_machine.name})" > > def __repr__(self) -> str: > return f"ELF('{self.path.absolute()}', {self.e_class.name}, { > self.e_machine.name})" > > @property > def entry_point(self) -> int: > return self.e_entry > > @classmethod > def is_valid(cls, path: pathlib.Path) -> bool: > return u32(path.open("rb").read(4), e = Endianness.BIG_ENDIAN) == > Elf.ELF_MAGIC > > @property > def checksec(self) -> dict[str, bool]: > """Check the security property of the ELF binary. The following > properties are: > - Canary > - NX > - PIE > - Fortify > - Partial/Full RelRO. > Return a dict() with the different keys mentioned above, and the > boolean > associated whether the protection was found.""" > if not self.__checksec: > def __check_security_property(opt: str, filename: str, > pattern: str) -> bool: > cmd = [readelf,] > cmd += opt.split() > cmd += [filename,] > lines = gef_execute_external(cmd, as_list=True) > for line in lines: > if re.search(pattern, line): > return True > return False > > abspath = str(self.path.absolute()) > readelf = gef.session.constants["readelf"] > self.__checksec["Canary"] = __check_security_property("-rs", > abspath, r"__stack_chk_fail") is True > has_gnu_stack = __check_security_property("-W -l", abspath, > r"GNU_STACK") is True > if has_gnu_stack: > self.__checksec["NX"] = __check_security_property("-W -l", > abspath, r"GNU_STACK.*RWE") is False > else: > self.__checksec["NX"] = False > self.__checksec["PIE"] = __check_security_property("-h", > abspath, r":.*EXEC") is False > self.__checksec["Fortify"] = __check_security_property("-s", > abspath, r"_chk@GLIBC") is True > self.__checksec["Partial RelRO"] = > __check_security_property("-l", abspath, r"GNU_RELRO") is True > self.__checksec["Full RelRO"] = self.__checksec["Partial > RelRO"] and __check_security_property("-d", abspath, r"BIND_NOW") is True > return self.__checksec > > @classproperty > @deprecated("use `Elf.Abi.X86_64`") > def X86_64(cls) -> int: return Elf.Abi.X86_64.value # pylint: > disable=no-self-argument > > @classproperty > @deprecated("use `Elf.Abi.X86_32`") > def X86_32(cls) -> int : return Elf.Abi.X86_32.value # pylint: > disable=no-self-argument > > @classproperty > @deprecated("use `Elf.Abi.ARM`") > def ARM(cls) -> int : return Elf.Abi.ARM.value # pylint: > disable=no-self-argument > > @classproperty > @deprecated("use `Elf.Abi.MIPS`") > def MIPS(cls) -> int : return Elf.Abi.MIPS.value # pylint: > disable=no-self-argument > > @classproperty > @deprecated("use `Elf.Abi.POWERPC`") > def POWERPC(cls) -> int : return Elf.Abi.POWERPC.value # pylint: > disable=no-self-argument > > @classproperty > @deprecated("use `Elf.Abi.POWERPC64`") > def POWERPC64(cls) -> int : return Elf.Abi.POWERPC64.value # pylint: > disable=no-self-argument > > @classproperty > @deprecated("use `Elf.Abi.SPARC`") > def SPARC(cls) -> int : return Elf.Abi.SPARC.value # pylint: > disable=no-self-argument > > @classproperty > @deprecated("use `Elf.Abi.SPARC64`") > def SPARC64(cls) -> int : return Elf.Abi.SPARC64.value # pylint: > disable=no-self-argument > > @classproperty > @deprecated("use `Elf.Abi.AARCH64`") > def AARCH64(cls) -> int : return Elf.Abi.AARCH64.value # pylint: > disable=no-self-argument > > @classproperty > @deprecated("use `Elf.Abi.RISCV`") > def RISCV(cls) -> int : return Elf.Abi.RISCV.value # pylint: > disable=no-self-argument > > [-- Attachment #2: Type: text/html, Size: 19416 bytes --] ^ permalink raw reply [flat|nested] 5+ messages in thread
* bug#74738: 31.0.50; Freezes in Python-mode on some Python file when searching or scrolling 2024-12-09 14:58 ` rehan malak @ 2024-12-11 14:24 ` kobarity 0 siblings, 0 replies; 5+ messages in thread From: kobarity @ 2024-12-11 14:24 UTC (permalink / raw) To: rehan malak, Eli Zaretskii; +Cc: 74738, Stefan Monnier [-- Attachment #1: Type: text/plain, Size: 1682 bytes --] rehan malak wrote: > Deleting all the "#" in the f-string of the gef.py file removes the problem on my side. > sed -i -e 's/#0{align/0{align/g' gef.py > sed -i -e 's/#0{width/0{width/g' gef.py > sed -i -e 's/#07x}/07x}/g' gef.py > sed -i -e 's/#06x}/06x}/g' gef.py > sed -i -e 's/#04x}/04x}/g' gef.py > sed -i -e 's/#4x}/4x}/g' gef.py > sed -i -e 's/#8x}/8x}/g' gef.py > sed -i -e 's/#10x}/10x}/g' gef.py > sed -i -e 's/#x}/x}/g' gef.py > > Your smaller example Eli also contains a f-string with the "#". > > This python f-string format specifier is described here: > https://docs.python.org/3/library/string.html#format-specification-mini-language > > Is this format specifier supported from the beginning ? > > For example : > > value = 0xab > print(f"{value:x} is a value") > > => color syntax : is a value has the color defined by font-lock-string-face > > while > > value = 0xab > print(f"{value:#x} is a value") > > => color are messed up... is has color defined by font-lock-keyword-face and a value has color defined by default face 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. 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. [-- Attachment #2: 0001-Fix-font-lock-of-Python-f-strings.patch --] [-- Type: text/plain, Size: 1999 bytes --] From 697f942d70e40bcd9bd32596313a9a371fffe35c Mon Sep 17 00:00:00 2001 From: kobarity <kobarity@gmail.com> Date: Wed, 11 Dec 2024 23:21:04 +0900 Subject: [PATCH] Fix font-lock of Python f-strings * lisp/progmodes/python.el (python--font-lock-f-strings): Bind 'parse-sexp-ignore-comments' to nil so that we can look for closing braces even if a hash is used in the format specifier. * test/lisp/progmodes/python-tests.el (python-font-lock-f-string-1): New test. (Bug#74738) --- lisp/progmodes/python.el | 4 +++- test/lisp/progmodes/python-tests.el | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index cfa3cc59568..827f437013f 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -631,7 +631,9 @@ python--font-lock-f-strings (forward-char 1) ;Just skip over {{ (let ((beg (match-beginning 0)) (end (condition-case nil - (progn (up-list 1) (min send (point))) + (let ((parse-sexp-ignore-comments)) + (up-list 1) + (min send (point))) (scan-error send)))) (goto-char end) (put-text-property beg end 'face nil)))) diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el index 4cc4040d0ff..ac17c5b1263 100644 --- a/test/lisp/progmodes/python-tests.el +++ b/test/lisp/progmodes/python-tests.el @@ -709,6 +709,14 @@ python-font-lock-string-literal-concatenation (17 . font-lock-operator-face) (18) (19 . font-lock-string-face)))) +(ert-deftest python-font-lock-f-string-1 () + "Test for bug#74738." + (python-tests-assert-faces + "print(f\"{value:#x} is a value\")" + '((1 . font-lock-builtin-face) (6) + (8 . font-lock-string-face) (9) + (19 . font-lock-string-face) (31)))) + \f ;;; Indentation -- 2.43.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* bug#74738: 31.0.50; Freezes in Python-mode on some Python file when searching or scrolling 2024-12-08 17:37 ` Eli Zaretskii 2024-12-09 14:58 ` rehan malak @ 2024-12-09 15:04 ` kobarity 1 sibling, 0 replies; 5+ messages in thread From: kobarity @ 2024-12-09 15:04 UTC (permalink / raw) To: rehan malak, Eli Zaretskii; +Cc: 74738, Stefan Monnier rehan malak wrote: > Please tell me if there is a workaround without losing python syntax color ? If you can configure Emacs to use tree-sitter, python-ts-mode would not be affected by this problem. Eli Zaretskii wrote: > > > From: rehan malak <rehan.malak@gmail.com> > > Date: Sun, 8 Dec 2024 14:05:36 +0100 > > > > I can reproduce systematically this freeze dealing with a 10000lines > > Python file : > > > > wget https://raw.githubusercontent.com/hugsy/gef/refs/heads/main/gef.py > > emacs -Q gef.py It seems to be reproducible after the following commit: commit 3b3274a85c2f5df21d76d82e0d7740005aa84fdf Author: Stefan Monnier <monnier@iro.umontreal.ca> Date: Fri Oct 16 14:03:59 2020 -0400 * lisp/progmodes/python.el: Teach f-strings to `font-lock` (python--f-string-p, python--font-lock-f-strings): New functions. (python-font-lock-keywords-maximum-decoration): Use them. > A much smaller reproducer is attached below. Just visiting it freezes > Emacs. But if I change the line marked below: > > @classmethod > def is_valid(cls, _: pathlib.Path) -> bool: > raise NotImplementedError > > def __str__(self) -> str: > >>>>> return f"{self.name}('{self.path.absolute()}', entry @ {self.entry_point:#x})" > > to say this instead: > > return self.name > > the problem goes away. So it is something in that complex expression > that trips syntax-ppss. > > Stefan and kobarity, any suggestions or ideas? I could not reproduce the freeze, but it took several seconds to visit the reproducer. It appears to be possible to work around this by simply removing the format specifier ":#x". return f"{self.name}('{self.path.absolute()}', entry @ {self.entry_point})" So there may be a problem with font-lock when there is an f-string format specifier, but I have not found the root cause yet. ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2024-12-11 14:24 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-12-08 13:05 bug#74738: 31.0.50; Freezes in Python-mode on some Python file when searching or scrolling rehan malak 2024-12-08 17:37 ` Eli Zaretskii 2024-12-09 14:58 ` rehan malak 2024-12-11 14:24 ` kobarity 2024-12-09 15:04 ` kobarity
Code repositories for project(s) associated with this public inbox https://git.savannah.gnu.org/cgit/emacs.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).