arelle-release 2.37.45__py3-none-any.whl → 2.37.47__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 +5 -0
- arelle/ErrorManager.py +21 -12
- arelle/PluginManager.py +4 -0
- arelle/Validate.py +4 -0
- arelle/_version.py +2 -2
- arelle/examples/plugin/validate/XYZ/ValidationPluginExtension.py +2 -1
- 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.45.dist-info → arelle_release-2.37.47.dist-info}/METADATA +1 -1
- {arelle_release-2.37.45.dist-info → arelle_release-2.37.47.dist-info}/RECORD +23 -22
- {arelle_release-2.37.45.dist-info → arelle_release-2.37.47.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.45.dist-info → arelle_release-2.37.47.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.45.dist-info → arelle_release-2.37.47.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.37.45.dist-info → arelle_release-2.37.47.dist-info}/top_level.txt +0 -0
arelle/CntlrCmdLine.py
CHANGED
|
@@ -1012,6 +1012,11 @@ class CntlrCmdLine(Cntlr.Cntlr):
|
|
|
1012
1012
|
|
|
1013
1013
|
for pluginXbrlMethod in PluginManager.pluginClassMethods("CntlrCmdLine.Filing.Start"):
|
|
1014
1014
|
pluginXbrlMethod(self, options, filesource, _entrypointFiles, sourceZipStream=sourceZipStream, responseZipStream=responseZipStream)
|
|
1015
|
+
|
|
1016
|
+
if options.validate:
|
|
1017
|
+
for pluginXbrlMethod in PluginManager.pluginClassMethods("Validate.FileSource"):
|
|
1018
|
+
pluginXbrlMethod(self, filesource, _entrypointFiles)
|
|
1019
|
+
|
|
1015
1020
|
if len(_entrypointFiles) == 0 and not options.packages:
|
|
1016
1021
|
if options.entrypointFile:
|
|
1017
1022
|
msg = _("No XBRL entry points could be loaded from provided file: {}").format(options.entrypointFile)
|
arelle/ErrorManager.py
CHANGED
|
@@ -13,7 +13,7 @@ from arelle import UrlUtil, XmlUtil, ModelValue, XbrlConst
|
|
|
13
13
|
from arelle.FileSource import FileSource
|
|
14
14
|
from arelle.Locale import format_string
|
|
15
15
|
from arelle.ModelObject import ModelObject, ObjectPropertyViewWrapper
|
|
16
|
-
from arelle.PluginManager import pluginClassMethods
|
|
16
|
+
from arelle.PluginManager import hasPluginWithHook, pluginClassMethods
|
|
17
17
|
from arelle.PythonUtil import flattenSequence
|
|
18
18
|
|
|
19
19
|
if TYPE_CHECKING:
|
|
@@ -26,10 +26,10 @@ EMPTY_TUPLE: EmptyTuple = ()
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class ErrorManager:
|
|
29
|
+
logHasRelevelerPlugin: bool | None
|
|
29
30
|
_errorCaptureLevel: int
|
|
30
31
|
_errors: list[str | None]
|
|
31
32
|
_logCount: dict[str, int] = {}
|
|
32
|
-
_logHasRelevelerPlugin: bool
|
|
33
33
|
_logRefFileRelUris: defaultdict[Any, dict[str, str]]
|
|
34
34
|
_modelManager: ModelManager
|
|
35
35
|
|
|
@@ -39,6 +39,7 @@ class ErrorManager:
|
|
|
39
39
|
self._logCount = {}
|
|
40
40
|
self._logRefFileRelUris = defaultdict(dict)
|
|
41
41
|
self._modelManager = modelManager
|
|
42
|
+
self.logHasRelevelerPlugin = None
|
|
42
43
|
|
|
43
44
|
@property
|
|
44
45
|
def errors(self) -> list[str | None]:
|
|
@@ -111,7 +112,9 @@ class ErrorManager:
|
|
|
111
112
|
if messageCode == "asrtNoLog":
|
|
112
113
|
self._errors.append(args["assertionResults"])
|
|
113
114
|
return
|
|
114
|
-
if
|
|
115
|
+
if self.logHasRelevelerPlugin is None:
|
|
116
|
+
self.logHasRelevelerPlugin = hasPluginWithHook("Logging.Severity.Releveler")
|
|
117
|
+
if sourceModelXbrl is not None and self.logHasRelevelerPlugin:
|
|
115
118
|
for pluginXbrlMethod in pluginClassMethods("Logging.Severity.Releveler"):
|
|
116
119
|
level, messageCode = pluginXbrlMethod(sourceModelXbrl, level, messageCode, args) # args must be passed as dict because it may contain modelXbrl or messageCode key value
|
|
117
120
|
if (messageCode and
|
|
@@ -157,10 +160,7 @@ class ErrorManager:
|
|
|
157
160
|
fmtArgs: dict[str, LoggableValue] = {}
|
|
158
161
|
extras: dict[str, Any] = {"messageCode":messageCode}
|
|
159
162
|
modelObjectArgs: tuple[Any, ...] | list[Any] = ()
|
|
160
|
-
sourceModelDocument = None
|
|
161
|
-
if sourceModelXbrl is not None:
|
|
162
|
-
sourceModelDocument = sourceModelXbrl.modelDocument
|
|
163
|
-
|
|
163
|
+
sourceModelDocument = getattr(sourceModelXbrl, "modelDocument", None)
|
|
164
164
|
for argName, argValue in codedArgs.items():
|
|
165
165
|
if argName in ("modelObject", "modelXbrl", "modelDocument"):
|
|
166
166
|
if sourceModelDocument is not None:
|
|
@@ -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/PluginManager.py
CHANGED
|
@@ -602,6 +602,10 @@ def loadModule(moduleInfo: dict[str, Any], packagePrefix: str="") -> None:
|
|
|
602
602
|
logPluginTrace(_msg, logging.ERROR)
|
|
603
603
|
|
|
604
604
|
|
|
605
|
+
def hasPluginWithHook(name: str) -> bool:
|
|
606
|
+
return next(pluginClassMethods(name), None) is not None
|
|
607
|
+
|
|
608
|
+
|
|
605
609
|
def pluginClassMethods(className: str) -> Iterator[Callable[..., Any]]:
|
|
606
610
|
if pluginConfig:
|
|
607
611
|
try:
|
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
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
+
from arelle.Cntlr import Cntlr
|
|
8
9
|
from arelle.ModelDocument import LoadingException, ModelDocument
|
|
9
10
|
from arelle.ModelXbrl import ModelXbrl
|
|
10
11
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
@@ -16,7 +17,7 @@ _: TypeGetText
|
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class ValidationPluginExtension(ValidationPlugin):
|
|
19
|
-
def newPluginData(self, validateXbrl: ValidateXbrl) -> PluginValidationDataExtension:
|
|
20
|
+
def newPluginData(self, cntlr: Cntlr, validateXbrl: ValidateXbrl | None) -> PluginValidationDataExtension:
|
|
20
21
|
return PluginValidationDataExtension(self.name)
|
|
21
22
|
|
|
22
23
|
def modelDocumentPullLoader(
|
|
@@ -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
|
}
|
|
@@ -6,8 +6,10 @@ from __future__ import annotations
|
|
|
6
6
|
import re
|
|
7
7
|
from collections import defaultdict
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import Any, Iterable
|
|
9
|
+
from typing import Any, Iterable, TYPE_CHECKING
|
|
10
10
|
|
|
11
|
+
from arelle.Cntlr import Cntlr
|
|
12
|
+
from arelle.FileSource import FileSource
|
|
11
13
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
12
14
|
from arelle.typing import TypeGetText
|
|
13
15
|
from arelle.utils.PluginHooks import ValidationHook
|
|
@@ -17,6 +19,9 @@ from ..DisclosureSystems import (DISCLOSURE_SYSTEM_EDINET)
|
|
|
17
19
|
from ..InstanceType import InstanceType, HTML_EXTENSIONS, IMAGE_EXTENSIONS
|
|
18
20
|
from ..PluginValidationDataExtension import PluginValidationDataExtension
|
|
19
21
|
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from ..ControllerPluginData import ControllerPluginData
|
|
24
|
+
|
|
20
25
|
_: TypeGetText
|
|
21
26
|
|
|
22
27
|
FILE_COUNT_LIMITS = {
|
|
@@ -35,12 +40,13 @@ FILENAME_STEM_PATTERN = re.compile(r'[a-zA-Z0-9_-]*')
|
|
|
35
40
|
|
|
36
41
|
|
|
37
42
|
@validation(
|
|
38
|
-
hook=ValidationHook.
|
|
43
|
+
hook=ValidationHook.FILESOURCE,
|
|
39
44
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
40
45
|
)
|
|
41
46
|
def rule_EC0121E(
|
|
42
|
-
pluginData:
|
|
43
|
-
|
|
47
|
+
pluginData: ControllerPluginData,
|
|
48
|
+
cntlr: Cntlr,
|
|
49
|
+
fileSource: FileSource,
|
|
44
50
|
*args: Any,
|
|
45
51
|
**kwargs: Any,
|
|
46
52
|
) -> Iterable[Validation]:
|
|
@@ -53,9 +59,7 @@ def rule_EC0121E(
|
|
|
53
59
|
i.e. amendment documents. For now, we will only check amendment documents, directory
|
|
54
60
|
names, or other files in unexpected locations.
|
|
55
61
|
"""
|
|
56
|
-
|
|
57
|
-
return
|
|
58
|
-
uploadContents = pluginData.getUploadContents(val.modelXbrl)
|
|
62
|
+
uploadContents = pluginData.getUploadContents(fileSource)
|
|
59
63
|
paths = set(uploadContents.directories | uploadContents.unknownPaths)
|
|
60
64
|
for amendmentPaths in uploadContents.amendmentPaths.values():
|
|
61
65
|
paths.update(amendmentPaths)
|
|
@@ -75,21 +79,20 @@ def rule_EC0121E(
|
|
|
75
79
|
|
|
76
80
|
|
|
77
81
|
@validation(
|
|
78
|
-
hook=ValidationHook.
|
|
82
|
+
hook=ValidationHook.FILESOURCE,
|
|
79
83
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
80
84
|
)
|
|
81
85
|
def rule_EC0124E(
|
|
82
|
-
pluginData:
|
|
83
|
-
|
|
86
|
+
pluginData: ControllerPluginData,
|
|
87
|
+
cntlr: Cntlr,
|
|
88
|
+
fileSource: FileSource,
|
|
84
89
|
*args: Any,
|
|
85
90
|
**kwargs: Any,
|
|
86
91
|
) -> Iterable[Validation]:
|
|
87
92
|
"""
|
|
88
93
|
EDINET.EC0124E: There are no empty directories.
|
|
89
94
|
"""
|
|
90
|
-
|
|
91
|
-
return
|
|
92
|
-
uploadFilepaths = pluginData.getUploadFilepaths(val.modelXbrl)
|
|
95
|
+
uploadFilepaths = pluginData.getUploadFilepaths(fileSource)
|
|
93
96
|
emptyDirectories = []
|
|
94
97
|
for path in uploadFilepaths:
|
|
95
98
|
if path.suffix:
|
|
@@ -107,12 +110,13 @@ def rule_EC0124E(
|
|
|
107
110
|
|
|
108
111
|
|
|
109
112
|
@validation(
|
|
110
|
-
hook=ValidationHook.
|
|
113
|
+
hook=ValidationHook.FILESOURCE,
|
|
111
114
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
112
115
|
)
|
|
113
116
|
def rule_EC0129E(
|
|
114
|
-
pluginData:
|
|
115
|
-
|
|
117
|
+
pluginData: ControllerPluginData,
|
|
118
|
+
cntlr: Cntlr,
|
|
119
|
+
fileSource: FileSource,
|
|
116
120
|
*args: Any,
|
|
117
121
|
**kwargs: Any,
|
|
118
122
|
) -> Iterable[Validation]:
|
|
@@ -120,9 +124,7 @@ def rule_EC0129E(
|
|
|
120
124
|
EDINET.EC0129E: Limit the number of subfolders to 3 or less from the XBRL directory.
|
|
121
125
|
"""
|
|
122
126
|
startingDirectory = 'XBRL'
|
|
123
|
-
|
|
124
|
-
return
|
|
125
|
-
uploadFilepaths = pluginData.getUploadFilepaths(val.modelXbrl)
|
|
127
|
+
uploadFilepaths = pluginData.getUploadFilepaths(fileSource)
|
|
126
128
|
for path in uploadFilepaths:
|
|
127
129
|
parents = [parent.name for parent in path.parents]
|
|
128
130
|
if startingDirectory in parents:
|
|
@@ -144,21 +146,20 @@ def rule_EC0129E(
|
|
|
144
146
|
|
|
145
147
|
|
|
146
148
|
@validation(
|
|
147
|
-
hook=ValidationHook.
|
|
149
|
+
hook=ValidationHook.FILESOURCE,
|
|
148
150
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
149
151
|
)
|
|
150
152
|
def rule_EC0130E(
|
|
151
|
-
pluginData:
|
|
152
|
-
|
|
153
|
+
pluginData: ControllerPluginData,
|
|
154
|
+
cntlr: Cntlr,
|
|
155
|
+
fileSource: FileSource,
|
|
153
156
|
*args: Any,
|
|
154
157
|
**kwargs: Any,
|
|
155
158
|
) -> Iterable[Validation]:
|
|
156
159
|
"""
|
|
157
160
|
EDINET.EC0130E: File extensions must match the file extensions allowed in Figure 2-1-3 and Figure 2-1-5.
|
|
158
161
|
"""
|
|
159
|
-
|
|
160
|
-
return
|
|
161
|
-
uploadContents = pluginData.getUploadContents(val.modelXbrl)
|
|
162
|
+
uploadContents = pluginData.getUploadContents(fileSource)
|
|
162
163
|
checks = []
|
|
163
164
|
for instanceType, amendmentPaths in uploadContents.amendmentPaths.items():
|
|
164
165
|
for amendmentPath in amendmentPaths:
|
|
@@ -192,21 +193,20 @@ def rule_EC0130E(
|
|
|
192
193
|
|
|
193
194
|
|
|
194
195
|
@validation(
|
|
195
|
-
hook=ValidationHook.
|
|
196
|
+
hook=ValidationHook.FILESOURCE,
|
|
196
197
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
197
198
|
)
|
|
198
199
|
def rule_EC0132E(
|
|
199
|
-
pluginData:
|
|
200
|
-
|
|
200
|
+
pluginData: ControllerPluginData,
|
|
201
|
+
cntlr: Cntlr,
|
|
202
|
+
fileSource: FileSource,
|
|
201
203
|
*args: Any,
|
|
202
204
|
**kwargs: Any,
|
|
203
205
|
) -> Iterable[Validation]:
|
|
204
206
|
"""
|
|
205
207
|
EDINET.EC0132E: Store the manifest file directly under the relevant folder.
|
|
206
208
|
"""
|
|
207
|
-
|
|
208
|
-
return
|
|
209
|
-
uploadContents = pluginData.getUploadContents(val.modelXbrl)
|
|
209
|
+
uploadContents = pluginData.getUploadContents(fileSource)
|
|
210
210
|
for instanceType in (InstanceType.AUDIT_DOC, InstanceType.PRIVATE_DOC, InstanceType.PUBLIC_DOC):
|
|
211
211
|
if instanceType not in uploadContents.instances:
|
|
212
212
|
continue
|
|
@@ -222,21 +222,20 @@ def rule_EC0132E(
|
|
|
222
222
|
|
|
223
223
|
|
|
224
224
|
@validation(
|
|
225
|
-
hook=ValidationHook.
|
|
225
|
+
hook=ValidationHook.FILESOURCE,
|
|
226
226
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
227
227
|
)
|
|
228
228
|
def rule_EC0183E(
|
|
229
|
-
pluginData:
|
|
230
|
-
|
|
229
|
+
pluginData: ControllerPluginData,
|
|
230
|
+
cntlr: Cntlr,
|
|
231
|
+
fileSource: FileSource,
|
|
231
232
|
*args: Any,
|
|
232
233
|
**kwargs: Any,
|
|
233
234
|
) -> Iterable[Validation]:
|
|
234
235
|
"""
|
|
235
236
|
EDINET.EC0183E: The compressed file size exceeds 55MB.
|
|
236
237
|
"""
|
|
237
|
-
|
|
238
|
-
return
|
|
239
|
-
size = val.modelXbrl.fileSource.getBytesSize()
|
|
238
|
+
size = fileSource.getBytesSize()
|
|
240
239
|
if size is None:
|
|
241
240
|
return # File size is not available, cannot validate
|
|
242
241
|
if size > 55_000_000: # Interpretting MB as megabytes (1,000,000 bytes)
|
|
@@ -248,22 +247,21 @@ def rule_EC0183E(
|
|
|
248
247
|
|
|
249
248
|
|
|
250
249
|
@validation(
|
|
251
|
-
hook=ValidationHook.
|
|
250
|
+
hook=ValidationHook.FILESOURCE,
|
|
252
251
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
253
252
|
)
|
|
254
253
|
def rule_EC0188E(
|
|
255
|
-
pluginData:
|
|
256
|
-
|
|
254
|
+
pluginData: ControllerPluginData,
|
|
255
|
+
cntlr: Cntlr,
|
|
256
|
+
fileSource: FileSource,
|
|
257
257
|
*args: Any,
|
|
258
258
|
**kwargs: Any,
|
|
259
259
|
) -> Iterable[Validation]:
|
|
260
260
|
"""
|
|
261
261
|
EDINET.EC0188E: There is an HTML file directly under PublicDoc or PrivateDoc whose first 7 characters are not numbers.
|
|
262
262
|
"""
|
|
263
|
-
if not pluginData.shouldValidateUpload(val):
|
|
264
|
-
return
|
|
265
263
|
pattern = re.compile(r'^\d{7}')
|
|
266
|
-
uploadFilepaths = pluginData.getUploadFilepaths(
|
|
264
|
+
uploadFilepaths = pluginData.getUploadFilepaths(fileSource)
|
|
267
265
|
docFolders = frozenset({"PublicDoc", "PrivateDoc"})
|
|
268
266
|
for path in uploadFilepaths:
|
|
269
267
|
if path.suffix not in HTML_EXTENSIONS:
|
|
@@ -282,22 +280,21 @@ def rule_EC0188E(
|
|
|
282
280
|
|
|
283
281
|
|
|
284
282
|
@validation(
|
|
285
|
-
hook=ValidationHook.
|
|
283
|
+
hook=ValidationHook.FILESOURCE,
|
|
286
284
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
287
285
|
)
|
|
288
286
|
def rule_EC0198E(
|
|
289
|
-
pluginData:
|
|
290
|
-
|
|
287
|
+
pluginData: ControllerPluginData,
|
|
288
|
+
cntlr: Cntlr,
|
|
289
|
+
fileSource: FileSource,
|
|
291
290
|
*args: Any,
|
|
292
291
|
**kwargs: Any,
|
|
293
292
|
) -> Iterable[Validation]:
|
|
294
293
|
"""
|
|
295
294
|
EDINET.EC0198E: The number of files in the total submission and directories can not exceed the upper limit.
|
|
296
295
|
"""
|
|
297
|
-
if not pluginData.shouldValidateUpload(val):
|
|
298
|
-
return
|
|
299
296
|
fileCounts: dict[Path, int] = defaultdict(int)
|
|
300
|
-
uploadFilepaths = pluginData.getUploadFilepaths(
|
|
297
|
+
uploadFilepaths = pluginData.getUploadFilepaths(fileSource)
|
|
301
298
|
for path in uploadFilepaths:
|
|
302
299
|
if len(path.suffix) == 0:
|
|
303
300
|
continue
|
|
@@ -319,21 +316,20 @@ def rule_EC0198E(
|
|
|
319
316
|
|
|
320
317
|
|
|
321
318
|
@validation(
|
|
322
|
-
hook=ValidationHook.
|
|
319
|
+
hook=ValidationHook.FILESOURCE,
|
|
323
320
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
324
321
|
)
|
|
325
322
|
def rule_EC0237E(
|
|
326
|
-
pluginData:
|
|
327
|
-
|
|
323
|
+
pluginData: ControllerPluginData,
|
|
324
|
+
cntlr: Cntlr,
|
|
325
|
+
fileSource: FileSource,
|
|
328
326
|
*args: Any,
|
|
329
327
|
**kwargs: Any,
|
|
330
328
|
) -> Iterable[Validation]:
|
|
331
329
|
"""
|
|
332
330
|
EDINET.EC0237E: The directory or file path to the lowest level exceeds the maximum value (259 characters).
|
|
333
331
|
"""
|
|
334
|
-
|
|
335
|
-
return
|
|
336
|
-
uploadFilepaths = pluginData.getUploadFilepaths(val.modelXbrl)
|
|
332
|
+
uploadFilepaths = pluginData.getUploadFilepaths(fileSource)
|
|
337
333
|
for path in uploadFilepaths:
|
|
338
334
|
if len(str(path)) <= 259:
|
|
339
335
|
continue
|
|
@@ -348,21 +344,20 @@ def rule_EC0237E(
|
|
|
348
344
|
|
|
349
345
|
|
|
350
346
|
@validation(
|
|
351
|
-
hook=ValidationHook.
|
|
347
|
+
hook=ValidationHook.FILESOURCE,
|
|
352
348
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
353
349
|
)
|
|
354
350
|
def rule_EC0206E(
|
|
355
|
-
pluginData:
|
|
356
|
-
|
|
351
|
+
pluginData: ControllerPluginData,
|
|
352
|
+
cntlr: Cntlr,
|
|
353
|
+
fileSource: FileSource,
|
|
357
354
|
*args: Any,
|
|
358
355
|
**kwargs: Any,
|
|
359
356
|
) -> Iterable[Validation]:
|
|
360
357
|
"""
|
|
361
358
|
EDINET.EC0206E: Empty files are not permitted.
|
|
362
359
|
"""
|
|
363
|
-
|
|
364
|
-
return
|
|
365
|
-
for path, size in pluginData.getUploadFileSizes(val.modelXbrl).items():
|
|
360
|
+
for path, size in pluginData.getUploadFileSizes(fileSource).items():
|
|
366
361
|
if size > 0:
|
|
367
362
|
continue
|
|
368
363
|
yield Validation.error(
|
|
@@ -376,21 +371,20 @@ def rule_EC0206E(
|
|
|
376
371
|
|
|
377
372
|
|
|
378
373
|
@validation(
|
|
379
|
-
hook=ValidationHook.
|
|
374
|
+
hook=ValidationHook.FILESOURCE,
|
|
380
375
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
381
376
|
)
|
|
382
377
|
def rule_EC1016E(
|
|
383
|
-
pluginData:
|
|
384
|
-
|
|
378
|
+
pluginData: ControllerPluginData,
|
|
379
|
+
cntlr: Cntlr,
|
|
380
|
+
fileSource: FileSource,
|
|
385
381
|
*args: Any,
|
|
386
382
|
**kwargs: Any,
|
|
387
383
|
) -> Iterable[Validation]:
|
|
388
384
|
"""
|
|
389
385
|
EDINET.EC1016E: The image file is over 300KB.
|
|
390
386
|
"""
|
|
391
|
-
|
|
392
|
-
return
|
|
393
|
-
for path, size in pluginData.getUploadFileSizes(val.modelXbrl).items():
|
|
387
|
+
for path, size in pluginData.getUploadFileSizes(fileSource).items():
|
|
394
388
|
if path.suffix not in IMAGE_EXTENSIONS:
|
|
395
389
|
continue
|
|
396
390
|
if size <= 300_000: # Interpretting KB as kilobytes (1,000 bytes)
|
|
@@ -422,8 +416,6 @@ def rule_EC1020E(
|
|
|
422
416
|
Note: Some violations of this rule (such as multiple DOCTYPE declarations) prevent Arelle from parsing
|
|
423
417
|
the XML at all, and thus an XML schema error will be triggered rather than this validation error.
|
|
424
418
|
"""
|
|
425
|
-
if not pluginData.shouldValidateUpload(val):
|
|
426
|
-
return
|
|
427
419
|
checkNames = frozenset({'body', 'head', 'html'})
|
|
428
420
|
for modelDocument in val.modelXbrl.urlDocs.values():
|
|
429
421
|
path = Path(modelDocument.uri)
|
|
@@ -453,12 +445,13 @@ def rule_EC1020E(
|
|
|
453
445
|
|
|
454
446
|
|
|
455
447
|
@validation(
|
|
456
|
-
hook=ValidationHook.
|
|
448
|
+
hook=ValidationHook.FILESOURCE,
|
|
457
449
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
458
450
|
)
|
|
459
451
|
def rule_manifest_preferredFilename(
|
|
460
|
-
pluginData:
|
|
461
|
-
|
|
452
|
+
pluginData: ControllerPluginData,
|
|
453
|
+
cntlr: Cntlr,
|
|
454
|
+
fileSource: FileSource,
|
|
462
455
|
*args: Any,
|
|
463
456
|
**kwargs: Any,
|
|
464
457
|
) -> Iterable[Validation]:
|
|
@@ -474,9 +467,7 @@ def rule_manifest_preferredFilename(
|
|
|
474
467
|
The preferredFilename attribute value of the instance element in the manifest
|
|
475
468
|
file must be unique within the same file.
|
|
476
469
|
"""
|
|
477
|
-
|
|
478
|
-
return
|
|
479
|
-
instances = pluginData.getManifestInstances(val.modelXbrl)
|
|
470
|
+
instances = pluginData.getManifestInstances()
|
|
480
471
|
preferredFilenames: dict[Path, set[str]] = defaultdict(set)
|
|
481
472
|
duplicateFilenames = defaultdict(set)
|
|
482
473
|
for instance in instances:
|
|
@@ -4,6 +4,7 @@ See COPYRIGHT.md for copyright information.
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
|
+
from arelle.Cntlr import Cntlr
|
|
7
8
|
from arelle.ModelDocument import LoadingException, ModelDocument
|
|
8
9
|
from arelle.ModelValue import qname
|
|
9
10
|
from arelle.ModelXbrl import ModelXbrl
|
|
@@ -19,7 +20,8 @@ _: TypeGetText
|
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class ValidationPluginExtension(ValidationPlugin):
|
|
22
|
-
def newPluginData(self, validateXbrl: ValidateXbrl) -> PluginValidationDataExtension:
|
|
23
|
+
def newPluginData(self, cntlr: Cntlr, validateXbrl: ValidateXbrl | None) -> PluginValidationDataExtension:
|
|
24
|
+
assert validateXbrl is not None
|
|
23
25
|
disclosureSystem = validateXbrl.disclosureSystem.name
|
|
24
26
|
if disclosureSystem == DISCLOSURE_SYSTEM_NT16:
|
|
25
27
|
ifrsNamespace = None
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
See COPYRIGHT.md for copyright information.
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from arelle.Cntlr import Cntlr
|
|
5
7
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
6
8
|
from arelle.typing import TypeGetText
|
|
7
9
|
from arelle.utils.validate.ValidationPlugin import ValidationPlugin
|
|
@@ -18,7 +20,7 @@ TURNOVER_REVENUE = 'DPLTurnoverRevenue'
|
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
class ValidationPluginExtension(ValidationPlugin):
|
|
21
|
-
def newPluginData(self, validateXbrl: ValidateXbrl) -> PluginValidationDataExtension:
|
|
23
|
+
def newPluginData(self, cntlr: Cntlr, validateXbrl: ValidateXbrl | None) -> PluginValidationDataExtension:
|
|
22
24
|
return PluginValidationDataExtension(
|
|
23
25
|
self.name
|
|
24
26
|
)
|
arelle/utils/PluginHooks.py
CHANGED
|
@@ -34,6 +34,7 @@ class ValidationHook(Enum):
|
|
|
34
34
|
These hooks are called at different stages of validation, but all provide a common interface (ValidateXbrl is the first param).
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
|
+
FILESOURCE = "Validate.FileSource"
|
|
37
38
|
XBRL_START = "Validate.XBRL.Start"
|
|
38
39
|
XBRL_FINALLY = "Validate.XBRL.Finally"
|
|
39
40
|
XBRL_DTS_DOCUMENT = "Validate.XBRL.DTS.document"
|
|
@@ -568,6 +569,37 @@ class PluginHooks(ABC):
|
|
|
568
569
|
"""
|
|
569
570
|
raise NotImplementedError
|
|
570
571
|
|
|
572
|
+
@staticmethod
|
|
573
|
+
def validateFileSource(
|
|
574
|
+
cntlr: Cntlr,
|
|
575
|
+
fileSource: FileSource,
|
|
576
|
+
entrypoints: list[dict[str, Any]] | None = None,
|
|
577
|
+
*args: Any,
|
|
578
|
+
**kwargs: Any,
|
|
579
|
+
) -> None:
|
|
580
|
+
"""
|
|
581
|
+
Plugin hook: `Validate.FileSource`
|
|
582
|
+
|
|
583
|
+
Hook for executing validation rules applicable to entrypoint files.
|
|
584
|
+
|
|
585
|
+
Example:
|
|
586
|
+
```python
|
|
587
|
+
size = fileSource.getBytesSize()
|
|
588
|
+
if size is None:
|
|
589
|
+
return
|
|
590
|
+
if size > 100_000_000:
|
|
591
|
+
yield Validation.error(codes="0.0.0", msg="File size exceeds 100MB.")
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
:param cntlr: The [Cntlr](#arelle.Cntlr.Cntlr) instance.
|
|
595
|
+
:param fileSource: The [FileSource](#arelle.FileSource.FileSource) involved in loading the entrypoint files.
|
|
596
|
+
:param entrypoints: A list of entrypoint configurations.
|
|
597
|
+
:param args: Argument capture to ensure new parameters don't break plugin hook.
|
|
598
|
+
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
599
|
+
:return: None
|
|
600
|
+
"""
|
|
601
|
+
raise NotImplementedError
|
|
602
|
+
|
|
571
603
|
@staticmethod
|
|
572
604
|
def validateFinally(
|
|
573
605
|
val: ValidateXbrl,
|
|
@@ -8,17 +8,19 @@ from pathlib import Path
|
|
|
8
8
|
from types import ModuleType
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
|
+
from arelle.Cntlr import Cntlr
|
|
11
12
|
from arelle.DisclosureSystem import DisclosureSystem
|
|
13
|
+
from arelle.FileSource import FileSource
|
|
12
14
|
from arelle.ModelDocument import LoadingException, ModelDocument
|
|
13
15
|
from arelle.ModelXbrl import ModelXbrl
|
|
14
16
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
17
|
+
from arelle.utils.PluginData import PluginData
|
|
15
18
|
from arelle.utils.PluginHooks import ValidationHook
|
|
16
19
|
from arelle.utils.validate.Decorator import (
|
|
17
20
|
ValidationAttributes,
|
|
18
21
|
ValidationFunction,
|
|
19
22
|
getValidationAttributes,
|
|
20
23
|
)
|
|
21
|
-
from arelle.utils.PluginData import PluginData
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
class ValidationPlugin:
|
|
@@ -55,7 +57,7 @@ class ValidationPlugin:
|
|
|
55
57
|
ValidationHook, dict[ValidationFunction, set[str]]
|
|
56
58
|
] = {}
|
|
57
59
|
|
|
58
|
-
def newPluginData(self, validateXbrl: ValidateXbrl) -> PluginData:
|
|
60
|
+
def newPluginData(self, cntlr: Cntlr, validateXbrl: ValidateXbrl | None) -> PluginData:
|
|
59
61
|
"""
|
|
60
62
|
Returns a dataclass intended to be overriden by plugins to facilitate caching and passing data between rule functions.
|
|
61
63
|
The default implementation doesn't provide any fields other than the plugin name.
|
|
@@ -99,6 +101,28 @@ class ValidationPlugin:
|
|
|
99
101
|
) -> ModelDocument | LoadingException | None:
|
|
100
102
|
raise NotImplementedError
|
|
101
103
|
|
|
104
|
+
def validateFileSource(
|
|
105
|
+
self,
|
|
106
|
+
cntlr: Cntlr,
|
|
107
|
+
fileSource: FileSource,
|
|
108
|
+
entrypoints: list[dict[str, Any]] | None = None,
|
|
109
|
+
*args: Any,
|
|
110
|
+
**kwargs: Any,
|
|
111
|
+
) -> None:
|
|
112
|
+
"""
|
|
113
|
+
Executes validation functions in the rules module that was provided to the constructor of this class.
|
|
114
|
+
Each function decorated with [@validation](#arelle.utils.validate.Decorator.validation) will be run if:
|
|
115
|
+
1. the decorator was used with the xbrl start hook: `@validation(hook=ValidationHook.FILESOURCE)`
|
|
116
|
+
|
|
117
|
+
:param cntlr: The [Cntlr](#arelle.Cntlr.Cntlr) instance.
|
|
118
|
+
:param fileSource: The [FileSource](#arelle.FileSource.FileSource) involved in loading the entrypoint files.
|
|
119
|
+
:param entrypoints: A list of entrypoint configurations.
|
|
120
|
+
:param args: Argument capture to ensure new parameters don't break plugin hook.
|
|
121
|
+
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
122
|
+
:return: None
|
|
123
|
+
"""
|
|
124
|
+
self._executeCntlrValidations(ValidationHook.FILESOURCE, cntlr, fileSource, entrypoints, *args, **kwargs)
|
|
125
|
+
|
|
102
126
|
def validateXbrlStart(
|
|
103
127
|
self,
|
|
104
128
|
val: ValidateXbrl,
|
|
@@ -118,7 +142,7 @@ class ValidationPlugin:
|
|
|
118
142
|
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
119
143
|
:return: None
|
|
120
144
|
"""
|
|
121
|
-
self.
|
|
145
|
+
self._executeModelValidations(ValidationHook.XBRL_START, val, parameters, *args, **kwargs)
|
|
122
146
|
|
|
123
147
|
def validateXbrlFinally(
|
|
124
148
|
self,
|
|
@@ -137,7 +161,7 @@ class ValidationPlugin:
|
|
|
137
161
|
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
138
162
|
:return: None
|
|
139
163
|
"""
|
|
140
|
-
self.
|
|
164
|
+
self._executeModelValidations(ValidationHook.XBRL_FINALLY, val, *args, **kwargs)
|
|
141
165
|
|
|
142
166
|
def validateXbrlDtsDocument(
|
|
143
167
|
self,
|
|
@@ -158,7 +182,7 @@ class ValidationPlugin:
|
|
|
158
182
|
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
159
183
|
:return: None
|
|
160
184
|
"""
|
|
161
|
-
self.
|
|
185
|
+
self._executeModelValidations(ValidationHook.XBRL_DTS_DOCUMENT, val, modelDocument, isFilingDocument, *args, **kwargs)
|
|
162
186
|
|
|
163
187
|
def validateFinally(
|
|
164
188
|
self,
|
|
@@ -177,9 +201,28 @@ class ValidationPlugin:
|
|
|
177
201
|
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
178
202
|
:return: None
|
|
179
203
|
"""
|
|
180
|
-
self.
|
|
204
|
+
self._executeModelValidations(ValidationHook.FINALLY, val, *args, **kwargs)
|
|
205
|
+
|
|
206
|
+
def _executeCntlrValidations(
|
|
207
|
+
self,
|
|
208
|
+
pluginHook: ValidationHook,
|
|
209
|
+
cntlr: Cntlr,
|
|
210
|
+
fileSource: FileSource | None = None,
|
|
211
|
+
entrypoints: list[dict[str, Any]] | None = None,
|
|
212
|
+
*args: Any,
|
|
213
|
+
**kwargs: Any,
|
|
214
|
+
) -> None:
|
|
215
|
+
pluginData = self.newPluginData(
|
|
216
|
+
cntlr=cntlr,
|
|
217
|
+
validateXbrl=None
|
|
218
|
+
)
|
|
219
|
+
for rule in self._getValidations(cntlr.modelManager.disclosureSystem, pluginHook):
|
|
220
|
+
validations = rule(pluginData, cntlr, fileSource, entrypoints, *args, **kwargs)
|
|
221
|
+
if validations is not None:
|
|
222
|
+
for val in validations:
|
|
223
|
+
cntlr.error(level=val.level.name, codes=val.codes, msg=val.msg, **val.args)
|
|
181
224
|
|
|
182
|
-
def
|
|
225
|
+
def _executeModelValidations(
|
|
183
226
|
self,
|
|
184
227
|
pluginHook: ValidationHook,
|
|
185
228
|
validateXbrl: ValidateXbrl,
|
|
@@ -189,7 +232,10 @@ class ValidationPlugin:
|
|
|
189
232
|
if self.disclosureSystemFromPluginSelected(validateXbrl):
|
|
190
233
|
pluginData = validateXbrl.getPluginData(self.name)
|
|
191
234
|
if pluginData is None:
|
|
192
|
-
pluginData = self.newPluginData(
|
|
235
|
+
pluginData = self.newPluginData(
|
|
236
|
+
cntlr=validateXbrl.modelXbrl.modelManager.cntlr,
|
|
237
|
+
validateXbrl=validateXbrl
|
|
238
|
+
)
|
|
193
239
|
validateXbrl.setPluginData(pluginData)
|
|
194
240
|
for rule in self._getValidations(validateXbrl.disclosureSystem, pluginHook):
|
|
195
241
|
validations = rule(pluginData, validateXbrl, *args, **kwargs)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
arelle/Aspect.py,sha256=Pn9I91D1os1RTVj6htuxTfRzVMhmVDtrbKvV_zy9xMI,5470
|
|
2
2
|
arelle/BetaFeatures.py,sha256=T_tPac-FiozHyYLCemt0RoHJ1JahUE71L-0tHmIRKpE,858
|
|
3
3
|
arelle/Cntlr.py,sha256=nwE_qIQMTa5R3sr_dLP6s3srLhiEA5zDOqfas2pDxoA,31691
|
|
4
|
-
arelle/CntlrCmdLine.py,sha256=
|
|
4
|
+
arelle/CntlrCmdLine.py,sha256=BHph7DWV9m87i23btO1G7l9KFt9F1EnMsorKRY9NdG8,90348
|
|
5
5
|
arelle/CntlrComServer.py,sha256=h1KPf31uMbErpxTZn_iklDqUMGFgQnjZkFkFjd8gtLQ,1888
|
|
6
6
|
arelle/CntlrProfiler.py,sha256=2VQJudiUhxryVypxjODx2ccP1-n60icTiWs5lSEokhQ,972
|
|
7
7
|
arelle/CntlrQuickBooks.py,sha256=BMqd5nkNQOZyNFPefkTeWUUDCYNS6BQavaG8k1Lepu4,31543
|
|
@@ -21,7 +21,7 @@ arelle/DialogRssWatch.py,sha256=mjc4pqyFDISY4tQtME0uSRQ3NlcWnNsOsMu9Zj8tTd0,1378
|
|
|
21
21
|
arelle/DialogURL.py,sha256=JH88OPFf588E8RW90uMaieok7A_4kOAURQ8kHWVhnao,4354
|
|
22
22
|
arelle/DialogUserPassword.py,sha256=kWPlCCihhwvsykDjanME9qBDtv6cxZlsrJyoMqiRep4,13769
|
|
23
23
|
arelle/DisclosureSystem.py,sha256=mQlz8eezPpJuG6gHBV-x4-5Hne3LVh8TQf-Qm9jiFxI,24757
|
|
24
|
-
arelle/ErrorManager.py,sha256=
|
|
24
|
+
arelle/ErrorManager.py,sha256=en3jVyUnSDzHGFE2e8UjuitU9sBwlciRMPiSn5OpTjs,16641
|
|
25
25
|
arelle/FileSource.py,sha256=asaX2wM47T7S6kELwmXm-YjGIoV6poWz_YdYThY0lpk,47983
|
|
26
26
|
arelle/FunctionCustom.py,sha256=d1FsBG14eykvpLpgaXpN8IdxnlG54dfGcsXPYfdpA9Q,5880
|
|
27
27
|
arelle/FunctionFn.py,sha256=BcZKah1rpfquSVPwjvknM1pgFXOnK4Hr1e_ArG_mcJY,38058
|
|
@@ -53,7 +53,7 @@ arelle/ModelVersObject.py,sha256=cPD1IzhkCfuV1eMgVFWes88DH_6WkUj5kj7sgGF2M0I,260
|
|
|
53
53
|
arelle/ModelVersReport.py,sha256=bXEA9K3qkH57aABn5l-m3CTY0FAcF1yX6O4fo-URjl8,73326
|
|
54
54
|
arelle/ModelXbrl.py,sha256=w7x74hBV-Ub4gRQ-iT4lIC13KCxp699W2FJ-AO7cebw,60639
|
|
55
55
|
arelle/PackageManager.py,sha256=BvPExMcxh8rHMxogOag-PGbX6vXdhCiXAHcDLA6Ypsc,32592
|
|
56
|
-
arelle/PluginManager.py,sha256=
|
|
56
|
+
arelle/PluginManager.py,sha256=Gnh7xmvyIQX2PyCwjDMFZVanCM9KW09I-x5x8YfDpJs,42220
|
|
57
57
|
arelle/PluginUtils.py,sha256=0vFQ29wVVpU0cTY3YOBL6FhNQhhCTwShBH4qTJGLnvc,2426
|
|
58
58
|
arelle/PrototypeDtsObject.py,sha256=0lf60VcXR_isx57hBPrc7vEMkFpYkVuK4JVBSmopzkQ,7989
|
|
59
59
|
arelle/PrototypeInstanceObject.py,sha256=CXUoDllhDqpMvSQjqIYi1Ywp-J8fLhQRV9wVD2YXgVo,13204
|
|
@@ -67,7 +67,7 @@ arelle/UITkTable.py,sha256=N83cXi5c0lLZLsDbwSKcPrlYoUoGsNavGN5YRx6d9XY,39810
|
|
|
67
67
|
arelle/UiUtil.py,sha256=3G0xPclZI8xW_XQDbiFrmylB7Nd5muqi5n2x2oMkMZU,34218
|
|
68
68
|
arelle/Updater.py,sha256=IZ8cq44Rq88WbQcB1VOpMA6bxdfZxfYQ8rgu9Ehpbes,7448
|
|
69
69
|
arelle/UrlUtil.py,sha256=HrxZSG59EUMGMMGmWPuZkPi5-0BGqY3jAMkp7V4IdZo,32400
|
|
70
|
-
arelle/Validate.py,sha256=
|
|
70
|
+
arelle/Validate.py,sha256=hIp9qD5n6SrQNGzoRio_rjR-9Nsmzv6UG2phxT_Pcww,58589
|
|
71
71
|
arelle/ValidateDuplicateFacts.py,sha256=L556J1Dhz4ZmsMlRNoDCMpFgDQYiryd9vuBYDvE0Aq8,21769
|
|
72
72
|
arelle/ValidateFilingText.py,sha256=xnXc0xgdNiHQk0eyP7VSSpvw7qr-pRFRwqqoUb569is,54051
|
|
73
73
|
arelle/ValidateInfoset.py,sha256=Rz_XBi5Ha43KpxXYhjLolURcWVx5qmqyjLxw48Yt9Dg,20396
|
|
@@ -125,7 +125,7 @@ arelle/XmlValidateConst.py,sha256=U_wN0Q-nWKwf6dKJtcu_83FXPn9c6P8JjzGA5b0w7P0,33
|
|
|
125
125
|
arelle/XmlValidateParticles.py,sha256=Mn6vhFl0ZKC_vag1mBwn1rH_x2jmlusJYqOOuxFPO2k,9231
|
|
126
126
|
arelle/XmlValidateSchema.py,sha256=6frtZOc1Yrx_5yYF6V6oHbScnglWrVbWr6xW4EHtLQI,7428
|
|
127
127
|
arelle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
128
|
-
arelle/_version.py,sha256=
|
|
128
|
+
arelle/_version.py,sha256=xztzzCsSO6dI_xsGmGG8v1j6UZRVyDBfVwKeEk-vY9M,515
|
|
129
129
|
arelle/typing.py,sha256=PRe-Fxwr2SBqYYUVPCJ3E7ddDX0_oOISNdT5Q97EbRM,1246
|
|
130
130
|
arelle/api/Session.py,sha256=27HVuK3Bz1_21l4_RLn1IQg6v0MNsUEYrHajymyWwxI,7429
|
|
131
131
|
arelle/archive/CustomLogger.py,sha256=v_JXOCQLDZcfaFWzxC9FRcEf9tQi4rCI4Sx7jCuAVQI,1231
|
|
@@ -222,7 +222,7 @@ arelle/examples/plugin/multi.py,sha256=LPu4BB1wanejVJAT8thLpYIjv4kuCm7nPaGXezzW7
|
|
|
222
222
|
arelle/examples/plugin/rssSaveOim.py,sha256=-6pMdjgrcvdKAiTIFK1qDj7El4HFXJIIuCnIna6RAI8,4297
|
|
223
223
|
arelle/examples/plugin/validate/XYZ/DisclosureSystems.py,sha256=6HxGoe6ioXoRg_0-iDlzckfN3RgS6fJRWivElH1gLFI,72
|
|
224
224
|
arelle/examples/plugin/validate/XYZ/PluginValidationDataExtension.py,sha256=JoaZ8wubDLTFJe7zbDnKham5vr6r3fOO1VzZ-LdMghY,235
|
|
225
|
-
arelle/examples/plugin/validate/XYZ/ValidationPluginExtension.py,sha256=
|
|
225
|
+
arelle/examples/plugin/validate/XYZ/ValidationPluginExtension.py,sha256=AAEko9mLTcRPwc4JG1fBkjvzuhBvDpn-tTctwrLLMPQ,1690
|
|
226
226
|
arelle/examples/plugin/validate/XYZ/__init__.py,sha256=uReCvbrDPUlLRLJckOc1DdJsncNukxHRXZ3O5W0ay_A,2493
|
|
227
227
|
arelle/examples/plugin/validate/XYZ/resources/config.xml,sha256=lcNWbfGMupREzaWRiuW49sCKmPL5llOtGQ5QZ_avQ0o,682
|
|
228
228
|
arelle/examples/plugin/validate/XYZ/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -383,7 +383,7 @@ arelle/plugin/validate/CIPC/__init__.py,sha256=R6KVETICUpfW--TvVkFNDo-67Kq_KpWz3
|
|
|
383
383
|
arelle/plugin/validate/CIPC/config.xml,sha256=4pyn40JAvQQeoRC8I046gZ4ZcmUnekX3TNfYpC5yonI,667
|
|
384
384
|
arelle/plugin/validate/DBA/DisclosureSystems.py,sha256=Dp_r-Pa3tahtCfDha2Zc97N0iyrY4Zagb8w2D9ErILg,294
|
|
385
385
|
arelle/plugin/validate/DBA/PluginValidationDataExtension.py,sha256=R0lNf-3-lKHlnCy4ByU1TT1lVImMe35KeV-mbDxeaco,7290
|
|
386
|
-
arelle/plugin/validate/DBA/ValidationPluginExtension.py,sha256=
|
|
386
|
+
arelle/plugin/validate/DBA/ValidationPluginExtension.py,sha256=UygF7oELnPJcP6Ta0ncy3dy5fnJq-Mz6N2-gEaVhigo,48690
|
|
387
387
|
arelle/plugin/validate/DBA/__init__.py,sha256=KhmlUkqgsRtEXpu5DZBXFzv43nUTvi-0sdDNRfw5Up4,1564
|
|
388
388
|
arelle/plugin/validate/DBA/resources/config.xml,sha256=KHfo7SrjzmjHbfwIJBmESvOOjdIv4Av26BCcZxfn3Pg,875
|
|
389
389
|
arelle/plugin/validate/DBA/rules/__init__.py,sha256=eBm6FAb_WnBBYfgLSwsO30gVjpWaHxLqRHRJAIBj7oo,10418
|
|
@@ -395,13 +395,14 @@ arelle/plugin/validate/DBA/rules/tr.py,sha256=4TootFjl0HXsKZk1XNBCyj-vnjRs4lg35h
|
|
|
395
395
|
arelle/plugin/validate/EBA/__init__.py,sha256=x3zXNcdSDJ3kHfL7kMs0Ve0Vs9oWbzNFVf1TK4Avmy8,45924
|
|
396
396
|
arelle/plugin/validate/EBA/config.xml,sha256=37wMVUAObK-XEqakqD8zPNog20emYt4a_yfL1AKubF8,2022
|
|
397
397
|
arelle/plugin/validate/EDINET/Constants.py,sha256=ENqevcx-d8ZMBbboxbbBnQpbyY-rzHX7_1SSuAaw75s,988
|
|
398
|
-
arelle/plugin/validate/EDINET/ControllerPluginData.py,sha256=
|
|
398
|
+
arelle/plugin/validate/EDINET/ControllerPluginData.py,sha256=PrEhllue_lh-sLeUy9w8I9XLMLgpo5eW-c7ZKgNyGLE,6256
|
|
399
399
|
arelle/plugin/validate/EDINET/DisclosureSystems.py,sha256=3rKG42Eg-17Xx_KXU_V5yHW6I3LTwQunvf4a44C9k_4,36
|
|
400
400
|
arelle/plugin/validate/EDINET/InstanceType.py,sha256=aLKb4-AJ6nDZKMOLCp7u08E9VD64ExeZy9_oGth-LTk,3207
|
|
401
401
|
arelle/plugin/validate/EDINET/ManifestInstance.py,sha256=SkQV-aOsYn3CTgCkH4IXNdM3QKoiz8okwb29ftMtV3Q,6882
|
|
402
|
-
arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=
|
|
403
|
-
arelle/plugin/validate/EDINET/
|
|
404
|
-
arelle/plugin/validate/EDINET/
|
|
402
|
+
arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=A_iJox9SEWCsSWwhlno6I3VUysdAhFrtsMSNO9CcJ7c,5508
|
|
403
|
+
arelle/plugin/validate/EDINET/UploadContents.py,sha256=L0u5171cLBKX7NT-_szRqOfkiy4Gv1xPsfpPVgPhtu0,409
|
|
404
|
+
arelle/plugin/validate/EDINET/ValidationPluginExtension.py,sha256=oMY0ntLr1qIh3uMi1W_M-bT5bhXPDx048X3oDFP5zOY,2042
|
|
405
|
+
arelle/plugin/validate/EDINET/__init__.py,sha256=OZ7gMknCHd0M-9nt8UOmjEZW50YQzbvSLongr9O7Yi0,3022
|
|
405
406
|
arelle/plugin/validate/EDINET/resources/config.xml,sha256=7uT4GcRgk5veMLpFhPPQJxbGKiQvM52P8EMrjn0qd0g,646
|
|
406
407
|
arelle/plugin/validate/EDINET/resources/edinet-taxonomies.xml,sha256=997I3RGTLg5OY3vn5hQxVFAAxOmDSOYpuyQe6VnWSY0,16285
|
|
407
408
|
arelle/plugin/validate/EDINET/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -410,7 +411,7 @@ arelle/plugin/validate/EDINET/rules/edinet.py,sha256=2PP47pJDz1jgZ4FcWS6fcBU6NIL
|
|
|
410
411
|
arelle/plugin/validate/EDINET/rules/frta.py,sha256=N0YglHYZuLD2IuwE26viR2ViwUYjneBuMFU9vlrS0aQ,7616
|
|
411
412
|
arelle/plugin/validate/EDINET/rules/gfm.py,sha256=4EKMho6eX-Ygl8yMBVabHQpbC-wxMvi067ubN9mp27U,21982
|
|
412
413
|
arelle/plugin/validate/EDINET/rules/manifests.py,sha256=MoT9R_a4BzuYdQVbF7RC5wz134Ve68svSdJ3NlpO_AU,4026
|
|
413
|
-
arelle/plugin/validate/EDINET/rules/upload.py,sha256=
|
|
414
|
+
arelle/plugin/validate/EDINET/rules/upload.py,sha256=k1o12K_vMN2N5bAXPxLRwyKjghoOGrgfLijE_j_5ilQ,19811
|
|
414
415
|
arelle/plugin/validate/ESEF/Const.py,sha256=JujF_XV-_TNsxjGbF-8SQS4OOZIcJ8zhCMnr-C1O5Ho,22660
|
|
415
416
|
arelle/plugin/validate/ESEF/Dimensions.py,sha256=MOJM7vwNPEmV5cu-ZzPrhx3347ZvxgD6643OB2HRnIk,10597
|
|
416
417
|
arelle/plugin/validate/ESEF/Util.py,sha256=QH3btcGqBpr42M7WSKZLSdNXygZaZLfEiEjlxoG21jE,7950
|
|
@@ -429,7 +430,7 @@ arelle/plugin/validate/FERC/config.xml,sha256=bn9b8eCqJA1J62rYq1Nz85wJrMGAahVmmn
|
|
|
429
430
|
arelle/plugin/validate/FERC/resources/ferc-utr.xml,sha256=OCRj9IUpdXATCBXKbB71apYx9kxcNtZW-Hq4s-avsRY,2663
|
|
430
431
|
arelle/plugin/validate/NL/DisclosureSystems.py,sha256=urRmYJ8RnGPlTgSVKW7zGN4_4CtL3OVKlcI3LwTpBz4,561
|
|
431
432
|
arelle/plugin/validate/NL/PluginValidationDataExtension.py,sha256=OswvJzy5AdvIDw1N_IeWDehKbUHdOFnipy0qhjAaImw,33678
|
|
432
|
-
arelle/plugin/validate/NL/ValidationPluginExtension.py,sha256=
|
|
433
|
+
arelle/plugin/validate/NL/ValidationPluginExtension.py,sha256=GCeR6xh1Dd7IhFoQk5XD6Hg2bg9PyyWEWp5GzxUdUq4,18070
|
|
433
434
|
arelle/plugin/validate/NL/__init__.py,sha256=W-SHohiAWM7Yi77gAbt-D3vvZNAB5s1j16mHCTFta6w,3158
|
|
434
435
|
arelle/plugin/validate/NL/resources/config.xml,sha256=qBE6zywFSmemBSWonuTII5iuOCUlNb1nvkpMbsZb5PM,1853
|
|
435
436
|
arelle/plugin/validate/NL/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -440,7 +441,7 @@ arelle/plugin/validate/NL/rules/fr_nl.py,sha256=vwzk6_P6jhszpeboTM09uOHtm-Lijzz7
|
|
|
440
441
|
arelle/plugin/validate/NL/rules/nl_kvk.py,sha256=bPD5lfr6dxdVGPZ2D_bEZtbz02tzsX_BZ5kC-CZJNwc,90170
|
|
441
442
|
arelle/plugin/validate/ROS/DisclosureSystems.py,sha256=rJ81mwQDYTi6JecFZ_zhqjjz3VNQRgjHNSh0wcQWAQE,18
|
|
442
443
|
arelle/plugin/validate/ROS/PluginValidationDataExtension.py,sha256=nCZoGWNX4aUN5oQBJqa8UrG9tElPW906xkfzRlbjBB8,4285
|
|
443
|
-
arelle/plugin/validate/ROS/ValidationPluginExtension.py,sha256=
|
|
444
|
+
arelle/plugin/validate/ROS/ValidationPluginExtension.py,sha256=OEDpi3CqcHrlX87UTG2KEYHnQrcbo8iajBY8X8nF6Tw,829
|
|
444
445
|
arelle/plugin/validate/ROS/__init__.py,sha256=KuWg1MHVzA2S6eaHFptvP3Vu_5gQWf3OUYC97clB7zI,2075
|
|
445
446
|
arelle/plugin/validate/ROS/config.xml,sha256=ZCpCFgr1ZAjoUuhb1eRpDnmKrae-sXA9yl6SOWnrfm8,654
|
|
446
447
|
arelle/plugin/validate/ROS/resources/config.xml,sha256=HXWume5HlrAqOx5AtiWWqgADbRatA8YSfm_JvZGwdgQ,657
|
|
@@ -751,21 +752,21 @@ arelle/utils/Contexts.py,sha256=j9uSBAXGkunlJGC9SscCbb1cj3oU_J3b_yjdhj2B4a4,1714
|
|
|
751
752
|
arelle/utils/EntryPointDetection.py,sha256=4RzercL0xE4PJrwoeUYq3S-E7PMSD-IspyS9bwK2RYM,4722
|
|
752
753
|
arelle/utils/Equivalence.py,sha256=Ac6sENvh-WHJlBJneV_-6n_MF2C1filHETkUEiucLJg,525
|
|
753
754
|
arelle/utils/PluginData.py,sha256=GUnuZaApm1J4Xm9ZA1U2M1aask-AaNGviLtc0fgXbFg,265
|
|
754
|
-
arelle/utils/PluginHooks.py,sha256=
|
|
755
|
+
arelle/utils/PluginHooks.py,sha256=SX0aH5KnP2E_m5nL_mwn1o0uBTMgG0TeMXfG_HuUe00,32323
|
|
755
756
|
arelle/utils/Units.py,sha256=c9bwnu9Xnm00gC9Q6qQ1ogsEeTEXGRH7rahlEbrEWnQ,1201
|
|
756
757
|
arelle/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
757
758
|
arelle/utils/validate/Decorator.py,sha256=8LGmA171HZgKrALtsMKyHqMNM-XCdwJOv6KpZz4pC2c,3161
|
|
758
759
|
arelle/utils/validate/DetectScriptsInXhtml.py,sha256=RFBh_Z24OjR69s71qQzSzbxdU-WCTWuvYlONN-BgpZ0,2098
|
|
759
760
|
arelle/utils/validate/ESEFImage.py,sha256=M3pR9zxz0Y8oNjrpniEYwztCV2hoBK4fDSi4U095C3k,15520
|
|
760
761
|
arelle/utils/validate/Validation.py,sha256=n6Ag7VeCj_VO5nqzw_P53hOfXXeT2APK0Enb3UQqBns,832
|
|
761
|
-
arelle/utils/validate/ValidationPlugin.py,sha256=
|
|
762
|
+
arelle/utils/validate/ValidationPlugin.py,sha256=LUlH24tSq1W_eFP3DQZfByFPEKpgdlOaTXKiIWQltSc,13658
|
|
762
763
|
arelle/utils/validate/ValidationUtil.py,sha256=9vmSvShn-EdQy56dfesyV8JjSRVPj7txrxRFgh8FxIs,548
|
|
763
764
|
arelle/utils/validate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
764
765
|
arelle/webserver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
765
766
|
arelle/webserver/bottle.py,sha256=P-JECd9MCTNcxCnKoDUvGcoi03ezYVOgoWgv2_uH-6M,362
|
|
766
|
-
arelle_release-2.37.
|
|
767
|
-
arelle_release-2.37.
|
|
768
|
-
arelle_release-2.37.
|
|
769
|
-
arelle_release-2.37.
|
|
770
|
-
arelle_release-2.37.
|
|
771
|
-
arelle_release-2.37.
|
|
767
|
+
arelle_release-2.37.47.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
|
|
768
|
+
arelle_release-2.37.47.dist-info/METADATA,sha256=YqUy_T7g_QnlcZG9bw4jw9RKuriEGVtBnn_vkEqrdnw,9137
|
|
769
|
+
arelle_release-2.37.47.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
770
|
+
arelle_release-2.37.47.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
|
|
771
|
+
arelle_release-2.37.47.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
|
|
772
|
+
arelle_release-2.37.47.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|