arelle-release 2.37.18__py3-none-any.whl → 2.37.20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of arelle-release might be problematic. Click here for more details.

Files changed (59) hide show
  1. arelle/CntlrWinMain.py +4 -1
  2. arelle/FileSource.py +2 -2
  3. arelle/ModelInstanceObject.py +1 -1
  4. arelle/ModelValue.py +1 -0
  5. arelle/ModelXbrl.py +1 -1
  6. arelle/PythonUtil.py +132 -24
  7. arelle/ViewWinTree.py +15 -9
  8. arelle/_version.py +2 -2
  9. arelle/formula/XPathContext.py +22 -22
  10. arelle/formula/XPathParser.py +2 -2
  11. arelle/plugin/OimTaxonomy/ModelValueMore.py +15 -0
  12. arelle/plugin/OimTaxonomy/ValidateDTS.py +484 -0
  13. arelle/plugin/OimTaxonomy/ViewXbrlTxmyObj.py +239 -0
  14. arelle/plugin/OimTaxonomy/XbrlAbstract.py +16 -0
  15. arelle/plugin/OimTaxonomy/XbrlConcept.py +68 -0
  16. arelle/plugin/OimTaxonomy/XbrlConst.py +261 -0
  17. arelle/plugin/OimTaxonomy/XbrlCube.py +91 -0
  18. arelle/plugin/OimTaxonomy/XbrlDimension.py +38 -0
  19. arelle/plugin/OimTaxonomy/XbrlDts.py +152 -0
  20. arelle/plugin/OimTaxonomy/XbrlEntity.py +16 -0
  21. arelle/plugin/OimTaxonomy/XbrlGroup.py +22 -0
  22. arelle/plugin/OimTaxonomy/XbrlImportedTaxonomy.py +22 -0
  23. arelle/plugin/OimTaxonomy/XbrlLabel.py +31 -0
  24. arelle/plugin/OimTaxonomy/XbrlNetwork.py +100 -0
  25. arelle/plugin/OimTaxonomy/XbrlProperty.py +28 -0
  26. arelle/plugin/OimTaxonomy/XbrlReference.py +33 -0
  27. arelle/plugin/OimTaxonomy/XbrlReport.py +24 -0
  28. arelle/plugin/OimTaxonomy/XbrlTableTemplate.py +35 -0
  29. arelle/plugin/OimTaxonomy/XbrlTaxonomy.py +93 -0
  30. arelle/plugin/OimTaxonomy/XbrlTaxonomyObject.py +154 -0
  31. arelle/plugin/OimTaxonomy/XbrlTransform.py +17 -0
  32. arelle/plugin/OimTaxonomy/XbrlTypes.py +23 -0
  33. arelle/plugin/OimTaxonomy/XbrlUnit.py +17 -0
  34. arelle/plugin/OimTaxonomy/__init__.py +1037 -0
  35. arelle/plugin/OimTaxonomy/resources/iso4217.json +4479 -0
  36. arelle/plugin/OimTaxonomy/resources/oim-taxonomy-schema.json +935 -0
  37. arelle/plugin/OimTaxonomy/resources/ref.json +333 -0
  38. arelle/plugin/OimTaxonomy/resources/transform-types.json +2481 -0
  39. arelle/plugin/OimTaxonomy/resources/types.json +727 -0
  40. arelle/plugin/OimTaxonomy/resources/utr.json +3046 -0
  41. arelle/plugin/OimTaxonomy/resources/xbrlSpec.json +1082 -0
  42. arelle/plugin/saveOIMTaxonomy.py +311 -0
  43. arelle/plugin/validate/NL/PluginValidationDataExtension.py +35 -1
  44. arelle/plugin/validate/NL/__init__.py +7 -0
  45. arelle/plugin/validate/NL/rules/nl_kvk.py +144 -2
  46. {arelle_release-2.37.18.dist-info → arelle_release-2.37.20.dist-info}/METADATA +3 -1
  47. {arelle_release-2.37.18.dist-info → arelle_release-2.37.20.dist-info}/RECORD +59 -24
  48. tests/integration_tests/validation/README.md +2 -0
  49. tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024.py +19 -5
  50. tests/integration_tests/validation/run_conformance_suites.py +10 -1
  51. tests/integration_tests/validation/validation_util.py +10 -5
  52. tests/unit_tests/arelle/test_frozen_dict.py +176 -0
  53. tests/unit_tests/arelle/test_frozen_ordered_set.py +315 -0
  54. tests/unit_tests/arelle/test_import.py +1 -0
  55. tests/unit_tests/arelle/test_ordered_set.py +272 -0
  56. {arelle_release-2.37.18.dist-info → arelle_release-2.37.20.dist-info}/WHEEL +0 -0
  57. {arelle_release-2.37.18.dist-info → arelle_release-2.37.20.dist-info}/entry_points.txt +0 -0
  58. {arelle_release-2.37.18.dist-info → arelle_release-2.37.20.dist-info}/licenses/LICENSE.md +0 -0
  59. {arelle_release-2.37.18.dist-info → arelle_release-2.37.20.dist-info}/top_level.txt +0 -0
arelle/CntlrWinMain.py CHANGED
@@ -949,7 +949,10 @@ class CntlrWinMain (Cntlr.Cntlr):
949
949
  self.parent.title(_("arelle - {0}").format(
950
950
  os.path.basename(modelXbrl.modelDocument.uri)))
951
951
  self.setValidateTooltipText()
952
- if modelXbrl.modelDocument.type in ModelDocument.Type.TESTCASETYPES:
952
+ if any(extViews(self, modelXbrl)
953
+ for extViews in pluginClassMethods("CntlrWinMain.Xbrl.Views")):
954
+ currentAction = "ext views provided"
955
+ elif modelXbrl.modelDocument.type in ModelDocument.Type.TESTCASETYPES:
953
956
  currentAction = "tree view of tests"
954
957
  ViewWinTests.viewTests(modelXbrl, self.tabWinTopRt)
955
958
  topView = modelXbrl.views[-1]
arelle/FileSource.py CHANGED
@@ -13,7 +13,7 @@ import struct
13
13
  import tarfile
14
14
  import zipfile
15
15
  import zlib
16
- from typing import IO, TYPE_CHECKING, Any, TextIO, cast
16
+ from typing import IO, TYPE_CHECKING, Any, Optional, TextIO, cast
17
17
 
18
18
  import regex as re
19
19
  from lxml import etree
@@ -452,7 +452,7 @@ class FileSource:
452
452
  def reportPackage(self) -> ReportPackage | None:
453
453
  try:
454
454
  self._reportPackage: ReportPackage | None
455
- return self._reportPackage
455
+ return cast(Optional[ReportPackage], self._reportPackage)
456
456
  except AttributeError:
457
457
  self._reportPackage = ReportPackage.fromFileSource(self)
458
458
  return self._reportPackage
@@ -1133,7 +1133,7 @@ class ModelContext(ModelObject):
1133
1133
  dimValue = self.modelXbrl.qnameDimensionDefaults.get(dimQname)
1134
1134
  return dimValue
1135
1135
 
1136
- def dimMemberQname(self, dimQname, includeDefaults=False):
1136
+ def dimMemberQname(self, dimQname: ModelValue.QName, includeDefaults: bool = False) -> ModelValue.QName | None:
1137
1137
  """(QName) -- QName of explicit dimension if reported (or defaulted if includeDefaults is True), else None"""
1138
1138
  dimValue = self.dimValue(dimQname)
1139
1139
  if isinstance(dimValue, (ModelDimensionValue,DimValuePrototype)) and dimValue.isExplicit:
arelle/ModelValue.py CHANGED
@@ -1013,6 +1013,7 @@ TypeXValue = Union[
1013
1013
  gMonthDay,
1014
1014
  gYearMonth,
1015
1015
  gYear,
1016
+ IsoDuration,
1016
1017
  Fraction,
1017
1018
  list[Optional[QName]],
1018
1019
  None,
arelle/ModelXbrl.py CHANGED
@@ -878,7 +878,7 @@ class ModelXbrl:
878
878
  def dimensionsInUse(self) -> set[Any]:
879
879
  self._dimensionsInUse: set[Any]
880
880
  try:
881
- return self._dimensionsInUse
881
+ return cast(set[Any], self._dimensionsInUse)
882
882
  except AttributeError:
883
883
  self._dimensionsInUse = set(dim.dimension
884
884
  for cntx in self.contexts.values() # use contextsInUse? slower?
arelle/PythonUtil.py CHANGED
@@ -6,18 +6,19 @@ do not convert 3 to 2
6
6
  '''
7
7
  from __future__ import annotations
8
8
 
9
+ import fractions
9
10
  import os
10
11
  import subprocess
11
12
  import sys
12
13
  from collections import OrderedDict
13
- from collections.abc import MappingView, MutableSet
14
+ from collections.abc import Iterable, Iterator, Mapping, MappingView, MutableSet, Set
14
15
  from decimal import Decimal
15
- from fractions import Fraction
16
- from typing import Any
16
+ from types import MappingProxyType
17
+ from typing import Any, Generic, TypeVar
17
18
 
18
19
  from arelle.typing import OptionalString
19
20
 
20
- STR_NUM_TYPES = (str, int, float, Decimal, Fraction)
21
+ STR_NUM_TYPES = (str, int, float, Decimal, fractions.Fraction)
21
22
 
22
23
  # python 3 unquote, because py2 unquote doesn't do utf-8 correctly
23
24
  def py3unquote(string, encoding='utf-8', errors='replace'):
@@ -163,71 +164,178 @@ class OrderedDefaultDict(OrderedDict):
163
164
  self[key] = _missingValue
164
165
  return _missingValue
165
166
 
166
- class OrderedSet(MutableSet):
167
167
 
168
- def __init__(self, iterable=None):
169
- self.end = end = []
170
- end += [None, end, end] # sentinel node for doubly linked list
171
- self.map = {} # key --> [key, prev, next]
168
+ T = TypeVar('T')
169
+
170
+ class OrderedSet(MutableSet[T]):
171
+ """
172
+ OrderedSet implementation copied from Python recipe:
173
+ https://code.activestate.com/recipes/576694/
174
+ """
175
+
176
+ def __init__(self, iterable: Iterable[T] | None = None) -> None:
177
+ self.end: list[Any] = []
178
+ end = self.end
179
+ end += [None, end, end] # sentinel node for doubly linked list
180
+ self.map: dict[T, list[Any]] = {} # key --> [key, prev, next]
172
181
  if iterable is not None:
173
- self |= iterable
182
+ self.update(iterable)
183
+
184
+ def __getitem__(self, index: int) -> T:
185
+ if not isinstance(index, int):
186
+ raise TypeError("Index must be an integer")
187
+ if index < 0:
188
+ index += len(self)
189
+ if index < 0 or index >= len(self):
190
+ raise IndexError("Index out of range")
191
+ end = self.end
192
+ curr = end[2]
193
+ for _ in range(index):
194
+ curr = curr[2]
195
+ return curr[0]
174
196
 
175
- def __len__(self):
197
+ def __len__(self) -> int:
176
198
  return len(self.map)
177
199
 
178
- def __contains__(self, key):
200
+ def __contains__(self, key: object) -> bool:
179
201
  return key in self.map
180
202
 
181
- def add(self, key):
203
+ def add(self, key: T) -> None:
182
204
  if key not in self.map:
183
205
  end = self.end
184
206
  curr = end[1]
185
207
  curr[2] = end[1] = self.map[key] = [key, curr, end]
186
208
 
187
- def update(self, other):
209
+ def update(self, other: Iterable[T]) -> None:
188
210
  for key in other:
189
211
  self.add(key)
190
212
 
191
- def discard(self, key):
213
+ def discard(self, key: T) -> None:
192
214
  if key in self.map:
193
215
  key, prev, next = self.map.pop(key)
194
216
  prev[2] = next
195
217
  next[1] = prev
196
218
 
197
- def __iter__(self):
219
+ def __iter__(self) -> Iterator[T]:
198
220
  end = self.end
199
221
  curr = end[2]
200
222
  while curr is not end:
201
223
  yield curr[0]
202
224
  curr = curr[2]
203
225
 
204
- def __reversed__(self):
226
+ def __reversed__(self) -> Iterator[T]:
205
227
  end = self.end
206
228
  curr = end[1]
207
229
  while curr is not end:
208
230
  yield curr[0]
209
231
  curr = curr[1]
210
232
 
211
- def pop(self, last=True):
233
+ def pop(self, last: bool = True) -> T:
212
234
  if not self:
213
235
  raise KeyError('set is empty')
214
236
  key = self.end[1][0] if last else self.end[2][0]
215
237
  self.discard(key)
216
238
  return key
217
239
 
218
- def __repr__(self):
240
+ def __repr__(self) -> str:
219
241
  if not self:
220
- return '%s()' % (self.__class__.__name__,)
221
- return '%s(%r)' % (self.__class__.__name__, list(self))
242
+ return f'{self.__class__.__name__}()'
243
+ return f'{self.__class__.__name__}({list(self)!r})'
222
244
 
223
- def __eq__(self, other):
245
+ def __eq__(self, other: object) -> bool:
224
246
  if isinstance(other, OrderedSet):
225
247
  return len(self) == len(other) and list(self) == list(other)
226
- return set(self) == set(other)
248
+ if isinstance(other, Iterable):
249
+ return set(self) == set(other)
250
+ return NotImplemented
251
+
252
+ class FrozenOrderedSet(Set[T]):
253
+ """
254
+ Like frozenset vs set, this is the immutable counterpart to OrderedSet.
255
+ Maintains insertion order and provides set-like operations without mutation.
256
+ """
257
+
258
+ def __init__(self, iterable: Iterable[T] | None = None) -> None:
259
+ if iterable is None:
260
+ self._items: tuple[T, ...] = ()
261
+ self._set: frozenset[T] = frozenset()
262
+ else:
263
+ unique_items = dict.fromkeys(iterable)
264
+ self._items = tuple(unique_items.keys())
265
+ self._set = frozenset(unique_items.keys())
266
+ self._hash: int | None = None
267
+
268
+ def __getitem__(self, index: int) -> T:
269
+ return self._items[index]
270
+
271
+ def __len__(self) -> int:
272
+ return len(self._items)
273
+
274
+ def __contains__(self, key: object) -> bool:
275
+ return key in self._set
276
+
277
+ def __iter__(self) -> Iterator[T]:
278
+ return iter(self._items)
279
+
280
+ def __reversed__(self) -> Iterator[T]:
281
+ return reversed(self._items)
282
+
283
+ def __repr__(self) -> str:
284
+ if not self:
285
+ return f'{self.__class__.__name__}()'
286
+ return f'{self.__class__.__name__}({self._items!r})'
287
+
288
+ def __eq__(self, other: object) -> bool:
289
+ if isinstance(other, (FrozenOrderedSet, OrderedSet)):
290
+ return len(self) == len(other) and list(self) == list(other)
291
+ if isinstance(other, Iterable):
292
+ return set(self) == set(other)
293
+ return NotImplemented
294
+
295
+ def __hash__(self) -> int:
296
+ if self._hash is None:
297
+ self._hash = hash(self._items)
298
+ return self._hash
299
+
300
+ KT = TypeVar('KT')
301
+ VT = TypeVar('VT')
302
+
303
+
304
+ class FrozenDict(Generic[KT, VT], Mapping[KT, VT]):
305
+ def __init__(self, data: Mapping[KT, VT] | None = None) -> None:
306
+ self._dict: Mapping[KT, VT] = MappingProxyType(dict(data) if data is not None else dict())
307
+ self._hash: int | None = None
308
+
309
+ def __getitem__(self, key: KT) -> VT:
310
+ return self._dict[key]
311
+
312
+ def __iter__(self) -> Iterator[KT]:
313
+ return iter(self._dict)
314
+
315
+ def __len__(self) -> int:
316
+ return len(self._dict)
317
+
318
+ def __repr__(self) -> str:
319
+ if not self:
320
+ return f'{self.__class__.__name__}()'
321
+ return f"{self.__class__.__name__}({self._dict})"
322
+
323
+ def __eq__(self, other: Any) -> bool:
324
+ if isinstance(other, FrozenDict):
325
+ return self._dict == other._dict
326
+ if isinstance(other, Mapping):
327
+ return self._dict == other
328
+ return NotImplemented
329
+
330
+ def __hash__(self) -> int:
331
+ if self._hash is None:
332
+ self._hash = hash(tuple(sorted(self._dict.items())))
333
+ return self._hash
334
+
227
335
 
228
336
  def Fraction(numerator,denominator=None):
229
337
  if denominator is None:
230
- if isinstance(numerator, (Fraction,str,Decimal)):
338
+ if isinstance(numerator, (fractions.Fraction,str,Decimal)):
231
339
  return Fraction(numerator)
232
340
  elif isinstance(numerator, Decimal) and isinstance(denominator, Decimal):
233
341
  return Fraction(int(numerator), int(denominator))
arelle/ViewWinTree.py CHANGED
@@ -220,14 +220,16 @@ class ViewTree:
220
220
  modelObject=self.modelXbrl.modelDocument, title=self.tabTitle, error=str(ex))
221
221
  self.menu = None
222
222
 
223
- def menuAddLabelRoles(self, includeConceptName=False, menulabel=None):
223
+ def menuAddLabelRoles(self, includeConceptName=False, menulabel=None, usedLabelroles=None):
224
224
  if self.menu:
225
225
  try:
226
226
  if menulabel is None: menulabel = _("Label role")
227
227
  rolesMenu = Menu(self.viewFrame, tearoff=0)
228
228
  self.menu.add_cascade(label=menulabel, menu=rolesMenu, underline=0)
229
- from arelle.ModelRelationshipSet import labelroles
230
- for x in labelroles(self.modelXbrl, includeConceptName):
229
+ if usedLabelroles is None: # provided for OIM taxonomy
230
+ from arelle.ModelRelationshipSet import labelroles
231
+ usedLabelroles = labelroles(self.modelXbrl, includeConceptName) # arelle infrastructure
232
+ for x in usedLabelroles:
231
233
  rolesMenu.add_command(label=x[0][1:], underline=0, command=lambda a=x[1]: self.setLabelrole(a))
232
234
  except Exception as ex: # tkinter menu problem maybe
233
235
  self.modelXbrl.info("arelle:internalException",
@@ -263,7 +265,7 @@ class ViewTree:
263
265
  modelObject=self.modelXbrl.modelDocument, title=self.tabTitle, error=str(ex))
264
266
  self.menu = None
265
267
 
266
- def menuAddViews(self, addClose=True, tabWin=None):
268
+ def menuAddViews(self, addClose=True, tabWin=None, additionalViews=None, additionalViewMethod=None):
267
269
  if self.menu:
268
270
  try:
269
271
  if tabWin is None: tabWin = self.tabWin
@@ -273,10 +275,14 @@ class ViewTree:
273
275
  if addClose:
274
276
  viewMenu.add_command(label=_("Close"), underline=0, command=self.close)
275
277
  viewMenu.add_cascade(label=_("Additional view"), menu=newViewsMenu, underline=0)
276
- newViewsMenu.add_command(label=_("Arcrole group..."), underline=0, command=lambda: self.newArcroleGroupView(tabWin))
277
- from arelle.ModelRelationshipSet import baseSetArcroles
278
- for x in baseSetArcroles(self.modelXbrl) + [( " Role Types","!CustomRoleTypes!"), (" Arcrole Types", "!CustomArcroleTypes!")]:
279
- newViewsMenu.add_command(label=x[0][1:], underline=0, command=lambda a=x[1]: self.newView(a, tabWin))
278
+ if not additionalViews: # 2.1 view
279
+ newViewsMenu.add_command(label=_("Arcrole group..."), underline=0, command=lambda: self.newArcroleGroupView(tabWin))
280
+ from arelle.ModelRelationshipSet import baseSetArcroles
281
+ for x in baseSetArcroles(self.modelXbrl) + [( " Role Types","!CustomRoleTypes!"), (" Arcrole Types", "!CustomArcroleTypes!")]:
282
+ newViewsMenu.add_command(label=x[0][1:], underline=0, command=lambda a=x[1]: self.newView(a, tabWin))
283
+ else:
284
+ for viewParams in additionalViews:
285
+ newViewsMenu.add_command(label=viewParams[2], underline=0, command=lambda a=additionalViewMethod, b=viewParams, c=additionalViews: a(self.modelXbrl, *b, additionalViews=c))
280
286
  except Exception as ex: # tkinter menu problem maybe
281
287
  self.modelXbrl.info("arelle:internalException",
282
288
  _("Exception creating context add-views menu in %(title)s: %(error)s"),
@@ -324,7 +330,7 @@ class ViewTree:
324
330
  self.sortImages = (PhotoImage(file=os.path.join(self.modelXbrl.modelManager.cntlr.imagesDir, "columnSortUp.gif")),
325
331
  PhotoImage(file=os.path.join(self.modelXbrl.modelManager.cntlr.imagesDir, "columnSortDown.gif")),
326
332
  PhotoImage())
327
- for col in ("#0",) + self.treeView["columns"]:
333
+ for col in ("#0",) + (self.treeView["columns"] or () ):
328
334
  self.treeView.heading(col, command=lambda c=col: self.sortColumn(c))
329
335
  if not startUnsorted:
330
336
  self.treeView.heading(initialSortCol, image=self.sortImages[not initialSortDirForward])
arelle/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '2.37.18'
21
- __version_tuple__ = version_tuple = (2, 37, 18)
20
+ __version__ = version = '2.37.20'
21
+ __version_tuple__ = version_tuple = (2, 37, 20)
@@ -351,8 +351,8 @@ class XPathContext:
351
351
  raise XPathException(p, 'err:XPST0017', _('Function named {0} does not have a custom or built-in implementation.').format(op))
352
352
  elif op in VALUE_OPS:
353
353
  # binary arithmetic operations and value comparisons
354
- s1 = self.atomize(p, resultStack.pop()) if len(resultStack) > 0 else []
355
- s2 = self.atomize(p, self.evaluate(p.args, contextItem=contextItem))
354
+ s1: Sequence[ContextItem] = self.atomize(p, resultStack.pop()) if len(resultStack) > 0 else []
355
+ s2: Sequence[ContextItem] = self.atomize(p, self.evaluate(p.args, contextItem=contextItem))
356
356
  # value comparisons
357
357
  if len(s1) > 1 or len(s2) > 1:
358
358
  raise XPathException(p, 'err:XPTY0004', _("Value operation '{0}' sequence length error").format(op))
@@ -383,35 +383,35 @@ class XPathContext:
383
383
  elif isinstance(op2, Decimal) and isinstance(op1, float):
384
384
  op2 = float(op2)
385
385
  if op == '+':
386
- result = op1 + op2
386
+ result = op1 + op2 # type: ignore[assignment,operator]
387
387
  elif op == '-':
388
- result = op1 - op2
388
+ result = op1 - op2 # type: ignore[assignment,operator]
389
389
  elif op == '*':
390
- result = op1 * op2
390
+ result = op1 * op2 # type: ignore[assignment,operator]
391
391
  elif op in ('div', 'idiv', "mod"):
392
392
  try:
393
393
  if op == 'div':
394
- result = op1 / op2
394
+ result = op1 / op2 # type: ignore[assignment,operator]
395
395
  elif op == 'idiv':
396
- result = op1 // op2
396
+ result = op1 // op2 # type: ignore[assignment,operator]
397
397
  elif op == 'mod':
398
- result = op1 % op2
398
+ result = op1 % op2 # type: ignore[assignment,operator]
399
399
  except ZeroDivisionError:
400
400
  raise XPathException(p, 'err:FOAR0001', _('Attempt to divide by zero: {0} {1} {2}.').format(op1, op, op2))
401
401
  elif op == 'ge':
402
- result = op1 >= op2
402
+ result = op1 >= op2 # type: ignore[operator]
403
403
  elif op == 'gt':
404
- result = op1 > op2
404
+ result = op1 > op2 # type: ignore[operator]
405
405
  elif op == 'le':
406
- result = op1 <= op2
406
+ result = op1 <= op2 # type: ignore[operator]
407
407
  elif op == 'lt':
408
- result = op1 < op2
408
+ result = op1 < op2 # type: ignore[operator]
409
409
  elif op == 'eq':
410
410
  result = op1 == op2
411
411
  elif op == 'ne':
412
412
  result = op1 != op2
413
413
  elif op == 'to':
414
- result = range(int(op1), int(op2) + 1)
414
+ result = range(int(op1), int(op2) + 1) # type: ignore[arg-type]
415
415
  elif op in GENERALCOMPARISON_OPS:
416
416
  # general comparisons
417
417
  s1 = self.atomize(p, resultStack.pop()) if len(resultStack) > 0 else []
@@ -440,23 +440,23 @@ class XPathContext:
440
440
  elif op in NODECOMPARISON_OPS:
441
441
  # node comparisons
442
442
  s1 = resultStack.pop() if len(resultStack) > 0 else []
443
- s2 = self.evaluate(p.args, contextItem=contextItem)
444
- if len(s1) > 1 or len(s2) > 1 or not self.isNodeSequence(s1) or not self.isNodeSequence(s2[0]):
443
+ _s2 = self.evaluate(p.args, contextItem=contextItem)
444
+ if len(s1) > 1 or len(_s2) > 1 or not self.isNodeSequence(s1) or not self.isNodeSequence(_s2[0]):
445
445
  raise XPathException(p, 'err:XPTY0004', _('Node comparison sequence error'))
446
- if len(s1) == 0 or len(s2[0]) == 0:
446
+ if len(s1) == 0 or len(_s2[0]) == 0:
447
447
  result = []
448
448
  else:
449
449
  n1 = s1[0]
450
- n2 = s2[0][0]
450
+ n2 = _s2[0][0]
451
451
  result = False
452
452
  for op1 in s1:
453
- for op2 in s2:
453
+ for _op2 in _s2:
454
454
  if op == 'is':
455
455
  result = n1 == n2
456
456
  elif op == '>>':
457
- result = op1 > op2
457
+ result = op1 > _op2 # type: ignore[operator]
458
458
  elif op == '<<':
459
- result = op1 <= op2
459
+ result = op1 <= _op2 # type: ignore[operator]
460
460
  if result:
461
461
  break
462
462
  elif op in COMBINING_OPS:
@@ -503,7 +503,7 @@ class XPathContext:
503
503
  if op == 'u+':
504
504
  result = op1
505
505
  elif op == 'u-':
506
- result = -op1
506
+ result = -op1 # type: ignore[assignment,operator]
507
507
  elif op == 'instance':
508
508
  result = False
509
509
  s1 = self.flattenSequence(resultStack.pop()) if len(resultStack) > 0 else []
@@ -671,7 +671,7 @@ class XPathContext:
671
671
  if hasPrevValue:
672
672
  prevValue = self.inScopeVars[rvQname]
673
673
  for rv in r:
674
- self.inScopeVars[rvQname] = rv # type: ignore[assignment]
674
+ self.inScopeVars[rvQname] = rv
675
675
  self.evaluateRangeVars(op, args[0], args[1:], contextItem, result)
676
676
  if op != 'for' and len(result) > 0:
677
677
  break # short circuit evaluation
@@ -9,7 +9,7 @@ import time
9
9
  import traceback
10
10
  from collections.abc import Iterable, Sequence
11
11
  from decimal import Decimal
12
- from typing import Any, TYPE_CHECKING, Union
12
+ from typing import Any, TYPE_CHECKING, Union, cast
13
13
  from xml.dom import minidom
14
14
 
15
15
  from pyparsing import (
@@ -954,7 +954,7 @@ def staticExpressionFunctionContext() -> minidom.Element:
954
954
  ' xmlns:fn="http://www.w3.org/2005/xpath-functions"'
955
955
  '/>'
956
956
  ).documentElement
957
- return _staticExpressionFunctionContext
957
+ return cast(minidom.Element, _staticExpressionFunctionContext)
958
958
 
959
959
 
960
960
  def parse(
@@ -0,0 +1,15 @@
1
+ """
2
+ See COPYRIGHT.md for copyright information.
3
+ """
4
+
5
+ from typing import TYPE_CHECKING, Optional, Set, Any
6
+
7
+ from arelle.ModelValue import QName
8
+
9
+ class QNameAt(QName):
10
+ def __init__(self, prefix: Optional[str], namespaceURI: Optional[str], localName: str, atSuffix:str = "end") -> None:
11
+ super(QNameAt, self).__init__(prefix, namespaceURI, localName)
12
+ self.atSuffix: str = atSuffix # The context suffix must be either @end or @start. If an @ value is not provided then the suffix defaults to @end.
13
+
14
+ class SQName(QName):
15
+ pass # no properties added to QName, just the localName part doesn't have to be an xml identifier