arelle-release 2.37.53__py3-none-any.whl → 2.37.55__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 +11 -36
- arelle/CntlrWinMain.py +56 -33
- arelle/ModelDocument.py +4 -1
- arelle/Validate.py +3 -0
- arelle/ViewWinDTS.py +4 -1
- arelle/_version.py +2 -2
- arelle/packages/report/ReportPackage.py +15 -6
- arelle/packages/report/ReportPackageConst.py +0 -1
- arelle/plugin/inlineXbrlDocumentSet.py +18 -2
- arelle/plugin/validate/EDINET/Constants.py +37 -0
- arelle/plugin/validate/EDINET/ControllerPluginData.py +8 -0
- arelle/plugin/validate/EDINET/ManifestInstance.py +1 -1
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +89 -5
- arelle/plugin/validate/EDINET/ValidationPluginExtension.py +11 -0
- arelle/plugin/validate/EDINET/__init__.py +5 -0
- arelle/plugin/validate/EDINET/rules/gfm.py +29 -0
- arelle/plugin/validate/EDINET/rules/upload.py +141 -0
- arelle/utils/EntryPointDetection.py +63 -3
- arelle/utils/PluginHooks.py +94 -0
- arelle/utils/validate/ValidationPlugin.py +21 -1
- {arelle_release-2.37.53.dist-info → arelle_release-2.37.55.dist-info}/METADATA +1 -1
- {arelle_release-2.37.53.dist-info → arelle_release-2.37.55.dist-info}/RECORD +26 -26
- {arelle_release-2.37.53.dist-info → arelle_release-2.37.55.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.53.dist-info → arelle_release-2.37.55.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.53.dist-info → arelle_release-2.37.55.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.37.53.dist-info → arelle_release-2.37.55.dist-info}/top_level.txt +0 -0
arelle/CntlrCmdLine.py
CHANGED
|
@@ -57,8 +57,7 @@ from arelle.RuntimeOptions import RuntimeOptions, RuntimeOptionsException
|
|
|
57
57
|
from arelle.SocketUtils import INTERNET_CONNECTIVITY, OFFLINE
|
|
58
58
|
from arelle.SystemInfo import PlatformOS, getSystemInfo, getSystemWordSize, hasWebServer, isCGI, isGAE
|
|
59
59
|
from arelle.typing import TypeGetText
|
|
60
|
-
from arelle.
|
|
61
|
-
from arelle.utils.EntryPointDetection import filesourceEntrypointFiles
|
|
60
|
+
from arelle.utils.EntryPointDetection import parseEntrypointFileInput
|
|
62
61
|
from arelle.ValidateXbrlDTS import ValidateBaseTaxonomiesMode
|
|
63
62
|
from arelle.WebCache import proxyTuple
|
|
64
63
|
|
|
@@ -978,41 +977,12 @@ class CntlrCmdLine(Cntlr.Cntlr):
|
|
|
978
977
|
if not (options.entrypointFile or sourceZipStream):
|
|
979
978
|
return True # success
|
|
980
979
|
|
|
980
|
+
entrypointParseResult = parseEntrypointFileInput(self, options.entrypointFile, sourceZipStream)
|
|
981
|
+
if not entrypointParseResult.success:
|
|
982
|
+
return False
|
|
983
|
+
filesource = entrypointParseResult.filesource
|
|
984
|
+
_entrypointFiles = entrypointParseResult.entrypointFiles
|
|
981
985
|
success = True
|
|
982
|
-
# entrypointFile may be absent (if input is a POSTED zip or file name ending in .zip)
|
|
983
|
-
# or may be a | separated set of file names
|
|
984
|
-
_entryPoints = []
|
|
985
|
-
_checkIfXmlIsEis = self.modelManager.disclosureSystem and self.modelManager.disclosureSystem.validationType == "EFM"
|
|
986
|
-
if options.entrypointFile:
|
|
987
|
-
_f = options.entrypointFile
|
|
988
|
-
try: # may be a json list
|
|
989
|
-
_entryPoints = json.loads(_f)
|
|
990
|
-
_checkIfXmlIsEis = False # json entry objects never specify an xml EIS archive
|
|
991
|
-
except ValueError as e:
|
|
992
|
-
# is it malformed json?
|
|
993
|
-
if _f.startswith("[{") or _f.endswith("]}") or '"file:"' in _f:
|
|
994
|
-
self.addToLog(_("File name parameter appears to be malformed JSON: {}\n{}").format(e, _f),
|
|
995
|
-
messageCode="FileNameFormatError",
|
|
996
|
-
level=logging.ERROR)
|
|
997
|
-
success = False
|
|
998
|
-
else: # try as file names separated by '|'
|
|
999
|
-
for f in (_f or '').split('|'):
|
|
1000
|
-
if not sourceZipStream and not isHttpUrl(f) and not os.path.isabs(f):
|
|
1001
|
-
f = os.path.normpath(os.path.join(os.getcwd(), f)) # make absolute normed path
|
|
1002
|
-
_entryPoints.append({"file":f})
|
|
1003
|
-
filesource = None # file source for all instances if not None
|
|
1004
|
-
if sourceZipStream:
|
|
1005
|
-
filesource = FileSource.openFileSource(None, self, sourceZipStream)
|
|
1006
|
-
elif len(_entryPoints) == 1 and "file" in _entryPoints[0]: # check if an archive and need to discover entry points (and not IXDS)
|
|
1007
|
-
entryPath = PackageManager.mappedUrl(_entryPoints[0]["file"])
|
|
1008
|
-
filesource = FileSource.openFileSource(entryPath, self, checkIfXmlIsEis=_checkIfXmlIsEis)
|
|
1009
|
-
_entrypointFiles = _entryPoints
|
|
1010
|
-
if filesource and not filesource.selection and not (sourceZipStream and len(_entrypointFiles) > 0):
|
|
1011
|
-
try:
|
|
1012
|
-
filesourceEntrypointFiles(filesource, _entrypointFiles)
|
|
1013
|
-
except Exception as err:
|
|
1014
|
-
self.addToLog(str(err), messageCode="error", level=logging.ERROR)
|
|
1015
|
-
return False
|
|
1016
986
|
|
|
1017
987
|
for pluginXbrlMethod in PluginManager.pluginClassMethods("CntlrCmdLine.Filing.Start"):
|
|
1018
988
|
pluginXbrlMethod(self, options, filesource, _entrypointFiles, sourceZipStream=sourceZipStream, responseZipStream=responseZipStream)
|
|
@@ -1248,6 +1218,11 @@ class CntlrCmdLine(Cntlr.Cntlr):
|
|
|
1248
1218
|
self.modelManager.close(modelDiffReport)
|
|
1249
1219
|
elif modelXbrl:
|
|
1250
1220
|
self.modelManager.close(modelXbrl)
|
|
1221
|
+
|
|
1222
|
+
if options.validate:
|
|
1223
|
+
for pluginXbrlMethod in PluginManager.pluginClassMethods("Validate.Complete"):
|
|
1224
|
+
pluginXbrlMethod(self, filesource)
|
|
1225
|
+
|
|
1251
1226
|
if filesource is not None and not options.keepOpen:
|
|
1252
1227
|
# Archive filesource potentially used by multiple reports may still be open.
|
|
1253
1228
|
filesource.close()
|
arelle/CntlrWinMain.py
CHANGED
|
@@ -5,11 +5,14 @@ See COPYRIGHT.md for copyright information.
|
|
|
5
5
|
'''
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
8
10
|
from arelle import ValidateDuplicateFacts
|
|
9
11
|
import os, sys, subprocess, pickle, time, locale, fnmatch, platform, webbrowser
|
|
10
12
|
import regex as re
|
|
11
13
|
|
|
12
14
|
from arelle.logging.formatters.LogFormatter import logRefsFileLines
|
|
15
|
+
from arelle.utils.EntryPointDetection import parseEntrypointFileInput
|
|
13
16
|
|
|
14
17
|
if sys.platform == 'win32' and getattr(sys, 'frozen', False):
|
|
15
18
|
# need the .dll directory in path to be able to access Tk and Tcl DLLs efore importinng Tk, etc.
|
|
@@ -816,15 +819,22 @@ class CntlrWinMain (Cntlr.Cntlr):
|
|
|
816
819
|
if filename:
|
|
817
820
|
for xbrlLoadedMethod in pluginClassMethods("CntlrWinMain.Xbrl.Open"):
|
|
818
821
|
filename = xbrlLoadedMethod(self, filename) # runs in GUI thread, allows mapping filename, mult return filename
|
|
819
|
-
|
|
822
|
+
entrypointParseResult = parseEntrypointFileInput(self, filename, fallbackSelect=False)
|
|
823
|
+
if not entrypointParseResult.success or entrypointParseResult.filesource is None:
|
|
824
|
+
return
|
|
825
|
+
filesource = entrypointParseResult.filesource
|
|
826
|
+
entrypointFiles = entrypointParseResult.entrypointFiles
|
|
820
827
|
# check for archive files
|
|
821
|
-
filesource = openFileSource(filename, self,
|
|
822
|
-
checkIfXmlIsEis=self.modelManager.disclosureSystem and
|
|
823
|
-
self.modelManager.disclosureSystem.validationType == "EFM")
|
|
824
828
|
if filesource.isArchive:
|
|
825
|
-
if
|
|
829
|
+
if (
|
|
830
|
+
len(entrypointFiles) == 0 and
|
|
831
|
+
not filesource.selection and
|
|
832
|
+
not filesource.isReportPackage
|
|
833
|
+
):
|
|
826
834
|
from arelle import DialogOpenArchive
|
|
827
835
|
filename = DialogOpenArchive.askArchiveFile(self, filesource)
|
|
836
|
+
if filename is not None:
|
|
837
|
+
entrypointFiles.append({"file": filename})
|
|
828
838
|
if filename and filesource.basefile and not isHttpUrl(filesource.basefile):
|
|
829
839
|
self.config["fileOpenDir"] = os.path.dirname(filesource.baseurl)
|
|
830
840
|
filesource.loadTaxonomyPackageMappings() # if a package, load mappings if not loaded yet
|
|
@@ -837,7 +847,7 @@ class CntlrWinMain (Cntlr.Cntlr):
|
|
|
837
847
|
self.updateFileHistory(filename, importToDTS)
|
|
838
848
|
elif len(filename) == 1:
|
|
839
849
|
self.updateFileHistory(filename[0], importToDTS)
|
|
840
|
-
thread = threading.Thread(target=self.backgroundLoadXbrl, args=(filesource,importToDTS,selectTopView), daemon=True).start()
|
|
850
|
+
thread = threading.Thread(target=self.backgroundLoadXbrl, args=(filesource,entrypointFiles,importToDTS,selectTopView), daemon=True).start()
|
|
841
851
|
|
|
842
852
|
def webOpen(self, *ignore):
|
|
843
853
|
if not self.okayToContinue():
|
|
@@ -848,12 +858,24 @@ class CntlrWinMain (Cntlr.Cntlr):
|
|
|
848
858
|
self.updateFileHistory(url, False)
|
|
849
859
|
for xbrlLoadedMethod in pluginClassMethods("CntlrWinMain.Xbrl.Open"):
|
|
850
860
|
url = xbrlLoadedMethod(self, url) # runs in GUI thread, allows mapping url, mult return url
|
|
851
|
-
|
|
852
|
-
if
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
861
|
+
entrypointParseResult = parseEntrypointFileInput(self, url, fallbackSelect=False)
|
|
862
|
+
if not entrypointParseResult.success or entrypointParseResult.filesource is None:
|
|
863
|
+
return
|
|
864
|
+
filesource = entrypointParseResult.filesource
|
|
865
|
+
entrypointFiles = entrypointParseResult.entrypointFiles
|
|
866
|
+
if filesource.isArchive:
|
|
867
|
+
if (
|
|
868
|
+
len(entrypointFiles) == 0 and
|
|
869
|
+
not filesource.selection and
|
|
870
|
+
not filesource.isReportPackage
|
|
871
|
+
):
|
|
872
|
+
from arelle import DialogOpenArchive
|
|
873
|
+
url = DialogOpenArchive.askArchiveFile(self, filesource)
|
|
874
|
+
if url is not None:
|
|
875
|
+
entrypointFiles.append({"file": url})
|
|
876
|
+
self.updateFileHistory(url, False)
|
|
877
|
+
filesource.loadTaxonomyPackageMappings()
|
|
878
|
+
thread = threading.Thread(target=self.backgroundLoadXbrl, args=(filesource,entrypointFiles,False,False), daemon=True).start()
|
|
857
879
|
|
|
858
880
|
def importWebOpen(self, *ignore):
|
|
859
881
|
if not self.modelManager.modelXbrl or self.modelManager.modelXbrl.modelDocument.type not in (
|
|
@@ -867,36 +889,37 @@ class CntlrWinMain (Cntlr.Cntlr):
|
|
|
867
889
|
self.fileOpenFile(url, importToDTS=True)
|
|
868
890
|
|
|
869
891
|
|
|
870
|
-
def backgroundLoadXbrl(self, filesource, importToDTS, selectTopView):
|
|
892
|
+
def backgroundLoadXbrl(self, filesource, entrypointFiles: list[dict[str, Any]], importToDTS, selectTopView):
|
|
871
893
|
startedAt = time.time()
|
|
872
894
|
loadedModels = []
|
|
873
895
|
try:
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
if
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
if modelXbrl:
|
|
893
|
-
loadedModels.append(modelXbrl)
|
|
896
|
+
action = _("preparing entrypoints")
|
|
897
|
+
for pluginXbrlMethod in pluginClassMethods("CntlrWinMain.Filing.Start"):
|
|
898
|
+
pluginXbrlMethod(self, filesource, entrypointFiles)
|
|
899
|
+
for entrypoint in entrypointFiles:
|
|
900
|
+
entrypointFile = entrypoint.get("file", None) if isinstance(entrypoint,dict) else entrypoint
|
|
901
|
+
if filesource and filesource.isArchive:
|
|
902
|
+
filesource.select(entrypointFile)
|
|
903
|
+
else:
|
|
904
|
+
entrypointFile = PackageManager.mappedUrl(entrypointFile)
|
|
905
|
+
filesource = openFileSource(entrypointFile, self)
|
|
906
|
+
if importToDTS:
|
|
907
|
+
action = _("imported")
|
|
908
|
+
profileStat = "import"
|
|
909
|
+
modelXbrl = self.modelManager.modelXbrl
|
|
910
|
+
if modelXbrl:
|
|
911
|
+
ModelDocument.load(modelXbrl, filesource.url, isSupplemental=importToDTS)
|
|
912
|
+
modelXbrl.relationshipSets.clear() # relationships have to be re-cached
|
|
913
|
+
loadedModels.append(modelXbrl)
|
|
894
914
|
else:
|
|
915
|
+
action = _("loaded")
|
|
916
|
+
profileStat = "load"
|
|
895
917
|
modelXbrl = self.modelManager.load(
|
|
896
918
|
filesource,
|
|
897
919
|
_("views loading"),
|
|
898
920
|
# check modified time if GUI-loading from web
|
|
899
921
|
checkModifiedTime=isHttpUrl(filesource.url),
|
|
922
|
+
entrypoint=entrypoint,
|
|
900
923
|
)
|
|
901
924
|
if modelXbrl:
|
|
902
925
|
loadedModels.append(modelXbrl)
|
arelle/ModelDocument.py
CHANGED
|
@@ -446,7 +446,7 @@ def loadSchemalocatedSchema(modelXbrl, element, relativeUrl, namespace, baseUrl)
|
|
|
446
446
|
doc.inDTS = False
|
|
447
447
|
return doc
|
|
448
448
|
|
|
449
|
-
def create(modelXbrl, type, uri, schemaRefs=None, isEntry=False, initialXml=None, initialComment=None, base=None, discover=True, documentEncoding="utf-8", xbrliNamespacePrefix=None) -> ModelDocument:
|
|
449
|
+
def create(modelXbrl, type, uri, schemaRefs=None, isEntry=False, initialXml=None, initialComment=None, base=None, discover=True, documentEncoding="utf-8", xbrliNamespacePrefix=None, entrypoint=None) -> ModelDocument:
|
|
450
450
|
"""Returns a new modelDocument, created from scratch, with any necessary header elements
|
|
451
451
|
|
|
452
452
|
(such as the schema, instance, or RSS feed top level elements)
|
|
@@ -538,6 +538,7 @@ def create(modelXbrl, type, uri, schemaRefs=None, isEntry=False, initialXml=None
|
|
|
538
538
|
modelDocument.isQualifiedElementFormDefault = False
|
|
539
539
|
modelDocument.isQualifiedAttributeFormDefault = False
|
|
540
540
|
modelDocument.definesUTR = False
|
|
541
|
+
modelDocument.entrypoint = entrypoint
|
|
541
542
|
return modelDocument
|
|
542
543
|
|
|
543
544
|
class Type:
|
|
@@ -727,10 +728,12 @@ class ModelDocument:
|
|
|
727
728
|
# The document encoding. The XML declaration is stripped from the document
|
|
728
729
|
# before lxml parses the document making the lxml DocInfo encoding unreliable.
|
|
729
730
|
documentEncoding: str
|
|
731
|
+
entrypoint: dict[str, Any] | None
|
|
730
732
|
xmlRootElement: Any
|
|
731
733
|
targetXbrlRootElement: ModelObject
|
|
732
734
|
|
|
733
735
|
def __init__(self, modelXbrl, type, uri, filepath, xmlDocument):
|
|
736
|
+
self.entrypoint = None
|
|
734
737
|
self.modelXbrl = modelXbrl
|
|
735
738
|
self.skipDTS = modelXbrl.skipDTS
|
|
736
739
|
self.type = type
|
arelle/Validate.py
CHANGED
|
@@ -304,6 +304,7 @@ class Validate:
|
|
|
304
304
|
def _testcaseLoadReadMeFirstUri(self, testcase, modelTestcaseVariation, index, readMeFirstUri, resultIsVersioningReport, resultIsTaxonomyPackage, inputDTSes, errorCaptureLevel, baseForElement, parameters):
|
|
305
305
|
preLoadingErrors = [] # accumulate pre-loading errors, such as during taxonomy package loading
|
|
306
306
|
loadedModels = []
|
|
307
|
+
filesource = None
|
|
307
308
|
readMeFirstElements = modelTestcaseVariation.readMeFirstElements
|
|
308
309
|
expectTaxonomyPackage = (index < len(readMeFirstElements) and
|
|
309
310
|
readMeFirstElements[index] is not None and
|
|
@@ -492,6 +493,8 @@ class Validate:
|
|
|
492
493
|
_("Testcase variation validation exception: %(error)s, instance: %(instance)s"),
|
|
493
494
|
modelXbrl=model, instance=model.modelDocument.basename, error=err, exc_info=(type(err) is not AssertionError))
|
|
494
495
|
model.hasFormulae = _hasFormulae
|
|
496
|
+
for pluginXbrlMethod in pluginClassMethods("Validate.Complete"):
|
|
497
|
+
pluginXbrlMethod(self.modelXbrl.modelManager.cntlr, filesource)
|
|
495
498
|
errors = [error for model in loadedModels for error in model.errors]
|
|
496
499
|
for err in preLoadingErrors:
|
|
497
500
|
if err not in errors:
|
arelle/ViewWinDTS.py
CHANGED
|
@@ -30,7 +30,10 @@ class ViewDTS(ViewWinTree.ViewTree):
|
|
|
30
30
|
|
|
31
31
|
def viewDtsElement(self, modelDocument, parentNode, n, parents, siblings):
|
|
32
32
|
if modelDocument.type == ModelDocument.Type.INLINEXBRLDOCUMENTSET:
|
|
33
|
-
|
|
33
|
+
if modelDocument.entrypoint is not None and "id" in modelDocument.entrypoint:
|
|
34
|
+
text = f"{modelDocument.entrypoint['id']} (IXDS)"
|
|
35
|
+
else:
|
|
36
|
+
text = "Inline XBRL Document Set" # no file name or ID to display
|
|
34
37
|
else:
|
|
35
38
|
text = "{0} - {1}".format(os.path.basename(modelDocument.uri), modelDocument.gettype())
|
|
36
39
|
node = self.treeView.insert(parentNode, "end",
|
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.
|
|
32
|
-
__version_tuple__ = version_tuple = (2, 37,
|
|
31
|
+
__version__ = version = '2.37.55'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 37, 55)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -19,13 +19,22 @@ if TYPE_CHECKING:
|
|
|
19
19
|
from arelle.FileSource import FileSource
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
def pathsContainReportsDirectory(paths: set[str], topLevelDir: str) -> bool:
|
|
23
|
+
reportDirectory = f"{topLevelDir}/{Const.REPORTS_DIRECTORY}"
|
|
24
|
+
if reportDirectory in paths:
|
|
25
|
+
return True
|
|
26
|
+
reportDirectory = f"{reportDirectory}/"
|
|
27
|
+
return any(path.startswith(reportDirectory) for path in paths)
|
|
28
|
+
|
|
22
29
|
def getReportPackageTopLevelDirectory(filesource: FileSource) -> str | None:
|
|
23
30
|
packageEntries = set(filesource.dir or [])
|
|
24
|
-
potentialTopLevelReportDirs =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
potentialTopLevelReportDirs = set()
|
|
32
|
+
for topLevelDir in PackageUtils.getPackageTopLevelDirectories(filesource):
|
|
33
|
+
if (
|
|
34
|
+
f"{topLevelDir}/{Const.REPORT_PACKAGE_FILE}" in packageEntries or
|
|
35
|
+
pathsContainReportsDirectory(packageEntries, topLevelDir)
|
|
36
|
+
):
|
|
37
|
+
potentialTopLevelReportDirs.add(topLevelDir)
|
|
29
38
|
if len(potentialTopLevelReportDirs) == 1:
|
|
30
39
|
return next(iter(potentialTopLevelReportDirs))
|
|
31
40
|
return None
|
|
@@ -98,7 +107,7 @@ def getAllReportEntries(filesource: FileSource, stld: str | None) -> list[Report
|
|
|
98
107
|
if not isinstance(filesource.fs, zipfile.ZipFile):
|
|
99
108
|
return None
|
|
100
109
|
entries = [f.orig_filename for f in filesource.fs.infolist()]
|
|
101
|
-
if not
|
|
110
|
+
if not pathsContainReportsDirectory(set(entries), stld):
|
|
102
111
|
return None
|
|
103
112
|
topReportEntries = []
|
|
104
113
|
entriesBySubDir = defaultdict(list)
|
|
@@ -36,7 +36,6 @@ REPORT_PACKAGE_EXTENSIONS = frozenset(
|
|
|
36
36
|
|
|
37
37
|
REPORT_PACKAGE_FILE = f"{META_INF_DIRECTORY}/reportPackage.json"
|
|
38
38
|
REPORTS_DIRECTORY = "reports"
|
|
39
|
-
REPORT_PACKAGE_PATHS = [REPORT_PACKAGE_FILE, f"{REPORTS_DIRECTORY}/"]
|
|
40
39
|
|
|
41
40
|
XBRL_2_1_REPORT_FILE_EXTENSION = ".xbrl"
|
|
42
41
|
JSON_REPORT_FILE_EXTENSION = ".json"
|
|
@@ -248,7 +248,7 @@ def inlineXbrlDocumentSetLoader(modelXbrl, normalizedUri, filepath, isEntry=Fals
|
|
|
248
248
|
for url in modelXbrl.ixdsDocUrls:
|
|
249
249
|
xml.append("<instance>{}</instance>\n".format(url))
|
|
250
250
|
xml.append("</instances>\n")
|
|
251
|
-
ixdocset = create(modelXbrl, Type.INLINEXBRLDOCUMENTSET, docsetUrl, isEntry=True, initialXml="".join(xml))
|
|
251
|
+
ixdocset = create(modelXbrl, Type.INLINEXBRLDOCUMENTSET, docsetUrl, isEntry=True, initialXml="".join(xml), entrypoint=kwargs.get('entrypoint'))
|
|
252
252
|
ixdocset.type = Type.INLINEXBRLDOCUMENTSET
|
|
253
253
|
ixdocset.targetDocumentPreferredFilename = None # possibly no inline docs in this doc set
|
|
254
254
|
for i, elt in enumerate(ixdocset.xmlRootElement.iter(tag="instance")):
|
|
@@ -739,7 +739,22 @@ def commandLineOptionExtender(parser, *args, **kwargs):
|
|
|
739
739
|
def commandLineFilingStart(cntlr, options, filesource, entrypointFiles, *args, **kwargs):
|
|
740
740
|
global skipExpectedInstanceComparison
|
|
741
741
|
skipExpectedInstanceComparison = getattr(options, "skipExpectedInstanceComparison", False)
|
|
742
|
-
|
|
742
|
+
filingStart(
|
|
743
|
+
cntlr,
|
|
744
|
+
entrypointFiles,
|
|
745
|
+
getattr(options, "inlineTarget", None),
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
def guiFilingStart(cntlr, filesource, entrypointFiles, *args, **kwargs):
|
|
749
|
+
global skipExpectedInstanceComparison
|
|
750
|
+
skipExpectedInstanceComparison = False
|
|
751
|
+
filingStart(
|
|
752
|
+
cntlr,
|
|
753
|
+
entrypointFiles,
|
|
754
|
+
inlineTarget=None
|
|
755
|
+
)
|
|
756
|
+
|
|
757
|
+
def filingStart(cntlr, entrypointFiles, inlineTarget):
|
|
743
758
|
if inlineTarget:
|
|
744
759
|
if isinstance(entrypointFiles, dict):
|
|
745
760
|
entrypointFiles = [entrypointFiles]
|
|
@@ -1018,6 +1033,7 @@ __pluginInfo__ = {
|
|
|
1018
1033
|
'InlineDocumentSet.Discovery': inlineDocsetDiscovery,
|
|
1019
1034
|
'InlineDocumentSet.Url.Separator': inlineDocsetUrlSeparator,
|
|
1020
1035
|
'InlineDocumentSet.CreateTargetInstance': createTargetInstance,
|
|
1036
|
+
'CntlrWinMain.Filing.Start': guiFilingStart,
|
|
1021
1037
|
'CntlrWinMain.Menu.File.Open': fileOpenMenuEntender,
|
|
1022
1038
|
'CntlrWinMain.Menu.Tools': saveTargetDocumentMenuEntender,
|
|
1023
1039
|
'CntlrCmdLine.Options': commandLineOptionExtender,
|
|
@@ -39,3 +39,40 @@ qnEdinetManifestTocComposition = qname("{http://disclosure.edinet-fsa.go.jp/2013
|
|
|
39
39
|
xhtmlDtdExtension = "xhtml1-strict-ix.dtd"
|
|
40
40
|
|
|
41
41
|
COVER_PAGE_FILENAME_PREFIX = "0000000_header_"
|
|
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
|
+
|
|
64
|
+
PROHIBITED_HTML_TAGS = frozenset({
|
|
65
|
+
'applet',
|
|
66
|
+
'embed',
|
|
67
|
+
'form',
|
|
68
|
+
'frame',
|
|
69
|
+
'frameset',
|
|
70
|
+
'iframe',
|
|
71
|
+
'input',
|
|
72
|
+
'object',
|
|
73
|
+
'plaintext',
|
|
74
|
+
'pre',
|
|
75
|
+
'script',
|
|
76
|
+
'select',
|
|
77
|
+
'textarea',
|
|
78
|
+
})
|
|
@@ -27,10 +27,12 @@ _: TypeGetText
|
|
|
27
27
|
@dataclass
|
|
28
28
|
class ControllerPluginData(PluginData):
|
|
29
29
|
_manifestInstancesById: dict[str, ManifestInstance]
|
|
30
|
+
_usedFilepaths: set[Path]
|
|
30
31
|
|
|
31
32
|
def __init__(self, name: str):
|
|
32
33
|
super().__init__(name)
|
|
33
34
|
self._manifestInstancesById = {}
|
|
35
|
+
self._usedFilepaths = set()
|
|
34
36
|
|
|
35
37
|
def __hash__(self) -> int:
|
|
36
38
|
return id(self)
|
|
@@ -120,6 +122,9 @@ class ControllerPluginData(PluginData):
|
|
|
120
122
|
if not i.is_dir()
|
|
121
123
|
}
|
|
122
124
|
|
|
125
|
+
def getUsedFilepaths(self) -> frozenset[Path]:
|
|
126
|
+
return frozenset(self._usedFilepaths)
|
|
127
|
+
|
|
123
128
|
@lru_cache(1)
|
|
124
129
|
def isUpload(self, fileSource: FileSource) -> bool:
|
|
125
130
|
fileSource.open() # Make sure file source is open
|
|
@@ -165,6 +170,9 @@ class ControllerPluginData(PluginData):
|
|
|
165
170
|
break
|
|
166
171
|
return matchedInstance
|
|
167
172
|
|
|
173
|
+
def addUsedFilepath(self, path: Path) -> None:
|
|
174
|
+
self._usedFilepaths.add(path)
|
|
175
|
+
|
|
168
176
|
@staticmethod
|
|
169
177
|
def get(cntlr: Cntlr, name: str) -> ControllerPluginData:
|
|
170
178
|
controllerPluginData = cntlr.getPluginData(name)
|
|
@@ -7,15 +7,18 @@ from collections import defaultdict
|
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from decimal import Decimal
|
|
9
9
|
from functools import lru_cache
|
|
10
|
-
from
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from lxml.etree import DTD, XML, _ElementTree, _Comment, _ProcessingInstruction
|
|
11
13
|
from operator import attrgetter
|
|
12
14
|
from typing import Callable, Hashable, Iterable, cast
|
|
13
15
|
|
|
14
16
|
import os
|
|
15
17
|
import regex
|
|
16
18
|
|
|
19
|
+
from arelle import UrlUtil
|
|
17
20
|
from arelle.LinkbaseType import LinkbaseType
|
|
18
|
-
from arelle.ModelDocument import Type as ModelDocumentType
|
|
21
|
+
from arelle.ModelDocument import Type as ModelDocumentType, ModelDocument
|
|
19
22
|
from arelle.ModelDtsObject import ModelConcept
|
|
20
23
|
from arelle.ModelInstanceObject import ModelFact, ModelUnit, ModelContext, ModelInlineFact
|
|
21
24
|
from arelle.ModelObject import ModelObject
|
|
@@ -23,10 +26,12 @@ from arelle.ModelValue import QName, qname
|
|
|
23
26
|
from arelle.ModelXbrl import ModelXbrl
|
|
24
27
|
from arelle.PrototypeDtsObject import LinkPrototype
|
|
25
28
|
from arelle.ValidateDuplicateFacts import getDeduplicatedFacts, DeduplicationType
|
|
29
|
+
from arelle.ValidateXbrl import ValidateXbrl
|
|
30
|
+
from arelle.XhtmlValidate import htmlEltUriAttrs
|
|
26
31
|
from arelle.XmlValidate import VALID
|
|
27
32
|
from arelle.typing import TypeGetText
|
|
28
33
|
from arelle.utils.PluginData import PluginData
|
|
29
|
-
from .Constants import CORPORATE_FORMS, FormType, xhtmlDtdExtension
|
|
34
|
+
from .Constants import CORPORATE_FORMS, FormType, xhtmlDtdExtension, PROHIBITED_HTML_TAGS, PROHIBITED_HTML_ATTRIBUTES
|
|
30
35
|
from .ControllerPluginData import ControllerPluginData
|
|
31
36
|
from .ManifestInstance import ManifestInstance
|
|
32
37
|
from .Statement import Statement, STATEMENTS, BalanceSheet, StatementInstance, StatementType
|
|
@@ -37,6 +42,14 @@ _: TypeGetText
|
|
|
37
42
|
_DEBIT_QNAME_PATTERN = regex.compile('.*(Liability|Liabilities|Equity)')
|
|
38
43
|
|
|
39
44
|
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
class UriReference:
|
|
47
|
+
attributeName: str
|
|
48
|
+
attributeValue: str
|
|
49
|
+
document: ModelDocument
|
|
50
|
+
element: ModelObject
|
|
51
|
+
|
|
52
|
+
|
|
40
53
|
@dataclass
|
|
41
54
|
class PluginValidationDataExtension(PluginData):
|
|
42
55
|
accountingStandardsDeiQn: QName
|
|
@@ -51,9 +64,9 @@ class PluginValidationDataExtension(PluginData):
|
|
|
51
64
|
|
|
52
65
|
contextIdPattern: regex.Pattern[str]
|
|
53
66
|
|
|
54
|
-
|
|
67
|
+
_uriReferences: list[UriReference]
|
|
55
68
|
|
|
56
|
-
def __init__(self, name: str):
|
|
69
|
+
def __init__(self, name: str, validateXbrl: ValidateXbrl):
|
|
57
70
|
super().__init__(name)
|
|
58
71
|
jpcrpEsrNamespace = "http://disclosure.edinet-fsa.go.jp/taxonomy/jpcrp-esr/2024-11-01/jpcrp-esr_cor"
|
|
59
72
|
self.jpcrpNamespace = 'http://disclosure.edinet-fsa.go.jp/taxonomy/jpcrp/2024-11-01/jpcrp_cor'
|
|
@@ -74,6 +87,9 @@ class PluginValidationDataExtension(PluginData):
|
|
|
74
87
|
|
|
75
88
|
self.contextIdPattern = regex.compile(r'(Prior[1-9]Year|CurrentYear|Prior[1-9]Interim|Interim)(Duration|Instant)')
|
|
76
89
|
|
|
90
|
+
self._uriReferences = []
|
|
91
|
+
self._initialize(validateXbrl.modelXbrl)
|
|
92
|
+
|
|
77
93
|
# Identity hash for caching.
|
|
78
94
|
def __hash__(self) -> int:
|
|
79
95
|
return id(self)
|
|
@@ -93,6 +109,28 @@ class PluginValidationDataExtension(PluginData):
|
|
|
93
109
|
contextIsConsolidated = memberValue != self.nonConsolidatedMemberQn
|
|
94
110
|
return bool(statement.isConsolidated == contextIsConsolidated)
|
|
95
111
|
|
|
112
|
+
def _initialize(self, modelXbrl: ModelXbrl) -> None:
|
|
113
|
+
if not isinstance(modelXbrl.fileSource.basefile, str):
|
|
114
|
+
return
|
|
115
|
+
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
116
|
+
basePath = Path(modelXbrl.fileSource.basefile)
|
|
117
|
+
for uri, doc in modelXbrl.urlDocs.items():
|
|
118
|
+
docPath = Path(uri)
|
|
119
|
+
if not docPath.is_relative_to(basePath):
|
|
120
|
+
continue
|
|
121
|
+
controllerPluginData.addUsedFilepath(docPath.relative_to(basePath))
|
|
122
|
+
for elt, name, value in self.getUriAttributeValues(doc):
|
|
123
|
+
self._uriReferences.append(UriReference(
|
|
124
|
+
attributeName=name,
|
|
125
|
+
attributeValue=value,
|
|
126
|
+
document=doc,
|
|
127
|
+
element=elt,
|
|
128
|
+
))
|
|
129
|
+
fullPath = Path(doc.uri).parent / value
|
|
130
|
+
if fullPath.is_relative_to(basePath):
|
|
131
|
+
fileSourcePath = fullPath.relative_to(basePath)
|
|
132
|
+
controllerPluginData.addUsedFilepath(fileSourcePath)
|
|
133
|
+
|
|
96
134
|
def _isDebitConcept(self, concept: ModelConcept) -> bool:
|
|
97
135
|
"""
|
|
98
136
|
:return: Whether the given concept is a debit concept.
|
|
@@ -201,6 +239,10 @@ class PluginValidationDataExtension(PluginData):
|
|
|
201
239
|
if (statementInstance := self.getStatementInstance(modelXbrl, statement)) is not None
|
|
202
240
|
]
|
|
203
241
|
|
|
242
|
+
@property
|
|
243
|
+
def uriReferences(self) -> list[UriReference]:
|
|
244
|
+
return self._uriReferences
|
|
245
|
+
|
|
204
246
|
@lru_cache(1)
|
|
205
247
|
def getDeduplicatedFacts(self, modelXbrl: ModelXbrl) -> list[ModelFact]:
|
|
206
248
|
return getDeduplicatedFacts(modelXbrl, DeduplicationType.CONSISTENT_PAIRS)
|
|
@@ -259,6 +301,44 @@ class PluginValidationDataExtension(PluginData):
|
|
|
259
301
|
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
260
302
|
return controllerPluginData.matchManifestInstance(modelXbrl.ixdsDocUrls)
|
|
261
303
|
|
|
304
|
+
@lru_cache(1)
|
|
305
|
+
def getProhibitedAttributeElements(self, modelDocument: ModelDocument) -> list[tuple[ModelObject, str]]:
|
|
306
|
+
results: list[tuple[ModelObject, str]] = []
|
|
307
|
+
if modelDocument.type not in (ModelDocumentType.INLINEXBRL, ModelDocumentType.HTML):
|
|
308
|
+
return results
|
|
309
|
+
for elt in modelDocument.xmlRootElement.iter():
|
|
310
|
+
if not isinstance(elt, ModelObject):
|
|
311
|
+
continue
|
|
312
|
+
for attributeName in elt.attrib.keys():
|
|
313
|
+
if attributeName in PROHIBITED_HTML_ATTRIBUTES:
|
|
314
|
+
results.append((elt, str(attributeName)))
|
|
315
|
+
return results
|
|
316
|
+
|
|
317
|
+
@lru_cache(1)
|
|
318
|
+
def getProhibitedTagElements(self, modelDocument: ModelDocument) -> list[ModelObject]:
|
|
319
|
+
elts: list[ModelObject] = []
|
|
320
|
+
if modelDocument.type not in (ModelDocumentType.INLINEXBRL, ModelDocumentType.HTML):
|
|
321
|
+
return elts
|
|
322
|
+
for elt in modelDocument.xmlRootElement.iter():
|
|
323
|
+
if not isinstance(elt, ModelObject):
|
|
324
|
+
continue
|
|
325
|
+
tag = elt.qname.localName
|
|
326
|
+
if tag in PROHIBITED_HTML_TAGS:
|
|
327
|
+
elts.append(elt)
|
|
328
|
+
return elts
|
|
329
|
+
|
|
330
|
+
@lru_cache(1)
|
|
331
|
+
def getUriAttributeValues(self, modelDocument: ModelDocument) -> list[tuple[ModelObject, str, str]]:
|
|
332
|
+
results: list[tuple[ModelObject, str, str]] = []
|
|
333
|
+
if modelDocument.type not in (ModelDocumentType.INLINEXBRL, ModelDocumentType.HTML):
|
|
334
|
+
return results
|
|
335
|
+
for elt in modelDocument.xmlRootElement.iter():
|
|
336
|
+
for name in htmlEltUriAttrs.get(elt.localName, ()):
|
|
337
|
+
value = elt.get(name)
|
|
338
|
+
if value is not None:
|
|
339
|
+
results.append((elt, name, value))
|
|
340
|
+
return results
|
|
341
|
+
|
|
262
342
|
def hasValidNonNilFact(self, modelXbrl: ModelXbrl, qname: QName) -> bool:
|
|
263
343
|
return any(True for fact in self.iterValidNonNilFacts(modelXbrl, qname))
|
|
264
344
|
|
|
@@ -270,3 +350,7 @@ class PluginValidationDataExtension(PluginData):
|
|
|
270
350
|
for fact in facts:
|
|
271
351
|
if fact.xValid >= VALID and not fact.isNil:
|
|
272
352
|
yield fact
|
|
353
|
+
|
|
354
|
+
def addUsedFilepath(self, modelXbrl: ModelXbrl, path: Path) -> None:
|
|
355
|
+
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
356
|
+
controllerPluginData.addUsedFilepath(path)
|
|
@@ -3,6 +3,7 @@ See COPYRIGHT.md for copyright information.
|
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
+
import logging
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
8
9
|
from arelle.Cntlr import Cntlr
|
|
@@ -26,6 +27,15 @@ class ValidationPluginExtension(ValidationPlugin):
|
|
|
26
27
|
if len(instances) == 0:
|
|
27
28
|
return None
|
|
28
29
|
assert filesource.cntlr is not None
|
|
30
|
+
filesource.cntlr.addToLog(
|
|
31
|
+
_("EDINET manifest(s) detected (%(manifests)s). Loading %(count)s instances (%(instances)s)."),
|
|
32
|
+
messageCode="info",
|
|
33
|
+
messageArgs={
|
|
34
|
+
"manifests": ', '.join(instance.type for instance in instances),
|
|
35
|
+
"count": len(instances),
|
|
36
|
+
"instances": ', '.join(instance.id for instance in instances),
|
|
37
|
+
}, level=logging.INFO
|
|
38
|
+
)
|
|
29
39
|
pluginData = ControllerPluginData.get(filesource.cntlr, self.name)
|
|
30
40
|
entrypointFiles = []
|
|
31
41
|
for instance in instances:
|
|
@@ -49,4 +59,5 @@ class ValidationPluginExtension(ValidationPlugin):
|
|
|
49
59
|
raise ValueError(f'Invalid EDINET disclosure system: {disclosureSystem}')
|
|
50
60
|
return PluginValidationDataExtension(
|
|
51
61
|
self.name,
|
|
62
|
+
validateXbrl
|
|
52
63
|
)
|
|
@@ -59,6 +59,10 @@ def loggingSeverityReleveler(modelXbrl: ModelXbrl, level: str, messageCode: str,
|
|
|
59
59
|
return level, messageCode
|
|
60
60
|
|
|
61
61
|
|
|
62
|
+
def validateComplete(*args: Any, **kwargs: Any) -> None:
|
|
63
|
+
return validationPlugin.validateComplete(*args, **kwargs)
|
|
64
|
+
|
|
65
|
+
|
|
62
66
|
def validateFileSource(*args: Any, **kwargs: Any) -> None:
|
|
63
67
|
return validationPlugin.validateFileSource(*args, **kwargs)
|
|
64
68
|
|
|
@@ -83,6 +87,7 @@ __pluginInfo__ = {
|
|
|
83
87
|
"DisclosureSystem.ConfigURL": disclosureSystemConfigURL,
|
|
84
88
|
"FileSource.EntrypointFiles": fileSourceEntrypointFiles,
|
|
85
89
|
"Logging.Severity.Releveler": loggingSeverityReleveler,
|
|
90
|
+
"Validate.Complete": validateComplete,
|
|
86
91
|
"Validate.FileSource": validateFileSource,
|
|
87
92
|
"Validate.XBRL.Finally": validateXbrlFinally,
|
|
88
93
|
"ValidateFormula.Finished": validateFinally,
|
|
@@ -641,3 +641,32 @@ 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
|
+
)
|
|
@@ -10,6 +10,7 @@ from typing import Any, Iterable, TYPE_CHECKING
|
|
|
10
10
|
|
|
11
11
|
import regex
|
|
12
12
|
|
|
13
|
+
from arelle import UrlUtil
|
|
13
14
|
from arelle.Cntlr import Cntlr
|
|
14
15
|
from arelle.FileSource import FileSource
|
|
15
16
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
@@ -602,6 +603,87 @@ def rule_EC0352E(
|
|
|
602
603
|
)
|
|
603
604
|
|
|
604
605
|
|
|
606
|
+
@validation(
|
|
607
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
608
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
609
|
+
)
|
|
610
|
+
def rule_EC1006E(
|
|
611
|
+
pluginData: PluginValidationDataExtension,
|
|
612
|
+
val: ValidateXbrl,
|
|
613
|
+
*args: Any,
|
|
614
|
+
**kwargs: Any,
|
|
615
|
+
) -> Iterable[Validation]:
|
|
616
|
+
"""
|
|
617
|
+
EDINET.EC1006E: Prohibited tag is used in HTML.
|
|
618
|
+
"""
|
|
619
|
+
for doc in val.modelXbrl.urlDocs.values():
|
|
620
|
+
for elt in pluginData.getProhibitedTagElements(doc):
|
|
621
|
+
yield Validation.error(
|
|
622
|
+
codes='EDINET.EC1006E',
|
|
623
|
+
msg=_("Prohibited tag (%(tag)s) is used in HTML. File name: %(file)s (line %(line)s). "
|
|
624
|
+
"Please correct the prohibited tags for the relevant files. "
|
|
625
|
+
"For information on prohibited tags, please refer to \"4-1-4 Prohibited Rules\" "
|
|
626
|
+
"in the Validation Guidelines."),
|
|
627
|
+
tag=elt.qname.localName,
|
|
628
|
+
file=doc.basename,
|
|
629
|
+
line=elt.sourceline,
|
|
630
|
+
modelObject=elt,
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
@validation(
|
|
635
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
636
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
637
|
+
)
|
|
638
|
+
def rule_uri_references(
|
|
639
|
+
pluginData: PluginValidationDataExtension,
|
|
640
|
+
val: ValidateXbrl,
|
|
641
|
+
*args: Any,
|
|
642
|
+
**kwargs: Any,
|
|
643
|
+
) -> Iterable[Validation]:
|
|
644
|
+
"""
|
|
645
|
+
EDINET.EC1007E: The URI in the HTML specifies a URL or absolute path.
|
|
646
|
+
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.
|
|
648
|
+
"""
|
|
649
|
+
for uriReference in pluginData.uriReferences:
|
|
650
|
+
if UrlUtil.isAbsolute(uriReference.attributeValue):
|
|
651
|
+
yield Validation.error(
|
|
652
|
+
codes='EDINET.EC1007E',
|
|
653
|
+
msg=_("The URI in the HTML specifies a URL or absolute path. "
|
|
654
|
+
"File name: '%(file)s' (line %(line)s). "
|
|
655
|
+
"Please change the links in the files to relative paths."),
|
|
656
|
+
file=uriReference.document.basename,
|
|
657
|
+
line=uriReference.element.sourceline,
|
|
658
|
+
modelObject=uriReference.element,
|
|
659
|
+
)
|
|
660
|
+
continue
|
|
661
|
+
path = Path(uriReference.attributeValue)
|
|
662
|
+
if len(path.parts) < 2:
|
|
663
|
+
yield Validation.error(
|
|
664
|
+
codes='EDINET.EC1013E',
|
|
665
|
+
msg=_("The URI in the HTML specifies a path not under a subdirectory. "
|
|
666
|
+
"File name: '%(file)s' (line %(line)s). "
|
|
667
|
+
"Please move the referenced file into a subfolder, or correct the URI."),
|
|
668
|
+
file=uriReference.document.basename,
|
|
669
|
+
line=uriReference.element.sourceline,
|
|
670
|
+
modelObject=uriReference.element,
|
|
671
|
+
)
|
|
672
|
+
continue
|
|
673
|
+
fullPath = Path(uriReference.document.uri).parent / path
|
|
674
|
+
if not val.modelXbrl.fileSource.exists(str(fullPath)):
|
|
675
|
+
yield Validation.error(
|
|
676
|
+
codes='EDINET.EC1014E',
|
|
677
|
+
msg=_("The URI in the HTML specifies a path to a file that doesn't exist. "
|
|
678
|
+
"File name: '%(file)s' (line %(line)s). "
|
|
679
|
+
"Please update the URI to reference a file."),
|
|
680
|
+
file=uriReference.document.basename,
|
|
681
|
+
line=uriReference.element.sourceline,
|
|
682
|
+
modelObject=uriReference.element,
|
|
683
|
+
)
|
|
684
|
+
continue
|
|
685
|
+
|
|
686
|
+
|
|
605
687
|
@validation(
|
|
606
688
|
hook=ValidationHook.FILESOURCE,
|
|
607
689
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
@@ -631,6 +713,38 @@ def rule_EC1016E(
|
|
|
631
713
|
)
|
|
632
714
|
|
|
633
715
|
|
|
716
|
+
@validation(
|
|
717
|
+
hook=ValidationHook.COMPLETE,
|
|
718
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
719
|
+
)
|
|
720
|
+
def rule_EC1017E(
|
|
721
|
+
pluginData: ControllerPluginData,
|
|
722
|
+
cntlr: Cntlr,
|
|
723
|
+
fileSource: FileSource,
|
|
724
|
+
*args: Any,
|
|
725
|
+
**kwargs: Any,
|
|
726
|
+
) -> Iterable[Validation]:
|
|
727
|
+
"""
|
|
728
|
+
EDINET.EC1017E: There is an unused file.
|
|
729
|
+
"""
|
|
730
|
+
uploadContents = pluginData.getUploadContents(fileSource)
|
|
731
|
+
existingSubdirectoryFilepaths = {
|
|
732
|
+
path
|
|
733
|
+
for path, pathInfo in uploadContents.uploadPaths.items()
|
|
734
|
+
if pathInfo.isSubdirectory and not pathInfo.isDirectory
|
|
735
|
+
}
|
|
736
|
+
usedFilepaths = pluginData.getUsedFilepaths()
|
|
737
|
+
unusedSubdirectoryFilepaths = existingSubdirectoryFilepaths - usedFilepaths
|
|
738
|
+
for path in unusedSubdirectoryFilepaths:
|
|
739
|
+
yield Validation.error(
|
|
740
|
+
codes='EDINET.EC1017E',
|
|
741
|
+
msg=_("There is an unused file. "
|
|
742
|
+
"File name: '%(file)s'. "
|
|
743
|
+
"Please remove the file or reference it in the HTML."),
|
|
744
|
+
file=str(path),
|
|
745
|
+
)
|
|
746
|
+
|
|
747
|
+
|
|
634
748
|
@validation(
|
|
635
749
|
hook=ValidationHook.XBRL_FINALLY,
|
|
636
750
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
@@ -676,6 +790,33 @@ def rule_EC1020E(
|
|
|
676
790
|
)
|
|
677
791
|
|
|
678
792
|
|
|
793
|
+
@validation(
|
|
794
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
795
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
796
|
+
)
|
|
797
|
+
def rule_EC1031E(
|
|
798
|
+
pluginData: PluginValidationDataExtension,
|
|
799
|
+
val: ValidateXbrl,
|
|
800
|
+
*args: Any,
|
|
801
|
+
**kwargs: Any,
|
|
802
|
+
) -> Iterable[Validation]:
|
|
803
|
+
"""
|
|
804
|
+
EDINET.EC1031E: Prohibited attribute is used in HTML.
|
|
805
|
+
"""
|
|
806
|
+
for doc in val.modelXbrl.urlDocs.values():
|
|
807
|
+
for elt, attributeName in pluginData.getProhibitedAttributeElements(doc):
|
|
808
|
+
yield Validation.error(
|
|
809
|
+
codes='EDINET.EC1031E',
|
|
810
|
+
msg=_("Prohibited attribute '%(attributeName)s' is used in HTML. "
|
|
811
|
+
"File name: %(file)s (line %(line)s). "
|
|
812
|
+
"Please correct the tag attributes of the relevant file."),
|
|
813
|
+
attributeName=elt.qname.localName,
|
|
814
|
+
file=doc.basename,
|
|
815
|
+
line=elt.sourceline,
|
|
816
|
+
modelObject=elt,
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
|
|
679
820
|
@validation(
|
|
680
821
|
hook=ValidationHook.FILESOURCE,
|
|
681
822
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
@@ -1,12 +1,72 @@
|
|
|
1
|
+
"""
|
|
2
|
+
See COPYRIGHT.md for copyright information.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
1
8
|
import os
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Any, TYPE_CHECKING
|
|
2
11
|
|
|
3
12
|
from arelle import (
|
|
4
13
|
ModelDocument,
|
|
5
|
-
PluginManager,
|
|
14
|
+
PluginManager, FileSource, PackageManager,
|
|
6
15
|
)
|
|
16
|
+
from arelle.UrlUtil import isHttpUrl
|
|
17
|
+
from arelle.typing import TypeGetText
|
|
18
|
+
|
|
19
|
+
_: TypeGetText
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from arelle.Cntlr import Cntlr
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class EntrypointParseResult:
|
|
27
|
+
success: bool
|
|
28
|
+
entrypointFiles: list[dict[str, Any]]
|
|
29
|
+
filesource: FileSource | None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def parseEntrypointFileInput(cntlr: Cntlr, entrypointFile: str | None, sourceZipStream=None, fallbackSelect=True) -> EntrypointParseResult:
|
|
33
|
+
# entrypointFile may be absent (if input is a POSTED zip or file name ending in .zip)
|
|
34
|
+
# or may be a | separated set of file names
|
|
35
|
+
_entryPoints = []
|
|
36
|
+
_checkIfXmlIsEis = cntlr.modelManager.disclosureSystem and cntlr.modelManager.disclosureSystem.validationType == "EFM"
|
|
37
|
+
if entrypointFile:
|
|
38
|
+
_f = entrypointFile
|
|
39
|
+
try: # may be a json list
|
|
40
|
+
_entryPoints = json.loads(_f)
|
|
41
|
+
_checkIfXmlIsEis = False # json entry objects never specify an xml EIS archive
|
|
42
|
+
except ValueError as e:
|
|
43
|
+
# is it malformed json?
|
|
44
|
+
if _f.startswith("[{") or _f.endswith("]}") or '"file:"' in _f:
|
|
45
|
+
cntlr.addToLog(_("File name parameter appears to be malformed JSON: {}\n{}").format(e, _f),
|
|
46
|
+
messageCode="FileNameFormatError",
|
|
47
|
+
level=logging.ERROR)
|
|
48
|
+
else: # try as file names separated by '|'
|
|
49
|
+
for f in (_f or '').split('|'):
|
|
50
|
+
if not sourceZipStream and not isHttpUrl(f) and not os.path.isabs(f):
|
|
51
|
+
f = os.path.normpath(os.path.join(os.getcwd(), f)) # make absolute normed path
|
|
52
|
+
_entryPoints.append({"file":f})
|
|
53
|
+
filesource = None # file source for all instances if not None
|
|
54
|
+
if sourceZipStream:
|
|
55
|
+
filesource = FileSource.openFileSource(None, cntlr, sourceZipStream)
|
|
56
|
+
elif len(_entryPoints) == 1 and "file" in _entryPoints[0]: # check if an archive and need to discover entry points (and not IXDS)
|
|
57
|
+
entryPath = PackageManager.mappedUrl(_entryPoints[0]["file"])
|
|
58
|
+
filesource = FileSource.openFileSource(entryPath, cntlr, checkIfXmlIsEis=_checkIfXmlIsEis)
|
|
59
|
+
_entrypointFiles = _entryPoints
|
|
60
|
+
if filesource and not filesource.selection and not (sourceZipStream and len(_entrypointFiles) > 0):
|
|
61
|
+
try:
|
|
62
|
+
filesourceEntrypointFiles(filesource, _entrypointFiles, fallbackSelect=fallbackSelect)
|
|
63
|
+
except Exception as err:
|
|
64
|
+
cntlr.addToLog(str(err), messageCode="error", level=logging.ERROR)
|
|
65
|
+
return EntrypointParseResult(success=False, entrypointFiles=_entrypointFiles, filesource=filesource)
|
|
66
|
+
return EntrypointParseResult(success=True, entrypointFiles=_entrypointFiles, filesource=filesource)
|
|
7
67
|
|
|
8
68
|
|
|
9
|
-
def filesourceEntrypointFiles(filesource, entrypointFiles=None, inlineOnly=False):
|
|
69
|
+
def filesourceEntrypointFiles(filesource, entrypointFiles=None, inlineOnly=False, fallbackSelect=True):
|
|
10
70
|
if entrypointFiles is None:
|
|
11
71
|
entrypointFiles = []
|
|
12
72
|
for pluginXbrlMethod in PluginManager.pluginClassMethods("FileSource.EntrypointFiles"):
|
|
@@ -34,7 +94,7 @@ def filesourceEntrypointFiles(filesource, entrypointFiles=None, inlineOnly=False
|
|
|
34
94
|
entrypointFiles.extend(reportEntries)
|
|
35
95
|
elif not inlineOnly:
|
|
36
96
|
entrypointFiles.append({"file": report.fullPathPrimary})
|
|
37
|
-
|
|
97
|
+
elif fallbackSelect:
|
|
38
98
|
# attempt to find inline XBRL files before instance files, .xhtml before probing others (ESMA)
|
|
39
99
|
urlsByType = {}
|
|
40
100
|
for _archiveFile in (filesource.dir or ()): # .dir might be none if IOerror
|
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
|
+
COMPLETE = "Validate.Complete"
|
|
37
38
|
FILESOURCE = "Validate.FileSource"
|
|
38
39
|
XBRL_START = "Validate.XBRL.Start"
|
|
39
40
|
XBRL_FINALLY = "Validate.XBRL.Finally"
|
|
@@ -42,6 +43,42 @@ class ValidationHook(Enum):
|
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
class PluginHooks(ABC):
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def cntlrCmdLineFilingStart(
|
|
49
|
+
cntlr: CntlrCmdLine,
|
|
50
|
+
options: RuntimeOptions,
|
|
51
|
+
filesource: FileSource | None,
|
|
52
|
+
entrypoints: list[dict[str, Any]] | None = None,
|
|
53
|
+
sourceZipStream: BinaryIO | None = None,
|
|
54
|
+
responseZipStream: BinaryIO | None = None,
|
|
55
|
+
*args: Any,
|
|
56
|
+
**kwargs: Any,
|
|
57
|
+
) -> None:
|
|
58
|
+
"""
|
|
59
|
+
Plugin hook: `CntlrCmdLine.Filing.Start`
|
|
60
|
+
|
|
61
|
+
This hook is triggered after entrypoints have been discovered and parsed, but before
|
|
62
|
+
models have been loaded.
|
|
63
|
+
|
|
64
|
+
Example:
|
|
65
|
+
```python
|
|
66
|
+
timeAtStart = time.time()
|
|
67
|
+
myOptionEnabled = options.myOption
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
:param cntlr: The [CntlrCmdLine](#arelle.CntlrCmdLine.CntlrCmdLine) that is currently running.
|
|
71
|
+
:param options: Parsed options object.
|
|
72
|
+
:param filesource: FileSource, if available.
|
|
73
|
+
:param entrypoints: A list of entrypoint configurations.
|
|
74
|
+
:param sourceZipStream: The source zip stream if the model was loaded from a zip that was POSTed to the webserver.
|
|
75
|
+
:param responseZipStream: The response zip stream if loaded from the webserver and the user requested a zip response.
|
|
76
|
+
:param args: Argument capture to ensure new parameters don't break plugin hook.
|
|
77
|
+
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
78
|
+
:return: None
|
|
79
|
+
"""
|
|
80
|
+
raise NotImplementedError
|
|
81
|
+
|
|
45
82
|
@staticmethod
|
|
46
83
|
def cntlrCmdLineOptions(
|
|
47
84
|
parser: OptionParser,
|
|
@@ -218,6 +255,34 @@ class PluginHooks(ABC):
|
|
|
218
255
|
"""
|
|
219
256
|
raise NotImplementedError
|
|
220
257
|
|
|
258
|
+
@staticmethod
|
|
259
|
+
def cntlrWinMainFilingStart(
|
|
260
|
+
cntlr: CntlrWinMain,
|
|
261
|
+
filesource: FileSource | None,
|
|
262
|
+
entrypoints: list[dict[str, Any]] | None = None,
|
|
263
|
+
*args: Any,
|
|
264
|
+
**kwargs: Any,
|
|
265
|
+
) -> None:
|
|
266
|
+
"""
|
|
267
|
+
Plugin hook: `CntlrWinMain.Filing.Start`
|
|
268
|
+
|
|
269
|
+
This hook is triggered after entrypoints have been discovered and parsed, but before
|
|
270
|
+
models have been loaded.
|
|
271
|
+
|
|
272
|
+
Example:
|
|
273
|
+
```python
|
|
274
|
+
timeAtStart = time.time()
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
:param cntlr: The [CntlrWinMain](#arelle.CntlrWinMain.CntlrWinMain) that is currently running.
|
|
278
|
+
:param filesource: FileSource, if available.
|
|
279
|
+
:param entrypoints: A list of entrypoint configurations.
|
|
280
|
+
:param args: Argument capture to ensure new parameters don't break plugin hook.
|
|
281
|
+
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
282
|
+
:return: None
|
|
283
|
+
"""
|
|
284
|
+
raise NotImplementedError
|
|
285
|
+
|
|
221
286
|
@staticmethod
|
|
222
287
|
def cntlrWinMainMenuHelp(
|
|
223
288
|
cntlr: CntlrWinMain,
|
|
@@ -569,6 +634,35 @@ class PluginHooks(ABC):
|
|
|
569
634
|
"""
|
|
570
635
|
raise NotImplementedError
|
|
571
636
|
|
|
637
|
+
@staticmethod
|
|
638
|
+
def validateComplete(
|
|
639
|
+
cntlr: Cntlr,
|
|
640
|
+
fileSource: FileSource,
|
|
641
|
+
*args: Any,
|
|
642
|
+
**kwargs: Any,
|
|
643
|
+
) -> None:
|
|
644
|
+
"""
|
|
645
|
+
Plugin hook: `Validate.Complete`
|
|
646
|
+
|
|
647
|
+
Hook for executing controller-level validation rules after model-level validation is complete.
|
|
648
|
+
This can be useful for validating multi-instance filings when a rule requires information that
|
|
649
|
+
is only available after all instances have been parsed and validated.
|
|
650
|
+
|
|
651
|
+
Example:
|
|
652
|
+
```python
|
|
653
|
+
unusedFilepaths = pluginData.getExistingFilepaths() - pluginData.getUsedFilepaths()
|
|
654
|
+
if len(unusedSubdirectoryFilepaths) > 0:
|
|
655
|
+
yield Validation.error(codes="0.0.0", msg="Unused files exist.")
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
:param cntlr: The [Cntlr](#arelle.Cntlr.Cntlr) instance.
|
|
659
|
+
:param fileSource: The [FileSource](#arelle.FileSource.FileSource) involved in loading the entrypoint files.
|
|
660
|
+
:param args: Argument capture to ensure new parameters don't break plugin hook.
|
|
661
|
+
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
662
|
+
:return: None
|
|
663
|
+
"""
|
|
664
|
+
raise NotImplementedError
|
|
665
|
+
|
|
572
666
|
@staticmethod
|
|
573
667
|
def validateFileSource(
|
|
574
668
|
cntlr: Cntlr,
|
|
@@ -101,6 +101,26 @@ class ValidationPlugin:
|
|
|
101
101
|
) -> ModelDocument | LoadingException | None:
|
|
102
102
|
raise NotImplementedError
|
|
103
103
|
|
|
104
|
+
def validateComplete(
|
|
105
|
+
self,
|
|
106
|
+
cntlr: Cntlr,
|
|
107
|
+
fileSource: FileSource,
|
|
108
|
+
*args: Any,
|
|
109
|
+
**kwargs: Any,
|
|
110
|
+
) -> None:
|
|
111
|
+
"""
|
|
112
|
+
Executes validation functions in the rules module that was provided to the constructor of this class.
|
|
113
|
+
Each function decorated with [@validation](#arelle.utils.validate.Decorator.validation) will be run if:
|
|
114
|
+
1. the decorator was used with the validation complete hook: `@validation(hook=ValidationHook.COMPLETE)`
|
|
115
|
+
|
|
116
|
+
:param cntlr: The [Cntlr](#arelle.Cntlr.Cntlr) instance.
|
|
117
|
+
:param fileSource: The [FileSource](#arelle.FileSource.FileSource) involved in loading the entrypoint files.
|
|
118
|
+
:param args: Argument capture to ensure new parameters don't break plugin hook.
|
|
119
|
+
:param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
|
|
120
|
+
:return: None
|
|
121
|
+
"""
|
|
122
|
+
self._executeCntlrValidations(ValidationHook.COMPLETE, cntlr, fileSource, *args, **kwargs)
|
|
123
|
+
|
|
104
124
|
def validateFileSource(
|
|
105
125
|
self,
|
|
106
126
|
cntlr: Cntlr,
|
|
@@ -112,7 +132,7 @@ class ValidationPlugin:
|
|
|
112
132
|
"""
|
|
113
133
|
Executes validation functions in the rules module that was provided to the constructor of this class.
|
|
114
134
|
Each function decorated with [@validation](#arelle.utils.validate.Decorator.validation) will be run if:
|
|
115
|
-
1. the decorator was used with the
|
|
135
|
+
1. the decorator was used with the FileSource validation hook: `@validation(hook=ValidationHook.FILESOURCE)`
|
|
116
136
|
|
|
117
137
|
:param cntlr: The [Cntlr](#arelle.Cntlr.Cntlr) instance.
|
|
118
138
|
:param fileSource: The [FileSource](#arelle.FileSource.FileSource) involved in loading the entrypoint files.
|
|
@@ -1,12 +1,12 @@
|
|
|
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=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
|
|
8
8
|
arelle/CntlrWebMain.py,sha256=_jhosP0Pg-erutBRzTVsL0cmWVgbc6z0WIIlNO-z1N4,50712
|
|
9
|
-
arelle/CntlrWinMain.py,sha256=
|
|
9
|
+
arelle/CntlrWinMain.py,sha256=SWMdTK4Vv1I8cFpVBxbPiJwfuGn7yMbNDbNZM9UYn9E,98220
|
|
10
10
|
arelle/CntlrWinTooltip.py,sha256=6MzoAIfkYnNu_bl_je8n0adhwmKxAIcymkg9Tij9Z4M,7951
|
|
11
11
|
arelle/DialogAbout.py,sha256=XXzMV0fO4BQ3-l1Puirzmn7EZEdmgJg7JNYdJm1FueM,1987
|
|
12
12
|
arelle/DialogArcroleGroup.py,sha256=r81OT3LFmMkoROpFenk97oVEyQhibKZ1QgDHvMsyCl0,7547
|
|
@@ -36,7 +36,7 @@ arelle/LeiUtil.py,sha256=tSPrbQrXEeH5pXgGA_6MAdgMZp20NaW5izJglIXyEQk,5095
|
|
|
36
36
|
arelle/LinkbaseType.py,sha256=BwRQl4XZFFCopufC2FEMLhYENNTk2JUWVQvnIUsaqtI,3108
|
|
37
37
|
arelle/LocalViewer.py,sha256=WVrfek_bLeFFxgWITi1EQb6xCQN8O9Ks-ZL16vRncSk,3080
|
|
38
38
|
arelle/Locale.py,sha256=07IDxv8nIfvhyRRllFdEAKRI3yo1D2v781cTrjb_RoQ,33193
|
|
39
|
-
arelle/ModelDocument.py,sha256=
|
|
39
|
+
arelle/ModelDocument.py,sha256=R4uVfqks2XRDziRPAp_AUFebrAQBf4FgG97nb5h7fAs,130651
|
|
40
40
|
arelle/ModelDtsObject.py,sha256=nvHQs4BDmPxY6mqLiBuqIGRJXyA1EqOEGB2f3S6A6w4,88656
|
|
41
41
|
arelle/ModelFormulaObject.py,sha256=-eb0lBYciEeAvvGduIs3AHGNGrxge9_0g1cVT6UUgvc,122560
|
|
42
42
|
arelle/ModelInstanceObject.py,sha256=77rkxl1FK8OyJhTXkrNv6B_4oEK6IZm9eRQdFDxslGk,74278
|
|
@@ -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=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
|
|
@@ -94,7 +94,7 @@ arelle/ViewFileTests.py,sha256=l5phDVBWYNd7xECgJOy9Tsjpjo0mzwP6AlmGbq8ufOU,7798
|
|
|
94
94
|
arelle/ViewUtil.py,sha256=A8SEWiQB9tyCPTMaxrQNMF8AI7Y2CxP5I9RuYeVkytU,2279
|
|
95
95
|
arelle/ViewUtilFormulae.py,sha256=AQ9sQ3e1giqI54YgNNiR7RAxeoXPvunUraqEoAitYak,1837
|
|
96
96
|
arelle/ViewWinConcepts.py,sha256=FVKJ607_aZrwjPMUdVEfPlA6s7oGqg3Vtf_MxlEEZts,6812
|
|
97
|
-
arelle/ViewWinDTS.py,sha256=
|
|
97
|
+
arelle/ViewWinDTS.py,sha256=L2q-Pyn-g61RN7SQxTamqDmgKJIIy5UE4vNb8A4lZfg,1913
|
|
98
98
|
arelle/ViewWinDiffs.py,sha256=Q8Yd-L3zKhgOab4UK01CPHyXyMd3G37DIaDQ00DAolA,4856
|
|
99
99
|
arelle/ViewWinFactGrid.py,sha256=tQk9MQ9aiTzJ2SsX8Y16V7qccf-Jl1FcBPEP4_Cxc0E,14349
|
|
100
100
|
arelle/ViewWinFactList.py,sha256=eoSzG-Kt90kb7Ok4HCIn6NqGe8fp8WoN03f8Wn3H5LI,8858
|
|
@@ -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=Ob4Yjv_aHEp_grPDRfpg4fr4W3Qb5_yliBYKjfPLi7w,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
|
|
@@ -222,8 +222,8 @@ arelle/packages/PackageType.py,sha256=2s29OWZ-XpKW1bPJ1kVBcpKMDBr9gzLe25Cukg-YTE
|
|
|
222
222
|
arelle/packages/PackageUtils.py,sha256=uraFfkyYQVlY8YdK4j-HgxhkIAOsEaU5Mk4To0ymKxE,575
|
|
223
223
|
arelle/packages/PackageValidation.py,sha256=y0J6EAR5O8fF51vIrj-2Ur42TObIMvHVFtPXTR-GGOc,7408
|
|
224
224
|
arelle/packages/report/DetectReportPackage.py,sha256=AemOF0eDbnCR3n6cNTWDb93FttQ18s3jsNQuA-37zM4,305
|
|
225
|
-
arelle/packages/report/ReportPackage.py,sha256=
|
|
226
|
-
arelle/packages/report/ReportPackageConst.py,sha256=
|
|
225
|
+
arelle/packages/report/ReportPackage.py,sha256=raRUThRiyJqcYODjr1VaTEzwd86m4Wg8xGVi0daJJTQ,10322
|
|
226
|
+
arelle/packages/report/ReportPackageConst.py,sha256=rTs-fV0BztXo_NleIIyN2Y8mpmC_r3eSzUxeZbvSiB8,2612
|
|
227
227
|
arelle/packages/report/ReportPackageValidator.py,sha256=3Ur_CXUtCPtXzUgDi9t3VI3vcew0I9pq2B-cn_rIkX4,10322
|
|
228
228
|
arelle/plugin/CacheBuilder.py,sha256=-aDFD3rx83glOprQYO-8gsaIEXcdwB6PFfIq7jy20lo,4489
|
|
229
229
|
arelle/plugin/EdgarRendererAllReports.py,sha256=V3AeDj29U1XbnFGUO30kAvZtWzDH5G-teyzoJE0HDU0,5031
|
|
@@ -234,7 +234,7 @@ arelle/plugin/formulaLoader.py,sha256=_pPZQPAZeNjGj85rvH7QRl4gEjYD7Yhxl1JhuV9wOo
|
|
|
234
234
|
arelle/plugin/formulaSaver.py,sha256=STlKyDA-pVUxZoEW57MSu74RdpyHVTxaHvOZyOt0cyg,31385
|
|
235
235
|
arelle/plugin/formulaXPathChecker.py,sha256=sEEeLHx17XSj8eOgFdzYLBp9ZFk2UUYLOEKtaF_pq34,18795
|
|
236
236
|
arelle/plugin/functionsMath.py,sha256=Z8N7ok3w1aOusCQA9QvqYwQ8W1j80bb-_4lVclBnNQM,9037
|
|
237
|
-
arelle/plugin/inlineXbrlDocumentSet.py,sha256=
|
|
237
|
+
arelle/plugin/inlineXbrlDocumentSet.py,sha256=mG7BVPblkm6A81sAKR8NxjlNDTqlpKoErJTHn7lw1tk,55712
|
|
238
238
|
arelle/plugin/loadFromExcel.py,sha256=galvvaj9jTKMMRUasCSh1SQnCCBzoN_HEpYWv0fmUdM,124407
|
|
239
239
|
arelle/plugin/loadFromOIM.py,sha256=dJHnX56bmuY40f6vRMsQ7pJmIWcB5N_3jmYWGGnZSdM,903
|
|
240
240
|
arelle/plugin/profileCmdLine.py,sha256=uLL0fGshpiwtzyLKAtW0WuXAvcRtZgxQG6swM0e4BHA,2370
|
|
@@ -312,25 +312,25 @@ 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=
|
|
316
|
-
arelle/plugin/validate/EDINET/ControllerPluginData.py,sha256=
|
|
315
|
+
arelle/plugin/validate/EDINET/Constants.py,sha256=IkJ1R0ZQAs9kUXY8eB7e8azYlC4m500VILrQhniB5Q4,1949
|
|
316
|
+
arelle/plugin/validate/EDINET/ControllerPluginData.py,sha256=2hMPxpL0vZqmzcuvPl9o4_g79ZOtJgLI1p_VRW2YYVk,7259
|
|
317
317
|
arelle/plugin/validate/EDINET/DisclosureSystems.py,sha256=3rKG42Eg-17Xx_KXU_V5yHW6I3LTwQunvf4a44C9k_4,36
|
|
318
|
-
arelle/plugin/validate/EDINET/ManifestInstance.py,sha256=
|
|
319
|
-
arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=
|
|
318
|
+
arelle/plugin/validate/EDINET/ManifestInstance.py,sha256=o6BGlaQHSsn6D0VKH4zn59UscKnjTKlo99kSGfGdYlU,6910
|
|
319
|
+
arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=aM3OtB0X6NSHouFDut68pkLfe143rTW6nL6tIcI84D4,16092
|
|
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
322
|
arelle/plugin/validate/EDINET/UploadContents.py,sha256=IKQYl6lXYTfAZKLIDzRRGSRt3FXoL2Eldbx3Dh7T2I4,712
|
|
323
|
-
arelle/plugin/validate/EDINET/ValidationPluginExtension.py,sha256=
|
|
324
|
-
arelle/plugin/validate/EDINET/__init__.py,sha256=
|
|
323
|
+
arelle/plugin/validate/EDINET/ValidationPluginExtension.py,sha256=xr4Ft9v-zpwgRuKSjregyHjtUb5KgHqdSx9NKg9UVeg,2528
|
|
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
|
|
327
327
|
arelle/plugin/validate/EDINET/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
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=
|
|
331
|
+
arelle/plugin/validate/EDINET/rules/gfm.py,sha256=3kV7jahj_Y8GxwgjebUtnIrnDTbZEYEDfzokDTGcehc,25490
|
|
332
332
|
arelle/plugin/validate/EDINET/rules/manifests.py,sha256=MoT9R_a4BzuYdQVbF7RC5wz134Ve68svSdJ3NlpO_AU,4026
|
|
333
|
-
arelle/plugin/validate/EDINET/rules/upload.py,sha256=
|
|
333
|
+
arelle/plugin/validate/EDINET/rules/upload.py,sha256=f9MLg7Dz9czsAHKE-SQHRlCe77AXWAPyp4RDeB0G8Rc,36818
|
|
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
|
|
@@ -665,24 +665,24 @@ arelle/resources/libs/Tktable2.11/win-x86_64/Tktable.dll,sha256=R-Xf_J9tYjTe4pZE
|
|
|
665
665
|
arelle/resources/libs/Tktable2.11/win-x86_64/pkgIndex.tcl,sha256=tmkATyDWsKAA3NgvlNGhOOdTWdBD91X1BgAHmybd_lY,151
|
|
666
666
|
arelle/resources/libs/Tktable2.11/win-x86_64/tkTable.tcl,sha256=JrSZRngZFHtw8Svpzl68AVoffCuzzqG0zs3Zxe8UUoM,24094
|
|
667
667
|
arelle/utils/Contexts.py,sha256=j9uSBAXGkunlJGC9SscCbb1cj3oU_J3b_yjdhj2B4a4,1714
|
|
668
|
-
arelle/utils/EntryPointDetection.py,sha256=
|
|
668
|
+
arelle/utils/EntryPointDetection.py,sha256=TciYbi49dv7fs5inFQR-G6rKCB2XfX_TLgDJ_3FcRK0,7680
|
|
669
669
|
arelle/utils/Equivalence.py,sha256=Ac6sENvh-WHJlBJneV_-6n_MF2C1filHETkUEiucLJg,525
|
|
670
670
|
arelle/utils/PluginData.py,sha256=GUnuZaApm1J4Xm9ZA1U2M1aask-AaNGviLtc0fgXbFg,265
|
|
671
|
-
arelle/utils/PluginHooks.py,sha256=
|
|
671
|
+
arelle/utils/PluginHooks.py,sha256=kuiNeR7l30-FIxW1lbqm_m4dI5gTY0BHrz60yj9rI_U,36053
|
|
672
672
|
arelle/utils/Units.py,sha256=c9bwnu9Xnm00gC9Q6qQ1ogsEeTEXGRH7rahlEbrEWnQ,1201
|
|
673
673
|
arelle/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
674
674
|
arelle/utils/validate/Decorator.py,sha256=8LGmA171HZgKrALtsMKyHqMNM-XCdwJOv6KpZz4pC2c,3161
|
|
675
675
|
arelle/utils/validate/DetectScriptsInXhtml.py,sha256=RFBh_Z24OjR69s71qQzSzbxdU-WCTWuvYlONN-BgpZ0,2098
|
|
676
676
|
arelle/utils/validate/ESEFImage.py,sha256=M3pR9zxz0Y8oNjrpniEYwztCV2hoBK4fDSi4U095C3k,15520
|
|
677
677
|
arelle/utils/validate/Validation.py,sha256=n6Ag7VeCj_VO5nqzw_P53hOfXXeT2APK0Enb3UQqBns,832
|
|
678
|
-
arelle/utils/validate/ValidationPlugin.py,sha256=
|
|
678
|
+
arelle/utils/validate/ValidationPlugin.py,sha256=qd08cSng-wzZw2utFSwuNzZwiT2-6JJ_J2rqZXmfbwc,14689
|
|
679
679
|
arelle/utils/validate/ValidationUtil.py,sha256=9vmSvShn-EdQy56dfesyV8JjSRVPj7txrxRFgh8FxIs,548
|
|
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.
|
|
684
|
-
arelle_release-2.37.
|
|
685
|
-
arelle_release-2.37.
|
|
686
|
-
arelle_release-2.37.
|
|
687
|
-
arelle_release-2.37.
|
|
688
|
-
arelle_release-2.37.
|
|
683
|
+
arelle_release-2.37.55.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
|
|
684
|
+
arelle_release-2.37.55.dist-info/METADATA,sha256=7zIMq7-ff7-wq1Vq8wfHYzjd4A_sMGZsPHuiIz2-nNY,9327
|
|
685
|
+
arelle_release-2.37.55.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
686
|
+
arelle_release-2.37.55.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
|
|
687
|
+
arelle_release-2.37.55.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
|
|
688
|
+
arelle_release-2.37.55.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|