arelle-release 2.36.40__py3-none-any.whl → 2.37.1__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 +1 -2
- arelle/CntlrWebMain.py +7 -7
- arelle/CntlrWinMain.py +2 -3
- arelle/FileSource.py +2 -1
- arelle/LocalViewer.py +8 -2
- arelle/ModelDocument.py +2 -2
- arelle/PackageManager.py +2 -1
- arelle/PluginManager.py +2 -1
- arelle/PythonUtil.py +13 -0
- arelle/Validate.py +4 -3
- arelle/ValidateFilingText.py +2 -1
- arelle/WebCache.py +4 -2
- arelle/_version.py +2 -2
- arelle/oim/Load.py +2 -2
- arelle/plugin/inlineXbrlDocumentSet.py +2 -2
- arelle/plugin/loadFromExcel.py +5 -5
- arelle/plugin/validate/ESEF/ESEF_2021/ValidateXbrlFinally.py +2 -2
- arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py +4 -4
- arelle/plugin/validate/NL/DisclosureSystems.py +1 -0
- arelle/plugin/validate/NL/PluginValidationDataExtension.py +3 -0
- arelle/plugin/validate/NL/ValidationPluginExtension.py +3 -3
- arelle/plugin/validate/NL/__init__.py +2 -2
- arelle/plugin/validate/NL/resources/config.xml +5 -0
- arelle/plugin/validate/NL/rules/nl_kvk.py +67 -0
- arelle/plugin/xbrlDB/SqlDb.py +1 -1
- arelle/utils/PluginHooks.py +3 -2
- arelle/webserver/bottle.py +5 -4426
- {arelle_release-2.36.40.dist-info → arelle_release-2.37.1.dist-info}/METADATA +4 -2
- {arelle_release-2.36.40.dist-info → arelle_release-2.37.1.dist-info}/RECORD +36 -35
- tests/integration_tests/ui_tests/ArelleGUITest/ArelleGUITest/ArelleGUITest.csproj +2 -2
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_formula_1_0_function_registry.py +6 -6
- tests/integration_tests/validation/discover_tests.py +2 -1
- {arelle_release-2.36.40.dist-info → arelle_release-2.37.1.dist-info}/WHEEL +0 -0
- {arelle_release-2.36.40.dist-info → arelle_release-2.37.1.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.36.40.dist-info → arelle_release-2.37.1.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.36.40.dist-info → arelle_release-2.37.1.dist-info}/top_level.txt +0 -0
arelle/CntlrCmdLine.py
CHANGED
|
@@ -486,7 +486,7 @@ def parseArgs(args):
|
|
|
486
486
|
"\n Python(r) {pythonVersion} (c) 2001-2013 Python Software Foundation"
|
|
487
487
|
"\n PyParsing (c) 2003-2013 Paul T. McGuire"
|
|
488
488
|
"\n lxml {lxmlVersion} (c) 2004 Infrae, ElementTree (c) 1999-2004 by Fredrik Lundh"
|
|
489
|
-
"
|
|
489
|
+
"\n Bottle (c) 2009-2024 Marcel Hellkamp"
|
|
490
490
|
"\n May include installable plug-in modules with author-specific license terms").format(
|
|
491
491
|
version=Version.__version__,
|
|
492
492
|
wordSize=getSystemWordSize(),
|
|
@@ -494,7 +494,6 @@ def parseArgs(args):
|
|
|
494
494
|
copyrightLabel=Version.copyrightLabel,
|
|
495
495
|
pythonVersion=f'{sys.version_info[0]}.{sys.version_info[1]}.{sys.version_info[2]}',
|
|
496
496
|
lxmlVersion=f'{etree.LXML_VERSION[0]}.{etree.LXML_VERSION[1]}.{etree.LXML_VERSION[2]}',
|
|
497
|
-
bottleCopyright="\n Bottle (c) 2011-2013 Marcel Hellkamp" if hasWebServer() else ""
|
|
498
497
|
))
|
|
499
498
|
parser.exit() # Printing the message in parser.exit sends it to stderr NOT stdout
|
|
500
499
|
elif options.diagnostics:
|
arelle/CntlrWebMain.py
CHANGED
|
@@ -15,16 +15,16 @@ import zipfile
|
|
|
15
15
|
from collections.abc import Iterable
|
|
16
16
|
from copy import deepcopy
|
|
17
17
|
|
|
18
|
+
from bottle import Bottle, HTTPResponse, request, response, static_file # type: ignore[import-untyped]
|
|
19
|
+
|
|
18
20
|
from arelle import Version
|
|
19
21
|
from arelle.CntlrCmdLine import CntlrCmdLine
|
|
20
22
|
from arelle.FileSource import FileNamedStringIO
|
|
21
|
-
from arelle.PluginManager import pluginClassMethods
|
|
22
|
-
from arelle.RuntimeOptions import RuntimeOptions
|
|
23
23
|
from arelle.logging.formatters.LogFormatter import LogFormatter
|
|
24
24
|
from arelle.logging.handlers.LogToBufferHandler import LogToBufferHandler
|
|
25
|
+
from arelle.PluginManager import pluginClassMethods
|
|
26
|
+
from arelle.RuntimeOptions import RuntimeOptions
|
|
25
27
|
from arelle.typing import TypeGetText
|
|
26
|
-
from arelle.webserver.bottle import (Bottle, HTTPResponse, request, response,
|
|
27
|
-
static_file)
|
|
28
28
|
|
|
29
29
|
_: TypeGetText
|
|
30
30
|
|
|
@@ -136,13 +136,13 @@ def startWebserver(cntlr: CntlrCmdLine, options: RuntimeOptions) -> Bottle | Non
|
|
|
136
136
|
elif server == "cgi":
|
|
137
137
|
if sys.stdin is None:
|
|
138
138
|
sys.stdin = open(os.devnull, 'r')
|
|
139
|
-
app.run(server=server)
|
|
139
|
+
app.run(server=server)
|
|
140
140
|
sys.exit(0)
|
|
141
141
|
elif server:
|
|
142
142
|
sys.path.insert(0,os.path.join(os.path.dirname(__file__),"webserver"))
|
|
143
|
-
app.run(host=host, port=port or 80, server=server)
|
|
143
|
+
app.run(host=host, port=port or 80, server=server)
|
|
144
144
|
else:
|
|
145
|
-
app.run(host=host, port=port or 80)
|
|
145
|
+
app.run(host=host, port=port or 80)
|
|
146
146
|
return None
|
|
147
147
|
|
|
148
148
|
def cgiInterface(cgiAppPath: str) -> str | HTTPResponse:
|
arelle/CntlrWinMain.py
CHANGED
|
@@ -1486,7 +1486,8 @@ class CntlrWinMain (Cntlr.Cntlr):
|
|
|
1486
1486
|
"\n Tcl/Tk {tcltkVersion} \u00a9 Univ. of Calif., Sun, Scriptics, ActiveState, and others"
|
|
1487
1487
|
"\n PyParsing \u00a9 2003-2013 Paul T. McGuire"
|
|
1488
1488
|
"\n lxml {lxmlVersion} \u00a9 2004 Infrae, ElementTree \u00a9 1999-2004 by Fredrik Lundh"
|
|
1489
|
-
"
|
|
1489
|
+
"\n Bottle \u00a9 2009-2024 Marcel Hellkamp"
|
|
1490
|
+
"\n CherryPy \u00a9 2004-2019 CherryPy Team"
|
|
1490
1491
|
"\n May include installable plug-in modules with author-specific license terms").format(
|
|
1491
1492
|
version=Version.__version__,
|
|
1492
1493
|
wordSize=self.systemWordSize,
|
|
@@ -1495,8 +1496,6 @@ class CntlrWinMain (Cntlr.Cntlr):
|
|
|
1495
1496
|
pythonVersion=f'{sys.version_info[0]}.{sys.version_info[1]}.{sys.version_info[2]}',
|
|
1496
1497
|
tcltkVersion=Tcl().eval('info patchlevel'),
|
|
1497
1498
|
lxmlVersion=f'{etree.LXML_VERSION[0]}.{etree.LXML_VERSION[1]}.{etree.LXML_VERSION[2]}',
|
|
1498
|
-
bottleCopyright=_("\n Bottle \u00a9 2011-2013 Marcel Hellkamp"
|
|
1499
|
-
"\n CherryPy \u00a9 2002-2013 CherryPy Team") if self.hasWebServer else ""
|
|
1500
1499
|
))
|
|
1501
1500
|
|
|
1502
1501
|
|
arelle/FileSource.py
CHANGED
|
@@ -20,6 +20,7 @@ from lxml import etree
|
|
|
20
20
|
|
|
21
21
|
import arelle.PluginManager
|
|
22
22
|
from arelle import PackageManager, XmlUtil
|
|
23
|
+
from arelle.PythonUtil import isLegacyAbs
|
|
23
24
|
from arelle.packages.report.DetectReportPackage import isReportPackageExtension
|
|
24
25
|
from arelle.packages.report.ReportPackage import ReportPackage
|
|
25
26
|
from arelle.typing import TypeGetText
|
|
@@ -769,7 +770,7 @@ class FileSource:
|
|
|
769
770
|
|
|
770
771
|
def basedUrl(self, selection: str) -> str:
|
|
771
772
|
baseurl = getattr(self, "baseurl", None)
|
|
772
|
-
if not baseurl or isHttpUrl(selection) or
|
|
773
|
+
if not baseurl or isHttpUrl(selection) or isLegacyAbs(selection):
|
|
773
774
|
return selection
|
|
774
775
|
assert isinstance(baseurl, str)
|
|
775
776
|
if self.baseIsHttp or os.sep == '/':
|
arelle/LocalViewer.py
CHANGED
|
@@ -4,8 +4,14 @@ See COPYRIGHT.md for copyright information.
|
|
|
4
4
|
Provides infrastructure for local viewers of GUI applications such as inline XBRL viewers
|
|
5
5
|
|
|
6
6
|
'''
|
|
7
|
-
|
|
8
|
-
import
|
|
7
|
+
import logging
|
|
8
|
+
import sys
|
|
9
|
+
import threading
|
|
10
|
+
import time
|
|
11
|
+
import traceback
|
|
12
|
+
|
|
13
|
+
from bottle import Bottle, HTTPResponse, request
|
|
14
|
+
|
|
9
15
|
|
|
10
16
|
class LocalViewer:
|
|
11
17
|
noCacheHeaders = {'Cache-Control': 'no-cache, no-store, must-revalidate',
|
arelle/ModelDocument.py
CHANGED
|
@@ -20,7 +20,7 @@ from arelle.ModelInstanceObject import ModelFact
|
|
|
20
20
|
from arelle.ModelObjectFactory import parser
|
|
21
21
|
from arelle.PrototypeDtsObject import LinkPrototype, LocPrototype, ArcPrototype, DocumentPrototype, PrototypeElementTree
|
|
22
22
|
from arelle.PluginManager import pluginClassMethods
|
|
23
|
-
from arelle.PythonUtil import OrderedDefaultDict, normalizeSpace
|
|
23
|
+
from arelle.PythonUtil import OrderedDefaultDict, isLegacyAbs, normalizeSpace
|
|
24
24
|
from arelle.XhtmlValidate import ixMsgCode
|
|
25
25
|
from arelle.XmlValidateConst import VALID
|
|
26
26
|
from arelle.XmlValidate import validate as xmlValidate, lxmlSchemaValidate
|
|
@@ -1106,7 +1106,7 @@ class ModelDocument:
|
|
|
1106
1106
|
break # break because it is now absolute
|
|
1107
1107
|
baseElt = baseElt.getparent()
|
|
1108
1108
|
if base: # neither None nor ''
|
|
1109
|
-
if base.startswith('http://') or
|
|
1109
|
+
if base.startswith('http://') or isLegacyAbs(base):
|
|
1110
1110
|
return base
|
|
1111
1111
|
else:
|
|
1112
1112
|
return os.path.dirname(self.uri) + "/" + base
|
arelle/PackageManager.py
CHANGED
|
@@ -15,6 +15,7 @@ from urllib.parse import urljoin
|
|
|
15
15
|
from lxml import etree
|
|
16
16
|
|
|
17
17
|
from arelle import Locale
|
|
18
|
+
from arelle.PythonUtil import isLegacyAbs
|
|
18
19
|
from arelle.packages import PackageValidation
|
|
19
20
|
from arelle.packages.PackageType import PackageType
|
|
20
21
|
from arelle.typing import TypeGetText
|
|
@@ -314,7 +315,7 @@ def _parseCatalog(
|
|
|
314
315
|
replaceValue = os.path.join(base, replaceValue)
|
|
315
316
|
if replaceValue and not isAbsolute(replaceValue):
|
|
316
317
|
# neither None nor ''
|
|
317
|
-
if not
|
|
318
|
+
if not isLegacyAbs(replaceValue):
|
|
318
319
|
replaceValue = fileBase + replaceValue
|
|
319
320
|
if not isHttpUrl(replaceValue):
|
|
320
321
|
replaceValue = replaceValue.replace("/", os.sep)
|
arelle/PluginManager.py
CHANGED
|
@@ -15,6 +15,7 @@ from types import ModuleType
|
|
|
15
15
|
from typing import TYPE_CHECKING, Any, cast
|
|
16
16
|
from arelle.Locale import getLanguageCodes
|
|
17
17
|
import arelle.FileSource
|
|
18
|
+
from arelle.PythonUtil import isLegacyAbs
|
|
18
19
|
from arelle.UrlUtil import isAbsolute
|
|
19
20
|
from pathlib import Path
|
|
20
21
|
from collections import OrderedDict, defaultdict
|
|
@@ -447,7 +448,7 @@ def moduleModuleInfo(
|
|
|
447
448
|
mergedImportURLs.append(moduleImport + ".py")
|
|
448
449
|
imports = []
|
|
449
450
|
for _url in mergedImportURLs:
|
|
450
|
-
if isAbsolute(_url) or
|
|
451
|
+
if isAbsolute(_url) or isLegacyAbs(_url):
|
|
451
452
|
_importURL = _url # URL is absolute http or local file system
|
|
452
453
|
else: # check if exists relative to this module's directory
|
|
453
454
|
_importURL = os.path.join(os.path.dirname(moduleURL), os.path.normpath(_url))
|
arelle/PythonUtil.py
CHANGED
|
@@ -6,6 +6,7 @@ do not convert 3 to 2
|
|
|
6
6
|
'''
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
import os
|
|
9
10
|
import subprocess
|
|
10
11
|
import sys
|
|
11
12
|
from collections import OrderedDict
|
|
@@ -269,3 +270,15 @@ def tryRunCommand(*args: str) -> str | None:
|
|
|
269
270
|
).stdout.strip()
|
|
270
271
|
except (OSError, subprocess.SubprocessError):
|
|
271
272
|
return None
|
|
273
|
+
|
|
274
|
+
def isLegacyAbs(path: str) -> bool:
|
|
275
|
+
"""
|
|
276
|
+
Implements legacy behavior of os.path.isabs() prior to Python 3.13
|
|
277
|
+
where True was returned for paths beginning with single slashes on Windows.
|
|
278
|
+
"""
|
|
279
|
+
if os.path.isabs(path):
|
|
280
|
+
return True
|
|
281
|
+
from arelle.SystemInfo import PlatformOS
|
|
282
|
+
if PlatformOS.getPlatformOS() == PlatformOS.WINDOWS:
|
|
283
|
+
return path.startswith(("/", "\\"))
|
|
284
|
+
return False
|
arelle/Validate.py
CHANGED
|
@@ -12,6 +12,7 @@ from collections import defaultdict, OrderedDict
|
|
|
12
12
|
from arelle import (FileSource, ModelXbrl, ModelDocument, ModelVersReport, XbrlConst,
|
|
13
13
|
ValidateXbrl, ValidateVersReport,
|
|
14
14
|
ValidateInfoset, ViewFileRenderedLayout, UrlUtil)
|
|
15
|
+
from arelle.PythonUtil import isLegacyAbs
|
|
15
16
|
from arelle.formula import ValidateFormula
|
|
16
17
|
from arelle.ModelDocument import Type, ModelDocumentReference, load as modelDocumentLoad
|
|
17
18
|
from arelle.ModelDtsObject import ModelResource
|
|
@@ -157,7 +158,7 @@ class Validate:
|
|
|
157
158
|
modelXbrl = None
|
|
158
159
|
try:
|
|
159
160
|
rssItemUrl = rssItem.zippedUrl
|
|
160
|
-
if self.useFileSource.isArchive and (
|
|
161
|
+
if self.useFileSource.isArchive and (isLegacyAbs(rssItemUrl) or not rssItemUrl.endswith(".zip")):
|
|
161
162
|
modelXbrl = ModelXbrl.load(self.modelXbrl.modelManager,
|
|
162
163
|
openFileSource(rssItemUrl, self.modelXbrl.modelManager.cntlr, reloadCache=reloadCache),
|
|
163
164
|
_("validating"), rssItem=rssItem)
|
|
@@ -337,7 +338,7 @@ class Validate:
|
|
|
337
338
|
PackageManager.packageInfo(self.modelXbrl.modelManager.cntlr, readMeFirstUri, reload=True, errors=modelXbrl.errors)
|
|
338
339
|
else: # not a multi-schemaRef versioning report
|
|
339
340
|
readMeFirstUriIsEmbeddedZipFile = False
|
|
340
|
-
if self.useFileSource.isArchive and not
|
|
341
|
+
if self.useFileSource.isArchive and not isLegacyAbs(readMeFirstUri):
|
|
341
342
|
if isReportPackageExtension(readMeFirstUri):
|
|
342
343
|
readMeFirstUriIsEmbeddedZipFile = True
|
|
343
344
|
else:
|
|
@@ -360,7 +361,7 @@ class Validate:
|
|
|
360
361
|
newSourceFileSource = False
|
|
361
362
|
if (
|
|
362
363
|
self.useFileSource
|
|
363
|
-
and not
|
|
364
|
+
and not isLegacyAbs(readMeFirstUri)
|
|
364
365
|
and (readMeFirstUriIsEmbeddedZipFile or isReportPackageExtension(readMeFirstUri))
|
|
365
366
|
):
|
|
366
367
|
if self.useFileSource.isArchive:
|
arelle/ValidateFilingText.py
CHANGED
|
@@ -9,6 +9,7 @@ from PIL import Image as pilImage
|
|
|
9
9
|
import os, io, base64
|
|
10
10
|
import regex as re
|
|
11
11
|
import string
|
|
12
|
+
from arelle.PythonUtil import isLegacyAbs
|
|
12
13
|
from arelle.XbrlConst import ixbrlAll, xhtml
|
|
13
14
|
from arelle.XmlUtil import setXmlns, xmlstring, xmlDeclarationPattern, XmlDeclarationLocationException
|
|
14
15
|
from arelle.ModelObject import ModelObject
|
|
@@ -1007,7 +1008,7 @@ def referencedFiles(modelXbrl, localFilesOnly=True):
|
|
|
1007
1008
|
if (attrTag in ("href", "src") and
|
|
1008
1009
|
scheme(attrValue) not in ("data", "javascript") and (
|
|
1009
1010
|
not localFilesOnly or
|
|
1010
|
-
(not isHttpUrl(attrValue) and not
|
|
1011
|
+
(not isHttpUrl(attrValue) and not isLegacyAbs(attrValue)))):
|
|
1011
1012
|
attrValue = attrValue.partition('#')[0].strip() # remove anchor
|
|
1012
1013
|
if attrValue not in ("", "."): # ignore anchor references to base document
|
|
1013
1014
|
base = docElt.modelDocument.baseForElement(docElt)
|
arelle/WebCache.py
CHANGED
|
@@ -21,6 +21,8 @@ from urllib import request as proxyhandlers
|
|
|
21
21
|
|
|
22
22
|
import certifi
|
|
23
23
|
|
|
24
|
+
from arelle.PythonUtil import isLegacyAbs
|
|
25
|
+
|
|
24
26
|
try:
|
|
25
27
|
import ssl
|
|
26
28
|
except ImportError:
|
|
@@ -315,7 +317,7 @@ class WebCache:
|
|
|
315
317
|
if url:
|
|
316
318
|
if url.startswith("file://"): url = url[7:]
|
|
317
319
|
elif url.startswith("file:\\"): url = url[6:]
|
|
318
|
-
if url and not (isHttpUrl(url) or
|
|
320
|
+
if url and not (isHttpUrl(url) or isLegacyAbs(url)):
|
|
319
321
|
if base is not None and not isHttpUrl(base) and '%' in url:
|
|
320
322
|
url = unquote(url)
|
|
321
323
|
if base:
|
|
@@ -332,7 +334,7 @@ class WebCache:
|
|
|
332
334
|
elif normedPath.startswith("file:\\"): normedPath = normedPath[6:]
|
|
333
335
|
|
|
334
336
|
# no base, not normalized, must be relative to current working directory
|
|
335
|
-
if base is None and not
|
|
337
|
+
if base is None and not isLegacyAbs(url):
|
|
336
338
|
normedPath = os.path.abspath(normedPath)
|
|
337
339
|
else:
|
|
338
340
|
normedPath = url
|
arelle/_version.py
CHANGED
arelle/oim/Load.py
CHANGED
|
@@ -24,7 +24,7 @@ from arelle.ModelValue import (DATETIME, dateTime, dayTimeDuration, qname,
|
|
|
24
24
|
yearMonthDuration)
|
|
25
25
|
from arelle.PluginManager import pluginClassMethods
|
|
26
26
|
from arelle.PrototypeInstanceObject import DimValuePrototype
|
|
27
|
-
from arelle.PythonUtil import attrdict, strTruncate
|
|
27
|
+
from arelle.PythonUtil import attrdict, isLegacyAbs, strTruncate
|
|
28
28
|
from arelle.typing import TypeGetText
|
|
29
29
|
from arelle.ValidateDuplicateFacts import (DuplicateTypeArg,
|
|
30
30
|
getDuplicateFactSetsWithType)
|
|
@@ -1769,7 +1769,7 @@ def _loadFromOIM(cntlr, error, warning, modelXbrl, oimFile, mappedUri):
|
|
|
1769
1769
|
# relativize taxonomyRefs to base where feasible
|
|
1770
1770
|
txBase = os.path.dirname(documentBase or (modelXbrl.entryLoadingUrl if modelXbrl else ""))
|
|
1771
1771
|
for i, tUrl in enumerate(taxonomyRefs or ()):
|
|
1772
|
-
if not UrlUtil.isAbsolute(tUrl) and
|
|
1772
|
+
if not UrlUtil.isAbsolute(tUrl) and isLegacyAbs(tUrl) and not UrlUtil.isAbsolute(txBase) and isLegacyAbs(txBase):
|
|
1773
1773
|
taxonomyRefs[i] = os.path.relpath(tUrl, txBase)
|
|
1774
1774
|
prevErrLen = len(modelXbrl.errors) # track any xbrl validation errors
|
|
1775
1775
|
xbrliNamespacePrefix = None
|
|
@@ -139,7 +139,7 @@ from arelle.ModelObject import ModelObject
|
|
|
139
139
|
from arelle.ModelValue import INVALIDixVALUE, qname
|
|
140
140
|
from arelle.PluginManager import pluginClassMethods
|
|
141
141
|
from arelle.PrototypeDtsObject import ArcPrototype, LocPrototype
|
|
142
|
-
from arelle.PythonUtil import attrdict
|
|
142
|
+
from arelle.PythonUtil import attrdict, isLegacyAbs
|
|
143
143
|
from arelle.RuntimeOptions import RuntimeOptions
|
|
144
144
|
from arelle.UrlUtil import isHttpUrl
|
|
145
145
|
from arelle.ValidateDuplicateFacts import DeduplicationType
|
|
@@ -305,7 +305,7 @@ def createTargetInstance(
|
|
|
305
305
|
def addLocallyReferencedFile(elt,filingFiles):
|
|
306
306
|
if elt.tag in ("a", "img"):
|
|
307
307
|
for attrTag, attrValue in elt.items():
|
|
308
|
-
if attrTag in ("href", "src") and not isHttpUrl(attrValue) and not
|
|
308
|
+
if attrTag in ("href", "src") and not isHttpUrl(attrValue) and not isLegacyAbs(attrValue):
|
|
309
309
|
attrValue = attrValue.partition('#')[0] # remove anchor
|
|
310
310
|
if attrValue: # ignore anchor references to base document
|
|
311
311
|
attrValue = os.path.normpath(attrValue) # change url path separators to host separators
|
arelle/plugin/loadFromExcel.py
CHANGED
|
@@ -11,7 +11,7 @@ import regex as re
|
|
|
11
11
|
from fnmatch import fnmatch
|
|
12
12
|
from collections import defaultdict, OrderedDict
|
|
13
13
|
from arelle import PythonUtil, XbrlConst, ModelDocument, UrlUtil
|
|
14
|
-
from arelle.PythonUtil import OrderedDefaultDict, OrderedSet
|
|
14
|
+
from arelle.PythonUtil import OrderedDefaultDict, OrderedSet, isLegacyAbs
|
|
15
15
|
from arelle.ModelDocument import Type, create as createModelDocument
|
|
16
16
|
from arelle.ModelValue import qname, QName
|
|
17
17
|
from arelle.Version import authorLabel, copyrightLabel
|
|
@@ -212,7 +212,7 @@ def loadFromExcel(cntlr, modelXbrl, excelFile, mappedUri):
|
|
|
212
212
|
if baseDir is None:
|
|
213
213
|
baseDir = thisDoc.extensionSchemaRelDirname
|
|
214
214
|
if (baseDir is not None and
|
|
215
|
-
not (UrlUtil.isAbsolute(filename) or
|
|
215
|
+
not (UrlUtil.isAbsolute(filename) or isLegacyAbs(filename))):
|
|
216
216
|
return posixpath.relpath(filename, baseDir)
|
|
217
217
|
return filename
|
|
218
218
|
|
|
@@ -279,7 +279,7 @@ def loadFromExcel(cntlr, modelXbrl, excelFile, mappedUri):
|
|
|
279
279
|
thisDoc.extensionSchemaPrefix = prefix
|
|
280
280
|
thisDoc.extensionSchemaFilename = filename
|
|
281
281
|
thisDoc.extensionSchemaNamespaceURI = namespaceURI
|
|
282
|
-
if not UrlUtil.isAbsolute(filename) and not
|
|
282
|
+
if not UrlUtil.isAbsolute(filename) and not isLegacyAbs(filename):
|
|
283
283
|
thisDoc.extensionSchemaRelDirname = posixpath.dirname(filename)
|
|
284
284
|
else:
|
|
285
285
|
thisDoc.extensionSchemaRelDirname = None
|
|
@@ -1893,7 +1893,7 @@ def saveDts(cntlr, modelXbrl, outputDtsDir):
|
|
|
1893
1893
|
import shutil
|
|
1894
1894
|
excelFileDir = os.path.dirname(modelXbrl.fileSource.url)
|
|
1895
1895
|
def saveToFile(url):
|
|
1896
|
-
if
|
|
1896
|
+
if isLegacyAbs(url):
|
|
1897
1897
|
return url
|
|
1898
1898
|
filepath = os.path.join(outputDtsDir, url)
|
|
1899
1899
|
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
|
@@ -1908,7 +1908,7 @@ def saveDts(cntlr, modelXbrl, outputDtsDir):
|
|
|
1908
1908
|
if refDoc.type == ModelDocument.Type.LINKBASE:
|
|
1909
1909
|
cntlr.showStatus(_("Saving XBRL DTS: {0}").format(os.path.basename(refDoc.uri)))
|
|
1910
1910
|
refDoc.save(saveToFile(refDoc.uri), updateFileHistory=False)
|
|
1911
|
-
elif not (UrlUtil.isAbsolute(doc.uri) or
|
|
1911
|
+
elif not (UrlUtil.isAbsolute(doc.uri) or isLegacyAbs(doc.uri) or outputDtsDir == excelFileDir):
|
|
1912
1912
|
srcfile = os.path.join(excelFileDir, doc.uri)
|
|
1913
1913
|
destfile = saveToFile(doc.uri)
|
|
1914
1914
|
if os.path.exists(srcfile):
|
|
@@ -22,7 +22,7 @@ from arelle.ModelInstanceObject import ModelInlineFootnote
|
|
|
22
22
|
from arelle.ModelObject import ModelObject
|
|
23
23
|
from arelle.ModelRelationshipSet import ModelRelationshipSet
|
|
24
24
|
from arelle.ModelValue import QName, qname
|
|
25
|
-
from arelle.PythonUtil import strTruncate
|
|
25
|
+
from arelle.PythonUtil import isLegacyAbs, strTruncate
|
|
26
26
|
from arelle.utils.validate.DetectScriptsInXhtml import containsScriptMarkers
|
|
27
27
|
from arelle.UrlUtil import decodeBase64DataImage, isHttpUrl, scheme
|
|
28
28
|
from arelle.ValidateFilingText import parseImageDataURL
|
|
@@ -365,7 +365,7 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
365
365
|
modelObject=elt, element=eltTag)
|
|
366
366
|
elif len(modelXbrl.ixdsHtmlElements) > 1:
|
|
367
367
|
_file = elt.get("href")
|
|
368
|
-
if not _file or isHttpUrl(_file) or
|
|
368
|
+
if not _file or isHttpUrl(_file) or isLegacyAbs(_file):
|
|
369
369
|
modelXbrl.warning("ESEF.2.5.4.externalCssReportPackage",
|
|
370
370
|
_("The CSS file should be physically stored within the report package: %{file}s."),
|
|
371
371
|
modelObject=elt, file=_file)
|
|
@@ -26,7 +26,7 @@ from arelle.ModelValue import QName
|
|
|
26
26
|
from arelle.ModelValue import qname
|
|
27
27
|
from arelle.ModelXbrl import ModelXbrl
|
|
28
28
|
|
|
29
|
-
from arelle.PythonUtil import normalizeSpace
|
|
29
|
+
from arelle.PythonUtil import isLegacyAbs, normalizeSpace
|
|
30
30
|
from arelle.PythonUtil import strTruncate
|
|
31
31
|
from arelle.utils.validate.DetectScriptsInXhtml import containsScriptMarkers
|
|
32
32
|
from arelle.UrlUtil import isHttpUrl
|
|
@@ -378,7 +378,7 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
378
378
|
modelObject=elt, element=eltTag)
|
|
379
379
|
elif len(modelXbrl.ixdsHtmlElements) > 1:
|
|
380
380
|
_file = elt.get("href")
|
|
381
|
-
if not _file or isHttpUrl(_file) or
|
|
381
|
+
if not _file or isHttpUrl(_file) or isLegacyAbs(_file):
|
|
382
382
|
modelXbrl.warning("ESEF.2.5.4.externalCssReportPackage",
|
|
383
383
|
_("The CSS file should be physically stored within the report package: %{file}s."),
|
|
384
384
|
modelObject=elt, file=_file)
|
|
@@ -664,14 +664,14 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
664
664
|
if not f.id:
|
|
665
665
|
factsMissingId.append(f)
|
|
666
666
|
escaped = f.get("escape") in ("true", "1")
|
|
667
|
-
if escaped != f.concept.
|
|
667
|
+
if f.concept is not None and escaped != f.concept.isTextBlock:
|
|
668
668
|
modelXbrl.error("ESEF.2.2.7.improperApplicationOfEscapeAttribute",
|
|
669
669
|
_("Facts with datatype 'dtr-types:textBlockItemType' MUST use the 'escape' attribute set to 'true'. Facts with any other datatype MUST use the 'escape' attribute set to 'false' - fact %(conceptName)s"),
|
|
670
670
|
modelObject=f, conceptName=f.concept.qname)
|
|
671
671
|
if f.effectiveValue in ["0", "-0"] and f.xValue != 0:
|
|
672
672
|
modelXbrl.warning("ESEF.2.2.5.roundedValueBelowScaleNotNull",
|
|
673
673
|
_("A value that has been rounded and is below the scale should show a value of zero. It has been found to have the value %(value)s - fact %(conceptName)s"),
|
|
674
|
-
modelObject=f, value=f.value, conceptName=f.concept
|
|
674
|
+
modelObject=f, value=f.value, conceptName=getattr(f.concept, "qname", ""))
|
|
675
675
|
if f.precision is not None:
|
|
676
676
|
precisionFacts.add(f)
|
|
677
677
|
if f.isNumeric and f.concept is not None and getattr(f, "xValid", 0) >= VALID:
|
|
@@ -3,6 +3,7 @@ See COPYRIGHT.md for copyright information.
|
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
+
import regex as re
|
|
6
7
|
from collections import defaultdict
|
|
7
8
|
from dataclasses import dataclass
|
|
8
9
|
|
|
@@ -12,6 +13,8 @@ from arelle.ModelXbrl import ModelXbrl
|
|
|
12
13
|
from arelle.utils.PluginData import PluginData
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
XBRLI_IDENTIFIER_PATTERN = re.compile(r"^(?!00)\d{8}$")
|
|
17
|
+
|
|
15
18
|
@dataclass
|
|
16
19
|
class PluginValidationDataExtension(PluginData):
|
|
17
20
|
financialReportingPeriodCurrentStartDateQn: QName
|
|
@@ -10,7 +10,7 @@ from arelle.ModelXbrl import ModelXbrl
|
|
|
10
10
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
11
11
|
from arelle.typing import TypeGetText
|
|
12
12
|
from arelle.utils.validate.ValidationPlugin import ValidationPlugin
|
|
13
|
-
from .DisclosureSystems import DISCLOSURE_SYSTEM_NT16, DISCLOSURE_SYSTEM_NT17, DISCLOSURE_SYSTEM_NT18, DISCLOSURE_SYSTEM_NT19
|
|
13
|
+
from .DisclosureSystems import DISCLOSURE_SYSTEM_NT16, DISCLOSURE_SYSTEM_NT17, DISCLOSURE_SYSTEM_NT18, DISCLOSURE_SYSTEM_NT19, DISCLOSURE_SYSTEM_INLINE_NT19
|
|
14
14
|
from .PluginValidationDataExtension import PluginValidationDataExtension
|
|
15
15
|
|
|
16
16
|
_: TypeGetText
|
|
@@ -129,7 +129,7 @@ class ValidationPluginExtension(ValidationPlugin):
|
|
|
129
129
|
'kvk-rpt-jaarverantwoording-2023-nlgaap-verzekeringsmaatschappijen.xsd',
|
|
130
130
|
'kvk-rpt-jaarverantwoording-2023-nlgaap-zorginstellingen.xsd',
|
|
131
131
|
]}
|
|
132
|
-
elif disclosureSystem == DISCLOSURE_SYSTEM_NT19:
|
|
132
|
+
elif disclosureSystem == DISCLOSURE_SYSTEM_NT19 or disclosureSystem == DISCLOSURE_SYSTEM_INLINE_NT19:
|
|
133
133
|
jenvNamespace = 'http://www.nltaxonomie.nl/nt19/jenv/20241211/dictionary/jenv-bw2-data'
|
|
134
134
|
kvkINamespace = 'http://www.nltaxonomie.nl/nt19/kvk/20241211/dictionary/kvk-data'
|
|
135
135
|
nlTypesNamespace = 'http://www.nltaxonomie.nl/nt19/sbr/20240301/dictionary/nl-types'
|
|
@@ -187,7 +187,7 @@ class ValidationPluginExtension(ValidationPlugin):
|
|
|
187
187
|
def modelXbrlLoadComplete(self, modelXbrl: ModelXbrl, *args: Any, **kwargs: Any) -> ModelDocument | LoadingException | None:
|
|
188
188
|
if self.disclosureSystemFromPluginSelected(modelXbrl):
|
|
189
189
|
disclosureSystem = modelXbrl.modelManager.disclosureSystem.name
|
|
190
|
-
if disclosureSystem in (DISCLOSURE_SYSTEM_NT16, DISCLOSURE_SYSTEM_NT17, DISCLOSURE_SYSTEM_NT18, DISCLOSURE_SYSTEM_NT19):
|
|
190
|
+
if disclosureSystem in (DISCLOSURE_SYSTEM_NT16, DISCLOSURE_SYSTEM_NT17, DISCLOSURE_SYSTEM_NT18, DISCLOSURE_SYSTEM_NT19, DISCLOSURE_SYSTEM_INLINE_NT19):
|
|
191
191
|
# Dutch taxonomies prior to 2025 incorrectly used hypercube linkrole for roots instead of dimension linkrole.
|
|
192
192
|
paramQName = qname('tlbDimRelsUseHcRoleForDomainRoots', noPrefixIsNoNamespace=True)
|
|
193
193
|
modelXbrl.modelManager.formulaOptions.parameterValues[paramQName] = (None, "true")
|
|
@@ -16,7 +16,7 @@ from typing import Any
|
|
|
16
16
|
from arelle.ModelDocument import LoadingException, ModelDocument
|
|
17
17
|
from arelle.Version import authorLabel, copyrightLabel
|
|
18
18
|
from .ValidationPluginExtension import ValidationPluginExtension
|
|
19
|
-
from .rules import br_kvk, fg_nl, fr_kvk, fr_nl
|
|
19
|
+
from .rules import br_kvk, fg_nl, fr_kvk, fr_nl, nl_kvk
|
|
20
20
|
|
|
21
21
|
PLUGIN_NAME = "Validate NL"
|
|
22
22
|
DISCLOSURE_SYSTEM_VALIDATION_TYPE = "NL"
|
|
@@ -26,7 +26,7 @@ validationPlugin = ValidationPluginExtension(
|
|
|
26
26
|
name=PLUGIN_NAME,
|
|
27
27
|
disclosureSystemConfigUrl=Path(__file__).parent / "resources" / "config.xml",
|
|
28
28
|
validationTypes=[DISCLOSURE_SYSTEM_VALIDATION_TYPE],
|
|
29
|
-
validationRuleModules=[br_kvk, fg_nl, fr_kvk, fr_nl],
|
|
29
|
+
validationRuleModules=[br_kvk, fg_nl, fr_kvk, fr_nl, nl_kvk],
|
|
30
30
|
)
|
|
31
31
|
|
|
32
32
|
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
4
4
|
xsi:noNamespaceSchemaLocation="../../../../config/disclosuresystems.xsd">
|
|
5
5
|
<!-- see arelle/config/disclosuresystems.xml for full comments -->
|
|
6
|
+
<DisclosureSystem
|
|
7
|
+
names="INLINE-NT19|inline-nt19|kvk-ixbrl-2024-preview"
|
|
8
|
+
description="Checks for NT2024"
|
|
9
|
+
validationType="NL"
|
|
10
|
+
/>
|
|
6
11
|
<DisclosureSystem
|
|
7
12
|
names="NT19|nt19|NT19-preview|nt19-preview"
|
|
8
13
|
description="Checks for NT19"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
See COPYRIGHT.md for copyright information.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from datetime import date, timedelta
|
|
7
|
+
from dateutil import relativedelta
|
|
8
|
+
from collections.abc import Iterable
|
|
9
|
+
from typing import Any, cast, TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from regex import regex
|
|
12
|
+
|
|
13
|
+
from arelle import XmlUtil, XbrlConst
|
|
14
|
+
from arelle.ModelObject import ModelObject
|
|
15
|
+
from arelle.ValidateXbrl import ValidateXbrl
|
|
16
|
+
from arelle.XmlValidate import INVALID
|
|
17
|
+
from arelle.typing import TypeGetText
|
|
18
|
+
from arelle.utils.PluginHooks import ValidationHook
|
|
19
|
+
from arelle.utils.validate.Decorator import validation
|
|
20
|
+
from arelle.utils.validate.Validation import Validation
|
|
21
|
+
from ..DisclosureSystems import (
|
|
22
|
+
DISCLOSURE_SYSTEM_INLINE_NT19
|
|
23
|
+
)
|
|
24
|
+
from ..PluginValidationDataExtension import PluginValidationDataExtension, XBRLI_IDENTIFIER_PATTERN
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from arelle.ModelXbrl import ModelXbrl
|
|
28
|
+
from arelle.ModelValue import QName
|
|
29
|
+
|
|
30
|
+
_: TypeGetText
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _getReportingPeriodDateValue(modelXbrl: ModelXbrl, qname: QName) -> date | None:
|
|
34
|
+
facts = modelXbrl.factsByQname.get(qname)
|
|
35
|
+
if facts and len(facts) == 1:
|
|
36
|
+
datetimeValue = XmlUtil.datetimeValue(next(iter(facts)))
|
|
37
|
+
if datetimeValue:
|
|
38
|
+
return datetimeValue.date()
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@validation(
|
|
43
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
44
|
+
disclosureSystems=[
|
|
45
|
+
DISCLOSURE_SYSTEM_INLINE_NT19
|
|
46
|
+
],
|
|
47
|
+
)
|
|
48
|
+
def rule_nl_kvk_3_1_1_1(
|
|
49
|
+
pluginData: PluginValidationDataExtension,
|
|
50
|
+
val: ValidateXbrl,
|
|
51
|
+
*args: Any,
|
|
52
|
+
**kwargs: Any,
|
|
53
|
+
) -> Iterable[Validation]:
|
|
54
|
+
"""
|
|
55
|
+
NL-KVK.3.1.1.1: xbrli:identifier content to match KVK number format that must consist of 8 consecutive digits;
|
|
56
|
+
first two digits must not be '00'.
|
|
57
|
+
"""
|
|
58
|
+
entityIdentifierValues = {context.entityIdentifier for context in val.modelXbrl.contexts.values()}
|
|
59
|
+
for entityId in entityIdentifierValues:
|
|
60
|
+
if not XBRLI_IDENTIFIER_PATTERN.match(entityId):
|
|
61
|
+
yield Validation.error(
|
|
62
|
+
codes='NL.NL-KVK-3.1.1.1',
|
|
63
|
+
msg=_('xbrli:identifier content to match KVK number format that must consist of 8 consecutive digits.'
|
|
64
|
+
'Additionally the first two digits must not be "00".'),
|
|
65
|
+
modelObject = val.modelXbrl
|
|
66
|
+
)
|
|
67
|
+
return
|
arelle/plugin/xbrlDB/SqlDb.py
CHANGED
|
@@ -36,7 +36,7 @@ try:
|
|
|
36
36
|
mysqlProgrammingError = pymysql.ProgrammingError
|
|
37
37
|
mysqlInterfaceError = pymysql.InterfaceError
|
|
38
38
|
mysqlInternalError = pymysql.InternalError
|
|
39
|
-
except ImportError:
|
|
39
|
+
except (ImportError, OSError):
|
|
40
40
|
try :
|
|
41
41
|
import MySQLdb # LGPL License and used on GAE, Python 2.7 only
|
|
42
42
|
hasMySql = True
|
arelle/utils/PluginHooks.py
CHANGED
|
@@ -15,6 +15,8 @@ if TYPE_CHECKING:
|
|
|
15
15
|
from optparse import OptionParser
|
|
16
16
|
from tkinter import Menu
|
|
17
17
|
|
|
18
|
+
from bottle import Bottle # type: ignore[import-untyped]
|
|
19
|
+
|
|
18
20
|
from arelle.Cntlr import Cntlr
|
|
19
21
|
from arelle.CntlrCmdLine import CntlrCmdLine
|
|
20
22
|
from arelle.CntlrWinMain import CntlrWinMain
|
|
@@ -25,7 +27,6 @@ if TYPE_CHECKING:
|
|
|
25
27
|
from arelle.ModelManager import ModelManager
|
|
26
28
|
from arelle.ModelXbrl import ModelXbrl
|
|
27
29
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
28
|
-
from arelle.webserver.bottle import Bottle
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
class ValidationHook(Enum):
|
|
@@ -191,7 +192,7 @@ class PluginHooks(ABC):
|
|
|
191
192
|
app.route('/rest/my-run/<file:path>', ("GET", "POST"), my_run)
|
|
192
193
|
```
|
|
193
194
|
|
|
194
|
-
:param app: The
|
|
195
|
+
:param app: The Bottle server.
|
|
195
196
|
:param cntlr: The [controller](#arelle.CntlrCmdLine.CntlrCmdLine) for the server.
|
|
196
197
|
:param host: The webserver host.
|
|
197
198
|
:param port: The webserver port.
|