From b1c1932fc771c04ecf7ab46e7563d479b3f5c635 Mon Sep 17 00:00:00 2001 From: Danstiv <50794055+Danstiv@users.noreply.github.com> Date: Sun, 5 May 2024 22:11:54 +0700 Subject: [PATCH 1/6] Refactor DiffMatchPatch diff handler --- source/diffHandler.py | 59 +++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/source/diffHandler.py b/source/diffHandler.py index 09fa3288d54..0cdc250a5b2 100644 --- a/source/diffHandler.py +++ b/source/diffHandler.py @@ -62,6 +62,21 @@ def _initialize(self): def _getText(self, ti: TextInfo) -> str: return ti.text + @classmethod + def _readData(cls, size): + """Reads from stdout, raises exception on EOF.""" + buffer = b"" + while (requiredLength := size - len(buffer)) > 0: + chunk = cls._proc.stdout.read(requiredLength) + if chunk: + buffer += chunk + continue + return_code = cls._proc.poll() + if return_code is None: + continue + raise RuntimeError(f'Diff-match-patch proxy process died! Return code {return_code}') + return buffer + def diff(self, newText: str, oldText: str) -> List[str]: try: if not newText and not oldText: @@ -70,31 +85,23 @@ def diff(self, newText: str, oldText: str) -> List[str]: return [] with DiffMatchPatch._lock: self._initialize() - old = oldText.encode("utf-8") - new = newText.encode("utf-8") # Sizes are packed as 32-bit ints in native byte order. # Since nvda and nvda_dmp are running on the same Python # platform/version, this is okay. - tl = struct.pack("=II", len(old), len(new)) - DiffMatchPatch._proc.stdin.write(tl) - DiffMatchPatch._proc.stdin.write(old) - DiffMatchPatch._proc.stdin.write(new) - buf = b"" - sizeb = b"" - SIZELEN = 4 - while len(sizeb) < SIZELEN: - try: - sizeb += DiffMatchPatch._proc.stdout.read(SIZELEN - len(sizeb)) - except TypeError: - pass - (size,) = struct.unpack("=I", sizeb) - while len(buf) < size: - buf += DiffMatchPatch._proc.stdout.read(size - len(buf)) + oldEncodedText = oldText.encode() + newEncodedText = newText.encode() + packedTextLength = struct.pack("=II", len(oldEncodedText), len(newEncodedText)) + DiffMatchPatch._proc.stdin.write(packedTextLength) + DiffMatchPatch._proc.stdin.write(oldEncodedText) + DiffMatchPatch._proc.stdin.write(newEncodedText) DiffMatchPatch._proc.stdin.flush() - DiffMatchPatch._proc.stdout.flush() + DIFF_LENGTH_BUFFER_SIZE = 4 + diffLengthBuffer = DiffMatchPatch._readData(DIFF_LENGTH_BUFFER_SIZE) + (diff_length,) = struct.unpack("=I", diffLengthBuffer) + diffBuffer = DiffMatchPatch._readData(diff_length) return [ line - for line in buf.decode("utf-8").splitlines() + for line in diffBuffer.decode("utf-8").splitlines() if line and not line.isspace() ] except Exception: @@ -107,11 +114,15 @@ def _terminate(self): if DiffMatchPatch._proc: log.debug("Terminating diff-match-patch proxy") # nvda_dmp exits when it receives two zero-length texts. - try: - DiffMatchPatch._proc.stdin.write(struct.pack("=II", 0, 0)) - DiffMatchPatch._proc.wait(timeout=5) - except Exception: - log.exception("Exception during DMP termination") + returnCode = DiffMatchPatch._proc.poll() + if returnCode is None: + try: + DiffMatchPatch._proc.stdin.write(struct.pack("=II", 0, 0)) + DiffMatchPatch._proc.wait(timeout=5) + except Exception: + log.exception("Exception during DMP termination") + else: + log.debug(f"Diff-match-patch proxy already terminated, return code is {returnCode}") DiffMatchPatch._proc = None From 0e3562d742b6ba997dffde21c2485aa2443a8c3c Mon Sep 17 00:00:00 2001 From: Danstiv <50794055+Danstiv@users.noreply.github.com> Date: Mon, 6 May 2024 15:07:26 +0700 Subject: [PATCH 2/6] Add type hints to _readData method Co-authored-by: Sean Budd --- source/diffHandler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/diffHandler.py b/source/diffHandler.py index 0cdc250a5b2..84a5a9ecbc3 100644 --- a/source/diffHandler.py +++ b/source/diffHandler.py @@ -63,7 +63,7 @@ def _getText(self, ti: TextInfo) -> str: return ti.text @classmethod - def _readData(cls, size): + def _readData(cls, size: int) -> bytes: """Reads from stdout, raises exception on EOF.""" buffer = b"" while (requiredLength := size - len(buffer)) > 0: From 6e68f9e41d14dacf762bd32a65663de2f5dec391 Mon Sep 17 00:00:00 2001 From: Danstiv <50794055+Danstiv@users.noreply.github.com> Date: Tue, 7 May 2024 14:22:56 +0700 Subject: [PATCH 3/6] Add changelog entry --- user_docs/en/changes.t2t | 1 + 1 file changed, 1 insertion(+) diff --git a/user_docs/en/changes.t2t b/user_docs/en/changes.t2t index ecf9d73d6b1..ed8ee2e28ef 100644 --- a/user_docs/en/changes.t2t +++ b/user_docs/en/changes.t2t @@ -97,6 +97,7 @@ There are many minor bug fixes for applications, such as Thunderbird, Adobe Read - Fixed bugs causing NVDA to hang. (#16393, #16394) - Backspace key is now working correctly in Gmail sign-in fields. (#16395) - +- Diff match patch proxy crash no longer causes NVDA to freeze. (#15850, #16027, @Danstiv) - Backspace now works correctly when using Nudi 6.1 with NVDA's "Handle keys from other applications" setting enabled. (#15822, @jcsteh) - Fixed a bug where audio coordinates would be played while the application is in sleep mode when "Play audio coordinates when mouse moves" is enabled. (#8059, @hwf1324) - In Adobe Reader, NVDA no longer ignores alternative text set on formulas in PDFs. (#12715) From 456f2dbdc53756e962f847e4749b15c43c30847a Mon Sep 17 00:00:00 2001 From: Danstiv <50794055+Danstiv@users.noreply.github.com> Date: Tue, 7 May 2024 19:46:54 +0700 Subject: [PATCH 4/6] Move comments --- source/diffHandler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/diffHandler.py b/source/diffHandler.py index 84a5a9ecbc3..1dc1d557f0e 100644 --- a/source/diffHandler.py +++ b/source/diffHandler.py @@ -85,11 +85,11 @@ def diff(self, newText: str, oldText: str) -> List[str]: return [] with DiffMatchPatch._lock: self._initialize() + oldEncodedText = oldText.encode() + newEncodedText = newText.encode() # Sizes are packed as 32-bit ints in native byte order. # Since nvda and nvda_dmp are running on the same Python # platform/version, this is okay. - oldEncodedText = oldText.encode() - newEncodedText = newText.encode() packedTextLength = struct.pack("=II", len(oldEncodedText), len(newEncodedText)) DiffMatchPatch._proc.stdin.write(packedTextLength) DiffMatchPatch._proc.stdin.write(oldEncodedText) From 4bac3a97017fba148a604d1f589f20649ae8710a Mon Sep 17 00:00:00 2001 From: Danstiv <50794055+Danstiv@users.noreply.github.com> Date: Wed, 8 May 2024 15:25:11 +0700 Subject: [PATCH 5/6] Improve naming --- source/diffHandler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/diffHandler.py b/source/diffHandler.py index 1dc1d557f0e..de3bc031516 100644 --- a/source/diffHandler.py +++ b/source/diffHandler.py @@ -66,8 +66,8 @@ def _getText(self, ti: TextInfo) -> str: def _readData(cls, size: int) -> bytes: """Reads from stdout, raises exception on EOF.""" buffer = b"" - while (requiredLength := size - len(buffer)) > 0: - chunk = cls._proc.stdout.read(requiredLength) + while (remainingLength := size - len(buffer)) > 0: + chunk = cls._proc.stdout.read(remainingLength) if chunk: buffer += chunk continue From e4c1537d854fa6e6c531b1330822fc1a8d5ea483 Mon Sep 17 00:00:00 2001 From: Danstiv <50794055+Danstiv@users.noreply.github.com> Date: Wed, 8 May 2024 15:28:22 +0700 Subject: [PATCH 6/6] Update changelog entry --- user_docs/en/changes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_docs/en/changes.md b/user_docs/en/changes.md index 96811999b1a..067b8a4663a 100644 --- a/user_docs/en/changes.md +++ b/user_docs/en/changes.md @@ -12,7 +12,7 @@ ### Bug Fixes -* Diff match patch proxy crash no longer causes NVDA to freeze. (#15850, #16027, @Danstiv) +* Improved reliability of automatic text readout, particularly in terminal applications. (#15850, #16027, @Danstiv) * Braille cursor routing is now much more reliable when a line contains one or more Unicode variation selectors or decomposed characters. (#10960, #16477, @mltony, @LeonarddeR) ### Changes for Developers