Skip to content

Commit

Permalink
Move some helper functions from main IAccessibleHandler module to sat…
Browse files Browse the repository at this point in the history
…isfy Linter and decrease likelihood of circular imports (#12367)

Summary of the issue:
Linter complains about various imports in IAccessibleHandler not being at the top of file. In the current situation moving them to the top causes errors about imports from not fully initialized module.

Description of how this pull request fixes the issue:
At first I thought this can be solved by not importing NVDAObjects.IAccessible in the api module which is unused there anyway. Unfortunately removing this import just delayed the error but NVDA still was unable to start. Therefore I've moved parts of IAccessibleHandler which are imported by various other files in this package to two new files:

types.py contains typing information for IAccessibleHandler
utils.py contains various utility functions mostly introduced in #11521

Co-authored-by: buddsean <sean@nvaccess.org>
  • Loading branch information
lukaszgo1 and seanbudd authored May 12, 2021
1 parent 11b9950 commit 60bf94e
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 88 deletions.
93 changes: 12 additions & 81 deletions source/IAccessibleHandler/__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.

from typing import Tuple
import re
import struct
import weakref
from ctypes import (
Expand All @@ -21,97 +21,28 @@
from comtypes import IUnknown, IServiceProvider, COMError
import comtypes.client
import oleacc
import JABHandler
import UIAHandler

from comInterfaces import Accessibility as IA

from comInterfaces import IAccessible2Lib as IA2
import config


_winEventNameCache = {}


def getWinEventName(eventID):
""" Looks up the name of an EVENT_* winEvent constant. """
global _winEventNameCache
if not _winEventNameCache:
_winEventNameCache = {y: x for x, y in vars(winUser).items() if x.startswith('EVENT_')}
_winEventNameCache.update({y: x for x, y in vars(IA2).items() if x.startswith('IA2_EVENT_')})
name = _winEventNameCache.get(eventID)
if not name:
name = "unknown event ({eventID})"
return name


_objectIDNameCache = {}


def getObjectIDName(objectID):
""" Looks up the name of an OBJID_* winEvent constant. """
global _objectIDNameCache
if not _objectIDNameCache:
_objectIDNameCache = {y: x for x, y in vars(winUser).items() if x.startswith('OBJID_')}
name = _objectIDNameCache.get(objectID)
if not name:
name = str(objectID)
return name


def getWinEventLogInfo(window, objectID, childID, eventID=None, threadID=None):
"""
Formats the given winEvent parameters into a printable string.
window, objectID and childID are mandatory,
but eventID and threadID are optional.
"""
windowClassName = winUser.getClassName(window) or "unknown"
objectIDName = getObjectIDName(objectID)
processID = winUser.getWindowThreadProcessID(window)[0]
if processID:
processName = appModuleHandler.getAppModuleFromProcessID(processID).appName
else:
processName = "unknown application"
messageList = []
if eventID is not None:
eventName = getWinEventName(eventID)
messageList.append(f"{eventName}")
messageList.append(
f"window {window} ({windowClassName}), objectID {objectIDName}, childID {childID}, "
f"process {processID} ({processName})"
)
if threadID is not None:
messageList.append(f"thread {threadID}")
return ", ".join(messageList)


def isMSAADebugLoggingEnabled():
""" Whether the user has configured NVDA to log extra information about MSAA events. """
return config.conf["debugLog"]["MSAA"]


IAccessibleObjectIdentifierType = Tuple[
int, # windowHandle
int, # objectID
int, # childID
]

from . import internalWinEventHandler

from logHandler import log
import JABHandler
import eventHandler
import winUser
import api
import NVDAObjects.IAccessible
import NVDAObjects.window
import appModuleHandler
import mouseHandler
import controlTypes
import keyboardHandler
import core
import re
import eventHandler
import keyboardHandler
from logHandler import log
import mouseHandler
import NVDAObjects.IAccessible
import NVDAObjects.window
import winUser

from . import internalWinEventHandler
from .orderedWinEventLimiter import MENU_EVENTIDS
from .utils import getWinEventLogInfo, getWinEventName, isMSAADebugLoggingEnabled


# Special Mozilla gecko MSAA constant additions
NAVRELATION_LABEL_FOR = 0x1002
Expand Down
3 changes: 1 addition & 2 deletions source/IAccessibleHandler/internalWinEventHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@

import core
import winUser
from . import getWinEventLogInfo
from . import isMSAADebugLoggingEnabled
from .utils import getWinEventLogInfo, isMSAADebugLoggingEnabled

from comInterfaces import IAccessible2Lib as IA2

Expand Down
4 changes: 2 additions & 2 deletions source/IAccessibleHandler/orderedWinEventLimiter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import itertools

import winUser
from . import IAccessibleObjectIdentifierType
from .types import IAccessibleObjectIdentifierType
from logHandler import log
from . import isMSAADebugLoggingEnabled, getWinEventLogInfo
from .utils import getWinEventLogInfo, isMSAADebugLoggingEnabled


MAX_WINEVENTS_PER_THREAD = 10
Expand Down
17 changes: 17 additions & 0 deletions source/IAccessibleHandler/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2020-2021 NV Access Limited
# 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


"""Types used in IAccessibleHander.
Kept here so they can be re-used without having to worry about circular imports.
"""

from typing import Tuple

IAccessibleObjectIdentifierType = Tuple[
int, # windowHandle
int, # objectID
int, # childID
]
74 changes: 74 additions & 0 deletions source/IAccessibleHandler/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2020-2021 NV Access Limited
# 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


"""Utility functions for IAccessibleHander.
Kept here so they can be re-used without having to worry about circular imports.
"""

import appModuleHandler
from comInterfaces import IAccessible2Lib as IA2
import config
import winUser


_winEventNameCache = {}


def getWinEventName(eventID):
""" Looks up the name of an EVENT_* winEvent constant. """
global _winEventNameCache
if not _winEventNameCache:
_winEventNameCache = {y: x for x, y in vars(winUser).items() if x.startswith('EVENT_')}
_winEventNameCache.update({y: x for x, y in vars(IA2).items() if x.startswith('IA2_EVENT_')})
name = _winEventNameCache.get(eventID)
if not name:
name = "unknown event ({eventID})"
return name


_objectIDNameCache = {}


def getObjectIDName(objectID):
""" Looks up the name of an OBJID_* winEvent constant. """
global _objectIDNameCache
if not _objectIDNameCache:
_objectIDNameCache = {y: x for x, y in vars(winUser).items() if x.startswith('OBJID_')}
name = _objectIDNameCache.get(objectID)
if not name:
name = str(objectID)
return name


def getWinEventLogInfo(window, objectID, childID, eventID=None, threadID=None):
"""
Formats the given winEvent parameters into a printable string.
window, objectID and childID are mandatory,
but eventID and threadID are optional.
"""
windowClassName = winUser.getClassName(window) or "unknown"
objectIDName = getObjectIDName(objectID)
processID = winUser.getWindowThreadProcessID(window)[0]
if processID:
processName = appModuleHandler.getAppModuleFromProcessID(processID).appName
else:
processName = "unknown application"
messageList = []
if eventID is not None:
eventName = getWinEventName(eventID)
messageList.append(f"{eventName}")
messageList.append(
f"window {window} ({windowClassName}), objectID {objectIDName}, childID {childID}, "
f"process {processID} ({processName})"
)
if threadID is not None:
messageList.append(f"thread {threadID}")
return ", ".join(messageList)


def isMSAADebugLoggingEnabled():
""" Whether the user has configured NVDA to log extra information about MSAA events. """
return config.conf["debugLog"]["MSAA"]
3 changes: 0 additions & 3 deletions source/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,14 @@

"""General functions for NVDA"""

import ctypes
import config
import textInfos
import review
import globalVars
from logHandler import log
import ui
import treeInterceptorHandler
import virtualBuffers
import NVDAObjects
import NVDAObjects.IAccessible
import winUser
import controlTypes
import eventHandler
Expand Down
1 change: 1 addition & 0 deletions user_docs/en/changes.t2t
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ What's New in NVDA
- `speech.speechMode_off` becomes `speech.SpeechMode.off`
- `speech.speechMode_beeps` becomes `speech.SpeechMode.beeps`
- `speech.speechMode_talk` becomes `speech.SpeechMode.talk`
- `IAccessibleHandler.IAccessibleObjectIdentifierType` is now `IAccessibleHandler.types.IAccessibleObjectIdentifierType`. (#12367)


= 2020.4 =
Expand Down

0 comments on commit 60bf94e

Please sign in to comment.