Fix braille symbol resolution on NVDA 2026.1, and make the Punktum app module valid UTF-8 - #8
Fix braille symbol resolution on NVDA 2026.1, and make the Punktum app module valid UTF-8#8serrebidev wants to merge 3 commits into
Conversation
NVDA 2026.1 split the monolithic braille and brailleInput modules into the braille and braille.input packages. The old names still resolve through the module level __getattr__ installed by utils._deprecate, but only for reads: * Each read logs a warning plus a full stack trace. Loading the add-on writes around fifty of those before NVDA has finished starting. * Assigning to a moved name binds a new attribute on the old module instead of replacing the real one. NVDA core resolves getPropertiesBraille, getControlFieldBraille and getFormatFieldBraille from braille.regions.properties, and braille.regions.NVDAObject and braille.regions.textInfo bind two of them by name at import time, so the add-on's replacements were no longer reached at all. Role labels, document formatting indicators and undefined character representations silently stopped applying on 2026.1. Add brailleCompat, which resolves each symbol the add-on needs from its new home when the package layout is present and falls back to the pre-2026.1 names otherwise, keeping addon_minimumNVDAVersion 2025.3.3 supported. Module level functions are patched through patchBrailleFunction, which writes to every namespace that holds a binding rather than to braille alone. brailleInput.LOUIS_DOTS_IO_START was removed outright in 2026.1 with no public replacement, so its value is carried in brailleCompat for _translate. Tested on NVDA alpha-57493 (2026.1) and against stubs shaped like both module layouts: the add-on loads with no deprecation warnings and no behaviour change.
appModules/punktum.py is cp1252 encoded — the German comment on line 1 uses 0xFC for the u umlaut — and carries no PEP 263 encoding declaration. This is survivable on NVDA 2026.1 only because Python 3.13 does not decode comment bytes: non-UTF-8 sequences pass if they sit inside a comment, and fail anywhere else. Python 3.14 tightened that and rejects them in comments too: SyntaxError: Non-UTF-8 code starting with '\xfc' in file punktum.py on line 1, but no encoding declared So the module imports today and stops importing the moment NVDA moves to 3.14. It also means ruff cannot read the file at all (E902), which quietly excludes it from both `ruff check` and `ruff format --check`. Re-encode as UTF-8 and add the "# coding: utf-8" line the rest of the add-on's modules carry. The file is otherwise unchanged apart from ruff format, which could not reach it before.
a61f7c3 to
265a45a
Compare
`TextInfoRegion.update` still did a function-local `import brailleInput` before reading `handler.untranslatedBraille`. That binds `brailleInput` as a local for the whole function, so it shadowed the module-level `from .brailleCompat import brailleInput` added by this branch and went back to the deprecated flat module -- meaning `brailleInput.handler` logged a deprecation warning on every braille update, the one path where it hurts most. The late import is also no longer needed: brailleCompat is imported at module scope here and resolves braille.input itself, so the circular import the comment guarded against cannot occur. Only shows up with a braille display attached; with noBraille the region never updates, which is why it survived the first pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AUFZd6deUGuMmJYnLAvXxL
|
Pushed one more commit to this branch (6ee2cb9).
The late import is also no longer necessary: It only shows up with a braille display attached — with |
Two independent fixes, one commit each.
1. Resolve braille symbols from their NVDA 2026.1 locations
The problem
NVDA 2026.1 split the monolithic
brailleandbrailleInputmodules into thebrailleandbraille.inputpackages. The old names still resolve, through the module level__getattr__installed byutils._deprecate— but only for reads, and that has two consequences for this add-on.1a. The add-on's patches of the property/field functions no longer take effect.
_deprecate.handleDeprecationsonly intercepts attribute access. Assigning to a moved name binds a brand new attribute on the old module; it does not replace the real one. NVDA core now resolves these three from their new home:textInfos/__init__.pycallsbraille.regions.properties.getControlFieldBraille(...)NVDAObjects/__init__.py(andNVDAObjects/UIA,NVDAObjects/IAccessible,behaviors, several app modules) callbraille.regions.properties.getPropertiesBraille(...)and two of them are additionally bound by name at import time in the modules that use them:
braille/regions/NVDAObject.py:from .properties import getPropertiesBraillebraille/regions/textInfo.py:from .properties import getFormatFieldBrailleSo
braille.getPropertiesBraille = ...inpatches.pywas writing somewhere nothing reads any more. On NVDA 2026.1 the customizations built on those three functions — role labels, document formatting indicators, undefined character representations — silently stopped applying. Note that patching only the defining module would still not be enough, because of the two import-time bindings above.1b. Loading the add-on floods the log.
Every deprecated read logs a warning with a full stack trace. On a stock profile that is around fifty warnings and several hundred lines before NVDA has finished starting, which makes the log impractical for diagnosing anything else.
The change
Adds
globalPlugins/brailleEssentials/brailleCompat.py, which resolves each symbol the add-on needs from its new location when the package layout is present, and falls back to the pre-2026.1 names otherwise — soaddon_minimumNVDAVersion = 2025.3.3stays supported. Call sites import from it instead of reaching throughbraille/brailleInput.Module level functions go through
patchBrailleFunction(name, func), which writes to every namespace that actually holds a binding (found viavars(), so the deprecation__getattr__is never triggered and modules that merely re-export through the shim are not mistaken for holders).getBrailleFunction(name)is its counterpart for capturing the originals in_saveOriginals.Two details worth flagging:
brailleInput.LOUIS_DOTS_IO_STARTis aRemovedSymbolin 2026.1, not a moved one — it still returns0x8000today but raises onceNVDAState._allowDeprecatedAPI()goes false. Its value is carried inbrailleCompatfor_translate.patchBrailleFunctiondeliberately does not also set the name onbrailleunder the new layout; doing so would shadow the deprecation shim for other add-ons.Symbols covered:
Region,TextRegion,TextInfoRegion,NVDAObjectRegion,NVDAObjectHasUsefulText,BrailleHandler,BrailleBuffer,BrailleInputHandler,getPropertiesBraille,getControlFieldBraille,getFormatFieldBraille,getFocusRegions,getFocusContextRegions,getDisplayList, the four label dicts,TEXT_SEPARATOR,INPUT_START_IND,INPUT_END_IND,SELECTION_SHAPE,brailleInput.handlerandLOUIS_DOTS_IO_START.Testing
_saveOriginalsresolving all three functions throughgetBrailleFunctionon the real 2026.1 module tree also confirms the holder lookup finds them.brailleCompatagainst stub module trees shaped like both layouts, asserting that a patch reaches bothbraille.regions.propertiesand the import-time binding inbraille.regions.NVDAObject, that it does not shadow the shim onbraille, that the flat-layout path still patchesbraille, and that no code path triggers the deprecation__getattr__.No behaviour change is intended on NVDA versions before 2026.1.
2. Make the Punktum app module valid UTF-8
addon/appModules/punktum.pyis cp1252 encoded — the German comment on line 1 uses0xFCfor the u umlaut — and carries no PEP 263 encoding declaration.I originally filed this as "the module never loads". That was wrong, and testing against real Punktum corrected it. Python 3.13 does not decode bytes inside comments, so a non-UTF-8 sequence passes if it sits in a comment and fails anywhere else. Python 3.14 rejects them in comments too. Measured on this exact file:
SyntaxErrorSyntaxErrorSyntaxErrorSo the module imports fine today, and stops importing the moment NVDA moves to Python 3.14. Separately, ruff cannot read the file at all (
E902 stream did not contain valid UTF-8), which quietly excludes it from bothruff checkandruff format --check.The fix re-encodes as UTF-8 and adds the
# coding: utf-8line the rest of the add-on's modules carry. Nothing else changes exceptruff format, which could not reach the file before — two blank lines and a trailing comma.Testing
Installed Punktum V1.10 (
PunktumSoftware.zip, DBSV, August 2026) and ran it under NVDA alpha-57493, probing both candidate app modules with a module levellog.infoto see which one Python actually imported.3. Two findings from that test, for your call — no code change here
3a. The bundled copy is shadowed whenever the official Punktum add-on is installed.
Punktum ships its own NVDA add-on inside the download (
NVDA/punktum_0.3.0.nvda-addon, author Christian Punz,lastTestedNVDAVersion 2026.1.1), and Punktum's own introduction text tells users to install it. It providesappModules/punktum.pyas well.addonHandler.Addon.addToPackagePathdoespackage.__path__.insert(0, converted_path), and add-ons are processed alphabetically — sopunktumis inserted afterbrailleEssentialsand therefore lands earlier on__path__. Confirmed by probe:With the official add-on moved aside, the same probe shows this add-on's copy importing instead. So the bundled module only ever runs for users who have Punktum but not its add-on.
3b. The bundled copy is an older revision, and touches the braille handler off the main thread.
0.3.0 also returns
True/Falseso its global plugin can tell whether the gesture was handled, and guards each step withtry/except.Given both points, you may prefer to drop
appModules/punktum.pyfrom this add-on entirely and let the official add-on own it, or to resync it with 0.3.0. I have deliberately done neither here — this PR only makes the file valid UTF-8. Happy to follow up with whichever you would rather have.Lint
Measured against upstream
main, not a stashed tree.ruff check: the only difference is thatpunktum.py: E902 stream did not contain valid UTF-8is gone, because ruff can now read the file. No new findings.ruff format --check: the set of flagged files is byte for byte identical before and after. Both files this PR adds or rewrites —brailleCompat.pyandpunktum.py— are on the clean side.