arelle-release 2.37.54__py3-none-any.whl → 2.37.56__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 CHANGED
@@ -1171,10 +1171,6 @@ class CntlrCmdLine(Cntlr.Cntlr):
1171
1171
  for pluginXbrlMethod in PluginManager.pluginClassMethods("CntlrCmdLine.Xbrl.Run"):
1172
1172
  pluginXbrlMethod(self, options, modelXbrl, _entrypoint, sourceZipStream=sourceZipStream, responseZipStream=responseZipStream)
1173
1173
 
1174
- if options.validate:
1175
- for pluginXbrlMethod in PluginManager.pluginClassMethods("Validate.Complete"):
1176
- pluginXbrlMethod(self, filesource)
1177
-
1178
1174
  except OSError as err:
1179
1175
  self.addToLog(_("[IOError] Failed to save output:\n {0}").format(err),
1180
1176
  messageCode="IOError",
@@ -1222,6 +1218,11 @@ class CntlrCmdLine(Cntlr.Cntlr):
1222
1218
  self.modelManager.close(modelDiffReport)
1223
1219
  elif modelXbrl:
1224
1220
  self.modelManager.close(modelXbrl)
1221
+
1222
+ if options.validate:
1223
+ for pluginXbrlMethod in PluginManager.pluginClassMethods("Validate.Complete"):
1224
+ pluginXbrlMethod(self, filesource)
1225
+
1225
1226
  if filesource is not None and not options.keepOpen:
1226
1227
  # Archive filesource potentially used by multiple reports may still be open.
1227
1228
  filesource.close()
arelle/Validate.py CHANGED
@@ -488,13 +488,13 @@ class Validate:
488
488
  self.instValidator.validate(model, parameters)
489
489
  for pluginXbrlMethod in pluginClassMethods("TestcaseVariation.Xbrl.Validated"):
490
490
  pluginXbrlMethod(self.modelXbrl, model)
491
- for pluginXbrlMethod in pluginClassMethods("Validate.Complete"):
492
- pluginXbrlMethod(self.modelXbrl.modelManager.cntlr, filesource)
493
491
  except Exception as err:
494
492
  model.error("exception:" + type(err).__name__,
495
493
  _("Testcase variation validation exception: %(error)s, instance: %(instance)s"),
496
494
  modelXbrl=model, instance=model.modelDocument.basename, error=err, exc_info=(type(err) is not AssertionError))
497
495
  model.hasFormulae = _hasFormulae
496
+ for pluginXbrlMethod in pluginClassMethods("Validate.Complete"):
497
+ pluginXbrlMethod(self.modelXbrl.modelManager.cntlr, filesource)
498
498
  errors = [error for model in loadedModels for error in model.errors]
499
499
  for err in preLoadingErrors:
500
500
  if err not in errors:
arelle/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '2.37.54'
32
- __version_tuple__ = version_tuple = (2, 37, 54)
31
+ __version__ = version = '2.37.56'
32
+ __version_tuple__ = version_tuple = (2, 37, 56)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -40,6 +40,27 @@ xhtmlDtdExtension = "xhtml1-strict-ix.dtd"
40
40
 
41
41
  COVER_PAGE_FILENAME_PREFIX = "0000000_header_"
42
42
 
43
+ PROHIBITED_HTML_ATTRIBUTES = frozenset({
44
+ 'onblur',
45
+ 'onchange',
46
+ 'onclick',
47
+ 'ondblclick',
48
+ 'onfocus',
49
+ 'onkeydown',
50
+ 'onkeypress',
51
+ 'onkeyup',
52
+ 'onload',
53
+ 'onmousedown',
54
+ 'onmousemove',
55
+ 'onmouseout',
56
+ 'onmouseover',
57
+ 'onmouseup',
58
+ 'onreset',
59
+ 'onselect',
60
+ 'onsubmit',
61
+ 'onunload',
62
+ })
63
+
43
64
  PROHIBITED_HTML_TAGS = frozenset({
44
65
  'applet',
45
66
  'embed',
@@ -27,12 +27,14 @@ _: TypeGetText
27
27
  @dataclass
28
28
  class ControllerPluginData(PluginData):
29
29
  _manifestInstancesById: dict[str, ManifestInstance]
30
+ _uploadContents: UploadContents | None
30
31
  _usedFilepaths: set[Path]
31
32
 
32
33
  def __init__(self, name: str):
33
34
  super().__init__(name)
34
35
  self._manifestInstancesById = {}
35
36
  self._usedFilepaths = set()
37
+ self._uploadContents = None
36
38
 
37
39
  def __hash__(self) -> int:
38
40
  return id(self)
@@ -49,18 +51,21 @@ class ControllerPluginData(PluginData):
49
51
  """
50
52
  return list(self._manifestInstancesById.values())
51
53
 
52
- @lru_cache(1)
53
- def getUploadContents(self, fileSource: FileSource) -> UploadContents:
54
- uploadFilepaths = self.getUploadFilepaths(fileSource)
54
+ def getUploadContents(self) -> UploadContents | None:
55
+ return self._uploadContents
56
+
57
+ def setUploadContents(self, fileSource: FileSource) -> UploadContents:
55
58
  reports = defaultdict(list)
56
59
  uploadPaths = {}
57
- for path in uploadFilepaths:
60
+ for path, zipPath in self.getUploadFilepaths(fileSource).items():
58
61
  if len(path.parts) == 0:
59
62
  continue
63
+ assert isinstance(fileSource.basefile, str)
64
+ fullPath = Path(fileSource.basefile) / path
60
65
  parents = list(reversed([p.name for p in path.parents if len(p.name) > 0]))
61
66
  reportFolderType = None
62
67
  isCorrection = True
63
- isDirectory = len(path.suffix) == 0
68
+ isDirectory = zipPath.is_dir()
64
69
  isInSubdirectory = False
65
70
  reportPath = None
66
71
  if len(parents) > 0:
@@ -79,32 +84,43 @@ class ControllerPluginData(PluginData):
79
84
  if not isCorrection:
80
85
  reports[reportFolderType].append(path)
81
86
  uploadPaths[path] = UploadPathInfo(
87
+ fullPath=fullPath,
82
88
  isAttachment=reportFolderType is not None and reportFolderType.isAttachment,
83
89
  isCorrection=isCorrection,
84
90
  isCoverPage=not isDirectory and path.stem.startswith(Constants.COVER_PAGE_FILENAME_PREFIX),
85
- isDirectory=len(path.suffix) == 0,
91
+ isDirectory=isDirectory,
86
92
  isRoot=len(path.parts) == 1,
87
93
  isSubdirectory=isInSubdirectory or (isDirectory and reportFolderType is not None),
88
94
  path=path,
89
95
  reportFolderType=reportFolderType,
90
96
  reportPath=reportPath,
91
97
  )
92
- return UploadContents(
98
+ self._uploadContents = UploadContents(
93
99
  reports={k: frozenset(v) for k, v in reports.items() if len(v) > 0},
94
- uploadPaths=uploadPaths
100
+ uploadPaths=list(uploadPaths.values())
95
101
  )
102
+ return self._uploadContents
96
103
 
97
104
  @lru_cache(1)
98
- def getUploadFilepaths(self, fileSource: FileSource) -> list[Path]:
105
+ def getUploadFilepaths(self, fileSource: FileSource) -> dict[Path, zipfile.Path]:
99
106
  if not self.isUpload(fileSource):
100
- return []
101
- paths = set()
107
+ return {}
108
+ paths = {}
102
109
  assert isinstance(fileSource.fs, zipfile.ZipFile)
103
- for name in fileSource.fs.namelist():
104
- path = Path(name)
105
- paths.add(path)
106
- paths.update(path.parents)
107
- return sorted(paths)
110
+ # First, fill in paths from zip file list
111
+ for file in fileSource.fs.filelist:
112
+ zipPath = zipfile.Path(fileSource.fs, file.filename)
113
+ paths[Path(file.filename)] = zipPath
114
+ # Then, fill in any parent directories that weren't in file list
115
+ for path in list(paths):
116
+ for parent in path.parents:
117
+ if parent in paths:
118
+ continue
119
+ paths[parent] = zipfile.Path(fileSource.fs, parent.as_posix() + '/')
120
+ return {
121
+ path: paths[path]
122
+ for path in sorted(paths)
123
+ }
108
124
 
109
125
  @lru_cache(1)
110
126
  def getUploadFileSizes(self, fileSource: FileSource) -> dict[Path, int]:
@@ -31,10 +31,11 @@ from arelle.XhtmlValidate import htmlEltUriAttrs
31
31
  from arelle.XmlValidate import VALID
32
32
  from arelle.typing import TypeGetText
33
33
  from arelle.utils.PluginData import PluginData
34
- from .Constants import CORPORATE_FORMS, FormType, xhtmlDtdExtension, PROHIBITED_HTML_TAGS
34
+ from .Constants import CORPORATE_FORMS, FormType, xhtmlDtdExtension, PROHIBITED_HTML_TAGS, PROHIBITED_HTML_ATTRIBUTES
35
35
  from .ControllerPluginData import ControllerPluginData
36
36
  from .ManifestInstance import ManifestInstance
37
37
  from .Statement import Statement, STATEMENTS, BalanceSheet, StatementInstance, StatementType
38
+ from .UploadContents import UploadContents
38
39
 
39
40
  _: TypeGetText
40
41
 
@@ -301,6 +302,19 @@ class PluginValidationDataExtension(PluginData):
301
302
  controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
302
303
  return controllerPluginData.matchManifestInstance(modelXbrl.ixdsDocUrls)
303
304
 
305
+ @lru_cache(1)
306
+ def getProhibitedAttributeElements(self, modelDocument: ModelDocument) -> list[tuple[ModelObject, str]]:
307
+ results: list[tuple[ModelObject, str]] = []
308
+ if modelDocument.type not in (ModelDocumentType.INLINEXBRL, ModelDocumentType.HTML):
309
+ return results
310
+ for elt in modelDocument.xmlRootElement.iter():
311
+ if not isinstance(elt, ModelObject):
312
+ continue
313
+ for attributeName in elt.attrib:
314
+ if attributeName in PROHIBITED_HTML_ATTRIBUTES:
315
+ results.append((elt, str(attributeName)))
316
+ return results
317
+
304
318
  @lru_cache(1)
305
319
  def getProhibitedTagElements(self, modelDocument: ModelDocument) -> list[ModelObject]:
306
320
  elts: list[ModelObject] = []
@@ -314,6 +328,10 @@ class PluginValidationDataExtension(PluginData):
314
328
  elts.append(elt)
315
329
  return elts
316
330
 
331
+ def getUploadContents(self, modelXbrl: ModelXbrl) -> UploadContents | None:
332
+ controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
333
+ return controllerPluginData.getUploadContents()
334
+
317
335
  @lru_cache(1)
318
336
  def getUriAttributeValues(self, modelDocument: ModelDocument) -> list[tuple[ModelObject, str, str]]:
319
337
  results: list[tuple[ModelObject, str, str]] = []
@@ -4,6 +4,7 @@ See COPYRIGHT.md for copyright information.
4
4
  from __future__ import annotations
5
5
 
6
6
  from dataclasses import dataclass
7
+ from functools import cached_property
7
8
  from pathlib import Path
8
9
 
9
10
  from .ReportFolderType import ReportFolderType
@@ -12,15 +13,30 @@ from .ReportFolderType import ReportFolderType
12
13
  @dataclass(frozen=True)
13
14
  class UploadContents:
14
15
  reports: dict[ReportFolderType, frozenset[Path]]
15
- uploadPaths: dict[Path, UploadPathInfo]
16
+ uploadPaths: list[UploadPathInfo]
16
17
 
17
18
  @property
18
19
  def sortedPaths(self) -> list[Path]:
19
- return sorted(self.uploadPaths.keys())
20
+ return sorted(uploadPath.path for uploadPath in self.uploadPaths)
21
+
22
+ @cached_property
23
+ def uploadPathsByFullPath(self) -> dict[Path, UploadPathInfo]:
24
+ return {
25
+ uploadPath.fullPath: uploadPath
26
+ for uploadPath in self.uploadPaths
27
+ }
28
+
29
+ @cached_property
30
+ def uploadPathsByPath(self) -> dict[Path, UploadPathInfo]:
31
+ return {
32
+ uploadPath.path: uploadPath
33
+ for uploadPath in self.uploadPaths
34
+ }
20
35
 
21
36
 
22
37
  @dataclass(frozen=True)
23
38
  class UploadPathInfo:
39
+ fullPath: Path
24
40
  isAttachment: bool
25
41
  isCorrection: bool
26
42
  isCoverPage: bool
@@ -37,6 +37,7 @@ class ValidationPluginExtension(ValidationPlugin):
37
37
  }, level=logging.INFO
38
38
  )
39
39
  pluginData = ControllerPluginData.get(filesource.cntlr, self.name)
40
+ pluginData.setUploadContents(filesource)
40
41
  entrypointFiles = []
41
42
  for instance in instances:
42
43
  pluginData.addManifestInstance(instance)
@@ -641,3 +641,63 @@ def rule_gfm_1_3_1(
641
641
  msg=_("The submitter-specific taxonomy contains include elements."),
642
642
  modelObject=warnings
643
643
  )
644
+
645
+
646
+ @validation(
647
+ hook=ValidationHook.XBRL_FINALLY,
648
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
649
+ )
650
+ def rule_gfm_1_3_8(
651
+ pluginData: PluginValidationDataExtension,
652
+ val: ValidateXbrl,
653
+ *args: Any,
654
+ **kwargs: Any,
655
+ ) -> Iterable[Validation]:
656
+ """
657
+ EDINET.EC5700W: [GFM 1.3.8] TThe submitter-specific taxonomy has an embedded linkbase.
658
+ """
659
+ embeddedElements = []
660
+ for modelDocument in val.modelXbrl.urlDocs.values():
661
+ if pluginData.isStandardTaxonomyUrl(modelDocument.uri, val.modelXbrl):
662
+ continue
663
+ rootElt = modelDocument.xmlRootElement
664
+ for elt in rootElt.iterdescendants(XbrlConst.qnLinkLinkbaseRef.clarkNotation):
665
+ if elt.attrib.get(XbrlConst.qnXlinkType.clarkNotation) in ('extended', 'arc', 'resource', 'locator'):
666
+ embeddedElements.append(elt)
667
+ if len(embeddedElements) > 0:
668
+ yield Validation.warning(
669
+ codes='EDINET.EC5700W.GFM.1.3.8',
670
+ msg=_("The submitter-specific taxonomy has an embedded linkbase."),
671
+ modelObject=embeddedElements
672
+ )
673
+
674
+
675
+ @validation(
676
+ hook=ValidationHook.XBRL_FINALLY,
677
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
678
+ )
679
+ def rule_gfm_1_5_6(
680
+ pluginData: PluginValidationDataExtension,
681
+ val: ValidateXbrl,
682
+ *args: Any,
683
+ **kwargs: Any,
684
+ ) -> Iterable[Validation]:
685
+ """
686
+ EDINET.EC5700W: [GFM 1.5.6] The length of a label must be less than 511 characters unless it its role is documentation.
687
+ """
688
+ labelRelationshipSet = val.modelXbrl.relationshipSet(XbrlConst.conceptLabel)
689
+ if labelRelationshipSet is None:
690
+ return
691
+ for concept in val.modelXbrl.qnameConcepts.values():
692
+ labelRels = labelRelationshipSet.fromModelObject(concept)
693
+ for rel in labelRels:
694
+ label = rel.toModelObject
695
+ if label.role != XbrlConst.documentationLabel and label.viewText() is not None and len(label.viewText()) > 511:
696
+ yield Validation.warning(
697
+ codes='EDINET.EC5700W.GFM.1.5.6',
698
+ msg=_("The concept of '%(concept)s' has a label classified as '%(role)s' that is longer than 511 characters: %(label)s"),
699
+ concept=concept.qname,
700
+ role=label.role,
701
+ label=label.viewText(),
702
+ modelObject=label
703
+ )
@@ -122,8 +122,10 @@ def rule_EC0100E(
122
122
  For this implementation, we will allow all directories that may be valid for at least one submission type.
123
123
  This allows for a false-negative outcome when a non-correction submission has a correction-only root directory.
124
124
  """
125
- uploadContents = pluginData.getUploadContents(fileSource)
126
- for path, pathInfo in uploadContents.uploadPaths.items():
125
+ uploadContents = pluginData.getUploadContents()
126
+ if uploadContents is None:
127
+ return
128
+ for path, pathInfo in uploadContents.uploadPathsByPath.items():
127
129
  if pathInfo.isRoot and path.name not in ALLOWED_ROOT_FOLDERS:
128
130
  yield Validation.error(
129
131
  codes='EDINET.EC0100E',
@@ -153,8 +155,8 @@ def rule_EC0124E_EC0187E(
153
155
  """
154
156
  uploadFilepaths = pluginData.getUploadFilepaths(fileSource)
155
157
  emptyDirectories = []
156
- for path in uploadFilepaths:
157
- if path.suffix:
158
+ for path, zipPath in uploadFilepaths.items():
159
+ if not zipPath.is_dir():
158
160
  continue
159
161
  if not any(path in p.parents for p in uploadFilepaths):
160
162
  emptyDirectories.append(path)
@@ -227,8 +229,10 @@ def rule_EC0130E(
227
229
  """
228
230
  EDINET.EC0130E: File extensions must match the file extensions allowed in Figure 2-1-3 and Figure 2-1-5.
229
231
  """
230
- uploadContents = pluginData.getUploadContents(fileSource)
231
- for path, pathInfo in uploadContents.uploadPaths.items():
232
+ uploadContents = pluginData.getUploadContents()
233
+ if uploadContents is None:
234
+ return
235
+ for path, pathInfo in uploadContents.uploadPathsByPath.items():
232
236
  if pathInfo.reportFolderType is None or pathInfo.isDirectory:
233
237
  continue
234
238
  validExtensions = pathInfo.reportFolderType.getValidExtensions(pathInfo.isCorrection, pathInfo.isSubdirectory)
@@ -265,7 +269,9 @@ def rule_EC0132E(
265
269
  """
266
270
  EDINET.EC0132E: Store the manifest file directly under the relevant folder.
267
271
  """
268
- uploadContents = pluginData.getUploadContents(fileSource)
272
+ uploadContents = pluginData.getUploadContents()
273
+ if uploadContents is None:
274
+ return
269
275
  for reportFolderType, paths in uploadContents.reports.items():
270
276
  if reportFolderType.isAttachment:
271
277
  continue
@@ -353,8 +359,10 @@ def rule_EC0192E(
353
359
  PublicDoc cover file. Please delete the cover file from PrivateDoc and upload
354
360
  it again.
355
361
  """
356
- uploadContents = pluginData.getUploadContents(fileSource)
357
- for path, pathInfo in uploadContents.uploadPaths.items():
362
+ uploadContents = pluginData.getUploadContents()
363
+ if uploadContents is None:
364
+ return
365
+ for path, pathInfo in uploadContents.uploadPathsByPath.items():
358
366
  if not pathInfo.isCoverPage:
359
367
  continue
360
368
  # Only applies to PrivateDoc correction reports
@@ -383,8 +391,8 @@ def rule_EC0198E(
383
391
  """
384
392
  fileCounts: dict[Path, int] = defaultdict(int)
385
393
  uploadFilepaths = pluginData.getUploadFilepaths(fileSource)
386
- for path in uploadFilepaths:
387
- if len(path.suffix) == 0:
394
+ for path, zipPath in uploadFilepaths.items():
395
+ if zipPath.is_dir():
388
396
  continue
389
397
  for directory in FILE_COUNT_LIMITS.keys():
390
398
  if directory in path.parents:
@@ -421,10 +429,12 @@ def rule_EC0233E(
421
429
  NOTE: This includes files in subdirectories. For example, PublicDoc/00000000_images/image.png
422
430
  comes before PublicDoc/0000000_header_*.htm
423
431
  """
424
- uploadContents = pluginData.getUploadContents(fileSource)
432
+ uploadContents = pluginData.getUploadContents()
433
+ if uploadContents is None:
434
+ return
425
435
  directories = defaultdict(list)
426
436
  for path in uploadContents.sortedPaths:
427
- pathInfo = uploadContents.uploadPaths[path]
437
+ pathInfo = uploadContents.uploadPathsByPath[path]
428
438
  if pathInfo.isDirectory:
429
439
  continue
430
440
  if pathInfo.reportFolderType in (ReportFolderType.PRIVATE_DOC, ReportFolderType.PUBLIC_DOC):
@@ -462,8 +472,10 @@ def rule_EC0234E(
462
472
  """
463
473
  EDINET.EC0234E: A cover file exists in an unsupported subdirectory.
464
474
  """
465
- uploadContents = pluginData.getUploadContents(fileSource)
466
- for path, pathInfo in uploadContents.uploadPaths.items():
475
+ uploadContents = pluginData.getUploadContents()
476
+ if uploadContents is None:
477
+ return
478
+ for path, pathInfo in uploadContents.uploadPathsByPath.items():
467
479
  if pathInfo.isDirectory:
468
480
  continue
469
481
  if pathInfo.reportFolderType not in (ReportFolderType.PRIVATE_DOC, ReportFolderType.PUBLIC_DOC):
@@ -549,14 +561,16 @@ def rule_EC0349E(
549
561
  EDINET.EC0349E: An unexpected directory or file exists in the XBRL directory.
550
562
  Only PublicDoc, PrivateDoc, or AuditDoc directories may exist beneath the XBRL directory.
551
563
  """
552
- uploadContent = pluginData.getUploadContents(fileSource)
564
+ uploadContents = pluginData.getUploadContents()
565
+ if uploadContents is None:
566
+ return
553
567
  xbrlDirectoryPath = Path('XBRL')
554
568
  allowedPaths = {p.xbrlDirectory for p in (
555
569
  ReportFolderType.AUDIT_DOC,
556
570
  ReportFolderType.PRIVATE_DOC,
557
571
  ReportFolderType.PUBLIC_DOC,
558
572
  )}
559
- for path, pathInfo in uploadContent.uploadPaths.items():
573
+ for path, pathInfo in uploadContents.uploadPathsByPath.items():
560
574
  if path.parent != xbrlDirectoryPath:
561
575
  continue
562
576
  if path not in allowedPaths:
@@ -583,8 +597,10 @@ def rule_EC0352E(
583
597
  """
584
598
  EDINET.EC0352E: An XBRL file with an invalid name exists.
585
599
  """
586
- uploadContent = pluginData.getUploadContents(fileSource)
587
- for path, pathInfo in uploadContent.uploadPaths.items():
600
+ uploadContents = pluginData.getUploadContents()
601
+ if uploadContents is None:
602
+ return
603
+ for path, pathInfo in uploadContents.uploadPathsByPath.items():
588
604
  if (
589
605
  pathInfo.isDirectory or
590
606
  pathInfo.isCorrection or
@@ -603,6 +619,43 @@ def rule_EC0352E(
603
619
  )
604
620
 
605
621
 
622
+ @validation(
623
+ hook=ValidationHook.XBRL_FINALLY,
624
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
625
+ )
626
+ def rules_cover_page(
627
+ pluginData: PluginValidationDataExtension,
628
+ val: ValidateXbrl,
629
+ *args: Any,
630
+ **kwargs: Any,
631
+ ) -> Iterable[Validation]:
632
+ """
633
+ EDINET.EC1000E: Cover page must contain "【表紙】".
634
+ """
635
+ uploadContents = pluginData.getUploadContents(val.modelXbrl)
636
+ if uploadContents is None:
637
+ return
638
+ for url, doc in val.modelXbrl.urlDocs.items():
639
+ path = Path(url)
640
+ pathInfo = uploadContents.uploadPathsByFullPath.get(path)
641
+ if pathInfo is None or not pathInfo.isCoverPage:
642
+ continue
643
+ rootElt = doc.xmlRootElement
644
+ coverPageTextFound = False
645
+ for elt in rootElt.iterdescendants():
646
+ if not coverPageTextFound and elt.text and '【表紙】' in elt.text:
647
+ coverPageTextFound = True
648
+ # TODO: Other checks related to the cover page will be implemented here.
649
+ if not coverPageTextFound:
650
+ yield Validation.error(
651
+ codes='EDINET.EC1000E',
652
+ msg=_("There is no '【表紙】' on the cover page. "
653
+ "File name: '%(file)s'. "
654
+ "Please add '【表紙】' to the relevant file."),
655
+ file=doc.basename,
656
+ )
657
+
658
+
606
659
  @validation(
607
660
  hook=ValidationHook.XBRL_FINALLY,
608
661
  disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
@@ -644,8 +697,13 @@ def rule_uri_references(
644
697
  """
645
698
  EDINET.EC1007E: The URI in the HTML specifies a URL or absolute path.
646
699
  EDINET.EC1013E: The URI in the HTML specifies a path not under a subdirectory.
647
- EDINET.EC1014E: The URI in the HTML specifies a path to a file that doesn't exist.
700
+ EDINET.EC1014E: The URI in the HTML specifies a path to a directory.
701
+ EDINET.EC1021E: The URI in the HTML specifies a path to a file that doesn't exist.
702
+ EDINET.EC1023E: The URI in the HTML specifies a path to a PDF file.
648
703
  """
704
+ uploadContents = pluginData.getUploadContents(val.modelXbrl)
705
+ if uploadContents is None:
706
+ return
649
707
  for uriReference in pluginData.uriReferences:
650
708
  if UrlUtil.isAbsolute(uriReference.attributeValue):
651
709
  yield Validation.error(
@@ -671,10 +729,11 @@ def rule_uri_references(
671
729
  )
672
730
  continue
673
731
  fullPath = Path(uriReference.document.uri).parent / path
674
- if not val.modelXbrl.fileSource.exists(str(fullPath)):
732
+ pathInfo = uploadContents.uploadPathsByFullPath.get(fullPath)
733
+ if pathInfo is not None and pathInfo.isDirectory:
675
734
  yield Validation.error(
676
735
  codes='EDINET.EC1014E',
677
- msg=_("The URI in the HTML specifies a path to a file that doesn't exist. "
736
+ msg=_("The URI in the HTML specifies a path to a directory. "
678
737
  "File name: '%(file)s' (line %(line)s). "
679
738
  "Please update the URI to reference a file."),
680
739
  file=uriReference.document.basename,
@@ -682,6 +741,29 @@ def rule_uri_references(
682
741
  modelObject=uriReference.element,
683
742
  )
684
743
  continue
744
+ if path.suffix.lower() == '.pdf':
745
+ yield Validation.error(
746
+ codes='EDINET.EC1023E',
747
+ msg=_("The URI in the HTML specifies a path to a PDF file. "
748
+ "File name: '%(file)s' (line %(line)s). "
749
+ "Please remove the link from the relevant file."),
750
+ file=uriReference.document.basename,
751
+ line=uriReference.element.sourceline,
752
+ modelObject=uriReference.element,
753
+ )
754
+ continue
755
+ if not val.modelXbrl.fileSource.exists(str(fullPath)):
756
+ yield Validation.error(
757
+ codes='EDINET.EC1021E',
758
+ msg=_("The linked file ('%(path)s') does not exist. "
759
+ "File name: '%(file)s' (line %(line)s). "
760
+ "Please update the URI to reference a file."),
761
+ path=str(path),
762
+ file=uriReference.document.basename,
763
+ line=uriReference.element.sourceline,
764
+ modelObject=uriReference.element,
765
+ )
766
+ continue
685
767
 
686
768
 
687
769
  @validation(
@@ -727,10 +809,12 @@ def rule_EC1017E(
727
809
  """
728
810
  EDINET.EC1017E: There is an unused file.
729
811
  """
730
- uploadContents = pluginData.getUploadContents(fileSource)
812
+ uploadContents = pluginData.getUploadContents()
813
+ if uploadContents is None:
814
+ return
731
815
  existingSubdirectoryFilepaths = {
732
816
  path
733
- for path, pathInfo in uploadContents.uploadPaths.items()
817
+ for path, pathInfo in uploadContents.uploadPathsByPath.items()
734
818
  if pathInfo.isSubdirectory and not pathInfo.isDirectory
735
819
  }
736
820
  usedFilepaths = pluginData.getUsedFilepaths()
@@ -790,6 +874,33 @@ def rule_EC1020E(
790
874
  )
791
875
 
792
876
 
877
+ @validation(
878
+ hook=ValidationHook.XBRL_FINALLY,
879
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
880
+ )
881
+ def rule_EC1031E(
882
+ pluginData: PluginValidationDataExtension,
883
+ val: ValidateXbrl,
884
+ *args: Any,
885
+ **kwargs: Any,
886
+ ) -> Iterable[Validation]:
887
+ """
888
+ EDINET.EC1031E: Prohibited attribute is used in HTML.
889
+ """
890
+ for doc in val.modelXbrl.urlDocs.values():
891
+ for elt, attributeName in pluginData.getProhibitedAttributeElements(doc):
892
+ yield Validation.error(
893
+ codes='EDINET.EC1031E',
894
+ msg=_("Prohibited attribute '%(attributeName)s' is used in HTML. "
895
+ "File name: %(file)s (line %(line)s). "
896
+ "Please correct the tag attributes of the relevant file."),
897
+ attributeName=attributeName,
898
+ file=doc.basename,
899
+ line=elt.sourceline,
900
+ modelObject=elt,
901
+ )
902
+
903
+
793
904
  @validation(
794
905
  hook=ValidationHook.FILESOURCE,
795
906
  disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
@@ -811,7 +922,10 @@ def rule_filenames(
811
922
  than those allowed (alphanumeric characters, '-' and '_').
812
923
  Note: Applies ONLY to files directly beneath non-correction report folders.
813
924
  """
814
- for path, pathInfo in pluginData.getUploadContents(fileSource).uploadPaths.items():
925
+ uploadContents = pluginData.getUploadContents()
926
+ if uploadContents is None:
927
+ return
928
+ for path, pathInfo in uploadContents.uploadPathsByPath.items():
815
929
  isReportFile = (
816
930
  not pathInfo.isAttachment and
817
931
  not pathInfo.isCorrection and
@@ -302,14 +302,14 @@ def rule_ros19(
302
302
  equity_facts = val.modelXbrl.factsByLocalName.get(EQUITY, set())
303
303
  largest_equity_fact = None
304
304
  for e_fact in equity_facts:
305
- if e_fact.xValid >= VALID:
305
+ if e_fact.xValid >= VALID and e_fact.xValue is not None:
306
306
  if largest_equity_fact is None or abs(convertToDecimal(e_fact.xValue)) > abs(convertToDecimal(largest_equity_fact.xValue)):
307
307
  largest_equity_fact = e_fact
308
308
 
309
309
  turnover_facts = val.modelXbrl.factsByLocalName.get(TURNOVER_REVENUE, set())
310
310
  largest_turnover_fact = None
311
311
  for t_fact in turnover_facts:
312
- if t_fact.xValid >= VALID:
312
+ if t_fact.xValid >= VALID and t_fact.xValue is not None:
313
313
  if largest_turnover_fact is None or convertToDecimal(t_fact.xValue) > convertToDecimal(largest_turnover_fact.xValue):
314
314
  largest_turnover_fact = t_fact
315
315
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arelle-release
3
- Version: 2.37.54
3
+ Version: 2.37.56
4
4
  Summary: An open source XBRL platform.
5
5
  Author-email: "arelle.org" <support@arelle.org>
6
6
  License-Expression: Apache-2.0
@@ -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=FN1LhP9ojd2A5WeShvwtVUe-osRf5gvV0EMHpWNZZo0,88752
4
+ arelle/CntlrCmdLine.py,sha256=D5Xs847rIAMsI_fmJC27GSB0jIMGjE3VejpKvMTqncU,88717
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
@@ -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=3qZj-Kmbb3vTvc2klvmeTP01vS1EXql3fFXqQp8yZls,58788
70
+ arelle/Validate.py,sha256=cX1OA3JPiwmjNmxfecZc24GBW1qXdjcEEynJ9F1K7Zg,58764
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=34F-WgCaXY1j36DrZxBRdRZS8LLBVS06qbhNlb5StZA,708
128
+ arelle/_version.py,sha256=fDTO72M4k2Bg8Q2mmpC5_JjetoiOyfrQn3nJSak1r6w,708
129
129
  arelle/typing.py,sha256=PRe-Fxwr2SBqYYUVPCJ3E7ddDX0_oOISNdT5Q97EbRM,1246
130
130
  arelle/api/Session.py,sha256=kgSxS7VckA1sQ7xp0pJiK7IK-vRxAdAZKUo8gEx27s8,7549
131
131
  arelle/config/creationSoftwareNames.json,sha256=5MK7XUjfDJ9OpRCCHXeOErJ1SlTBZji4WEcEOdOacx0,3128
@@ -312,15 +312,15 @@ arelle/plugin/validate/DBA/rules/tm.py,sha256=ui9oKBqlAForwkQ9kk9KBiUogTJE5pv1Rb
312
312
  arelle/plugin/validate/DBA/rules/tr.py,sha256=4TootFjl0HXsKZk1XNBCyj-vnjRs4lg35hfiz_b_4wU,14684
313
313
  arelle/plugin/validate/EBA/__init__.py,sha256=x3zXNcdSDJ3kHfL7kMs0Ve0Vs9oWbzNFVf1TK4Avmy8,45924
314
314
  arelle/plugin/validate/EBA/config.xml,sha256=37wMVUAObK-XEqakqD8zPNog20emYt4a_yfL1AKubF8,2022
315
- arelle/plugin/validate/EDINET/Constants.py,sha256=1qZ9fhD3JSIHxysJ8iIFxmAL60L_g5UX7g7o3vIDX6k,1607
316
- arelle/plugin/validate/EDINET/ControllerPluginData.py,sha256=2hMPxpL0vZqmzcuvPl9o4_g79ZOtJgLI1p_VRW2YYVk,7259
315
+ arelle/plugin/validate/EDINET/Constants.py,sha256=IkJ1R0ZQAs9kUXY8eB7e8azYlC4m500VILrQhniB5Q4,1949
316
+ arelle/plugin/validate/EDINET/ControllerPluginData.py,sha256=zsBOWkSatXza0v_ZB-jIdFLSIj9Btzd50GdNIgVw6Fo,8027
317
317
  arelle/plugin/validate/EDINET/DisclosureSystems.py,sha256=3rKG42Eg-17Xx_KXU_V5yHW6I3LTwQunvf4a44C9k_4,36
318
318
  arelle/plugin/validate/EDINET/ManifestInstance.py,sha256=o6BGlaQHSsn6D0VKH4zn59UscKnjTKlo99kSGfGdYlU,6910
319
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=2PEdDuOn9yIg-_Gf-fK8L6RFW3YVVEYy-7JU0n7VZtw,15433
319
+ arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=CVTtWTYrvC_oeK9iWHLt5l9dN3KmzgLAEZmGdlPqv3g,16362
320
320
  arelle/plugin/validate/EDINET/ReportFolderType.py,sha256=Q-9a-5tJfhK-cjY8WUB2AT1NI-Nn9cFtARVOIJoLRGU,2979
321
321
  arelle/plugin/validate/EDINET/Statement.py,sha256=0Mw5IB7LMtvUZ-2xKZfxmq67xF_dCgJo3eNLweLFRHU,9350
322
- arelle/plugin/validate/EDINET/UploadContents.py,sha256=IKQYl6lXYTfAZKLIDzRRGSRt3FXoL2Eldbx3Dh7T2I4,712
323
- arelle/plugin/validate/EDINET/ValidationPluginExtension.py,sha256=xr4Ft9v-zpwgRuKSjregyHjtUb5KgHqdSx9NKg9UVeg,2528
322
+ arelle/plugin/validate/EDINET/UploadContents.py,sha256=o29mDoX48M3S2jQqrJO4ZaulltAPt4vD-qdsWTMCUPc,1196
323
+ arelle/plugin/validate/EDINET/ValidationPluginExtension.py,sha256=8LNqvXzNaWP54dShEjet5ely4BnM8ByCSyimKpUx3_s,2577
324
324
  arelle/plugin/validate/EDINET/__init__.py,sha256=ECWgqzwHA7MZ3g7SeoFI7ttR9Wq_lywV-TlqeUW_juY,3186
325
325
  arelle/plugin/validate/EDINET/resources/config.xml,sha256=7uT4GcRgk5veMLpFhPPQJxbGKiQvM52P8EMrjn0qd0g,646
326
326
  arelle/plugin/validate/EDINET/resources/edinet-taxonomies.xml,sha256=997I3RGTLg5OY3vn5hQxVFAAxOmDSOYpuyQe6VnWSY0,16285
@@ -328,9 +328,9 @@ arelle/plugin/validate/EDINET/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQ
328
328
  arelle/plugin/validate/EDINET/rules/contexts.py,sha256=KPoyWfRaURvxoGVcWP64mTMTAKPMSmQSX06RClCLddw,7590
329
329
  arelle/plugin/validate/EDINET/rules/edinet.py,sha256=VYrDZaKbsQuQEvOY5F0Pv4Jzk9YZ4iETOkAFOggrhEY,12632
330
330
  arelle/plugin/validate/EDINET/rules/frta.py,sha256=N0YglHYZuLD2IuwE26viR2ViwUYjneBuMFU9vlrS0aQ,7616
331
- arelle/plugin/validate/EDINET/rules/gfm.py,sha256=Elyd0Vqooj_rC0yDWC8NneWCQ_Ckb9IZy1XCn7m1_IE,24389
331
+ arelle/plugin/validate/EDINET/rules/gfm.py,sha256=1SBUQG2M4VOhxp4e2ALnm_Uq8p3g-S0WbKkRCEH-ttw,26769
332
332
  arelle/plugin/validate/EDINET/rules/manifests.py,sha256=MoT9R_a4BzuYdQVbF7RC5wz134Ve68svSdJ3NlpO_AU,4026
333
- arelle/plugin/validate/EDINET/rules/upload.py,sha256=XVLL_txEoYFQznNI8bSjs41dMW9x7SRsj1f1aS0YU-4,35871
333
+ arelle/plugin/validate/EDINET/rules/upload.py,sha256=opGEEFf80RLKk_Z43gkUYHJLBvq_Hd-BMzMMg2Vhnns,40064
334
334
  arelle/plugin/validate/ESEF/Const.py,sha256=JujF_XV-_TNsxjGbF-8SQS4OOZIcJ8zhCMnr-C1O5Ho,22660
335
335
  arelle/plugin/validate/ESEF/Dimensions.py,sha256=MOJM7vwNPEmV5cu-ZzPrhx3347ZvxgD6643OB2HRnIk,10597
336
336
  arelle/plugin/validate/ESEF/Util.py,sha256=QH3btcGqBpr42M7WSKZLSdNXygZaZLfEiEjlxoG21jE,7950
@@ -365,7 +365,7 @@ arelle/plugin/validate/ROS/__init__.py,sha256=KuWg1MHVzA2S6eaHFptvP3Vu_5gQWf3OUY
365
365
  arelle/plugin/validate/ROS/config.xml,sha256=ZCpCFgr1ZAjoUuhb1eRpDnmKrae-sXA9yl6SOWnrfm8,654
366
366
  arelle/plugin/validate/ROS/resources/config.xml,sha256=HXWume5HlrAqOx5AtiWWqgADbRatA8YSfm_JvZGwdgQ,657
367
367
  arelle/plugin/validate/ROS/rules/__init__.py,sha256=wW7BUAIb7sRkOxC1Amc_ZKrz03FM-Qh1TyZe6wxYaAU,1567
368
- arelle/plugin/validate/ROS/rules/ros.py,sha256=Dk5BkfKQYItImdx5FcFvkMWT5BlJ1r_L7Vn-EsCG85A,19870
368
+ arelle/plugin/validate/ROS/rules/ros.py,sha256=yGvvJZJwENgFLpA5r464eez1aHf3pKZm0HyzaWiQn6E,19930
369
369
  arelle/plugin/validate/UK/ValidateUK.py,sha256=h7-tnCubHme8Meaif-o55TV2rCfMWuikfpZCcK6NNDs,56447
370
370
  arelle/plugin/validate/UK/__init__.py,sha256=GT67T_7qpOASzdmgRXFPmLxfPg3Gjubli7luQDK3zck,27462
371
371
  arelle/plugin/validate/UK/config.xml,sha256=mUFhWDfBzGTn7v0ZSmf4HaweQTMJh_4ZcJmD9mzCHrA,1547
@@ -680,9 +680,9 @@ arelle/utils/validate/ValidationUtil.py,sha256=9vmSvShn-EdQy56dfesyV8JjSRVPj7txr
680
680
  arelle/utils/validate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
681
681
  arelle/webserver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
682
682
  arelle/webserver/bottle.py,sha256=P-JECd9MCTNcxCnKoDUvGcoi03ezYVOgoWgv2_uH-6M,362
683
- arelle_release-2.37.54.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
684
- arelle_release-2.37.54.dist-info/METADATA,sha256=LMpvNRlAnrYIwHfxClG8zKapyKs6s17iAqYHgdwE8h4,9327
685
- arelle_release-2.37.54.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
686
- arelle_release-2.37.54.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
687
- arelle_release-2.37.54.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
688
- arelle_release-2.37.54.dist-info/RECORD,,
683
+ arelle_release-2.37.56.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
684
+ arelle_release-2.37.56.dist-info/METADATA,sha256=Wtmg0dACPtcIDHq93LetjVlxqOerxZ1VGQdsa1ZgUds,9327
685
+ arelle_release-2.37.56.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
686
+ arelle_release-2.37.56.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
687
+ arelle_release-2.37.56.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
688
+ arelle_release-2.37.56.dist-info/RECORD,,