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
|
@@ -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)
|