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/DialogFind.py
CHANGED
|
@@ -186,7 +186,7 @@ class DialogFind(Toplevel):
|
|
|
186
186
|
else:
|
|
187
187
|
if not self.modelManager.modelXbrl or not docType in (
|
|
188
188
|
ModelDocument.Type.SCHEMA, ModelDocument.Type.LINKBASE, ModelDocument.Type.INSTANCE, ModelDocument.Type.INLINEXBRL,
|
|
189
|
-
ModelDocument.Type.RSSFEED):
|
|
189
|
+
ModelDocument.Type.RSSFEED, ModelDocument.Type.INLINEXBRLDOCUMENTSET):
|
|
190
190
|
messagebox.showerror(_("Find cannot be completed"),
|
|
191
191
|
_("Find requires an opened DTS or RSS Feed"), parent=self.parent)
|
|
192
192
|
return
|
arelle/DialogPluginManager.py
CHANGED
|
@@ -386,15 +386,17 @@ class DialogPluginManager(Toplevel):
|
|
|
386
386
|
group = {
|
|
387
387
|
"ixbrl-viewer": "1", # pip installed Arelle viewer
|
|
388
388
|
"iXBRLViewerPlugin": "2", # git clone installed Arelle viewer
|
|
389
|
-
"
|
|
389
|
+
"EDGAR": "3",
|
|
390
|
+
"Inline XBRL Document Set": "4",
|
|
391
|
+
"XBRL rule processor (xule)": "5",
|
|
390
392
|
}.get(key)
|
|
391
393
|
if not group:
|
|
392
394
|
if key.startswith("Validate"):
|
|
393
|
-
group = "
|
|
395
|
+
group = "6"
|
|
394
396
|
elif key.startswith("xbrlDB"):
|
|
395
|
-
group = "
|
|
397
|
+
group = "7"
|
|
396
398
|
else:
|
|
397
|
-
group = "
|
|
399
|
+
group = "8"
|
|
398
400
|
return group + key.lower()
|
|
399
401
|
|
|
400
402
|
@staticmethod
|
arelle/DisclosureSystem.py
CHANGED
|
@@ -62,6 +62,7 @@ class DisclosureSystem:
|
|
|
62
62
|
self.validateEntryText = False
|
|
63
63
|
self.allowedExternalHrefPattern = None
|
|
64
64
|
self.allowedImageTypes = None
|
|
65
|
+
self.arcroleCyclesAllowed = {}
|
|
65
66
|
self.schemaValidateSchema = None
|
|
66
67
|
self.blockDisallowedReferences = False
|
|
67
68
|
self.maxSubmissionSubdirectoryEntryNesting = 0
|
|
@@ -79,6 +80,7 @@ class DisclosureSystem:
|
|
|
79
80
|
self.utrUrl = ["http://www.xbrl.org/utr/utr.xml"]
|
|
80
81
|
self.utrStatusFilters = None
|
|
81
82
|
self.utrTypeEntries = None
|
|
83
|
+
self.keepOpen: bool = False
|
|
82
84
|
self.identifierSchemePattern = None
|
|
83
85
|
self.identifierValuePattern = None
|
|
84
86
|
self.identifierValueName = None
|
|
@@ -182,6 +184,10 @@ class DisclosureSystem:
|
|
|
182
184
|
self.allowedExternalHrefPattern = re.compile(dsElt.get("allowedExternalHrefPattern"))
|
|
183
185
|
if dsElt.get("allowedImageTypes"):
|
|
184
186
|
self.allowedImageTypes = json.loads(dsElt.get("allowedImageTypes"))
|
|
187
|
+
if dsElt.get("arcroleCyclesAllowed"):
|
|
188
|
+
self.arcroleCyclesAllowed = json.loads(dsElt.get("arcroleCyclesAllowed"))
|
|
189
|
+
for arcrole, specSet in self.arcroleCyclesAllowed.items():
|
|
190
|
+
self.arcroleCyclesAllowed[arcrole] = tuple(specSet)
|
|
185
191
|
self.blockDisallowedReferences = dsElt.get("blockDisallowedReferences") == "true"
|
|
186
192
|
try:
|
|
187
193
|
self.maxSubmissionSubdirectoryEntryNesting = int(dsElt.get("maxSubmissionSubdirectoryEntryNesting"))
|
|
@@ -209,6 +215,7 @@ class DisclosureSystem:
|
|
|
209
215
|
self.utrUrl = [self.modelManager.cntlr.webCache.normalizeUrl(u, url)
|
|
210
216
|
for u in dsElt.get("utrUrl").split()]
|
|
211
217
|
self.utrStatusFilters = dsElt.get("utrStatusFilters")
|
|
218
|
+
self.keepOpen = dsElt.get("keepOpen") == "true"
|
|
212
219
|
self.identifierSchemePattern = compileAttrPattern(dsElt,"identifierSchemePattern")
|
|
213
220
|
self.identifierValuePattern = compileAttrPattern(dsElt,"identifierValuePattern")
|
|
214
221
|
self.identifierValueName = dsElt.get("identifierValueName")
|
arelle/ErrorManager.py
CHANGED
|
@@ -180,7 +180,10 @@ class ErrorManager:
|
|
|
180
180
|
objectUrl = arg
|
|
181
181
|
else:
|
|
182
182
|
try:
|
|
183
|
-
|
|
183
|
+
if isinstance(arg, ObjectPropertyViewWrapper):
|
|
184
|
+
objectUrl = arg.modelObject.modelDocument.displayUri
|
|
185
|
+
else:
|
|
186
|
+
objectUrl = arg.modelDocument.displayUri
|
|
184
187
|
except AttributeError:
|
|
185
188
|
try:
|
|
186
189
|
objectUrl = arg.displayUri
|
|
@@ -204,13 +207,22 @@ class ErrorManager:
|
|
|
204
207
|
_arg:ModelObject = arg.modelObject if isinstance(arg, ObjectPropertyViewWrapper) else arg
|
|
205
208
|
if len(modelObjectArgs) > 1 and getattr(arg,"tag",None) == "instance":
|
|
206
209
|
continue # skip IXDS top level element
|
|
207
|
-
fragmentIdentifier = cast(str, XmlUtil.elementFragmentIdentifier(_arg))
|
|
208
|
-
if not hasattr(_arg, 'modelDocument') and _arg.namespaceURI == XbrlConst.svg:
|
|
210
|
+
fragmentIdentifier = "#" + cast(str, XmlUtil.elementFragmentIdentifier(_arg))
|
|
211
|
+
if not hasattr(_arg, 'modelDocument') and _arg.namespaceURI == XbrlConst.svg and len(refs) > 0:
|
|
209
212
|
# This is an embedded SVG document without its own file.
|
|
210
|
-
|
|
213
|
+
# Set the href to the containing document element that defined the encoded SVG.
|
|
214
|
+
# and define a nestedHrefs attribute with the fragment identifier.
|
|
215
|
+
priorRef = refs[-1]
|
|
216
|
+
ref["href"] = priorRef["href"]
|
|
217
|
+
priorNestedHrefs = priorRef.get("customAttributes", {}).get("nestedHrefs", [])
|
|
218
|
+
ref["customAttributes"] = {
|
|
219
|
+
"nestedHrefs": [*priorNestedHrefs, fragmentIdentifier]
|
|
220
|
+
}
|
|
221
|
+
if priorArgSourceline := priorRef.get("sourceLine"):
|
|
222
|
+
ref["sourceLine"] = priorArgSourceline
|
|
211
223
|
else:
|
|
212
|
-
ref["href"] = file +
|
|
213
|
-
|
|
224
|
+
ref["href"] = file + fragmentIdentifier
|
|
225
|
+
ref["sourceLine"] = _arg.sourceline
|
|
214
226
|
ref["objectId"] = _arg.objectId()
|
|
215
227
|
if logRefObjectProperties:
|
|
216
228
|
try:
|
|
@@ -304,3 +316,6 @@ class ErrorManager:
|
|
|
304
316
|
if isinstance(argValue, dict):
|
|
305
317
|
return dict((self._loggableValue(k), self._loggableValue(v)) for k, v in argValue.items())
|
|
306
318
|
return str(argValue)
|
|
319
|
+
|
|
320
|
+
def setErrorCaptureLevel(self, errorCaptureLevel: int) -> None:
|
|
321
|
+
self._errorCaptureLevel = errorCaptureLevel
|
arelle/FileSource.py
CHANGED
|
@@ -51,7 +51,10 @@ def openFileSource(
|
|
|
51
51
|
sourceFileSource: FileSource | None = None,
|
|
52
52
|
) -> FileSource:
|
|
53
53
|
if sourceZipStream:
|
|
54
|
-
if
|
|
54
|
+
if name := getattr(sourceZipStream, "name", None):
|
|
55
|
+
# Python IO convention is to use the name attribute
|
|
56
|
+
sourceZipStreamFileName: str = os.sep + str(name)
|
|
57
|
+
elif isinstance(sourceZipStream, FileNamedBytesIO) and sourceZipStream.fileName:
|
|
55
58
|
sourceZipStreamFileName = os.sep + sourceZipStream.fileName
|
|
56
59
|
else:
|
|
57
60
|
sourceZipStreamFileName = os.sep + "POSTupload.zip"
|
|
@@ -564,7 +567,7 @@ class FileSource:
|
|
|
564
567
|
if encoding is None:
|
|
565
568
|
encoding = XmlUtil.encoding(b)
|
|
566
569
|
if stripDeclaration:
|
|
567
|
-
b = stripDeclarationBytes(b)
|
|
570
|
+
b = stripDeclarationBytes(b, encoding)
|
|
568
571
|
return (FileNamedTextIOWrapper(filepath, io.BytesIO(b), encoding=encoding),
|
|
569
572
|
encoding)
|
|
570
573
|
except KeyError as err:
|
|
@@ -581,7 +584,7 @@ class FileSource:
|
|
|
581
584
|
if encoding is None:
|
|
582
585
|
encoding = XmlUtil.encoding(b)
|
|
583
586
|
if stripDeclaration:
|
|
584
|
-
b = stripDeclarationBytes(b)
|
|
587
|
+
b = stripDeclarationBytes(b, encoding)
|
|
585
588
|
return (FileNamedTextIOWrapper(filepath, io.BytesIO(b), encoding=encoding),
|
|
586
589
|
encoding)
|
|
587
590
|
except KeyError as err:
|
|
@@ -914,7 +917,11 @@ def openXmlFileStream(
|
|
|
914
917
|
text = stripDeclarationText(text)
|
|
915
918
|
return (FileNamedStringIO(filepath, initial_value=text), encoding)
|
|
916
919
|
|
|
917
|
-
def stripDeclarationBytes(xml: bytes) -> bytes:
|
|
920
|
+
def stripDeclarationBytes(xml: bytes, encoding: str | None) -> bytes:
|
|
921
|
+
if encoding is not None and not encoding.lower().startswith('utf-8'):
|
|
922
|
+
text = xml.decode(encoding)
|
|
923
|
+
text = stripDeclarationText(text)
|
|
924
|
+
return text.encode(encoding)
|
|
918
925
|
xmlStart = xml[0:120]
|
|
919
926
|
indexOfDeclaration = xmlStart.find(b"<?xml")
|
|
920
927
|
if indexOfDeclaration >= 0:
|
arelle/FunctionIxt.py
CHANGED
|
@@ -1254,20 +1254,25 @@ tr5Functions.update ({
|
|
|
1254
1254
|
})
|
|
1255
1255
|
deprecatedNamespaceURI = 'http://www.xbrl.org/2008/inlineXBRL/transformation' # the CR/PR pre-REC namespace
|
|
1256
1256
|
|
|
1257
|
+
ixtNamespacesByVersion = {
|
|
1258
|
+
1: 'http://www.xbrl.org/inlineXBRL/transformation/2010-04-20',
|
|
1259
|
+
2: 'http://www.xbrl.org/inlineXBRL/transformation/2011-07-31',
|
|
1260
|
+
3: 'http://www.xbrl.org/inlineXBRL/transformation/2015-02-26',
|
|
1261
|
+
4: 'http://www.xbrl.org/inlineXBRL/transformation/2020-02-12',
|
|
1262
|
+
5: 'http://www.xbrl.org/inlineXBRL/transformation/2022-02-16',
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1257
1265
|
ixtNamespaces = {
|
|
1258
|
-
"ixt
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
"ixt v4": 'http://www.xbrl.org/inlineXBRL/transformation/2020-02-12',
|
|
1262
|
-
"ixt v5": 'http://www.xbrl.org/inlineXBRL/transformation/2022-02-16',
|
|
1263
|
-
}
|
|
1266
|
+
f"ixt v{key}": val
|
|
1267
|
+
for key, val in ixtNamespacesByVersion.items()
|
|
1268
|
+
}
|
|
1264
1269
|
|
|
1265
1270
|
ixtNamespaceFunctions = {
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
+
ixtNamespacesByVersion[1]: tr1Functions, # transformation registry v1
|
|
1272
|
+
ixtNamespacesByVersion[2]: tr2Functions, # transformation registry v2
|
|
1273
|
+
ixtNamespacesByVersion[3]: tr3Functions, # transformation registry v3
|
|
1274
|
+
ixtNamespacesByVersion[4]: tr4Functions, # transformation registry v4
|
|
1275
|
+
ixtNamespacesByVersion[5]: tr5Functions, # transformation registry v5
|
|
1271
1276
|
"http://www.xbrl.org/inlineXBRL/transformation/WGWD/YYYY-MM-DD": tr5Functions, # transformation registry v4 draft
|
|
1272
1277
|
'http://www.xbrl.org/2008/inlineXBRL/transformation': tr1Functions # the CR/PR pre-REC namespace
|
|
1273
1278
|
}
|
arelle/HtmlUtil.py
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'''
|
|
2
2
|
See COPYRIGHT.md for copyright information.
|
|
3
3
|
'''
|
|
4
|
-
|
|
4
|
+
from __future__ import annotations
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
def attrValue(attr: str) -> str:
|
|
7
8
|
# retrieves attribute in a string, such as xyz="abc" or xyz='abc' or xyz=abc;
|
|
8
|
-
prestuff, matchedName, valuePart =
|
|
9
|
-
value = []
|
|
9
|
+
prestuff, matchedName, valuePart = attr.lower().partition("charset")
|
|
10
|
+
value: list[str] = []
|
|
10
11
|
endSep = None
|
|
11
12
|
beforeEquals = True
|
|
12
13
|
for c in valuePart:
|
arelle/LeiUtil.py
CHANGED
|
@@ -1,55 +1,75 @@
|
|
|
1
|
-
|
|
1
|
+
"""
|
|
2
2
|
See COPYRIGHT.md for copyright information.
|
|
3
3
|
|
|
4
4
|
Implementation of ISO 17442:2012(E) Appendix A
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import string
|
|
9
|
+
from enum import Enum, auto
|
|
10
|
+
|
|
7
11
|
import regex as re
|
|
8
12
|
|
|
9
|
-
LEI_VALID = 0
|
|
10
|
-
LEI_INVALID_LEXICAL = 1
|
|
11
|
-
LEI_INVALID_CHECKSUM = 2
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
class LEIValidationResult(Enum):
|
|
15
|
+
VALID = auto()
|
|
16
|
+
INVALID_LEXICAL = auto()
|
|
17
|
+
INVALID_CHECKSUM = auto()
|
|
18
|
+
|
|
19
|
+
def description(self) -> str:
|
|
20
|
+
return self.name.lower().replace("_", " ")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
LEI_VALID = LEIValidationResult.VALID
|
|
24
|
+
LEI_INVALID_LEXICAL = LEIValidationResult.INVALID_LEXICAL
|
|
25
|
+
LEI_INVALID_CHECKSUM = LEIValidationResult.INVALID_CHECKSUM
|
|
26
|
+
|
|
27
|
+
_leiLexicalPattern = re.compile(r"[0-9A-Z]{18}[0-9]{2}", re.ASCII)
|
|
28
|
+
_requiredLEILength = 20
|
|
29
|
+
|
|
30
|
+
_leiToDigitTable = str.maketrans(
|
|
31
|
+
dict(
|
|
32
|
+
(ord(char), str(index))
|
|
33
|
+
for index, char in enumerate(string.ascii_uppercase, start=len(string.digits))
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# The pattern comes from Solvency II documentation here https://dev.eiopa.europa.eu/Taxonomy/Full/2.3.0/PF/EIOPA_PensionFunds_Validations_2.3.0_PWD2.xlsx
|
|
38
|
+
_validLeiDespiteChecksumFailPatternString = r"^(029200720E3M3A4D6D01|029200758D5M0AI3F601|315700X8JQ3IU0NGK501|3157007SCCESQAUH5Z01|315700TCC9NTEP7J8Z01|13250000000000000301|315700ADOXDR5PCY5400|315700A0UB9Q7DOQIZ00|315700BM9Z39TNTGQW00|315700BZ5F7DRYG2UM00|315700B8401GTFFY6X01|315700DJ07P6OX10FK01|315700DKCD4QSKLAMO01|315700D23JL5C1DZNT00|315700EIYO2TLSEGQ700|315700ET7M7VQ4C84R00|315700EZSEA51937KX01|315700GXBGM8DBYKHF01|315700GZA843JXKTJ400|315700G5G24XYL1TXH00|315700HS7WJ1B0SUWM01|315700HXOEOK58E58P01|315700HZU4SMI8LZTU00|315700I3W2AFHP8MNQ01|315700JICZ3SY5SAXX00|315700JKZH6I0067ND01|315700JO5E28SRE00Q01|315700JXUHL9H2C3P700|315700LDDN3RM7Y2MP00|315700LWYOZNQ7V1T100|315700MBYPT6PGKO7M01|315700MW2F0KFR45QW01|315700M5843O6DU83901|315700NNMGS8F3P2CN00|315700N0VEIBHP0NPQ01|315700OASRCM664PAW01|315700OFW4YCOBNX4U01|315700OVW93X0T3HP200|315700O666JVNCQU9X00|315700PLI0I7W8IOV400|315700PN3J57ZUNF1V00|315700PXKOSX7WQV4N00|315700P4N9VSLK5QZV01|315700P40OV6BT045900|315700P6TZOLP92KN801|315700P89WR82VNB8Z00|315700QGM4XWZE1I5N01|315700Q1S8O1UORF9700|315700RK8M4FAHMYAP01|315700RTEHY362KXWJ00|315700SNUXK41WMW5J00|315700S22RGYRIEEOT00|315700S3TF79ALV82F01|315700TF5Z7T28HZJK01|315700TWGZ89LLSRS000|315700TXNX10N8XH4K00|315700T2EEQAPBO0C301|315700T6T49EDM16YO01|315700UJ6N4LGKLNPB00|315700UKZXWXEO126601|315700UYFD5GF9R13F01|315700VG7PTE9EJJRX01|315700VITYR7AL4M9S01|315700VMAJZ9JZTXNQ00|315700WH3YMKHCVYW201|315700WKCDF4QGRRO200|315700WR4IHOO1M5LP00|315700WYOZ6994UATN00|315700WZPEIS41QDKE00|315700XEFYMA5EZ0P500|315700XE21UYOA3GAC01|315700XI4Z8GF5BDUJ01|315700XSCP1S8WOD8E01|315700X40GNCOUWJYR00|315700YS6RQ5TF3VBP01|315700Y1W7W1JHAUBW00|315700Y5JNQMMUF5ID01|3157000VAJWZ3P8ZED00|3157001KM8GOU7PXZY01|3157001K2LAL04D87901|3157001MLDD3SDFQA901|3157001TPR6K4GBTLN00|31570010000000006400|31570010000000009601|31570010000000019301|31570010000000025800|31570010000000029001|31570010000000035500|31570010000000038701|31570010000000045200|31570010000000048401|31570010000000054900|31570010000000064600|31570010000000067801|31570010000000077501|31570010000000084000|31570010000000087201|31570010000000096901|31570010000000103400|31570010000000106601|31570010000000116301|31570010000000122800|31570010000000126001|3157002CKDQOCIHE5H01|31570020000000005900|31570029808HJVCNFA01|3157003FQSSGS9OZ9E01|3157004PTVTDOKB46401|3157004R6CH6C1P4KX00|3157005RUI28M8FANK00|3157005VJE7A3MBUS201|3157005WT1SENAE17R00|31570058O0Z320C4GZ00|3157006B6JVZ5DFMSN00|3157006DE3SPNIUY9K01|3157006FR3JBBOLOMX01|3157006I3B6RSTPQLI00|3157006KT1EZ15OIXW00|315700659AALVVLVIO01|31570067WSCDST0S3F01|3157008VKYORMUNC5O01|3157008ZY7CW6LVU5J00|3157009FTHFDDK7FHW01|3157009OVCV07O4HXM00|315700T8U7IU4W8J3A01|315700BBRQHDWX6SHZ00|3157008KD17KROO7UT01)$"
|
|
14
39
|
|
|
15
|
-
|
|
40
|
+
# Turn the _validLeiDespiteChecksumFailPattern in to an immutable set as that is much faster to check
|
|
41
|
+
_validLeiDespiteChecksumFailSet = frozenset(
|
|
42
|
+
_validLeiDespiteChecksumFailPatternString[2:-2].split("|")
|
|
43
|
+
)
|
|
16
44
|
|
|
17
|
-
validInvalidLeiPattern = re.compile("^(029200720E3M3A4D6D01|029200758D5M0AI3F601|315700X8JQ3IU0NGK501|3157007SCCESQAUH5Z01|315700TCC9NTEP7J8Z01|13250000000000000301|315700ADOXDR5PCY5400|315700A0UB9Q7DOQIZ00|315700BM9Z39TNTGQW00|315700BZ5F7DRYG2UM00|315700B8401GTFFY6X01|315700DJ07P6OX10FK01|315700DKCD4QSKLAMO01|315700D23JL5C1DZNT00|315700EIYO2TLSEGQ700|315700ET7M7VQ4C84R00|315700EZSEA51937KX01|315700GXBGM8DBYKHF01|315700GZA843JXKTJ400|315700G5G24XYL1TXH00|315700HS7WJ1B0SUWM01|315700HXOEOK58E58P01|315700HZU4SMI8LZTU00|315700I3W2AFHP8MNQ01|315700JICZ3SY5SAXX00|315700JKZH6I0067ND01|315700JO5E28SRE00Q01|315700JXUHL9H2C3P700|315700LDDN3RM7Y2MP00|315700LWYOZNQ7V1T100|315700MBYPT6PGKO7M01|315700MW2F0KFR45QW01|315700M5843O6DU83901|315700NNMGS8F3P2CN00|315700N0VEIBHP0NPQ01|315700OASRCM664PAW01|315700OFW4YCOBNX4U01|315700OVW93X0T3HP200|315700O666JVNCQU9X00|315700PLI0I7W8IOV400|315700PN3J57ZUNF1V00|315700PXKOSX7WQV4N00|315700P4N9VSLK5QZV01|315700P40OV6BT045900|315700P6TZOLP92KN801|315700P89WR82VNB8Z00|315700QGM4XWZE1I5N01|315700Q1S8O1UORF9700|315700RK8M4FAHMYAP01|315700RTEHY362KXWJ00|315700SNUXK41WMW5J00|315700S22RGYRIEEOT00|315700S3TF79ALV82F01|315700TF5Z7T28HZJK01|315700TWGZ89LLSRS000|315700TXNX10N8XH4K00|315700T2EEQAPBO0C301|315700T6T49EDM16YO01|315700UJ6N4LGKLNPB00|315700UKZXWXEO126601|315700UYFD5GF9R13F01|315700VG7PTE9EJJRX01|315700VITYR7AL4M9S01|315700VMAJZ9JZTXNQ00|315700WH3YMKHCVYW201|315700WKCDF4QGRRO200|315700WR4IHOO1M5LP00|315700WYOZ6994UATN00|315700WZPEIS41QDKE00|315700XEFYMA5EZ0P500|315700XE21UYOA3GAC01|315700XI4Z8GF5BDUJ01|315700XSCP1S8WOD8E01|315700X40GNCOUWJYR00|315700YS6RQ5TF3VBP01|315700Y1W7W1JHAUBW00|315700Y5JNQMMUF5ID01|3157000VAJWZ3P8ZED00|3157001KM8GOU7PXZY01|3157001K2LAL04D87901|3157001MLDD3SDFQA901|3157001TPR6K4GBTLN00|31570010000000006400|31570010000000009601|31570010000000019301|31570010000000025800|31570010000000029001|31570010000000035500|31570010000000038701|31570010000000045200|31570010000000048401|31570010000000054900|31570010000000064600|31570010000000067801|31570010000000077501|31570010000000084000|31570010000000087201|31570010000000096901|31570010000000103400|31570010000000106601|31570010000000116301|31570010000000122800|31570010000000126001|3157002CKDQOCIHE5H01|31570020000000005900|31570029808HJVCNFA01|3157003FQSSGS9OZ9E01|3157004PTVTDOKB46401|3157004R6CH6C1P4KX00|3157005RUI28M8FANK00|3157005VJE7A3MBUS201|3157005WT1SENAE17R00|31570058O0Z320C4GZ00|3157006B6JVZ5DFMSN00|3157006DE3SPNIUY9K01|3157006FR3JBBOLOMX01|3157006I3B6RSTPQLI00|3157006KT1EZ15OIXW00|315700659AALVVLVIO01|31570067WSCDST0S3F01|3157008VKYORMUNC5O01|3157008ZY7CW6LVU5J00|3157009FTHFDDK7FHW01|3157009OVCV07O4HXM00|315700T8U7IU4W8J3A01|315700BBRQHDWX6SHZ00|3157008KD17KROO7UT01)$")
|
|
18
45
|
|
|
19
|
-
def checkLei(lei: str) ->
|
|
20
|
-
if
|
|
21
|
-
return
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
46
|
+
def checkLei(lei: str) -> LEIValidationResult:
|
|
47
|
+
if len(lei) != _requiredLEILength:
|
|
48
|
+
return LEIValidationResult.INVALID_LEXICAL
|
|
49
|
+
|
|
50
|
+
if not _leiLexicalPattern.fullmatch(lei):
|
|
51
|
+
return LEIValidationResult.INVALID_LEXICAL
|
|
52
|
+
|
|
53
|
+
result = LEIValidationResult.INVALID_CHECKSUM
|
|
54
|
+
|
|
55
|
+
if int(lei.translate(_leiToDigitTable)) % 97 == 1:
|
|
56
|
+
result = LEIValidationResult.VALID
|
|
57
|
+
|
|
58
|
+
if (
|
|
59
|
+
result is LEIValidationResult.INVALID_CHECKSUM
|
|
60
|
+
and lei in _validLeiDespiteChecksumFailSet
|
|
61
|
+
):
|
|
62
|
+
result = LEIValidationResult.VALID
|
|
63
|
+
|
|
64
|
+
return result
|
|
65
|
+
|
|
33
66
|
|
|
34
67
|
if __name__ == "__main__":
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
("21380016W7GAG26FIJ74", "SOCIETE FRANCAISE ET SUISSE"),
|
|
44
|
-
("21380058ERUIT9H53T71", "TOTAN ICAP CO., LTD"),
|
|
45
|
-
("213800A9GT65GAES2V60", "BARCLAYS SECURITIES JAPAN LIMITED"),
|
|
46
|
-
("213800DELL1MWFDHVN53", "PIRELLI JAPAN"),
|
|
47
|
-
("213800A9GT65GAES2V60", "BARCLAYS SECURITIES JAPAN LIMITED"),
|
|
48
|
-
("214800A9GT65GAES2V60", "Error 1"),
|
|
49
|
-
("213800A9GT65GAE%2V60", "Error 2"),
|
|
50
|
-
("213800A9GT65GAES2V62", "Error 3"),
|
|
51
|
-
("1234", "Error 4"),
|
|
52
|
-
("""
|
|
53
|
-
5299003M8JKHEFX58Y02""", "Error 4")
|
|
54
|
-
):
|
|
55
|
-
print ("LEI {} result {} name {}".format(lei, LEI_RESULTS[checkLei(lei)], name) )
|
|
68
|
+
import sys
|
|
69
|
+
|
|
70
|
+
leis = sys.argv[1:]
|
|
71
|
+
if not leis:
|
|
72
|
+
raise SystemExit("Specify the LEI(s) you want to check as arguments.")
|
|
73
|
+
|
|
74
|
+
for arg in sys.argv[1:]:
|
|
75
|
+
print(f"{arg:20s} : {checkLei(arg).description()}")
|
arelle/ModelDocument.py
CHANGED
|
@@ -446,7 +446,7 @@ def loadSchemalocatedSchema(modelXbrl, element, relativeUrl, namespace, baseUrl)
|
|
|
446
446
|
doc.inDTS = False
|
|
447
447
|
return doc
|
|
448
448
|
|
|
449
|
-
def create(modelXbrl, type, uri, schemaRefs=None, isEntry=False, initialXml=None, initialComment=None, base=None, discover=True, documentEncoding="utf-8", xbrliNamespacePrefix=None) -> ModelDocument:
|
|
449
|
+
def create(modelXbrl, type, uri, schemaRefs=None, isEntry=False, initialXml=None, initialComment=None, base=None, discover=True, documentEncoding="utf-8", xbrliNamespacePrefix=None, entrypoint=None) -> ModelDocument:
|
|
450
450
|
"""Returns a new modelDocument, created from scratch, with any necessary header elements
|
|
451
451
|
|
|
452
452
|
(such as the schema, instance, or RSS feed top level elements)
|
|
@@ -538,6 +538,7 @@ def create(modelXbrl, type, uri, schemaRefs=None, isEntry=False, initialXml=None
|
|
|
538
538
|
modelDocument.isQualifiedElementFormDefault = False
|
|
539
539
|
modelDocument.isQualifiedAttributeFormDefault = False
|
|
540
540
|
modelDocument.definesUTR = False
|
|
541
|
+
modelDocument.entrypoint = entrypoint
|
|
541
542
|
return modelDocument
|
|
542
543
|
|
|
543
544
|
class Type:
|
|
@@ -727,10 +728,12 @@ class ModelDocument:
|
|
|
727
728
|
# The document encoding. The XML declaration is stripped from the document
|
|
728
729
|
# before lxml parses the document making the lxml DocInfo encoding unreliable.
|
|
729
730
|
documentEncoding: str
|
|
731
|
+
entrypoint: dict[str, Any] | None
|
|
730
732
|
xmlRootElement: Any
|
|
731
733
|
targetXbrlRootElement: ModelObject
|
|
732
734
|
|
|
733
735
|
def __init__(self, modelXbrl, type, uri, filepath, xmlDocument):
|
|
736
|
+
self.entrypoint = None
|
|
734
737
|
self.modelXbrl = modelXbrl
|
|
735
738
|
self.skipDTS = modelXbrl.skipDTS
|
|
736
739
|
self.type = type
|
|
@@ -1579,12 +1582,6 @@ def inlineIxdsDiscover(modelXbrl, modelIxdsDocument, setTargetModelXbrl=False, *
|
|
|
1579
1582
|
modelXbrl.targetRoleRefs = {} # roleRefs used by selected target
|
|
1580
1583
|
modelXbrl.targetArcroleRefs = {} # arcroleRefs used by selected target
|
|
1581
1584
|
modelXbrl.targetRelationships = set() # relationship elements used by selected target
|
|
1582
|
-
targetModelXbrl = modelXbrl if setTargetModelXbrl else None # modelXbrl of target for contexts/units in multi-target/multi-instance situation
|
|
1583
|
-
assignUnusedContextsUnits = (not setTargetModelXbrl and not ixdsTarget and
|
|
1584
|
-
not getattr(modelXbrl, "supplementalModelXbrls", ()) and (
|
|
1585
|
-
not getattr(modelXbrl, "targetIXDSesToLoad", ()) or
|
|
1586
|
-
set(e.modelDocument for e in modelXbrl.ixdsHtmlElements) ==
|
|
1587
|
-
set(x.modelDocument for e in getattr(modelXbrl, "targetIXDSesToLoad", ()) for x in e[1])))
|
|
1588
1585
|
hasResources = hasHeader = False
|
|
1589
1586
|
for htmlElement in modelXbrl.ixdsHtmlElements:
|
|
1590
1587
|
mdlDoc = htmlElement.modelDocument
|
|
@@ -1611,8 +1608,12 @@ def inlineIxdsDiscover(modelXbrl, modelIxdsDocument, setTargetModelXbrl=False, *
|
|
|
1611
1608
|
for modelInlineFact in htmlElement.iterdescendants(ixNStag + "nonNumeric", ixNStag + "nonFraction", ixNStag + "fraction"):
|
|
1612
1609
|
if isinstance(modelInlineFact,ModelObject):
|
|
1613
1610
|
_target = modelInlineFact.get("target")
|
|
1614
|
-
|
|
1615
|
-
|
|
1611
|
+
contextRef = modelInlineFact.get("contextRef")
|
|
1612
|
+
if contextRef is not None:
|
|
1613
|
+
factTargetContextRefs[_target].add(contextRef.strip())
|
|
1614
|
+
unitRef = modelInlineFact.get("unitRef")
|
|
1615
|
+
if unitRef is not None:
|
|
1616
|
+
factTargetUnitRefs[_target].add(unitRef.strip())
|
|
1616
1617
|
if modelInlineFact.id:
|
|
1617
1618
|
factsByFactID[modelInlineFact.id] = modelInlineFact
|
|
1618
1619
|
for elt in htmlElement.iterdescendants(tag=ixNStag + "continuation"):
|
|
@@ -1734,9 +1735,9 @@ def inlineIxdsDiscover(modelXbrl, modelIxdsDocument, setTargetModelXbrl=False, *
|
|
|
1734
1735
|
targetRoleUris[_target].add(footnoteRole)
|
|
1735
1736
|
|
|
1736
1737
|
contextRefs = factTargetContextRefs[ixdsTarget]
|
|
1738
|
+
contextRefsForAllTargets = {ref for refs in factTargetContextRefs.values() for ref in refs}
|
|
1737
1739
|
unitRefs = factTargetUnitRefs[ixdsTarget]
|
|
1738
|
-
|
|
1739
|
-
allUnitRefs = set.union(*factTargetUnitRefs.values())
|
|
1740
|
+
unitRefsForAllTargets = {ref for refs in factTargetUnitRefs.values() for ref in refs}
|
|
1740
1741
|
|
|
1741
1742
|
# discovery of contexts, units and roles which are used by target document
|
|
1742
1743
|
for htmlElement in modelXbrl.ixdsHtmlElements:
|
|
@@ -1745,13 +1746,17 @@ def inlineIxdsDiscover(modelXbrl, modelIxdsDocument, setTargetModelXbrl=False, *
|
|
|
1745
1746
|
|
|
1746
1747
|
for inlineElement in htmlElement.iterdescendants(tag=ixNStag + "resources"):
|
|
1747
1748
|
for elt in inlineElement.iterchildren("{http://www.xbrl.org/2003/instance}context"):
|
|
1748
|
-
|
|
1749
|
-
if
|
|
1749
|
+
contextId = elt.get("id")
|
|
1750
|
+
if contextId in contextRefs:
|
|
1750
1751
|
modelIxdsDocument.contextDiscover(elt, setTargetModelXbrl)
|
|
1752
|
+
elif contextId not in contextRefsForAllTargets:
|
|
1753
|
+
modelXbrl.ixdsUnmappedContexts[contextId] = elt
|
|
1751
1754
|
for elt in inlineElement.iterchildren("{http://www.xbrl.org/2003/instance}unit"):
|
|
1752
|
-
|
|
1753
|
-
if
|
|
1755
|
+
unitId = elt.get("id")
|
|
1756
|
+
if unitId in unitRefs:
|
|
1754
1757
|
modelIxdsDocument.unitDiscover(elt, setTargetModelXbrl)
|
|
1758
|
+
elif unitId not in unitRefsForAllTargets:
|
|
1759
|
+
modelXbrl.ixdsUnmappedUnits[unitId] = elt
|
|
1755
1760
|
for refElement in inlineElement.iterchildren("{http://www.xbrl.org/2003/linkbase}roleRef"):
|
|
1756
1761
|
r = refElement.get("roleURI")
|
|
1757
1762
|
if r in targetRoleUris[ixdsTarget]:
|
arelle/ModelDtsObject.py
CHANGED
|
@@ -1690,6 +1690,7 @@ class RelationStatus:
|
|
|
1690
1690
|
INEFFECTIVE = 4
|
|
1691
1691
|
|
|
1692
1692
|
arcCustAttrsExclusions = {XbrlConst.xlink, "use","priority","order","weight","preferredLabel"}
|
|
1693
|
+
modelObjectAttrs = frozenset(dir(ModelObject))
|
|
1693
1694
|
|
|
1694
1695
|
class ModelRelationship(ModelObject):
|
|
1695
1696
|
"""
|
|
@@ -2014,6 +2015,7 @@ class ModelRelationship(ModelObject):
|
|
|
2014
2015
|
@property
|
|
2015
2016
|
def equivalenceHash(self): # not exact, use equivalenceKey if hashes are the same
|
|
2016
2017
|
return hash((self.qname,
|
|
2018
|
+
self.arcrole,
|
|
2017
2019
|
self.linkQname,
|
|
2018
2020
|
self.linkrole, # needed when linkrole=None merges multiple links
|
|
2019
2021
|
self.fromModelObject.objectIndex if isinstance(self.fromModelObject, ModelObject) else -1,
|
|
@@ -2027,6 +2029,7 @@ class ModelRelationship(ModelObject):
|
|
|
2027
2029
|
"""(tuple) -- Key to determine relationship equivalence per 2.1 spec"""
|
|
2028
2030
|
# cannot be cached because this is unique per relationship
|
|
2029
2031
|
return (self.qname,
|
|
2032
|
+
self.arcrole,
|
|
2030
2033
|
self.linkQname,
|
|
2031
2034
|
self.linkrole, # needed when linkrole=None merges multiple links
|
|
2032
2035
|
self.fromModelObject.objectIndex if isinstance(self.fromModelObject, ModelObject) else -1,
|
|
@@ -2086,6 +2089,11 @@ class ModelRelationship(ModelObject):
|
|
|
2086
2089
|
self.toModelObject.qname if isinstance(self.toModelObject, ModelObject) else "??",
|
|
2087
2090
|
self.modelDocument.basename, self.sourceline))
|
|
2088
2091
|
|
|
2092
|
+
def __dir__(self):
|
|
2093
|
+
# Ignore ModelObject attributes because most relate to the underlying lxml element,
|
|
2094
|
+
# which ModelRelationship does not have.
|
|
2095
|
+
return [a for a in object.__dir__(self) if a.startswith('__') or a not in modelObjectAttrs]
|
|
2096
|
+
|
|
2089
2097
|
@property
|
|
2090
2098
|
def viewConcept(self):
|
|
2091
2099
|
if isinstance(self.toModelObject, ModelConcept):
|
arelle/ModelInstanceObject.py
CHANGED
|
@@ -1159,7 +1159,7 @@ class ModelContext(ModelObject):
|
|
|
1159
1159
|
self._dimsHash = hash( frozenset(self.qnameDims.values()) )
|
|
1160
1160
|
return self._dimsHash
|
|
1161
1161
|
|
|
1162
|
-
def nonDimValues(self, contextElement):
|
|
1162
|
+
def nonDimValues(self, contextElement: str | int) -> list[ModelObject]:
|
|
1163
1163
|
"""([ModelObject]) -- ContextElement is either string or Aspect code for segment or scenario, returns nonXDT ModelObject children of context element.
|
|
1164
1164
|
|
|
1165
1165
|
:param contextElement: one of 'segment', 'scenario', Aspect.NON_XDT_SEGMENT, Aspect.NON_XDT_SCENARIO, Aspect.COMPLETE_SEGMENT, Aspect.COMPLETE_SCENARIO
|
arelle/ModelObject.py
CHANGED
|
@@ -8,7 +8,6 @@ from lxml import etree
|
|
|
8
8
|
from arelle import Locale
|
|
9
9
|
from arelle import ModelValue
|
|
10
10
|
from arelle.XmlValidateConst import VALID_NO_CONTENT
|
|
11
|
-
from arelle.model import CommentBase, ElementBase, PIBase
|
|
12
11
|
|
|
13
12
|
if TYPE_CHECKING:
|
|
14
13
|
from arelle.ModelDocument import ModelDocument
|
|
@@ -21,7 +20,7 @@ if TYPE_CHECKING:
|
|
|
21
20
|
from arelle.ModelInstanceObject import ModelInlineFootnote
|
|
22
21
|
from arelle.ModelInstanceObject import ModelInlineFact
|
|
23
22
|
from arelle.ModelInstanceObject import ModelDimensionValue
|
|
24
|
-
from arelle.ModelValue import
|
|
23
|
+
from arelle.ModelValue import QName, TypeSValue, TypeXValue
|
|
25
24
|
|
|
26
25
|
XmlUtil: Any = None
|
|
27
26
|
|
|
@@ -32,7 +31,7 @@ def init() -> None: # init globals
|
|
|
32
31
|
if XmlUtil is None:
|
|
33
32
|
from arelle import XmlUtil
|
|
34
33
|
|
|
35
|
-
class ModelObject(ElementBase):
|
|
34
|
+
class ModelObject(etree.ElementBase):
|
|
36
35
|
"""ModelObjects represent the XML elements within a document, and are implemented as custom
|
|
37
36
|
lxml proxy objects. Each modelDocument has a parser with the parser objects in ModelObjectFactory.py,
|
|
38
37
|
to determine the type of model object to correspond to a proxied lxml XML element.
|
|
@@ -117,6 +116,7 @@ class ModelObject(ElementBase):
|
|
|
117
116
|
xValueError: Exception | None
|
|
118
117
|
xValid: int
|
|
119
118
|
xlinkLabel: str
|
|
119
|
+
tag: str
|
|
120
120
|
targetModelXbrl: ModelXbrl
|
|
121
121
|
|
|
122
122
|
def _init(self) -> None:
|
|
@@ -125,9 +125,9 @@ class ModelObject(ElementBase):
|
|
|
125
125
|
if parent is not None and hasattr(parent, "modelDocument"):
|
|
126
126
|
self.init(parent.modelDocument)
|
|
127
127
|
|
|
128
|
-
def clear(self) -> None:
|
|
128
|
+
def clear(self, keep_tail: bool = False) -> None:
|
|
129
129
|
self.__dict__.clear() # delete local attributes
|
|
130
|
-
super(
|
|
130
|
+
super().clear(keep_tail) # delete children
|
|
131
131
|
|
|
132
132
|
def init(self, modelDocument: ModelDocument) -> None:
|
|
133
133
|
self.modelDocument = modelDocument
|
|
@@ -160,9 +160,10 @@ class ModelObject(ElementBase):
|
|
|
160
160
|
return emptySet
|
|
161
161
|
|
|
162
162
|
def setNamespaceLocalName(self) -> None:
|
|
163
|
-
|
|
163
|
+
tag = self.tag
|
|
164
|
+
ns, sep, self._localName = tag.rpartition("}")
|
|
164
165
|
if sep:
|
|
165
|
-
self._namespaceURI = ns[1:]
|
|
166
|
+
self._namespaceURI: str | None = ns[1:]
|
|
166
167
|
else:
|
|
167
168
|
self._namespaceURI = None
|
|
168
169
|
if self.prefix:
|
|
@@ -257,7 +258,7 @@ class ModelObject(ElementBase):
|
|
|
257
258
|
return self._parentQname
|
|
258
259
|
except AttributeError:
|
|
259
260
|
parentObj = self.getparent()
|
|
260
|
-
self._parentQname = parentObj.elementQname if parentObj is not None else None
|
|
261
|
+
self._parentQname = parentObj.elementQname if parentObj is not None else None
|
|
261
262
|
return self._parentQname
|
|
262
263
|
|
|
263
264
|
|
|
@@ -267,18 +268,18 @@ class ModelObject(ElementBase):
|
|
|
267
268
|
|
|
268
269
|
@property
|
|
269
270
|
def stringValue(self) -> str: # "string value" of node, text of all Element descendants
|
|
270
|
-
return ''.join(self.
|
|
271
|
+
return ''.join(self.textNodes(recurse=True)) # return text of Element descendants
|
|
271
272
|
|
|
272
273
|
@property
|
|
273
274
|
def textValue(self) -> str: # xml axis text() differs from string value, no descendant element text
|
|
274
|
-
return ''.join(self.
|
|
275
|
+
return ''.join(self.textNodes()) # no text nodes returns ''
|
|
275
276
|
|
|
276
|
-
def
|
|
277
|
+
def textNodes(self, recurse:bool = False) -> Generator[str | Any, None, None]:
|
|
277
278
|
if self.text and getattr(self,"xValid", 0) != VALID_NO_CONTENT: # skip tuple whitespaces
|
|
278
279
|
yield self.text
|
|
279
280
|
for c in self.iterchildren():
|
|
280
281
|
if recurse and isinstance(c, ModelObject):
|
|
281
|
-
for nestedText in c.
|
|
282
|
+
for nestedText in c.textNodes(recurse):
|
|
282
283
|
yield nestedText
|
|
283
284
|
if c.tail and getattr(self,"xValid", 0) != VALID_NO_CONTENT: # skip tuple whitespaces
|
|
284
285
|
yield c.tail # get tail of nested element, comment or processor nodes
|
|
@@ -305,10 +306,7 @@ class ModelObject(ElementBase):
|
|
|
305
306
|
|
|
306
307
|
@property
|
|
307
308
|
def elementAttributesStr(self) -> str:
|
|
308
|
-
|
|
309
|
-
# Mypy raises the following error. Not sure why this is the case, this returns a str not binary data?
|
|
310
|
-
# On Python 3 formatting "b'abc'" with "{}" produces "b'abc'", not "abc"; use "{!r}" if this is desired behavior
|
|
311
|
-
return ', '.join(["{0}='{1}'".format(name, value) for name, value in self.items()]) # type: ignore[str-bytes-safe]
|
|
309
|
+
return ', '.join(["{0}='{1}'".format(name, value) for name, value in self.items()])
|
|
312
310
|
|
|
313
311
|
def resolveUri(
|
|
314
312
|
self,
|
|
@@ -398,7 +396,7 @@ class ModelObject(ElementBase):
|
|
|
398
396
|
def __repr__(self) -> str:
|
|
399
397
|
return ("{0}[{1}, {2} line {3})".format(type(self).__name__, self.objectIndex, self.modelDocument.basename, self.sourceline))
|
|
400
398
|
|
|
401
|
-
class ModelComment(CommentBase):
|
|
399
|
+
class ModelComment(etree.CommentBase):
|
|
402
400
|
"""ModelConcept is a custom proxy objects for etree.
|
|
403
401
|
"""
|
|
404
402
|
def _init(self) -> None:
|
|
@@ -410,7 +408,7 @@ class ModelComment(CommentBase): # type: ignore[misc]
|
|
|
410
408
|
def init(self, modelDocument: ModelDocument) -> None:
|
|
411
409
|
self.modelDocument = modelDocument
|
|
412
410
|
|
|
413
|
-
class ModelProcessingInstruction(PIBase):
|
|
411
|
+
class ModelProcessingInstruction(etree.PIBase):
|
|
414
412
|
"""ModelProcessingInstruction is a custom proxy object for etree.
|
|
415
413
|
"""
|
|
416
414
|
def _init(self) -> None:
|