arelle-release 2.37.46__py3-none-any.whl → 2.38.0__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.
- arelle/BetaFeatures.py +0 -21
- arelle/Cntlr.py +15 -8
- arelle/CntlrCmdLine.py +121 -56
- arelle/CntlrWinMain.py +143 -70
- arelle/DialogFind.py +1 -1
- arelle/DialogPluginManager.py +6 -4
- arelle/DisclosureSystem.py +7 -0
- arelle/ErrorManager.py +21 -6
- arelle/FileSource.py +11 -4
- arelle/FunctionIxt.py +16 -11
- arelle/HtmlUtil.py +5 -4
- arelle/LeiUtil.py +63 -43
- arelle/ModelDocument.py +20 -15
- arelle/ModelDtsObject.py +8 -0
- arelle/ModelInstanceObject.py +1 -1
- arelle/ModelObject.py +16 -18
- arelle/ModelObjectFactory.py +35 -17
- arelle/ModelXbrl.py +28 -11
- arelle/PluginManager.py +130 -105
- arelle/RuntimeOptions.py +1 -0
- arelle/UrlUtil.py +14 -0
- arelle/Validate.py +17 -12
- arelle/ValidateDuplicateFacts.py +3 -1
- arelle/ValidateFileSource.py +38 -0
- arelle/ValidateFilingText.py +3 -3
- arelle/ValidateXbrl.py +5 -2
- arelle/ValidateXbrlCalcs.py +210 -186
- arelle/ValidateXbrlDTS.py +1 -1
- arelle/ViewFile.py +1 -0
- arelle/ViewFileFactTable.py +2 -2
- arelle/ViewWinDTS.py +4 -1
- arelle/WebCache.py +28 -24
- arelle/XbrlConst.py +22 -0
- arelle/XmlUtil.py +16 -21
- arelle/XmlValidate.py +6 -9
- arelle/_version.py +16 -3
- arelle/api/Session.py +11 -2
- arelle/config/disclosuresystems.xsd +2 -0
- arelle/config/rosettaEntitlements.plist +8 -0
- arelle/conformance/CSVTestcaseLoader.py +1 -1
- arelle/formula/XPathContext.py +3 -3
- arelle/logging/formatters/LogFormatter.py +3 -1
- arelle/packages/report/ReportPackage.py +26 -13
- arelle/packages/report/ReportPackageConst.py +0 -1
- arelle/plugin/inlineXbrlDocumentSet.py +19 -5
- arelle/plugin/validate/DBA/DisclosureSystems.py +19 -1
- arelle/plugin/validate/DBA/PluginValidationDataExtension.py +2 -4
- arelle/plugin/validate/DBA/ValidationPluginExtension.py +2 -1
- arelle/plugin/validate/DBA/resources/config.xml +5 -0
- arelle/plugin/validate/DBA/rules/__init__.py +2 -2
- arelle/plugin/validate/DBA/rules/fr.py +19 -2
- arelle/plugin/validate/DBA/rules/tc.py +2 -0
- arelle/plugin/validate/DBA/rules/th.py +6 -0
- arelle/plugin/validate/DBA/rules/tm.py +18 -5
- arelle/plugin/validate/DBA/rules/tr.py +11 -5
- arelle/plugin/validate/EDINET/Constants.py +193 -9
- arelle/plugin/validate/EDINET/ContextRequirement.py +58 -0
- arelle/plugin/validate/EDINET/ControllerPluginData.py +220 -1
- arelle/plugin/validate/EDINET/CoverItemRequirements.py +42 -0
- arelle/plugin/validate/EDINET/DeiRequirements.py +118 -0
- arelle/plugin/validate/EDINET/FilingFormat.py +275 -0
- arelle/plugin/validate/EDINET/FormType.py +134 -0
- arelle/plugin/validate/EDINET/ManifestInstance.py +72 -5
- arelle/plugin/validate/EDINET/NamespaceConfig.py +50 -0
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +493 -132
- arelle/plugin/validate/EDINET/{InstanceType.py → ReportFolderType.py} +72 -15
- arelle/plugin/validate/EDINET/Statement.py +139 -0
- arelle/plugin/validate/EDINET/TableOfContentsBuilder.py +595 -0
- arelle/plugin/validate/EDINET/UploadContents.py +48 -0
- arelle/plugin/validate/EDINET/ValidationPluginExtension.py +20 -2
- arelle/plugin/validate/EDINET/__init__.py +31 -6
- arelle/plugin/validate/EDINET/resources/config.xml +8 -1
- arelle/plugin/validate/EDINET/resources/cover-item-requirements.json +793 -0
- arelle/plugin/validate/EDINET/resources/dei-requirements.csv +27 -0
- arelle/plugin/validate/EDINET/resources/edinet-taxonomies.xml +2 -0
- arelle/plugin/validate/EDINET/rules/contexts.py +375 -14
- arelle/plugin/validate/EDINET/rules/edinet.py +1934 -45
- arelle/plugin/validate/EDINET/rules/frta.py +122 -3
- arelle/plugin/validate/EDINET/rules/gfm.py +1907 -11
- arelle/plugin/validate/EDINET/rules/upload.py +989 -141
- arelle/plugin/validate/ESEF/Const.py +3 -1
- arelle/plugin/validate/ESEF/ESEF_2021/DTS.py +5 -0
- arelle/plugin/validate/ESEF/ESEF_2021/Image.py +2 -2
- arelle/plugin/validate/ESEF/ESEF_2021/ValidateXbrlFinally.py +23 -20
- arelle/plugin/validate/ESEF/ESEF_Current/DTS.py +47 -14
- arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py +100 -25
- arelle/plugin/validate/ESEF/__init__.py +20 -6
- arelle/plugin/validate/ESEF/resources/authority-validations.json +76 -9
- arelle/plugin/validate/ESEF/resources/config.xml +20 -0
- arelle/plugin/validate/NL/DisclosureSystems.py +22 -0
- arelle/plugin/validate/NL/PluginValidationDataExtension.py +27 -9
- arelle/plugin/validate/NL/ValidationPluginExtension.py +51 -7
- arelle/plugin/validate/NL/resources/config.xml +18 -0
- arelle/plugin/validate/NL/rules/br_kvk.py +17 -61
- arelle/plugin/validate/NL/rules/fg_nl.py +7 -38
- arelle/plugin/validate/NL/rules/fr_kvk.py +7 -42
- arelle/plugin/validate/NL/rules/fr_nl.py +31 -147
- arelle/plugin/validate/NL/rules/nl_kvk.py +142 -28
- arelle/plugin/validate/ROS/PluginValidationDataExtension.py +2 -0
- arelle/plugin/validate/ROS/ValidationPluginExtension.py +4 -1
- arelle/plugin/validate/ROS/rules/ros.py +41 -9
- arelle/plugin/validate/UK/ValidateUK.py +130 -66
- arelle/plugin/validate/UK/__init__.py +89 -103
- arelle/utils/EntryPointDetection.py +79 -13
- arelle/utils/PluginHooks.py +125 -0
- arelle/utils/validate/ESEFImage.py +6 -6
- arelle/utils/validate/Validation.py +18 -0
- arelle/utils/validate/ValidationPlugin.py +76 -11
- arelle/utils/validate/ValidationUtil.py +35 -3
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/METADATA +30 -20
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/RECORD +115 -191
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/licenses/LICENSE.md +0 -3
- arelle/archive/CustomLogger.py +0 -43
- arelle/archive/LoadEFMvalidate.py +0 -32
- arelle/archive/LoadSavePreLbCsv.py +0 -26
- arelle/archive/LoadValidate.cs +0 -31
- arelle/archive/LoadValidate.py +0 -36
- arelle/archive/LoadValidateCmdLine.java +0 -69
- arelle/archive/LoadValidatePostedZip.java +0 -57
- arelle/archive/LoadValidateWebService.java +0 -34
- arelle/archive/SaveTableToExelle.py +0 -140
- arelle/archive/TR3toTR4.py +0 -88
- arelle/archive/plugin/ESEF_2022/__init__.py +0 -47
- arelle/archive/plugin/bigInstance.py +0 -394
- arelle/archive/plugin/cmdWebServerExtension.py +0 -43
- arelle/archive/plugin/crashTest.py +0 -38
- arelle/archive/plugin/functionsXmlCreation.py +0 -106
- arelle/archive/plugin/hello_i18n.pot +0 -26
- arelle/archive/plugin/hello_i18n.py +0 -32
- arelle/archive/plugin/importTestChild1.py +0 -21
- arelle/archive/plugin/importTestChild2.py +0 -22
- arelle/archive/plugin/importTestGrandchild1.py +0 -21
- arelle/archive/plugin/importTestGrandchild2.py +0 -21
- arelle/archive/plugin/importTestImported1.py +0 -23
- arelle/archive/plugin/importTestImported11.py +0 -22
- arelle/archive/plugin/importTestParent.py +0 -48
- arelle/archive/plugin/instanceInfo.py +0 -306
- arelle/archive/plugin/loadFromOIM-2018.py +0 -1282
- arelle/archive/plugin/locale/fr/LC_MESSAGES/hello_i18n.po +0 -25
- arelle/archive/plugin/objectmaker.py +0 -285
- arelle/archive/plugin/packagedImportTest/__init__.py +0 -47
- arelle/archive/plugin/packagedImportTest/importTestChild1.py +0 -21
- arelle/archive/plugin/packagedImportTest/importTestChild2.py +0 -22
- arelle/archive/plugin/packagedImportTest/importTestGrandchild1.py +0 -21
- arelle/archive/plugin/packagedImportTest/importTestGrandchild2.py +0 -21
- arelle/archive/plugin/packagedImportTest/importTestImported1.py +0 -24
- arelle/archive/plugin/packagedImportTest/importTestImported11.py +0 -21
- arelle/archive/plugin/packagedImportTest/subdir/importTestImported111.py +0 -21
- arelle/archive/plugin/packagedImportTest/subdir/subsubdir/importTestImported1111.py +0 -21
- arelle/archive/plugin/sakaCalendar.py +0 -215
- arelle/archive/plugin/saveInstanceInfoset.py +0 -121
- arelle/archive/plugin/sphinx/FormulaGenerator.py +0 -823
- arelle/archive/plugin/sphinx/SphinxContext.py +0 -404
- arelle/archive/plugin/sphinx/SphinxEvaluator.py +0 -783
- arelle/archive/plugin/sphinx/SphinxMethods.py +0 -1287
- arelle/archive/plugin/sphinx/SphinxParser.py +0 -1093
- arelle/archive/plugin/sphinx/SphinxValidator.py +0 -163
- arelle/archive/plugin/sphinx/US-GAAP Ratios Example.xsr +0 -52
- arelle/archive/plugin/sphinx/__init__.py +0 -285
- arelle/archive/plugin/streamingExtensions.py +0 -335
- arelle/archive/plugin/updateTableLB.py +0 -242
- arelle/archive/plugin/validate/SBRnl/CustomLoader.py +0 -19
- arelle/archive/plugin/validate/SBRnl/DTS.py +0 -305
- arelle/archive/plugin/validate/SBRnl/Dimensions.py +0 -357
- arelle/archive/plugin/validate/SBRnl/Document.py +0 -799
- arelle/archive/plugin/validate/SBRnl/Filing.py +0 -467
- arelle/archive/plugin/validate/SBRnl/__init__.py +0 -75
- arelle/archive/plugin/validate/SBRnl/config.xml +0 -26
- arelle/archive/plugin/validate/SBRnl/sbr-nl-taxonomies.xml +0 -754
- arelle/archive/plugin/validate/USBestPractices.py +0 -570
- arelle/archive/plugin/validate/USCorpAction.py +0 -557
- arelle/archive/plugin/validate/USSecTagging.py +0 -337
- arelle/archive/plugin/validate/XDC/__init__.py +0 -77
- arelle/archive/plugin/validate/XDC/config.xml +0 -20
- arelle/archive/plugin/validate/XFsyntax/__init__.py +0 -64
- arelle/archive/plugin/validate/XFsyntax/xf.py +0 -2227
- arelle/archive/plugin/validate/calc2.py +0 -536
- arelle/archive/plugin/validateSchemaLxml.py +0 -156
- arelle/archive/plugin/validateTableInfoset.py +0 -52
- arelle/archive/us-gaap-dei-docType-extraction-frm.xml +0 -90
- arelle/archive/us-gaap-dei-ratio-cash-frm.xml +0 -150
- arelle/examples/plugin/formulaSuiteConverter.py +0 -212
- arelle/examples/plugin/functionsCustom.py +0 -59
- arelle/examples/plugin/hello_dolly.py +0 -64
- arelle/examples/plugin/multi.py +0 -58
- arelle/examples/plugin/rssSaveOim.py +0 -96
- arelle/examples/plugin/validate/XYZ/DisclosureSystems.py +0 -2
- arelle/examples/plugin/validate/XYZ/PluginValidationDataExtension.py +0 -10
- arelle/examples/plugin/validate/XYZ/ValidationPluginExtension.py +0 -49
- arelle/examples/plugin/validate/XYZ/__init__.py +0 -75
- arelle/examples/plugin/validate/XYZ/resources/config.xml +0 -16
- arelle/examples/plugin/validate/XYZ/rules/__init__.py +0 -0
- arelle/examples/plugin/validate/XYZ/rules/rules01.py +0 -110
- arelle/examples/plugin/validate/XYZ/rules/rules02.py +0 -59
- arelle/model/CommentBase.py +0 -9
- arelle/model/ElementBase.py +0 -11
- arelle/model/PIBase.py +0 -10
- arelle/model/__init__.py +0 -15
- arelle/scripts-macOS/startWebServer.command +0 -3
- arelle/scripts-unix/startWebServer.sh +0 -1
- arelle/scripts-windows/startWebServer.bat +0 -5
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,72 @@
|
|
|
1
|
+
"""
|
|
2
|
+
See COPYRIGHT.md for copyright information.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
1
8
|
import os
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Any, TYPE_CHECKING
|
|
2
11
|
|
|
3
12
|
from arelle import (
|
|
4
13
|
ModelDocument,
|
|
5
|
-
PluginManager,
|
|
14
|
+
PluginManager, FileSource, PackageManager,
|
|
6
15
|
)
|
|
16
|
+
from arelle.UrlUtil import isHttpUrl
|
|
17
|
+
from arelle.typing import TypeGetText
|
|
18
|
+
|
|
19
|
+
_: TypeGetText
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from arelle.Cntlr import Cntlr
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class EntrypointParseResult:
|
|
27
|
+
success: bool
|
|
28
|
+
entrypointFiles: list[dict[str, Any]]
|
|
29
|
+
filesource: FileSource.FileSource | None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def parseEntrypointFileInput(cntlr: Cntlr, entrypointFile: str | None, sourceZipStream=None, fallbackSelect=True) -> EntrypointParseResult:
|
|
33
|
+
# entrypointFile may be absent (if input is a POSTED zip or file name ending in .zip)
|
|
34
|
+
# or may be a | separated set of file names
|
|
35
|
+
_entryPoints = []
|
|
36
|
+
_checkIfXmlIsEis = cntlr.modelManager.disclosureSystem and cntlr.modelManager.disclosureSystem.validationType == "EFM"
|
|
37
|
+
if entrypointFile:
|
|
38
|
+
_f = entrypointFile
|
|
39
|
+
try: # may be a json list
|
|
40
|
+
_entryPoints = json.loads(_f)
|
|
41
|
+
_checkIfXmlIsEis = False # json entry objects never specify an xml EIS archive
|
|
42
|
+
except ValueError as e:
|
|
43
|
+
# is it malformed json?
|
|
44
|
+
if _f.startswith("[{") or _f.endswith("]}") or '"file:"' in _f:
|
|
45
|
+
cntlr.addToLog(_("File name parameter appears to be malformed JSON: {}\n{}").format(e, _f),
|
|
46
|
+
messageCode="FileNameFormatError",
|
|
47
|
+
level=logging.ERROR)
|
|
48
|
+
else: # try as file names separated by '|'
|
|
49
|
+
for f in (_f or '').split('|'):
|
|
50
|
+
if not sourceZipStream and not isHttpUrl(f) and not os.path.isabs(f):
|
|
51
|
+
f = os.path.normpath(os.path.join(os.getcwd(), f)) # make absolute normed path
|
|
52
|
+
_entryPoints.append({"file":f})
|
|
53
|
+
filesource = None # file source for all instances if not None
|
|
54
|
+
if sourceZipStream:
|
|
55
|
+
filesource = FileSource.openFileSource(None, cntlr, sourceZipStream)
|
|
56
|
+
elif len(_entryPoints) == 1 and "file" in _entryPoints[0]: # check if an archive and need to discover entry points (and not IXDS)
|
|
57
|
+
entryPath = PackageManager.mappedUrl(_entryPoints[0]["file"])
|
|
58
|
+
filesource = FileSource.openFileSource(entryPath, cntlr, checkIfXmlIsEis=_checkIfXmlIsEis)
|
|
59
|
+
_entrypointFiles = _entryPoints
|
|
60
|
+
if filesource and not filesource.selection and not (sourceZipStream and len(_entrypointFiles) > 0):
|
|
61
|
+
try:
|
|
62
|
+
filesourceEntrypointFiles(filesource, _entrypointFiles, fallbackSelect=fallbackSelect)
|
|
63
|
+
except Exception as err:
|
|
64
|
+
cntlr.addToLog(str(err), messageCode="error", level=logging.ERROR)
|
|
65
|
+
return EntrypointParseResult(success=False, entrypointFiles=_entrypointFiles, filesource=filesource)
|
|
66
|
+
return EntrypointParseResult(success=True, entrypointFiles=_entrypointFiles, filesource=filesource)
|
|
7
67
|
|
|
8
68
|
|
|
9
|
-
def filesourceEntrypointFiles(filesource, entrypointFiles=None, inlineOnly=False):
|
|
69
|
+
def filesourceEntrypointFiles(filesource, entrypointFiles=None, inlineOnly=False, fallbackSelect=True):
|
|
10
70
|
if entrypointFiles is None:
|
|
11
71
|
entrypointFiles = []
|
|
12
72
|
for pluginXbrlMethod in PluginManager.pluginClassMethods("FileSource.EntrypointFiles"):
|
|
@@ -19,8 +79,9 @@ def filesourceEntrypointFiles(filesource, entrypointFiles=None, inlineOnly=False
|
|
|
19
79
|
if filesource.isTaxonomyPackage: # if archive is also a taxonomy package, activate mappings
|
|
20
80
|
filesource.loadTaxonomyPackageMappings()
|
|
21
81
|
# HF note: a web api request to load a specific file from archive is ignored, is this right?
|
|
22
|
-
|
|
82
|
+
discoveredEntrypointFiles = []
|
|
23
83
|
if reportPackage := filesource.reportPackage:
|
|
84
|
+
del entrypointFiles[:]
|
|
24
85
|
assert isinstance(filesource.basefile, str)
|
|
25
86
|
for report in reportPackage.reports or []:
|
|
26
87
|
if report.isInline:
|
|
@@ -31,10 +92,10 @@ def filesourceEntrypointFiles(filesource, entrypointFiles=None, inlineOnly=False
|
|
|
31
92
|
ixdsDiscovered = True
|
|
32
93
|
if not ixdsDiscovered and len(reportEntries) > 1:
|
|
33
94
|
raise RuntimeError(_("Loading error. Inline document set encountered. Enable 'InlineXbrlDocumentSet' plug-in to load this filing: {0}").format(filesource.url))
|
|
34
|
-
|
|
95
|
+
discoveredEntrypointFiles.extend(reportEntries)
|
|
35
96
|
elif not inlineOnly:
|
|
36
|
-
|
|
37
|
-
|
|
97
|
+
discoveredEntrypointFiles.append({"file": report.fullPathPrimary})
|
|
98
|
+
elif fallbackSelect:
|
|
38
99
|
# attempt to find inline XBRL files before instance files, .xhtml before probing others (ESMA)
|
|
39
100
|
urlsByType = {}
|
|
40
101
|
for _archiveFile in (filesource.dir or ()): # .dir might be none if IOerror
|
|
@@ -45,20 +106,25 @@ def filesourceEntrypointFiles(filesource, entrypointFiles=None, inlineOnly=False
|
|
|
45
106
|
# use inline instances, if any, else non-inline instances
|
|
46
107
|
for identifiedType in ((ModelDocument.Type.INLINEXBRL,) if inlineOnly else (ModelDocument.Type.INLINEXBRL, ModelDocument.Type.INSTANCE)):
|
|
47
108
|
for url in urlsByType.get(identifiedType, []):
|
|
48
|
-
|
|
49
|
-
if
|
|
109
|
+
discoveredEntrypointFiles.append({"file":url})
|
|
110
|
+
if discoveredEntrypointFiles:
|
|
50
111
|
if identifiedType == ModelDocument.Type.INLINEXBRL:
|
|
51
112
|
for pluginXbrlMethod in PluginManager.pluginClassMethods("InlineDocumentSet.Discovery"):
|
|
52
|
-
pluginXbrlMethod(filesource,
|
|
113
|
+
pluginXbrlMethod(filesource, discoveredEntrypointFiles) # group into IXDS if plugin feature is available
|
|
53
114
|
break # found inline (or non-inline) entrypoint files, don't look for any other type
|
|
54
115
|
# for ESEF non-consolidated xhtml documents accept an xhtml entry point
|
|
55
|
-
if not
|
|
116
|
+
if not discoveredEntrypointFiles and not inlineOnly:
|
|
56
117
|
for url in urlsByType.get(ModelDocument.Type.HTML, []):
|
|
57
|
-
|
|
58
|
-
if not
|
|
118
|
+
discoveredEntrypointFiles.append({"file":url})
|
|
119
|
+
if not discoveredEntrypointFiles and filesource.taxonomyPackage is not None:
|
|
59
120
|
for packageEntry in filesource.taxonomyPackage.get('entryPoints', {}).values():
|
|
60
121
|
for _resolvedUrl, remappedUrl, _closest in packageEntry:
|
|
61
|
-
|
|
122
|
+
discoveredEntrypointFiles.append({"file": remappedUrl})
|
|
123
|
+
if discoveredEntrypointFiles:
|
|
124
|
+
# Only clear archive entry points if we discovered new ones.
|
|
125
|
+
# This could be a plugin loaded archive, such as an Excel file.
|
|
126
|
+
del entrypointFiles[:]
|
|
127
|
+
entrypointFiles.extend(discoveredEntrypointFiles)
|
|
62
128
|
|
|
63
129
|
|
|
64
130
|
elif os.path.isdir(filesource.url):
|
arelle/utils/PluginHooks.py
CHANGED
|
@@ -34,6 +34,8 @@ class ValidationHook(Enum):
|
|
|
34
34
|
These hooks are called at different stages of validation, but all provide a common interface (ValidateXbrl is the first param).
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
|
+
COMPLETE = "Validate.Complete"
|
|
38
|
+
FILESOURCE = "Validate.FileSource"
|
|
37
39
|
XBRL_START = "Validate.XBRL.Start"
|
|
38
40
|
XBRL_FINALLY = "Validate.XBRL.Finally"
|
|
39
41
|
XBRL_DTS_DOCUMENT = "Validate.XBRL.DTS.document"
|
|
@@ -41,6 +43,42 @@ class ValidationHook(Enum):
|
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
class PluginHooks(ABC):
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def cntlrCmdLineFilingStart(
|
|
49
|
+
cntlr: CntlrCmdLine,
|
|
50
|
+
options: RuntimeOptions,
|
|
51
|
+
filesource: FileSource | None,
|
|
52
|
+
entrypoints: list[dict[str, Any]] | None = None,
|
|
53
|
+
sourceZipStream: BinaryIO | None = None,
|
|
54
|
+
responseZipStream: BinaryIO | None = None,
|
|
55
|
+
*args: Any,
|
|
56
|
+
**kwargs: Any,
|
|
57
|
+
) -> None:
|
|
58
|
+
"""
|
|
59
|
+
Plugin hook: `CntlrCmdLine.Filing.Start`
|
|
60
|
+
|
|
61
|
+
This hook is triggered after entrypoints have been discovered and parsed, but before
|
|
62
|
+
models have been loaded.
|
|
63
|
+
|
|
64
|
+
Example:
|
|
65
|
+
```python
|
|
66
|
+
timeAtStart = time.time()
|
|
67
|
+
myOptionEnabled = options.myOption
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
:param cntlr: The [CntlrCmdLine](#arelle.CntlrCmdLine.CntlrCmdLine) that is currently running.
|
|
71
|
+
:param options: Parsed options object.
|
|
72
|
+
:param filesource: FileSource, if available.
|
|
73
|
+
:param entrypoints: A list of entrypoint configurations.
|
|
74
|
+
:param sourceZipStream: The source zip stream if the model was loaded from a zip that was POSTed to the webserver.
|
|
75
|
+
:param responseZipStream: The response zip stream if loaded from the webserver and the user requested a zip response.
|
|
76
|
+
:param args: Argument capture to ensure new parameters don't break plugin hook.
|
|
77
|
+
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
78
|
+
:return: None
|
|
79
|
+
"""
|
|
80
|
+
raise NotImplementedError
|
|
81
|
+
|
|
44
82
|
@staticmethod
|
|
45
83
|
def cntlrCmdLineOptions(
|
|
46
84
|
parser: OptionParser,
|
|
@@ -217,6 +255,34 @@ class PluginHooks(ABC):
|
|
|
217
255
|
"""
|
|
218
256
|
raise NotImplementedError
|
|
219
257
|
|
|
258
|
+
@staticmethod
|
|
259
|
+
def cntlrWinMainFilingStart(
|
|
260
|
+
cntlr: CntlrWinMain,
|
|
261
|
+
filesource: FileSource | None,
|
|
262
|
+
entrypoints: list[dict[str, Any]] | None = None,
|
|
263
|
+
*args: Any,
|
|
264
|
+
**kwargs: Any,
|
|
265
|
+
) -> None:
|
|
266
|
+
"""
|
|
267
|
+
Plugin hook: `CntlrWinMain.Filing.Start`
|
|
268
|
+
|
|
269
|
+
This hook is triggered after entrypoints have been discovered and parsed, but before
|
|
270
|
+
models have been loaded.
|
|
271
|
+
|
|
272
|
+
Example:
|
|
273
|
+
```python
|
|
274
|
+
timeAtStart = time.time()
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
:param cntlr: The [CntlrWinMain](#arelle.CntlrWinMain.CntlrWinMain) that is currently running.
|
|
278
|
+
:param filesource: FileSource, if available.
|
|
279
|
+
:param entrypoints: A list of entrypoint configurations.
|
|
280
|
+
:param args: Argument capture to ensure new parameters don't break plugin hook.
|
|
281
|
+
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
282
|
+
:return: None
|
|
283
|
+
"""
|
|
284
|
+
raise NotImplementedError
|
|
285
|
+
|
|
220
286
|
@staticmethod
|
|
221
287
|
def cntlrWinMainMenuHelp(
|
|
222
288
|
cntlr: CntlrWinMain,
|
|
@@ -568,6 +634,65 @@ class PluginHooks(ABC):
|
|
|
568
634
|
"""
|
|
569
635
|
raise NotImplementedError
|
|
570
636
|
|
|
637
|
+
@staticmethod
|
|
638
|
+
def validateComplete(
|
|
639
|
+
cntlr: Cntlr,
|
|
640
|
+
fileSource: FileSource,
|
|
641
|
+
*args: Any,
|
|
642
|
+
**kwargs: Any,
|
|
643
|
+
) -> None:
|
|
644
|
+
"""
|
|
645
|
+
Plugin hook: `Validate.Complete`
|
|
646
|
+
|
|
647
|
+
Hook for executing controller-level validation rules after model-level validation is complete.
|
|
648
|
+
This can be useful for validating multi-instance filings when a rule requires information that
|
|
649
|
+
is only available after all instances have been parsed and validated.
|
|
650
|
+
|
|
651
|
+
Example:
|
|
652
|
+
```python
|
|
653
|
+
unusedFilepaths = pluginData.getExistingFilepaths() - pluginData.getUsedFilepaths()
|
|
654
|
+
if len(unusedSubdirectoryFilepaths) > 0:
|
|
655
|
+
yield Validation.error(codes="0.0.0", msg="Unused files exist.")
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
:param cntlr: The [Cntlr](#arelle.Cntlr.Cntlr) instance.
|
|
659
|
+
:param fileSource: The [FileSource](#arelle.FileSource.FileSource) involved in loading the entrypoint files.
|
|
660
|
+
:param args: Argument capture to ensure new parameters don't break plugin hook.
|
|
661
|
+
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
662
|
+
:return: None
|
|
663
|
+
"""
|
|
664
|
+
raise NotImplementedError
|
|
665
|
+
|
|
666
|
+
@staticmethod
|
|
667
|
+
def validateFileSource(
|
|
668
|
+
cntlr: Cntlr,
|
|
669
|
+
fileSource: FileSource,
|
|
670
|
+
*args: Any,
|
|
671
|
+
**kwargs: Any,
|
|
672
|
+
) -> None:
|
|
673
|
+
"""
|
|
674
|
+
Plugin hook: `Validate.FileSource`
|
|
675
|
+
|
|
676
|
+
Hook for validating FileSource. This is useful for validations that apply to the filesource and not the
|
|
677
|
+
XBRL models loaded from it.
|
|
678
|
+
|
|
679
|
+
Example:
|
|
680
|
+
```python
|
|
681
|
+
size = fileSource.getBytesSize()
|
|
682
|
+
if size is None:
|
|
683
|
+
return
|
|
684
|
+
if size > 100_000_000:
|
|
685
|
+
yield Validation.error(codes="0.0.0", msg="File size exceeds 100MB.")
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
:param cntlr: The [Cntlr](#arelle.Cntlr.Cntlr) instance.
|
|
689
|
+
:param fileSource: The [FileSource](#arelle.FileSource.FileSource) to validate.
|
|
690
|
+
:param args: Argument capture to ensure new parameters don't break plugin hook.
|
|
691
|
+
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
692
|
+
:return: None
|
|
693
|
+
"""
|
|
694
|
+
raise NotImplementedError
|
|
695
|
+
|
|
571
696
|
@staticmethod
|
|
572
697
|
def validateFinally(
|
|
573
698
|
val: ValidateXbrl,
|
|
@@ -17,7 +17,7 @@ from arelle import ModelDocument
|
|
|
17
17
|
from arelle.ModelObjectFactory import parser
|
|
18
18
|
from arelle.ModelXbrl import ModelXbrl
|
|
19
19
|
from arelle.typing import TypeGetText
|
|
20
|
-
from arelle.UrlUtil import decodeBase64DataImage,
|
|
20
|
+
from arelle.UrlUtil import decodeBase64DataImage, isExternalUrl
|
|
21
21
|
from arelle.utils.validate.Validation import Validation
|
|
22
22
|
from arelle.ValidateFilingText import parseImageDataURL, validateGraphicHeaderType
|
|
23
23
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
@@ -105,15 +105,15 @@ def validateImage(
|
|
|
105
105
|
if minExternalRessourceSize != -1:
|
|
106
106
|
# transform kb to b
|
|
107
107
|
minExternalRessourceSize = minExternalRessourceSize * 1024
|
|
108
|
-
if
|
|
108
|
+
if isExternalUrl(image):
|
|
109
109
|
yield Validation.error(("ESEF.4.1.6.xHTMLDocumentContainsExternalReferences" if not params.consolidated
|
|
110
110
|
else "ESEF.3.5.1.inlineXbrlDocumentContainsExternalReferences",
|
|
111
|
-
"NL.NL-KVK.3.6.2.1.
|
|
111
|
+
"NL.NL-KVK.3.6.2.1.inlineXbrlDocumentSetContainsExternalReferences"),
|
|
112
112
|
_("Inline XBRL instance documents MUST NOT contain any reference pointing to resources outside the reporting package: %(element)s"),
|
|
113
113
|
modelObject=elts, element=elts[0].tag, evaluatedMsg=evaluatedMsg,
|
|
114
114
|
messageCodes=("ESEF.3.5.1.inlineXbrlDocumentContainsExternalReferences",
|
|
115
115
|
"ESEF.4.1.6.xHTMLDocumentContainsExternalReferences",
|
|
116
|
-
"NL.NL-KVK.3.6.2.1.
|
|
116
|
+
"NL.NL-KVK.3.6.2.1.inlineXbrlDocumentSetContainsExternalReferences"))
|
|
117
117
|
elif image.startswith("data:image"):
|
|
118
118
|
dataURLParts = parseImageDataURL(image)
|
|
119
119
|
if not dataURLParts or not dataURLParts.isBase64:
|
|
@@ -277,7 +277,7 @@ def checkSVGContentElt(
|
|
|
277
277
|
yield Validation.error((f"{guidance}.executableCodePresent", "NL.NL-KVK.3.5.1.1.executableCodePresent"),
|
|
278
278
|
_("Inline XBRL images MUST NOT contain executable code: %(element)s"),
|
|
279
279
|
modelObject=imgElts, element=eltTag)
|
|
280
|
-
elif
|
|
281
|
-
yield Validation.error((f"{guidance}.referencesPointingOutsideOfTheReportingPackagePresent", "NL.NL-KVK.3.6.2.1.
|
|
280
|
+
elif isExternalUrl(href):
|
|
281
|
+
yield Validation.error((f"{guidance}.referencesPointingOutsideOfTheReportingPackagePresent", "NL.NL-KVK.3.6.2.1.inlineXbrlDocumentSetContainsExternalReferences"),
|
|
282
282
|
_("Inline XBRL instance document [image] MUST NOT contain any reference pointing to resources outside the reporting package: %(element)s"),
|
|
283
283
|
modelObject=imgElts, element=eltTag)
|
|
@@ -10,6 +10,7 @@ from typing import Any
|
|
|
10
10
|
|
|
11
11
|
class Level(Enum):
|
|
12
12
|
ERROR = "ERROR"
|
|
13
|
+
INCONSISTENCY = "INCONSISTENCY"
|
|
13
14
|
WARNING = "WARNING"
|
|
14
15
|
|
|
15
16
|
@dataclass(frozen=True)
|
|
@@ -19,6 +20,15 @@ class Validation:
|
|
|
19
20
|
msg: str
|
|
20
21
|
args: dict[str, Any] = field(default_factory=dict)
|
|
21
22
|
|
|
23
|
+
@staticmethod
|
|
24
|
+
def build(
|
|
25
|
+
level: Level,
|
|
26
|
+
codes: str | tuple[str, ...],
|
|
27
|
+
msg: str,
|
|
28
|
+
**kwargs: Any,
|
|
29
|
+
) -> Validation:
|
|
30
|
+
return Validation(level=level, codes=codes, msg=msg, args=kwargs)
|
|
31
|
+
|
|
22
32
|
@staticmethod
|
|
23
33
|
def error(
|
|
24
34
|
codes: str | tuple[str, ...],
|
|
@@ -27,6 +37,14 @@ class Validation:
|
|
|
27
37
|
) -> Validation:
|
|
28
38
|
return Validation(level=Level.ERROR, codes=codes, msg=msg, args=kwargs)
|
|
29
39
|
|
|
40
|
+
@staticmethod
|
|
41
|
+
def inconsistency(
|
|
42
|
+
codes: str | tuple[str, ...],
|
|
43
|
+
msg: str,
|
|
44
|
+
**kwargs: Any,
|
|
45
|
+
) -> Validation:
|
|
46
|
+
return Validation(level=Level.INCONSISTENCY, codes=codes, msg=msg, args=kwargs)
|
|
47
|
+
|
|
30
48
|
@staticmethod
|
|
31
49
|
def warning(
|
|
32
50
|
codes: str | tuple[str, ...],
|
|
@@ -8,17 +8,20 @@ from pathlib import Path
|
|
|
8
8
|
from types import ModuleType
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
|
+
from arelle.Cntlr import Cntlr
|
|
11
12
|
from arelle.DisclosureSystem import DisclosureSystem
|
|
13
|
+
from arelle.FileSource import FileSource
|
|
12
14
|
from arelle.ModelDocument import LoadingException, ModelDocument
|
|
15
|
+
from arelle.ModelManager import ModelManager
|
|
13
16
|
from arelle.ModelXbrl import ModelXbrl
|
|
14
17
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
18
|
+
from arelle.utils.PluginData import PluginData
|
|
15
19
|
from arelle.utils.PluginHooks import ValidationHook
|
|
16
20
|
from arelle.utils.validate.Decorator import (
|
|
17
21
|
ValidationAttributes,
|
|
18
22
|
ValidationFunction,
|
|
19
23
|
getValidationAttributes,
|
|
20
24
|
)
|
|
21
|
-
from arelle.utils.PluginData import PluginData
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
class ValidationPlugin:
|
|
@@ -55,7 +58,7 @@ class ValidationPlugin:
|
|
|
55
58
|
ValidationHook, dict[ValidationFunction, set[str]]
|
|
56
59
|
] = {}
|
|
57
60
|
|
|
58
|
-
def newPluginData(self, validateXbrl: ValidateXbrl) -> PluginData:
|
|
61
|
+
def newPluginData(self, cntlr: Cntlr, validateXbrl: ValidateXbrl | None) -> PluginData:
|
|
59
62
|
"""
|
|
60
63
|
Returns a dataclass intended to be overriden by plugins to facilitate caching and passing data between rule functions.
|
|
61
64
|
The default implementation doesn't provide any fields other than the plugin name.
|
|
@@ -99,6 +102,46 @@ class ValidationPlugin:
|
|
|
99
102
|
) -> ModelDocument | LoadingException | None:
|
|
100
103
|
raise NotImplementedError
|
|
101
104
|
|
|
105
|
+
def validateComplete(
|
|
106
|
+
self,
|
|
107
|
+
cntlr: Cntlr,
|
|
108
|
+
fileSource: FileSource,
|
|
109
|
+
*args: Any,
|
|
110
|
+
**kwargs: Any,
|
|
111
|
+
) -> None:
|
|
112
|
+
"""
|
|
113
|
+
Executes validation functions in the rules module that was provided to the constructor of this class.
|
|
114
|
+
Each function decorated with [@validation](#arelle.utils.validate.Decorator.validation) will be run if:
|
|
115
|
+
1. the decorator was used with the validation complete hook: `@validation(hook=ValidationHook.COMPLETE)`
|
|
116
|
+
|
|
117
|
+
:param cntlr: The [Cntlr](#arelle.Cntlr.Cntlr) instance.
|
|
118
|
+
:param fileSource: The [FileSource](#arelle.FileSource.FileSource) involved in loading the entrypoint files.
|
|
119
|
+
:param args: Argument capture to ensure new parameters don't break plugin hook.
|
|
120
|
+
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
121
|
+
:return: None
|
|
122
|
+
"""
|
|
123
|
+
self._executeCntlrValidations(ValidationHook.COMPLETE, cntlr, fileSource, *args, **kwargs)
|
|
124
|
+
|
|
125
|
+
def validateFileSource(
|
|
126
|
+
self,
|
|
127
|
+
cntlr: Cntlr,
|
|
128
|
+
fileSource: FileSource,
|
|
129
|
+
*args: Any,
|
|
130
|
+
**kwargs: Any,
|
|
131
|
+
) -> None:
|
|
132
|
+
"""
|
|
133
|
+
Executes validation functions in the rules module that was provided to the constructor of this class.
|
|
134
|
+
Each function decorated with [@validation](#arelle.utils.validate.Decorator.validation) will be run if:
|
|
135
|
+
1. the decorator was used with the FileSource validation hook: `@validation(hook=ValidationHook.FILESOURCE)`
|
|
136
|
+
|
|
137
|
+
:param cntlr: The [Cntlr](#arelle.Cntlr.Cntlr) instance.
|
|
138
|
+
:param fileSource: The [FileSource](#arelle.FileSource.FileSource) to validate.
|
|
139
|
+
:param args: Argument capture to ensure new parameters don't break plugin hook.
|
|
140
|
+
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
141
|
+
:return: None
|
|
142
|
+
"""
|
|
143
|
+
self._executeCntlrValidations(ValidationHook.FILESOURCE, cntlr, fileSource, *args, **kwargs)
|
|
144
|
+
|
|
102
145
|
def validateXbrlStart(
|
|
103
146
|
self,
|
|
104
147
|
val: ValidateXbrl,
|
|
@@ -118,7 +161,7 @@ class ValidationPlugin:
|
|
|
118
161
|
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
119
162
|
:return: None
|
|
120
163
|
"""
|
|
121
|
-
self.
|
|
164
|
+
self._executeModelValidations(ValidationHook.XBRL_START, val, parameters, *args, **kwargs)
|
|
122
165
|
|
|
123
166
|
def validateXbrlFinally(
|
|
124
167
|
self,
|
|
@@ -137,7 +180,7 @@ class ValidationPlugin:
|
|
|
137
180
|
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
138
181
|
:return: None
|
|
139
182
|
"""
|
|
140
|
-
self.
|
|
183
|
+
self._executeModelValidations(ValidationHook.XBRL_FINALLY, val, *args, **kwargs)
|
|
141
184
|
|
|
142
185
|
def validateXbrlDtsDocument(
|
|
143
186
|
self,
|
|
@@ -158,7 +201,7 @@ class ValidationPlugin:
|
|
|
158
201
|
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
159
202
|
:return: None
|
|
160
203
|
"""
|
|
161
|
-
self.
|
|
204
|
+
self._executeModelValidations(ValidationHook.XBRL_DTS_DOCUMENT, val, modelDocument, isFilingDocument, *args, **kwargs)
|
|
162
205
|
|
|
163
206
|
def validateFinally(
|
|
164
207
|
self,
|
|
@@ -177,9 +220,28 @@ class ValidationPlugin:
|
|
|
177
220
|
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
178
221
|
:return: None
|
|
179
222
|
"""
|
|
180
|
-
self.
|
|
223
|
+
self._executeModelValidations(ValidationHook.FINALLY, val, *args, **kwargs)
|
|
224
|
+
|
|
225
|
+
def _executeCntlrValidations(
|
|
226
|
+
self,
|
|
227
|
+
pluginHook: ValidationHook,
|
|
228
|
+
cntlr: Cntlr,
|
|
229
|
+
fileSource: FileSource | None = None,
|
|
230
|
+
*args: Any,
|
|
231
|
+
**kwargs: Any,
|
|
232
|
+
) -> None:
|
|
233
|
+
if self.disclosureSystemFromPluginSelected(cntlr.modelManager):
|
|
234
|
+
pluginData = self.newPluginData(
|
|
235
|
+
cntlr=cntlr,
|
|
236
|
+
validateXbrl=None
|
|
237
|
+
)
|
|
238
|
+
for rule in self._getValidations(cntlr.modelManager.disclosureSystem, pluginHook):
|
|
239
|
+
validations = rule(pluginData, cntlr, fileSource, *args, **kwargs)
|
|
240
|
+
if validations is not None:
|
|
241
|
+
for val in validations:
|
|
242
|
+
cntlr.validation(val, fileSource=fileSource)
|
|
181
243
|
|
|
182
|
-
def
|
|
244
|
+
def _executeModelValidations(
|
|
183
245
|
self,
|
|
184
246
|
pluginHook: ValidationHook,
|
|
185
247
|
validateXbrl: ValidateXbrl,
|
|
@@ -189,20 +251,23 @@ class ValidationPlugin:
|
|
|
189
251
|
if self.disclosureSystemFromPluginSelected(validateXbrl):
|
|
190
252
|
pluginData = validateXbrl.getPluginData(self.name)
|
|
191
253
|
if pluginData is None:
|
|
192
|
-
pluginData = self.newPluginData(
|
|
254
|
+
pluginData = self.newPluginData(
|
|
255
|
+
cntlr=validateXbrl.modelXbrl.modelManager.cntlr,
|
|
256
|
+
validateXbrl=validateXbrl
|
|
257
|
+
)
|
|
193
258
|
validateXbrl.setPluginData(pluginData)
|
|
194
259
|
for rule in self._getValidations(validateXbrl.disclosureSystem, pluginHook):
|
|
195
260
|
validations = rule(pluginData, validateXbrl, *args, **kwargs)
|
|
196
261
|
if validations is not None:
|
|
197
262
|
modelXbrl = validateXbrl.modelXbrl
|
|
198
263
|
for val in validations:
|
|
199
|
-
modelXbrl.
|
|
264
|
+
modelXbrl.validation(val)
|
|
200
265
|
|
|
201
266
|
def disclosureSystemFromPluginSelected(
|
|
202
267
|
self,
|
|
203
|
-
model: ValidateXbrl | ModelXbrl,
|
|
268
|
+
model: ValidateXbrl | ModelManager | ModelXbrl,
|
|
204
269
|
) -> bool:
|
|
205
|
-
if isinstance(model, ValidateXbrl):
|
|
270
|
+
if isinstance(model, (ModelManager, ValidateXbrl)):
|
|
206
271
|
disclosureSystem = model.disclosureSystem
|
|
207
272
|
elif isinstance(model, ModelXbrl):
|
|
208
273
|
disclosureSystem = model.modelManager.disclosureSystem
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
"""
|
|
2
2
|
See COPYRIGHT.md for copyright information.
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
from __future__ import annotations
|
|
5
6
|
|
|
6
|
-
from collections.abc import Generator
|
|
7
|
+
from collections.abc import Generator, Iterable
|
|
7
8
|
|
|
8
9
|
from lxml.etree import _Element
|
|
9
10
|
|
|
11
|
+
from arelle import XbrlConst
|
|
12
|
+
from arelle.ModelDtsObject import ModelConcept
|
|
10
13
|
from arelle.ModelObject import ModelObject
|
|
14
|
+
from arelle.ModelValue import QName
|
|
15
|
+
from arelle.ModelXbrl import ModelXbrl
|
|
11
16
|
from arelle.typing import TypeGetText
|
|
12
17
|
|
|
13
18
|
_: TypeGetText
|
|
@@ -19,5 +24,32 @@ def etreeIterWithDepth(
|
|
|
19
24
|
) -> Generator[tuple[ModelObject | _Element, int], None, None]:
|
|
20
25
|
yield node, depth
|
|
21
26
|
for child in node.iterchildren():
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
yield from etreeIterWithDepth(child, depth + 1)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def hasPresentationalConceptsWithFacts(
|
|
31
|
+
modelXbrl: ModelXbrl,
|
|
32
|
+
roleUris: Iterable[str],
|
|
33
|
+
memberQnameFilter: set[QName] | None = None,
|
|
34
|
+
) -> bool:
|
|
35
|
+
"""
|
|
36
|
+
Returns True if any concepts used in the presentation network of the role URIs have been tagged with facts.
|
|
37
|
+
This DOES NOT check if the facts are dimensionally valid against hypercubes defined in the roles.
|
|
38
|
+
"""
|
|
39
|
+
roleRelSet = modelXbrl.relationshipSet(XbrlConst.parentChild, tuple(roleUris))
|
|
40
|
+
concepts = set(roleRelSet.fromModelObjects().keys()) | set(roleRelSet.toModelObjects().keys())
|
|
41
|
+
for concept in concepts:
|
|
42
|
+
if not isinstance(concept, ModelConcept):
|
|
43
|
+
continue
|
|
44
|
+
if concept.qname is None:
|
|
45
|
+
continue
|
|
46
|
+
if concept.isAbstract:
|
|
47
|
+
continue
|
|
48
|
+
for fact in modelXbrl.factsByQname.get(concept.qname, set()):
|
|
49
|
+
if memberQnameFilter is None:
|
|
50
|
+
return True
|
|
51
|
+
if fact.context is not None:
|
|
52
|
+
for dimValue in fact.context.qnameDims.values():
|
|
53
|
+
if dimValue.memberQname in memberQnameFilter:
|
|
54
|
+
return True
|
|
55
|
+
return False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arelle-release
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.38.0
|
|
4
4
|
Summary: An open source XBRL platform.
|
|
5
5
|
Author-email: "arelle.org" <support@arelle.org>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -18,47 +18,57 @@ Classifier: Intended Audience :: End Users/Desktop
|
|
|
18
18
|
Classifier: Intended Audience :: Developers
|
|
19
19
|
Classifier: Natural Language :: English
|
|
20
20
|
Classifier: Programming Language :: Python :: 3
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.10
|
|
23
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
24
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
25
24
|
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
26
26
|
Classifier: Operating System :: OS Independent
|
|
27
27
|
Classifier: Topic :: Text Processing :: Markup :: XML
|
|
28
|
-
Requires-Python:
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
License-File: LICENSE.md
|
|
31
31
|
Requires-Dist: bottle<0.14,>=0.13
|
|
32
32
|
Requires-Dist: certifi
|
|
33
33
|
Requires-Dist: filelock
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist:
|
|
34
|
+
Requires-Dist: isodate<1,>=0
|
|
35
|
+
Requires-Dist: jaconv<1,>=0
|
|
36
|
+
Requires-Dist: jsonschema<5,>=4
|
|
37
|
+
Requires-Dist: lxml!=6.0.0,<7,>=4
|
|
37
38
|
Requires-Dist: numpy<3,>=1
|
|
38
|
-
Requires-Dist: openpyxl
|
|
39
|
-
Requires-Dist: pillow<
|
|
40
|
-
Requires-Dist: pyparsing
|
|
41
|
-
Requires-Dist: python-dateutil
|
|
39
|
+
Requires-Dist: openpyxl<4,>=3
|
|
40
|
+
Requires-Dist: pillow<13,>=10
|
|
41
|
+
Requires-Dist: pyparsing<4,>=3
|
|
42
|
+
Requires-Dist: python-dateutil<3,>=2
|
|
42
43
|
Requires-Dist: regex
|
|
43
|
-
Requires-Dist: truststore
|
|
44
|
-
Requires-Dist: typing-extensions
|
|
44
|
+
Requires-Dist: truststore<1,>=0
|
|
45
|
+
Requires-Dist: typing-extensions<5,>=4
|
|
45
46
|
Provides-Extra: crypto
|
|
46
|
-
Requires-Dist: pycryptodome
|
|
47
|
+
Requires-Dist: pycryptodome<4,>=3; extra == "crypto"
|
|
47
48
|
Provides-Extra: db
|
|
48
|
-
Requires-Dist:
|
|
49
|
-
Requires-Dist:
|
|
49
|
+
Requires-Dist: cx_Oracle<9,>=8; extra == "db"
|
|
50
|
+
Requires-Dist: pg8000<2,>=1; extra == "db"
|
|
51
|
+
Requires-Dist: PyMySQL<2,>=1; extra == "db"
|
|
50
52
|
Requires-Dist: pyodbc<6,>=4; extra == "db"
|
|
51
53
|
Requires-Dist: rdflib<8,>=5; extra == "db"
|
|
52
54
|
Provides-Extra: efm
|
|
53
|
-
Requires-Dist:
|
|
55
|
+
Requires-Dist: aniso8601<11,>=10; extra == "efm"
|
|
56
|
+
Requires-Dist: holidays<1,>=0; extra == "efm"
|
|
57
|
+
Requires-Dist: matplotlib<4,>=3; extra == "efm"
|
|
54
58
|
Requires-Dist: pytz; extra == "efm"
|
|
55
59
|
Provides-Extra: esef
|
|
56
|
-
Requires-Dist: tinycss2
|
|
60
|
+
Requires-Dist: tinycss2<2,>=1; extra == "esef"
|
|
61
|
+
Provides-Extra: gui
|
|
62
|
+
Requires-Dist: pywin32<400,>=300; sys_platform == "win32" and extra == "gui"
|
|
57
63
|
Provides-Extra: objectmaker
|
|
58
|
-
Requires-Dist: graphviz
|
|
64
|
+
Requires-Dist: graphviz<1,>=0; extra == "objectmaker"
|
|
65
|
+
Provides-Extra: viewer
|
|
66
|
+
Requires-Dist: ixbrl-viewer<2,>=1; extra == "viewer"
|
|
59
67
|
Provides-Extra: webserver
|
|
60
|
-
Requires-Dist: cheroot<
|
|
61
|
-
Requires-Dist: tornado
|
|
68
|
+
Requires-Dist: cheroot<12,>=8; extra == "webserver"
|
|
69
|
+
Requires-Dist: tornado<7,>=6; extra == "webserver"
|
|
70
|
+
Provides-Extra: xule
|
|
71
|
+
Requires-Dist: aniso8601<11,>=10; extra == "xule"
|
|
62
72
|
Dynamic: license-file
|
|
63
73
|
|
|
64
74
|
# Arelle
|