arelle-release 2.37.20__py3-none-any.whl → 2.37.22__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 +8 -0
- arelle/RuntimeOptions.py +0 -6
- arelle/XbrlConst.py +4 -0
- arelle/_version.py +2 -2
- arelle/api/Session.py +92 -60
- arelle/plugin/validate/NL/DisclosureSystems.py +14 -0
- arelle/plugin/validate/NL/LinkbaseType.py +77 -0
- arelle/plugin/validate/NL/PluginValidationDataExtension.py +236 -27
- arelle/plugin/validate/NL/ValidationPluginExtension.py +11 -1
- arelle/plugin/validate/NL/__init__.py +0 -4
- arelle/plugin/validate/NL/resources/config.xml +6 -1
- arelle/plugin/validate/NL/rules/nl_kvk.py +596 -118
- {arelle_release-2.37.20.dist-info → arelle_release-2.37.22.dist-info}/METADATA +1 -1
- {arelle_release-2.37.20.dist-info → arelle_release-2.37.22.dist-info}/RECORD +23 -21
- tests/integration_tests/validation/conformance_suite_configs.py +2 -0
- tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024.py +16 -31
- tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024_gaap_other.py +244 -0
- tests/integration_tests/validation/discover_tests.py +2 -2
- tests/unit_tests/arelle/test_runtimeoptions.py +0 -13
- {arelle_release-2.37.20.dist-info → arelle_release-2.37.22.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.20.dist-info → arelle_release-2.37.22.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.20.dist-info → arelle_release-2.37.22.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.37.20.dist-info → arelle_release-2.37.22.dist-info}/top_level.txt +0 -0
arelle/CntlrCmdLine.py
CHANGED
|
@@ -537,6 +537,14 @@ def parseArgs(args):
|
|
|
537
537
|
runtimeOptions = RuntimeOptions(pluginOptions=pluginOptions, **baseOptions)
|
|
538
538
|
except RuntimeOptionsException as e:
|
|
539
539
|
parser.error(f"{e}, please try\n python CntlrCmdLine.py --help")
|
|
540
|
+
if (
|
|
541
|
+
runtimeOptions.entrypointFile is None and
|
|
542
|
+
not runtimeOptions.proxy and
|
|
543
|
+
not runtimeOptions.plugins and
|
|
544
|
+
not pluginOptions and
|
|
545
|
+
not runtimeOptions.webserver
|
|
546
|
+
):
|
|
547
|
+
parser.error("No entrypoint specified, please try\n python CntlrCmdLine.py --help")
|
|
540
548
|
return runtimeOptions, arellePluginModules
|
|
541
549
|
|
|
542
550
|
|
arelle/RuntimeOptions.py
CHANGED
|
@@ -183,12 +183,6 @@ class RuntimeOptions:
|
|
|
183
183
|
raise RuntimeOptionsException('Provided plugin options already exist as base options {}'.format(existingBaseOptions))
|
|
184
184
|
for optionName, optionValue in pluginOptions.items():
|
|
185
185
|
setattr(self, optionName, optionValue)
|
|
186
|
-
if (self.entrypointFile is None and
|
|
187
|
-
not self.proxy and
|
|
188
|
-
not self.plugins and
|
|
189
|
-
not pluginOptions and
|
|
190
|
-
not self.webserver):
|
|
191
|
-
raise RuntimeOptionsException('Incorrect arguments')
|
|
192
186
|
if self.webserver and not hasWebServer():
|
|
193
187
|
raise RuntimeOptionsException("Webserver option requires webserver module")
|
|
194
188
|
if self.webserver and any((
|
arelle/XbrlConst.py
CHANGED
|
@@ -101,6 +101,7 @@ elementLabel = "http://xbrl.org/arcrole/2008/element-label"
|
|
|
101
101
|
genLabel = "http://xbrl.org/2008/label"
|
|
102
102
|
qnGenLabel = qname("{http://xbrl.org/2008/label}label")
|
|
103
103
|
xbrldt = "http://xbrl.org/2005/xbrldt"
|
|
104
|
+
qnXbrldtClosed = qname("{http://xbrl.org/2005/xbrldt}xbrldt:closed")
|
|
104
105
|
qnXbrldtHypercubeItem = qname("{http://xbrl.org/2005/xbrldt}xbrldt:hypercubeItem")
|
|
105
106
|
qnXbrldtDimensionItem = qname("{http://xbrl.org/2005/xbrldt}xbrldt:dimensionItem")
|
|
106
107
|
qnXbrldtContextElement = qname("{http://xbrl.org/2005/xbrldt}xbrldt:contextElement")
|
|
@@ -108,6 +109,9 @@ xbrldi = "http://xbrl.org/2006/xbrldi"
|
|
|
108
109
|
qnXbrldiExplicitMember = qname("{http://xbrl.org/2006/xbrldi}xbrldi:explicitMember")
|
|
109
110
|
qnXbrldiTypedMember = qname("{http://xbrl.org/2006/xbrldi}xbrldi:typedMember")
|
|
110
111
|
xlink = "http://www.w3.org/1999/xlink"
|
|
112
|
+
qnXlinkArcRole = qname("{http://www.w3.org/1999/xlink}xlink:arcrole")
|
|
113
|
+
qnXlinkFrom = qname("{http://www.w3.org/1999/xlink}xlink:from")
|
|
114
|
+
qnXlinkType = qname("{http://www.w3.org/1999/xlink}xlink:type")
|
|
111
115
|
xl = "http://www.xbrl.org/2003/XLink"
|
|
112
116
|
qnXlExtended = qname("{http://www.xbrl.org/2003/XLink}xl:extended")
|
|
113
117
|
qnXlLocator = qname("{http://www.xbrl.org/2003/XLink}xl:locator")
|
arelle/_version.py
CHANGED
arelle/api/Session.py
CHANGED
|
@@ -1,26 +1,49 @@
|
|
|
1
1
|
"""
|
|
2
2
|
See COPYRIGHT.md for copyright information.
|
|
3
3
|
|
|
4
|
-
The
|
|
5
|
-
A roadmap for this API is in development.
|
|
6
|
-
|
|
7
|
-
Users of this API should expect changes in future releases.
|
|
4
|
+
The `arelle.api` module is the supported method for integrating Arelle into other Python applications.
|
|
8
5
|
"""
|
|
9
6
|
from __future__ import annotations
|
|
10
7
|
|
|
11
8
|
import logging
|
|
9
|
+
import threading
|
|
12
10
|
from types import TracebackType
|
|
13
11
|
from typing import Any, BinaryIO
|
|
14
12
|
|
|
15
|
-
from arelle import
|
|
13
|
+
from arelle import PackageManager, PluginManager
|
|
16
14
|
from arelle.CntlrCmdLine import CntlrCmdLine, createCntlrAndPreloadPlugins
|
|
17
15
|
from arelle.ModelXbrl import ModelXbrl
|
|
18
16
|
from arelle.RuntimeOptions import RuntimeOptions
|
|
19
17
|
|
|
18
|
+
_session_lock = threading.Lock()
|
|
19
|
+
|
|
20
20
|
|
|
21
21
|
class Session:
|
|
22
|
+
"""
|
|
23
|
+
CRITICAL THREAD SAFETY WARNING:
|
|
24
|
+
|
|
25
|
+
Arelle uses shared global state (PackageManager, PluginManager) which is NOT thread-safe.
|
|
26
|
+
Only ONE Session can run at a time across the entire process.
|
|
27
|
+
|
|
28
|
+
Safe usage:
|
|
29
|
+
- Use one Session at a time per process
|
|
30
|
+
- Use a process pool instead of thread pool for parallelism
|
|
31
|
+
|
|
32
|
+
Unsafe usage:
|
|
33
|
+
- Running multiple Sessions concurrently in any threads
|
|
34
|
+
- Threading.Thread with Session.run()
|
|
35
|
+
"""
|
|
36
|
+
|
|
22
37
|
def __init__(self) -> None:
|
|
23
38
|
self._cntlr: CntlrCmdLine | None = None
|
|
39
|
+
self._thread_id = threading.get_ident()
|
|
40
|
+
|
|
41
|
+
def _check_thread(self) -> None:
|
|
42
|
+
"""Ensure session is only used from the thread that created it."""
|
|
43
|
+
if threading.get_ident() != self._thread_id:
|
|
44
|
+
raise RuntimeError(
|
|
45
|
+
"Session objects cannot be shared between threads. Create a new Session instance in each thread."
|
|
46
|
+
)
|
|
24
47
|
|
|
25
48
|
def __enter__(self) -> Any:
|
|
26
49
|
return self
|
|
@@ -34,9 +57,11 @@ class Session:
|
|
|
34
57
|
self.close()
|
|
35
58
|
|
|
36
59
|
def close(self) -> None:
|
|
37
|
-
|
|
38
|
-
self.
|
|
39
|
-
|
|
60
|
+
with _session_lock:
|
|
61
|
+
self._check_thread()
|
|
62
|
+
if self._cntlr is not None:
|
|
63
|
+
self._cntlr.close()
|
|
64
|
+
PluginManager.close()
|
|
40
65
|
|
|
41
66
|
def get_log_messages(self) -> list[dict[str, Any]]:
|
|
42
67
|
"""
|
|
@@ -81,6 +106,7 @@ class Session:
|
|
|
81
106
|
responseZipStream: BinaryIO | None = None,
|
|
82
107
|
logHandler: logging.Handler | None = None,
|
|
83
108
|
logFilters: list[logging.Filter] | None = None,
|
|
109
|
+
sourceZipStreamFileName: str | None = None,
|
|
84
110
|
) -> bool:
|
|
85
111
|
"""
|
|
86
112
|
Perform a run using the given options.
|
|
@@ -88,59 +114,65 @@ class Session:
|
|
|
88
114
|
:param sourceZipStream: Optional stream to read source data from.
|
|
89
115
|
:param responseZipStream: Options stream to write response data to.
|
|
90
116
|
:param logHandler: Optional log handler to use for logging.
|
|
117
|
+
:param sourceZipStreamFileName: Optional file name to use for the passed zip stream.
|
|
91
118
|
:return: True if the run was successful, False otherwise.
|
|
92
119
|
"""
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
self._cntlr.setUiLanguage(options.uiLang)
|
|
106
|
-
self._cntlr.disablePersistentConfig = options.disablePersistentConfig or False
|
|
107
|
-
logRefObjectProperties = True
|
|
108
|
-
if options.logRefObjectProperties is not None:
|
|
109
|
-
logRefObjectProperties = options.logRefObjectProperties
|
|
110
|
-
if options.webserver:
|
|
111
|
-
assert sourceZipStream is None, "Source streaming is not supported with webserver"
|
|
112
|
-
assert responseZipStream is None, "Response streaming is not supported with webserver"
|
|
113
|
-
if not self._cntlr.logger:
|
|
114
|
-
self._cntlr.startLogging(
|
|
115
|
-
logFileName='logToBuffer',
|
|
116
|
-
logFilters=logFilters,
|
|
117
|
-
logHandler=logHandler,
|
|
118
|
-
logTextMaxLength=options.logTextMaxLength,
|
|
119
|
-
logRefObjectProperties=logRefObjectProperties,
|
|
120
|
-
logPropagate=options.logPropagate,
|
|
120
|
+
with _session_lock:
|
|
121
|
+
self._check_thread()
|
|
122
|
+
if sourceZipStreamFileName is not None and sourceZipStream is None:
|
|
123
|
+
raise ValueError("sourceZipStreamFileName may only be provided if sourceZipStream is not None.")
|
|
124
|
+
PackageManager.reset()
|
|
125
|
+
PluginManager.reset()
|
|
126
|
+
if self._cntlr is None:
|
|
127
|
+
# Certain options must be passed into the controller constructor to have the intended effect
|
|
128
|
+
self._cntlr = createCntlrAndPreloadPlugins(
|
|
129
|
+
uiLang=options.uiLang,
|
|
130
|
+
disablePersistentConfig=options.disablePersistentConfig,
|
|
131
|
+
arellePluginModules={},
|
|
121
132
|
)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
133
|
+
else:
|
|
134
|
+
# Certain options passed into the controller constructor need to be updated
|
|
135
|
+
if self._cntlr.uiLang != options.uiLang:
|
|
136
|
+
self._cntlr.setUiLanguage(options.uiLang)
|
|
137
|
+
self._cntlr.disablePersistentConfig = options.disablePersistentConfig or False
|
|
138
|
+
logRefObjectProperties = True
|
|
139
|
+
if options.logRefObjectProperties is not None:
|
|
140
|
+
logRefObjectProperties = options.logRefObjectProperties
|
|
141
|
+
if options.webserver:
|
|
142
|
+
assert sourceZipStream is None, "Source streaming is not supported with webserver"
|
|
143
|
+
assert responseZipStream is None, "Response streaming is not supported with webserver"
|
|
144
|
+
if not self._cntlr.logger:
|
|
145
|
+
self._cntlr.startLogging(
|
|
146
|
+
logFileName='logToBuffer',
|
|
147
|
+
logFilters=logFilters,
|
|
148
|
+
logHandler=logHandler,
|
|
149
|
+
logTextMaxLength=options.logTextMaxLength,
|
|
150
|
+
logRefObjectProperties=logRefObjectProperties,
|
|
151
|
+
logPropagate=options.logPropagate,
|
|
152
|
+
)
|
|
153
|
+
self._cntlr.postLoggingInit()
|
|
154
|
+
from arelle import CntlrWebMain
|
|
155
|
+
CntlrWebMain.startWebserver(self._cntlr, options)
|
|
156
|
+
return True
|
|
157
|
+
else:
|
|
158
|
+
if not self._cntlr.logger:
|
|
159
|
+
self._cntlr.startLogging(
|
|
160
|
+
logFileName=(options.logFile or "logToPrint"),
|
|
161
|
+
logFileMode=options.logFileMode,
|
|
162
|
+
logFormat=(options.logFormat or "[%(messageCode)s] %(message)s - %(file)s"),
|
|
163
|
+
logLevel=(options.logLevel or "DEBUG"),
|
|
164
|
+
logFilters=logFilters,
|
|
165
|
+
logHandler=logHandler,
|
|
166
|
+
logToBuffer=options.logFile == 'logToBuffer',
|
|
167
|
+
logTextMaxLength=options.logTextMaxLength, # e.g., used by EDGAR/render to require buffered logging
|
|
168
|
+
logRefObjectProperties=logRefObjectProperties,
|
|
169
|
+
logXmlMaxAttributeLength=options.logXmlMaxAttributeLength,
|
|
170
|
+
logPropagate=options.logPropagate,
|
|
171
|
+
)
|
|
172
|
+
self._cntlr.postLoggingInit() # Cntlr options after logging is started
|
|
173
|
+
return self._cntlr.run(
|
|
174
|
+
options,
|
|
175
|
+
sourceZipStream=sourceZipStream,
|
|
176
|
+
responseZipStream=responseZipStream,
|
|
177
|
+
sourceZipStreamFileName=sourceZipStreamFileName,
|
|
140
178
|
)
|
|
141
|
-
self._cntlr.postLoggingInit() # Cntlr options after logging is started
|
|
142
|
-
return self._cntlr.run(
|
|
143
|
-
options,
|
|
144
|
-
sourceZipStream=sourceZipStream,
|
|
145
|
-
responseZipStream=responseZipStream,
|
|
146
|
-
)
|
|
@@ -3,3 +3,17 @@ DISCLOSURE_SYSTEM_NT17 = 'NT17'
|
|
|
3
3
|
DISCLOSURE_SYSTEM_NT18 = 'NT18'
|
|
4
4
|
DISCLOSURE_SYSTEM_NT19 = 'NT19'
|
|
5
5
|
DISCLOSURE_SYSTEM_NL_INLINE_2024 = 'NL-INLINE-2024'
|
|
6
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024_GAAP_OTHER = 'NL-INLINE-2024-GAAP-OTHER'
|
|
7
|
+
|
|
8
|
+
ALL_NL_INLINE_DISCLOSURE_SYSTEMS = [
|
|
9
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024,
|
|
10
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024_GAAP_OTHER,
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS = [
|
|
14
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024,
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS = [
|
|
18
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024_GAAP_OTHER,
|
|
19
|
+
]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'''
|
|
2
|
+
See COPYRIGHT.md for copyright information.
|
|
3
|
+
'''
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from enum import Enum
|
|
7
|
+
|
|
8
|
+
from arelle import XbrlConst
|
|
9
|
+
from arelle.ModelValue import QName
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LinkbaseType(Enum):
|
|
13
|
+
CALCULATION = "calculation"
|
|
14
|
+
DEFINITION = "definition"
|
|
15
|
+
LABEL = "label"
|
|
16
|
+
PRESENTATION = "presentation"
|
|
17
|
+
REFERENCE = "reference"
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def fromRefUri(refUri: str | None) -> LinkbaseType | None:
|
|
21
|
+
"""
|
|
22
|
+
Returns the LinkbaseType corresponding to the given ref URI.
|
|
23
|
+
If the URI does not match any known linkbase reference type, returns None.
|
|
24
|
+
"""
|
|
25
|
+
if refUri is None:
|
|
26
|
+
return None
|
|
27
|
+
return LINKBASE_TYPE_BY_REF_URI.get(refUri, None)
|
|
28
|
+
|
|
29
|
+
def getArcQn(self) -> QName:
|
|
30
|
+
"""
|
|
31
|
+
Returns the qname of the arc associated with this LinkbaseType.
|
|
32
|
+
"""
|
|
33
|
+
return LINKBASE_ARC_QN[self]
|
|
34
|
+
|
|
35
|
+
def getLinkQn(self) -> QName:
|
|
36
|
+
"""
|
|
37
|
+
Returns the qname of the link associated with this LinkbaseType.
|
|
38
|
+
"""
|
|
39
|
+
return LINKBASE_LINK_QN[self]
|
|
40
|
+
|
|
41
|
+
def getLowerName(self) -> str:
|
|
42
|
+
"""
|
|
43
|
+
Returns the lower-case name of this LinkbaseType.
|
|
44
|
+
"""
|
|
45
|
+
return self.value.lower()
|
|
46
|
+
|
|
47
|
+
def getRefUri(self) -> str:
|
|
48
|
+
"""
|
|
49
|
+
Returns the ref URI associated with this LinkbaseType.
|
|
50
|
+
"""
|
|
51
|
+
return LINKBASE_REF_URIS[self]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
LINKBASE_ARC_QN = {
|
|
55
|
+
LinkbaseType.CALCULATION: XbrlConst.qnLinkCalculationArc,
|
|
56
|
+
LinkbaseType.DEFINITION: XbrlConst.qnLinkDefinitionArc,
|
|
57
|
+
LinkbaseType.LABEL: XbrlConst.qnLinkLabelArc,
|
|
58
|
+
LinkbaseType.PRESENTATION: XbrlConst.qnLinkPresentationArc,
|
|
59
|
+
LinkbaseType.REFERENCE: XbrlConst.qnLinkReferenceArc,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
LINKBASE_LINK_QN = {
|
|
63
|
+
LinkbaseType.CALCULATION: XbrlConst.qnLinkCalculationLink,
|
|
64
|
+
LinkbaseType.DEFINITION: XbrlConst.qnLinkDefinitionLink,
|
|
65
|
+
LinkbaseType.LABEL: XbrlConst.qnLinkLabelLink,
|
|
66
|
+
LinkbaseType.PRESENTATION: XbrlConst.qnLinkPresentationLink,
|
|
67
|
+
LinkbaseType.REFERENCE: XbrlConst.qnLinkReferenceLink,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
LINKBASE_REF_URIS = {
|
|
71
|
+
LinkbaseType.CALCULATION: "http://www.xbrl.org/2003/role/calculationLinkbaseRef",
|
|
72
|
+
LinkbaseType.DEFINITION: "http://www.xbrl.org/2003/role/definitionLinkbaseRef",
|
|
73
|
+
LinkbaseType.LABEL: "http://www.xbrl.org/2003/role/labelLinkbaseRef",
|
|
74
|
+
LinkbaseType.PRESENTATION: "http://www.xbrl.org/2003/role/presentationLinkbaseRef",
|
|
75
|
+
LinkbaseType.REFERENCE: "http://www.xbrl.org/2003/role/referenceLinkbaseRef",
|
|
76
|
+
}
|
|
77
|
+
LINKBASE_TYPE_BY_REF_URI = {v: k for k, v in LINKBASE_REF_URIS.items()}
|