arelle-release 2.36.28__py3-none-any.whl → 2.36.29__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/ValidateDuplicateFacts.py +112 -49
- arelle/_version.py +2 -2
- {arelle_release-2.36.28.dist-info → arelle_release-2.36.29.dist-info}/METADATA +1 -1
- {arelle_release-2.36.28.dist-info → arelle_release-2.36.29.dist-info}/RECORD +8 -8
- {arelle_release-2.36.28.dist-info → arelle_release-2.36.29.dist-info}/LICENSE.md +0 -0
- {arelle_release-2.36.28.dist-info → arelle_release-2.36.29.dist-info}/WHEEL +0 -0
- {arelle_release-2.36.28.dist-info → arelle_release-2.36.29.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.36.28.dist-info → arelle_release-2.36.29.dist-info}/top_level.txt +0 -0
arelle/ValidateDuplicateFacts.py
CHANGED
|
@@ -1,31 +1,38 @@
|
|
|
1
1
|
"""
|
|
2
2
|
See COPYRIGHT.md for copyright information.
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
from __future__ import annotations
|
|
5
6
|
|
|
6
|
-
from _decimal import Decimal
|
|
7
7
|
from collections import defaultdict
|
|
8
8
|
from collections.abc import Iterator
|
|
9
9
|
from dataclasses import dataclass, field
|
|
10
|
-
from enum import
|
|
10
|
+
from enum import Enum, Flag, auto
|
|
11
11
|
from functools import cached_property
|
|
12
12
|
from math import isnan
|
|
13
|
-
from typing import
|
|
13
|
+
from typing import Any, SupportsFloat, cast
|
|
14
|
+
|
|
15
|
+
from _decimal import Decimal
|
|
14
16
|
|
|
15
17
|
from arelle import XmlValidateConst
|
|
16
|
-
from arelle.ModelInstanceObject import
|
|
18
|
+
from arelle.ModelInstanceObject import ModelContext, ModelFact, ModelUnit
|
|
17
19
|
from arelle.ModelValue import DateTime, QName, TypeXValue
|
|
18
20
|
from arelle.ModelXbrl import ModelXbrl
|
|
19
|
-
from arelle.ValidateXbrlCalcs import rangeValue, inferredDecimals
|
|
20
21
|
from arelle.typing import TypeGetText
|
|
22
|
+
from arelle.ValidateXbrlCalcs import inferredDecimals, rangeValue
|
|
23
|
+
|
|
21
24
|
_: TypeGetText
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
@dataclass(frozen=True)
|
|
25
28
|
class DuplicateFactSet:
|
|
26
29
|
facts: list[ModelFact]
|
|
27
|
-
_inferredDecimals: dict[ModelFact, float | int | None] = field(
|
|
28
|
-
|
|
30
|
+
_inferredDecimals: dict[ModelFact, float | int | None] = field(
|
|
31
|
+
init=False, default_factory=dict
|
|
32
|
+
)
|
|
33
|
+
_ranges: dict[ModelFact, tuple[Decimal, Decimal]] = field(
|
|
34
|
+
init=False, default_factory=dict
|
|
35
|
+
)
|
|
29
36
|
|
|
30
37
|
def __iter__(self) -> Iterator[ModelFact]:
|
|
31
38
|
return iter(self.facts)
|
|
@@ -80,7 +87,9 @@ class DuplicateFactSet:
|
|
|
80
87
|
"""
|
|
81
88
|
:return: Whether any facts in the set are complete duplicates of each other.
|
|
82
89
|
"""
|
|
83
|
-
decimalsValueMap: dict[float | int | None, set[TypeFactValueEqualityKey]] =
|
|
90
|
+
decimalsValueMap: dict[float | int | None, set[TypeFactValueEqualityKey]] = (
|
|
91
|
+
defaultdict(set)
|
|
92
|
+
)
|
|
84
93
|
for fact in self.facts:
|
|
85
94
|
decimals = self.getDecimals(fact)
|
|
86
95
|
value = getFactValueEqualityKey(fact)
|
|
@@ -181,7 +190,10 @@ class DuplicateFactSet:
|
|
|
181
190
|
seenKeys = set()
|
|
182
191
|
results = []
|
|
183
192
|
for fact in self.facts:
|
|
184
|
-
|
|
193
|
+
xValue = (
|
|
194
|
+
tuple(fact.xValue) if isinstance(fact.xValue, list) else fact.xValue
|
|
195
|
+
)
|
|
196
|
+
key = (fact.decimals, xValue)
|
|
185
197
|
if key in seenKeys:
|
|
186
198
|
continue
|
|
187
199
|
seenKeys.add(key)
|
|
@@ -208,7 +220,7 @@ class DuplicateFactSet:
|
|
|
208
220
|
sortedDecimals = sorted(decimalsMap.keys())
|
|
209
221
|
results = set(facts)
|
|
210
222
|
|
|
211
|
-
for a, decimalLower in enumerate(sortedDecimals[:len(sortedDecimals)-1]):
|
|
223
|
+
for a, decimalLower in enumerate(sortedDecimals[: len(sortedDecimals) - 1]):
|
|
212
224
|
groupLower = decimalsMap[decimalLower]
|
|
213
225
|
for factA in groupLower:
|
|
214
226
|
lowerA, upperA = self.getRange(factA)
|
|
@@ -216,7 +228,7 @@ class DuplicateFactSet:
|
|
|
216
228
|
continue
|
|
217
229
|
remove = False
|
|
218
230
|
# Iterate through each higher decimals group
|
|
219
|
-
for b, decimalHigher in enumerate(sortedDecimals[a+1:]):
|
|
231
|
+
for b, decimalHigher in enumerate(sortedDecimals[a + 1 :]):
|
|
220
232
|
groupHigher = decimalsMap[decimalHigher]
|
|
221
233
|
for factB in groupHigher:
|
|
222
234
|
lowerB, upperB = self.getRange(factB)
|
|
@@ -241,7 +253,7 @@ class DuplicateFactSet:
|
|
|
241
253
|
return self.deduplicateCompleteSubsets(), None
|
|
242
254
|
if not self.areAllConsistent:
|
|
243
255
|
# If facts are not all consistent, we will only perform complete deduplication
|
|
244
|
-
return self.deduplicateCompleteSubsets(),
|
|
256
|
+
return self.deduplicateCompleteSubsets(), "Set has inconsistent facts"
|
|
245
257
|
selectedFact = self.facts[0]
|
|
246
258
|
maxDecimals = self.getDecimals(selectedFact)
|
|
247
259
|
assert maxDecimals is not None
|
|
@@ -259,9 +271,11 @@ class DuplicateFactSet:
|
|
|
259
271
|
:param fact:
|
|
260
272
|
:return: Retrieve cached inferred decimals value for the provided fact.
|
|
261
273
|
"""
|
|
262
|
-
assert fact in self.facts,
|
|
274
|
+
assert fact in self.facts, "Attempted to get decimals for fact not in set"
|
|
263
275
|
if fact not in self._inferredDecimals:
|
|
264
|
-
self._inferredDecimals[fact] =
|
|
276
|
+
self._inferredDecimals[fact] = (
|
|
277
|
+
None if fact.decimals is None else inferredDecimals(fact)
|
|
278
|
+
)
|
|
265
279
|
return self._inferredDecimals[fact]
|
|
266
280
|
|
|
267
281
|
def getRange(self, fact: ModelFact) -> tuple[Decimal, Decimal]:
|
|
@@ -270,8 +284,8 @@ class DuplicateFactSet:
|
|
|
270
284
|
:param fact:
|
|
271
285
|
:return: Retrieve cached range values for the provided fact.
|
|
272
286
|
"""
|
|
273
|
-
assert fact in self.facts,
|
|
274
|
-
assert fact.isNumeric,
|
|
287
|
+
assert fact in self.facts, "Attempted to get range for fact not in set"
|
|
288
|
+
assert fact.isNumeric, "Attempted to get range for non-numeric fact"
|
|
275
289
|
if fact not in self._ranges:
|
|
276
290
|
lower, upper, __, __ = rangeValue(fact.xValue, self.getDecimals(fact))
|
|
277
291
|
self._ranges[fact] = lower, upper
|
|
@@ -297,25 +311,25 @@ class DuplicateType(Flag):
|
|
|
297
311
|
|
|
298
312
|
@property
|
|
299
313
|
def description(self) -> str:
|
|
300
|
-
return
|
|
314
|
+
return "|".join([str(n.name) for n in self if n.name]).lower()
|
|
301
315
|
|
|
302
316
|
|
|
303
317
|
class DuplicateTypeArg(Enum):
|
|
304
|
-
NONE =
|
|
305
|
-
INCONSISTENT =
|
|
306
|
-
CONSISTENT =
|
|
307
|
-
INCOMPLETE =
|
|
308
|
-
COMPLETE =
|
|
309
|
-
ALL =
|
|
318
|
+
NONE = "none"
|
|
319
|
+
INCONSISTENT = "inconsistent"
|
|
320
|
+
CONSISTENT = "consistent"
|
|
321
|
+
INCOMPLETE = "incomplete"
|
|
322
|
+
COMPLETE = "complete"
|
|
323
|
+
ALL = "all"
|
|
310
324
|
|
|
311
325
|
def duplicateType(self) -> DuplicateType:
|
|
312
326
|
return DUPLICATE_TYPE_ARG_MAP.get(self, DuplicateType.NONE)
|
|
313
327
|
|
|
314
328
|
|
|
315
329
|
class DeduplicationType(Enum):
|
|
316
|
-
COMPLETE =
|
|
317
|
-
CONSISTENT_PAIRS =
|
|
318
|
-
CONSISTENT_SETS =
|
|
330
|
+
COMPLETE = "complete"
|
|
331
|
+
CONSISTENT_PAIRS = "consistent-pairs"
|
|
332
|
+
CONSISTENT_SETS = "consistent-sets"
|
|
319
333
|
|
|
320
334
|
|
|
321
335
|
DUPLICATE_TYPE_ARG_MAP = {
|
|
@@ -328,7 +342,9 @@ DUPLICATE_TYPE_ARG_MAP = {
|
|
|
328
342
|
}
|
|
329
343
|
|
|
330
344
|
|
|
331
|
-
def doesSetHaveDuplicateType(
|
|
345
|
+
def doesSetHaveDuplicateType(
|
|
346
|
+
duplicateFacts: DuplicateFactSet, duplicateType: DuplicateType
|
|
347
|
+
) -> bool:
|
|
332
348
|
"""
|
|
333
349
|
:param duplicateFacts:
|
|
334
350
|
:param duplicateType:
|
|
@@ -363,7 +379,9 @@ def areFactsValueEqual(factA: ModelFact, factB: ModelFact) -> bool:
|
|
|
363
379
|
return getFactValueEqualityKey(factA) == getFactValueEqualityKey(factB)
|
|
364
380
|
|
|
365
381
|
|
|
366
|
-
def getAspectEqualFacts(
|
|
382
|
+
def getAspectEqualFacts(
|
|
383
|
+
hashEquivalentFacts: list[ModelFact], includeSingles: bool
|
|
384
|
+
) -> Iterator[list[ModelFact]]:
|
|
367
385
|
"""
|
|
368
386
|
Given a list of concept/context/unit hash-equivalent facts,
|
|
369
387
|
yields sublists of aspect-equal facts from this list.
|
|
@@ -371,12 +389,24 @@ def getAspectEqualFacts(hashEquivalentFacts: list[ModelFact], includeSingles: bo
|
|
|
371
389
|
:param includeSingles: Whether to include lists of single facts (with no duplicates).
|
|
372
390
|
:return: Lists of aspect-equal facts.
|
|
373
391
|
"""
|
|
374
|
-
aspectEqualFacts: dict[
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
392
|
+
aspectEqualFacts: dict[
|
|
393
|
+
tuple[QName, str | None], dict[tuple[ModelContext, ModelUnit], list[ModelFact]]
|
|
394
|
+
] = defaultdict(dict)
|
|
395
|
+
for (
|
|
396
|
+
fact
|
|
397
|
+
) in (
|
|
398
|
+
hashEquivalentFacts
|
|
399
|
+
): # check for hash collision by value checks on context and unit
|
|
400
|
+
contextUnitDict = aspectEqualFacts[
|
|
401
|
+
(
|
|
402
|
+
fact.qname,
|
|
403
|
+
(
|
|
404
|
+
cast(str, fact.xmlLang or "").lower()
|
|
405
|
+
if fact.concept.type.isWgnStringFactType
|
|
406
|
+
else None
|
|
407
|
+
),
|
|
408
|
+
)
|
|
409
|
+
]
|
|
380
410
|
_matched = False
|
|
381
411
|
for (context, unit), contextUnitFacts in contextUnitDict.items():
|
|
382
412
|
if fact.context is None:
|
|
@@ -395,12 +425,16 @@ def getAspectEqualFacts(hashEquivalentFacts: list[ModelFact], includeSingles: bo
|
|
|
395
425
|
if not _matched:
|
|
396
426
|
contextUnitDict[(fact.context, fact.unit)] = [fact]
|
|
397
427
|
for contextUnitDict in aspectEqualFacts.values(): # dups by qname, lang
|
|
398
|
-
for
|
|
428
|
+
for (
|
|
429
|
+
duplicateFacts
|
|
430
|
+
) in contextUnitDict.values(): # dups by equal-context equal-unit
|
|
399
431
|
if includeSingles or len(duplicateFacts) > 1:
|
|
400
432
|
yield duplicateFacts
|
|
401
433
|
|
|
402
434
|
|
|
403
|
-
def getDeduplicatedFacts(
|
|
435
|
+
def getDeduplicatedFacts(
|
|
436
|
+
modelXbrl: ModelXbrl, deduplicationType: DeduplicationType
|
|
437
|
+
) -> list[ModelFact]:
|
|
404
438
|
results = []
|
|
405
439
|
for duplicateFactSet in getDuplicateFactSets(modelXbrl.facts, includeSingles=True):
|
|
406
440
|
message = None
|
|
@@ -418,12 +452,20 @@ def getDeduplicatedFacts(modelXbrl: ModelXbrl, deduplicationType: DeduplicationT
|
|
|
418
452
|
if message is not None:
|
|
419
453
|
modelXbrl.warning(
|
|
420
454
|
"info:deduplicationNotPossible",
|
|
421
|
-
_(
|
|
422
|
-
|
|
455
|
+
_(
|
|
456
|
+
"Deduplication of %(concept)s fact set not possible: %(message)s. concept=%(concept)s, context=%(context)s"
|
|
457
|
+
),
|
|
458
|
+
modelObject=facts[0],
|
|
459
|
+
concept=facts[0].concept.qname,
|
|
460
|
+
context=facts[0].contextID,
|
|
461
|
+
message=message,
|
|
462
|
+
)
|
|
423
463
|
return results
|
|
424
464
|
|
|
425
465
|
|
|
426
|
-
def getDuplicateFactSets(
|
|
466
|
+
def getDuplicateFactSets(
|
|
467
|
+
facts: list[ModelFact], includeSingles: bool
|
|
468
|
+
) -> Iterator[DuplicateFactSet]:
|
|
427
469
|
"""
|
|
428
470
|
:param facts: Facts to find duplicate sets from.
|
|
429
471
|
:param includeSingles: Whether to include lists of single facts (with no duplicates).
|
|
@@ -433,11 +475,15 @@ def getDuplicateFactSets(facts: list[ModelFact], includeSingles: bool) -> Iterat
|
|
|
433
475
|
for hashEquivalentFacts in hashEquivalentFactGroups:
|
|
434
476
|
if not includeSingles and len(hashEquivalentFacts) < 2:
|
|
435
477
|
continue
|
|
436
|
-
for duplicateFactList in getAspectEqualFacts(
|
|
478
|
+
for duplicateFactList in getAspectEqualFacts(
|
|
479
|
+
hashEquivalentFacts, includeSingles=includeSingles
|
|
480
|
+
): # dups by equal-context equal-unit
|
|
437
481
|
yield DuplicateFactSet(facts=duplicateFactList)
|
|
438
482
|
|
|
439
483
|
|
|
440
|
-
def getDuplicateFactSetsWithType(
|
|
484
|
+
def getDuplicateFactSetsWithType(
|
|
485
|
+
facts: list[ModelFact], duplicateType: DuplicateType
|
|
486
|
+
) -> Iterator[DuplicateFactSet]:
|
|
441
487
|
"""
|
|
442
488
|
:param facts: Facts to find duplicate sets from.
|
|
443
489
|
:param duplicateType: Type of duplicate to filter duplicate sets by.
|
|
@@ -451,9 +497,9 @@ def getDuplicateFactSetsWithType(facts: list[ModelFact], duplicateType: Duplicat
|
|
|
451
497
|
|
|
452
498
|
|
|
453
499
|
class FactValueEqualityType(Enum):
|
|
454
|
-
DEFAULT =
|
|
455
|
-
DATETIME =
|
|
456
|
-
LANGUAGE =
|
|
500
|
+
DEFAULT = "default"
|
|
501
|
+
DATETIME = "datetime"
|
|
502
|
+
LANGUAGE = "language"
|
|
457
503
|
|
|
458
504
|
|
|
459
505
|
TypeFactValueEqualityKey = tuple[FactValueEqualityType, tuple[Any, ...]]
|
|
@@ -471,7 +517,9 @@ def getFactValueEqualityKey(fact: ModelFact) -> TypeFactValueEqualityKey:
|
|
|
471
517
|
if isnan(cast(SupportsFloat, xValue)):
|
|
472
518
|
return FactValueEqualityType.DEFAULT, (float("nan"),)
|
|
473
519
|
if fact.concept.isLanguage:
|
|
474
|
-
return FactValueEqualityType.LANGUAGE, (
|
|
520
|
+
return FactValueEqualityType.LANGUAGE, (
|
|
521
|
+
cast(str, xValue).lower() if xValue is not None else None,
|
|
522
|
+
)
|
|
475
523
|
if isinstance(xValue, DateTime): # with/without time makes values unequal
|
|
476
524
|
return FactValueEqualityType.DATETIME, (xValue, xValue.dateOnly)
|
|
477
525
|
return FactValueEqualityType.DEFAULT, (fact.value,)
|
|
@@ -486,7 +534,16 @@ def getHashEquivalentFactGroups(facts: list[ModelFact]) -> list[list[ModelFact]]
|
|
|
486
534
|
"""
|
|
487
535
|
hashDict = defaultdict(list)
|
|
488
536
|
for f in facts:
|
|
489
|
-
if (
|
|
537
|
+
if (
|
|
538
|
+
(
|
|
539
|
+
f.isNil
|
|
540
|
+
or getattr(f, "xValid", XmlValidateConst.UNVALIDATED)
|
|
541
|
+
>= XmlValidateConst.VALID
|
|
542
|
+
)
|
|
543
|
+
and f.context is not None
|
|
544
|
+
and f.concept is not None
|
|
545
|
+
and f.concept.type is not None
|
|
546
|
+
):
|
|
490
547
|
hashDict[f.conceptContextUnitHash].append(f)
|
|
491
548
|
return list(hashDict.values())
|
|
492
549
|
|
|
@@ -494,7 +551,9 @@ def getHashEquivalentFactGroups(facts: list[ModelFact]) -> list[list[ModelFact]]
|
|
|
494
551
|
def logDeduplicatedFact(modelXbrl: ModelXbrl, fact: ModelFact) -> None:
|
|
495
552
|
modelXbrl.info(
|
|
496
553
|
"info:deduplicatedFact",
|
|
497
|
-
_(
|
|
554
|
+
_(
|
|
555
|
+
"Duplicate fact was excluded from deduplicated instance: %(fact)s, value=%(value)s, decimals=%(decimals)s"
|
|
556
|
+
),
|
|
498
557
|
modelObject=fact,
|
|
499
558
|
fact=fact.qname,
|
|
500
559
|
value=fact.xValue,
|
|
@@ -502,7 +561,9 @@ def logDeduplicatedFact(modelXbrl: ModelXbrl, fact: ModelFact) -> None:
|
|
|
502
561
|
)
|
|
503
562
|
|
|
504
563
|
|
|
505
|
-
def saveDeduplicatedInstance(
|
|
564
|
+
def saveDeduplicatedInstance(
|
|
565
|
+
modelXbrl: ModelXbrl, deduplicationType: DeduplicationType, outputFilepath: str
|
|
566
|
+
) -> None:
|
|
506
567
|
deduplicatedFacts = frozenset(getDeduplicatedFacts(modelXbrl, deduplicationType))
|
|
507
568
|
duplicateFacts = set(modelXbrl.facts) - deduplicatedFacts
|
|
508
569
|
for fact in duplicateFacts:
|
|
@@ -513,7 +574,9 @@ def saveDeduplicatedInstance(modelXbrl: ModelXbrl, deduplicationType: Deduplicat
|
|
|
513
574
|
modelXbrl.saveInstance(overrideFilepath=outputFilepath)
|
|
514
575
|
modelXbrl.info(
|
|
515
576
|
"info:deduplicatedInstance",
|
|
516
|
-
_(
|
|
577
|
+
_(
|
|
578
|
+
"Deduplicated instance was saved after removing %(count)s fact(s): %(filepath)s"
|
|
579
|
+
),
|
|
517
580
|
count=len(duplicateFacts),
|
|
518
581
|
filepath=outputFilepath,
|
|
519
582
|
)
|
arelle/_version.py
CHANGED
|
@@ -66,7 +66,7 @@ arelle/UiUtil.py,sha256=3G0xPclZI8xW_XQDbiFrmylB7Nd5muqi5n2x2oMkMZU,34218
|
|
|
66
66
|
arelle/Updater.py,sha256=ho8Z_9GOL39H1jHL3Gaw5uc6av7J8ZBB6dR_X-nF_e0,7124
|
|
67
67
|
arelle/UrlUtil.py,sha256=HrxZSG59EUMGMMGmWPuZkPi5-0BGqY3jAMkp7V4IdZo,32400
|
|
68
68
|
arelle/Validate.py,sha256=CBgvAqT_v3LwF0Z1b_QiiePf80x_sTqiSvA9RYBAwqI,55276
|
|
69
|
-
arelle/ValidateDuplicateFacts.py,sha256=
|
|
69
|
+
arelle/ValidateDuplicateFacts.py,sha256=074y-VWCOBHoi6iV6wDL_IXBtdz9oeI1uPvjEcC1oDs,21717
|
|
70
70
|
arelle/ValidateFilingText.py,sha256=XLlwKldK__191s6VDbgbhFeNhG5wp8hmZNr4kRCLu_E,53996
|
|
71
71
|
arelle/ValidateInfoset.py,sha256=Rz_XBi5Ha43KpxXYhjLolURcWVx5qmqyjLxw48Yt9Dg,20396
|
|
72
72
|
arelle/ValidateUtr.py,sha256=oxOPrOa1XEzBay4miXvx6eRLTnVFYUIJC9ueWUk4EkI,13633
|
|
@@ -123,7 +123,7 @@ arelle/XmlValidateConst.py,sha256=U_wN0Q-nWKwf6dKJtcu_83FXPn9c6P8JjzGA5b0w7P0,33
|
|
|
123
123
|
arelle/XmlValidateParticles.py,sha256=Mn6vhFl0ZKC_vag1mBwn1rH_x2jmlusJYqOOuxFPO2k,9231
|
|
124
124
|
arelle/XmlValidateSchema.py,sha256=6frtZOc1Yrx_5yYF6V6oHbScnglWrVbWr6xW4EHtLQI,7428
|
|
125
125
|
arelle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
126
|
-
arelle/_version.py,sha256=
|
|
126
|
+
arelle/_version.py,sha256=sRa_qk_q3QBynjyOYjA-67YubqjzRKZedln2S9tzhN4,515
|
|
127
127
|
arelle/typing.py,sha256=Ct5lrNKRow_o9CraMEXNza8nFsJ_iGIKoUeGfPs2dxI,1084
|
|
128
128
|
arelle/api/Session.py,sha256=Vd09RAutWX7mxHSsrW7Bl8CsE1UzXpQpAJsZb55mqng,6188
|
|
129
129
|
arelle/archive/CustomLogger.py,sha256=v_JXOCQLDZcfaFWzxC9FRcEf9tQi4rCI4Sx7jCuAVQI,1231
|
|
@@ -1553,9 +1553,9 @@ tests/unit_tests/arelle/oim/test_load.py,sha256=NxiUauQwJVfWAHbbpsMHGSU2d3Br8Pki
|
|
|
1553
1553
|
tests/unit_tests/arelle/plugin/test_plugin_imports.py,sha256=bdhIs9frAnFsdGU113yBk09_jis-z43dwUItMFYuSYM,1064
|
|
1554
1554
|
tests/unit_tests/arelle/plugin/validate/ESEF/ESEF_Current/test_validate_css_url.py,sha256=XHABmejQt7RlZ0udh7v42f2Xb2STGk_fSaIaJ9i2xo0,878
|
|
1555
1555
|
tests/unit_tests/arelle/utils/validate/test_decorator.py,sha256=ZS8FqIY1g-2FCbjF4UYm609dwViax6qBMRJSi0vfuhY,2482
|
|
1556
|
-
arelle_release-2.36.
|
|
1557
|
-
arelle_release-2.36.
|
|
1558
|
-
arelle_release-2.36.
|
|
1559
|
-
arelle_release-2.36.
|
|
1560
|
-
arelle_release-2.36.
|
|
1561
|
-
arelle_release-2.36.
|
|
1556
|
+
arelle_release-2.36.29.dist-info/LICENSE.md,sha256=rMbWwFLGzPgLoEjEu8LCmkpWDTqsvfOI-wzLSfeJsis,4107
|
|
1557
|
+
arelle_release-2.36.29.dist-info/METADATA,sha256=15UCnvMqfHvhaCle9jkkfDypnkx9UtyeUkHMfoDbGAk,9010
|
|
1558
|
+
arelle_release-2.36.29.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
|
|
1559
|
+
arelle_release-2.36.29.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
|
|
1560
|
+
arelle_release-2.36.29.dist-info/top_level.txt,sha256=ZYmYGmhW5Jvo3vJ4iXBZPUI29LvYhntom04w90esJvU,13
|
|
1561
|
+
arelle_release-2.36.29.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|