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
arelle/BetaFeatures.py
CHANGED
|
@@ -3,27 +3,6 @@ See COPYRIGHT.md for copyright information.
|
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
-
BETA_OBJECT_MODEL_FEATURE = "betaObjectModel"
|
|
7
6
|
# Add camelCaseOptionName
|
|
8
7
|
BETA_FEATURES_AND_DESCRIPTIONS: dict[str, str] = {
|
|
9
|
-
BETA_OBJECT_MODEL_FEATURE: "Replace lxml based object model with a pure Python class hierarchy.",
|
|
10
8
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
_NEW_OBJECT_MODEL_STATUS_ACCESSED = False
|
|
14
|
-
_USE_NEW_OBJECT_MODEL = False
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def enableNewObjectModel() -> None:
|
|
18
|
-
global _USE_NEW_OBJECT_MODEL
|
|
19
|
-
if _USE_NEW_OBJECT_MODEL:
|
|
20
|
-
return
|
|
21
|
-
if _NEW_OBJECT_MODEL_STATUS_ACCESSED:
|
|
22
|
-
raise RuntimeError("Can't change object model transition setting after classes have already been defined.")
|
|
23
|
-
_USE_NEW_OBJECT_MODEL = True
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def newObjectModelEnabled() -> bool:
|
|
27
|
-
global _NEW_OBJECT_MODEL_STATUS_ACCESSED
|
|
28
|
-
_NEW_OBJECT_MODEL_STATUS_ACCESSED = True
|
|
29
|
-
return _USE_NEW_OBJECT_MODEL
|
arelle/Cntlr.py
CHANGED
|
@@ -18,24 +18,25 @@ import os
|
|
|
18
18
|
import subprocess
|
|
19
19
|
import sys
|
|
20
20
|
import tempfile
|
|
21
|
-
from typing import
|
|
21
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
22
22
|
|
|
23
|
-
import regex
|
|
23
|
+
import regex
|
|
24
24
|
|
|
25
25
|
from arelle import Locale, ModelManager, PackageManager, PluginManager, XbrlConst
|
|
26
26
|
from arelle.BetaFeatures import BETA_FEATURES_AND_DESCRIPTIONS
|
|
27
27
|
from arelle.ErrorManager import ErrorManager
|
|
28
28
|
from arelle.FileSource import FileSource
|
|
29
|
-
from arelle.SystemInfo import PlatformOS, getSystemWordSize, hasFileSystem, hasWebServer, isCGI, isGAE
|
|
30
|
-
from arelle.WebCache import WebCache
|
|
31
29
|
from arelle.logging.formatters.LogFormatter import LogFormatter, logRefsFileLines # noqa: F401 - for reimport
|
|
32
30
|
from arelle.logging.handlers.LogHandlerWithXml import LogHandlerWithXml # noqa: F401 - for reimport
|
|
33
31
|
from arelle.logging.handlers.LogToBufferHandler import LogToBufferHandler
|
|
34
32
|
from arelle.logging.handlers.LogToPrintHandler import LogToPrintHandler
|
|
35
33
|
from arelle.logging.handlers.LogToXmlHandler import LogToXmlHandler
|
|
36
34
|
from arelle.logging.handlers.StructuredMessageLogHandler import StructuredMessageLogHandler
|
|
35
|
+
from arelle.SystemInfo import PlatformOS, getSystemWordSize, hasFileSystem, hasWebServer, isCGI, isGAE
|
|
37
36
|
from arelle.typing import TypeGetText
|
|
38
37
|
from arelle.utils.PluginData import PluginData
|
|
38
|
+
from arelle.utils.validate.Validation import Validation
|
|
39
|
+
from arelle.WebCache import WebCache
|
|
39
40
|
|
|
40
41
|
_: TypeGetText
|
|
41
42
|
|
|
@@ -60,7 +61,7 @@ def resourcesDir() -> str:
|
|
|
60
61
|
# for python 3.2 remove __pycache__
|
|
61
62
|
if _moduleDir.endswith("__pycache__"):
|
|
62
63
|
_moduleDir = os.path.dirname(_moduleDir)
|
|
63
|
-
if (
|
|
64
|
+
if (regex.match(r".*[\\/](library|python{0.major}{0.minor}).zip[\\/]arelle$".format(sys.version_info),
|
|
64
65
|
_moduleDir)): # cx_Freexe uses library up to 3.4 and python35 after 3.5
|
|
65
66
|
_resourcesDir = os.path.dirname(os.path.dirname(_moduleDir))
|
|
66
67
|
else:
|
|
@@ -161,11 +162,12 @@ class Cntlr:
|
|
|
161
162
|
disable_persistent_config: bool = False,
|
|
162
163
|
betaFeatures: dict[str, bool] | None = None
|
|
163
164
|
) -> None:
|
|
165
|
+
self.logger = None
|
|
164
166
|
if betaFeatures is None:
|
|
165
167
|
betaFeatures = {}
|
|
166
168
|
self.betaFeatures = {
|
|
167
169
|
b: betaFeatures.get(b, False)
|
|
168
|
-
for b in BETA_FEATURES_AND_DESCRIPTIONS
|
|
170
|
+
for b in BETA_FEATURES_AND_DESCRIPTIONS
|
|
169
171
|
}
|
|
170
172
|
self.errorManager = None
|
|
171
173
|
self.hasWin32gui = False
|
|
@@ -297,6 +299,11 @@ class Cntlr:
|
|
|
297
299
|
self.startLogging(logFileName, logFileMode, logFileEncoding, logFormat)
|
|
298
300
|
self.errorManager = ErrorManager(self.modelManager, logging._checkLevel("INCONSISTENCY")) # type: ignore[attr-defined]
|
|
299
301
|
|
|
302
|
+
def validation(self, val: Validation, fileSource: FileSource | None = None) -> None:
|
|
303
|
+
"""Same as `error`, but parameters passed in from Validation object
|
|
304
|
+
"""
|
|
305
|
+
self.error(codes=val.codes, msg=val.msg, level=val.level.name, fileSource=fileSource, **val.args)
|
|
306
|
+
|
|
300
307
|
def error(self, codes: Any, msg: str, level: str = "ERROR", fileSource: FileSource | None = None, **args: Any) -> None:
|
|
301
308
|
if self.logger is None or self.errorManager is None:
|
|
302
309
|
self.addToLog(
|
|
@@ -434,11 +441,11 @@ class Cntlr:
|
|
|
434
441
|
|
|
435
442
|
def setLogLevelFilter(self, logLevelFilter: str) -> None:
|
|
436
443
|
if self.logger:
|
|
437
|
-
setattr(self.logger, "messageLevelFilter",
|
|
444
|
+
setattr(self.logger, "messageLevelFilter", regex.compile(logLevelFilter) if logLevelFilter else None)
|
|
438
445
|
|
|
439
446
|
def setLogCodeFilter(self, logCodeFilter: str) -> None:
|
|
440
447
|
if self.logger:
|
|
441
|
-
setattr(self.logger, "messageCodeFilter",
|
|
448
|
+
setattr(self.logger, "messageCodeFilter", regex.compile(logCodeFilter) if logCodeFilter else None)
|
|
442
449
|
|
|
443
450
|
def addToLog(
|
|
444
451
|
self,
|
arelle/CntlrCmdLine.py
CHANGED
|
@@ -51,14 +51,14 @@ from arelle.BetaFeatures import BETA_FEATURES_AND_DESCRIPTIONS
|
|
|
51
51
|
from arelle.Locale import format_string, setApplicationLocale, setDisableRTL
|
|
52
52
|
from arelle.ModelFormulaObject import FormulaOptions
|
|
53
53
|
from arelle.ModelValue import qname
|
|
54
|
+
from arelle.ValidateFileSource import ValidateFileSource
|
|
54
55
|
from arelle.oim.xml.Save import saveOimReportToXmlInstance
|
|
55
56
|
from arelle.rendering import RenderingEvaluator
|
|
56
57
|
from arelle.RuntimeOptions import RuntimeOptions, RuntimeOptionsException
|
|
57
58
|
from arelle.SocketUtils import INTERNET_CONNECTIVITY, OFFLINE
|
|
58
59
|
from arelle.SystemInfo import PlatformOS, getSystemInfo, getSystemWordSize, hasWebServer, isCGI, isGAE
|
|
59
60
|
from arelle.typing import TypeGetText
|
|
60
|
-
from arelle.
|
|
61
|
-
from arelle.utils.EntryPointDetection import filesourceEntrypointFiles
|
|
61
|
+
from arelle.utils.EntryPointDetection import parseEntrypointFileInput
|
|
62
62
|
from arelle.ValidateXbrlDTS import ValidateBaseTaxonomiesMode
|
|
63
63
|
from arelle.WebCache import proxyTuple
|
|
64
64
|
|
|
@@ -98,6 +98,38 @@ def parseAndRun(args):
|
|
|
98
98
|
return cntlr
|
|
99
99
|
|
|
100
100
|
|
|
101
|
+
PREPARSE_ARG_CONFIGS = frozenset([
|
|
102
|
+
(re.compile(r'^--plugins?.*$'), 'plugins'),
|
|
103
|
+
(re.compile(r'^--options(F|f)ile.*$'), 'optionsFile'),
|
|
104
|
+
])
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def preparseArgs(args: list[str], parser: OptionParser) -> dict[str, str]:
|
|
108
|
+
"""
|
|
109
|
+
Some command line arguments influence the actual parsing of other arguments.
|
|
110
|
+
This function pre-parses those arguments to allow for processing before full
|
|
111
|
+
argument parseing occurs.
|
|
112
|
+
:param args: Command line arguments
|
|
113
|
+
:param parser: OptionParser to report errors
|
|
114
|
+
:return: Dictionary of pre-parsed options
|
|
115
|
+
"""
|
|
116
|
+
preparsedArgs = {}
|
|
117
|
+
for i, arg in enumerate(args):
|
|
118
|
+
for pattern, preparsedArg in PREPARSE_ARG_CONFIGS:
|
|
119
|
+
if pattern.fullmatch(arg):
|
|
120
|
+
__, sep, value = arg.partition('=')
|
|
121
|
+
if sep: # --arg=value
|
|
122
|
+
preparsedValue = value
|
|
123
|
+
elif i < len(args) - 1: # --arg value
|
|
124
|
+
preparsedValue = args[i+1]
|
|
125
|
+
else: # --arg
|
|
126
|
+
preparsedValue = ""
|
|
127
|
+
if preparsedArg in preparsedArgs:
|
|
128
|
+
parser.error(_("Multiple '{}' values found during argument preparsing.").format(preparsedArg))
|
|
129
|
+
preparsedArgs[preparsedArg] = preparsedValue
|
|
130
|
+
return preparsedArgs
|
|
131
|
+
|
|
132
|
+
|
|
101
133
|
def parseArgs(args):
|
|
102
134
|
"""
|
|
103
135
|
Parses the command line arguments and generates runtimeOptions and arellePluginModules
|
|
@@ -412,24 +444,27 @@ def parseArgs(args):
|
|
|
412
444
|
pluginOptionsIndex = len(parser.option_list)
|
|
413
445
|
pluginOptionsGroupIndex = len(parser.option_groups)
|
|
414
446
|
|
|
447
|
+
preparsedArgs = preparseArgs(args, parser)
|
|
448
|
+
|
|
449
|
+
preloadPlugins = []
|
|
450
|
+
optionsFile = preparsedArgs.get('optionsFile')
|
|
451
|
+
optionsFileOptions = {}
|
|
452
|
+
if optionsFile:
|
|
453
|
+
optionsFileOptions = _parseOptionsFile(optionsFile, parser)
|
|
454
|
+
preloadPlugins.extend(optionsFileOptions.get('plugins', '').split('|'))
|
|
455
|
+
|
|
456
|
+
preloadPlugins.extend(preparsedArgs.get('plugins', '').split('|'))
|
|
457
|
+
|
|
415
458
|
# install any dynamic plugins so their command line options can be parsed if present
|
|
416
459
|
arellePluginModules = {}
|
|
417
|
-
for
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
for pluginCmd in preloadPlugins.split('|'):
|
|
426
|
-
cmd = pluginCmd.strip()
|
|
427
|
-
if cmd not in ("show", "temp") and len(cmd) > 0 and cmd[0] not in ('-', '~', '+'):
|
|
428
|
-
moduleInfo = PluginManager.addPluginModule(cmd)
|
|
429
|
-
if moduleInfo:
|
|
430
|
-
arellePluginModules[cmd] = moduleInfo
|
|
431
|
-
PluginManager.reset()
|
|
432
|
-
break
|
|
460
|
+
for pluginCmd in preloadPlugins:
|
|
461
|
+
cmd = pluginCmd.strip()
|
|
462
|
+
if cmd not in ("show", "temp") and len(cmd) > 0 and cmd[0] not in ('-', '~', '+'):
|
|
463
|
+
moduleInfo = PluginManager.addPluginModule(cmd)
|
|
464
|
+
if moduleInfo:
|
|
465
|
+
arellePluginModules[cmd] = moduleInfo
|
|
466
|
+
PluginManager.reset()
|
|
467
|
+
|
|
433
468
|
# add plug-in options
|
|
434
469
|
for optionsExtender in PluginManager.pluginClassMethods("CntlrCmdLine.Options"):
|
|
435
470
|
optionsExtender(parser)
|
|
@@ -440,6 +475,10 @@ def parseArgs(args):
|
|
|
440
475
|
help=_("Show product version, copyright, and license."))
|
|
441
476
|
parser.add_option("--diagnostics", action="store_true", dest="diagnostics",
|
|
442
477
|
help=_("output system diagnostics information"))
|
|
478
|
+
parser.add_option("--optionsFile", "--optionsfile",
|
|
479
|
+
action="store", dest="optionsFile",
|
|
480
|
+
help=_("Provide a path to a JSON file containing runtime options. "
|
|
481
|
+
"These options will be overridden by any command line options provided."))
|
|
443
482
|
|
|
444
483
|
if not args and isGAE():
|
|
445
484
|
args = ["--webserver=::gae"]
|
|
@@ -518,9 +557,22 @@ def parseArgs(args):
|
|
|
518
557
|
for optGroup in parser.option_groups[pluginOptionsGroupIndex:pluginLastOptionsGroupIndex]:
|
|
519
558
|
for groupOption in optGroup.option_list:
|
|
520
559
|
pluginOptionDestinations.add(groupOption.dest)
|
|
560
|
+
|
|
521
561
|
baseOptions = {}
|
|
522
|
-
|
|
562
|
+
# Collect options from options file
|
|
563
|
+
for optionName, optionValue in optionsFileOptions.items():
|
|
564
|
+
if not hasattr(RuntimeOptions, optionName) and optionName not in pluginOptionDestinations:
|
|
565
|
+
parser.error(_("Unexpected name '{}' found in options file.").format(optionName))
|
|
566
|
+
continue
|
|
567
|
+
baseOptions[optionName] = optionValue
|
|
568
|
+
# Collect options from command line
|
|
523
569
|
for optionName, optionValue in vars(options).items():
|
|
570
|
+
if optionName not in baseOptions or optionValue is not None:
|
|
571
|
+
baseOptions[optionName] = optionValue
|
|
572
|
+
|
|
573
|
+
pluginOptions = {}
|
|
574
|
+
finalOptions = {} # Validated options for RuntimeOptions
|
|
575
|
+
for optionName, optionValue in baseOptions.items():
|
|
524
576
|
if optionName in pluginOptionDestinations:
|
|
525
577
|
pluginOptions[optionName] = optionValue
|
|
526
578
|
else:
|
|
@@ -532,9 +584,10 @@ def parseArgs(args):
|
|
|
532
584
|
parser.error(_("--testcaseExpectedErrors must be in the format '--testcaseExpectedErrors=testcase-index.xml:v-1|errorCode1,errorCode2,...'"))
|
|
533
585
|
expectedErrors[expectedErrorSplit[0]] = expectedErrorSplit[1].split(',')
|
|
534
586
|
optionValue = expectedErrors
|
|
535
|
-
|
|
587
|
+
if optionValue is not None or optionName not in finalOptions:
|
|
588
|
+
finalOptions[optionName] = optionValue
|
|
536
589
|
try:
|
|
537
|
-
runtimeOptions = RuntimeOptions(pluginOptions=pluginOptions, **
|
|
590
|
+
runtimeOptions = RuntimeOptions(pluginOptions=pluginOptions, **finalOptions)
|
|
538
591
|
except RuntimeOptionsException as e:
|
|
539
592
|
parser.error(f"{e}, please try\n python CntlrCmdLine.py --help")
|
|
540
593
|
if (
|
|
@@ -620,7 +673,33 @@ class ParserForDynamicPlugins:
|
|
|
620
673
|
|
|
621
674
|
|
|
622
675
|
def _pluginHasCliOptions(moduleInfo):
|
|
623
|
-
|
|
676
|
+
if "CntlrCmdLine.Options" in moduleInfo["classMethods"]:
|
|
677
|
+
return True
|
|
678
|
+
if imports := moduleInfo.get("imports"):
|
|
679
|
+
return any(_pluginHasCliOptions(importedModule) for importedModule in imports)
|
|
680
|
+
return False
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
def _parseOptionsFile(optionsFile: str, parser: OptionParser) -> dict:
|
|
684
|
+
"""
|
|
685
|
+
Parse the JSON options within the provided filepath.
|
|
686
|
+
:param optionsFile: The path to the JSON options file.
|
|
687
|
+
:param parser: The parser to log an error to if needed.
|
|
688
|
+
:return: The parsed options as a dictionary.
|
|
689
|
+
"""
|
|
690
|
+
try:
|
|
691
|
+
with open(optionsFile) as f:
|
|
692
|
+
jsonOptions = json.load(f)
|
|
693
|
+
except OSError:
|
|
694
|
+
parser.error(_("Options file path does not exist: {}").format(optionsFile))
|
|
695
|
+
return {}
|
|
696
|
+
except Exception as e:
|
|
697
|
+
parser.error(_("Unable to parse options JSON file: {}").format(e))
|
|
698
|
+
return {}
|
|
699
|
+
if not isinstance(jsonOptions, dict):
|
|
700
|
+
parser.error(_("Options JSON file must contain a JSON object at its root."))
|
|
701
|
+
return {}
|
|
702
|
+
return jsonOptions
|
|
624
703
|
|
|
625
704
|
|
|
626
705
|
class CntlrCmdLine(Cntlr.Cntlr):
|
|
@@ -823,6 +902,9 @@ class CntlrCmdLine(Cntlr.Cntlr):
|
|
|
823
902
|
else:
|
|
824
903
|
self.modelManager.disclosureSystem.select(None) # just load ordinary mappings
|
|
825
904
|
self.modelManager.validateDisclosureSystem = False
|
|
905
|
+
if self.modelManager.disclosureSystem.keepOpen:
|
|
906
|
+
# Force keepOpen if specified by disclosure system.
|
|
907
|
+
options.keepOpen = True
|
|
826
908
|
if options.baseTaxonomyValidationMode is not None:
|
|
827
909
|
self.modelManager.baseTaxonomyValidationMode = ValidateBaseTaxonomiesMode.fromName(options.baseTaxonomyValidationMode)
|
|
828
910
|
self.modelManager.validateXmlOim = bool(options.validateXmlOim)
|
|
@@ -947,6 +1029,7 @@ class CntlrCmdLine(Cntlr.Cntlr):
|
|
|
947
1029
|
if options.testcaseFilters:
|
|
948
1030
|
fo.testcaseFilters = options.testcaseFilters
|
|
949
1031
|
if options.testcaseResultsCaptureWarnings:
|
|
1032
|
+
self.errorManager.setErrorCaptureLevel(logging._checkLevel("WARNING"))
|
|
950
1033
|
fo.testcaseResultsCaptureWarnings = True
|
|
951
1034
|
if options.testcaseResultOptions:
|
|
952
1035
|
fo.testcaseResultOptions = options.testcaseResultOptions
|
|
@@ -974,44 +1057,19 @@ class CntlrCmdLine(Cntlr.Cntlr):
|
|
|
974
1057
|
if not (options.entrypointFile or sourceZipStream):
|
|
975
1058
|
return True # success
|
|
976
1059
|
|
|
1060
|
+
entrypointParseResult = parseEntrypointFileInput(self, options.entrypointFile, sourceZipStream)
|
|
1061
|
+
if not entrypointParseResult.success:
|
|
1062
|
+
return False
|
|
1063
|
+
filesource = entrypointParseResult.filesource
|
|
1064
|
+
_entrypointFiles = entrypointParseResult.entrypointFiles
|
|
977
1065
|
success = True
|
|
978
|
-
# entrypointFile may be absent (if input is a POSTED zip or file name ending in .zip)
|
|
979
|
-
# or may be a | separated set of file names
|
|
980
|
-
_entryPoints = []
|
|
981
|
-
_checkIfXmlIsEis = self.modelManager.disclosureSystem and self.modelManager.disclosureSystem.validationType == "EFM"
|
|
982
|
-
if options.entrypointFile:
|
|
983
|
-
_f = options.entrypointFile
|
|
984
|
-
try: # may be a json list
|
|
985
|
-
_entryPoints = json.loads(_f)
|
|
986
|
-
_checkIfXmlIsEis = False # json entry objects never specify an xml EIS archive
|
|
987
|
-
except ValueError as e:
|
|
988
|
-
# is it malformed json?
|
|
989
|
-
if _f.startswith("[{") or _f.endswith("]}") or '"file:"' in _f:
|
|
990
|
-
self.addToLog(_("File name parameter appears to be malformed JSON: {}\n{}").format(e, _f),
|
|
991
|
-
messageCode="FileNameFormatError",
|
|
992
|
-
level=logging.ERROR)
|
|
993
|
-
success = False
|
|
994
|
-
else: # try as file names separated by '|'
|
|
995
|
-
for f in (_f or '').split('|'):
|
|
996
|
-
if not sourceZipStream and not isHttpUrl(f) and not os.path.isabs(f):
|
|
997
|
-
f = os.path.normpath(os.path.join(os.getcwd(), f)) # make absolute normed path
|
|
998
|
-
_entryPoints.append({"file":f})
|
|
999
|
-
filesource = None # file source for all instances if not None
|
|
1000
|
-
if sourceZipStream:
|
|
1001
|
-
filesource = FileSource.openFileSource(None, self, sourceZipStream)
|
|
1002
|
-
elif len(_entryPoints) == 1 and "file" in _entryPoints[0]: # check if an archive and need to discover entry points (and not IXDS)
|
|
1003
|
-
entryPath = PackageManager.mappedUrl(_entryPoints[0]["file"])
|
|
1004
|
-
filesource = FileSource.openFileSource(entryPath, self, checkIfXmlIsEis=_checkIfXmlIsEis)
|
|
1005
|
-
_entrypointFiles = _entryPoints
|
|
1006
|
-
if filesource and not filesource.selection and not (sourceZipStream and len(_entrypointFiles) > 0):
|
|
1007
|
-
try:
|
|
1008
|
-
filesourceEntrypointFiles(filesource, _entrypointFiles)
|
|
1009
|
-
except Exception as err:
|
|
1010
|
-
self.addToLog(str(err), messageCode="error", level=logging.ERROR)
|
|
1011
|
-
return False
|
|
1012
1066
|
|
|
1013
1067
|
for pluginXbrlMethod in PluginManager.pluginClassMethods("CntlrCmdLine.Filing.Start"):
|
|
1014
1068
|
pluginXbrlMethod(self, options, filesource, _entrypointFiles, sourceZipStream=sourceZipStream, responseZipStream=responseZipStream)
|
|
1069
|
+
|
|
1070
|
+
if options.validate and filesource is not None:
|
|
1071
|
+
ValidateFileSource(self, filesource).validate(options.reportPackage)
|
|
1072
|
+
|
|
1015
1073
|
if len(_entrypointFiles) == 0 and not options.packages:
|
|
1016
1074
|
if options.entrypointFile:
|
|
1017
1075
|
msg = _("No XBRL entry points could be loaded from provided file: {}").format(options.entrypointFile)
|
|
@@ -1027,6 +1085,8 @@ class CntlrCmdLine(Cntlr.Cntlr):
|
|
|
1027
1085
|
else:
|
|
1028
1086
|
_entrypointFile = PackageManager.mappedUrl(_entrypointFile)
|
|
1029
1087
|
filesource = FileSource.openFileSource(_entrypointFile, self, sourceZipStream)
|
|
1088
|
+
if options.validate:
|
|
1089
|
+
ValidateFileSource(self, filesource).validate(options.reportPackage)
|
|
1030
1090
|
self.entrypointFile = _entrypointFile
|
|
1031
1091
|
timeNow = XmlUtil.dateunionValue(datetime.datetime.now())
|
|
1032
1092
|
firstStartedAt = startedAt = time.time()
|
|
@@ -1239,6 +1299,11 @@ class CntlrCmdLine(Cntlr.Cntlr):
|
|
|
1239
1299
|
self.modelManager.close(modelDiffReport)
|
|
1240
1300
|
elif modelXbrl:
|
|
1241
1301
|
self.modelManager.close(modelXbrl)
|
|
1302
|
+
|
|
1303
|
+
if options.validate:
|
|
1304
|
+
for pluginXbrlMethod in PluginManager.pluginClassMethods("Validate.Complete"):
|
|
1305
|
+
pluginXbrlMethod(self, filesource)
|
|
1306
|
+
|
|
1242
1307
|
if filesource is not None and not options.keepOpen:
|
|
1243
1308
|
# Archive filesource potentially used by multiple reports may still be open.
|
|
1244
1309
|
filesource.close()
|
arelle/CntlrWinMain.py
CHANGED
|
@@ -5,65 +5,110 @@ See COPYRIGHT.md for copyright information.
|
|
|
5
5
|
'''
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
import os
|
|
8
|
+
import fnmatch
|
|
9
|
+
import os
|
|
10
|
+
import pickle
|
|
11
|
+
import platform
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
import time
|
|
15
|
+
import webbrowser
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
10
18
|
import regex as re
|
|
11
19
|
|
|
20
|
+
from arelle import UrlUtil, ValidateDuplicateFacts
|
|
21
|
+
from arelle.ValidateFileSource import ValidateFileSource
|
|
12
22
|
from arelle.logging.formatters.LogFormatter import logRefsFileLines
|
|
23
|
+
from arelle.utils.EntryPointDetection import parseEntrypointFileInput
|
|
13
24
|
|
|
14
25
|
if sys.platform == 'win32' and getattr(sys, 'frozen', False):
|
|
15
26
|
# need the .dll directory in path to be able to access Tk and Tcl DLLs efore importinng Tk, etc.
|
|
16
27
|
os.environ['PATH'] = os.path.dirname(sys.executable) + ";" + os.environ['PATH']
|
|
17
28
|
|
|
18
|
-
from tkinter import (
|
|
19
|
-
|
|
29
|
+
from tkinter import (
|
|
30
|
+
END,
|
|
31
|
+
EW,
|
|
32
|
+
HORIZONTAL,
|
|
33
|
+
VERTICAL,
|
|
34
|
+
BooleanVar,
|
|
35
|
+
E,
|
|
36
|
+
IntVar,
|
|
37
|
+
Menu,
|
|
38
|
+
N,
|
|
39
|
+
PhotoImage,
|
|
40
|
+
S,
|
|
41
|
+
StringVar,
|
|
42
|
+
Tcl,
|
|
43
|
+
TclError,
|
|
44
|
+
Tk,
|
|
45
|
+
W,
|
|
46
|
+
)
|
|
47
|
+
from tkinter import font as tkFont
|
|
48
|
+
|
|
20
49
|
try:
|
|
21
|
-
from tkinter.ttk import
|
|
50
|
+
from tkinter.ttk import Button, Combobox, Frame, Label, Notebook, PanedWindow, Separator
|
|
22
51
|
except ImportError: # 3.0 versions of tkinter
|
|
23
|
-
from ttk import
|
|
52
|
+
from ttk import Button, Combobox, Frame, Label, Notebook, PanedWindow, Separator
|
|
24
53
|
try:
|
|
25
54
|
import syslog
|
|
26
55
|
except ImportError:
|
|
27
56
|
syslog = None
|
|
57
|
+
|
|
58
|
+
import logging
|
|
59
|
+
import multiprocessing
|
|
60
|
+
import queue
|
|
61
|
+
import threading
|
|
28
62
|
import tkinter.filedialog
|
|
29
|
-
import tkinter.messagebox
|
|
63
|
+
import tkinter.messagebox
|
|
30
64
|
import tkinter.simpledialog
|
|
31
|
-
|
|
65
|
+
import traceback
|
|
66
|
+
|
|
67
|
+
from arelle import (
|
|
68
|
+
Cntlr,
|
|
69
|
+
DialogLanguage,
|
|
70
|
+
DialogPackageManager,
|
|
71
|
+
DialogPluginManager,
|
|
72
|
+
DialogURL,
|
|
73
|
+
ModelDocument,
|
|
74
|
+
PackageManager,
|
|
75
|
+
TableStructure,
|
|
76
|
+
Updater,
|
|
77
|
+
ViewFileConcepts,
|
|
78
|
+
ViewFileDTS,
|
|
79
|
+
ViewFileFactList,
|
|
80
|
+
ViewFileFactTable,
|
|
81
|
+
ViewFileFormulae,
|
|
82
|
+
ViewFileRelationshipSet,
|
|
83
|
+
ViewFileRoleTypes,
|
|
84
|
+
ViewFileTests,
|
|
85
|
+
ViewWinConcepts,
|
|
86
|
+
ViewWinDTS,
|
|
87
|
+
ViewWinFactList,
|
|
88
|
+
ViewWinFactTable,
|
|
89
|
+
ViewWinFormulae,
|
|
90
|
+
ViewWinProperties,
|
|
91
|
+
ViewWinRelationshipSet,
|
|
92
|
+
ViewWinRenderedGrid,
|
|
93
|
+
ViewWinRoleTypes,
|
|
94
|
+
ViewWinRssFeed,
|
|
95
|
+
ViewWinTests,
|
|
96
|
+
ViewWinTree,
|
|
97
|
+
ViewWinVersReport,
|
|
98
|
+
ViewWinXml,
|
|
99
|
+
XbrlConst,
|
|
100
|
+
)
|
|
32
101
|
from arelle.CntlrWinTooltip import ToolTip
|
|
33
|
-
from arelle import
|
|
102
|
+
from arelle.FileSource import openFileSource
|
|
103
|
+
from arelle.Locale import format_string, setApplicationLocale
|
|
104
|
+
from arelle.ModelFormulaObject import FormulaOptions
|
|
105
|
+
from arelle.oim.xml.Save import saveOimReportToXmlInstance
|
|
34
106
|
from arelle.PluginManager import pluginClassMethods
|
|
107
|
+
from arelle.rendering import RenderingEvaluator
|
|
35
108
|
from arelle.UrlUtil import isHttpUrl
|
|
36
109
|
from arelle.ValidateXbrlCalcs import ValidateCalcsMode as CalcsMode
|
|
37
110
|
from arelle.ValidateXbrlDTS import ValidateBaseTaxonomiesMode
|
|
38
111
|
from arelle.Version import copyrightLabel
|
|
39
|
-
from arelle.oim.xml.Save import saveOimReportToXmlInstance
|
|
40
|
-
import logging
|
|
41
|
-
import multiprocessing
|
|
42
|
-
|
|
43
|
-
import threading, queue
|
|
44
|
-
|
|
45
|
-
from arelle import Cntlr
|
|
46
|
-
from arelle import (DialogURL, DialogLanguage,
|
|
47
|
-
DialogPluginManager, DialogPackageManager,
|
|
48
|
-
ModelDocument,
|
|
49
|
-
ModelManager,
|
|
50
|
-
PackageManager,
|
|
51
|
-
TableStructure,
|
|
52
|
-
ViewWinDTS,
|
|
53
|
-
ViewWinProperties, ViewWinConcepts, ViewWinRelationshipSet, ViewWinFormulae,
|
|
54
|
-
ViewWinFactList, ViewFileFactList, ViewWinFactTable, ViewWinRenderedGrid, ViewWinXml,
|
|
55
|
-
ViewWinRoleTypes, ViewFileRoleTypes, ViewFileConcepts,
|
|
56
|
-
ViewWinTests, ViewWinTree, ViewWinVersReport, ViewWinRssFeed,
|
|
57
|
-
ViewFileDTS,
|
|
58
|
-
ViewFileFactTable,
|
|
59
|
-
ViewFileFormulae,
|
|
60
|
-
ViewFileTests,
|
|
61
|
-
ViewFileRelationshipSet,
|
|
62
|
-
Updater
|
|
63
|
-
)
|
|
64
|
-
from arelle.ModelFormulaObject import FormulaOptions
|
|
65
|
-
from arelle.FileSource import openFileSource
|
|
66
|
-
from arelle.rendering import RenderingEvaluator
|
|
67
112
|
|
|
68
113
|
restartMain = True
|
|
69
114
|
|
|
@@ -816,15 +861,25 @@ class CntlrWinMain (Cntlr.Cntlr):
|
|
|
816
861
|
if filename:
|
|
817
862
|
for xbrlLoadedMethod in pluginClassMethods("CntlrWinMain.Xbrl.Open"):
|
|
818
863
|
filename = xbrlLoadedMethod(self, filename) # runs in GUI thread, allows mapping filename, mult return filename
|
|
819
|
-
|
|
864
|
+
entrypointParseResult = parseEntrypointFileInput(self, filename, fallbackSelect=False)
|
|
865
|
+
if not entrypointParseResult.success or entrypointParseResult.filesource is None:
|
|
866
|
+
return
|
|
867
|
+
filesource = entrypointParseResult.filesource
|
|
868
|
+
entrypointFiles = entrypointParseResult.entrypointFiles
|
|
820
869
|
# check for archive files
|
|
821
|
-
filesource = openFileSource(filename, self,
|
|
822
|
-
checkIfXmlIsEis=self.modelManager.disclosureSystem and
|
|
823
|
-
self.modelManager.disclosureSystem.validationType == "EFM")
|
|
824
870
|
if filesource.isArchive:
|
|
825
|
-
|
|
871
|
+
filenameWithoutFakeIxdsPrefix = UrlUtil.stripIxdsSurrogatePrefix(filename)
|
|
872
|
+
if all(e.get("file") == filenameWithoutFakeIxdsPrefix for e in entrypointFiles):
|
|
873
|
+
entrypointFiles = []
|
|
874
|
+
if (
|
|
875
|
+
len(entrypointFiles) == 0 and
|
|
876
|
+
not filesource.selection and
|
|
877
|
+
not filesource.isReportPackage
|
|
878
|
+
):
|
|
826
879
|
from arelle import DialogOpenArchive
|
|
827
880
|
filename = DialogOpenArchive.askArchiveFile(self, filesource)
|
|
881
|
+
if filename is not None:
|
|
882
|
+
entrypointFiles.append({"file": filename})
|
|
828
883
|
if filename and filesource.basefile and not isHttpUrl(filesource.basefile):
|
|
829
884
|
self.config["fileOpenDir"] = os.path.dirname(filesource.baseurl)
|
|
830
885
|
filesource.loadTaxonomyPackageMappings() # if a package, load mappings if not loaded yet
|
|
@@ -837,7 +892,7 @@ class CntlrWinMain (Cntlr.Cntlr):
|
|
|
837
892
|
self.updateFileHistory(filename, importToDTS)
|
|
838
893
|
elif len(filename) == 1:
|
|
839
894
|
self.updateFileHistory(filename[0], importToDTS)
|
|
840
|
-
thread = threading.Thread(target=self.backgroundLoadXbrl, args=(filesource,importToDTS,selectTopView), daemon=True).start()
|
|
895
|
+
thread = threading.Thread(target=self.backgroundLoadXbrl, args=(filesource,entrypointFiles,importToDTS,selectTopView), daemon=True).start()
|
|
841
896
|
|
|
842
897
|
def webOpen(self, *ignore):
|
|
843
898
|
if not self.okayToContinue():
|
|
@@ -848,12 +903,24 @@ class CntlrWinMain (Cntlr.Cntlr):
|
|
|
848
903
|
self.updateFileHistory(url, False)
|
|
849
904
|
for xbrlLoadedMethod in pluginClassMethods("CntlrWinMain.Xbrl.Open"):
|
|
850
905
|
url = xbrlLoadedMethod(self, url) # runs in GUI thread, allows mapping url, mult return url
|
|
851
|
-
|
|
852
|
-
if
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
906
|
+
entrypointParseResult = parseEntrypointFileInput(self, url, fallbackSelect=False)
|
|
907
|
+
if not entrypointParseResult.success or entrypointParseResult.filesource is None:
|
|
908
|
+
return
|
|
909
|
+
filesource = entrypointParseResult.filesource
|
|
910
|
+
entrypointFiles = entrypointParseResult.entrypointFiles
|
|
911
|
+
if filesource.isArchive:
|
|
912
|
+
if (
|
|
913
|
+
len(entrypointFiles) == 0 and
|
|
914
|
+
not filesource.selection and
|
|
915
|
+
not filesource.isReportPackage
|
|
916
|
+
):
|
|
917
|
+
from arelle import DialogOpenArchive
|
|
918
|
+
url = DialogOpenArchive.askArchiveFile(self, filesource)
|
|
919
|
+
if url is not None:
|
|
920
|
+
entrypointFiles.append({"file": url})
|
|
921
|
+
self.updateFileHistory(url, False)
|
|
922
|
+
filesource.loadTaxonomyPackageMappings()
|
|
923
|
+
thread = threading.Thread(target=self.backgroundLoadXbrl, args=(filesource,entrypointFiles,False,False), daemon=True).start()
|
|
857
924
|
|
|
858
925
|
def importWebOpen(self, *ignore):
|
|
859
926
|
if not self.modelManager.modelXbrl or self.modelManager.modelXbrl.modelDocument.type not in (
|
|
@@ -867,36 +934,37 @@ class CntlrWinMain (Cntlr.Cntlr):
|
|
|
867
934
|
self.fileOpenFile(url, importToDTS=True)
|
|
868
935
|
|
|
869
936
|
|
|
870
|
-
def backgroundLoadXbrl(self, filesource, importToDTS, selectTopView):
|
|
937
|
+
def backgroundLoadXbrl(self, filesource, entrypointFiles: list[dict[str, Any]], importToDTS, selectTopView):
|
|
871
938
|
startedAt = time.time()
|
|
872
939
|
loadedModels = []
|
|
873
940
|
try:
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
if
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
loadedModels.append(modelXbrl)
|
|
882
|
-
else:
|
|
883
|
-
action = _("loaded")
|
|
884
|
-
profileStat = "load"
|
|
885
|
-
if (reportPackage := filesource.reportPackage) and "_IXDS#?#" not in filesource.url:
|
|
886
|
-
for report in reportPackage.reports or []:
|
|
887
|
-
if len(report.fullPathFiles) > 1:
|
|
888
|
-
self.addToLog(_("Loading error. Inline document set encountered. Enable 'Inline XBRL Document Set' plug-in and use the Open Inline Doc Set dialog from the file menu to open this filing: {0}").format(filesource.url))
|
|
889
|
-
continue
|
|
890
|
-
filesource.select(report.fullPathPrimary)
|
|
891
|
-
modelXbrl = self.modelManager.load(filesource, _("views loading"))
|
|
892
|
-
if modelXbrl:
|
|
893
|
-
loadedModels.append(modelXbrl)
|
|
941
|
+
action = _("preparing entrypoints")
|
|
942
|
+
for pluginXbrlMethod in pluginClassMethods("CntlrWinMain.Filing.Start"):
|
|
943
|
+
pluginXbrlMethod(self, filesource, entrypointFiles)
|
|
944
|
+
for entrypoint in entrypointFiles:
|
|
945
|
+
entrypointFile = entrypoint.get("file", None) if isinstance(entrypoint,dict) else entrypoint
|
|
946
|
+
if filesource and filesource.isArchive:
|
|
947
|
+
filesource.select(entrypointFile)
|
|
894
948
|
else:
|
|
949
|
+
entrypointFile = PackageManager.mappedUrl(entrypointFile)
|
|
950
|
+
filesource = openFileSource(entrypointFile, self)
|
|
951
|
+
if importToDTS:
|
|
952
|
+
action = _("imported")
|
|
953
|
+
profileStat = "import"
|
|
954
|
+
modelXbrl = self.modelManager.modelXbrl
|
|
955
|
+
if modelXbrl:
|
|
956
|
+
ModelDocument.load(modelXbrl, filesource.url, isSupplemental=importToDTS)
|
|
957
|
+
modelXbrl.relationshipSets.clear() # relationships have to be re-cached
|
|
958
|
+
loadedModels.append(modelXbrl)
|
|
959
|
+
else:
|
|
960
|
+
action = _("loaded")
|
|
961
|
+
profileStat = "load"
|
|
895
962
|
modelXbrl = self.modelManager.load(
|
|
896
963
|
filesource,
|
|
897
964
|
_("views loading"),
|
|
898
965
|
# check modified time if GUI-loading from web
|
|
899
966
|
checkModifiedTime=isHttpUrl(filesource.url),
|
|
967
|
+
entrypoint=entrypoint,
|
|
900
968
|
)
|
|
901
969
|
if modelXbrl:
|
|
902
970
|
loadedModels.append(modelXbrl)
|
|
@@ -1135,12 +1203,17 @@ class CntlrWinMain (Cntlr.Cntlr):
|
|
|
1135
1203
|
|
|
1136
1204
|
def backgroundValidate(self):
|
|
1137
1205
|
from arelle import Validate
|
|
1206
|
+
validatedFileSources = set()
|
|
1138
1207
|
for loadedModelXbrl in self.modelManager.loadedModelXbrls:
|
|
1139
1208
|
if loadedModelXbrl.modelDocument:
|
|
1140
1209
|
startedAt = time.time()
|
|
1141
1210
|
if loadedModelXbrl.modelDocument.type in ModelDocument.Type.TESTCASETYPES:
|
|
1142
1211
|
for pluginXbrlMethod in pluginClassMethods("Testcases.Start"):
|
|
1143
1212
|
pluginXbrlMethod(self, None, loadedModelXbrl)
|
|
1213
|
+
if loadedModelXbrl.fileSource not in validatedFileSources:
|
|
1214
|
+
validatedFileSources.add(loadedModelXbrl.fileSource)
|
|
1215
|
+
ValidateFileSource(self, loadedModelXbrl.fileSource).validate(self.modelManager.validateAllFilesAsReportPackages)
|
|
1216
|
+
|
|
1144
1217
|
for modelXbrl in [loadedModelXbrl] + getattr(loadedModelXbrl, "supplementalModelXbrls", []):
|
|
1145
1218
|
priorOutputInstance = modelXbrl.formulaOutputInstance
|
|
1146
1219
|
modelXbrl.formulaOutputInstance = None # prevent closing on background thread by validateFormula
|