arelle-release 2.37.17__py3-none-any.whl → 2.37.19__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.
- arelle/CntlrWinMain.py +4 -1
- arelle/ModelValue.py +1 -0
- arelle/PythonUtil.py +132 -24
- arelle/ViewWinTree.py +15 -9
- arelle/_version.py +2 -2
- arelle/plugin/OimTaxonomy/ModelValueMore.py +15 -0
- arelle/plugin/OimTaxonomy/ValidateDTS.py +484 -0
- arelle/plugin/OimTaxonomy/ViewXbrlTxmyObj.py +240 -0
- arelle/plugin/OimTaxonomy/XbrlAbstract.py +16 -0
- arelle/plugin/OimTaxonomy/XbrlConcept.py +67 -0
- arelle/plugin/OimTaxonomy/XbrlConst.py +261 -0
- arelle/plugin/OimTaxonomy/XbrlCube.py +91 -0
- arelle/plugin/OimTaxonomy/XbrlDimension.py +38 -0
- arelle/plugin/OimTaxonomy/XbrlDts.py +152 -0
- arelle/plugin/OimTaxonomy/XbrlEntity.py +16 -0
- arelle/plugin/OimTaxonomy/XbrlGroup.py +22 -0
- arelle/plugin/OimTaxonomy/XbrlImportedTaxonomy.py +22 -0
- arelle/plugin/OimTaxonomy/XbrlLabel.py +31 -0
- arelle/plugin/OimTaxonomy/XbrlNetwork.py +100 -0
- arelle/plugin/OimTaxonomy/XbrlProperty.py +28 -0
- arelle/plugin/OimTaxonomy/XbrlReference.py +33 -0
- arelle/plugin/OimTaxonomy/XbrlReport.py +24 -0
- arelle/plugin/OimTaxonomy/XbrlTableTemplate.py +35 -0
- arelle/plugin/OimTaxonomy/XbrlTaxonomy.py +93 -0
- arelle/plugin/OimTaxonomy/XbrlTaxonomyObject.py +154 -0
- arelle/plugin/OimTaxonomy/XbrlTransform.py +17 -0
- arelle/plugin/OimTaxonomy/XbrlTypes.py +23 -0
- arelle/plugin/OimTaxonomy/XbrlUnit.py +17 -0
- arelle/plugin/OimTaxonomy/__init__.py +1038 -0
- arelle/plugin/OimTaxonomy/resources/iso4217.json +4479 -0
- arelle/plugin/OimTaxonomy/resources/oim-taxonomy-schema.json +935 -0
- arelle/plugin/OimTaxonomy/resources/ref.json +333 -0
- arelle/plugin/OimTaxonomy/resources/transform-types.json +2481 -0
- arelle/plugin/OimTaxonomy/resources/types.json +727 -0
- arelle/plugin/OimTaxonomy/resources/utr.json +3046 -0
- arelle/plugin/OimTaxonomy/resources/xbrlSpec.json +1082 -0
- arelle/plugin/saveOIMTaxonomy.py +311 -0
- arelle/plugin/validate/NL/PluginValidationDataExtension.py +36 -2
- arelle/plugin/validate/NL/__init__.py +3 -0
- arelle/plugin/validate/NL/rules/nl_kvk.py +84 -2
- {arelle_release-2.37.17.dist-info → arelle_release-2.37.19.dist-info}/METADATA +2 -1
- {arelle_release-2.37.17.dist-info → arelle_release-2.37.19.dist-info}/RECORD +54 -19
- tests/integration_tests/validation/README.md +2 -0
- tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024.py +15 -4
- tests/integration_tests/validation/run_conformance_suites.py +10 -1
- tests/integration_tests/validation/validation_util.py +10 -5
- tests/unit_tests/arelle/test_frozen_dict.py +176 -0
- tests/unit_tests/arelle/test_frozen_ordered_set.py +315 -0
- tests/unit_tests/arelle/test_import.py +31 -0
- tests/unit_tests/arelle/test_ordered_set.py +272 -0
- {arelle_release-2.37.17.dist-info → arelle_release-2.37.19.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.17.dist-info → arelle_release-2.37.19.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.17.dist-info → arelle_release-2.37.19.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.37.17.dist-info → arelle_release-2.37.19.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
|
|
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/ModelValue.py
CHANGED
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
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
|
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 '
|
|
221
|
-
return '
|
|
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
|
-
|
|
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
|
-
|
|
230
|
-
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
See COPYRIGHT.md for copyright information.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Set, Any
|
|
6
|
+
|
|
7
|
+
from arelle.ModelValue import QName
|
|
8
|
+
|
|
9
|
+
class QNameAt(QName):
|
|
10
|
+
def __init__(self, prefix: str | None, namespaceURI: str | None, 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
|