Skip to content

Commit 4dbd937

Browse files
authored
Merge 033efc2 into 816496f
2 parents 816496f + 033efc2 commit 4dbd937

9 files changed

Lines changed: 55 additions & 62 deletions

File tree

source/NVDAObjects/IAccessible/ia2Web.py

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@
55

66
"""Base classes with common support for browsers exposing IAccessible2.
77
"""
8-
import typing
8+
99
from typing import (
10-
Iterable,
10+
Generator,
11+
Optional,
12+
Tuple,
1113
)
1214
from ctypes import c_short
1315
from comtypes import COMError, BSTR
1416

1517
import oleacc
1618
from annotation import (
19+
_AnnotationRolesT,
1720
AnnotationTarget,
1821
AnnotationOrigin,
1922
)
@@ -57,21 +60,24 @@ def __bool__(self) -> bool:
5760
)
5861

5962
@property
60-
def targets(self) -> Iterable[AnnotationTarget]:
63+
def targets(self) -> Tuple[AnnotationTarget]:
6164
if not bool(self):
6265
# optimisation that avoids having to fetch details relations which may be a more costly procedure.
6366
if config.conf["debugLog"]["annotations"]:
6467
log.debug("no annotations available")
6568
return
6669

67-
ia2WebAnnotationTargetsGen = (
70+
return tuple(
6871
IA2WebAnnotationTarget(rel)
6972
for rel in self._originObj.detailsRelations
7073
)
71-
yield from ia2WebAnnotationTargetsGen
7274

7375
@property
74-
def roles(self) -> Iterable[controlTypes.Role]:
76+
def roles(self) -> _AnnotationRolesT:
77+
return tuple(self._rolesGenerator)
78+
79+
@property
80+
def _rolesGenerator(self) -> Generator[Optional[controlTypes.Role], None, None]:
7581
"""
7682
Since Chromium exposes the roles via the "details-roles" IA2Attributes, an optimisation can be used
7783
to return them.
@@ -95,11 +101,6 @@ def roles(self) -> Iterable[controlTypes.Role]:
95101
log.debug(f"detailsRole: {repr(detailsRole)}")
96102
yield detailsRole
97103

98-
@property
99-
def summaries(self) -> Iterable[str]:
100-
for target in self.targets:
101-
yield target.summary
102-
103104

104105
class Ia2Web(IAccessible):
105106
IAccessibleTableUsesTableCellIndexAttrib=True
@@ -129,7 +130,7 @@ def _get_positionInfo(self):
129130
return info
130131

131132
def _get_descriptionFrom(self) -> controlTypes.DescriptionFrom:
132-
ia2attrDescriptionFrom: typing.Optional[str] = self.IA2Attributes.get("description-from")
133+
ia2attrDescriptionFrom: Optional[str] = self.IA2Attributes.get("description-from")
133134
try:
134135
return controlTypes.DescriptionFrom(ia2attrDescriptionFrom)
135136
except ValueError:
@@ -144,14 +145,13 @@ def _get_annotations(self) -> "AnnotationOrigin":
144145
annotationOrigin = IA2WebAnnotation(self)
145146
return annotationOrigin
146147

147-
def _get_detailsSummary(self) -> typing.Optional[str]:
148+
def _get_detailsSummary(self) -> Optional[str]:
148149
log.warning(
149150
"NVDAObject.detailsSummary is deprecated. Use NVDAObject.annotations instead.",
150151
stack_info=True,
151152
)
152-
for summary in self.annotations.summaries:
153-
# just take the first for now.
154-
return summary
153+
# just take the first for now.
154+
return self.annotations.targets[0].summary
155155

156156
@property
157157
def hasDetails(self) -> bool:
@@ -161,14 +161,13 @@ def hasDetails(self) -> bool:
161161
)
162162
return bool(self.annotations)
163163

164-
def _get_detailsRole(self) -> typing.Optional[controlTypes.Role]:
164+
def _get_detailsRole(self) -> Optional[controlTypes.Role]:
165165
log.warning(
166166
"NVDAObject.detailsRole is deprecated. Use NVDAObject.annotations instead.",
167167
stack_info=True,
168168
)
169-
for role in self.annotations.roles:
170-
# just take the first for now.
171-
return role
169+
# just take the first for now.
170+
return self.annotations.roles[0]
172171

173172
def _get_isCurrent(self) -> controlTypes.IsCurrent:
174173
ia2attrCurrent: str = self.IA2Attributes.get("current", "false")

source/NVDAObjects/IAccessible/mozilla.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
# Copyright (C) 2006-2022 NV Access Limited, Peter Vágner
66

77
from typing import (
8-
Iterable,
8+
Generator,
99
Optional,
10+
Tuple,
1011
)
1112

1213
from annotation import (
14+
_AnnotationRolesT,
1315
AnnotationTarget,
1416
AnnotationOrigin,
1517
)
@@ -34,7 +36,7 @@ def summary(self) -> str:
3436
return self._target.summarizeInProcess()
3537

3638
@property
37-
def role(self) -> controlTypes.Role:
39+
def role(self) -> Optional[controlTypes.Role]:
3840
# details-roles is currently only defined in Chromium
3941
# this may diverge in Firefox in the future.
4042
from .chromium import supportedAriaDetailsRoles
@@ -46,7 +48,10 @@ def role(self) -> controlTypes.Role:
4648
log.debug(f"detailsRole: {repr(detailsRole)}")
4749
if detailsRole in supportedAriaDetailsRoles.values():
4850
return detailsRole
49-
raise ValueError(f"Unsupported aria details role: {detailsRole}")
51+
52+
if config.conf["debugLog"]["annotations"]:
53+
log.warning(f"Unsupported aria details role: {detailsRole}")
54+
return None
5055

5156
@property
5257
def targetObject(self) -> IAccessible:
@@ -68,28 +73,25 @@ def __bool__(self) -> bool:
6873
)
6974

7075
@property
71-
def targets(self) -> Iterable[MozAnnotationTarget]:
72-
detailsRelations = self._originObj.detailsRelations
73-
for rel in detailsRelations:
74-
yield MozAnnotationTarget(rel)
76+
def targets(self) -> Tuple[MozAnnotationTarget]:
77+
return tuple(MozAnnotationTarget(rel) for rel in self._originObj.detailsRelations)
78+
79+
80+
@property
81+
def roles(self) -> _AnnotationRolesT:
82+
return tuple(self._rolesGenerator)
7583

7684
@property
77-
def roles(self) -> Iterable[controlTypes.Role]:
85+
def _rolesGenerator(self) -> Generator[Optional[controlTypes.Role], None, None]:
7886
# Unlike base Ia2Web implementation, the details-roles
7987
# IA2 attribute is not exposed in Firefox.
8088
# Although slower, we have to fetch the details relations instead.
8189
for target in self.targets:
82-
# just take the first target for now.
8390
try:
8491
yield target.role
8592
except ValueError:
8693
log.error("Error getting role.", exc_info=True)
8794

88-
@property
89-
def summaries(self) -> Iterable[str]:
90-
for target in self.targets:
91-
yield target.summary
92-
9395

9496
class Mozilla(ia2Web.Ia2Web):
9597

@@ -148,18 +150,16 @@ def _get_detailsSummary(self) -> Optional[str]:
148150
"NVDAObject.detailsSummary is deprecated. Use NVDAObject.annotations instead.",
149151
stack_info=True,
150152
)
151-
for summary in self.annotations.summaries:
152-
# just take the first for now.
153-
return summary
153+
# just take the first for now.
154+
return self.annotations.targets[0].summary
154155

155156
def _get_detailsRole(self) -> Optional[controlTypes.Role]:
156157
log.warning(
157158
"NVDAObject.detailsRole is deprecated. Use NVDAObject.annotations instead.",
158159
stack_info=True,
159160
)
160-
for role in self.annotations.roles:
161-
# just take the first target for now.
162-
return role
161+
# just take the first target for now.
162+
return self.annotations.roles[0]
163163

164164
@property
165165
def hasDetails(self) -> bool:

source/NVDAObjects/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ def _get_annotations(self) -> typing.Optional[AnnotationOrigin]:
528528
detailsSummary: typing.Optional[str]
529529
"""
530530
Typing information for auto property _get_detailsSummary
531-
Deprecated, use self.annotations.roles instead.
531+
Deprecated, use self.annotations.targets instead.
532532
"""
533533

534534
def _get_detailsSummary(self) -> typing.Optional[str]:

source/annotation.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,17 @@
1313
from dataclasses import dataclass
1414
from typing import (
1515
TYPE_CHECKING,
16-
Iterable,
1716
List,
1817
Optional,
19-
Set,
20-
Union,
18+
Tuple,
2119
)
2220

2321
if TYPE_CHECKING:
2422
import controlTypes
2523
from NVDAObjects import NVDAObject
2624

2725

28-
_AnnotationRolesT = Set[Union[None, "controlTypes.Role"]]
26+
_AnnotationRolesT = Tuple[Optional["controlTypes.Role"]]
2927

3028

3129
class AnnotationTarget:
@@ -35,7 +33,7 @@ class AnnotationTarget:
3533
"""
3634

3735
@property
38-
def role(self) -> "controlTypes.Role":
36+
def role(self) -> Optional["controlTypes.Role"]:
3937
raise NotImplementedError
4038

4139
@property
@@ -65,15 +63,11 @@ def __bool__(self):
6563
raise NotImplementedError
6664

6765
@property
68-
def targets(self) -> Iterable[AnnotationTarget]:
66+
def targets(self) -> Tuple[AnnotationTarget]:
6967
raise NotImplementedError
7068

7169
@property
72-
def roles(self) -> Iterable["controlTypes.Role"]:
73-
raise NotImplementedError
74-
75-
@property
76-
def summaries(self) -> Iterable[str]:
70+
def roles(self) -> _AnnotationRolesT:
7771
raise NotImplementedError
7872

7973

source/braille.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ def _getAnnotationProperty(
529529
) -> str:
530530
# Translators: Braille when there are further details/annotations that can be fetched manually.
531531
genericDetailsRole = _("details")
532-
detailsRoles: _AnnotationRolesT = set(propertyValues.get("detailsRoles", []))
532+
detailsRoles: _AnnotationRolesT = propertyValues.get("detailsRoles", tuple())
533533
if not detailsRoles:
534534
log.debugWarning(
535535
"There should always be detailsRoles (at least a single None value) when hasDetails is true."

source/compoundDocuments.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def _getControlFieldForObject(self, obj: NVDAObject, ignoreEditableText=True):
156156
field['description'] = obj.description
157157
field['_description-from'] = obj.descriptionFrom
158158
field['hasDetails'] = bool(obj.annotations)
159-
field["detailsRoles"] = set(obj.annotations.roles if obj.annotations else [])
159+
field["detailsRoles"] = obj.annotations.roles if obj.annotations else tuple()
160160
# The user doesn't care about certain states, as they are obvious.
161161
states.discard(controlTypes.State.EDITABLE)
162162
states.discard(controlTypes.State.MULTILINE)

source/controlTypes/state.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def negativeDisplayString(self) -> str:
8989
OVERFLOWING = setBit(40)
9090
UNLOCKED = setBit(41)
9191
# HAS_ARIA_DETAILS is not used internally.
92-
# See instead NVDAObject.hasDetails introduced with commit aa351c55ada5254e061957097a9e0e638091b13d
92+
# See instead refer to NVDAObject.annotations
9393
# This enum value was initially added to controlTypes.py in commit d6787b8f47861f5e76aba68da7a13a217404196f
9494
HAS_ARIA_DETAILS = setBit(42) # Restored for backwards compat only.
9595
HASNOTE = setBit(43)

source/globalCommands.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2306,9 +2306,9 @@ def _getNvdaObjWithAnnotationUnderCaret(self) -> Optional[NVDAObject]:
23062306
if _isDebugLogCatEnabled:
23072307
log.debug(f"Trying with nvdaObject : {objAtStart}")
23082308

2309-
if objAtStart.detailsSummary:
2309+
if objAtStart.annotations:
23102310
if _isDebugLogCatEnabled:
2311-
log.debug(f"NVDAObjectAtStart of caret has details: {objAtStart.detailsSummary}")
2311+
log.debug(f"NVDAObjectAtStart of caret has details")
23122312
return objAtStart
23132313
elif api.getFocusObject():
23142314
# If fetching from the caret position fails, try via the focus object
@@ -2320,7 +2320,7 @@ def _getNvdaObjWithAnnotationUnderCaret(self) -> Optional[NVDAObject]:
23202320
if _isDebugLogCatEnabled:
23212321
log.debug(f"Trying focus object: {focus}")
23222322

2323-
if focus.detailsSummary:
2323+
if objAtStart.annotations:
23242324
if _isDebugLogCatEnabled:
23252325
log.debug("focus object has details, able to proceed")
23262326
return focus
@@ -2358,7 +2358,7 @@ def script_reportDetailsSummary(self, gesture: inputCore.InputGesture):
23582358
ui.message(_("No additional details"))
23592359
return
23602360

2361-
targets = list(objWithAnnotation.annotations.targets)
2361+
targets = objWithAnnotation.annotations.targets
23622362
if _isDebugLogCatEnabled:
23632363
log.debug(f"Number of targets: {len(targets)}")
23642364

source/speech/speech.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ def getObjectPropertiesSpeech( # noqa: C901
497497
elif value and name == "hasDetails":
498498
newPropertyValues['hasDetails'] = bool(obj.annotations)
499499
elif value and name == "detailsRoles":
500-
newPropertyValues["detailsRoles"] = set(obj.annotations.roles if obj.annotations else [])
500+
newPropertyValues["detailsRoles"] = obj.annotations.roles if obj.annotations else tuple()
501501
elif value and name == "descriptionFrom" and (
502502
obj.descriptionFrom == controlTypes.DescriptionFrom.ARIA_DESCRIPTION
503503
):
@@ -1814,7 +1814,7 @@ def getPropertiesSpeech( # noqa: C901
18141814
# are there further details
18151815
hasDetails = propertyValues.get('hasDetails', False)
18161816
if hasDetails:
1817-
detailsRoles: _AnnotationRolesT = propertyValues.get("detailsRoles", set())
1817+
detailsRoles: _AnnotationRolesT = propertyValues.get("detailsRoles", tuple())
18181818
if detailsRoles:
18191819
roleStrings = (role.displayString if role else _("details") for role in detailsRoles)
18201820
for roleString in roleStrings:
@@ -1929,7 +1929,7 @@ def getControlFieldSpeech( # noqa: C901
19291929
keyboardShortcut=attrs.get('keyboardShortcut', "")
19301930
isCurrent = attrs.get('current', controlTypes.IsCurrent.NO)
19311931
hasDetails = attrs.get('hasDetails', False)
1932-
detailsRoles: _AnnotationRolesT = set(attrs.get("detailsRoles", []))
1932+
detailsRoles: _AnnotationRolesT = attrs.get("detailsRoles", tuple())
19331933
placeholderValue=attrs.get('placeholder', None)
19341934
value=attrs.get('value',"")
19351935

0 commit comments

Comments
 (0)