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.

Files changed (107) hide show
  1. arelle/CntlrCmdLine.py +10 -1
  2. arelle/ErrorManager.py +14 -5
  3. arelle/ModelObjectFactory.py +18 -2
  4. arelle/Validate.py +4 -0
  5. arelle/_version.py +2 -2
  6. arelle/plugin/validate/DBA/ValidationPluginExtension.py +2 -1
  7. arelle/plugin/validate/EDINET/ControllerPluginData.py +84 -0
  8. arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +0 -114
  9. arelle/plugin/validate/EDINET/UploadContents.py +17 -0
  10. arelle/plugin/validate/EDINET/ValidationPluginExtension.py +8 -2
  11. arelle/plugin/validate/EDINET/__init__.py +5 -0
  12. arelle/plugin/validate/EDINET/rules/upload.py +66 -75
  13. arelle/plugin/validate/NL/ValidationPluginExtension.py +3 -1
  14. arelle/plugin/validate/ROS/ValidationPluginExtension.py +3 -1
  15. arelle/utils/PluginHooks.py +32 -0
  16. arelle/utils/validate/ValidationPlugin.py +54 -8
  17. {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/METADATA +1 -1
  18. {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/RECORD +22 -106
  19. arelle/archive/CustomLogger.py +0 -43
  20. arelle/archive/LoadEFMvalidate.py +0 -32
  21. arelle/archive/LoadSavePreLbCsv.py +0 -26
  22. arelle/archive/LoadValidate.cs +0 -31
  23. arelle/archive/LoadValidate.py +0 -36
  24. arelle/archive/LoadValidateCmdLine.java +0 -69
  25. arelle/archive/LoadValidatePostedZip.java +0 -57
  26. arelle/archive/LoadValidateWebService.java +0 -34
  27. arelle/archive/SaveTableToExelle.py +0 -140
  28. arelle/archive/TR3toTR4.py +0 -88
  29. arelle/archive/plugin/ESEF_2022/__init__.py +0 -47
  30. arelle/archive/plugin/bigInstance.py +0 -394
  31. arelle/archive/plugin/cmdWebServerExtension.py +0 -43
  32. arelle/archive/plugin/crashTest.py +0 -38
  33. arelle/archive/plugin/functionsXmlCreation.py +0 -106
  34. arelle/archive/plugin/hello_i18n.pot +0 -26
  35. arelle/archive/plugin/hello_i18n.py +0 -32
  36. arelle/archive/plugin/importTestChild1.py +0 -21
  37. arelle/archive/plugin/importTestChild2.py +0 -22
  38. arelle/archive/plugin/importTestGrandchild1.py +0 -21
  39. arelle/archive/plugin/importTestGrandchild2.py +0 -21
  40. arelle/archive/plugin/importTestImported1.py +0 -23
  41. arelle/archive/plugin/importTestImported11.py +0 -22
  42. arelle/archive/plugin/importTestParent.py +0 -48
  43. arelle/archive/plugin/instanceInfo.py +0 -306
  44. arelle/archive/plugin/loadFromOIM-2018.py +0 -1282
  45. arelle/archive/plugin/locale/fr/LC_MESSAGES/hello_i18n.po +0 -25
  46. arelle/archive/plugin/objectmaker.py +0 -285
  47. arelle/archive/plugin/packagedImportTest/__init__.py +0 -47
  48. arelle/archive/plugin/packagedImportTest/importTestChild1.py +0 -21
  49. arelle/archive/plugin/packagedImportTest/importTestChild2.py +0 -22
  50. arelle/archive/plugin/packagedImportTest/importTestGrandchild1.py +0 -21
  51. arelle/archive/plugin/packagedImportTest/importTestGrandchild2.py +0 -21
  52. arelle/archive/plugin/packagedImportTest/importTestImported1.py +0 -24
  53. arelle/archive/plugin/packagedImportTest/importTestImported11.py +0 -21
  54. arelle/archive/plugin/packagedImportTest/subdir/importTestImported111.py +0 -21
  55. arelle/archive/plugin/packagedImportTest/subdir/subsubdir/importTestImported1111.py +0 -21
  56. arelle/archive/plugin/sakaCalendar.py +0 -215
  57. arelle/archive/plugin/saveInstanceInfoset.py +0 -121
  58. arelle/archive/plugin/sphinx/FormulaGenerator.py +0 -823
  59. arelle/archive/plugin/sphinx/SphinxContext.py +0 -404
  60. arelle/archive/plugin/sphinx/SphinxEvaluator.py +0 -783
  61. arelle/archive/plugin/sphinx/SphinxMethods.py +0 -1287
  62. arelle/archive/plugin/sphinx/SphinxParser.py +0 -1093
  63. arelle/archive/plugin/sphinx/SphinxValidator.py +0 -163
  64. arelle/archive/plugin/sphinx/US-GAAP Ratios Example.xsr +0 -52
  65. arelle/archive/plugin/sphinx/__init__.py +0 -285
  66. arelle/archive/plugin/streamingExtensions.py +0 -335
  67. arelle/archive/plugin/updateTableLB.py +0 -242
  68. arelle/archive/plugin/validate/SBRnl/CustomLoader.py +0 -19
  69. arelle/archive/plugin/validate/SBRnl/DTS.py +0 -305
  70. arelle/archive/plugin/validate/SBRnl/Dimensions.py +0 -357
  71. arelle/archive/plugin/validate/SBRnl/Document.py +0 -799
  72. arelle/archive/plugin/validate/SBRnl/Filing.py +0 -467
  73. arelle/archive/plugin/validate/SBRnl/__init__.py +0 -75
  74. arelle/archive/plugin/validate/SBRnl/config.xml +0 -26
  75. arelle/archive/plugin/validate/SBRnl/sbr-nl-taxonomies.xml +0 -754
  76. arelle/archive/plugin/validate/USBestPractices.py +0 -570
  77. arelle/archive/plugin/validate/USCorpAction.py +0 -557
  78. arelle/archive/plugin/validate/USSecTagging.py +0 -337
  79. arelle/archive/plugin/validate/XDC/__init__.py +0 -77
  80. arelle/archive/plugin/validate/XDC/config.xml +0 -20
  81. arelle/archive/plugin/validate/XFsyntax/__init__.py +0 -64
  82. arelle/archive/plugin/validate/XFsyntax/xf.py +0 -2227
  83. arelle/archive/plugin/validate/calc2.py +0 -536
  84. arelle/archive/plugin/validateSchemaLxml.py +0 -156
  85. arelle/archive/plugin/validateTableInfoset.py +0 -52
  86. arelle/archive/us-gaap-dei-docType-extraction-frm.xml +0 -90
  87. arelle/archive/us-gaap-dei-ratio-cash-frm.xml +0 -150
  88. arelle/examples/plugin/formulaSuiteConverter.py +0 -212
  89. arelle/examples/plugin/functionsCustom.py +0 -59
  90. arelle/examples/plugin/hello_dolly.py +0 -64
  91. arelle/examples/plugin/multi.py +0 -58
  92. arelle/examples/plugin/rssSaveOim.py +0 -96
  93. arelle/examples/plugin/validate/XYZ/DisclosureSystems.py +0 -2
  94. arelle/examples/plugin/validate/XYZ/PluginValidationDataExtension.py +0 -10
  95. arelle/examples/plugin/validate/XYZ/ValidationPluginExtension.py +0 -49
  96. arelle/examples/plugin/validate/XYZ/__init__.py +0 -75
  97. arelle/examples/plugin/validate/XYZ/resources/config.xml +0 -16
  98. arelle/examples/plugin/validate/XYZ/rules/__init__.py +0 -0
  99. arelle/examples/plugin/validate/XYZ/rules/rules01.py +0 -110
  100. arelle/examples/plugin/validate/XYZ/rules/rules02.py +0 -59
  101. arelle/scripts-macOS/startWebServer.command +0 -3
  102. arelle/scripts-unix/startWebServer.sh +0 -1
  103. arelle/scripts-windows/startWebServer.bat +0 -5
  104. {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/WHEEL +0 -0
  105. {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/entry_points.txt +0 -0
  106. {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/licenses/LICENSE.md +0 -0
  107. {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/top_level.txt +0 -0
arelle/CntlrCmdLine.py CHANGED
@@ -620,7 +620,11 @@ class ParserForDynamicPlugins:
620
620
 
621
621
 
622
622
  def _pluginHasCliOptions(moduleInfo):
623
- return "CntlrCmdLine.Options" in moduleInfo["classMethods"]
623
+ if "CntlrCmdLine.Options" in moduleInfo["classMethods"]:
624
+ return True
625
+ if imports := moduleInfo.get("imports"):
626
+ return any(_pluginHasCliOptions(importedModule) for importedModule in imports)
627
+ return False
624
628
 
625
629
 
626
630
  class CntlrCmdLine(Cntlr.Cntlr):
@@ -1012,6 +1016,11 @@ class CntlrCmdLine(Cntlr.Cntlr):
1012
1016
 
1013
1017
  for pluginXbrlMethod in PluginManager.pluginClassMethods("CntlrCmdLine.Filing.Start"):
1014
1018
  pluginXbrlMethod(self, options, filesource, _entrypointFiles, sourceZipStream=sourceZipStream, responseZipStream=responseZipStream)
1019
+
1020
+ if options.validate:
1021
+ for pluginXbrlMethod in PluginManager.pluginClassMethods("Validate.FileSource"):
1022
+ pluginXbrlMethod(self, filesource, _entrypointFiles)
1023
+
1015
1024
  if len(_entrypointFiles) == 0 and not options.packages:
1016
1025
  if options.entrypointFile:
1017
1026
  msg = _("No XBRL entry points could be loaded from provided file: {}").format(options.entrypointFile)
arelle/ErrorManager.py CHANGED
@@ -204,13 +204,22 @@ class ErrorManager:
204
204
  _arg:ModelObject = arg.modelObject if isinstance(arg, ObjectPropertyViewWrapper) else arg
205
205
  if len(modelObjectArgs) > 1 and getattr(arg,"tag",None) == "instance":
206
206
  continue # skip IXDS top level element
207
- fragmentIdentifier = cast(str, XmlUtil.elementFragmentIdentifier(_arg))
208
- if not hasattr(_arg, 'modelDocument') and _arg.namespaceURI == XbrlConst.svg:
207
+ fragmentIdentifier = "#" + cast(str, XmlUtil.elementFragmentIdentifier(_arg))
208
+ if not hasattr(_arg, 'modelDocument') and _arg.namespaceURI == XbrlConst.svg and len(refs) > 0:
209
209
  # This is an embedded SVG document without its own file.
210
- ref["href"] = "#" + fragmentIdentifier
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 + "#" + fragmentIdentifier
213
- ref["sourceLine"] = _arg.sourceline
221
+ ref["href"] = file + fragmentIdentifier
222
+ ref["sourceLine"] = _arg.sourceline
214
223
  ref["objectId"] = _arg.objectId()
215
224
  if logRefObjectProperties:
216
225
  try:
@@ -68,6 +68,22 @@ LINKBASE = 2
68
68
  VERSIONINGREPORT = 3
69
69
  RSSFEED = 4
70
70
 
71
+ LINK_LOCALNAME_TO_MODEL_CLASS = {
72
+ 'loc': ModelLocator,
73
+ 'label': ModelResource,
74
+ 'reference': ModelResource,
75
+ 'roleType': ModelRoleType,
76
+ 'arcroleType': ModelRoleType,
77
+ } | {
78
+ q.localName: ModelObject
79
+ for q in [
80
+ XbrlConst.qnLinkCalculationArc,
81
+ XbrlConst.qnLinkDefinitionArc,
82
+ XbrlConst.qnLinkPresentationArc,
83
+ XbrlConst.qnLinkReferenceArc,
84
+ ]
85
+ }
86
+
71
87
 
72
88
  class KnownNamespacesModelObjectClassLookup(etree.CustomElementClassLookup):
73
89
  def __init__(self, modelXbrl: ModelXbrl, fallback: etree.ElementClassLookup | None = None) -> None:
@@ -106,8 +122,8 @@ class KnownNamespacesModelObjectClassLookup(etree.CustomElementClassLookup):
106
122
  elif ns == XbrlConst.link:
107
123
  if self.type is None:
108
124
  self.type = LINKBASE
109
- if ln == "roleType" or ln == "arcroleType":
110
- return ModelRoleType
125
+ if modelObjectClass := LINK_LOCALNAME_TO_MODEL_CLASS.get(ln):
126
+ return modelObjectClass
111
127
  elif ns == "http://edgar/2009/conformance":
112
128
  # don't force loading of test schema
113
129
  if ln == "variation":
arelle/Validate.py CHANGED
@@ -398,6 +398,8 @@ class Validate:
398
398
  filesource.select(None) # must select loadable reports (not the taxonomy package itself)
399
399
  elif not filesource.isReportPackage:
400
400
  entrypoints = filesourceEntrypointFiles(filesource)
401
+ for pluginXbrlMethod in pluginClassMethods("Validate.FileSource"):
402
+ pluginXbrlMethod(self.modelXbrl.modelManager.cntlr, filesource, entrypoints)
401
403
  if entrypoints:
402
404
  # resolve an IXDS in entrypoints
403
405
  for pluginXbrlMethod in pluginClassMethods("ModelTestcaseVariation.ArchiveIxds"):
@@ -421,6 +423,8 @@ class Validate:
421
423
  if not reportPackageErrors:
422
424
  assert isinstance(filesource.basefile, str)
423
425
  if entrypoints := filesourceEntrypointFiles(filesource):
426
+ for pluginXbrlMethod in pluginClassMethods("Validate.FileSource"):
427
+ pluginXbrlMethod(self.modelXbrl.modelManager.cntlr, filesource, entrypoints)
424
428
  for pluginXbrlMethod in pluginClassMethods("ModelTestcaseVariation.ArchiveIxds"):
425
429
  pluginXbrlMethod(self, filesource, entrypoints)
426
430
  for entrypoint in entrypoints:
arelle/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '2.37.46'
21
- __version_tuple__ = version_tuple = (2, 37, 46)
20
+ __version__ = version = '2.37.48'
21
+ __version_tuple__ = version_tuple = (2, 37, 48)
@@ -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) -> PluginValidationDataExtension:
39
- disclosureSystem = validateXbrl.disclosureSystem.name
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
  }