Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ dependencies = [
"mdx-gh-links==0.4",
"l2m4m==1.0.4",
"pymdown-extensions==10.17.1",
"regex>=2026.4.4",
]

[project.urls]
Expand Down
2 changes: 1 addition & 1 deletion source/IAccessibleHandler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# should be used to export it.
from .types import RelationType # noqa: F401

import re
import _regex as re
import struct
from typing import (
Optional,
Expand Down
2 changes: 1 addition & 1 deletion source/NVDAObjects/IAccessible/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import comtypes.client
import ctypes
import os
import re
import _regex as re
import sys
import itertools
import importlib
Expand Down
2 changes: 1 addition & 1 deletion source/NVDAObjects/IAccessible/msOffice.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import winUser
from . import IAccessible, getNVDAObjectFromEvent
import eventHandler
import re
import _regex as re

"""Miscellaneous support for Microsoft Office applications.
"""
Expand Down
2 changes: 1 addition & 1 deletion source/NVDAObjects/JAB/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# See the file COPYING for more details.

import ctypes
import re
import _regex as re
from typing import (
Any,
)
Expand Down
2 changes: 1 addition & 1 deletion source/NVDAObjects/UIA/excel.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import ui
from logHandler import log
from . import UIA
import re
import _regex as re


class ExcelCustomProperties:
Expand Down
2 changes: 1 addition & 1 deletion source/NVDAObjects/UIA/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from logHandler import log
import controlTypes
import cursorManager
import re
import _regex as re
import aria
import textInfos
from UIAHandler.browseMode import (
Expand Down
2 changes: 1 addition & 1 deletion source/NVDAObjects/window/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

import re
import _regex as re
import ctypes
import ctypes.wintypes
from winBindings import user32
Expand Down
2 changes: 1 addition & 1 deletion source/NVDAObjects/window/_msOfficeChart.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import controlTypes
import colors
import inputCore
import re
import _regex as re
from logHandler import log
import browseMode
from scriptHandler import script
Expand Down
2 changes: 1 addition & 1 deletion source/NVDAObjects/window/excel.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import wx
import time
import winsound
import re
import _regex as re
import uuid
import NVDAHelper
import oleacc
Expand Down
7 changes: 6 additions & 1 deletion source/_bridge/runtimes/synthDriverHost/main.pyw
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import tempfile
from winBindings.kernel32 import GetCurrentProcessId

oldRecordFactory = logging.getLogRecordFactory()


def recordFactory(*args, **kwargs):
record = oldRecordFactory(*args, **kwargs)
frame = inspect.currentframe()
Expand All @@ -27,6 +29,8 @@ def recordFactory(*args, **kwargs):
# co_qualname may be unavailable for some frames; in that case, keep the default record.name
pass
return record


logging.setLogRecordFactory(recordFactory)

exeName = os.path.splitext(os.path.basename(sys.executable))[0]
Expand All @@ -36,7 +40,7 @@ logging.basicConfig(
filename=logPath,
filemode="w",
level=logging.DEBUG,
format="%(levelname)s - %(module)s.%(name)s (%(asctime)s):\n%(message)s"
format="%(levelname)s - %(module)s.%(name)s (%(asctime)s):\n%(message)s",
)
log = logging.getLogger(exeName)
# No comtypes debug logging
Expand All @@ -46,6 +50,7 @@ log.info(f"Logging initialized, log file: {logPath}")
try:
gettext.install("nvda", names=["pgettext", "npgettext", "ngettext"])
import core

core.main()
except Exception:
log.exception("Unhandled exception")
49 changes: 49 additions & 0 deletions source/_regex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# A part of NonVisual Desktop Access (NVDA)
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.
# Copyright (C) 2026 NV Access Limited

"""Internal regex backend shim.

Forwards attribute access to either stdlib :mod:`re` or the third-party :mod:`regex`
module. The backend is chosen once by :func:`initialize` from the ``regexBackend``
feature flag in user config and is then frozen for the lifetime of the process.
Changes to the setting therefore require an NVDA restart.

Intended usage::

import _regex as re

pattern = re.compile(r"\\w+", re.IGNORECASE)

Before :func:`initialize` runs (early bootstrap, unit tests outside startup),
attribute access falls back to stdlib :mod:`re`.
"""

import re as _re


_backend = _re


def initialize() -> None:
"""Resolve the regex backend from user config and freeze it for this process.

Call once after :func:`config.initialize`. Subsequent calls are ignored.
"""
global _backend
if _backend is not _re:
return
import config
from config.featureFlagEnums import RegexBackendFlag

if config.conf["featureFlag"]["regexBackend"].calculated() != RegexBackendFlag.REGEX:
return
import regex

regex.DEFAULT_VERSION = regex.VERSION1
_backend = regex


def __getattr__(name: str):
return getattr(_backend, name)
2 changes: 1 addition & 1 deletion source/addonAPIVersion.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


import buildVersion
import re
import _regex as re


"""
Expand Down
2 changes: 1 addition & 1 deletion source/appModules/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import api
import appModuleHandler
import controlTypes
import re
import _regex as re
from collections import deque
from logHandler import log
from NVDAObjects.behaviors import EditableTextBase
Expand Down
2 changes: 1 addition & 1 deletion source/appModules/foobar2000.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# See the file COPYING for more details.

from datetime import datetime
import re
import _regex as re
from typing import (
Dict,
NamedTuple,
Expand Down
2 changes: 1 addition & 1 deletion source/appModules/lync.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import appModuleHandler
from logHandler import log

import re
import _regex as re


class NetUIRicherLabel(UIA):
Expand Down
2 changes: 1 addition & 1 deletion source/appModules/securecrt.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

"""App module for SecureCRT"""

import re
import _regex as re
import oleacc
from NVDAObjects.behaviors import Terminal
from NVDAObjects.window import DisplayModelEditableText, DisplayModelLiveText
Expand Down
2 changes: 1 addition & 1 deletion source/bdDetect.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import config
import appModuleHandler
from baseObject import AutoPropertyObject
import re
import _regex as re
from winAPI import messageWindow
import extensionPoints
from logHandler import log
Expand Down
2 changes: 1 addition & 1 deletion source/braille.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
import brailleDisplayDrivers
import inputCore
import brailleTables
import re
import _regex as re
import scriptHandler
import collections
import extensionPoints
Expand Down
2 changes: 1 addition & 1 deletion source/brailleDisplayDrivers/eurobraille/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from collections import defaultdict
from typing import Dict, Any, List, Union
import re
import _regex as re

from io import BytesIO
import serial
Expand Down
2 changes: 1 addition & 1 deletion source/brailleDisplayDrivers/seikantk.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
see www.seika-braille.com for more details
"""

import re
import _regex as re
from io import BytesIO
import typing
from typing import Dict, List, Optional, Set
Expand Down
2 changes: 1 addition & 1 deletion source/browseMode.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import winsound
import time
import weakref
import re
import _regex as re
from comtypes import COMError

import wx
Expand Down
2 changes: 1 addition & 1 deletion source/characterProcessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import os
import codecs
import collections
import re
import _regex as re
from typing import (
Callable,
Dict,
Expand Down
2 changes: 1 addition & 1 deletion source/colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from collections import namedtuple
import colorsys
from ctypes.wintypes import COLORREF
import re
import _regex as re
from functools import lru_cache
from typing import Union

Expand Down
1 change: 1 addition & 0 deletions source/config/configSpec.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@
cancelExpiredFocusSpeech = integer(0, 2, default=0)
# 0: Only in test versions, 1: Yes, 2: No
playErrorSound = integer(0, 2, default=0)
regexBackend = featureFlag(optionsEnum="RegexBackendFlag", behaviorOfDefault="RE")

[addonStore]
automaticUpdates = option("notify", "update", "disabled", default="notify")
Expand Down
17 changes: 17 additions & 0 deletions source/config/featureFlagEnums.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,23 @@ def _displayStringLabels(self) -> dict["FontFormattingBrailleModeFlag", str]:
}


class RegexBackendFlag(DisplayStringEnum):
"""Feature flag for selecting the regular expression backend."""

DEFAULT = enum.auto()
RE = enum.auto()
REGEX = enum.auto()

@property
def _displayStringLabels(self) -> dict["RegexBackendFlag", str]:
return {
# Translators: Label for the regex backend feature flag option using stdlib re.
RegexBackendFlag.RE: _("Python re"),
# Translators: Label for the regex backend feature flag option using the regex module.
RegexBackendFlag.REGEX: _("regex"),
}


def getAvailableEnums() -> typing.Generator[typing.Tuple[str, FlagValueEnum], None, None]:
for name, value in globals().items():
if (
Expand Down
2 changes: 1 addition & 1 deletion source/controlTypes/formatFields.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# See the file COPYING for more details.
# Copyright (C) 2021-2023 NV Access Limited, Cyrille Bougot

import re
import _regex as re
from typing import Dict

from logHandler import log
Expand Down
3 changes: 3 additions & 0 deletions source/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,9 @@ def main():
import config

config.initialize()
import _regex

_regex.initialize()
if config.conf["development"]["enableScratchpadDir"]:
log.info("Developer Scratchpad mode enabled")
if languageHandler.isLanguageForced():
Expand Down
2 changes: 1 addition & 1 deletion source/documentNavigation/sentenceHelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# This file may be used under the terms of the GNU General Public License, version 2 or later.
# For more details see: https://www.gnu.org/licenses/gpl-2.0.html

import re
import _regex as re
from typing import Optional

WESTERN_TERMINATORS = "[.?!](?: |\r|\n|$)+"
Expand Down
2 changes: 1 addition & 1 deletion source/gui/inputGestures.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# See the file COPYING for more details.

import itertools
import re
import _regex as re
from typing import Tuple, Union, Dict

import wx
Expand Down
Loading
Loading