arelle-release 2.37.46__py3-none-any.whl → 2.37.48__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of arelle-release might be problematic. Click here for more details.
- arelle/CntlrCmdLine.py +10 -1
- arelle/ErrorManager.py +14 -5
- arelle/ModelObjectFactory.py +18 -2
- arelle/Validate.py +4 -0
- arelle/_version.py +2 -2
- arelle/plugin/validate/DBA/ValidationPluginExtension.py +2 -1
- arelle/plugin/validate/EDINET/ControllerPluginData.py +84 -0
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +0 -114
- arelle/plugin/validate/EDINET/UploadContents.py +17 -0
- arelle/plugin/validate/EDINET/ValidationPluginExtension.py +8 -2
- arelle/plugin/validate/EDINET/__init__.py +5 -0
- arelle/plugin/validate/EDINET/rules/upload.py +66 -75
- arelle/plugin/validate/NL/ValidationPluginExtension.py +3 -1
- arelle/plugin/validate/ROS/ValidationPluginExtension.py +3 -1
- arelle/utils/PluginHooks.py +32 -0
- arelle/utils/validate/ValidationPlugin.py +54 -8
- {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/METADATA +1 -1
- {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/RECORD +22 -106
- 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/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.37.48.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/top_level.txt +0 -0
arelle/CntlrCmdLine.py
CHANGED
|
@@ -620,7 +620,11 @@ class ParserForDynamicPlugins:
|
|
|
620
620
|
|
|
621
621
|
|
|
622
622
|
def _pluginHasCliOptions(moduleInfo):
|
|
623
|
-
|
|
623
|
+
if "CntlrCmdLine.Options" in moduleInfo["classMethods"]:
|
|
624
|
+
return True
|
|
625
|
+
if imports := moduleInfo.get("imports"):
|
|
626
|
+
return any(_pluginHasCliOptions(importedModule) for importedModule in imports)
|
|
627
|
+
return False
|
|
624
628
|
|
|
625
629
|
|
|
626
630
|
class CntlrCmdLine(Cntlr.Cntlr):
|
|
@@ -1012,6 +1016,11 @@ class CntlrCmdLine(Cntlr.Cntlr):
|
|
|
1012
1016
|
|
|
1013
1017
|
for pluginXbrlMethod in PluginManager.pluginClassMethods("CntlrCmdLine.Filing.Start"):
|
|
1014
1018
|
pluginXbrlMethod(self, options, filesource, _entrypointFiles, sourceZipStream=sourceZipStream, responseZipStream=responseZipStream)
|
|
1019
|
+
|
|
1020
|
+
if options.validate:
|
|
1021
|
+
for pluginXbrlMethod in PluginManager.pluginClassMethods("Validate.FileSource"):
|
|
1022
|
+
pluginXbrlMethod(self, filesource, _entrypointFiles)
|
|
1023
|
+
|
|
1015
1024
|
if len(_entrypointFiles) == 0 and not options.packages:
|
|
1016
1025
|
if options.entrypointFile:
|
|
1017
1026
|
msg = _("No XBRL entry points could be loaded from provided file: {}").format(options.entrypointFile)
|
arelle/ErrorManager.py
CHANGED
|
@@ -204,13 +204,22 @@ class ErrorManager:
|
|
|
204
204
|
_arg:ModelObject = arg.modelObject if isinstance(arg, ObjectPropertyViewWrapper) else arg
|
|
205
205
|
if len(modelObjectArgs) > 1 and getattr(arg,"tag",None) == "instance":
|
|
206
206
|
continue # skip IXDS top level element
|
|
207
|
-
fragmentIdentifier = cast(str, XmlUtil.elementFragmentIdentifier(_arg))
|
|
208
|
-
if not hasattr(_arg, 'modelDocument') and _arg.namespaceURI == XbrlConst.svg:
|
|
207
|
+
fragmentIdentifier = "#" + cast(str, XmlUtil.elementFragmentIdentifier(_arg))
|
|
208
|
+
if not hasattr(_arg, 'modelDocument') and _arg.namespaceURI == XbrlConst.svg and len(refs) > 0:
|
|
209
209
|
# This is an embedded SVG document without its own file.
|
|
210
|
-
|
|
210
|
+
# Set the href to the containing document element that defined the encoded SVG.
|
|
211
|
+
# and define a nestedHrefs attribute with the fragment identifier.
|
|
212
|
+
priorRef = refs[-1]
|
|
213
|
+
ref["href"] = priorRef["href"]
|
|
214
|
+
priorNestedHrefs = priorRef.get("customAttributes", {}).get("nestedHrefs", [])
|
|
215
|
+
ref["customAttributes"] = {
|
|
216
|
+
"nestedHrefs": [*priorNestedHrefs, fragmentIdentifier]
|
|
217
|
+
}
|
|
218
|
+
if priorArgSourceline := priorRef.get("sourceLine"):
|
|
219
|
+
ref["sourceLine"] = priorArgSourceline
|
|
211
220
|
else:
|
|
212
|
-
ref["href"] = file +
|
|
213
|
-
|
|
221
|
+
ref["href"] = file + fragmentIdentifier
|
|
222
|
+
ref["sourceLine"] = _arg.sourceline
|
|
214
223
|
ref["objectId"] = _arg.objectId()
|
|
215
224
|
if logRefObjectProperties:
|
|
216
225
|
try:
|
arelle/ModelObjectFactory.py
CHANGED
|
@@ -68,6 +68,22 @@ LINKBASE = 2
|
|
|
68
68
|
VERSIONINGREPORT = 3
|
|
69
69
|
RSSFEED = 4
|
|
70
70
|
|
|
71
|
+
LINK_LOCALNAME_TO_MODEL_CLASS = {
|
|
72
|
+
'loc': ModelLocator,
|
|
73
|
+
'label': ModelResource,
|
|
74
|
+
'reference': ModelResource,
|
|
75
|
+
'roleType': ModelRoleType,
|
|
76
|
+
'arcroleType': ModelRoleType,
|
|
77
|
+
} | {
|
|
78
|
+
q.localName: ModelObject
|
|
79
|
+
for q in [
|
|
80
|
+
XbrlConst.qnLinkCalculationArc,
|
|
81
|
+
XbrlConst.qnLinkDefinitionArc,
|
|
82
|
+
XbrlConst.qnLinkPresentationArc,
|
|
83
|
+
XbrlConst.qnLinkReferenceArc,
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
|
|
71
87
|
|
|
72
88
|
class KnownNamespacesModelObjectClassLookup(etree.CustomElementClassLookup):
|
|
73
89
|
def __init__(self, modelXbrl: ModelXbrl, fallback: etree.ElementClassLookup | None = None) -> None:
|
|
@@ -106,8 +122,8 @@ class KnownNamespacesModelObjectClassLookup(etree.CustomElementClassLookup):
|
|
|
106
122
|
elif ns == XbrlConst.link:
|
|
107
123
|
if self.type is None:
|
|
108
124
|
self.type = LINKBASE
|
|
109
|
-
if
|
|
110
|
-
return
|
|
125
|
+
if modelObjectClass := LINK_LOCALNAME_TO_MODEL_CLASS.get(ln):
|
|
126
|
+
return modelObjectClass
|
|
111
127
|
elif ns == "http://edgar/2009/conformance":
|
|
112
128
|
# don't force loading of test schema
|
|
113
129
|
if ln == "variation":
|
arelle/Validate.py
CHANGED
|
@@ -398,6 +398,8 @@ class Validate:
|
|
|
398
398
|
filesource.select(None) # must select loadable reports (not the taxonomy package itself)
|
|
399
399
|
elif not filesource.isReportPackage:
|
|
400
400
|
entrypoints = filesourceEntrypointFiles(filesource)
|
|
401
|
+
for pluginXbrlMethod in pluginClassMethods("Validate.FileSource"):
|
|
402
|
+
pluginXbrlMethod(self.modelXbrl.modelManager.cntlr, filesource, entrypoints)
|
|
401
403
|
if entrypoints:
|
|
402
404
|
# resolve an IXDS in entrypoints
|
|
403
405
|
for pluginXbrlMethod in pluginClassMethods("ModelTestcaseVariation.ArchiveIxds"):
|
|
@@ -421,6 +423,8 @@ class Validate:
|
|
|
421
423
|
if not reportPackageErrors:
|
|
422
424
|
assert isinstance(filesource.basefile, str)
|
|
423
425
|
if entrypoints := filesourceEntrypointFiles(filesource):
|
|
426
|
+
for pluginXbrlMethod in pluginClassMethods("Validate.FileSource"):
|
|
427
|
+
pluginXbrlMethod(self.modelXbrl.modelManager.cntlr, filesource, entrypoints)
|
|
424
428
|
for pluginXbrlMethod in pluginClassMethods("ModelTestcaseVariation.ArchiveIxds"):
|
|
425
429
|
pluginXbrlMethod(self, filesource, entrypoints)
|
|
426
430
|
for entrypoint in entrypoints:
|
arelle/_version.py
CHANGED
|
@@ -3,6 +3,7 @@ See COPYRIGHT.md for copyright information.
|
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
+
from arelle.Cntlr import Cntlr
|
|
6
7
|
from arelle.ModelValue import qname
|
|
7
8
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
8
9
|
from arelle.typing import TypeGetText
|
|
@@ -26,7 +27,7 @@ REQUIRED_DISCLOSURE_OF_EQUITY_FACTS = 2
|
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
class ValidationPluginExtension(ValidationPlugin):
|
|
29
|
-
def newPluginData(self, validateXbrl: ValidateXbrl) -> PluginValidationDataExtension:
|
|
30
|
+
def newPluginData(self, cntlr: Cntlr, validateXbrl: ValidateXbrl | None) -> PluginValidationDataExtension:
|
|
30
31
|
return PluginValidationDataExtension(
|
|
31
32
|
self.name,
|
|
32
33
|
accountingPolicyConceptQns=frozenset([
|
|
@@ -3,12 +3,20 @@ See COPYRIGHT.md for copyright information.
|
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
+
import zipfile
|
|
7
|
+
from collections import defaultdict
|
|
6
8
|
from dataclasses import dataclass
|
|
9
|
+
from functools import lru_cache
|
|
10
|
+
from pathlib import Path
|
|
7
11
|
from typing import TYPE_CHECKING
|
|
8
12
|
|
|
9
13
|
from arelle.Cntlr import Cntlr
|
|
14
|
+
from arelle.FileSource import FileSource
|
|
15
|
+
from arelle.ModelXbrl import ModelXbrl
|
|
10
16
|
from arelle.typing import TypeGetText
|
|
11
17
|
from arelle.utils.PluginData import PluginData
|
|
18
|
+
from .InstanceType import InstanceType
|
|
19
|
+
from .UploadContents import UploadContents
|
|
12
20
|
|
|
13
21
|
if TYPE_CHECKING:
|
|
14
22
|
from .ManifestInstance import ManifestInstance
|
|
@@ -39,6 +47,82 @@ class ControllerPluginData(PluginData):
|
|
|
39
47
|
"""
|
|
40
48
|
return list(self._manifestInstancesById.values())
|
|
41
49
|
|
|
50
|
+
@lru_cache(1)
|
|
51
|
+
def getUploadContents(self, fileSource: FileSource) -> UploadContents:
|
|
52
|
+
uploadFilepaths = self.getUploadFilepaths(fileSource)
|
|
53
|
+
amendmentPaths = defaultdict(list)
|
|
54
|
+
unknownPaths = []
|
|
55
|
+
directories = []
|
|
56
|
+
forms = defaultdict(list)
|
|
57
|
+
for path in uploadFilepaths:
|
|
58
|
+
parents = list(reversed([p.name for p in path.parents if len(p.name) > 0]))
|
|
59
|
+
if len(parents) == 0:
|
|
60
|
+
continue
|
|
61
|
+
if parents[0] == 'XBRL':
|
|
62
|
+
if len(parents) > 1:
|
|
63
|
+
formName = parents[1]
|
|
64
|
+
instanceType = InstanceType.parse(formName)
|
|
65
|
+
if instanceType is not None:
|
|
66
|
+
forms[instanceType].append(path)
|
|
67
|
+
continue
|
|
68
|
+
formName = parents[0]
|
|
69
|
+
instanceType = InstanceType.parse(formName)
|
|
70
|
+
if instanceType is not None:
|
|
71
|
+
amendmentPaths[instanceType].append(path)
|
|
72
|
+
continue
|
|
73
|
+
if len(path.suffix) == 0:
|
|
74
|
+
directories.append(path)
|
|
75
|
+
continue
|
|
76
|
+
unknownPaths.append(path)
|
|
77
|
+
return UploadContents(
|
|
78
|
+
amendmentPaths={k: frozenset(v) for k, v in amendmentPaths.items() if len(v) > 0},
|
|
79
|
+
directories=frozenset(directories),
|
|
80
|
+
instances={k: frozenset(v) for k, v in forms.items() if len(v) > 0},
|
|
81
|
+
unknownPaths=frozenset(unknownPaths)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
@lru_cache(1)
|
|
85
|
+
def getUploadFilepaths(self, fileSource: FileSource) -> list[Path]:
|
|
86
|
+
if not self.isUpload(fileSource):
|
|
87
|
+
return []
|
|
88
|
+
paths = set()
|
|
89
|
+
assert isinstance(fileSource.fs, zipfile.ZipFile)
|
|
90
|
+
for name in fileSource.fs.namelist():
|
|
91
|
+
path = Path(name)
|
|
92
|
+
paths.add(path)
|
|
93
|
+
paths.update(path.parents)
|
|
94
|
+
return sorted(paths)
|
|
95
|
+
|
|
96
|
+
@lru_cache(1)
|
|
97
|
+
def getUploadFileSizes(self, fileSource: FileSource) -> dict[Path, int]:
|
|
98
|
+
"""
|
|
99
|
+
Get the sizes of files in the upload directory.
|
|
100
|
+
:param fileSource: The FileSource instance to get file sizes for.
|
|
101
|
+
:return: A dictionary mapping file paths to their sizes.
|
|
102
|
+
"""
|
|
103
|
+
if not self.isUpload(fileSource):
|
|
104
|
+
return {}
|
|
105
|
+
assert isinstance(fileSource.fs, zipfile.ZipFile)
|
|
106
|
+
return {
|
|
107
|
+
Path(i.filename): i.file_size
|
|
108
|
+
for i in fileSource.fs.infolist()
|
|
109
|
+
if not i.is_dir()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@lru_cache(1)
|
|
113
|
+
def isUpload(self, fileSource: FileSource) -> bool:
|
|
114
|
+
fileSource.open() # Make sure file source is open
|
|
115
|
+
if (fileSource.fs is None or
|
|
116
|
+
not isinstance(fileSource.fs, zipfile.ZipFile)):
|
|
117
|
+
if fileSource.cntlr is not None:
|
|
118
|
+
fileSource.cntlr.error(
|
|
119
|
+
level="WARNING",
|
|
120
|
+
codes="EDINET.uploadNotValidated",
|
|
121
|
+
msg=_("The target file is not a zip file, so upload validation could not be performed.")
|
|
122
|
+
)
|
|
123
|
+
return False
|
|
124
|
+
return True
|
|
125
|
+
|
|
42
126
|
def matchManifestInstance(self, ixdsDocUrls: list[str]) -> ManifestInstance | None:
|
|
43
127
|
"""
|
|
44
128
|
Match a manifest instance based on the provided ixdsDocUrls.
|
|
@@ -4,7 +4,6 @@ See COPYRIGHT.md for copyright information.
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
6
|
import zipfile
|
|
7
|
-
from collections import defaultdict
|
|
8
7
|
from dataclasses import dataclass
|
|
9
8
|
from functools import lru_cache
|
|
10
9
|
from pathlib import Path
|
|
@@ -18,26 +17,16 @@ from arelle.ModelValue import QName, qname
|
|
|
18
17
|
from arelle.ModelXbrl import ModelXbrl
|
|
19
18
|
from arelle.PrototypeDtsObject import LinkPrototype
|
|
20
19
|
from arelle.ValidateDuplicateFacts import getDeduplicatedFacts, DeduplicationType
|
|
21
|
-
from arelle.ValidateXbrl import ValidateXbrl
|
|
22
20
|
from arelle.XmlValidate import VALID
|
|
23
21
|
from arelle.typing import TypeGetText
|
|
24
22
|
from arelle.utils.PluginData import PluginData
|
|
25
23
|
from .Constants import CORPORATE_FORMS
|
|
26
24
|
from .ControllerPluginData import ControllerPluginData
|
|
27
|
-
from .InstanceType import InstanceType
|
|
28
25
|
from .ManifestInstance import ManifestInstance
|
|
29
26
|
|
|
30
27
|
_: TypeGetText
|
|
31
28
|
|
|
32
29
|
|
|
33
|
-
@dataclass(frozen=True)
|
|
34
|
-
class UploadContents:
|
|
35
|
-
amendmentPaths: dict[InstanceType, frozenset[Path]]
|
|
36
|
-
directories: frozenset[Path]
|
|
37
|
-
instances: dict[InstanceType, frozenset[Path]]
|
|
38
|
-
unknownPaths: frozenset[Path]
|
|
39
|
-
|
|
40
|
-
|
|
41
30
|
@dataclass
|
|
42
31
|
class PluginValidationDataExtension(PluginData):
|
|
43
32
|
assetsIfrsQn: QName
|
|
@@ -83,32 +72,6 @@ class PluginValidationDataExtension(PluginData):
|
|
|
83
72
|
return True
|
|
84
73
|
return False
|
|
85
74
|
|
|
86
|
-
@lru_cache(1)
|
|
87
|
-
def shouldValidateUpload(self, val: ValidateXbrl) -> bool:
|
|
88
|
-
"""
|
|
89
|
-
Determine if the upload validation should be performed on this model.
|
|
90
|
-
|
|
91
|
-
Upload validation should not be performed if the target document is
|
|
92
|
-
not a zipfile.
|
|
93
|
-
|
|
94
|
-
Upload validation should only be performed once for the entire package,
|
|
95
|
-
not duplicated for each model. To facilitate this with Arelle's validation
|
|
96
|
-
system which largely prevents referencing other models, we can use `--keepOpen`
|
|
97
|
-
and check if the given model is the first to be loaded.
|
|
98
|
-
:param val: The ValidateXbrl instance with a model to check.
|
|
99
|
-
:return: True if upload validation should be performed, False otherwise.
|
|
100
|
-
"""
|
|
101
|
-
modelXbrl = val.modelXbrl
|
|
102
|
-
if modelXbrl == val.testModelXbrl:
|
|
103
|
-
# Not running within a testcase
|
|
104
|
-
if modelXbrl != modelXbrl.modelManager.loadedModelXbrls[0]:
|
|
105
|
-
return False
|
|
106
|
-
if not modelXbrl.fileSource.fs:
|
|
107
|
-
return False # No stream
|
|
108
|
-
if not isinstance(modelXbrl.fileSource.fs, zipfile.ZipFile):
|
|
109
|
-
return False # Not a zipfile
|
|
110
|
-
return True
|
|
111
|
-
|
|
112
75
|
@lru_cache(1)
|
|
113
76
|
def getDeduplicatedFacts(self, modelXbrl: ModelXbrl) -> list[ModelFact]:
|
|
114
77
|
return getDeduplicatedFacts(modelXbrl, DeduplicationType.CONSISTENT_PAIRS)
|
|
@@ -149,86 +112,9 @@ class PluginValidationDataExtension(PluginData):
|
|
|
149
112
|
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
150
113
|
return controllerPluginData.matchManifestInstance(modelXbrl.ixdsDocUrls)
|
|
151
114
|
|
|
152
|
-
@lru_cache(1)
|
|
153
|
-
def getManifestInstances(self, modelXbrl: ModelXbrl) -> list[ManifestInstance]:
|
|
154
|
-
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
155
|
-
return controllerPluginData.getManifestInstances()
|
|
156
|
-
|
|
157
|
-
def getUploadFileSizes(self, modelXbrl: ModelXbrl) -> dict[Path, int]:
|
|
158
|
-
"""
|
|
159
|
-
Get the sizes of files in the upload directory.
|
|
160
|
-
:param modelXbrl: The ModelXbrl instance to get file sizes for.
|
|
161
|
-
:return: A dictionary mapping file paths to their sizes.
|
|
162
|
-
"""
|
|
163
|
-
if not self.isUpload(modelXbrl):
|
|
164
|
-
return {}
|
|
165
|
-
assert isinstance(modelXbrl.fileSource.fs, zipfile.ZipFile)
|
|
166
|
-
return {
|
|
167
|
-
Path(i.filename): i.file_size
|
|
168
|
-
for i in modelXbrl.fileSource.fs.infolist()
|
|
169
|
-
if not i.is_dir()
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
@lru_cache(1)
|
|
173
|
-
def getUploadContents(self, modelXbrl: ModelXbrl) -> UploadContents:
|
|
174
|
-
uploadFilepaths = self.getUploadFilepaths(modelXbrl)
|
|
175
|
-
amendmentPaths = defaultdict(list)
|
|
176
|
-
unknownPaths = []
|
|
177
|
-
directories = []
|
|
178
|
-
forms = defaultdict(list)
|
|
179
|
-
for path in uploadFilepaths:
|
|
180
|
-
parents = list(reversed([p.name for p in path.parents if len(p.name) > 0]))
|
|
181
|
-
if len(parents) == 0:
|
|
182
|
-
continue
|
|
183
|
-
if parents[0] == 'XBRL':
|
|
184
|
-
if len(parents) > 1:
|
|
185
|
-
formName = parents[1]
|
|
186
|
-
instanceType = InstanceType.parse(formName)
|
|
187
|
-
if instanceType is not None:
|
|
188
|
-
forms[instanceType].append(path)
|
|
189
|
-
continue
|
|
190
|
-
formName = parents[0]
|
|
191
|
-
instanceType = InstanceType.parse(formName)
|
|
192
|
-
if instanceType is not None:
|
|
193
|
-
amendmentPaths[instanceType].append(path)
|
|
194
|
-
continue
|
|
195
|
-
if len(path.suffix) == 0:
|
|
196
|
-
directories.append(path)
|
|
197
|
-
continue
|
|
198
|
-
unknownPaths.append(path)
|
|
199
|
-
return UploadContents(
|
|
200
|
-
amendmentPaths={k: frozenset(v) for k, v in amendmentPaths.items() if len(v) > 0},
|
|
201
|
-
directories=frozenset(directories),
|
|
202
|
-
instances={k: frozenset(v) for k, v in forms.items() if len(v) > 0},
|
|
203
|
-
unknownPaths=frozenset(unknownPaths)
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
@lru_cache(1)
|
|
207
|
-
def getUploadFilepaths(self, modelXbrl: ModelXbrl) -> list[Path]:
|
|
208
|
-
if not self.isUpload(modelXbrl):
|
|
209
|
-
return []
|
|
210
|
-
paths = set()
|
|
211
|
-
assert isinstance(modelXbrl.fileSource.fs, zipfile.ZipFile)
|
|
212
|
-
for name in modelXbrl.fileSource.fs.namelist():
|
|
213
|
-
path = Path(name)
|
|
214
|
-
paths.add(path)
|
|
215
|
-
paths.update(path.parents)
|
|
216
|
-
return sorted(paths)
|
|
217
|
-
|
|
218
115
|
def hasValidNonNilFact(self, modelXbrl: ModelXbrl, qname: QName) -> bool:
|
|
219
116
|
requiredFacts = modelXbrl.factsByQname.get(qname, set())
|
|
220
117
|
return any(fact.xValid >= VALID and not fact.isNil for fact in requiredFacts)
|
|
221
118
|
|
|
222
|
-
@lru_cache(1)
|
|
223
|
-
def isUpload(self, modelXbrl: ModelXbrl) -> bool:
|
|
224
|
-
if not modelXbrl.fileSource.fs or \
|
|
225
|
-
not isinstance(modelXbrl.fileSource.fs, zipfile.ZipFile):
|
|
226
|
-
modelXbrl.warning(
|
|
227
|
-
codes="EDINET.uploadNotValidated",
|
|
228
|
-
msg=_("The target file is not a zip file, so upload validation could not be performed.")
|
|
229
|
-
)
|
|
230
|
-
return False
|
|
231
|
-
return True
|
|
232
|
-
|
|
233
119
|
def isStandardTaxonomyUrl(self, uri: str, modelXbrl: ModelXbrl) -> bool:
|
|
234
120
|
return modelXbrl.modelManager.disclosureSystem.hrefValidForDisclosureSystem(uri)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
See COPYRIGHT.md for copyright information.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from .InstanceType import InstanceType
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class UploadContents:
|
|
14
|
+
amendmentPaths: dict[InstanceType, frozenset[Path]]
|
|
15
|
+
directories: frozenset[Path]
|
|
16
|
+
instances: dict[InstanceType, frozenset[Path]]
|
|
17
|
+
unknownPaths: frozenset[Path]
|
|
@@ -5,9 +5,11 @@ from __future__ import annotations
|
|
|
5
5
|
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
+
from arelle.Cntlr import Cntlr
|
|
8
9
|
from arelle.FileSource import FileSource
|
|
9
10
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
10
11
|
from arelle.typing import TypeGetText
|
|
12
|
+
from arelle.utils.PluginData import PluginData
|
|
11
13
|
from arelle.utils.validate.ValidationPlugin import ValidationPlugin
|
|
12
14
|
from .ControllerPluginData import ControllerPluginData
|
|
13
15
|
from .DisclosureSystems import DISCLOSURE_SYSTEM_EDINET
|
|
@@ -35,8 +37,12 @@ class ValidationPluginExtension(ValidationPlugin):
|
|
|
35
37
|
entrypointFiles.append({'ixds': entrypoints, 'id': instance.id})
|
|
36
38
|
return entrypointFiles
|
|
37
39
|
|
|
38
|
-
def newPluginData(self, validateXbrl: ValidateXbrl) ->
|
|
39
|
-
|
|
40
|
+
def newPluginData(self, cntlr: Cntlr, validateXbrl: ValidateXbrl | None) -> PluginData:
|
|
41
|
+
if validateXbrl is None:
|
|
42
|
+
return ControllerPluginData.get(cntlr, self.name)
|
|
43
|
+
disclosureSystem = DISCLOSURE_SYSTEM_EDINET
|
|
44
|
+
if validateXbrl is not None:
|
|
45
|
+
disclosureSystem = str(validateXbrl.disclosureSystem.name)
|
|
40
46
|
if disclosureSystem == DISCLOSURE_SYSTEM_EDINET:
|
|
41
47
|
pass
|
|
42
48
|
else:
|
|
@@ -59,6 +59,10 @@ def loggingSeverityReleveler(modelXbrl: ModelXbrl, level: str, messageCode: str,
|
|
|
59
59
|
return level, messageCode
|
|
60
60
|
|
|
61
61
|
|
|
62
|
+
def validateFileSource(*args: Any, **kwargs: Any) -> None:
|
|
63
|
+
return validationPlugin.validateFileSource(*args, **kwargs)
|
|
64
|
+
|
|
65
|
+
|
|
62
66
|
def validateFinally(*args: Any, **kwargs: Any) -> None:
|
|
63
67
|
return validationPlugin.validateFinally(*args, **kwargs)
|
|
64
68
|
|
|
@@ -79,6 +83,7 @@ __pluginInfo__ = {
|
|
|
79
83
|
"DisclosureSystem.ConfigURL": disclosureSystemConfigURL,
|
|
80
84
|
"FileSource.EntrypointFiles": fileSourceEntrypointFiles,
|
|
81
85
|
"Logging.Severity.Releveler": loggingSeverityReleveler,
|
|
86
|
+
"Validate.FileSource": validateFileSource,
|
|
82
87
|
"Validate.XBRL.Finally": validateXbrlFinally,
|
|
83
88
|
"ValidateFormula.Finished": validateFinally,
|
|
84
89
|
}
|